git log: correctly do update() for cherry-pick (not allowed for current branch commits, not allowed for merge commits (additional logic required for that)); show stashed commits

This commit is contained in:
irengrig
2011-02-02 14:49:03 +03:00
parent e3b3a4d7dd
commit b8161808a7
22 changed files with 678 additions and 198 deletions
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author irengrig
* Date: 2/2/11
* Time: 10:11 AM
* Cartesian product
*/
public class CollectionsMultiplier<T> {
private List<List<T>> myInner;
public void add(@Nullable final List<T> list) {
if (list == null || list.isEmpty()) return;
if (myInner == null) {
myInner = Collections.singletonList(list);
return;
}
final List<List<T>> copy = myInner;
myInner = new ArrayList<List<T>>();
for (T t : list) {
for (List<T> existing : copy) {
final ArrayList<T> newList = new ArrayList<T>(existing);
newList.add(t);
myInner.add(newList);
}
}
}
public boolean isEmpty() {
return myInner == null;
}
public void iterateResult(final Consumer<List<T>> consumer) {
if (myInner == null) return;
for (List<T> list : myInner) {
consumer.consume(list);
}
}
}
+1
View File
@@ -32,6 +32,7 @@
<orderEntry type="library" name="TestNG" level="project" />
<orderEntry type="module" module-name="platform-api" />
<orderEntry type="library" name="commons-lang" level="project" />
<orderEntry type="library" name="Guava" level="project" />
</component>
</module>
@@ -34,7 +34,6 @@ import git4idea.commands.GitCommand;
import git4idea.commands.GitSimpleHandler;
import git4idea.commands.StringScanner;
import git4idea.history.browser.SHAHash;
import git4idea.history.wholeTree.CommitI;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -301,10 +300,12 @@ public class GitChangeUtils {
}
@Nullable
public static SHAHash commitExists(final Project project, final VirtualFile root, final String anyReference) {
public static SHAHash commitExists(final Project project, final VirtualFile root, final String anyReference,
final String... parameters) {
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
h.setNoSSH(true);
h.setSilent(true);
h.addParameters(parameters);
h.addParameters("--max-count=1", "--pretty=%H", "--encoding=UTF-8", "\"" + anyReference + "\"", "--");
try {
final String output = h.run().trim();
@@ -316,6 +317,22 @@ public class GitChangeUtils {
}
}
public static boolean isAnyLevelChild(final Project project, final VirtualFile root, final SHAHash parent,
final String anyReferenceChild) {
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.MERGE_BASE);
h.setNoSSH(true);
h.setSilent(true);
h.addParameters("\"" + parent.getValue() + "\"","\"" + anyReferenceChild + "\"", "--");
try {
final String output = h.run().trim();
if (StringUtil.isEmptyOrSpaces(output)) return false;
return parent.getValue().equals(output.trim());
}
catch (VcsException e) {
return false;
}
}
@Nullable
public static SHAHash commitExistsByComment(final Project project, final VirtualFile root, final String anyReference) {
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
@@ -37,13 +37,17 @@ import com.intellij.util.Consumer;
import com.intellij.util.concurrency.Semaphore;
import git4idea.*;
import git4idea.commands.*;
import git4idea.config.GitConfigUtil;
import git4idea.history.browser.GitCommit;
import git4idea.history.browser.SHAHash;
import git4idea.history.browser.SymbolicRefs;
import git4idea.history.wholeTree.AbstractHash;
import git4idea.history.wholeTree.CommitHashPlusParents;
import git4idea.ui.GitUIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
@@ -534,6 +538,36 @@ public class GitHistoryUtils {
return null;
}
@Nullable
public static List<Pair<String, GitCommit>> loadStashStackAsCommits(@NotNull Project project, @NotNull VirtualFile root,
SymbolicRefs refs, final String... parameters) throws VcsException {
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.STASH);
GitLogParser parser = new GitLogParser(SHORT_HASH, HASH, COMMIT_TIME, AUTHOR_NAME, AUTHOR_TIME, AUTHOR_EMAIL, COMMITTER_NAME, COMMITTER_EMAIL, SHORT_PARENTS, REF_NAMES, SHORT_REF_LOG_SELECTOR, SUBJECT, BODY);
h.setSilent(true);
h.setNoSSH(true);
h.addParameters("list");
h.addParameters(parameters);
h.addParameters(parser.getPretty());
parser.parseStatusBeforeName(true);
String out;
try {
h.setCharset(Charset.forName(GitConfigUtil.getLogEncoding(project, root)));
out = h.run();
}
catch (VcsException e) {
GitUIUtil.showOperationError(project, e, h.printableCommandLine());
return null;
}
final List<GitLogRecord> gitLogRecords = parser.parse(out);
final List<Pair<String, GitCommit>> result = new ArrayList<Pair<String, GitCommit>>();
for (GitLogRecord gitLogRecord : gitLogRecords) {
final GitCommit gitCommit = createCommit(project, refs, root, gitLogRecord);
result.add(new Pair<String, GitCommit>(gitLogRecord.getShortenedRefLog(), gitCommit));
}
return result;
}
public static List<GitCommit> commitsDetails(Project project,
FilePath path, SymbolicRefs refs,
final Collection<String> commitsIds) throws VcsException {
@@ -15,22 +15,9 @@
*/
package git4idea.history;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.containers.Convertor;
import git4idea.GitContentRevision;
import git4idea.GitRevisionNumber;
import git4idea.history.wholeTree.AbstractHash;
import java.io.File;
import java.util.*;
/**
@@ -80,8 +67,8 @@ class GitLogParser {
* These are the pieces of information about a commit which we want to get from 'git log'.
*/
enum GitLogOption {
SHORT_HASH("h"), HASH("H"), COMMIT_TIME("ct"), AUTHOR_NAME("an"), AUTHOR_TIME("at"), AUTHOR_EMAIL("ae"), COMMITTER_NAME("cn"), COMMITTER_EMAIL("ce"), SUBJECT("s"), BODY("b"),
SHORT_PARENTS("p"), PARENTS("P"), REF_NAMES("d");
SHORT_HASH("h"), HASH("H"), COMMIT_TIME("ct"), AUTHOR_NAME("an"), AUTHOR_TIME("at"), AUTHOR_EMAIL("ae"), COMMITTER_NAME("cn"),
COMMITTER_EMAIL("ce"), SUBJECT("s"), BODY("b"), SHORT_PARENTS("p"), PARENTS("P"), REF_NAMES("d"), SHORT_REF_LOG_SELECTOR("gd");
private String myPlaceholder;
private GitLogOption(String placeholder) { myPlaceholder = placeholder; }
@@ -31,12 +31,7 @@ import git4idea.GitUtil;
import git4idea.history.wholeTree.AbstractHash;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import static git4idea.history.GitLogParser.GitLogOption.*;
@@ -90,6 +85,7 @@ class GitLogRecord {
String getCommitterEmail() { return lookup(COMMITTER_EMAIL); }
String getSubject() { return lookup(SUBJECT); }
String getBody() { return lookup(BODY); }
String getShortenedRefLog() { return lookup(SHORT_REF_LOG_SELECTOR); }
// access methods with some formatting or conversion
@@ -21,6 +21,7 @@ import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.changes.FilePathsHelper;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.PairProcessor;
import git4idea.GitUtil;
import org.jetbrains.annotations.NotNull;
@@ -31,6 +32,21 @@ import java.util.regex.Pattern;
public class ChangesFilter {
public static void filtersToParameters(Collection<Filter> filters, List<String> parameters) {
for (Filter filter : filters) {
filter.getCommandParametersFilter().applyToCommandLine(parameters);
}
}
public static String[] filtersToParameterArray(Collection<Filter> filters) {
if (filters == null || filters.isEmpty()) return ArrayUtil.EMPTY_STRING_ARRAY;
final ArrayList<String> strings = new ArrayList<String>();
for (Filter filter : filters) {
filter.getCommandParametersFilter().applyToCommandLine(strings);
}
return strings.toArray(new String[strings.size()]);
}
public abstract static class Merger {
private final Collection<MemoryFilter> myFilters;
private MemoryFilter myResult;
@@ -16,7 +16,9 @@
package git4idea.history.browser;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.util.containers.Convertor;
import git4idea.history.wholeTree.AbstractHash;
import org.jetbrains.annotations.NotNull;
@@ -212,4 +214,13 @@ public class GitCommit {
public void setOnTracked(boolean onTracked) {
myOnTracked = onTracked;
}
public List<AbstractHash> getConvertedParents() {
return ObjectsConvertor.convert(getParentsHashes(), new Convertor<String, AbstractHash>() {
@Override
public AbstractHash convert(String o) {
return AbstractHash.create(o);
}
});
}
}
@@ -54,9 +54,7 @@ public class LowLevelAccessImpl implements LowLevelAccess {
final AsynchConsumer<CommitHashPlusParents> consumer,
Getter<Boolean> isCanceled, int useMaxCnt) throws VcsException {
final List<String> parameters = new ArrayList<String>();
for (ChangesFilter.Filter filter : filters) {
filter.getCommandParametersFilter().applyToCommandLine(parameters);
}
ChangesFilter.filtersToParameters(filters, parameters);
if (! startingPoints.isEmpty()) {
for (String startingPoint : startingPoints) {
@@ -110,6 +108,13 @@ public class LowLevelAccessImpl implements LowLevelAccess {
refs.setTrackedRemote(current.getTrackedRemoteName(myProject, myRoot));
}
refs.setUsername(GitConfigUtil.getValue(myProject, myRoot, GitConfigUtil.USER_NAME));
// todo
/*GitStashUtils.loadStashStack(myProject, myRoot, new Consumer<StashInfo>() {
@Override
public void consume(StashInfo stashInfo) {
}
});*/
return refs;
}
@@ -125,10 +130,8 @@ public class LowLevelAccessImpl implements LowLevelAccess {
parameters.add("--max-count=" + useMaxCnt);
}
for (ChangesFilter.Filter filter : filters) {
filter.getCommandParametersFilter().applyToCommandLine(parameters);
}
ChangesFilter.filtersToParameters(filters, parameters);
if (! startingPoints.isEmpty()) {
for (String startingPoint : startingPoints) {
parameters.add(startingPoint);
@@ -0,0 +1,207 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.wholeTree;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.Consumer;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import git4idea.changes.GitChangeUtils;
import git4idea.history.GitHistoryUtils;
import git4idea.history.browser.*;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author irengrig
* Date: 2/1/11
* Time: 6:30 PM
*
* We wouldn't include it into growth controller (not many rows loaded)
*/
public class ByRootLoader extends TaskDescriptor {
private final Project myProject;
private final LoaderAndRefresherImpl.MyRootHolder myRootHolder;
private final LowLevelAccess myLowLevelAccess;
private final Mediator myMediator;
private final DetailsCache myDetailsCache;
private SymbolicRefs mySymbolicRefs;
private final Mediator.Ticket myTicket;
private final UsersIndex myUsersIndex;
private final Collection<String> myStartingPoints;
@NotNull
private final GitLogFilters myGitLogFilters;
public ByRootLoader(Project project,
LoaderAndRefresherImpl.MyRootHolder rootHolder,
Mediator mediator,
DetailsCache detailsCache,
Mediator.Ticket ticket, UsersIndex usersIndex, GitLogFilters gitLogFilters, final Collection<String> startingPoints) {
super("Initial checks", Where.POOLED);
myProject = project;
myRootHolder = rootHolder;
myUsersIndex = usersIndex;
myStartingPoints = startingPoints;
myLowLevelAccess = new LowLevelAccessImpl(myProject, myRootHolder.getRoot());
myMediator = mediator;
myDetailsCache = detailsCache;
myTicket = ticket;
myGitLogFilters = gitLogFilters;
}
@Override
public void run(ContinuationContext context) {
final ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator();
progress(pi, "Load branches and tags");
initSymbRefs();
progress(pi, "Load stashed");
loadStash();
progress(pi, "Try to load by reference");
loadByHashesAside(context);
}
private void progress(final ProgressIndicator pi, final String progress) {
if (pi != null) {
pi.checkCanceled();
pi.setText(progress);
}
}
private void loadStash() {
// start is not on a branch
if (myStartingPoints != null && (! myStartingPoints.isEmpty())) return;
final List<GitCommit> details = new ArrayList<GitCommit>();
final List<CommitI> commits = new ArrayList<CommitI>();
final Map<AbstractHash, String> stashMap = new HashMap<AbstractHash, String>();
final List<List<AbstractHash>> parents = myGitLogFilters.isEmpty() ? new ArrayList<List<AbstractHash>>() : null;
myGitLogFilters.callConsumer(new Consumer<List<ChangesFilter.Filter>>() {
@Override
public void consume(List<ChangesFilter.Filter> filters) {
try {
final List<Pair<String,GitCommit>> stash = GitHistoryUtils.loadStashStackAsCommits(myProject, myRootHolder.getRoot(),
mySymbolicRefs, ChangesFilter.filtersToParameterArray(filters));
if (stash == null) return;
for (Pair<String, GitCommit> pair : stash) {
final GitCommit gitCommit = pair.getSecond();
if (stashMap.containsKey(gitCommit.getShortHash())) continue;
details.add(gitCommit);
if (parents != null) {
parents.add(gitCommit.getConvertedParents());
}
commits.add(createCommitI(gitCommit));
stashMap.put(gitCommit.getShortHash(), pair.getFirst());
}
}
catch (VcsException e) {
myMediator.acceptException(e);
}
}
}, true);
myDetailsCache.putStash(myRootHolder.getRoot(), stashMap);
// does not work
//myDetailsCache.acceptAnswer(details, myRootHolder.getRoot());
myMediator.appendResult(myTicket, commits, parents);
}
// if there're filters -> parents shouldn't be loaded
public void loadByHashesAside(final ContinuationContext context) {
final List<CommitI> result = new ArrayList<CommitI>();
final Set<SHAHash> controlSet = new HashSet<SHAHash>();
final List<String> hashes = myGitLogFilters.getPossibleReferencies();
if (hashes == null) return;
myGitLogFilters.callConsumer(new Consumer<List<ChangesFilter.Filter>>() {
@Override
public void consume(List<ChangesFilter.Filter> filters) {
for (String hash : hashes) {
try {
final SHAHash shaHash = GitChangeUtils.commitExists(myProject, myRootHolder.getRoot(), hash, ChangesFilter.filtersToParameterArray(filters));
if (shaHash == null) continue;
if (controlSet.contains(shaHash)) continue;
controlSet.add(shaHash);
if (myStartingPoints != null && (! myStartingPoints.isEmpty())) {
boolean matches = false;
for (String startingPoint : myStartingPoints) {
if(GitChangeUtils.isAnyLevelChild(myProject, myRootHolder.getRoot(), shaHash, startingPoint)) {
matches = true;
break;
}
}
if (! matches) continue;
}
final List<GitCommit> commits = myLowLevelAccess.getCommitDetails(Collections.singletonList(shaHash.getValue()), mySymbolicRefs);
if (commits.isEmpty()) continue;
myDetailsCache.acceptAnswer(commits, myRootHolder.getRoot());
appendCommits(result, commits);
}
catch (VcsException e1) {
continue;
}
}
}
}, false);
if (! result.isEmpty()) {
final StepType stepType = myMediator.appendResult(myTicket, result, null);
// here we react only on "stop", not on "pause"
if (StepType.STOP.equals(stepType)) {
context.cancelEverything();
}
}
}
private void appendCommits(List<CommitI> result, List<GitCommit> commits) {
for (GitCommit commit : commits) {
CommitI commitObj = createCommitI(commit);
result.add(commitObj);
}
}
private CommitI createCommitI(GitCommit commit) {
CommitI commitObj =
new Commit(commit.getShortHash().getString(), commit.getDate().getTime(), myUsersIndex.put(commit.getAuthor()));
commitObj = myRootHolder.decorateByRoot(commitObj);
return commitObj;
}
private void initSymbRefs() {
if (mySymbolicRefs == null) {
try {
mySymbolicRefs = myLowLevelAccess.getRefs();
myMediator.reportSymbolicRefs(myTicket, myRootHolder.getRoot(), mySymbolicRefs);
}
catch (VcsException e) {
myMediator.acceptException(e);
}
}
}
public SymbolicRefs getSymbolicRefs() {
return mySymbolicRefs;
}
}
@@ -20,26 +20,30 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.SLRUMap;
import git4idea.history.browser.GitCommit;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author irengrig
*/
public class DetailsCache {
private final static int ourSize = 400;
private boolean mySomethingIsMissing;
private final SLRUMap<Pair<VirtualFile, AbstractHash>, GitCommit> myCache;
private final SLRUMap<Pair<VirtualFile, AbstractHash>, List<String>> myBranches;
private final DetailsLoaderImpl myDetailsLoader;
private final ModalityState myModalityState;
private AbstractCalledLater myRefresh;
private final Map<VirtualFile, Map<AbstractHash, String>> myStash;
private final Object myLock;
public DetailsCache(final Project project, final UIRefresh uiRefresh, final DetailsLoaderImpl detailsLoader, final ModalityState modalityState) {
myDetailsLoader = detailsLoader;
myModalityState = modalityState;
myStash = new HashMap<VirtualFile, Map<AbstractHash,String>>();
myRefresh = new AbstractCalledLater(project, myModalityState) {
@Override
public void run() {
@@ -47,7 +51,6 @@ public class DetailsCache {
}
};
myLock = new Object();
mySomethingIsMissing = false;
myCache = new SLRUMap<Pair<VirtualFile, AbstractHash>, GitCommit>(ourSize, 50);
myBranches = new SLRUMap<Pair<VirtualFile, AbstractHash>, List<String>>(10, 10);
}
@@ -60,9 +63,6 @@ public class DetailsCache {
public void acceptQuestion(final MultiMap<VirtualFile,AbstractHash> hashes) {
if (hashes.isEmpty()) return;
synchronized (myLock) {
mySomethingIsMissing = ! hashes.isEmpty();
}
myDetailsLoader.load(hashes);
}
@@ -71,9 +71,6 @@ public class DetailsCache {
for (GitCommit commit : commits) {
myCache.put(new Pair<VirtualFile, AbstractHash>(root, commit.getShortHash()), commit);
}
// if (mySomethingIsMissing) {
mySomethingIsMissing = false;
// }
}
myRefresh.callMe();
}
@@ -94,9 +91,25 @@ public class DetailsCache {
}
}
public void resetBranchesCache() {
public void resetAsideCaches() {
synchronized (myLock) {
myBranches.clear();
myStash.clear();
myCache.clear();
}
}
public void putStash(final VirtualFile root, final Map<AbstractHash, String> stash) {
synchronized (myLock) {
myStash.put(root, stash);
}
}
@Nullable
public String getStashName(final VirtualFile root, final AbstractHash hash) {
synchronized (myLock) {
final Map<AbstractHash, String> map = myStash.get(root);
return map == null ? null : map.get(hash);
}
}
}
@@ -0,0 +1,101 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.history.wholeTree;
import com.google.common.collect.Sets;
import com.intellij.util.Consumer;
import git4idea.history.browser.ChangesFilter;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author irengrig
* Date: 2/1/11
* Time: 7:27 PM
*/
public class GitLogFilters {
@Nullable
private final ChangesFilter.Comment myCommentFilter;
@Nullable
private final Set<ChangesFilter.Filter> myCommitterFilters;
@Nullable
private final Set<ChangesFilter.Filter> myStructureFilters;
@Nullable
private final List<String> myPossibleReferencies;
public GitLogFilters() {
this(null, null, null, null);
}
public GitLogFilters(@Nullable ChangesFilter.Comment commentFilter,
@Nullable Set<ChangesFilter.Filter> committerFilters,
@Nullable Set<ChangesFilter.Filter> structureFilters, @Nullable List<String> possibleReferencies) {
myCommentFilter = commentFilter;
myCommitterFilters = committerFilters;
myStructureFilters = structureFilters;
myPossibleReferencies = possibleReferencies;
}
public void callConsumer(final Consumer<List<ChangesFilter.Filter>> consumer, boolean takeComment) {
final List<Set<ChangesFilter.Filter>> filters = new ArrayList<Set<ChangesFilter.Filter>>();
if (takeComment && myCommentFilter != null) {
filters.add(Collections.<ChangesFilter.Filter, ChangesFilter.Filter>singletonMap(myCommentFilter, myCommentFilter).keySet());
}
if (myCommitterFilters != null) {
filters.add(myCommitterFilters);
}
if (myStructureFilters != null) {
filters.add(myStructureFilters);
}
final Set<List<ChangesFilter.Filter>> cartesian = Sets.cartesianProduct(filters);
if (cartesian.isEmpty()) {
consumer.consume(Collections.<ChangesFilter.Filter>emptyList());
} else {
for (List<ChangesFilter.Filter> list : cartesian) {
consumer.consume(list);
}
}
}
@Nullable
public ChangesFilter.Comment getCommentFilter() {
return myCommentFilter;
}
@Nullable
public Set<ChangesFilter.Filter> getCommitterFilters() {
return myCommitterFilters;
}
@Nullable
public Set<ChangesFilter.Filter> getStructureFilters() {
return myStructureFilters;
}
public boolean isEmpty() {
return myCommentFilter == null && (myCommitterFilters == null || myCommitterFilters.isEmpty()) &&
(myStructureFilters == null || myStructureFilters.isEmpty());
}
@Nullable
public List<String> getPossibleReferencies() {
return myPossibleReferencies;
}
}
@@ -365,7 +365,7 @@ public class GitLogUI implements Disposable {
if (gitCommit == null) return;
final List<String> branches = myDetailsCache.getBranches(root, commit.getHash());
if (branches != null) {
myDetails.putBranches(gitCommit, branches);
myDetails.putBranches(root, gitCommit, branches);
}
final Application application = ApplicationManager.getApplication();
application.executeOnPooledThread(new Runnable() {
@@ -383,7 +383,7 @@ public class GitLogUI implements Disposable {
if (myDetails.isMissingBranchesInfo() && afterRows.length == 1 && afterRows[0] == rows[0]) {
final CommitI afterCommit = myTableModel.getCommitAt(rows[0]);
if (afterCommit.holdsDecoration() || (! afterCommit.equals(commit))) return;
myDetails.putBranches(gitCommit, branches);
myDetails.putBranches(root, gitCommit, branches);
}
}
}, ModalityState.NON_MODAL, myProject.getDisposed());
@@ -888,22 +888,27 @@ public class GitLogUI implements Disposable {
}
private Color getLogicBackground(final boolean isSelected, final int row) {
final Color bkgColor;
Color bkgColor;
final CommitI commitAt = myTableModel.getCommitAt(row);
GitCommit gitCommit = null;
VirtualFile root = null;
if (commitAt != null & (! commitAt.holdsDecoration())) {
gitCommit = myDetailsCache.convert(commitAt.selectRepository(myRootsUnderVcs), commitAt.getHash());
root = commitAt.selectRepository(myRootsUnderVcs);
gitCommit = myDetailsCache.convert(root, commitAt.getHash());
}
if (isSelected) {
bkgColor = UIUtil.getTableSelectionBackground();
} else {
if (gitCommit != null && gitCommit.isOnLocal() && gitCommit.isOnTracked()) {
bkgColor = Colors.commonThisBranch;
} else if (gitCommit != null && gitCommit.isOnLocal()) {
bkgColor = Colors.ownThisBranch;
} else {
bkgColor = UIUtil.getTableBackground();
bkgColor = UIUtil.getTableBackground();
if (gitCommit != null) {
if (myDetailsCache.getStashName(root, gitCommit.getShortHash()) != null) {
bkgColor = Colors.stashed;
} else if (gitCommit.isOnLocal() && gitCommit.isOnTracked()) {
bkgColor = Colors.commonThisBranch;
} else if (gitCommit.isOnLocal()) {
bkgColor = Colors.ownThisBranch;
}
}
}
return bkgColor;
@@ -986,7 +991,7 @@ public class GitLogUI implements Disposable {
private void reloadRequest() {
myState = StepType.CONTINUE;
final int was = myTableModel.getRowCount();
myDetailsCache.resetBranchesCache();
myDetailsCache.resetAsideCaches();
final Collection<String> startingPoints = mySelectedBranch == null ? Collections.<String>emptyList() : Collections.singletonList(mySelectedBranch);
myDescriptionRenderer.resetIcons();
final boolean commentFilterEmpty = StringUtil.isEmptyOrSpaces(myPreviousFilter);
@@ -995,18 +1000,18 @@ public class GitLogUI implements Disposable {
if (commentFilterEmpty && (myUserFilterI.myFilter == null)) {
myUsersSearchContext.clear();
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, Collections.<Collection<ChangesFilter.Filter>>emptyList(), null);
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, new GitLogFilters());
} else {
final List<Collection<ChangesFilter.Filter>> filters = new ArrayList<Collection<ChangesFilter.Filter>>();
ChangesFilter.Comment comment = null;
if (! commentFilterEmpty) {
final Pair<String, List<String>> preparse = preparse(myPreviousFilter);
final String first = preparse.getFirst();
filters.add(Collections.<ChangesFilter.Filter>singletonList(new ChangesFilter.Comment(first)));
comment = new ChangesFilter.Comment(first);
}
Set<ChangesFilter.Filter> userFilters = null;
if (myUserFilterI.myFilter != null) {
final String[] strings = myUserFilterI.myFilter.split(",");
final List<ChangesFilter.Filter> userFilters = new ArrayList<ChangesFilter.Filter>();
userFilters = new HashSet<ChangesFilter.Filter>();
for (String string : strings) {
string = string.trim();
if (string.length() == 0) continue;
@@ -1014,10 +1019,11 @@ public class GitLogUI implements Disposable {
final String regexp = StringUtil.escapeToRegexp(string);
userFilters.add(new ChangesFilter.Committer(regexp));
}
filters.add(userFilters);
}
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, filters, commentFilterEmpty ? null : myPreviousFilter.split("[\\s]"));
final List<String> possibleReferencies = commentFilterEmpty ? null : Arrays.asList(myPreviousFilter.split("[\\s]"));
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, new GitLogFilters(comment, userFilters, null,
possibleReferencies));
}
updateMoreVisibility();
selectionChanged();
@@ -1031,6 +1037,7 @@ public class GitLogUI implements Disposable {
Color local = new Color(117,238,199);
Color ownThisBranch = new Color(198,255,226);
Color commonThisBranch = new Color(223,223,255);
Color stashed = new Color(225,225,225);
}
private class MySpecificDetails {
@@ -1064,9 +1071,9 @@ public class GitLogUI implements Disposable {
return scrollPane;
}
public void putBranches(final GitCommit commit, final List<String> branches) {
public void putBranches(VirtualFile root, final GitCommit commit, final List<String> branches) {
myMissingBranchesInfo = branches == null;
myJEditorPane.setText(parseDetails(commit, branches));
myJEditorPane.setText(parseDetails(root, commit, branches));
}
public boolean isMissingBranchesInfo() {
@@ -1097,11 +1104,11 @@ public class GitLogUI implements Disposable {
s.equals(currentBranch))));
}
myMarksPanel.repaint();
myJEditorPane.setText(parseDetails(commit, branches));
myJEditorPane.setText(parseDetails(root, commit, branches));
}
}
private String parseDetails(final GitCommit c, final List<String> branches) {
private String parseDetails(VirtualFile root, final GitCommit c, final List<String> branches) {
final String hash = new HtmlHighlighter(c.getHash().getValue()).getResult();
final String author = new HtmlHighlighter(c.getAuthor()).getResult();
final String committer = new HtmlHighlighter(c.getCommitter()).getResult();
@@ -1114,14 +1121,19 @@ public class GitLogUI implements Disposable {
});
final StringBuilder sb = new StringBuilder().append("<html><head>").append(UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()))
.append("</head><body><table><tr valign=\"top\"><td><i>Hash:</i></td><td>").append(
hash).append("</td></tr>" + "<tr valign=\"top\"><td><i>Author:</i></td><td>")
.append("</head><body><table>");
final String stashName = myDetailsCache.getStashName(root, c.getShortHash());
if (! StringUtil.isEmptyOrSpaces(stashName)) {
sb.append("<tr valign=\"top\"><td><b>").append(stashName).append("</b></td><td></td></tr>");
}
sb.append("<tr valign=\"top\"><td><i>Hash:</i></td><td>").append(
hash).append("</td></tr>" + "<tr valign=\"top\"><td><i>Author:</i></td><td>")
.append(author).append(" (").append(c.getAuthorEmail()).append(") <i>at</i> ")
.append(DateFormatUtil.formatPrettyDateTime(c.getAuthorTime()))
.append("</td></tr>" + "<tr valign=\"top\"><td><i>Commiter:</i></td><td>")
.append(committer).append(" (").append(c.getComitterEmail()).append(") <i>at</i> ")
.append(DateFormatUtil.formatPrettyDateTime(c.getDate())).append(
"</td></tr>" + "<tr valign=\"top\"><td><i>Description:</i></td><td><b>")
"</td></tr>" + "<tr valign=\"top\"><td><i>Description:</i></td><td><b>")
.append(comment).append("</b></td></tr>");
sb.append("<tr valign=\"top\"><td><i>Contained in branches:<i></td><td>");
if (branches != null && (! branches.isEmpty())) {
@@ -1232,8 +1244,26 @@ public class GitLogUI implements Disposable {
@Override
public void update(AnActionEvent e) {
super.update(e);
final boolean enabled = getSelectedCommitsAndCheck() != null;
e.getPresentation().setEnabled(enabled);
e.getPresentation().setEnabled(enabled());
}
private boolean enabled() {
final MultiMap<VirtualFile, GitCommit> commitsAndCheck = getSelectedCommitsAndCheck();
if (commitsAndCheck == null) return false;
for (VirtualFile root : commitsAndCheck.keySet()) {
final SymbolicRefs refs = myRefs.get(root);
final String currentBranch = refs == null ? null : (refs.getCurrent() == null ? null : refs.getCurrent().getName());
if (currentBranch == null) continue;
final Collection<GitCommit> commits = commitsAndCheck.get(root);
for (GitCommit commit : commits) {
if (commit.getParentsHashes().size() > 1) return false;
final List<String> branches = myDetailsCache.getBranches(root, commit.getShortHash());
if (branches != null && branches.contains(currentBranch)) {
return false;
}
}
}
return true;
}
}
@@ -31,13 +31,13 @@ public class LoadAlgorithm {
private final Project myProject;
private final List<LoaderAndRefresher<CommitHashPlusParents>> myLoaders;
private final List<String> myAbstractHashs;
private final List<ByRootLoader> myShortLoaders;
private final Continuation myContinuation;
public LoadAlgorithm(final Project project, final List<LoaderAndRefresher<CommitHashPlusParents>> loaders, final List<String> abstractHashs) {
public LoadAlgorithm(final Project project, final List<LoaderAndRefresher<CommitHashPlusParents>> loaders, final List<ByRootLoader> shortLoaders) {
myProject = project;
myLoaders = loaders;
myAbstractHashs = abstractHashs;
myShortLoaders = shortLoaders;
myContinuation = new Continuation(myProject, false);
}
@@ -45,14 +45,14 @@ public class LoadAlgorithm {
final ContinuationContext.GatheringContinuationContext initContext =
new ContinuationContext.GatheringContinuationContext();
if (myAbstractHashs != null) {
initContext.last(new TryHashes());
}
for (LoaderAndRefresher<CommitHashPlusParents> loader : myLoaders) {
final LoaderFactory factory = new LoaderFactory(loader);
final State state = new State(factory);
state.scheduleSelf(initContext);
}
for (ByRootLoader shortLoader : myShortLoaders) {
initContext.next(shortLoader);
}
myContinuation.run(initContext.getList());
}
@@ -67,19 +67,6 @@ public class LoadAlgorithm {
myContinuation.resume();
}
private class TryHashes extends TaskDescriptor {
private TryHashes() {
super("Try load by hashes", Where.POOLED);
}
@Override
public void run(ContinuationContext context) {
for (LoaderAndRefresher loader : myLoaders) {
loader.loadByHashesAside(myAbstractHashs);
}
}
}
private static class LoadTaskDescriptor extends TaskDescriptor {
protected final State myState;
private final LoaderAndRefresher<CommitHashPlusParents> myLoader;
@@ -16,10 +16,13 @@ import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import git4idea.history.NewGitUsersComponent;
import git4idea.history.browser.ChangesFilter;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author irengrig
@@ -47,13 +50,13 @@ public class LoadController implements Loader {
public void loadSkeleton(final Mediator.Ticket ticket,
final RootsHolder rootsHolder,
final Collection<String> startingPoints,
final Collection<Collection<ChangesFilter.Filter>> filters,
String[] possibleHashes,
final GitLogFilters filters,
final LoadGrowthController loadGrowthController) {
if (myPreviousAlgorithm != null) {
myPreviousAlgorithm.stop();
}
final List<LoaderAndRefresher<CommitHashPlusParents>> list = new ArrayList<LoaderAndRefresher<CommitHashPlusParents>>();
final List<ByRootLoader> shortLoaders = new ArrayList<ByRootLoader>();
final List<VirtualFile> roots = rootsHolder.getRoots();
int i = 0;
for (VirtualFile root : roots) {
@@ -61,49 +64,23 @@ public class LoadController implements Loader {
new LoaderAndRefresherImpl.OneRootHolder(root) :
new LoaderAndRefresherImpl.ManyCaseHolder(i, rootsHolder);
if (filters.isEmpty()) {
final LoaderAndRefresherImpl loaderAndRefresher =
new LoaderAndRefresherImpl(ticket, Collections.<ChangesFilter.Filter>emptyList(), myMediator, startingPoints, myDetailsCache,
myProject, rootHolder, myUsersIndex, loadGrowthController.getId());
list.add(loaderAndRefresher);
} else {
Collection<Collection<ChangesFilter.Filter>> reordered = new ArrayList<Collection<ChangesFilter.Filter>>();
final Iterator<Collection<ChangesFilter.Filter>> iterator = filters.iterator();
if (iterator.hasNext()) {
final Collection<ChangesFilter.Filter> first = iterator.next();
for (ChangesFilter.Filter filter : first) {
final ArrayList<ChangesFilter.Filter> newList = new ArrayList<ChangesFilter.Filter>();
newList.add(filter);
reordered.add(newList);
}
}
while (iterator.hasNext()) {
final Collection<ChangesFilter.Filter> next = iterator.next();
final Collection<Collection<ChangesFilter.Filter>> reorderedCopy = reordered;
reordered = new ArrayList<Collection<ChangesFilter.Filter>>();
for (ChangesFilter.Filter filter : next) {
for (Collection<ChangesFilter.Filter> filterCollection : reorderedCopy) {
final ArrayList<ChangesFilter.Filter> newList = new ArrayList<ChangesFilter.Filter>(filterCollection);
newList.add(filter);
reordered.add(newList);
}
}
}
for (Collection<ChangesFilter.Filter> filterCollection : reordered) {
filters.callConsumer(new Consumer<List<ChangesFilter.Filter>>() {
@Override
public void consume(final List<ChangesFilter.Filter> filters) {
final LoaderAndRefresherImpl loaderAndRefresher =
new LoaderAndRefresherImpl(ticket, filterCollection, myMediator, startingPoints, myDetailsCache, myProject, rootHolder, myUsersIndex,
new LoaderAndRefresherImpl(ticket, filters, myMediator, startingPoints, myDetailsCache, myProject, rootHolder, myUsersIndex,
loadGrowthController.getId());
list.add(loaderAndRefresher);
}
}
}, true);
shortLoaders.add(new ByRootLoader(myProject, rootHolder, myMediator, myDetailsCache, ticket, myUsersIndex, filters, startingPoints));
++ i;
}
myUsersComponent.acceptUpdate(myUsersIndex.getKeys());
//final List<String> abstractHashs = possibleHashes == null ? null : filterNumbers(possibleHashes);
myPreviousAlgorithm = new LoadAlgorithm(myProject, list, possibleHashes == null ? null : Arrays.asList(possibleHashes));
myPreviousAlgorithm = new LoadAlgorithm(myProject, list, shortLoaders);
myPreviousAlgorithm.execute();
}
@@ -15,8 +15,6 @@
*/
package git4idea.history.wholeTree;
import git4idea.history.browser.ChangesFilter;
import java.util.Collection;
/**
@@ -26,7 +24,7 @@ public interface Loader {
void loadSkeleton(Mediator.Ticket ticket,
RootsHolder rootsHolder,
final Collection<String> startingPoints,
final Collection<Collection<ChangesFilter.Filter>> filters, String[] possibleHashes, LoadGrowthController loadGrowthController);
final GitLogFilters filters, LoadGrowthController loadGrowthController);
void resume();
}
@@ -84,7 +84,7 @@ public class LoaderAndRefresherImpl implements LoaderAndRefresher<CommitHashPlus
myProgressAnalog = new Getter<Boolean>() {
@Override
public Boolean get() {
return StepType.STOP.equals(myStepType);
return isInterrupted();
}
};
myLowLevelAccess = new LowLevelAccessImpl(myProject, myRootHolder.getRoot());
@@ -105,7 +105,7 @@ public class LoaderAndRefresherImpl implements LoaderAndRefresher<CommitHashPlus
}
}
StepType stepType = myMediator.appendResult(myTicket, buffer, parents, id);
StepType stepType = myMediator.appendResult(myTicket, buffer, parents);
if (! StepType.FINISHED.equals(myStepType)) {
myStepType = stepType;
}
@@ -267,7 +267,7 @@ public class LoaderAndRefresherImpl implements LoaderAndRefresher<CommitHashPlus
}
}
if (! result.isEmpty()) {
final StepType stepType = myMediator.appendResult(myTicket, result, parents, myId);
final StepType stepType = myMediator.appendResult(myTicket, result, parents);
// here we react only on "stop", not on "pause"
if (StepType.STOP.equals(stepType)) {
myStepType = StepType.STOP;
@@ -14,7 +14,6 @@ package git4idea.history.wholeTree;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.history.browser.ChangesFilter;
import git4idea.history.browser.SymbolicRefs;
import org.jetbrains.annotations.Nullable;
@@ -27,16 +26,14 @@ import java.util.List;
public interface Mediator {
void reload(RootsHolder rootsHolder,
final Collection<String> startingPoints,
final Collection<Collection<ChangesFilter.Filter>> filters,
@Nullable String[] possibleHashes);
@Nullable final GitLogFilters filters);
/**
* @return false -> ticket already changed
*/
StepType appendResult(final Ticket ticket,
final List<CommitI> result,
@Nullable final List<List<AbstractHash>> parents,
LoadGrowthController.ID id);
@Nullable final List<List<AbstractHash>> parents);
void reportSymbolicRefs(final Ticket ticket, VirtualFile root, final SymbolicRefs symbolicRefs);
@@ -19,7 +19,6 @@ import com.intellij.openapi.vcs.CalledInBackground;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.history.browser.ChangesFilter;
import git4idea.history.browser.SymbolicRefs;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -48,13 +47,12 @@ public class MediatorImpl implements Mediator {
@CalledInBackground
@Override
public StepType appendResult(final Ticket ticket, final List<CommitI> result,
final @Nullable List<List<AbstractHash>> parents, LoadGrowthController.ID id) {
public StepType appendResult(final Ticket ticket, final List<CommitI> result, final @Nullable List<List<AbstractHash>> parents) {
if (! myTicket.equals(ticket)) {
return StepType.STOP;
}
myTableWrapper.appendResult(ticket, id, result, parents);
myTableWrapper.appendResult(ticket, result, parents);
if (myTableWrapper.isSuspend()) {
return StepType.PAUSE;
}
@@ -102,12 +100,11 @@ public class MediatorImpl implements Mediator {
@Override
public void reload(final RootsHolder rootsHolder,
final Collection<String> startingPoints,
final Collection<Collection<ChangesFilter.Filter>> filters,
String[] possibleHashes) {
final GitLogFilters filters) {
myTicket.increment();
myTableWrapper.reset();
myController.reset();
myLoader.loadSkeleton(myTicket.copy(), rootsHolder, startingPoints, filters, possibleHashes, myController);
myLoader.loadSkeleton(myTicket.copy(), rootsHolder, startingPoints, filters, myController);
}
public void setLoader(Loader loader) {
@@ -152,14 +149,13 @@ public class MediatorImpl implements Mediator {
}
@CalledInBackground
public void appendResult(final Ticket ticket, final LoadGrowthController.ID id, final List<CommitI> result,
public void appendResult(final Ticket ticket, final List<CommitI> result,
final @Nullable List<List<AbstractHash>> parents) {
new AbstractCalledLater(myProject, myState) {
@Override
public void run() {
if (! myTicket.equals(ticket)) return;
myTableModel.appendData(result, parents);
//myController.registerTime(id, result.get(result.size() - 1).getTime());
if (myController.isEmpty()) {
myTableModel.restore();
mySuspend = false;
@@ -173,7 +169,6 @@ public class MediatorImpl implements Mediator {
}
}
}
//myUIRefresh.linesReloaded(myTableModel.isCut());
myUIRefresh.linesReloaded(mySuspend);
if (myController.isEmpty()) {
myUIRefresh.finished();
@@ -22,23 +22,18 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.DocumentAdapter;
import com.intellij.util.Consumer;
import git4idea.GitBranch;
import git4idea.GitRevisionNumber;
import git4idea.GitVcs;
import git4idea.actions.GitShowAllSubmittedFilesAction;
import git4idea.commands.GitCommand;
import git4idea.commands.GitHandlerUtil;
import git4idea.commands.GitLineHandler;
import git4idea.commands.GitLineHandlerAdapter;
import git4idea.commands.GitSimpleHandler;
import git4idea.commands.StringScanner;
import git4idea.config.GitConfigUtil;
import git4idea.commands.*;
import git4idea.config.GitVersionSpecialty;
import git4idea.i18n.GitBundle;
import git4idea.update.GitStashUtils;
import git4idea.validators.GitBranchNameValidator;
import org.jetbrains.annotations.NotNull;
@@ -48,7 +43,6 @@ import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -164,12 +158,12 @@ public class GitUnstashDialog extends DialogWrapper {
public void actionPerformed(final ActionEvent e) {
final StashInfo stash = getSelectedStash();
if (Messages.YES == Messages.showYesNoDialog(GitUnstashDialog.this.getContentPane(),
GitBundle.message("git.unstash.drop.confirmation.message", stash.myStash, stash.myMessage),
GitBundle.message("git.unstash.drop.confirmation.title", stash.myStash), Messages.getQuestionIcon())) {
ProgressManager.getInstance().run(new Task.Modal(myProject, "Removing stash " + stash.myStash, false) {
GitBundle.message("git.unstash.drop.confirmation.message", stash.getStash(), stash.getMessage()),
GitBundle.message("git.unstash.drop.confirmation.title", stash.getStash()), Messages.getQuestionIcon())) {
ProgressManager.getInstance().run(new Task.Modal(myProject, "Removing stash " + stash.getStash(), false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
GitSimpleHandler h = dropHandler(stash.myStash);
GitSimpleHandler h = dropHandler(stash.getStash());
try {
h.run();
h.unsilence();
@@ -178,7 +172,7 @@ public class GitUnstashDialog extends DialogWrapper {
try {
//noinspection HardCodedStringLiteral
if (ex.getMessage().startsWith("fatal: Needed a single revision")) {
h = dropHandler(translateStash(stash.myStash));
h = dropHandler(translateStash(stash.getStash()));
h.run();
}
else {
@@ -209,7 +203,7 @@ public class GitUnstashDialog extends DialogWrapper {
public void actionPerformed(final ActionEvent e) {
final VirtualFile root = getGitRoot();
String resolvedStash;
String selectedStash = getSelectedStash().myStash;
String selectedStash = getSelectedStash().getStash();
try {
resolvedStash = GitRevisionNumber.resolve(myProject, root, selectedStash).asString();
}
@@ -312,22 +306,12 @@ public class GitUnstashDialog extends DialogWrapper {
private void refreshStashList() {
final DefaultListModel listModel = (DefaultListModel)myStashList.getModel();
listModel.clear();
GitSimpleHandler h = new GitSimpleHandler(myProject, getGitRoot(), GitCommand.STASH);
h.setSilent(true);
h.setNoSSH(true);
h.addParameters("list");
String out;
try {
h.setCharset(Charset.forName(GitConfigUtil.getLogEncoding(myProject, getGitRoot())));
out = h.run();
}
catch (VcsException e) {
GitUIUtil.showOperationError(myProject, e, h.printableCommandLine());
return;
}
for (StringScanner s = new StringScanner(out); s.hasMoreData();) {
listModel.addElement(new StashInfo(s.boundedToken(':'), s.boundedToken(':'), s.line().trim()));
}
GitStashUtils.loadStashStack(myProject, getGitRoot(), new Consumer<StashInfo>() {
@Override
public void consume(StashInfo stashInfo) {
listModel.addElement(stashInfo);
}
});
myBranches.clear();
try {
GitBranch.listAsStrings(myProject, getGitRoot(), false, true, myBranches, null);
@@ -361,7 +345,7 @@ public class GitUnstashDialog extends DialogWrapper {
else {
h.addParameters("branch", branch);
}
String selectedStash = getSelectedStash().myStash;
String selectedStash = getSelectedStash().getStash();
if (escaped) {
selectedStash = translateStash(selectedStash);
} else if (GitVersionSpecialty.NEEDS_QUOTES_IN_STASH_NAME.existsIn(myVcs.getVersion())) { // else if, because escaping {} also solves the issue
@@ -438,27 +422,4 @@ public class GitUnstashDialog extends DialogWrapper {
GitUIUtil.showOperationErrors(project, h.errors(), h.printableCommandLine());
}
}
/**
* Information about one stash.
*/
private static class StashInfo {
private final String myStash; // stash codename (stash@{1})
private final String myBranch;
private final String myMessage;
private final String myText; // The formatted text representation
public StashInfo(final String stash, final String branch, final String message) {
myStash = stash;
myBranch = branch;
myMessage = message;
myText =
GitBundle.message("unstash.stashes.item", StringUtil.escapeXml(stash), StringUtil.escapeXml(branch), StringUtil.escapeXml(message));
}
@Override
public String toString() {
return myText;
}
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.ui;
import com.intellij.openapi.util.text.StringUtil;
import git4idea.i18n.GitBundle;
/**
* Information about one stash.
*/
public class StashInfo {
private final String myStash; // stash codename (stash@{1})
private final String myBranch;
private final String myMessage;
private final String myText; // The formatted text representation
public StashInfo(final String stash, final String branch, final String message) {
myStash = stash;
myBranch = branch;
myMessage = message;
myText =
GitBundle.message("unstash.stashes.item", StringUtil.escapeXml(stash), StringUtil.escapeXml(branch), StringUtil.escapeXml(message));
}
@Override
public String toString() {
return myText;
}
public String getStash() {
return myStash;
}
public String getBranch() {
return myBranch;
}
public String getMessage() {
return myMessage;
}
public String getText() {
return myText;
}
}
@@ -28,6 +28,7 @@ import com.intellij.openapi.vcs.changes.shelf.ShelvedChange;
import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.VcsUtil;
import git4idea.GitUtil;
@@ -35,7 +36,11 @@ import git4idea.GitVcs;
import git4idea.commands.GitCommand;
import git4idea.commands.GitFileUtils;
import git4idea.commands.GitSimpleHandler;
import git4idea.commands.StringScanner;
import git4idea.config.GitConfigUtil;
import git4idea.config.GitVersion;
import git4idea.ui.GitUIUtil;
import git4idea.ui.StashInfo;
import git4idea.vfs.GitVFSListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,6 +48,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.event.ChangeEvent;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
/**
@@ -74,6 +80,30 @@ public class GitStashUtils {
return !output.startsWith("No local changes to save");
}
public static void loadStashStack(@NotNull Project project, @NotNull VirtualFile root, Consumer<StashInfo> consumer) {
loadStashStack(project, root, Charset.forName(GitConfigUtil.getLogEncoding(project, root)), consumer);
}
public static void loadStashStack(@NotNull Project project, @NotNull VirtualFile root, final Charset charset,
final Consumer<StashInfo> consumer) {
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.STASH);
h.setSilent(true);
h.setNoSSH(true);
h.addParameters("list");
String out;
try {
h.setCharset(charset);
out = h.run();
}
catch (VcsException e) {
GitUIUtil.showOperationError(project, e, h.printableCommandLine());
return;
}
for (StringScanner s = new StringScanner(out); s.hasMoreData();) {
consumer.consume(new StashInfo(s.boundedToken(':'), s.boundedToken(':'), s.line().trim()));
}
}
/**
* Create stash for later use (it ignores exit code 1 [merge conflict])
*