summaryrefslogtreecommitdiff
path: root/plugins/git4idea/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/git4idea/src')
-rw-r--r--plugins/git4idea/src/META-INF/plugin.xml11
-rw-r--r--plugins/git4idea/src/git4idea/GitUtil.java15
-rw-r--r--plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java70
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchOperation.java67
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java12
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java4
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java6
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java15
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java2
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitMergeOperation.java11
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java19
-rw-r--r--plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java10
-rw-r--r--plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java5
-rw-r--r--plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java2
-rw-r--r--plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java17
-rw-r--r--plugins/git4idea/src/git4idea/commands/Git.java4
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitCommandResult.java19
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitHandler.java16
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitHttpAuthService.java5
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitHttpAuthServiceImpl.java7
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java21
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitImpl.java21
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java31
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitSSHGUIHandler.java15
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitTask.java1
-rw-r--r--plugins/git4idea/src/git4idea/config/GitVcsSettings.java11
-rw-r--r--plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java136
-rw-r--r--plugins/git4idea/src/git4idea/history/GitHistoryUtils.java25
-rw-r--r--plugins/git4idea/src/git4idea/i18n/GitBundle.properties4
-rw-r--r--plugins/git4idea/src/git4idea/log/GitBekParentFixer.java55
-rw-r--r--plugins/git4idea/src/git4idea/log/GitLogProvider.java15
-rw-r--r--plugins/git4idea/src/git4idea/merge/GitPullDialog.java68
-rw-r--r--plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java3
-rw-r--r--plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java2
-rw-r--r--plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java5
-rw-r--r--plugins/git4idea/src/git4idea/reset/GitNewResetDialog.java144
-rw-r--r--plugins/git4idea/src/git4idea/reset/GitResetAction.java65
-rw-r--r--plugins/git4idea/src/git4idea/reset/GitResetMode.java59
-rw-r--r--plugins/git4idea/src/git4idea/reset/GitResetOperation.java191
-rw-r--r--plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java63
-rw-r--r--plugins/git4idea/src/git4idea/ui/branch/GitBranchWidget.java2
-rw-r--r--plugins/git4idea/src/git4idea/update/GitMergeUpdater.java19
42 files changed, 791 insertions, 482 deletions
diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml
index 32d254f6fb67..dc12d0e03ad0 100644
--- a/plugins/git4idea/src/META-INF/plugin.xml
+++ b/plugins/git4idea/src/META-INF/plugin.xml
@@ -92,12 +92,13 @@
<separator/>
</group>
- <action id="Git.CherryPick" class="git4idea.cherrypick.GitCherryPickAction" text="Cherry-Pick" icon="Git4ideaIcons.CherryPick"/>
+ <action id="Git.CherryPick" class="git4idea.cherrypick.GitCherryPickAction" icon="Git4ideaIcons.CherryPick"/>
<action class="git4idea.actions.GitCheckoutRevisionAction" id="Git.CheckoutRevision" text="Checkout Revision"/>
- <action class="git4idea.actions.GitCreateNewBranchAction" id="Git.CreateNewBranch" text="New Branch"
+ <action class="git4idea.actions.GitCreateNewBranchAction" id="Git.CreateNewBranch" text="New Branch..."
description="Create new branch starting from the selected commit"/>
- <action class="git4idea.actions.GitCreateTagAction" id="Git.CreateNewTag" text="New Tag"
+ <action class="git4idea.actions.GitCreateTagAction" id="Git.CreateNewTag" text="New Tag..."
description="Create new tag pointing to this commit"/>
+ <action id="Git.Reset.In.Log" class="git4idea.reset.GitResetAction" text="Reset Current Branch to Here..." />
<group id="Git.Log.ContextMenu">
<separator/>
@@ -105,6 +106,8 @@
<reference id="Git.CheckoutRevision"/>
<reference id="Git.CreateNewBranch" />
<reference id="Git.CreateNewTag" />
+ <separator/>
+ <reference id="Git.Reset.In.Log" />
<add-to-group group-id="Vcs.Log.ContextMenu" />
</group>
@@ -121,7 +124,9 @@
<project-components>
<component>
+ <interface-class>git4idea.repo.GitRepositoryManager</interface-class>
<implementation-class>git4idea.repo.GitRepositoryManager</implementation-class>
+ <headless-implementation-class>git4idea.test.GitTestRepositoryManager</headless-implementation-class>
</component>
</project-components>
diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java
index 5f56de5674e1..4ff61fb4a68b 100644
--- a/plugins/git4idea/src/git4idea/GitUtil.java
+++ b/plugins/git4idea/src/git4idea/GitUtil.java
@@ -37,7 +37,6 @@ import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.ChangeListManagerEx;
-import com.intellij.openapi.vcs.changes.FilePathsHelper;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vcs.vfs.AbstractVcsVirtualFile;
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -778,6 +777,8 @@ public class GitUtil {
/**
* Returns absolute paths which have changed remotely comparing to the current branch, i.e. performs
* <code>git diff --name-only master..origin/master</code>
+ * <p/>
+ * Paths are absolute, Git-formatted (i.e. with forward slashes).
*/
@NotNull
public static Collection<String> getPathsDiffBetweenRefs(@NotNull Git git, @NotNull GitRepository repository,
@@ -797,7 +798,7 @@ public class GitUtil {
continue;
}
final String path = repository.getRoot().getPath() + "/" + unescapePath(relative);
- remoteChanges.add(FilePathsHelper.convertPath(path));
+ remoteChanges.add(path);
}
return remoteChanges;
}
@@ -1024,4 +1025,14 @@ public class GitUtil {
ApplicationManager.getApplication().getMessageBus().syncPublisher(BatchFileChangeListener.TOPIC).batchChangeCompleted(project);
}
+ @NotNull
+ public static String cleanupErrorPrefixes(@NotNull String msg) {
+ final String[] PREFIXES = { "fatal:", "error:" };
+ for (String prefix : PREFIXES) {
+ if (msg.startsWith(prefix)) {
+ return msg.substring(prefix.length()).trim();
+ }
+ }
+ return msg;
+ }
}
diff --git a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java
index 39cfb2a625ad..83512de71d1f 100644
--- a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java
+++ b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java
@@ -15,77 +15,21 @@
*/
package git4idea.actions;
-import com.intellij.dvcs.DvcsUtil;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.dvcs.ui.VcsLogSingleCommitAction;
import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
-import com.intellij.vcs.log.VcsFullCommitDetails;
-import com.intellij.vcs.log.VcsLog;
-import com.intellij.vcs.log.VcsLogDataKeys;
-import git4idea.GitVcs;
+import com.intellij.openapi.vfs.VirtualFile;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
-import java.util.List;
-
-/**
- * @author Kirill Likhodedov
- */
-public abstract class GitLogSingleCommitAction extends DumbAwareAction {
-
- private static final Logger LOG = Logger.getInstance(GitLogSingleCommitAction.class);
-
- protected abstract void actionPerformed(@NotNull GitRepository repository, @NotNull VcsFullCommitDetails commit);
-
- @Override
- public void actionPerformed(AnActionEvent e) {
- Data data = Data.collect(e);
- if (!data.isValid()) {
- return;
- }
-
- List<VcsFullCommitDetails> details = data.log.getSelectedDetails();
- if (details.size() != 1) {
- return;
- }
- VcsFullCommitDetails commit = details.get(0);
-
- GitRepositoryManager repositoryManager = ServiceManager.getService(data.project, GitRepositoryManager.class);
- final GitRepository repository = repositoryManager.getRepositoryForRoot(commit.getRoot());
- if (repository == null) {
- DvcsUtil.noVcsRepositoryForRoot(LOG, commit.getRoot(), data.project, repositoryManager, GitVcs.getInstance(data.project));
- return;
- }
-
- actionPerformed(repository, commit);
- }
+public abstract class GitLogSingleCommitAction extends VcsLogSingleCommitAction<GitRepository> {
@Override
- public void update(AnActionEvent e) {
- Data data = Data.collect(e);
- boolean enabled = data.isValid() && data.log.getSelectedCommits().size() == 1;
- e.getPresentation().setVisible(data.isValid());
- e.getPresentation().setEnabled(enabled);
- }
-
- private static class Data {
- Project project;
- VcsLog log;
-
- static Data collect(AnActionEvent e) {
- Data data = new Data();
- data.project = e.getData(CommonDataKeys.PROJECT);
- data.log = e.getData(VcsLogDataKeys.VSC_LOG);
- return data;
- }
-
- boolean isValid() {
- return project != null && log != null && DvcsUtil.logHasRootForVcs(log, GitVcs.getKey());
- }
+ @Nullable
+ protected GitRepository getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root) {
+ return ServiceManager.getService(project, GitRepositoryManager.class).getRepositoryForRoot(root);
}
}
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
index 7c2f77eeebd2..9147054ba7b9 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
@@ -15,6 +15,7 @@
*/
package git4idea.branch;
+import com.intellij.dvcs.repo.RepositoryUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
@@ -25,6 +26,9 @@ import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import git4idea.GitLocalBranch;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
import git4idea.commands.Git;
@@ -32,6 +36,7 @@ import git4idea.commands.GitMessageWithFilesDetector;
import git4idea.config.GitVcsSettings;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -50,7 +55,7 @@ abstract class GitBranchOperation {
@NotNull protected final Git myGit;
@NotNull protected final GitBranchUiHandler myUiHandler;
@NotNull private final Collection<GitRepository> myRepositories;
- @NotNull protected final String myCurrentBranchOrRev;
+ @NotNull protected final Map<GitRepository, String> myCurrentHeads;
private final GitVcsSettings mySettings;
@NotNull private final Collection<GitRepository> mySuccessfulRepositories;
@@ -63,7 +68,13 @@ abstract class GitBranchOperation {
myGit = git;
myUiHandler = uiHandler;
myRepositories = repositories;
- myCurrentBranchOrRev = GitBranchUtil.getCurrentBranchOrRev(repositories);
+ myCurrentHeads = ContainerUtil.map2Map(repositories, new Function<GitRepository, Pair<GitRepository, String>>() {
+ @Override
+ public Pair<GitRepository, String> fun(GitRepository repository) {
+ GitLocalBranch currentBranch = repository.getCurrentBranch();
+ return Pair.create(repository, currentBranch == null ? repository.getCurrentRevision() : currentBranch.getName());
+ }
+ });
mySuccessfulRepositories = new ArrayList<GitRepository>();
myRemainingRepositories = new ArrayList<GitRepository>(myRepositories);
mySettings = myFacade.getSettings(myProject);
@@ -220,11 +231,28 @@ abstract class GitBranchOperation {
protected void updateRecentBranch() {
if (getRepositories().size() == 1) {
GitRepository repository = myRepositories.iterator().next();
- mySettings.setRecentBranchOfRepository(repository.getRoot().getPath(), myCurrentBranchOrRev);
+ mySettings.setRecentBranchOfRepository(repository.getRoot().getPath(), myCurrentHeads.get(repository));
}
else {
- mySettings.setRecentCommonBranch(myCurrentBranchOrRev);
+ String recentCommonBranch = getRecentCommonBranch();
+ if (recentCommonBranch != null) {
+ mySettings.setRecentCommonBranch(recentCommonBranch);
+ }
+ }
+ }
+
+ @Nullable
+ private String getRecentCommonBranch() {
+ String recentCommonBranch = null;
+ for (String branch : myCurrentHeads.values()) {
+ if (recentCommonBranch == null) {
+ recentCommonBranch = branch;
+ }
+ else if (!recentCommonBranch.equals(branch)) {
+ return null;
+ }
}
+ return recentCommonBranch;
}
private void showUnmergedFilesDialogWithRollback() {
@@ -340,4 +368,35 @@ abstract class GitBranchOperation {
return Pair.create(allConflictingRepositories, affectedChanges);
}
+
+ @NotNull
+ protected static String stringifyBranchesByRepos(@NotNull Map<GitRepository, String> heads) {
+ MultiMap<String, VirtualFile> grouped = groupByBranches(heads);
+ if (grouped.size() == 1) {
+ return grouped.keySet().iterator().next();
+ }
+ return StringUtil.join(grouped.entrySet(), new Function<Map.Entry<String, Collection<VirtualFile>>, String>() {
+ @Override
+ public String fun(Map.Entry<String, Collection<VirtualFile>> entry) {
+ String roots = StringUtil.join(entry.getValue(), new Function<VirtualFile, String>() {
+ @Override
+ public String fun(VirtualFile file) {
+ return file.getName();
+ }
+ }, ", ");
+ return entry.getKey() + " (in " + roots + ")";
+ }
+ }, "<br/>");
+ }
+
+ @NotNull
+ private static MultiMap<String, VirtualFile> groupByBranches(@NotNull Map<GitRepository, String> heads) {
+ MultiMap<String, VirtualFile> result = MultiMap.createLinked();
+ List<GitRepository> sortedRepos = RepositoryUtil.sortRepositories(heads.keySet());
+ for (GitRepository repo : sortedRepos) {
+ result.putValue(heads.get(repo), repo.getRoot());
+ }
+ return result;
+ }
+
}
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
index 3b694c455c7b..95546f0547e8 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
@@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitCommit;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
@@ -68,14 +69,15 @@ public interface GitBranchUiHandler {
* Shows the dialog proposing to execute the operation (checkout or merge) smartly, i.e. stash-execute-unstash.
*
* @param project
- * @param changes local changes that would be overwritten by checkout or merge.
- * @param paths paths reported by Git (in most cases this is covered by {@code changes}.
- * @param operation operation name: checkout or merge
- * @param isForcePossible can the operation be executed force (force checkout is possible, force merge - not).
+ * @param changes local changes that would be overwritten by checkout or merge.
+ * @param paths paths reported by Git (in most cases this is covered by {@code changes}.
+ * @param operation operation name: checkout or merge
+ * @param forceButtonTitle if the operation can be executed force (force checkout is possible),
+ * specify the title of the force button; otherwise (force merge is not possible) pass null.
* @return the code of the decision.
*/
int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull Collection<String> paths,
- @NotNull String operation, boolean isForcePossible);
+ @NotNull String operation, @Nullable String forceButtonTitle);
boolean showBranchIsNotFullyMergedDialog(@NotNull Project project, @NotNull Map<GitRepository, List<GitCommit>> history,
@NotNull String unmergedBranch, @NotNull List<String> mergedToBranches,
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
index cea2b55596e0..20e207d14ba1 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
@@ -172,7 +172,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
@Override
public int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull Collection<String> paths,
- @NotNull String operation, boolean isForcePossible) {
+ @NotNull String operation, @Nullable String forceButtonTitle) {
JComponent fileBrowser;
if (!changes.isEmpty()) {
fileBrowser = new ChangesBrowserWithRollback(project, changes);
@@ -180,7 +180,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
else {
fileBrowser = new GitSimplePathsBrowser(project, paths);
}
- return GitSmartOperationDialog.showAndGetAnswer(myProject, fileBrowser, operation, isForcePossible);
+ return GitSmartOperationDialog.showAndGetAnswer(myProject, fileBrowser, operation, forceButtonTitle);
}
@Override
diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java
index 22e486e6eab0..cdc4f7711801 100644
--- a/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java
@@ -89,7 +89,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation {
protected String getRollbackProposal() {
return "However checkout has succeeded for the following " + repositories() + ":<br/>" +
successfulRepositoriesJoined() +
- "<br/>You may rollback (checkout back to " + myCurrentBranchOrRev + " and delete " + myNewBranchName + ") not to let branches diverge.";
+ "<br/>You may rollback (checkout previous branch back, and delete " + myNewBranchName + ") not to let branches diverge.";
}
@NotNull
@@ -104,7 +104,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation {
GitCompoundResult deleteResult = new GitCompoundResult(myProject);
Collection<GitRepository> repositories = getSuccessfulRepositories();
for (GitRepository repository : repositories) {
- GitCommandResult result = myGit.checkout(repository, myCurrentBranchOrRev, null, true);
+ GitCommandResult result = myGit.checkout(repository, myCurrentHeads.get(repository), null, true);
checkoutResult.append(repository, result);
if (result.success()) {
deleteResult.append(repository, myGit.branchDelete(repository, myNewBranchName, false));
@@ -113,7 +113,7 @@ class GitCheckoutNewBranchOperation extends GitBranchOperation {
}
if (checkoutResult.totalSuccess() && deleteResult.totalSuccess()) {
VcsNotifier.getInstance(myProject).notifySuccess("Rollback successful", String
- .format("Checked out %s and deleted %s on %s %s", code(myCurrentBranchOrRev), code(myNewBranchName),
+ .format("Checked out %s and deleted %s on %s %s", stringifyBranchesByRepos(myCurrentHeads), code(myNewBranchName),
StringUtil.pluralize("root", repositories.size()), successfulRepositoriesJoined()));
}
else {
diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
index 2441bf7d46cf..b772434959e8 100644
--- a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
@@ -47,7 +47,7 @@ import static git4idea.util.GitUIUtil.code;
*/
class GitCheckoutOperation extends GitBranchOperation {
- public static final String ROLLBACK_PROPOSAL_FORMAT = "You may rollback (checkout back to %s) not to let branches diverge.";
+ public static final String ROLLBACK_PROPOSAL_FORMAT = "You may rollback (checkout back to previous branch) not to let branches diverge.";
@NotNull private final String myStartPointReference;
@Nullable private final String myNewBranch;
@@ -115,12 +115,14 @@ class GitCheckoutOperation extends GitBranchOperation {
private boolean smartCheckoutOrNotify(@NotNull GitRepository repository,
@NotNull GitMessageWithFilesDetector localChangesOverwrittenByCheckout) {
Pair<List<GitRepository>, List<Change>> conflictingRepositoriesAndAffectedChanges =
- getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByCheckout, myCurrentBranchOrRev, myStartPointReference);
+ getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByCheckout, myCurrentHeads.get(repository),
+ myStartPointReference);
List<GitRepository> allConflictingRepositories = conflictingRepositoriesAndAffectedChanges.getFirst();
List<Change> affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond();
Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByCheckout.getRelativeFilePaths());
- int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "checkout", true);
+ int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "checkout",
+ "&Force Checkout");
if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) {
boolean smartCheckedOutSuccessfully = smartCheckout(allConflictingRepositories, myStartPointReference, myNewBranch, getIndicator());
if (smartCheckedOutSuccessfully) {
@@ -153,8 +155,7 @@ class GitCheckoutOperation extends GitBranchOperation {
@Override
protected String getRollbackProposal() {
return "However checkout has succeeded for the following " + repositories() + ":<br/>" +
- successfulRepositoriesJoined() +
- "<br/>" + String.format(ROLLBACK_PROPOSAL_FORMAT, myCurrentBranchOrRev);
+ successfulRepositoriesJoined() + "<br/>" + ROLLBACK_PROPOSAL_FORMAT;
}
@NotNull
@@ -168,7 +169,7 @@ class GitCheckoutOperation extends GitBranchOperation {
GitCompoundResult checkoutResult = new GitCompoundResult(myProject);
GitCompoundResult deleteResult = new GitCompoundResult(myProject);
for (GitRepository repository : getSuccessfulRepositories()) {
- GitCommandResult result = myGit.checkout(repository, myCurrentBranchOrRev, null, true);
+ GitCommandResult result = myGit.checkout(repository, myCurrentHeads.get(repository), null, true);
checkoutResult.append(repository, result);
if (result.success() && myNewBranch != null) {
/*
@@ -183,7 +184,7 @@ class GitCheckoutOperation extends GitBranchOperation {
if (!checkoutResult.totalSuccess() || !deleteResult.totalSuccess()) {
StringBuilder message = new StringBuilder();
if (!checkoutResult.totalSuccess()) {
- message.append("Errors during checking out ").append(myCurrentBranchOrRev).append(": ");
+ message.append("Errors during checkout: ");
message.append(checkoutResult.getErrorOutputWithReposIndication());
}
if (!deleteResult.totalSuccess()) {
diff --git a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java
index d3501caf7089..1dba1e07f6ac 100644
--- a/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java
@@ -68,7 +68,7 @@ class GitDeleteBranchOperation extends GitBranchOperation {
else if (notFullyMergedDetector.hasHappened()) {
String baseBranch = notMergedToUpstreamDetector.getBaseBranch();
if (baseBranch == null) { // GitBranchNotMergedToUpstreamDetector didn't happen
- baseBranch = myCurrentBranchOrRev;
+ baseBranch = myCurrentHeads.get(repository);
}
Collection<GitRepository> remainingRepositories = getRemainingRepositories();
diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
index 89a50cfd6054..1b8f382d1dd8 100644
--- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
@@ -34,6 +34,7 @@ import git4idea.commands.*;
import git4idea.merge.GitMergeCommittingConflictResolver;
import git4idea.merge.GitMerger;
import git4idea.repo.GitRepository;
+import git4idea.reset.GitResetMode;
import git4idea.util.GitPreservingProcess;
import org.jetbrains.annotations.NotNull;
@@ -189,12 +190,13 @@ class GitMergeOperation extends GitBranchOperation {
private boolean proposeSmartMergePerformAndNotify(@NotNull GitRepository repository,
@NotNull GitMessageWithFilesDetector localChangesOverwrittenByMerge) {
Pair<List<GitRepository>, List<Change>> conflictingRepositoriesAndAffectedChanges =
- getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByMerge, myCurrentBranchOrRev, myBranchToMerge);
+ getConflictingRepositoriesAndAffectedChanges(repository, localChangesOverwrittenByMerge, myCurrentHeads.get(repository),
+ myBranchToMerge);
List<GitRepository> allConflictingRepositories = conflictingRepositoriesAndAffectedChanges.getFirst();
List<Change> affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond();
Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByMerge.getRelativeFilePaths());
- int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "merge", false);
+ int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "merge", null);
if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) {
return doSmartMerge(allConflictingRepositories);
}
@@ -322,7 +324,7 @@ class GitMergeOperation extends GitBranchOperation {
@NotNull
private GitCommandResult rollback(@NotNull GitRepository repository) {
- return myGit.resetHard(repository, myCurrentRevisionsBeforeMerge.get(repository));
+ return myGit.reset(repository, GitResetMode.HARD, myCurrentRevisionsBeforeMerge.get(repository));
}
@NotNull
@@ -339,7 +341,8 @@ class GitMergeOperation extends GitBranchOperation {
@NotNull
@Override
public String getSuccessMessage() {
- return String.format("Merged <b><code>%s</code></b> to <b><code>%s</code></b>", myBranchToMerge, myCurrentBranchOrRev);
+ return String.format("Merged <b><code>%s</code></b> to <b><code>%s</code></b>",
+ myBranchToMerge, stringifyBranchesByRepos(myCurrentHeads));
}
@NotNull
diff --git a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
index 9c2ef4a8d107..4f5073027624 100644
--- a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
+++ b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
@@ -24,6 +24,7 @@ import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
import git4idea.GitPlatformFacade;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
@@ -44,18 +45,18 @@ public class GitSmartOperationDialog extends DialogWrapper {
@NotNull private final JComponent myFileBrowser;
@NotNull private final String myOperationTitle;
- private final boolean myShowForceButton;
+ @Nullable private final String myForceButton;
/**
* Shows the dialog with the list of local changes preventing merge/checkout and returns the dialog exit code.
*/
static int showAndGetAnswer(@NotNull final Project project, @NotNull final JComponent fileBrowser,
- @NotNull final String operationTitle, final boolean showForceButton) {
+ @NotNull final String operationTitle, @Nullable final String forceButtonTitle) {
final AtomicInteger exitCode = new AtomicInteger();
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
- GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, fileBrowser, operationTitle, showForceButton);
+ GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, fileBrowser, operationTitle, forceButtonTitle);
ServiceManager.getService(project, GitPlatformFacade.class).showDialog(dialog);
exitCode.set(dialog.getExitCode());
}
@@ -64,11 +65,11 @@ public class GitSmartOperationDialog extends DialogWrapper {
}
private GitSmartOperationDialog(@NotNull Project project, @NotNull JComponent fileBrowser, @NotNull String operationTitle,
- boolean showForceButton) {
+ @Nullable String forceButton) {
super(project);
myFileBrowser = fileBrowser;
myOperationTitle = operationTitle;
- myShowForceButton = showForceButton;
+ myForceButton = forceButton;
String capitalizedOperation = capitalize(myOperationTitle);
setTitle("Git " + capitalizedOperation + " Problem");
@@ -82,8 +83,8 @@ public class GitSmartOperationDialog extends DialogWrapper {
@NotNull
@Override
protected Action[] createLeftSideActions() {
- if (myShowForceButton) {
- return new Action[] {new ForceCheckoutAction(myOperationTitle) };
+ if (myForceButton != null) {
+ return new Action[]{new ForceCheckoutAction(myForceButton, myOperationTitle)};
}
return new Action[0];
}
@@ -110,8 +111,8 @@ public class GitSmartOperationDialog extends DialogWrapper {
private class ForceCheckoutAction extends AbstractAction {
- ForceCheckoutAction(@NotNull String operationTitle) {
- super("&Force " + capitalize(operationTitle));
+ ForceCheckoutAction(@NotNull String buttonTitle, @NotNull String operationTitle) {
+ super(buttonTitle);
putValue(Action.SHORT_DESCRIPTION, capitalize(operationTitle) + " and overwrite local changes");
}
diff --git a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java
index f1ab1d476c6f..31e731f44937 100644
--- a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java
+++ b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java
@@ -22,7 +22,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
-import com.intellij.openapi.vcs.changes.ContentRevision;
+import com.intellij.openapi.vcs.changes.ChangesUtil;
import com.intellij.openapi.vcs.changes.committed.DecoratorManager;
import com.intellij.openapi.vcs.changes.committed.VcsCommittedListsZipper;
import com.intellij.openapi.vcs.changes.committed.VcsCommittedViewAuxiliary;
@@ -199,13 +199,7 @@ public class GitCommittedChangeListProvider implements CommittedChangesProvider<
final Collection<Change> changes = commit.getChanges();
if (changes.size() == 1) {
Change change = changes.iterator().next();
- ContentRevision revision = change.getAfterRevision();
- if (revision == null) {
- revision = change.getBeforeRevision();
- }
- assert revision != null : "Revision can't be null in " + change;
- FilePath filePathInRevision = revision.getFile();
- return Pair.create(commit, filePathInRevision);
+ return Pair.create(commit, ChangesUtil.getFilePath(change));
}
for (Change change : changes) {
if (change.getAfterRevision() != null && FileUtil.filesEqual(filePath.getIOFile(), change.getAfterRevision().getFile().getIOFile())) {
diff --git a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java
index 3bb008a2f95f..448dd25ef7ab 100644
--- a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java
+++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java
@@ -226,10 +226,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
@NotNull
private static VcsException cleanupExceptionText(VcsException original) {
String msg = original.getMessage();
- final String FATAL_PREFIX = "fatal:";
- if (msg.startsWith(FATAL_PREFIX)) {
- msg = msg.substring(FATAL_PREFIX.length());
- }
+ msg = GitUtil.cleanupErrorPrefixes(msg);
final String DURING_EXECUTING_SUFFIX = GitSimpleHandler.DURING_EXECUTING_ERROR_MESSAGE;
int suffix = msg.indexOf(DURING_EXECUTING_SUFFIX);
if (suffix > 0) {
diff --git a/plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java b/plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java
index e46e98a7887f..ac9834efc3ce 100644
--- a/plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java
+++ b/plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java
@@ -35,7 +35,7 @@ class GitCommitAuthorCorrector {
if (at < 0) {
return author;
}
- int email = author.substring(0, at).lastIndexOf(' ');
+ int email = author.lastIndexOf(' ', at - 1);
if (email < 0) {
return author;
}
diff --git a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java
index aa3e1e440277..b4250a0d142b 100644
--- a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java
+++ b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java
@@ -49,6 +49,8 @@ import java.util.*;
* @author Kirill Likhodedov
*/
public class GitCherryPickAction extends DumbAwareAction {
+
+ private static final String NAME = "Cherry-Pick";
private static final Logger LOG = Logger.getInstance(GitCherryPickAction.class);
@NotNull private final GitPlatformFacade myPlatformFacade;
@@ -56,7 +58,7 @@ public class GitCherryPickAction extends DumbAwareAction {
@NotNull private final Set<Hash> myIdsInProgress;
public GitCherryPickAction() {
- super("Cherry-pick", "Cherry-pick", Git4ideaIcons.CherryPick);
+ super(NAME, null, Git4ideaIcons.CherryPick);
myGit = ServiceManager.getService(Git.class);
myPlatformFacade = ServiceManager.getService(GitPlatformFacade.class);
myIdsInProgress = ContainerUtil.newHashSet();
@@ -81,9 +83,8 @@ public class GitCherryPickAction extends DumbAwareAction {
new Task.Backgroundable(project, "Cherry-picking", false) {
public void run(@NotNull ProgressIndicator indicator) {
try {
- boolean autoCommit = GitVcsSettings.getInstance(myProject).isAutoCommitOnCherryPick();
Map<GitRepository, List<VcsFullCommitDetails>> commitsInRoots = sortCommits(groupCommitsByRoots(project, commits));
- new GitCherryPicker(myProject, myGit, myPlatformFacade, autoCommit).cherryPick(commitsInRoots);
+ new GitCherryPicker(project, myGit, myPlatformFacade, isAutoCommit(project)).cherryPick(commitsInRoots);
}
finally {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@@ -130,15 +131,21 @@ public class GitCherryPickAction extends DumbAwareAction {
return groupedCommits;
}
+ private static boolean isAutoCommit(@NotNull Project project) {
+ return GitVcsSettings.getInstance(project).isAutoCommitOnCherryPick();
+ }
+
@Override
public void update(AnActionEvent e) {
super.update(e);
- final VcsLog log = getVcsLog(e);
- if (log != null && !DvcsUtil.logHasRootForVcs(log, GitVcs.getKey())) {
+ VcsLog log = getVcsLog(e);
+ Project project = getEventProject(e);
+ if (project == null || log == null || !DvcsUtil.logHasRootForVcs(log, GitVcs.getKey())) {
e.getPresentation().setEnabledAndVisible(false);
}
else {
e.getPresentation().setEnabled(enabled(e));
+ e.getPresentation().setText(isAutoCommit(project) ? NAME : NAME + "...");
}
}
diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java
index 284ccc1e20c5..830f73b4508f 100644
--- a/plugins/git4idea/src/git4idea/commands/Git.java
+++ b/plugins/git4idea/src/git4idea/commands/Git.java
@@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitCommit;
import git4idea.push.GitPushSpec;
import git4idea.repo.GitRepository;
+import git4idea.reset.GitResetMode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -93,7 +94,8 @@ public interface Git {
GitCommandResult branchCreate(@NotNull GitRepository repository, @NotNull String branchName);
@NotNull
- GitCommandResult resetHard(@NotNull GitRepository repository, @NotNull String revision);
+ GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target,
+ @NotNull GitLineHandlerListener... listeners);
@NotNull
GitCommandResult resetMerge(@NotNull GitRepository repository, @Nullable String revision);
diff --git a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java
index f89a4890a76b..0683d738a9bf 100644
--- a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java
+++ b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java
@@ -16,10 +16,14 @@
package git4idea.commands;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import git4idea.GitUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -64,9 +68,10 @@ public class GitCommandResult {
@NotNull
public String getErrorOutputAsHtmlString() {
- return StringUtil.join(myErrorOutput, "<br/>");
+ return StringUtil.join(cleanup(myErrorOutput), "<br/>");
}
-
+
+ @NotNull
public String getErrorOutputAsJoinedString() {
return StringUtil.join(myErrorOutput, "\n");
}
@@ -90,4 +95,14 @@ public class GitCommandResult {
return false; // will be implemented later
}
+ @NotNull
+ private static Collection<String> cleanup(@NotNull Collection<String> errorOutput) {
+ return ContainerUtil.map(errorOutput, new Function<String, String>() {
+ @Override
+ public String fun(String errorMessage) {
+ return GitUtil.cleanupErrorPrefixes(errorMessage);
+ }
+ });
+ }
+
}
diff --git a/plugins/git4idea/src/git4idea/commands/GitHandler.java b/plugins/git4idea/src/git4idea/commands/GitHandler.java
index 3350b74b1c6a..c2d85b7e3126 100644
--- a/plugins/git4idea/src/git4idea/commands/GitHandler.java
+++ b/plugins/git4idea/src/git4idea/commands/GitHandler.java
@@ -18,7 +18,6 @@ package git4idea.commands;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -103,7 +102,6 @@ public abstract class GitHandler {
private long myStartTime; // git execution start timestamp
private static final long LONG_TIME = 10 * 1000;
- @Nullable private ModalityState myState;
@Nullable private String myUrl;
private boolean myHttpAuthFailed;
@@ -423,7 +421,7 @@ public abstract class GitHandler {
}
else {
LOG.debug("cd " + myWorkingDirectory);
- LOG.debug(printableCommandLine());
+ LOG.debug("[" + myWorkingDirectory.getName() + "] " + printableCommandLine());
}
// setup environment
@@ -431,7 +429,7 @@ public abstract class GitHandler {
if (remoteProtocol == GitRemoteProtocol.SSH && myProjectSettings.isIdeaSsh()) {
GitXmlRpcSshService ssh = ServiceManager.getService(GitXmlRpcSshService.class);
myEnv.put(GitSSHHandler.GIT_SSH_ENV, ssh.getScriptPath().getPath());
- myHandlerNo = ssh.registerHandler(new GitSSHGUIHandler(myProject, myState));
+ myHandlerNo = ssh.registerHandler(new GitSSHGUIHandler(myProject));
myEnvironmentCleanedUp = false;
myEnv.put(GitSSHHandler.SSH_HANDLER_ENV, Integer.toString(myHandlerNo));
int port = ssh.getXmlRcpPort();
@@ -458,7 +456,7 @@ public abstract class GitHandler {
GitHttpAuthService service = ServiceManager.getService(GitHttpAuthService.class);
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_ENV, service.getScriptPath().getPath());
assert myUrl != null : "myUrl can't be null here";
- GitHttpAuthenticator httpAuthenticator = service.createAuthenticator(myProject, myState, myCommand, myUrl);
+ GitHttpAuthenticator httpAuthenticator = service.createAuthenticator(myProject, myCommand, myUrl);
myHandlerNo = service.registerHandler(httpAuthenticator);
myEnvironmentCleanedUp = false;
myEnv.put(GitAskPassXmlRpcHandler.GIT_ASK_PASS_HANDLER_ENV, Integer.toString(myHandlerNo));
@@ -474,7 +472,9 @@ public abstract class GitHandler {
startHandlingStreams();
}
catch (Throwable t) {
- LOG.error(t);
+ if (!ApplicationManager.getApplication().isUnitTestMode() || !myProject.isDisposed()) {
+ LOG.error(t); // will surely happen if called during unit test disposal, because the working dir is simply removed then
+ }
cleanupEnv();
myListeners.getMulticaster().startFailed(t);
}
@@ -715,10 +715,6 @@ public abstract class GitHandler {
myResumeAction.run();
}
- public void setModalityState(@Nullable ModalityState state) {
- myState = state;
- }
-
/**
* @return true if the command line is too big
*/
diff --git a/plugins/git4idea/src/git4idea/commands/GitHttpAuthService.java b/plugins/git4idea/src/git4idea/commands/GitHttpAuthService.java
index 69be489be44b..712582339405 100644
--- a/plugins/git4idea/src/git4idea/commands/GitHttpAuthService.java
+++ b/plugins/git4idea/src/git4idea/commands/GitHttpAuthService.java
@@ -15,10 +15,8 @@
*/
package git4idea.commands;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.jetbrains.git4idea.http.GitAskPassApp;
import org.jetbrains.git4idea.http.GitAskPassXmlRpcHandler;
import org.jetbrains.git4idea.ssh.GitXmlRpcHandlerService;
@@ -47,8 +45,7 @@ public abstract class GitHttpAuthService extends GitXmlRpcHandlerService<GitHttp
* Creates new {@link GitHttpAuthenticator} that will be requested to handle username and password requests from Git.
*/
@NotNull
- public abstract GitHttpAuthenticator createAuthenticator(@NotNull Project project, @Nullable ModalityState state,
- @NotNull GitCommand command, @NotNull String url);
+ public abstract GitHttpAuthenticator createAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull String url);
/**
* Internal handler implementation class, it is made public to be accessible via XML RPC.
diff --git a/plugins/git4idea/src/git4idea/commands/GitHttpAuthServiceImpl.java b/plugins/git4idea/src/git4idea/commands/GitHttpAuthServiceImpl.java
index 06374bf78864..cda188d64e4b 100644
--- a/plugins/git4idea/src/git4idea/commands/GitHttpAuthServiceImpl.java
+++ b/plugins/git4idea/src/git4idea/commands/GitHttpAuthServiceImpl.java
@@ -15,10 +15,8 @@
*/
package git4idea.commands;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/**
* @author Kirill Likhodedov
@@ -27,9 +25,8 @@ class GitHttpAuthServiceImpl extends GitHttpAuthService {
@Override
@NotNull
- public GitHttpAuthenticator createAuthenticator(@NotNull Project project, @Nullable ModalityState state, @NotNull GitCommand command,
- @NotNull String url) {
- return new GitHttpGuiAuthenticator(project, state, command, url);
+ public GitHttpAuthenticator createAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull String url) {
+ return new GitHttpGuiAuthenticator(project, command, url);
}
}
diff --git a/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java b/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java
index e0312e5471fc..c5931f98df10 100644
--- a/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java
+++ b/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java
@@ -57,7 +57,6 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
private static final Class<GitHttpAuthenticator> PASS_REQUESTER = GitHttpAuthenticator.class;
@NotNull private final Project myProject;
- @Nullable private final ModalityState myModalityState;
@NotNull private final String myTitle;
@NotNull private final String myUrlFromCommand;
@@ -69,10 +68,8 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
@Nullable private GitHttpAuthDataProvider myDataProvider;
private boolean myWasCancelled;
- GitHttpGuiAuthenticator(@NotNull Project project, @Nullable ModalityState modalityState, @NotNull GitCommand command,
- @NotNull String url) {
+ GitHttpGuiAuthenticator(@NotNull Project project, @NotNull GitCommand command, @NotNull String url) {
myProject = project;
- myModalityState = modalityState;
myTitle = "Git " + StringUtil.capitalize(command.name());
myUrlFromCommand = url;
}
@@ -87,7 +84,7 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
return "";
}
url = adjustUrl(url);
- Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url, myModalityState);
+ Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url);
if (authData != null && authData.second.getPassword() != null) {
String password = authData.second.getPassword();
myDataProvider = authData.first;
@@ -97,7 +94,7 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
String prompt = "Enter the password for " + url;
myPasswordKey = url;
- String password = PasswordSafePromptDialog.askPassword(myProject, myModalityState, myTitle, prompt, PASS_REQUESTER, url, false, null);
+ String password = PasswordSafePromptDialog.askPassword(myProject, myTitle, prompt, PASS_REQUESTER, url, false, null);
if (password == null) {
myWasCancelled = true;
return "";
@@ -114,7 +111,7 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
@NotNull
public String askUsername(@NotNull String url) {
url = adjustUrl(url);
- Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url, myModalityState);
+ Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url);
String login = null;
String password = null;
if (authData != null) {
@@ -152,7 +149,7 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
dialog.set(new AuthDialog(myProject, myTitle, "Enter credentials for " + url, login, null, true));
dialog.get().show();
}
- }, myModalityState == null ? ModalityState.defaultModalityState() : myModalityState);
+ }, ModalityState.any());
return dialog.get();
}
@@ -223,10 +220,10 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
// return the first that knows username + password; otherwise return the first that knows just the username
@Nullable
- private Pair<GitHttpAuthDataProvider, AuthData> findBestAuthData(@NotNull String url, @Nullable ModalityState modalityState) {
+ private Pair<GitHttpAuthDataProvider, AuthData> findBestAuthData(@NotNull String url) {
Pair<GitHttpAuthDataProvider, AuthData> candidate = null;
for (GitHttpAuthDataProvider provider : getProviders()) {
- AuthData data = provider.getAuthData(url, modalityState);
+ AuthData data = provider.getAuthData(url);
if (data != null) {
Pair<GitHttpAuthDataProvider, AuthData> pair = Pair.create(provider, data);
if (data.getPassword() != null) {
@@ -268,12 +265,12 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
@Nullable
@Override
- public AuthData getAuthData(@NotNull String url, @Nullable ModalityState modalityState) {
+ public AuthData getAuthData(@NotNull String url) {
String userName = getUsername(url);
String key = makeKey(url, userName);
final PasswordSafe passwordSafe = PasswordSafe.getInstance();
try {
- String password = passwordSafe.getPassword(myProject, PASS_REQUESTER, key, modalityState);
+ String password = passwordSafe.getPassword(myProject, PASS_REQUESTER, key);
return new AuthData(StringUtil.notNullize(userName), password);
}
catch (PasswordSafeException e) {
diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java
index c873e71ff94b..09e254f5392b 100644
--- a/plugins/git4idea/src/git4idea/commands/GitImpl.java
+++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java
@@ -32,6 +32,7 @@ import git4idea.history.GitHistoryUtils;
import git4idea.push.GitPushSpec;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
+import git4idea.reset.GitResetMode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -338,20 +339,26 @@ public class GitImpl implements Git {
@Override
@NotNull
- public GitCommandResult resetHard(@NotNull GitRepository repository, @NotNull String revision) {
- final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET);
- handler.addParameters("--hard", revision);
- return run(handler);
+ public GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target,
+ @NotNull GitLineHandlerListener... listeners) {
+ return reset(repository, mode.getArgument(), target, listeners);
}
@Override
@NotNull
public GitCommandResult resetMerge(@NotNull GitRepository repository, @Nullable String revision) {
+ return reset(repository, "--merge", revision);
+ }
+
+ @NotNull
+ private static GitCommandResult reset(@NotNull GitRepository repository, @NotNull String argument, @Nullable String target,
+ @NotNull GitLineHandlerListener... listeners) {
final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET);
- handler.addParameters("--merge");
- if (revision != null) {
- handler.addParameters(revision);
+ handler.addParameters(argument);
+ if (target != null) {
+ handler.addParameters(target);
}
+ addListeners(handler, listeners);
return run(handler);
}
diff --git a/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java b/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java
index b62f12806761..e9d8df89614a 100644
--- a/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java
+++ b/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java
@@ -40,6 +40,13 @@ public class GitLocalChangesWouldBeOverwrittenDetector extends GitMessageWithFil
".*Your local changes to '(.*)' would be overwritten by merge.*"
);
+ private static final Pattern[] RESET_PATTERNS = new Pattern[]{Pattern.compile(
+ ".*Entry '(.*)' not uptodate. Cannot merge.*"
+ ),
+ Pattern.compile(
+ ".*Entry '(.*)' would be overwritten by merge.*"
+ )};
+
// common for checkout and merge
public static final Event NEW_PATTERN = new Event(
"Your local changes to the following files would be overwritten by",
@@ -49,17 +56,18 @@ public class GitLocalChangesWouldBeOverwrittenDetector extends GitMessageWithFil
public enum Operation {
CHECKOUT(OLD_CHECKOUT_PATTERN),
- MERGE(OLD_MERGE_PATTERN);
+ MERGE(OLD_MERGE_PATTERN),
+ RESET(RESET_PATTERNS);
- @NotNull private final Pattern myPattern;
+ @NotNull private final Pattern[] myPatterns;
- Operation(@NotNull Pattern pattern) {
- myPattern = pattern;
+ Operation(@NotNull Pattern... patterns) {
+ myPatterns = patterns;
}
@NotNull
- public Pattern getPattern() {
- return myPattern;
+ Pattern[] getPatterns() {
+ return myPatterns;
}
}
@@ -71,10 +79,13 @@ public class GitLocalChangesWouldBeOverwrittenDetector extends GitMessageWithFil
@Override
public void onLineAvailable(@NotNull String line, @NotNull Key outputType) {
super.onLineAvailable(line, outputType);
- Matcher m = myOperation.getPattern().matcher(line);
- if (m.matches()) {
- myMessageDetected = true;
- myAffectedFiles.add(m.group(1));
+ for (Pattern pattern : myOperation.getPatterns()) {
+ Matcher m = pattern.matcher(line);
+ if (m.matches()) {
+ myMessageDetected = true;
+ myAffectedFiles.add(m.group(1));
+ break;
+ }
}
}
}
diff --git a/plugins/git4idea/src/git4idea/commands/GitSSHGUIHandler.java b/plugins/git4idea/src/git4idea/commands/GitSSHGUIHandler.java
index 982270421968..4d1c13415ec2 100644
--- a/plugins/git4idea/src/git4idea/commands/GitSSHGUIHandler.java
+++ b/plugins/git4idea/src/git4idea/commands/GitSSHGUIHandler.java
@@ -16,7 +16,6 @@
package git4idea.commands;
import com.intellij.ide.passwordSafe.ui.PasswordSafePromptDialog;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
@@ -39,17 +38,9 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class GitSSHGUIHandler {
@Nullable private final Project myProject;
- @Nullable private final ModalityState myState;
- /**
- * A constructor
- *
- * @param project a project to use
- * @param state modality state using which any prompts initiated by the git process should be shown in the UI.
- */
- GitSSHGUIHandler(@Nullable Project project, @Nullable ModalityState state) {
+ GitSSHGUIHandler(@Nullable Project project) {
myProject = project;
- myState = state;
}
public boolean verifyServerHostKey(final String hostname,
@@ -76,7 +67,7 @@ public class GitSSHGUIHandler {
@Nullable
public String askPassphrase(final String username, final String keyPath, boolean resetPassword, final String lastError) {
String error = processLastError(resetPassword, lastError);
- return PasswordSafePromptDialog.askPassphrase(myProject, myState, GitBundle.getString("ssh.ask.passphrase.title"),
+ return PasswordSafePromptDialog.askPassphrase(myProject, GitBundle.getString("ssh.ask.passphrase.title"),
GitBundle.message("ssh.askPassphrase.message", keyPath, username),
GitSSHGUIHandler.class, "PASSPHRASE:" + keyPath, resetPassword, error
);
@@ -165,7 +156,7 @@ public class GitSSHGUIHandler {
@Nullable
public String askPassword(final String username, boolean resetPassword, final String lastError) {
String error = processLastError(resetPassword, lastError);
- return PasswordSafePromptDialog.askPassword(myProject, myState, GitBundle.getString("ssh.password.title"),
+ return PasswordSafePromptDialog.askPassword(myProject, GitBundle.getString("ssh.password.title"),
GitBundle.message("ssh.password.message", username),
GitSSHGUIHandler.class, "PASSWORD:" + username, resetPassword, error);
}
diff --git a/plugins/git4idea/src/git4idea/commands/GitTask.java b/plugins/git4idea/src/git4idea/commands/GitTask.java
index c22ce07819c9..79f8e33ef55e 100644
--- a/plugins/git4idea/src/git4idea/commands/GitTask.java
+++ b/plugins/git4idea/src/git4idea/commands/GitTask.java
@@ -324,7 +324,6 @@ public class GitTask {
@Override
public final void run(@NotNull ProgressIndicator indicator) {
- myHandler.setModalityState(indicator.getModalityState());
myDelegate.run(indicator);
}
diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java
index 4f74f7ca94f3..1b5860631e59 100644
--- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java
+++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java
@@ -19,6 +19,7 @@ import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.util.ArrayUtil;
+import git4idea.reset.GitResetMode;
import git4idea.ui.branch.GitBranchSyncSetting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -61,6 +62,7 @@ public class GitVcsSettings implements PersistentStateComponent<GitVcsSettings.S
public String RECENT_COMMON_BRANCH = null;
public boolean AUTO_COMMIT_ON_CHERRY_PICK = false;
public boolean WARN_ABOUT_CRLF = true;
+ public GitResetMode RESET_MODE = null;
}
public GitVcsSettings(GitVcsApplicationSettings appSettings) {
@@ -177,6 +179,15 @@ public class GitVcsSettings implements PersistentStateComponent<GitVcsSettings.S
myState.WARN_ABOUT_CRLF = warn;
}
+ @Nullable
+ public GitResetMode getResetMode() {
+ return myState.RESET_MODE;
+ }
+
+ public void setResetMode(@NotNull GitResetMode mode) {
+ myState.RESET_MODE = mode;
+ }
+
/**
* Provides migration from project settings.
* This method is to be removed in IDEA 13: it should be moved to {@link GitVcsApplicationSettings}
diff --git a/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java b/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java
index 87edd30ae077..a6488d4ce3d0 100644
--- a/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java
+++ b/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java
@@ -22,25 +22,22 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.DialogBuilder;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
-import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ContentRevision;
-import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
-import com.intellij.openapi.vcs.history.CurrentRevision;
+import com.intellij.openapi.vcs.history.BaseDiffFromHistoryHandler;
import com.intellij.openapi.vcs.history.DiffFromHistoryHandler;
import com.intellij.openapi.vcs.history.VcsFileRevision;
-import com.intellij.openapi.vcs.history.VcsHistoryUtil;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
+import com.intellij.util.containers.ContainerUtil;
import git4idea.GitFileRevision;
import git4idea.GitRevisionNumber;
import git4idea.GitUtil;
@@ -67,71 +64,59 @@ import java.util.List;
*
* @author Kirill Likhodedov
*/
-public class GitDiffFromHistoryHandler implements DiffFromHistoryHandler {
+public class GitDiffFromHistoryHandler extends BaseDiffFromHistoryHandler<GitFileRevision> {
private static final Logger LOG = Logger.getInstance(GitDiffFromHistoryHandler.class);
- @NotNull private final Project myProject;
@NotNull private final Git myGit;
@NotNull private final GitRepositoryManager myRepositoryManager;
public GitDiffFromHistoryHandler(@NotNull Project project) {
- myProject = project;
+ super(project);
myGit = ServiceManager.getService(project, Git.class);
myRepositoryManager = GitUtil.getRepositoryManager(project);
}
@Override
- public void showDiffForOne(@NotNull AnActionEvent e, @NotNull FilePath filePath,
- @NotNull VcsFileRevision previousRevision, @NotNull VcsFileRevision revision) {
+ public void showDiffForOne(@NotNull AnActionEvent e,
+ @NotNull FilePath filePath,
+ @NotNull VcsFileRevision previousRevision,
+ @NotNull VcsFileRevision revision) {
GitFileRevision rev = (GitFileRevision)revision;
Collection<String> parents = rev.getParents();
if (parents.size() < 2) {
- doShowDiff(filePath, previousRevision, revision, false);
+ super.showDiffForOne(e, filePath, previousRevision, revision);
}
else { // merge
showDiffForMergeCommit(e, filePath, rev, parents);
}
}
+ @NotNull
@Override
- public void showDiffForTwo(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2) {
- doShowDiff(filePath, revision1, revision2, true);
- }
+ protected List<Change> getChangesBetweenRevisions(@NotNull FilePath path, @NotNull GitFileRevision rev1, @Nullable GitFileRevision rev2)
+ throws VcsException {
+ GitRepository repository = getRepository(path);
+ String hash1 = rev1.getHash();
+ String hash2 = rev2 != null ? rev2.getHash() : null;
- private void doShowDiff(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2,
- boolean autoSort) {
- if (!filePath.isDirectory()) {
- VcsHistoryUtil.showDifferencesInBackground(myProject, filePath, revision1, revision2, autoSort);
- }
- else if (revision2 instanceof CurrentRevision) {
- GitFileRevision left = (GitFileRevision)revision1;
- showDiffForDirectory(filePath, left.getHash(), null);
- }
- else if (revision1.equals(VcsFileRevision.NULL)) {
- GitFileRevision right = (GitFileRevision)revision2;
- showDiffForDirectory(filePath, null, right.getHash());
- }
- else {
- GitFileRevision left = (GitFileRevision)revision1;
- GitFileRevision right = (GitFileRevision)revision2;
- if (autoSort) {
- Couple<VcsFileRevision> pair = VcsHistoryUtil.sortRevisions(revision1, revision2);
- left = (GitFileRevision)pair.first;
- right = (GitFileRevision)pair.second;
- }
- showDiffForDirectory(filePath, left.getHash(), right.getHash());
- }
+ return ContainerUtil
+ .newArrayList(GitChangeUtils.getDiff(repository.getProject(), repository.getRoot(), hash1, hash2, Collections.singletonList(path)));
}
- private void showDiffForDirectory(@NotNull final FilePath path, @Nullable final String hash1, @Nullable final String hash2) {
+ @NotNull
+ @Override
+ protected List<Change> getAffectedChanges(@NotNull FilePath path, @NotNull GitFileRevision rev) throws VcsException {
GitRepository repository = getRepository(path);
- calculateDiffInBackground(repository, path, hash1, hash2, new Consumer<List<Change>>() {
- @Override
- public void consume(List<Change> changes) {
- showDirDiffDialog(path, hash1, hash2, changes);
- }
- });
+
+ return ContainerUtil.newArrayList(
+ GitChangeUtils.getRevisionChanges(repository.getProject(), repository.getRoot(), rev.getHash(), false, true, true).getChanges());
+ }
+
+ @NotNull
+ @Override
+ protected String getPresentableName(@NotNull GitFileRevision revision) {
+ return GitUtil.getShortHash(revision.getHash());
}
@NotNull
@@ -141,63 +126,6 @@ public class GitDiffFromHistoryHandler implements DiffFromHistoryHandler {
return repository;
}
- // hash1 == null => hash2 is the initial commit
- // hash2 == null => comparing hash1 with local
- private void calculateDiffInBackground(@NotNull final GitRepository repository, @NotNull final FilePath path,
- @Nullable final String hash1, @Nullable final String hash2,
- final Consumer<List<Change>> successHandler) {
- new Task.Backgroundable(myProject, "Comparing revisions...") {
- private List<Change> myChanges;
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- try {
- if (hash1 != null) {
- // diff
- myChanges = new ArrayList<Change>(GitChangeUtils.getDiff(repository.getProject(), repository.getRoot(), hash1, hash2,
- Collections.singletonList(path)));
- }
- else {
- // show the initial commit
- myChanges = new ArrayList<Change>(GitChangeUtils.getRevisionChanges(repository.getProject(), repository.getRoot(), hash2, false,
- true, true).getChanges());
- }
- }
- catch (VcsException e) {
- showError(e, "Error during requesting diff for directory");
- }
- }
-
- @Override
- public void onSuccess() {
- successHandler.consume(myChanges);
- }
- }.queue();
- }
-
- private void showDirDiffDialog(@NotNull FilePath path, @Nullable String hash1, @Nullable String hash2, @NotNull List<Change> diff) {
- DialogBuilder dialogBuilder = new DialogBuilder(myProject);
- String title;
- if (hash2 != null) {
- if (hash1 != null) {
- title = String.format("Difference between %s and %s in %s", GitUtil.getShortHash(hash1), GitUtil.getShortHash(hash2), path.getName());
- }
- else {
- title = String.format("Initial commit %s in %s", GitUtil.getShortHash(hash2), path.getName());
- }
- }
- else {
- LOG.assertTrue(hash1 != null, "hash1 and hash2 can't both be null. Path: " + path);
- title = String.format("Difference between %s and local version in %s", GitUtil.getShortHash(hash1), path.getName());
- }
- dialogBuilder.setTitle(title);
- dialogBuilder.setActionDescriptors(new DialogBuilder.ActionDescriptor[] { new DialogBuilder.CloseDialogAction()});
- final ChangesBrowser changesBrowser = new ChangesBrowser(myProject, null, diff, null, false, true,
- null, ChangesBrowser.MyUseCase.COMMITTED_CHANGES, null);
- changesBrowser.setChangesToDisplay(diff);
- dialogBuilder.setCenterPanel(changesBrowser);
- dialogBuilder.showNotModal();
- }
-
private void showDiffForMergeCommit(@NotNull final AnActionEvent event, @NotNull final FilePath filePath,
@NotNull final GitFileRevision rev, @NotNull final Collection<String> parents) {
@@ -299,12 +227,6 @@ public class GitDiffFromHistoryHandler implements DiffFromHistoryHandler {
return makeRevisionFromHash(currentRevisionPath, parentHash);
}
-
- private void showError(VcsException e, String logMessage) {
- LOG.info(logMessage, e);
- VcsBalloonProblemNotifier.showOverVersionControlView(this.myProject, e.getMessage(), MessageType.ERROR);
- }
-
private void showPopup(@NotNull AnActionEvent event, @NotNull GitFileRevision rev, @NotNull FilePath filePath,
@NotNull Collection<GitFileRevision> parents) {
ActionGroup parentActions = createActionGroup(rev, filePath, parents);
diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java
index 114b4beb5435..8273c3e1abdc 100644
--- a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java
+++ b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java
@@ -520,9 +520,19 @@ public class GitHistoryUtils {
@NotNull VirtualFile root,
@NotNull final Consumer<VcsUser> userRegistry,
@NotNull List<String> parameters) throws VcsException {
+ List<TimedVcsCommit> collector = ContainerUtil.newArrayList();
+ readCommits(project, root, userRegistry, parameters, new CollectConsumer<TimedVcsCommit>(collector));
+ return collector;
+ }
+
+ public static void readCommits(@NotNull final Project project,
+ @NotNull VirtualFile root,
+ @NotNull final Consumer<VcsUser> userRegistry,
+ @NotNull List<String> parameters,
+ @NotNull final Consumer<TimedVcsCommit> commitConsumer) throws VcsException {
final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project);
if (factory == null) {
- return Collections.emptyList();
+ return;
}
final int COMMIT_BUFFER = 1000;
@@ -536,8 +546,6 @@ public class GitHistoryUtils {
h.addParameters(parameters);
h.endOptions();
- final List<TimedVcsCommit> commits = ContainerUtil.newArrayList();
-
final StringBuilder record = new StringBuilder();
final AtomicInteger records = new AtomicInteger();
final Ref<VcsException> ex = new Ref<VcsException>();
@@ -560,7 +568,10 @@ public class GitHistoryUtils {
afterParseRemainder = line.substring(recordEnd + 1);
}
if (afterParseRemainder != null && records.incrementAndGet() > COMMIT_BUFFER) { // null means can't parse now
- commits.addAll(parseCommit(parser, record, userRegistry, factory));
+ List<TimedVcsCommit> commits = parseCommit(parser, record, userRegistry, factory);
+ for (TimedVcsCommit commit : commits) {
+ commitConsumer.consume(commit);
+ }
record.setLength(0);
record.append(afterParseRemainder);
}
@@ -573,7 +584,10 @@ public class GitHistoryUtils {
@Override
public void processTerminated(int exitCode) {
try {
- commits.addAll(parseCommit(parser, record, userRegistry, factory));
+ List<TimedVcsCommit> commits = parseCommit(parser, record, userRegistry, factory);
+ for (TimedVcsCommit commit : commits) {
+ commitConsumer.consume(commit);
+ }
}
catch (Exception e) {
ex.set(new VcsException(e));
@@ -589,7 +603,6 @@ public class GitHistoryUtils {
if (!ex.isNull()) {
throw ex.get();
}
- return commits;
}
@NotNull
diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties
index 79e7f70df250..03803fbf985c 100644
--- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties
+++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties
@@ -491,9 +491,9 @@ git.executable.filechooser.description=Specify the full path to Git executable
git.push.active.close=Close
git.unstash.clear.confirmation.message=Remove all stashes? This cannot be undone.
-git.unstash.clear.confirmation.title=Remove all stashes?
+git.unstash.clear.confirmation.title=Remove All Stashes?
git.unstash.drop.confirmation.message=<html>Do you want to remove {0}?<br/>"{1}"</html>
-git.unstash.drop.confirmation.title=Remove stash {0}?
+git.unstash.drop.confirmation.title=Remove Stash {0}?
branch.delete.not_fully_merged.description=The branch <code><b>{0}</b></code> is not fully merged to the branch <code><b>{1}</b></code>.<br/>Below is the list of unmerged commits.
branch.delete.not_fully_merged.description.not_on_branch=You are currently not on the branch (<code>{1}</code>). <br>\
diff --git a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java
index e469e61ca315..054ed9f90b48 100644
--- a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java
+++ b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java
@@ -27,49 +27,34 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
class GitBekParentFixer {
- @NotNull
- private final static String MAGIC_TEXT = "Merge remote";
- @NotNull
- private final VcsLogFilterCollection MAGIC_FILTER = createVcsLogFilterCollection();
+ @NotNull private static final String MAGIC_TEXT = "Merge remote";
+ @NotNull private static final VcsLogFilterCollection MAGIC_FILTER = createVcsLogFilterCollection();
- @NotNull
- private final VirtualFile myRoot;
- @NotNull
- private final GitLogProvider myGitLogProvider;
- @NotNull
- private final List<TimedVcsCommit> myAllCommits;
+ @NotNull private final Set<Hash> myWrongCommits;
- GitBekParentFixer(@NotNull VirtualFile root, @NotNull GitLogProvider gitLogProvider, @NotNull List<TimedVcsCommit> allCommits) {
- myRoot = root;
- myGitLogProvider = gitLogProvider;
- myAllCommits = allCommits;
+ private GitBekParentFixer(@NotNull Set<Hash> wrongCommits) {
+ myWrongCommits = wrongCommits;
}
@NotNull
- List<TimedVcsCommit> getCorrectCommits() throws VcsException {
- if (!BekSorter.isBekEnabled())
- return myAllCommits;
-
- final Set<Hash> wrongCommits = getWrongCommits();
- return new AbstractList<TimedVcsCommit>() {
- @Override
- public TimedVcsCommit get(int index) {
- TimedVcsCommit commit = myAllCommits.get(index);
- if (!wrongCommits.contains(commit.getId()))
- return commit;
-
- return reverseParents(commit);
- }
+ static GitBekParentFixer prepare(@NotNull VirtualFile root, @NotNull GitLogProvider provider) throws VcsException {
+ if (!BekSorter.isBekEnabled()) {
+ return new GitBekParentFixer(Collections.<Hash>emptySet());
+ }
+ return new GitBekParentFixer(getWrongCommits(provider, root));
+ }
- @Override
- public int size() {
- return myAllCommits.size();
- }
- };
+ @NotNull
+ TimedVcsCommit fixCommit(@NotNull TimedVcsCommit commit) {
+ if (!myWrongCommits.contains(commit.getId())) {
+ return commit;
+ }
+ return reverseParents(commit);
}
- private Set<Hash> getWrongCommits() throws VcsException {
- List<TimedVcsCommit> commitsMatchingFilter = myGitLogProvider.getCommitsMatchingFilter(myRoot, MAGIC_FILTER, -1);
+ @NotNull
+ private static Set<Hash> getWrongCommits(@NotNull GitLogProvider provider, @NotNull VirtualFile root) throws VcsException {
+ List<TimedVcsCommit> commitsMatchingFilter = provider.getCommitsMatchingFilter(root, MAGIC_FILTER, -1);
return ContainerUtil.map2Set(commitsMatchingFilter, new Function<TimedVcsCommit, Hash>() {
@Override
public Hash fun(TimedVcsCommit timedVcsCommit) {
diff --git a/plugins/git4idea/src/git4idea/log/GitLogProvider.java b/plugins/git4idea/src/git4idea/log/GitLogProvider.java
index ecc4709be5d3..435313fa5c7d 100644
--- a/plugins/git4idea/src/git4idea/log/GitLogProvider.java
+++ b/plugins/git4idea/src/git4idea/log/GitLogProvider.java
@@ -154,18 +154,23 @@ public class GitLogProvider implements VcsLogProvider {
});
}
- @NotNull
@Override
- public List<TimedVcsCommit> readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry) throws VcsException {
+ public void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry,
+ @NotNull final Consumer<TimedVcsCommit> commitConsumer) throws VcsException {
if (!isRepositoryReady(root)) {
- return Collections.emptyList();
+ return;
}
List<String> parameters = new ArrayList<String>(GitHistoryUtils.LOG_ALL);
parameters.add("--sparse");
- List<TimedVcsCommit> timedVcsCommits = GitHistoryUtils.readCommits(myProject, root, userRegistry, parameters);
- return new GitBekParentFixer(root, this, timedVcsCommits).getCorrectCommits();
+ final GitBekParentFixer parentFixer = GitBekParentFixer.prepare(root, this);
+ GitHistoryUtils.readCommits(myProject, root, userRegistry, parameters, new Consumer<TimedVcsCommit>() {
+ @Override
+ public void consume(TimedVcsCommit commit) {
+ commitConsumer.consume(parentFixer.fixCommit(commit));
+ }
+ });
}
@NotNull
diff --git a/plugins/git4idea/src/git4idea/merge/GitPullDialog.java b/plugins/git4idea/src/git4idea/merge/GitPullDialog.java
index f9d8085cfab8..68b16164b335 100644
--- a/plugins/git4idea/src/git4idea/merge/GitPullDialog.java
+++ b/plugins/git4idea/src/git4idea/merge/GitPullDialog.java
@@ -44,66 +44,23 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
-/**
- * Git pull dialog
- */
public class GitPullDialog extends DialogWrapper {
private static final Logger LOG = Logger.getInstance(GitPullDialog.class);
- /**
- * root panel
- */
private JPanel myPanel;
- /**
- * The selected git root
- */
private JComboBox myGitRoot;
- /**
- * Current branch label
- */
private JLabel myCurrentBranch;
- /**
- * The merge strategy
- */
private JComboBox myStrategy;
- /**
- * No commit option
- */
private JCheckBox myNoCommitCheckBox;
- /**
- * Squash commit option
- */
private JCheckBox mySquashCommitCheckBox;
- /**
- * No fast forward option
- */
private JCheckBox myNoFastForwardCheckBox;
- /**
- * Add log info to commit option
- */
private JCheckBox myAddLogInformationCheckBox;
- /**
- * Selected remote option
- */
private JComboBox myRemote;
- /**
- * The branch chooser
- */
private ElementsChooser<String> myBranchChooser;
- /**
- * The context project
- */
private final Project myProject;
private final GitRepositoryManager myRepositoryManager;
- /**
- * A constructor
- *
- * @param project a project to select
- * @param roots a git repository roots for the project
- * @param defaultRoot a guessed default root
- */
public GitPullDialog(Project project, List<VirtualFile> roots, VirtualFile defaultRoot) {
super(project, true);
setTitle(GitBundle.getString("pull.title"));
@@ -138,9 +95,6 @@ public class GitPullDialog extends DialogWrapper {
init();
}
- /**
- * Validate dialog and enable buttons
- */
private void validateDialog() {
String selectedRemote = getRemote();
if (StringUtil.isEmptyOrSpaces(selectedRemote)) {
@@ -150,9 +104,6 @@ public class GitPullDialog extends DialogWrapper {
setOKActionEnabled(myBranchChooser.getMarkedElements().size() != 0);
}
- /**
- * @return a pull handler configured according to dialog options
- */
public GitLineHandler makeHandler(@NotNull String url) {
GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.PULL);
// ignore merge failure for the pull
@@ -232,9 +183,6 @@ public class GitPullDialog extends DialogWrapper {
return branch.getName().startsWith(remote + "/");
}
- /**
- * Update remotes for the git root
- */
private void updateRemotes() {
GitRepository repository = getRepository();
if (repository == null) {
@@ -331,39 +279,23 @@ public class GitPullDialog extends DialogWrapper {
};
}
- /**
- * @return a currently selected git root
- */
public VirtualFile gitRoot() {
return (VirtualFile)myGitRoot.getSelectedItem();
}
-
- /**
- * Create branch chooser
- */
private void createUIComponents() {
myBranchChooser = new ElementsChooser<String>(true);
}
- /**
- * {@inheritDoc}
- */
protected JComponent createCenterPanel() {
return myPanel;
}
- /**
- * {@inheritDoc}
- */
@Override
protected String getDimensionServiceKey() {
return getClass().getName();
}
- /**
- * {@inheritDoc}
- */
@Override
protected String getHelpId() {
return "reference.VersionControl.Git.Pull";
diff --git a/plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java b/plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java
index 281d69b772c3..6c0860ccf20a 100644
--- a/plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java
+++ b/plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java
@@ -15,7 +15,6 @@
*/
package git4idea.remote;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.util.AuthData;
import org.jetbrains.annotations.NotNull;
@@ -32,7 +31,7 @@ public interface GitHttpAuthDataProvider {
ExtensionPointName<GitHttpAuthDataProvider> EP_NAME = ExtensionPointName.create("Git4Idea.GitHttpAuthDataProvider");
@Nullable
- AuthData getAuthData(@NotNull String url, @Nullable ModalityState modalityState);
+ AuthData getAuthData(@NotNull String url);
void forgetPassword(@NotNull String url);
diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java
index 29f2c4cf3e2e..e401334416d6 100644
--- a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java
+++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java
@@ -34,7 +34,7 @@ import java.util.Collection;
/**
* @author Kirill Likhodedov
*/
-public class GitRepositoryImpl extends RepositoryImpl implements GitRepository, Disposable {
+public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
@NotNull private final GitPlatformFacade myPlatformFacade;
@NotNull private final GitRepositoryReader myReader;
diff --git a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java
index 0cf82d9a521f..4ad12d8a121f 100644
--- a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java
+++ b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java
@@ -237,9 +237,6 @@ public class GitUntrackedFilesHolder implements Disposable, BulkFileListener {
break;
}
String path = event.getPath();
- if (path == null) {
- continue;
- }
if (totalRefreshNeeded(path)) {
allChanged = true;
}
@@ -253,7 +250,7 @@ public class GitUntrackedFilesHolder implements Disposable, BulkFileListener {
// if index has changed, no need to refresh specific files - we get the full status of all files
if (allChanged) {
- LOG.info(String.format("GitUntrackedFilesHolder: Index has changed, marking %s recursively dirty", myRoot));
+ LOG.debug(String.format("GitUntrackedFilesHolder: Index has changed, marking %s recursively dirty", myRoot));
myDirtyScopeManager.dirDirtyRecursively(myRoot);
synchronized (LOCK) {
myReady = false;
diff --git a/plugins/git4idea/src/git4idea/reset/GitNewResetDialog.java b/plugins/git4idea/src/git4idea/reset/GitNewResetDialog.java
new file mode 100644
index 000000000000..5e2cb18e7c7e
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/reset/GitNewResetDialog.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2000-2014 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.reset;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.components.JBRadioButton;
+import com.intellij.util.ui.GridBag;
+import com.intellij.util.ui.RadioButtonEnumModel;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.vcs.log.VcsFullCommitDetails;
+import com.intellij.xml.util.XmlStringUtil;
+import git4idea.GitUtil;
+import git4idea.repo.GitRepository;
+import git4idea.repo.GitRepositoryManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Map;
+
+import static com.intellij.dvcs.DvcsUtil.getShortRepositoryName;
+
+public class GitNewResetDialog extends DialogWrapper {
+
+ private static final String DIALOG_ID = "git.new.reset.dialog";
+
+ @NotNull private final Project myProject;
+ @NotNull private final Map<GitRepository, VcsFullCommitDetails> myCommits;
+ @NotNull private final GitResetMode myDefaultMode;
+ @NotNull private final ButtonGroup myButtonGroup;
+
+ private RadioButtonEnumModel<GitResetMode> myEnumModel;
+
+ protected GitNewResetDialog(@NotNull Project project, @NotNull Map<GitRepository, VcsFullCommitDetails> commits,
+ @NotNull GitResetMode defaultMode) {
+ super(project);
+ myProject = project;
+ myCommits = commits;
+ myDefaultMode = defaultMode;
+ myButtonGroup = new ButtonGroup();
+
+ init();
+ setTitle("Git Reset");
+ setOKButtonText("Reset");
+ setOKButtonMnemonic('R');
+ setResizable(false);
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JPanel panel = new JPanel(new GridBagLayout());
+ GridBag gb = new GridBag().
+ setDefaultAnchor(GridBagConstraints.LINE_START).
+ setDefaultInsets(0, UIUtil.DEFAULT_HGAP, UIUtil.LARGE_VGAP, 0);
+
+ String description = prepareDescription(myProject, myCommits);
+ panel.add(new JBLabel(XmlStringUtil.wrapInHtml(description)), gb.nextLine().next().coverLine());
+
+ String explanation = "This will reset the current branch head to the selected commit, <br/>" +
+ "and update the working tree and the index according to the selected mode:";
+ panel.add(new JBLabel(XmlStringUtil.wrapInHtml(explanation), UIUtil.ComponentStyle.SMALL), gb.nextLine().next().coverLine());
+
+ for (GitResetMode mode : GitResetMode.values()) {
+ JBRadioButton button = new JBRadioButton(mode.getName());
+ button.setMnemonic(mode.getName().charAt(0));
+ myButtonGroup.add(button);
+ panel.add(button, gb.nextLine().next());
+ panel.add(new JBLabel(XmlStringUtil.wrapInHtml(mode.getDescription()), UIUtil.ComponentStyle.SMALL), gb.next());
+ }
+
+ myEnumModel = RadioButtonEnumModel.bindEnum(GitResetMode.class, myButtonGroup);
+ myEnumModel.setSelected(myDefaultMode);
+ return panel;
+ }
+
+ @Nullable
+ @Override
+ protected String getHelpId() {
+ return DIALOG_ID;
+ }
+
+ @NotNull
+ private static String prepareDescription(@NotNull Project project, @NotNull Map<GitRepository, VcsFullCommitDetails> commits) {
+ if (commits.size() == 1 && !isMultiRepo(project)) {
+ Map.Entry<GitRepository, VcsFullCommitDetails> entry = commits.entrySet().iterator().next();
+ return String.format("%s -> %s", getSourceText(entry.getKey()), getTargetText(entry.getValue()));
+ }
+
+ StringBuilder desc = new StringBuilder("");
+ for (Map.Entry<GitRepository, VcsFullCommitDetails> entry : commits.entrySet()) {
+ GitRepository repository = entry.getKey();
+ VcsFullCommitDetails commit = entry.getValue();
+ desc.append(String.format("%s in %s -> %s<br/>", getSourceText(repository),
+ getShortRepositoryName(repository), getTargetText(commit)));
+ }
+ return desc.toString();
+ }
+
+ @NotNull
+ private static String getTargetText(@NotNull VcsFullCommitDetails commit) {
+ String commitMessage = StringUtil.shortenTextWithEllipsis(commit.getSubject(), 20, 0);
+ return String.format("<code><b>%s</b> \"%s\"</code> by <code>%s</code>",
+ commit.getId().toShortString(), commitMessage, commit.getAuthor().getName());
+ }
+
+ @NotNull
+ private static String getSourceText(@NotNull GitRepository repository) {
+ String currentRevision = repository.getCurrentRevision();
+ assert currentRevision != null;
+ String text = repository.getCurrentBranch() == null ?
+ "HEAD (" + GitUtil.getShortHash(currentRevision) + ")" :
+ repository.getCurrentBranch().getName();
+ return "<b>" + text + "</b>";
+ }
+
+ private static boolean isMultiRepo(@NotNull Project project) {
+ return ServiceManager.getService(project, GitRepositoryManager.class).moreThanOneRoot();
+ }
+
+ @NotNull
+ public GitResetMode getResetMode() {
+ return myEnumModel.getSelected();
+ }
+
+}
diff --git a/plugins/git4idea/src/git4idea/reset/GitResetAction.java b/plugins/git4idea/src/git4idea/reset/GitResetAction.java
new file mode 100644
index 000000000000..749acd7e7c7c
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/reset/GitResetAction.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2000-2014 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.reset;
+
+import com.intellij.dvcs.ui.VcsLogOneCommitPerRepoAction;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ObjectUtils;
+import com.intellij.vcs.log.VcsFullCommitDetails;
+import git4idea.config.GitVcsSettings;
+import git4idea.repo.GitRepository;
+import git4idea.repo.GitRepositoryManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+public class GitResetAction extends VcsLogOneCommitPerRepoAction<GitRepository> {
+
+ @Nullable
+ @Override
+ protected GitRepository getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root) {
+ return getRepoManager(project).getRepositoryForRoot(root);
+ }
+
+ @Override
+ protected void actionPerformed(@NotNull final Project project, @NotNull final Map<GitRepository, VcsFullCommitDetails> commits) {
+ GitVcsSettings settings = GitVcsSettings.getInstance(project);
+ GitResetMode defaultMode = ObjectUtils.notNull(settings.getResetMode(), GitResetMode.getDefault());
+ GitNewResetDialog dialog = new GitNewResetDialog(project, commits, defaultMode);
+ dialog.show();
+ if (dialog.isOK()) {
+ final GitResetMode selectedMode = dialog.getResetMode();
+ settings.setResetMode(selectedMode);
+ new Task.Backgroundable(project, "Git reset", false) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ new GitResetOperation(project, commits, selectedMode, indicator).execute();
+ }
+ }.queue();
+ }
+ }
+
+ @NotNull
+ private static GitRepositoryManager getRepoManager(@NotNull Project project) {
+ return ServiceManager.getService(project, GitRepositoryManager.class);
+ }
+
+}
diff --git a/plugins/git4idea/src/git4idea/reset/GitResetMode.java b/plugins/git4idea/src/git4idea/reset/GitResetMode.java
new file mode 100644
index 000000000000..c10d95707073
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/reset/GitResetMode.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2014 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.reset;
+
+import org.jetbrains.annotations.NotNull;
+
+public enum GitResetMode {
+
+ SOFT("Soft", "--soft", "Files won't change, differences will be staged for commit."),
+ MIXED("Mixed", "--mixed", "Files won't change, differences won't be staged."),
+ HARD("Hard", "--hard", "Files will be reverted to the state of the selected commit.<br/>" +
+ "Warning: any local changes will be lost."),
+ KEEP("Keep", "--keep", "Files will be reverted to the state of the selected commit,<br/>" +
+ "but local changes will be kept intact.");
+
+ @NotNull private final String myName;
+ @NotNull private final String myArgument;
+ @NotNull private final String myDescription;
+
+ GitResetMode(@NotNull String name, @NotNull String argument, @NotNull String description) {
+ myName = name;
+ myArgument = argument;
+ myDescription = description;
+ }
+
+ @NotNull
+ public static GitResetMode getDefault() {
+ return MIXED;
+ }
+
+ @NotNull
+ public String getName() {
+ return myName;
+ }
+
+ @NotNull
+ public String getArgument() {
+ return myArgument;
+ }
+
+ @NotNull
+ public String getDescription() {
+ return myDescription;
+ }
+
+}
diff --git a/plugins/git4idea/src/git4idea/reset/GitResetOperation.java b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java
new file mode 100644
index 000000000000..9c66a5225a52
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2000-2014 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.reset;
+
+import com.intellij.dvcs.repo.RepositoryUtil;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vcs.VcsNotifier;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.vcs.log.VcsFullCommitDetails;
+import git4idea.GitPlatformFacade;
+import git4idea.GitUtil;
+import git4idea.branch.GitBranchUiHandlerImpl;
+import git4idea.branch.GitSmartOperationDialog;
+import git4idea.commands.Git;
+import git4idea.commands.GitCommandResult;
+import git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector;
+import git4idea.repo.GitRepository;
+import git4idea.util.GitPreservingProcess;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.RESET;
+
+public class GitResetOperation {
+
+ @NotNull private final Project myProject;
+ @NotNull private final Map<GitRepository, VcsFullCommitDetails> myCommits;
+ @NotNull private final GitResetMode myMode;
+ @NotNull private final ProgressIndicator myIndicator;
+ @NotNull private final Git myGit;
+ @NotNull private final VcsNotifier myNotifier;
+ @NotNull private final GitPlatformFacade myFacade;
+ @NotNull private final GitBranchUiHandlerImpl myUiHandler;
+
+ public GitResetOperation(@NotNull Project project, @NotNull Map<GitRepository, VcsFullCommitDetails> targetCommits,
+ @NotNull GitResetMode mode, @NotNull ProgressIndicator indicator) {
+ myProject = project;
+ myCommits = targetCommits;
+ myMode = mode;
+ myIndicator = indicator;
+ myGit = ServiceManager.getService(Git.class);
+ myNotifier = VcsNotifier.getInstance(project);
+ myFacade = ServiceManager.getService(GitPlatformFacade.class);
+ myUiHandler = new GitBranchUiHandlerImpl(myProject, myFacade, myGit, indicator);
+ }
+
+ public void execute() {
+ saveAllDocuments();
+ GitUtil.workingTreeChangeStarted(myProject);
+ Map<GitRepository, GitCommandResult> results = ContainerUtil.newHashMap();
+ try {
+ for (Map.Entry<GitRepository, VcsFullCommitDetails> entry : myCommits.entrySet()) {
+ GitRepository repository = entry.getKey();
+ VirtualFile root = repository.getRoot();
+ String target = entry.getValue().getId().asString();
+ GitLocalChangesWouldBeOverwrittenDetector detector = new GitLocalChangesWouldBeOverwrittenDetector(root, RESET);
+
+ GitCommandResult result = myGit.reset(repository, myMode, target, detector);
+ if (!result.success() && detector.wasMessageDetected()) {
+ GitCommandResult smartResult = proposeSmartReset(detector, repository, target);
+ if (smartResult != null) {
+ result = smartResult;
+ }
+ }
+ results.put(repository, result);
+ repository.update();
+ VfsUtil.markDirtyAndRefresh(true, true, false, root);
+ }
+ }
+ finally {
+ GitUtil.workingTreeChangeFinished(myProject);
+ }
+ notifyResult(results);
+ }
+
+ private GitCommandResult proposeSmartReset(@NotNull GitLocalChangesWouldBeOverwrittenDetector detector,
+ @NotNull final GitRepository repository, @NotNull final String target) {
+ Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), detector.getRelativeFilePaths());
+ List<Change> affectedChanges = GitUtil.findLocalChangesForPaths(myProject, repository.getRoot(), absolutePaths, false);
+ int choice = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "reset", "&Hard Reset");
+ if (choice == GitSmartOperationDialog.SMART_EXIT_CODE) {
+ final Ref<GitCommandResult> result = Ref.create();
+ new GitPreservingProcess(myProject, myFacade, myGit, Collections.singleton(repository), "reset", target, myIndicator, new Runnable() {
+ @Override
+ public void run() {
+ result.set(myGit.reset(repository, myMode, target));
+ }
+ }).execute();
+ return result.get();
+ }
+ if (choice == GitSmartOperationDialog.FORCE_EXIT_CODE) {
+ return myGit.reset(repository, GitResetMode.HARD, target);
+ }
+ return null;
+ }
+
+ private void notifyResult(@NotNull Map<GitRepository, GitCommandResult> results) {
+ Map<GitRepository, GitCommandResult> successes = ContainerUtil.newHashMap();
+ Map<GitRepository, GitCommandResult> errors = ContainerUtil.newHashMap();
+ for (Map.Entry<GitRepository, GitCommandResult> entry : results.entrySet()) {
+ GitCommandResult result = entry.getValue();
+ GitRepository repository = entry.getKey();
+ if (result.success()) {
+ successes.put(repository, result);
+ }
+ else {
+ errors.put(repository, result);
+ }
+ }
+
+ if (errors.isEmpty()) {
+ myNotifier.notifySuccess("", "Reset successful");
+ }
+ else if (!successes.isEmpty()) {
+ myNotifier.notifyImportantWarning("Reset partially failed",
+ "Reset was successful for " + joinRepos(successes.keySet())
+ + "<br/>but failed for " + joinRepos(errors.keySet()) + ": <br/>" + formErrorReport(errors));
+ }
+ else {
+ myNotifier.notifyError("Reset Failed", formErrorReport(errors));
+ }
+ }
+
+ @NotNull
+ private static String formErrorReport(@NotNull Map<GitRepository, GitCommandResult> errorResults) {
+ MultiMap<String, GitRepository> grouped = groupByResult(errorResults);
+ if (grouped.size() == 1) {
+ return "<code>" + grouped.keySet().iterator().next() + "</code>";
+ }
+ return StringUtil.join(grouped.entrySet(), new Function<Map.Entry<String, Collection<GitRepository>>, String>() {
+ @NotNull
+ @Override
+ public String fun(@NotNull Map.Entry<String, Collection<GitRepository>> entry) {
+ return joinRepos(entry.getValue()) + ":<br/><code>" + entry.getKey() + "</code>";
+ }
+ }, "<br/>");
+ }
+
+ // to avoid duplicate error reports if they are the same for different repositories
+ @NotNull
+ private static MultiMap<String, GitRepository> groupByResult(@NotNull Map<GitRepository, GitCommandResult> results) {
+ MultiMap<String, GitRepository> grouped = MultiMap.create();
+ for (Map.Entry<GitRepository, GitCommandResult> entry : results.entrySet()) {
+ grouped.putValue(entry.getValue().getErrorOutputAsHtmlString(), entry.getKey());
+ }
+ return grouped;
+ }
+
+ @NotNull
+ private static String joinRepos(@NotNull Collection<GitRepository> repositories) {
+ return StringUtil.join(RepositoryUtil.sortRepositories(repositories), ", ");
+ }
+
+ private static void saveAllDocuments() {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ FileDocumentManager.getInstance().saveAllDocuments();
+ }
+ });
+ }
+
+}
diff --git a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java
index 9b13bb6cdb65..eccaa446b0ed 100644
--- a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java
+++ b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java
@@ -72,65 +72,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
* The unstash dialog
*/
public class GitUnstashDialog extends DialogWrapper {
- /**
- * Git root selector
- */
private JComboBox myGitRootComboBox;
- /**
- * The current branch label
- */
private JLabel myCurrentBranch;
- /**
- * The view stash button
- */
private JButton myViewButton;
- /**
- * The drop stash button
- */
private JButton myDropButton;
- /**
- * The clear stashes button
- */
private JButton myClearButton;
- /**
- * The pop stash checkbox
- */
private JCheckBox myPopStashCheckBox;
- /**
- * The branch text field
- */
private JTextField myBranchTextField;
- /**
- * The root panel of the dialog
- */
private JPanel myPanel;
- /**
- * The stash list
- */
private JList myStashList;
- /**
- * If this checkbox is selected, the index is reinstated as well as working tree
- */
private JCheckBox myReinstateIndexCheckBox;
/**
* Set of branches for the current root
*/
private final HashSet<String> myBranches = new HashSet<String>();
- /**
- * The project
- */
private final Project myProject;
private GitVcs myVcs;
private static final Logger LOG = Logger.getInstance(GitUnstashDialog.class);
- /**
- * A constructor
- *
- * @param project the project
- * @param roots the list of the roots
- * @param defaultRoot the default root to select
- */
public GitUnstashDialog(final Project project, final List<VirtualFile> roots, final VirtualFile defaultRoot) {
super(project, true);
setModal(false);
@@ -310,9 +270,6 @@ public class GitUnstashDialog extends DialogWrapper {
setOKActionEnabled(true);
}
- /**
- * Refresh stash list
- */
private void refreshStashList() {
final DefaultListModel listModel = (DefaultListModel)myStashList.getModel();
listModel.clear();
@@ -334,16 +291,10 @@ public class GitUnstashDialog extends DialogWrapper {
myStashList.setSelectedIndex(0);
}
- /**
- * @return the selected git root
- */
private VirtualFile getGitRoot() {
return (VirtualFile)myGitRootComboBox.getSelectedItem();
}
- /**
- * @return unstash handler
- */
private GitLineHandler handler() {
GitLineHandler h = new GitLineHandler(myProject, getGitRoot(), GitCommand.STASH);
String branch = myBranchTextField.getText();
@@ -361,32 +312,19 @@ public class GitUnstashDialog extends DialogWrapper {
return h;
}
- /**
- * @return selected stash
- * @throws NullPointerException if no stash is selected
- */
private StashInfo getSelectedStash() {
return (StashInfo)myStashList.getSelectedValue();
}
- /**
- * {@inheritDoc}
- */
protected JComponent createCenterPanel() {
return myPanel;
}
- /**
- * {@inheritDoc}
- */
@Override
protected String getDimensionServiceKey() {
return getClass().getName();
}
- /**
- * {@inheritDoc}
- */
@Override
protected String getHelpId() {
return "reference.VersionControl.Git.Unstash";
@@ -418,6 +356,7 @@ public class GitUnstashDialog extends DialogWrapper {
final Ref<GitCommandResult> result = Ref.create();
ProgressManager.getInstance().run(new Task.Modal(h.project(), GitBundle.getString("unstash.unstashing"), false) {
public void run(@NotNull final ProgressIndicator indicator) {
+ indicator.setIndeterminate(true);
h.addLineListener(new GitHandlerUtil.GitLineHandlerListenerProgress(indicator, h, "stash", false));
Git git = ServiceManager.getService(Git.class);
result.set(git.runCommand(new Computable.PredefinedValueComputable<GitLineHandler>(h)));
diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchWidget.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchWidget.java
index 80074fb64a28..4d3497fae299 100644
--- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchWidget.java
+++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchWidget.java
@@ -136,7 +136,7 @@ public class GitBranchWidget extends EditorBasedWidget implements StatusBarWidge
@Override
public void run() {
Project project = getProject();
- if (project == null) {
+ if (project == null || project.isDisposed()) {
emptyTextAndTooltip();
return;
}
diff --git a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java
index 4315e65227a3..08c5103007d2 100644
--- a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java
+++ b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java
@@ -19,15 +19,21 @@ import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.VcsException;
-import com.intellij.openapi.vcs.changes.*;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.ContentRevision;
+import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.changes.ui.ChangeListViewerDialog;
import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import git4idea.GitUtil;
import git4idea.branch.GitBranchPair;
@@ -173,9 +179,14 @@ public class GitMergeUpdater extends GitUpdater {
final Collection<String> remotelyChanged = GitUtil.getPathsDiffBetweenRefs(ServiceManager.getService(Git.class), repository,
currentBranch, remoteBranch);
final List<File> locallyChanged = myChangeListManager.getAffectedPaths();
- for (File localPath : locallyChanged) {
- if (remotelyChanged.contains(FilePathsHelper.convertPath(localPath.getPath()))) {
- // found a file which was changed locally and remotely => need to save
+ for (final File localPath : locallyChanged) {
+ if (ContainerUtil.exists(remotelyChanged, new Condition<String>() {
+ @Override
+ public boolean value(String remotelyChangedPath) {
+ return FileUtil.pathsEqual(localPath.getPath(), remotelyChangedPath);
+ }
+ })) {
+ // found a file which was changed locally and remotely => need to save
return true;
}
}