summaryrefslogtreecommitdiff
path: root/plugins/git4idea
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/git4idea')
-rw-r--r--plugins/git4idea/git4idea.iml11
-rw-r--r--plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r.jarbin1619515 -> 0 bytes
-rw-r--r--plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r_source.zipbin1660774 -> 0 bytes
-rw-r--r--plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java152
-rw-r--r--plugins/git4idea/src/META-INF/plugin.xml2
-rw-r--r--plugins/git4idea/src/git4idea/GitUtil.java76
-rw-r--r--plugins/git4idea/src/git4idea/GitVcs.java5
-rw-r--r--plugins/git4idea/src/git4idea/actions/GitMerge.java62
-rw-r--r--plugins/git4idea/src/git4idea/actions/GitMergeAction.java141
-rw-r--r--plugins/git4idea/src/git4idea/actions/GitPull.java90
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchOperation.java51
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java16
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java88
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java6
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java13
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitMergeOperation.java5
-rw-r--r--plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java45
-rw-r--r--plugins/git4idea/src/git4idea/changes/GitChangeUtils.java32
-rw-r--r--plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java10
-rw-r--r--plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java116
-rw-r--r--plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java15
-rw-r--r--plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java55
-rw-r--r--plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java18
-rw-r--r--plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java3
-rw-r--r--plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java4
-rw-r--r--plugins/git4idea/src/git4idea/commands/Git.java4
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitHandlerUtil.java109
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java27
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitImpl.java6
-rw-r--r--plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java5
-rw-r--r--plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java525
-rw-r--r--plugins/git4idea/src/git4idea/jgit/GitHttpCredentialsProvider.java193
-rw-r--r--plugins/git4idea/src/git4idea/jgit/GitHttpRemoteCommand.java507
-rw-r--r--plugins/git4idea/src/git4idea/merge/GitMergeDialog.java30
-rw-r--r--plugins/git4idea/src/git4idea/push/GitPusher.java39
-rw-r--r--plugins/git4idea/src/git4idea/rebase/GitRebaser.java8
-rw-r--r--plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java (renamed from plugins/git4idea/src/git4idea/jgit/GitHttpAuthDataProvider.java)4
-rw-r--r--plugins/git4idea/src/git4idea/repo/GitConfig.java2
-rw-r--r--plugins/git4idea/src/git4idea/repo/GitRepositoryReader.java3
-rw-r--r--plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java2
-rw-r--r--plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java67
-rw-r--r--plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java25
-rw-r--r--plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java27
-rw-r--r--plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java45
-rw-r--r--plugins/git4idea/src/git4idea/update/GitFetcher.java28
-rw-r--r--plugins/git4idea/src/git4idea/update/GitMergeUpdater.java17
-rw-r--r--plugins/git4idea/src/git4idea/util/GitSimplePathsBrowser.java71
-rw-r--r--plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java85
-rw-r--r--plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java33
-rw-r--r--plugins/git4idea/src/org/hanuna/gitalk/git/reader/util/GitException.java11
-rw-r--r--plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy44
-rw-r--r--plugins/git4idea/tests/git4idea/checkin/GitCommitAuthorCorrectorTest.java56
-rw-r--r--plugins/git4idea/tests/git4idea/test/GitPlatformTest.java4
53 files changed, 1100 insertions, 1893 deletions
diff --git a/plugins/git4idea/git4idea.iml b/plugins/git4idea/git4idea.iml
index 75efba73b775..1b6091a77419 100644
--- a/plugins/git4idea/git4idea.iml
+++ b/plugins/git4idea/git4idea.iml
@@ -48,17 +48,6 @@
</SOURCES>
</library>
</orderEntry>
- <orderEntry type="module-library">
- <library name="jgit-2.1.0">
- <CLASSES>
- <root url="jar://$MODULE_DIR$/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r.jar!/" />
- </CLASSES>
- <JAVADOC />
- <SOURCES>
- <root url="jar://$MODULE_DIR$/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r_source.zip!/" />
- </SOURCES>
- </library>
- </orderEntry>
<orderEntry type="library" scope="TEST" name="Groovy" level="project" />
<orderEntry type="module" module-name="dvcs" exported="" />
<orderEntry type="library" scope="TEST" name="cucumber-jvm" level="project" />
diff --git a/plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r.jar b/plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r.jar
deleted file mode 100644
index 2257a22a8fb6..000000000000
--- a/plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r.jar
+++ /dev/null
Binary files differ
diff --git a/plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r_source.zip b/plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r_source.zip
deleted file mode 100644
index 78cb17ebe989..000000000000
--- a/plugins/git4idea/lib/jgit/org.eclipse.jgit-2.1.0.201209190230-r_source.zip
+++ /dev/null
Binary files differ
diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java
index 43a1c6cbe349..874360a86c5b 100644
--- a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java
+++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java
@@ -5,14 +5,13 @@ import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vcs.AbstractVcsHelper;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.*;
+import com.intellij.openapi.vcs.changes.ui.CommitChangeListDialog;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
@@ -30,12 +29,15 @@ import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import git4idea.util.GitFileUtils;
+import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -48,9 +50,79 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
private static final String COMMIT_MESSAGE = "Deploy";
+ private static final CommitSession NO_COMMIT = new CommitSession() {
+
+ @Nullable
+ @Override
+ public JComponent getAdditionalConfigurationUI() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public JComponent getAdditionalConfigurationUI(Collection<Change> changes, String commitMessage) {
+ return null;
+ }
+
+ @Override
+ public boolean canExecute(Collection<Change> changes, String commitMessage) {
+ return true;
+ }
+
+ @Override
+ public void execute(Collection<Change> changes, String commitMessage) {
+
+ }
+
+ @Override
+ public void executionCanceled() {
+
+ }
+
+ @Override
+ public String getHelpId() {
+ return null;
+ }
+ };
+
+ private static final List<CommitExecutor> ourCommitExecutors = Arrays.asList(
+ new CommitExecutor() {
+
+ @Nls
+ @Override
+ public String getActionText() {
+ return "Commit and Push";
+ }
+
+ @NotNull
+ @Override
+ public CommitSession createCommitSession() {
+ return CommitSession.VCS_COMMIT;
+ }
+ },
+ new CommitExecutorBase() {
+
+ @Nls
+ @Override
+ public String getActionText() {
+ return "Push without Commit";
+ }
+
+ @NotNull
+ @Override
+ public CommitSession createCommitSession() {
+ return NO_COMMIT;
+ }
+
+ @Override
+ public boolean areChangesRequired() {
+ return false;
+ }
+ }
+ );
+
private final GitRepositoryManager myGitRepositoryManager;
private final Git myGit;
- private final AbstractVcsHelper myVcsHelper;
private final VirtualFile myContentRoot;
private final File myRepositoryRootFile;
@@ -87,7 +159,6 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
throw new ServerRuntimeException("Can't initialize GIT");
}
GitPlatformFacade gitPlatformFacade = ServiceManager.getService(GitPlatformFacade.class);
- myVcsHelper = gitPlatformFacade.getVcsHelper(project);
myChangeListManager = gitPlatformFacade.getChangeListManager(project);
}
@@ -124,32 +195,6 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
relevantChanges.add(change);
}
}
- if (relevantChanges.isEmpty()) {
- Integer showCommitDialogChoice = runOnEdt(new Computable<Integer>() {
-
- @Override
- public Integer compute() {
- return Messages.showYesNoCancelDialog(getProject(),
- "Active changelist does not contain a relevant change.\n"
- + "Do you want to show commit dialog anyway?\n\n"
- + "Yes - show commit dialog\n"
- + "No - push immediately\n"
- + "Cancel - interrupt deploy",
- "Deploy",
- Messages.getWarningIcon());
- }
- });
- if (showCommitDialogChoice == Messages.YES) {
- relevantChanges.addAll(changes);
- }
- else if (showCommitDialogChoice == Messages.NO) {
- pushApplication(application);
- return;
- }
- else {
- throw new ServerRuntimeException("Deploy interrupted");
- }
- }
final Semaphore commitSemaphore = new Semaphore();
commitSemaphore.down();
@@ -159,20 +204,26 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
@Override
public Boolean compute() {
- return myVcsHelper.commitChanges(relevantChanges, activeChangeList, COMMIT_MESSAGE,
- new CommitResultHandler() {
-
- @Override
- public void onSuccess(@NotNull String commitMessage) {
- commitSucceeded.set(true);
- commitSemaphore.up();
- }
-
- @Override
- public void onFailure() {
- commitSemaphore.up();
- }
- });
+ return CommitChangeListDialog.commitChanges(getProject(),
+ relevantChanges,
+ activeChangeList,
+ ourCommitExecutors,
+ false,
+ COMMIT_MESSAGE,
+ new CommitResultHandler() {
+
+ @Override
+ public void onSuccess(@NotNull String commitMessage) {
+ commitSucceeded.set(true);
+ commitSemaphore.up();
+ }
+
+ @Override
+ public void onFailure() {
+ commitSemaphore.up();
+ }
+ },
+ false);
}
});
if (commitStarted != null && commitStarted) {
@@ -183,7 +234,7 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
}
}
else {
- getLoggingHandler().println("Commit canceled");
+ throw new ServerRuntimeException("Deploy interrupted");
}
repository.update();
@@ -211,17 +262,6 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
return result.get();
}
- public void undeploy() throws ServerRuntimeException {
- getAgentTaskExecutor().execute(new Computable<Object>() {
-
- @Override
- public Object compute() {
- getDeployment().deleteApplication();
- return null;
- }
- });
- }
-
public boolean isDeployed() throws ServerRuntimeException {
return findApplication() != null;
}
diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml
index 80fa47fa1a86..32d254f6fb67 100644
--- a/plugins/git4idea/src/META-INF/plugin.xml
+++ b/plugins/git4idea/src/META-INF/plugin.xml
@@ -209,6 +209,6 @@
</extensions>
<extensionPoints>
- <extensionPoint qualifiedName="Git4Idea.GitHttpAuthDataProvider" interface="git4idea.jgit.GitHttpAuthDataProvider"/>
+ <extensionPoint qualifiedName="Git4Idea.GitHttpAuthDataProvider" interface="git4idea.remote.GitHttpAuthDataProvider"/>
</extensionPoints>
</idea-plugin>
diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java
index 7204f61aa0ab..4286aee1536b 100644
--- a/plugins/git4idea/src/git4idea/GitUtil.java
+++ b/plugins/git4idea/src/git4idea/GitUtil.java
@@ -23,6 +23,8 @@ 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.ex.MultiLineLabel;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -30,6 +32,9 @@ import com.intellij.openapi.vcs.AbstractVcsHelper;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
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;
@@ -37,6 +42,7 @@ import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.VcsFileUtil;
import com.intellij.vcsUtil.VcsUtil;
@@ -50,6 +56,7 @@ import git4idea.repo.GitBranchTrackInfo;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
+import git4idea.util.GitSimplePathsBrowser;
import git4idea.util.GitUIUtil;
import git4idea.util.StringScanner;
import org.jetbrains.annotations.NotNull;
@@ -934,4 +941,73 @@ public class GitUtil {
return !output.trim().isEmpty();
}
+ @Nullable
+ public static VirtualFile findRefreshFileOrLog(@NotNull String absolutePath) {
+ VirtualFile file = LocalFileSystem.getInstance().findFileByPath(absolutePath);
+ if (file == null) {
+ file = LocalFileSystem.getInstance().refreshAndFindFileByPath(absolutePath);
+ }
+ if (file == null) {
+ LOG.warn("VirtualFile not found for " + absolutePath);
+ }
+ return file;
+ }
+
+ @NotNull
+ public static String toAbsolute(@NotNull VirtualFile root, @NotNull String relativePath) {
+ return StringUtil.trimEnd(root.getPath(), "/") + "/" + StringUtil.trimStart(relativePath, "/");
+ }
+
+ @NotNull
+ public static Collection<String> toAbsolute(@NotNull final VirtualFile root, @NotNull Collection<String> relativePaths) {
+ return ContainerUtil.map(relativePaths, new Function<String, String>() {
+ @Override
+ public String fun(String s) {
+ return toAbsolute(root, s);
+ }
+ });
+ }
+
+ /**
+ * Given the list of paths converts them to the list of {@link Change Changes} found in the {@link ChangeListManager},
+ * i.e. this works only for local changes. </br>
+ * Paths can be absolute or relative to the repository.
+ * If a path is not found in the local changes, it is ignored, but the fact is logged.
+ */
+ @NotNull
+ public static List<Change> findLocalChangesForPaths(@NotNull Project project, @NotNull VirtualFile root,
+ @NotNull Collection<String> affectedPaths, boolean relativePaths) {
+ ChangeListManagerEx changeListManager = (ChangeListManagerEx)ChangeListManager.getInstance(project);
+ List<Change> affectedChanges = new ArrayList<Change>();
+ for (String path : affectedPaths) {
+ String absolutePath = relativePaths ? toAbsolute(root, path) : path;
+ VirtualFile file = findRefreshFileOrLog(absolutePath);
+ if (file != null) {
+ Change change = changeListManager.getChange(file);
+ if (change != null) {
+ affectedChanges.add(change);
+ }
+ else {
+ String message = "Change is not found for " + file.getPath();
+ if (changeListManager.isInUpdate()) {
+ message += " because ChangeListManager is being updated.";
+ }
+ LOG.warn(message);
+ }
+ }
+ }
+ return affectedChanges;
+ }
+
+ public static void showPathsInDialog(@NotNull Project project, @NotNull Collection<String> absolutePaths, @NotNull String title,
+ @Nullable String description) {
+ DialogBuilder builder = new DialogBuilder(project);
+ builder.setCenterPanel(new GitSimplePathsBrowser(project, absolutePaths));
+ if (description != null) {
+ builder.setNorthPanel(new MultiLineLabel(description));
+ }
+ builder.addOkAction();
+ builder.setTitle(title);
+ builder.show();
+ }
}
diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java
index 6d1762117c0a..5b3aa4dc0f97 100644
--- a/plugins/git4idea/src/git4idea/GitVcs.java
+++ b/plugins/git4idea/src/git4idea/GitVcs.java
@@ -56,6 +56,7 @@ import com.intellij.util.containers.ComparatorDelegate;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.VcsLog;
+import com.intellij.vcs.log.VcsUserRegistry;
import git4idea.annotate.GitAnnotationProvider;
import git4idea.annotate.GitRepositoryForAnnotationsListener;
import git4idea.changes.GitCommittedChangeListProvider;
@@ -68,7 +69,6 @@ import git4idea.config.*;
import git4idea.diff.GitDiffProvider;
import git4idea.diff.GitTreeDiffProvider;
import git4idea.history.GitHistoryProvider;
-import git4idea.history.NewGitUsersComponent;
import git4idea.history.browser.GitHeavyCommit;
import git4idea.history.browser.GitProjectLogManager;
import git4idea.history.wholeTree.GitCommitDetailsProvider;
@@ -328,7 +328,7 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
if (myVFSListener == null) {
myVFSListener = new GitVFSListener(myProject, this, myGit);
}
- NewGitUsersComponent.getInstance(myProject).activate();
+ ServiceManager.getService(myProject, VcsUserRegistry.class); // make sure to read the registry before opening commit dialog
if (!Registry.is("git.new.log")) {
GitProjectLogManager.getInstance(myProject).activate();
}
@@ -368,7 +368,6 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
Disposer.dispose(myVFSListener);
myVFSListener = null;
}
- NewGitUsersComponent.getInstance(myProject).deactivate();
GitProjectLogManager.getInstance(myProject).deactivate();
if (myBranchWidget != null) {
diff --git a/plugins/git4idea/src/git4idea/actions/GitMerge.java b/plugins/git4idea/src/git4idea/actions/GitMerge.java
index a19568cb4912..5eb1714f6d7c 100644
--- a/plugins/git4idea/src/git4idea/actions/GitMerge.java
+++ b/plugins/git4idea/src/git4idea/actions/GitMerge.java
@@ -15,54 +15,36 @@
*/
package git4idea.actions;
-import com.intellij.history.Label;
-import com.intellij.history.LocalHistory;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.VcsException;
-import com.intellij.openapi.vcs.update.ActionInfo;
import com.intellij.openapi.vfs.VirtualFile;
-import git4idea.GitRevisionNumber;
-import git4idea.GitUtil;
import git4idea.GitVcs;
-import git4idea.commands.GitHandlerUtil;
import git4idea.commands.GitLineHandler;
import git4idea.i18n.GitBundle;
import git4idea.merge.GitMergeDialog;
-import git4idea.merge.GitMergeUtil;
-import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
-import java.util.Set;
-/**
- * Git "merge" action
- */
-public class GitMerge extends GitRepositoryAction {
+public class GitMerge extends GitMergeAction {
- /**
- * {@inheritDoc}
- */
@Override
@NotNull
protected String getActionName() {
return GitBundle.getString("merge.action.name");
}
- /**
- * {@inheritDoc}
- */
- protected void perform(@NotNull final Project project,
- @NotNull final List<VirtualFile> gitRoots,
- @NotNull final VirtualFile defaultRoot,
- final Set<VirtualFile> affectedRoots,
- final List<VcsException> exceptions) throws VcsException {
+ @Nullable
+ @Override
+ protected DialogState displayDialog(@NotNull Project project, @NotNull List<VirtualFile> gitRoots, @NotNull VirtualFile defaultRoot) {
GitVcs vcs = GitVcs.getInstance(project);
if (vcs == null) {
- return;
+ return null;
}
- GitMergeDialog dialog = new GitMergeDialog(project, gitRoots, defaultRoot);
+ final GitMergeDialog dialog = new GitMergeDialog(project, gitRoots, defaultRoot);
try {
dialog.updateBranches();
}
@@ -70,28 +52,18 @@ public class GitMerge extends GitRepositoryAction {
if (vcs.getExecutableValidator().checkExecutableAndShowMessageIfNeeded(null)) {
vcs.showErrors(Collections.singletonList(e), GitBundle.getString("merge.retrieving.branches"));
}
- return;
+ return null;
}
dialog.show();
if (!dialog.isOK()) {
- return;
- }
- Label beforeLabel = LocalHistory.getInstance().putSystemLabel(project, "Before update");
- GitLineHandler h = dialog.handler();
- final VirtualFile root = dialog.getSelectedRoot();
- affectedRoots.add(root);
- GitRevisionNumber currentRev = GitRevisionNumber.resolve(project, root, "HEAD");
- try {
- GitHandlerUtil.doSynchronously(h, GitBundle.message("merging.title", dialog.getSelectedRoot().getPath()), h.printableCommandLine());
+ return null;
}
- finally {
- exceptions.addAll(h.errors());
- GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
- manager.updateRepository(root);
- }
- if (exceptions.size() != 0) {
- return;
- }
- GitMergeUtil.showUpdates(this, project, exceptions, root, currentRev, beforeLabel, getActionName(), ActionInfo.INTEGRATE);
+ return new DialogState(dialog.getSelectedRoot(), GitBundle.message("merging.title", dialog.getSelectedRoot().getPath()),
+ new Computable<GitLineHandler>() {
+ @Override
+ public GitLineHandler compute() {
+ return dialog.handler();
+ }
+ });
}
}
diff --git a/plugins/git4idea/src/git4idea/actions/GitMergeAction.java b/plugins/git4idea/src/git4idea/actions/GitMergeAction.java
new file mode 100644
index 000000000000..1d46335cc1b1
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/actions/GitMergeAction.java
@@ -0,0 +1,141 @@
+/*
+ * 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.actions;
+
+import com.intellij.history.Label;
+import com.intellij.history.LocalHistory;
+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.util.Computable;
+import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.update.ActionInfo;
+import com.intellij.openapi.vfs.VirtualFile;
+import git4idea.GitRevisionNumber;
+import git4idea.GitUtil;
+import git4idea.GitVcs;
+import git4idea.commands.*;
+import git4idea.merge.GitMergeUtil;
+import git4idea.repo.GitRepository;
+import git4idea.repo.GitRepositoryManager;
+import git4idea.util.GitUIUtil;
+import git4idea.util.LocalChangesWouldBeOverwrittenHelper;
+import git4idea.util.UntrackedFilesNotifier;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.MERGE;
+
+abstract class GitMergeAction extends GitRepositoryAction {
+
+ protected static class DialogState {
+ final VirtualFile selectedRoot;
+ final String progressTitle;
+ final Computable<GitLineHandler> handlerProvider;
+ DialogState(@NotNull VirtualFile root, @NotNull String title, @NotNull Computable<GitLineHandler> provider) {
+ selectedRoot = root;
+ progressTitle = title;
+ handlerProvider = provider;
+ }
+ }
+
+ @Nullable
+ protected abstract DialogState displayDialog(@NotNull Project project, @NotNull List<VirtualFile> gitRoots,
+ @NotNull VirtualFile defaultRoot);
+
+ protected void perform(@NotNull final Project project,
+ @NotNull final List<VirtualFile> gitRoots,
+ @NotNull final VirtualFile defaultRoot,
+ final Set<VirtualFile> affectedRoots,
+ final List<VcsException> exceptions) throws VcsException {
+ final DialogState dialogState = displayDialog(project, gitRoots, defaultRoot);
+ if (dialogState == null) {
+ return;
+ }
+ final VirtualFile selectedRoot = dialogState.selectedRoot;
+ final Computable<GitLineHandler> handlerProvider = dialogState.handlerProvider;
+ final Label beforeLabel = LocalHistory.getInstance().putSystemLabel(project, "Before update");
+
+ new Task.Backgroundable(project, dialogState.progressTitle, false) {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ final GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project);
+ final Git git = ServiceManager.getService(Git.class);
+ final GitLocalChangesWouldBeOverwrittenDetector localChangesDetector =
+ new GitLocalChangesWouldBeOverwrittenDetector(selectedRoot, MERGE);
+ final GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector =
+ new GitUntrackedFilesOverwrittenByOperationDetector(selectedRoot);
+ GitCommandResult result = git.runCommand(new Computable<GitLineHandler>() {
+ @Override
+ public GitLineHandler compute() {
+ GitLineHandler handler = handlerProvider.compute();
+ handler.addLineListener(localChangesDetector);
+ handler.addLineListener(untrackedFilesDetector);
+ return handler;
+ }
+ });
+ affectedRoots.add(selectedRoot);
+
+ GitRepository repository = repositoryManager.getRepositoryForRoot(selectedRoot);
+ assert repository != null : "Repository can't be null for root " + selectedRoot;
+ String revision = repository.getCurrentRevision();
+ if (revision == null) {
+ return;
+ }
+ final GitRevisionNumber currentRev = new GitRevisionNumber(revision);
+ handleResult(result, project, localChangesDetector, untrackedFilesDetector, repository, currentRev, affectedRoots, beforeLabel);
+ }
+
+ }.queue();
+ }
+
+ private void handleResult(GitCommandResult result, Project project, GitLocalChangesWouldBeOverwrittenDetector localChangesDetector,
+ GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector, GitRepository repository,
+ GitRevisionNumber currentRev, Set<VirtualFile> affectedRoots, Label beforeLabel) {
+ GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project);
+ VirtualFile root = repository.getRoot();
+ if (result.success()) {
+ root.refresh(false, true);
+ List<VcsException> exceptions = new ArrayList<VcsException>();
+ GitMergeUtil.showUpdates(this, project, exceptions, root, currentRev, beforeLabel, getActionName(), ActionInfo.UPDATE);
+ repositoryManager.updateRepository(root);
+ runFinalTasks(project, GitVcs.getInstance(project), affectedRoots, getActionName(), exceptions);
+ }
+ else if (localChangesDetector.wasMessageDetected()) {
+ LocalChangesWouldBeOverwrittenHelper.showErrorNotification(project, repository.getRoot(), getActionName(),
+ localChangesDetector.getRelativeFilePaths());
+ }
+ else if (untrackedFilesDetector.wasMessageDetected()) {
+ UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(project, root, untrackedFilesDetector.getRelativeFilePaths(),
+ getActionName(), null);
+ }
+ else {
+ GitUIUtil.notifyError(project, "Git " + getActionName() + " Failed", result.getErrorOutputAsJoinedString(), true, null);
+ repositoryManager.updateRepository(root);
+ }
+ }
+
+ @Override
+ protected boolean executeFinalTasksSynchronously() {
+ return false;
+ }
+
+}
diff --git a/plugins/git4idea/src/git4idea/actions/GitPull.java b/plugins/git4idea/src/git4idea/actions/GitPull.java
index 1831be7c37d9..1df7e93abf7e 100644
--- a/plugins/git4idea/src/git4idea/actions/GitPull.java
+++ b/plugins/git4idea/src/git4idea/actions/GitPull.java
@@ -15,38 +15,21 @@
*/
package git4idea.actions;
-import com.intellij.history.Label;
-import com.intellij.history.LocalHistory;
-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.util.Computable;
-import com.intellij.openapi.vcs.VcsException;
-import com.intellij.openapi.vcs.update.ActionInfo;
import com.intellij.openapi.vfs.VirtualFile;
-import git4idea.GitRevisionNumber;
import git4idea.GitUtil;
-import git4idea.GitVcs;
-import git4idea.commands.Git;
-import git4idea.commands.GitCommandResult;
import git4idea.commands.GitLineHandler;
import git4idea.i18n.GitBundle;
-import git4idea.merge.GitMergeUtil;
import git4idea.merge.GitPullDialog;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
-import git4idea.util.GitUIUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
-import java.util.Set;
-/**
- * Git "pull" action
- */
-public class GitPull extends GitRepositoryAction {
+public class GitPull extends GitMergeAction {
@Override
@NotNull
@@ -54,64 +37,33 @@ public class GitPull extends GitRepositoryAction {
return GitBundle.getString("pull.action.name");
}
- protected void perform(@NotNull final Project project,
- @NotNull final List<VirtualFile> gitRoots,
- @NotNull final VirtualFile defaultRoot,
- final Set<VirtualFile> affectedRoots,
- final List<VcsException> exceptions) throws VcsException {
+ @Override
+ protected DialogState displayDialog(@NotNull Project project, @NotNull List<VirtualFile> gitRoots,
+ @NotNull VirtualFile defaultRoot) {
final GitPullDialog dialog = new GitPullDialog(project, gitRoots, defaultRoot);
dialog.show();
if (!dialog.isOK()) {
- return;
+ return null;
}
- final Label beforeLabel = LocalHistory.getInstance().putSystemLabel(project, "Before update");
-
- new Task.Backgroundable(project, GitBundle.message("pulling.title", dialog.getRemote()), true) {
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- final GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project);
- GitRepository repository = repositoryManager.getRepositoryForRoot(dialog.gitRoot());
- assert repository != null : "Repository can't be null for root " + dialog.gitRoot();
- String remoteOrUrl = dialog.getRemote();
-
-
- GitRemote remote = GitUtil.findRemoteByName(repository, remoteOrUrl);
- final String url = (remote == null) ? remoteOrUrl : remote.getFirstUrl();
- if (url == null) {
- return;
- }
+ GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project);
+ GitRepository repository = repositoryManager.getRepositoryForRoot(dialog.gitRoot());
+ assert repository != null : "Repository can't be null for root " + dialog.gitRoot();
+ String remoteOrUrl = dialog.getRemote();
+
+ GitRemote remote = GitUtil.findRemoteByName(repository, remoteOrUrl);
+ final String url = (remote == null) ? remoteOrUrl : remote.getFirstUrl();
+ if (url == null) {
+ return null;
+ }
- final Git git = ServiceManager.getService(Git.class);
- GitCommandResult result = git.runRemoteCommand(new Computable<GitLineHandler>() {
- @Override
- public GitLineHandler compute() {
- return dialog.makeHandler(url);
- }
- });
- final VirtualFile root = dialog.gitRoot();
- affectedRoots.add(root);
- String revision = repository.getCurrentRevision();
- if (revision == null) {
- return;
- }
- final GitRevisionNumber currentRev = new GitRevisionNumber(revision);
- if (result.success()) {
- root.refresh(false, true);
- GitMergeUtil.showUpdates(GitPull.this, project, exceptions, root, currentRev, beforeLabel, getActionName(), ActionInfo.UPDATE);
- repositoryManager.updateRepository(root);
- runFinalTasks(project, GitVcs.getInstance(project), affectedRoots, getActionName(), exceptions);
- }
- else {
- GitUIUtil.notifyError(project, "Error pulling " + dialog.getRemote(), result.getErrorOutputAsJoinedString(), true, null);
- repositoryManager.updateRepository(root);
- }
+ Computable<GitLineHandler> handlerProvider = new Computable<GitLineHandler>() {
+ @Override
+ public GitLineHandler compute() {
+ return dialog.makeHandler(url);
}
- }.queue();
+ };
+ return new DialogState(dialog.gitRoot(), GitBundle.message("pulling.title", dialog.getRemote()), handlerProvider);
}
- @Override
- protected boolean executeFinalTasksSynchronously() {
- return false;
- }
}
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
index b65d4cc6097e..7c2f77eeebd2 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
@@ -19,7 +19,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsNotifier;
@@ -261,21 +260,21 @@ abstract class GitBranchOperation {
* If some repositories succeeded, shows a dialog with the list of these files and a proposal to rollback the operation of those
* repositories.
*/
- protected void fatalUntrackedFilesError(@NotNull Collection<VirtualFile> untrackedFiles) {
+ protected void fatalUntrackedFilesError(@NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
if (wereSuccessful()) {
- showUntrackedFilesDialogWithRollback(untrackedFiles);
+ showUntrackedFilesDialogWithRollback(root, relativePaths);
}
else {
- showUntrackedFilesNotification(untrackedFiles);
+ showUntrackedFilesNotification(root, relativePaths);
}
}
- private void showUntrackedFilesNotification(@NotNull Collection<VirtualFile> untrackedFiles) {
- myUiHandler.showUntrackedFilesNotification(getOperationName(), untrackedFiles);
+ private void showUntrackedFilesNotification(@NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
+ myUiHandler.showUntrackedFilesNotification(getOperationName(), root, relativePaths);
}
- private void showUntrackedFilesDialogWithRollback(@NotNull Collection<VirtualFile> untrackedFiles) {
- boolean ok = myUiHandler.showUntrackedFilesDialogWithRollback(getOperationName(), getRollbackProposal(), untrackedFiles);
+ private void showUntrackedFilesDialogWithRollback(@NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
+ boolean ok = myUiHandler.showUntrackedFilesDialogWithRollback(getOperationName(), getRollbackProposal(), root, relativePaths);
if (ok) {
rollback();
}
@@ -293,7 +292,7 @@ abstract class GitBranchOperation {
for (GitRepository repository : repositories) {
try {
Collection<String> diff = GitUtil.getPathsDiffBetweenRefs(myGit, repository, currentBranch, otherBranch);
- List<Change> changesInRepo = convertPathsToChanges(repository, diff, false);
+ List<Change> changesInRepo = GitUtil.findLocalChangesForPaths(myProject, repository.getRoot(), diff, false);
if (!changesInRepo.isEmpty()) {
changes.put(repository, changesInRepo);
}
@@ -324,7 +323,9 @@ abstract class GitBranchOperation {
String currentBranch, String nextBranch) {
// get changes overwritten by checkout from the error message captured from Git
- List<Change> affectedChanges = convertPathsToChanges(currentRepository, localChangesOverwrittenBy.getRelativeFilePaths(), true);
+ List<Change> affectedChanges = GitUtil.findLocalChangesForPaths(myProject, currentRepository.getRoot(),
+ localChangesOverwrittenBy.getRelativeFilePaths(), true
+ );
// get all other conflicting changes
// get changes in all other repositories (except those which already have succeeded) to avoid multiple dialogs proposing smart checkout
Map<GitRepository, List<Change>> conflictingChangesInRepositories =
@@ -339,34 +340,4 @@ abstract class GitBranchOperation {
return Pair.create(allConflictingRepositories, affectedChanges);
}
-
- /**
- * Given the list of paths converts them to the list of {@link com.intellij.openapi.vcs.changes.Change Changes} found in the {@link com.intellij.openapi.vcs.changes.ChangeListManager},
- * i.e. this works only for local changes.
- * Paths can be absolute or relative to the repository.
- * If a path is not in the local changes, it is ignored.
- */
- @NotNull
- private List<Change> convertPathsToChanges(@NotNull GitRepository repository,
- @NotNull Collection<String> affectedPaths, boolean relativePaths) {
- List<Change> affectedChanges = new ArrayList<Change>();
- for (String path : affectedPaths) {
- VirtualFile file;
- if (relativePaths) {
- file = repository.getRoot().findFileByRelativePath(FileUtil.toSystemIndependentName(path));
- }
- else {
- file = myFacade.getVirtualFileByPath(path);
- }
-
- if (file != null) {
- Change change = myFacade.getChangeListManager(myProject).getChange(file);
- if (change != null) {
- affectedChanges.add(change);
- }
- }
- }
- return affectedChanges;
- }
-
}
diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
index 9a153bb9dfed..3b694c455c7b 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java
@@ -58,22 +58,24 @@ public interface GitBranchUiHandler {
/**
* Show notification about "untracked files would be overwritten by merge/checkout".
- * @param untrackedFiles
*/
- void showUntrackedFilesNotification(@NotNull String operationName, @NotNull Collection<VirtualFile> untrackedFiles);
+ void showUntrackedFilesNotification(@NotNull String operationName, @NotNull VirtualFile root, @NotNull Collection<String> relativePaths);
boolean showUntrackedFilesDialogWithRollback(@NotNull String operationName, @NotNull String rollbackProposal,
- @NotNull Collection<VirtualFile> untrackedFiles);
+ @NotNull VirtualFile root, @NotNull Collection<String> relativePaths);
/**
* 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 operation operation name
- * @param force 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 isForcePossible can the operation be executed force (force checkout is possible, force merge - not).
* @return the code of the decision.
*/
- int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull String operation, boolean force);
+ int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull Collection<String> paths,
+ @NotNull String operation, boolean isForcePossible);
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 0c1878a5ec82..cea2b55596e0 100644
--- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
+++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java
@@ -19,14 +19,19 @@ import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.MultiLineLabelUI;
import com.intellij.openapi.ui.VerticalFlowLayout;
+import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ui.SelectFilesDialog;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.components.JBLabel;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
import git4idea.GitCommit;
@@ -36,12 +41,15 @@ import git4idea.MessageManager;
import git4idea.commands.Git;
import git4idea.merge.GitConflictResolver;
import git4idea.repo.GitRepository;
+import git4idea.ui.ChangesBrowserWithRollback;
+import git4idea.util.GitSimplePathsBrowser;
import git4idea.util.UntrackedFilesNotifier;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
+import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -119,26 +127,41 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
}
@Override
- public void showUntrackedFilesNotification(@NotNull String operationName, @NotNull Collection<VirtualFile> untrackedFiles) {
- UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject,
- untrackedFiles, operationName, null);
+ public void showUntrackedFilesNotification(@NotNull String operationName, @NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
+ UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root, relativePaths, operationName, null);
}
@Override
- public boolean showUntrackedFilesDialogWithRollback(@NotNull String operationName, @NotNull String rollbackProposal,
- @NotNull Collection<VirtualFile> untrackedFiles) {
- String title = "Could not " + StringUtil.capitalize(operationName);
- String description = UntrackedFilesNotifier.createUntrackedFilesOverwrittenDescription(operationName, false);
+ public boolean showUntrackedFilesDialogWithRollback(@NotNull String operationName, @NotNull final String rollbackProposal,
+ @NotNull VirtualFile root, @NotNull final Collection<String> relativePaths) {
+ final String title = "Could not " + StringUtil.capitalize(operationName);
+ final String description = UntrackedFilesNotifier.createUntrackedFilesOverwrittenDescription(operationName, false);
- final SelectFilesDialog dialog = new UntrackedFilesDialog(myProject, untrackedFiles, StringUtil.stripHtml(description, true), rollbackProposal);
- dialog.setTitle(title);
- UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ final Collection<String> absolutePaths = GitUtil.toAbsolute(root, relativePaths);
+ final List<VirtualFile> untrackedFiles = ContainerUtil.mapNotNull(absolutePaths, new Function<String, VirtualFile>() {
@Override
- public void run() {
+ public VirtualFile fun(String absolutePath) {
+ return GitUtil.findRefreshFileOrLog(absolutePath);
+ }
+ });
+
+ return UIUtil.invokeAndWaitIfNeeded(new Computable<Boolean>() {
+ @Override
+ public Boolean compute() {
+ JComponent filesBrowser;
+ if (untrackedFiles.isEmpty()) {
+ filesBrowser = new GitSimplePathsBrowser(myProject, absolutePaths);
+ }
+ else {
+ filesBrowser = new SelectFilesDialog.VirtualFileList(myProject, untrackedFiles, false, false);
+ }
+ DialogWrapper dialog = new UntrackedFilesRollBackDialog(myProject, filesBrowser, StringUtil.stripHtml(description, true),
+ rollbackProposal);
+ dialog.setTitle(title);
myFacade.showDialog(dialog);
+ return dialog.isOK();
}
});
- return dialog.isOK();
}
@NotNull
@@ -148,8 +171,16 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
}
@Override
- public int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull String operation, boolean force) {
- return GitSmartOperationDialog.showAndGetAnswer(myProject, changes, operation, true);
+ public int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull Collection<String> paths,
+ @NotNull String operation, boolean isForcePossible) {
+ JComponent fileBrowser;
+ if (!changes.isEmpty()) {
+ fileBrowser = new ChangesBrowserWithRollback(project, changes);
+ }
+ else {
+ fileBrowser = new GitSimplePathsBrowser(project, paths);
+ }
+ return GitSmartOperationDialog.showAndGetAnswer(myProject, fileBrowser, operation, isForcePossible);
}
@Override
@@ -177,13 +208,17 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
"After resolving conflicts you also probably would want to commit your files to the current branch.";
}
- private static class UntrackedFilesDialog extends SelectFilesDialog {
+ private static class UntrackedFilesRollBackDialog extends DialogWrapper {
+ @NotNull private final JComponent myFilesBrowser;
+ @NotNull private final String myPrompt;
@NotNull private final String myRollbackProposal;
- public UntrackedFilesDialog(@NotNull Project project, @NotNull Collection<VirtualFile> originalFiles, @NotNull String prompt,
- @NotNull String rollbackProposal) {
- super(project, new ArrayList<VirtualFile>(originalFiles), prompt, null, false, false, false);
+ public UntrackedFilesRollBackDialog(@NotNull Project project, @NotNull JComponent filesBrowser, @NotNull String prompt,
+ @NotNull String rollbackProposal) {
+ super(project);
+ myFilesBrowser = filesBrowser;
+ myPrompt = prompt;
myRollbackProposal = rollbackProposal;
setOKButtonText("Rollback");
setCancelButtonText("Don't rollback");
@@ -198,5 +233,20 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler {
panel.add(buttons);
return panel;
}
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ return myFilesBrowser;
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createNorthPanel() {
+ JLabel label = new JLabel(myPrompt);
+ label.setUI(new MultiLineLabelUI());
+ label.setBorder(new EmptyBorder(5, 1, 5, 1));
+ return label;
+ }
}
}
diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
index a48582680fa0..d49dc15827b7 100644
--- a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
@@ -23,6 +23,7 @@ import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import git4idea.GitPlatformFacade;
+import git4idea.GitUtil;
import git4idea.commands.*;
import git4idea.repo.GitRepository;
import git4idea.util.GitPreservingProcess;
@@ -90,7 +91,7 @@ class GitCheckoutOperation extends GitBranchOperation {
}
}
else if (untrackedOverwrittenByCheckout.wasMessageDetected()) {
- fatalUntrackedFilesError(untrackedOverwrittenByCheckout.getFiles());
+ fatalUntrackedFilesError(repository.getRoot(), untrackedOverwrittenByCheckout.getRelativeFilePaths());
fatalErrorHappened = true;
}
else {
@@ -112,7 +113,8 @@ class GitCheckoutOperation extends GitBranchOperation {
List<GitRepository> allConflictingRepositories = conflictingRepositoriesAndAffectedChanges.getFirst();
List<Change> affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond();
- int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, "checkout", true);
+ Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByCheckout.getRelativeFilePaths());
+ int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "checkout", true);
if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) {
boolean smartCheckedOutSuccessfully = smartCheckout(allConflictingRepositories, myStartPointReference, myNewBranch, getIndicator());
if (smartCheckedOutSuccessfully) {
diff --git a/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java
index 5ce85f114892..46630d0e1125 100644
--- a/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java
@@ -23,12 +23,10 @@ import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.util.ui.UIUtil;
-import git4idea.GitBranch;
import git4idea.GitPlatformFacade;
import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitCompoundResult;
-import git4idea.jgit.GitHttpAdapter;
import git4idea.push.GitSimplePushResult;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
@@ -166,15 +164,7 @@ class GitDeleteRemoteBranchOperation extends GitBranchOperation {
LOG.warn("No urls are defined for remote: " + remote);
return GitCommandResult.error("There is no urls defined for remote " + remote.getName());
}
- if (GitHttpAdapter.shouldUseJGit(remoteUrl)) {
- String fullBranchName = branchName.startsWith(GitBranch.REFS_HEADS_PREFIX) ? branchName : GitBranch.REFS_HEADS_PREFIX + branchName;
- String spec = ":" + fullBranchName;
- GitSimplePushResult simplePushResult = GitHttpAdapter.push(repository, remote.getName(), remoteUrl, spec);
- return convertSimplePushResultToCommandResult(simplePushResult);
- }
- else {
- return pushDeletionNatively(repository, remoteName, remoteUrl, branchName);
- }
+ return pushDeletionNatively(repository, remoteName, remoteUrl, branchName);
}
@NotNull
@@ -255,6 +245,7 @@ class GitDeleteRemoteBranchOperation extends GitBranchOperation {
return false;
}
+ @NotNull
@Override
public String getDoNotShowMessage() {
return checkboxMessage;
diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
index 81e5cc35f750..e90d1eb808ef 100644
--- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java
@@ -116,7 +116,7 @@ class GitMergeOperation extends GitBranchOperation {
}
else if (untrackedOverwrittenByMerge.wasMessageDetected()) {
LOG.info("Untracked files would be overwritten by merge!");
- fatalUntrackedFilesError(untrackedOverwrittenByMerge.getFiles());
+ fatalUntrackedFilesError(repository.getRoot(), untrackedOverwrittenByMerge.getRelativeFilePaths());
fatalErrorHappened = true;
}
else {
@@ -187,7 +187,8 @@ class GitMergeOperation extends GitBranchOperation {
List<GitRepository> allConflictingRepositories = conflictingRepositoriesAndAffectedChanges.getFirst();
List<Change> affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond();
- int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, "merge", false);
+ Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByMerge.getRelativeFilePaths());
+ int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "merge", false);
if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) {
return doSmartMerge(allConflictingRepositories);
}
diff --git a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
index c7f7f9ab476e..9c2ef4a8d107 100644
--- a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
+++ b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java
@@ -19,8 +19,6 @@ import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
-import com.intellij.openapi.vcs.changes.Change;
-import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
@@ -29,7 +27,6 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.ActionEvent;
-import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static com.intellij.openapi.util.text.StringUtil.capitalize;
@@ -39,29 +36,26 @@ import static com.intellij.openapi.util.text.StringUtil.capitalize;
* "Your local changes to the following files would be overwritten by merge/checkout"
* happens.
* Displays the list of these files and proposes to make a "smart" merge or checkout.
- *
- * @author Kirill Likhodedov
*/
public class GitSmartOperationDialog extends DialogWrapper {
public static final int SMART_EXIT_CODE = OK_EXIT_CODE;
public static final int FORCE_EXIT_CODE = NEXT_USER_EXIT_CODE;
-
- private final Project myProject;
- private final List<Change> myChanges;
+
+ @NotNull private final JComponent myFileBrowser;
@NotNull private final String myOperationTitle;
- private final boolean myForceButton;
+ private final boolean myShowForceButton;
/**
* 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 List<Change> changes, @NotNull final String operationTitle,
- final boolean forceButton) {
+ static int showAndGetAnswer(@NotNull final Project project, @NotNull final JComponent fileBrowser,
+ @NotNull final String operationTitle, final boolean showForceButton) {
final AtomicInteger exitCode = new AtomicInteger();
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
- GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, changes, operationTitle, forceButton);
+ GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, fileBrowser, operationTitle, showForceButton);
ServiceManager.getService(project, GitPlatformFacade.class).showDialog(dialog);
exitCode.set(dialog.getExitCode());
}
@@ -69,15 +63,18 @@ public class GitSmartOperationDialog extends DialogWrapper {
return exitCode.get();
}
- private GitSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull String operationTitle,
- boolean forceButton) {
+ private GitSmartOperationDialog(@NotNull Project project, @NotNull JComponent fileBrowser, @NotNull String operationTitle,
+ boolean showForceButton) {
super(project);
- myProject = project;
- myChanges = changes;
+ myFileBrowser = fileBrowser;
myOperationTitle = operationTitle;
- myForceButton = forceButton;
- setOKButtonText("Smart " + capitalize(myOperationTitle));
- setCancelButtonText("Don't " + capitalize(myOperationTitle));
+ myShowForceButton = showForceButton;
+ String capitalizedOperation = capitalize(myOperationTitle);
+ setTitle("Git " + capitalizedOperation + " Problem");
+
+ setOKButtonText("Smart " + capitalizedOperation);
+ getOKAction().putValue(Action.SHORT_DESCRIPTION, "Stash local changes, " + operationTitle + ", unstash");
+ setCancelButtonText("Don't " + capitalizedOperation);
getCancelAction().putValue(FOCUSED_ACTION, Boolean.TRUE);
init();
}
@@ -85,8 +82,8 @@ public class GitSmartOperationDialog extends DialogWrapper {
@NotNull
@Override
protected Action[] createLeftSideActions() {
- if (myForceButton) {
- return new Action[] {new ForceCheckoutAction(myOperationTitle) };
+ if (myShowForceButton) {
+ return new Action[] {new ForceCheckoutAction(myOperationTitle) };
}
return new Action[0];
}
@@ -102,10 +99,7 @@ public class GitSmartOperationDialog extends DialogWrapper {
@Override
protected JComponent createCenterPanel() {
- ChangesBrowser changesBrowser =
- new ChangesBrowser(myProject, null, myChanges, null, false, true, null, ChangesBrowser.MyUseCase.LOCAL_CHANGES, null);
- changesBrowser.setChangesToDisplay(myChanges);
- return changesBrowser;
+ return myFileBrowser;
}
@Override
@@ -118,6 +112,7 @@ public class GitSmartOperationDialog extends DialogWrapper {
ForceCheckoutAction(@NotNull String operationTitle) {
super("&Force " + capitalize(operationTitle));
+ putValue(Action.SHORT_DESCRIPTION, capitalize(operationTitle) + " and overwrite local changes");
}
@Override
diff --git a/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java b/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java
index e23f8ed91c30..157373e4f8b8 100644
--- a/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java
+++ b/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java
@@ -186,18 +186,23 @@ public class GitChangeUtils {
@NotNull
public static GitRevisionNumber resolveReference(@NotNull Project project, @NotNull VirtualFile vcsRoot,
@NotNull String reference) throws VcsException {
- GitSimpleHandler handler = new GitSimpleHandler(project, vcsRoot, GitCommand.REV_LIST);
- handler.addParameters("--timestamp", "--max-count=1", reference);
- handler.endOptions();
- handler.setSilent(true);
+ GitSimpleHandler handler = createRefResolveHandler(project, vcsRoot, reference);
String output = handler.run();
StringTokenizer stk = new StringTokenizer(output, "\n\r \t", false);
if (!stk.hasMoreTokens()) {
- GitSimpleHandler dh = new GitSimpleHandler(project, vcsRoot, GitCommand.LOG);
- dh.addParameters("-1", "HEAD");
- dh.setSilent(true);
- String out = dh.run();
- LOG.info("Diagnostic output from 'git log -1 HEAD': [" + out + "]");
+ try {
+ GitSimpleHandler dh = new GitSimpleHandler(project, vcsRoot, GitCommand.LOG);
+ dh.addParameters("-1", "HEAD");
+ dh.setSilent(true);
+ String out = dh.run();
+ LOG.info("Diagnostic output from 'git log -1 HEAD': [" + out + "]");
+ dh = createRefResolveHandler(project, vcsRoot, reference);
+ out = dh.run();
+ LOG.info("Diagnostic output from 'git rev-list -1 --timestamp HEAD': [" + out + "]");
+ }
+ catch (VcsException e) {
+ LOG.info("Exception while trying to get some diagnostics info", e);
+ }
throw new VcsException(String.format("The string '%s' does not represent a revision number. Output: [%s]\n Root: %s",
reference, output, vcsRoot));
}
@@ -205,6 +210,15 @@ public class GitChangeUtils {
return new GitRevisionNumber(stk.nextToken(), timestamp);
}
+ @NotNull
+ private static GitSimpleHandler createRefResolveHandler(@NotNull Project project, @NotNull VirtualFile root, @NotNull String reference) {
+ GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.REV_LIST);
+ handler.addParameters("--timestamp", "--max-count=1", reference);
+ handler.endOptions();
+ handler.setSilent(true);
+ return handler;
+ }
+
/**
* Check if the exception means that HEAD is missing for the current repository.
*
diff --git a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java
index baf835820ddb..f1ab1d476c6f 100644
--- a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java
+++ b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java
@@ -22,6 +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.committed.DecoratorManager;
import com.intellij.openapi.vcs.changes.committed.VcsCommittedListsZipper;
import com.intellij.openapi.vcs.changes.committed.VcsCommittedViewAuxiliary;
@@ -197,7 +198,14 @@ public class GitCommittedChangeListProvider implements CommittedChangesProvider<
final Collection<Change> changes = commit.getChanges();
if (changes.size() == 1) {
- return Pair.create(commit, changes.iterator().next().getAfterRevision().getFile());
+ 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);
}
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 170dffff0492..2eb0219485b6 100644
--- a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java
+++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java
@@ -20,6 +20,8 @@ import com.intellij.dvcs.DvcsCommitAdditionalComponent;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.Ref;
@@ -27,7 +29,6 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.FilePath;
-import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.ui.SelectFilePathsDialog;
@@ -35,15 +36,15 @@ import com.intellij.openapi.vcs.checkin.CheckinChangeListSpecificComponent;
import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization;
import com.intellij.ui.GuiUtils;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.FunctionUtil;
-import com.intellij.util.NullableFunction;
-import com.intellij.util.PairConsumer;
+import com.intellij.ui.StringComboboxEditor;
+import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.VcsFullCommitDetails;
+import com.intellij.vcs.log.VcsUser;
+import com.intellij.vcs.log.VcsUserRegistry;
import com.intellij.vcsUtil.VcsFileUtil;
import com.intellij.vcsUtil.VcsUtil;
import git4idea.GitPlatformFacade;
@@ -54,7 +55,6 @@ import git4idea.commands.GitSimpleHandler;
import git4idea.config.GitConfigUtil;
import git4idea.config.GitVcsSettings;
import git4idea.config.GitVersionSpecialty;
-import git4idea.history.NewGitUsersComponent;
import git4idea.i18n.GitBundle;
import git4idea.push.GitPusher;
import git4idea.repo.GitRepositoryFiles;
@@ -71,9 +71,6 @@ import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
-/**
- * Git environment for commit operations.
- */
public class GitCheckinEnvironment implements CheckinEnvironment {
private static final Logger log = Logger.getInstance(GitCheckinEnvironment.class.getName());
@NonNls private static final String GIT_COMMIT_MSG_FILE_PREFIX = "git-commit-msg-"; // the file name prefix for commit message file
@@ -207,7 +204,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
}
}
catch (VcsException e) {
- exceptions.add(e);
+ exceptions.add(cleanupExceptionText(e));
}
}
catch (IOException ex) {
@@ -226,8 +223,23 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
return exceptions;
}
+ @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());
+ }
+ final String DURING_EXECUTING_SUFFIX = GitSimpleHandler.DURING_EXECUTING_ERROR_MESSAGE;
+ int suffix = msg.indexOf(DURING_EXECUTING_SUFFIX);
+ if (suffix > 0) {
+ msg = msg.substring(0, suffix);
+ }
+ return new VcsException(msg.trim(), original.getCause());
+ }
+
public List<VcsException> commit(List<Change> changes, String preparedComment) {
- return commit(changes, preparedComment, FunctionUtil.<Object, Object>nullConstant(), null);
+ return commit(changes, preparedComment, FunctionUtil.nullConstant(), null);
}
/**
@@ -424,9 +436,6 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
return file;
}
- /**
- * {@inheritDoc}
- */
public List<VcsException> scheduleMissingFileForDeletion(List<FilePath> files) {
ArrayList<VcsException> rc = new ArrayList<VcsException>();
Map<VirtualFile, List<FilePath>> sortedFiles;
@@ -450,21 +459,6 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
return rc;
}
- /**
- * Prepare delete files handler.
- *
- *
- *
- * @param project the project
- * @param root a vcs root
- * @param files a files to commit
- * @param message a message file to use
- * @param nextCommitAuthor a author for the next commit
- * @param nextCommitAmend true, if the commit should be amended
- * @param nextCommitAuthorDate Author date timestamp to override the date of the commit or null if this overriding is not needed.
- * @return a simple handler that does the task
- * @throws VcsException in case of git problem
- */
private static void commit(Project project,
VirtualFile root,
Collection<FilePath> files,
@@ -499,10 +493,6 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
}
}
-
- /**
- * {@inheritDoc}
- */
public List<VcsException> scheduleUnversionedFilesForAddition(List<VirtualFile> files) {
ArrayList<VcsException> rc = new ArrayList<VcsException>();
Map<VirtualFile, List<VirtualFile>> sortedFiles;
@@ -542,13 +532,6 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
}
}
- /**
- * Sort changes by roots
- *
- * @param changes a change list
- * @param exceptions exceptions to collect
- * @return sorted changes
- */
private static Map<VirtualFile, Collection<Change>> sortChangesByGitRoot(@NotNull List<Change> changes, List<VcsException> exceptions) {
Map<VirtualFile, Collection<Change>> result = new HashMap<VirtualFile, Collection<Change>>();
for (Change change : changes) {
@@ -579,11 +562,6 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
return result;
}
- /**
- * Mark root as dirty
- *
- * @param root a vcs root to rescan
- */
private void markRootDirty(final VirtualFile root) {
// Note that the root is invalidated because changes are detected per-root anyway.
// Otherwise it is not possible to detect moves.
@@ -597,14 +575,8 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
myNextCommitAuthorDate = null;
}
- /**
- * Checkin options for git
- */
private class GitCheckinOptions extends DvcsCommitAdditionalComponent implements CheckinChangeListSpecificComponent {
private final GitVcs myVcs;
- /**
- * The author ComboBox, the combobox contains previously selected authors.
- */
private final ComboBox myAuthor;
private Date myAuthorDate;
@@ -630,17 +602,26 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
c.weightx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
final List<String> usersList = getUsersList(project);
- final Set<String> authors = usersList == null ? new HashSet<String>() : new HashSet<String>(usersList);
+ final Set<String> authors = new HashSet<String>(usersList);
ContainerUtil.addAll(authors, mySettings.getCommitAuthors());
List<String> list = new ArrayList<String>(authors);
Collections.sort(list);
- list = ObjectsConvertor.convert(list, new Convertor<String, String>() {
+
+ myAuthor = new ComboBox(ArrayUtil.toObjectArray(list)) {
@Override
- public String convert(String o) {
- return StringUtil.shortenTextWithEllipsis(o, 30, 0);
+ public void addNotify() {
+ super.addNotify();
+
+ // adding in addNotify to make sure the editor is ready for further customization
+ StringComboboxEditor comboboxEditor = new StringComboboxEditor(project, FileTypes.PLAIN_TEXT, myAuthor, true);
+ myAuthor.setEditor(comboboxEditor);
+ EditorEx editor = (EditorEx)comboboxEditor.getEditor();
+ assert editor != null;
+ SpellCheckingEditorCustomization.getInstance(false).customize(editor);
}
- });
- myAuthor = new ComboBox(ArrayUtil.toObjectArray(list));
+ };
+ myAuthor.setMinimumAndPreferredWidth(100);
+
myAuthor.insertItemAt("", 0);
myAuthor.setSelectedItem("");
myAuthor.setEditable(true);
@@ -673,8 +654,15 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
return h.run();
}
- private List<String> getUsersList(final Project project) {
- return NewGitUsersComponent.getInstance(project).get();
+ @NotNull
+ private List<String> getUsersList(@NotNull Project project) {
+ VcsUserRegistry userRegistry = ServiceManager.getService(project, VcsUserRegistry.class);
+ return ContainerUtil.map(userRegistry.getUsers(), new Function<VcsUser, String>() {
+ @Override
+ public String fun(VcsUser user) {
+ return user.getName() + " <" + user.getEmail() + ">";
+ }
+ });
}
@Override
@@ -687,13 +675,12 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
@Override
public void saveState() {
String author = (String)myAuthor.getEditor().getItem();
- myNextCommitAuthor = author.length() == 0 ? null : author;
- if (author.length() == 0) {
+ if (StringUtil.isEmptyOrSpaces(author)) {
myNextCommitAuthor = null;
}
else {
- myNextCommitAuthor = author;
- mySettings.saveCommitAuthor(author);
+ myNextCommitAuthor = GitCommitAuthorCorrector.correct(author);
+ mySettings.saveCommitAuthor(myNextCommitAuthor);
}
myNextCommitAmend = myAmend.isSelected();
myNextCommitAuthorDate = myAuthorDate;
@@ -716,6 +703,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment {
}
}
+
public void setNextCommitIsPushed(Boolean nextCommitIsPushed) {
myNextCommitIsPushed = nextCommitIsPushed;
}
diff --git a/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java b/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java
index 75a6df3e8f67..8bfc52ca261a 100644
--- a/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java
+++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java
@@ -22,7 +22,9 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Couple;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.FilePath;
@@ -131,14 +133,17 @@ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory {
}
if (crlfHelper.get().shouldWarn()) {
- final GitCrlfDialog dialog = new GitCrlfDialog(myProject);
- UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ Pair<Integer, Boolean> codeAndDontWarn = UIUtil.invokeAndWaitIfNeeded(new Computable<Pair<Integer, Boolean>>() {
@Override
- public void run() {
+ public Pair<Integer, Boolean> compute() {
+ final GitCrlfDialog dialog = new GitCrlfDialog(myProject);
dialog.show();
+ return Pair.create(dialog.getExitCode(), dialog.dontWarnAgain());
}
});
- int decision = dialog.getExitCode();
+ int decision = codeAndDontWarn.first;
+ boolean dontWarnAgain = codeAndDontWarn.second;
+
if (decision == GitCrlfDialog.CANCEL) {
return ReturnResult.CANCEL;
}
@@ -148,7 +153,7 @@ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory {
setCoreAutoCrlfAttribute(anyRoot);
}
else {
- if (dialog.dontWarnAgain()) {
+ if (dontWarnAgain) {
settings.setWarnAboutCrlf(false);
}
}
diff --git a/plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java b/plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java
new file mode 100644
index 000000000000..e46e98a7887f
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/checkin/GitCommitAuthorCorrector.java
@@ -0,0 +1,55 @@
+/*
+ * 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.checkin;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Corrects some simple but popular mistakes on the author format.<p/>
+ * The required format is: {@code author name <author.name@email.com>}
+ */
+class GitCommitAuthorCorrector {
+
+ @NotNull
+ public static String correct(@NotNull String author) {
+ author = author.trim();
+
+ int openBrace = author.indexOf('<');
+ int closeBrace = author.indexOf('>');
+
+ if (openBrace < 0) { // email should open with "<"
+ int at = author.lastIndexOf("@");
+ if (at < 0) {
+ return author;
+ }
+ int email = author.substring(0, at).lastIndexOf(' ');
+ if (email < 0) {
+ return author;
+ }
+ author = author.substring(0, email + 1) + "<" + author.substring(email + 1);
+ }
+ else if (openBrace > 0 && author.charAt(openBrace - 1) != ' ') { // insert space before email
+ author = author.substring(0, openBrace) + " " + author.substring(openBrace);
+ }
+
+ if (closeBrace < 0) { // email should close with ">"
+ author += ">";
+ }
+
+ return author;
+ }
+
+}
diff --git a/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java b/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java
index a8b78a151423..5629a93bac73 100644
--- a/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java
+++ b/plugins/git4idea/src/git4idea/checkout/GitCheckoutProvider.java
@@ -30,9 +30,6 @@ import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitLineHandlerListener;
import git4idea.commands.GitStandardProgressAnalyzer;
-import git4idea.jgit.GitHttpAdapter;
-import git4idea.update.GitFetchResult;
-import git4idea.update.GitFetcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -109,26 +106,13 @@ public class GitCheckoutProvider implements CheckoutProvider {
public static boolean doClone(@NotNull Project project, @NotNull ProgressIndicator indicator, @NotNull Git git,
@NotNull String directoryName, @NotNull String parentDirectory, @NotNull String sourceRepositoryURL) {
- if (GitHttpAdapter.shouldUseJGit(sourceRepositoryURL)) {
- GitFetchResult result = GitHttpAdapter.cloneRepository(project, new File(parentDirectory, directoryName), sourceRepositoryURL);
- GitFetcher.displayFetchResult(project, result, "Clone failed", result.getErrors());
- return result.isSuccess();
- }
- else {
- return cloneNatively(project, indicator, git, new File(parentDirectory), sourceRepositoryURL, directoryName);
- }
- }
-
- private static boolean cloneNatively(@NotNull Project project, @NotNull final ProgressIndicator indicator,
- @NotNull Git git, @NotNull File directory, @NotNull String url, @NotNull String cloneDirectoryName) {
indicator.setIndeterminate(false);
GitLineHandlerListener progressListener = GitStandardProgressAnalyzer.createListener(indicator);
- GitCommandResult result = git.clone(project, directory, url, cloneDirectoryName, progressListener);
+ GitCommandResult result = git.clone(project, new File(parentDirectory), sourceRepositoryURL, directoryName, progressListener);
if (result.success()) {
return true;
}
VcsNotifier.getInstance(project).notifyError("Clone failed", result.getErrorOutputAsHtmlString());
return false;
}
-
}
diff --git a/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java b/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java
index 1f03a81c3fa4..831f833e0db5 100644
--- a/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java
+++ b/plugins/git4idea/src/git4idea/checkout/GitCloneDialog.java
@@ -40,8 +40,7 @@ public class GitCloneDialog extends CloneDvcsDialog {
}
/*
- * JGit doesn't have ls-remote command independent from repository yet.
- * That way, we have a hack here: if http response asked for a password, then the url is at least valid and existant, and we consider
+ * We have a hack here: if http response asked for a password, then the url is at least valid and existent, and we consider
* that the test passed.
*/
protected boolean test(@NotNull String url) {
diff --git a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java
index d4b3a0fbf1d8..bd8635db89be 100644
--- a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java
+++ b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPicker.java
@@ -140,8 +140,8 @@ public class GitCherryPicker {
"Please move, remove or add them before you can cherry-pick. <a href='view'>View them</a>";
description += getSuccessfulCommitDetailsIfAny(successfulCommits);
- UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, untrackedFilesDetector.getFiles(),
- "cherry-pick", description);
+ UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, repository.getRoot(),
+ untrackedFilesDetector.getRelativeFilePaths(), "cherry-pick", description);
return false;
}
else if (localChangesOverwrittenDetector.hasHappened()) {
diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java
index eaf58ff824d2..284ccc1e20c5 100644
--- a/plugins/git4idea/src/git4idea/commands/Git.java
+++ b/plugins/git4idea/src/git4idea/commands/Git.java
@@ -36,13 +36,13 @@ import java.util.Set;
public interface Git {
/**
- * A generic method to run a Git remote command, when existing methods like {@link #fetch(GitRepository, String, String, List, String...)}
+ * A generic method to run a Git command, when existing methods like {@link #fetch(GitRepository, String, String, List, String...)}
* are not sufficient.
* @param handlerConstructor this is needed, since the operation may need to repeat (e.g. in case of authentication failure).
* make sure to supply a stateless constructor.
*/
@NotNull
- GitCommandResult runRemoteCommand(@NotNull Computable<GitLineHandler> handlerConstructor);
+ GitCommandResult runCommand(@NotNull Computable<GitLineHandler> handlerConstructor);
@NotNull
GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners);
diff --git a/plugins/git4idea/src/git4idea/commands/GitHandlerUtil.java b/plugins/git4idea/src/git4idea/commands/GitHandlerUtil.java
index 03cfab25bc84..fbc84ca1a7a2 100644
--- a/plugins/git4idea/src/git4idea/commands/GitHandlerUtil.java
+++ b/plugins/git4idea/src/git4idea/commands/GitHandlerUtil.java
@@ -15,7 +15,6 @@
*/
package git4idea.commands;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
@@ -28,21 +27,13 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.awt.EventQueue;
-import java.util.Collection;
+import java.awt.*;
/**
* Handler utilities that allow running handlers with progress indicators
*/
public class GitHandlerUtil {
- /**
- * The logger instance
- */
- private static final Logger LOG = Logger.getInstance(GitHandlerUtil.class.getName());
- /**
- * a private constructor for utility class
- */
private GitHandlerUtil() {
}
@@ -55,7 +46,7 @@ public class GitHandlerUtil {
* @return A stdout content or null if there was error (exit code != 0 or exception during start).
*/
@Nullable
- public static String doSynchronously(final GitSimpleHandler handler, String operationTitle, @NonNls final String operationName) {
+ public static String doSynchronously(final GitSimpleHandler handler, final String operationTitle, @NonNls final String operationName) {
handler.addListener(new GitHandlerListenerBase(handler, operationName) {
protected String getErrorText() {
String text = handler.getStderr();
@@ -65,7 +56,13 @@ public class GitHandlerUtil {
return text;
}
});
- runHandlerSynchronously(handler, operationTitle, ProgressManager.getInstance(), true);
+ final ProgressManager manager = ProgressManager.getInstance();
+ manager.runProcessWithProgressSynchronously(new Runnable() {
+ public void run() {
+ runInCurrentThread(handler, manager.getProgressIndicator(), true,
+ operationTitle);
+ }
+ }, operationTitle, false, handler.project());
if (!handler.isStarted() || handler.getExitCode() != 0) {
return null;
}
@@ -80,47 +77,12 @@ public class GitHandlerUtil {
* @param operationName an operation name shown in failure dialog
* @return An exit code
*/
- public static int doSynchronously(final GitLineHandler handler, String operationTitle, @NonNls final String operationName) {
- return doSynchronously(handler, operationTitle, operationName, true);
- }
-
- /**
- * Execute simple process synchronously with progress
- *
- * @param handler a handler
- * @param operationTitle an operation title shown in progress dialog
- * @param operationName an operation name shown in failure dialog
- * @param showErrors if true, the errors are shown when process is terminated
- * @return An exit code
- */
- public static int doSynchronously(final GitLineHandler handler,
- String operationTitle,
- @NonNls final String operationName,
- boolean showErrors) {
- return doSynchronously(handler, operationTitle, operationName, showErrors, true);
- }
-
-
- /**
- * Execute simple process synchronously with progress
- *
- * @param handler a handler
- * @param operationTitle an operation title shown in progress dialog
- * @param operationName an operation name shown in failure dialog
- * @param showErrors if true, the errors are shown when process is terminated
- * @param setIndeterminateFlag a flag indicating that progress should be configured as indeterminate
- * @return An exit code
- */
- public static int doSynchronously(final GitLineHandler handler,
- final String operationTitle,
- @NonNls final String operationName,
- final boolean showErrors,
- final boolean setIndeterminateFlag) {
+ public static int doSynchronously(final GitLineHandler handler, final String operationTitle, @NonNls final String operationName) {
final ProgressManager manager = ProgressManager.getInstance();
manager.run(new Task.Modal(handler.project(), operationTitle, false) {
public void run(@NotNull final ProgressIndicator indicator) {
- handler.addLineListener(new GitLineHandlerListenerProgress(indicator, handler, operationName, showErrors));
- runInCurrentThread(handler, indicator, setIndeterminateFlag, operationTitle);
+ handler.addLineListener(new GitLineHandlerListenerProgress(indicator, handler, operationName, true));
+ runInCurrentThread(handler, indicator, true, operationTitle);
}
});
if (!handler.isStarted()) {
@@ -131,26 +93,6 @@ public class GitHandlerUtil {
/**
- * Run handler synchronously. The method assumes that all listeners are set up.
- *
- * @param handler a handler to run
- * @param operationTitle operation title
- * @param manager a progress manager
- * @param setIndeterminateFlag if true handler is configured as indeterminate
- */
- private static void runHandlerSynchronously(final GitHandler handler,
- final String operationTitle,
- final ProgressManager manager,
- final boolean setIndeterminateFlag) {
- manager.runProcessWithProgressSynchronously(new Runnable() {
- public void run() {
- runInCurrentThread(handler, manager.getProgressIndicator(), setIndeterminateFlag,
- operationTitle);
- }
- }, operationTitle, false, handler.project());
- }
-
- /**
* Run handler in the current thread
*
* @param handler a handler to run
@@ -185,33 +127,6 @@ public class GitHandlerUtil {
handler.runInCurrentThread(postStartAction);
}
- /**
- * Run synchronously using progress indicator, but collect exceptions instead of showing error dialog
- *
- * @param handler a handler to use
- * @return the collection of exception collected during operation
- */
- public static Collection<VcsException> doSynchronouslyWithExceptions(final GitLineHandler handler) {
- final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
- return doSynchronouslyWithExceptions(handler, progressIndicator, null);
- }
-
- /**
- * Run synchronously using progress indicator, but collect exception instead of showing error dialog
- *
- * @param handler a handler to use
- * @param progressIndicator a progress indicator
- * @param operationName
- * @return the collection of exception collected during operation
- */
- public static Collection<VcsException> doSynchronouslyWithExceptions(final GitLineHandler handler,
- final ProgressIndicator progressIndicator,
- @Nullable String operationName) {
- handler.addLineListener(new GitLineHandlerListenerProgress(progressIndicator, handler, operationName, false));
- runInCurrentThread(handler, progressIndicator, false, operationName);
- return handler.errors();
- }
-
public static String formatOperationName(String operation, @NotNull VirtualFile root) {
return operation + " '" + root.getName() + "'...";
}
diff --git a/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java b/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java
index 141adca79937..5b3a2422254f 100644
--- a/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java
+++ b/plugins/git4idea/src/git4idea/commands/GitHttpGuiAuthenticator.java
@@ -25,13 +25,14 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.AuthData;
import com.intellij.util.UriUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.URLUtil;
import com.intellij.vcsUtil.AuthDialog;
-import git4idea.jgit.GitHttpAuthDataProvider;
+import git4idea.remote.GitHttpAuthDataProvider;
import git4idea.remote.GitRememberedInputs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -126,15 +127,8 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
return login;
}
- final AuthDialog dialog = new AuthDialog(myProject, myTitle, "Enter credentials for " + url, login, null, true);
- ApplicationManager.getApplication().invokeAndWait(new Runnable() {
- @Override
- public void run() {
- dialog.show();
- }
- }, myModalityState == null ? ModalityState.defaultModalityState() : myModalityState);
-
- if (!dialog.isOK()) {
+ AuthDialog dialog = showAuthDialog(url, login);
+ if (dialog == null || !dialog.isOK()) {
myWasCancelled = true;
return "";
}
@@ -149,6 +143,19 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
return myLogin;
}
+ @Nullable
+ private AuthDialog showAuthDialog(final String url, final String login) {
+ final Ref<AuthDialog> dialog = Ref.create();
+ ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ dialog.set(new AuthDialog(myProject, myTitle, "Enter credentials for " + url, login, null, true));
+ dialog.get().show();
+ }
+ }, myModalityState == null ? ModalityState.defaultModalityState() : myModalityState);
+ return dialog.get();
+ }
+
@Override
public void saveAuthData() {
// save login and url
diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java
index b94cef0004ac..3e14a6414704 100644
--- a/plugins/git4idea/src/git4idea/commands/GitImpl.java
+++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java
@@ -373,7 +373,7 @@ public class GitImpl implements Git {
public GitCommandResult push(@NotNull final GitRepository repository, @NotNull final String remote, @NotNull final String url,
@NotNull final String spec, final boolean updateTracking,
@NotNull final GitLineHandlerListener... listeners) {
- return runRemoteCommand(new Computable<GitLineHandler>() {
+ return runCommand(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
final GitLineHandlerPasswordRequestAware h = new GitLineHandlerPasswordRequestAware(repository.getProject(), repository.getRoot(),
@@ -451,7 +451,7 @@ public class GitImpl implements Git {
@NotNull
public GitCommandResult fetch(@NotNull final GitRepository repository, @NotNull final String url, @NotNull final String remote,
@NotNull final List<GitLineHandlerListener> listeners, final String... params) {
- return runRemoteCommand(new Computable<GitLineHandler>() {
+ return runCommand(new Computable<GitLineHandler>() {
@Override
public GitLineHandler compute() {
final GitLineHandlerPasswordRequestAware h = new GitLineHandlerPasswordRequestAware(repository.getProject(), repository.getRoot(),
@@ -543,7 +543,7 @@ public class GitImpl implements Git {
@Override
@NotNull
- public GitCommandResult runRemoteCommand(@NotNull Computable<GitLineHandler> handlerConstructor) {
+ public GitCommandResult runCommand(@NotNull Computable<GitLineHandler> handlerConstructor) {
return run(handlerConstructor);
}
diff --git a/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java b/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java
index 91114f05cdfd..5e66246805d0 100644
--- a/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java
+++ b/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java
@@ -34,6 +34,9 @@ import java.io.File;
* simple commands.
*/
public class GitSimpleHandler extends GitTextHandler {
+
+ public static final String DURING_EXECUTING_ERROR_MESSAGE = "during executing";
+
/**
* Stderr output
*/
@@ -235,7 +238,7 @@ public class GitSimpleHandler extends GitTextHandler {
});
runInCurrentThread(null);
if (ex[0] != null) {
- throw new VcsException(ex[0].getMessage() + " during executing " + printableCommandLine(), ex[0]);
+ throw new VcsException(ex[0].getMessage() + " " + DURING_EXECUTING_ERROR_MESSAGE + " " + printableCommandLine(), ex[0]);
}
if (result[0] == null) {
throw new VcsException("The git command returned null: " + printableCommandLine());
diff --git a/plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java b/plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java
deleted file mode 100644
index dc468653b6f5..000000000000
--- a/plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright 2000-2011 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package git4idea.jgit;
-
-import com.intellij.ide.passwordSafe.PasswordSafe;
-import com.intellij.ide.passwordSafe.PasswordSafeException;
-import com.intellij.ide.passwordSafe.config.PasswordSafeSettings;
-import com.intellij.ide.passwordSafe.impl.PasswordSafeImpl;
-import com.intellij.ide.passwordSafe.impl.PasswordSafeProvider;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.SystemInfo;
-import com.intellij.util.AuthData;
-import com.intellij.util.proxy.CommonProxy;
-import git4idea.GitBranch;
-import git4idea.GitUtil;
-import git4idea.GitVcs;
-import git4idea.push.GitSimplePushResult;
-import git4idea.remote.GitRememberedInputs;
-import git4idea.repo.GitRemote;
-import git4idea.repo.GitRepository;
-import git4idea.update.GitFetchResult;
-import git4idea.update.GitFetcher;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.InvalidRemoteException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.errors.NoRemoteRepositoryException;
-import org.eclipse.jgit.errors.NotSupportedException;
-import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.lib.ConfigConstants;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.transport.RefSpec;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Handles remote operations over HTTP via JGit library.
- *
- * @author Kirill Likhodedov
- */
-public final class GitHttpAdapter {
-
- private static final Logger LOG = Logger.getInstance(GitHttpAdapter.class);
-
- private static final String IGNORECASE_SETTING = "ignorecase";
-
- public static boolean shouldUseJGit(@NotNull String url) {
- return "jgit".equals(System.getProperty("git.http"));
- }
-
- private enum GeneralResult {
- SUCCESS,
- CANCELLED,
- NOT_AUTHORIZED
- }
-
- private GitHttpAdapter() {
- }
-
- /**
- * Fetches the given remote in the given Git repository.
- * Asks username and password if needed.
- */
- @NotNull
- public static GitFetchResult fetch(@NotNull final GitRepository repository, @NotNull final GitRemote remote,
- @NotNull String remoteUrl, @Nullable String remoteBranch) {
- GitFetchResult.Type resultType;
- try {
- final Git git = convertToGit(repository);
- final GitHttpCredentialsProvider provider = new GitHttpCredentialsProvider(repository.getProject(), remoteUrl);
-
- List<String> specs;
- if (remoteBranch == null) {
- specs = remote.getFetchRefSpecs();
- }
- else {
- specs = Collections.singletonList(GitFetcher.getFetchSpecForBranch(remoteBranch, remote.getName()));
- }
-
- GeneralResult result = callWithAuthRetry(new GitHttpRemoteCommand.Fetch(git, provider, remoteUrl, convertRefSpecs(specs)),
- repository.getProject());
- resultType = convertToFetchResultType(result);
- } catch (IOException e) {
- logException(repository, remote.getName(), remoteUrl, e, "fetching");
- return GitFetchResult.error(e);
- }
- catch (InvalidRemoteException e) {
- logException(repository, remote.getName(), remoteUrl, e, "fetching");
- return GitFetchResult.error(e);
- }
- catch (URISyntaxException e) {
- logException(repository, remote.getName(), remoteUrl, e, "fetching");
- return GitFetchResult.error(e);
- }
- return new GitFetchResult(resultType);
- }
-
- @NotNull
- private static List<RefSpec> convertRefSpecs(@NotNull List<String> refSpecs) {
- List<RefSpec> jgitSpecs = new ArrayList<RefSpec>();
- for (String spec : refSpecs) {
- jgitSpecs.add(new RefSpec(spec));
- }
- return jgitSpecs;
- }
-
- private static void logException(GitRepository repository, String remoteName, String remoteUrl, Exception e, String operation) {
- LOG.error("Exception while " + operation + " " + remoteName + "(" + remoteUrl + ")" + " in " + repository.toLogString(), e);
- }
-
- private static GitFetchResult.Type convertToFetchResultType(GeneralResult result) {
- switch (result) {
- case CANCELLED: return GitFetchResult.Type.CANCELLED;
- case SUCCESS: return GitFetchResult.Type.SUCCESS;
- case NOT_AUTHORIZED: return GitFetchResult.Type.NOT_AUTHORIZED;
- }
- return GitFetchResult.Type.CANCELLED;
- }
-
- @NotNull
- public static GitSimplePushResult push(@NotNull final GitRepository repository, @NotNull final String remoteName,
- @NotNull final String remoteUrl, @NotNull String pushSpec) {
- try {
- final Git git = convertToGit(repository);
- final GitHttpCredentialsProvider provider = new GitHttpCredentialsProvider(repository.getProject(), remoteUrl);
- GitHttpRemoteCommand.Push pushCommand = new GitHttpRemoteCommand.Push(git, provider, remoteName, remoteUrl,
- convertRefSpecs(Collections.singletonList(pushSpec)));
- GeneralResult result = callWithAuthRetry(pushCommand, repository.getProject());
- GitSimplePushResult pushResult = pushCommand.getResult();
- if (pushResult == null) {
- return convertToPushResultType(result);
- } else {
- return pushResult;
- }
- }
- catch (SmartPushNotSupportedException e) {
- return GitSimplePushResult.error("Remote <code>" + remoteUrl + "</code> doesn't support <a href=\"http://progit.org/2010/03/04/smart-http.html\">" +
- "smart HTTP push. </a><br/>" +
- "Please set the server to use smart push or use other protocol (SSH for example). <br/>" +
- "If neither is possible, as a workaround you may add authentication data directly to the remote url in <code>.git/config</code>.");
- }
- catch (InvalidRemoteException e) {
- logException(repository, remoteName, remoteUrl, e, "pushing");
- return makeErrorResultFromException(e);
- }
- catch (IOException e) {
- logException(repository, remoteName, remoteUrl, e, "pushing");
- return makeErrorResultFromException(e);
- }
- catch (URISyntaxException e) {
- logException(repository, remoteName, remoteUrl, e, "pushing");
- return makeErrorResultFromException(e);
- }
- }
-
- @NotNull
- public static Collection<String> lsRemote(@NotNull GitRepository repository, @NotNull String remoteName, @NotNull String remoteUrl) {
- try {
- final Git git = convertToGit(repository);
- final GitHttpCredentialsProvider provider = new GitHttpCredentialsProvider(repository.getProject(), remoteUrl);
- GitHttpRemoteCommand.LsRemote lsRemoteCommand = new GitHttpRemoteCommand.LsRemote(git, provider, remoteUrl);
- callWithAuthRetry(lsRemoteCommand, repository.getProject());
- return convertRefsToStrings(lsRemoteCommand.getRefs());
- } catch (IOException e) {
- logException(repository, remoteName, remoteUrl, e, "ls-remote");
- }
- catch (InvalidRemoteException e) {
- logException(repository, remoteName, remoteUrl, e, "ls-remote");
- }
- catch (URISyntaxException e) {
- logException(repository, remoteName, remoteUrl, e, "ls-remote");
- }
- return Collections.emptyList();
- }
-
- @NotNull
- private static Collection<String> convertRefsToStrings(@NotNull Collection<Ref> lsRemoteCommandRefs) {
- Collection<String> refs = new ArrayList<String>();
- for (Ref ref : lsRemoteCommandRefs) {
- String refName = ref.getName();
- if (refName.startsWith(GitBranch.REFS_HEADS_PREFIX)) {
- refName = refName.substring(GitBranch.REFS_HEADS_PREFIX.length());
- }
- refs.add(refName);
- }
- return refs;
- }
-
- @NotNull
- public static GitFetchResult cloneRepository(@NotNull Project project, @NotNull final File directory, @NotNull final String url) {
- GitFetchResult.Type resultType;
- try {
- final GitHttpCredentialsProvider provider = new GitHttpCredentialsProvider(project, url);
- GitHttpRemoteCommand.Clone command = new GitHttpRemoteCommand.Clone(directory, provider, url);
- GeneralResult result = callWithAuthRetry(command, project);
- resultType = convertToFetchResultType(result);
- if (resultType.equals(GitFetchResult.Type.SUCCESS)) {
- updateCoreIgnoreCaseSetting(command.getGit());
- }
- return new GitFetchResult(resultType);
- }
- catch (InvalidRemoteException e) {
- LOG.info("Exception while cloning " + url + " to " + directory, e);
- return GitFetchResult.error(e);
- }
- catch (IOException e) {
- LOG.info("Exception while cloning " + url + " to " + directory, e);
- return GitFetchResult.error(e);
- }
- catch (URISyntaxException e) {
- LOG.info("Exception while cloning " + url + " to " + directory, e);
- return GitFetchResult.error(e);
- }
- }
-
- private static void updateCoreIgnoreCaseSetting(@Nullable Git git) {
- if (SystemInfo.isFileSystemCaseSensitive) {
- return;
- }
- if (git == null) {
- LOG.info("jgit.Git is null, the command should have failed. Not updating the settings.");
- return;
- }
- StoredConfig config = git.getRepository().getConfig();
- config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, IGNORECASE_SETTING, Boolean.TRUE.toString());
- try {
- config.save();
- }
- catch (IOException e) {
- LOG.info("Couldn't save config for " + git.getRepository().getDirectory().getPath(), e);
- }
- }
-
- @NotNull
- private static GitSimplePushResult convertToPushResultType(GeneralResult result) {
- switch (result) {
- case SUCCESS:
- return GitSimplePushResult.success();
- case CANCELLED:
- return GitSimplePushResult.cancel();
- case NOT_AUTHORIZED:
- return GitSimplePushResult.notAuthorized();
- default:
- return GitSimplePushResult.cancel();
- }
- }
-
-
- @NotNull
- private static GitSimplePushResult makeErrorResultFromException(Exception e) {
- return GitSimplePushResult.error(e.toString());
- }
-
- /**
- * Calls the given runnable.
- * If user cancels the authentication dialog, returns.
- * If user enters incorrect data, he has 2 more attempts to go before failure.
- * Cleanups are executed after each incorrect attempt to enter password, and after other retriable actions.
- */
- private static GeneralResult callWithAuthRetry(@NotNull GitHttpRemoteCommand command, @NotNull Project project) throws InvalidRemoteException, IOException, URISyntaxException {
- boolean httpTransportErrorFixTried = false;
- boolean noRemoteWithoutGitErrorFixTried = false;
-
- String url = command.getUrl();
- GitHttpCredentialsProvider provider = command.getCredentialsProvider();
- try {
- for (int i = 0; i < 3; i++) {
- try {
- AuthData authData = getUsernameAndPassword(provider.getProject(), provider.getUrl());
- if (authData != null) {
- provider.fillAuthDataIfNotFilled(authData.getLogin(), authData.getPassword());
- }
- if (i == 0) {
- provider.setAlwaysShowDialog(false); // if username and password are supplied, no need to show the dialog
- } else {
- provider.setAlwaysShowDialog(true); // unless these values fail authentication
- }
- command.run();
- rememberPassword(provider);
- return GeneralResult.SUCCESS;
- }
- catch (GitAPIException e) {
- if (!noRemoteWithoutGitErrorFixTried && isNoRemoteWithoutDotGitError(e, url)) {
- url = addDotGitToUrl(url);
- command.setUrl(url);
- provider.setUrl(url);
- noRemoteWithoutGitErrorFixTried = true;
- // don't "eat" one password entering attempt
- //noinspection AssignmentToForLoopParameter
- i--;
- }
- command.cleanup();
- }
- catch (JGitInternalException e) {
- try {
- if (authError(e)) {
- if (provider.wasCancelled()) { // if user cancels the dialog, just return
- return GeneralResult.CANCELLED;
- }
- // otherwise give more tries to enter password
- }
- else if (!httpTransportErrorFixTried && isTransportExceptionForHttp(e, url)) {
- url = url.replaceFirst("http", "https");
- command.setUrl(url);
- provider.setUrl(url);
- httpTransportErrorFixTried = true;
- // don't "eat" one password entering attempt
- //noinspection AssignmentToForLoopParameter
- i--;
- }
- else if (!noRemoteWithoutGitErrorFixTried && isNoRemoteWithoutDotGitError(e, url)) {
- url = addDotGitToUrl(url);
- command.setUrl(url);
- provider.setUrl(url);
- noRemoteWithoutGitErrorFixTried = true;
- // don't "eat" one password entering attempt
- //noinspection AssignmentToForLoopParameter
- i--;
- }
- else if (smartHttpPushNotSupported(e)) {
- throw new SmartPushNotSupportedException(e.getCause().getMessage());
- }
- else {
- throw e;
- }
- }
- finally {
- command.cleanup();
- }
- }
- }
- return GeneralResult.NOT_AUTHORIZED;
- }
- finally {
- log(command, project);
- }
- }
-
- private static CommonProxy.HostInfo getHostInfo(String url) throws URISyntaxException {
- final boolean isSecure = url.startsWith("https");
- final String protocol = isSecure ? "https" : "http";
- final URI uri = new URI(url);
- int port = uri.getPort();
- port = port < 0 ? (isSecure ? 443 : 80) : port;
- return new CommonProxy.HostInfo(protocol, uri.getHost(), port);
- }
-
- @NotNull
- private static String addDotGitToUrl(@NotNull String url) {
- if (url.endsWith("/")) {
- url = url.substring(0, url.length() - 1);
- }
- return url + GitUtil.DOT_GIT;
- }
-
- private static void log(@NotNull GitHttpRemoteCommand command, @NotNull Project project) {
- GitVcs vcs = GitVcs.getInstance(project);
- if (vcs != null) {
- vcs.showCommandLine(command.getCommandString());
- }
- LOG.info(command.getLogString());
- }
-
- private static boolean smartHttpPushNotSupported(JGitInternalException e) {
- if (e.getCause() instanceof NotSupportedException) {
- NotSupportedException nse = (NotSupportedException)e.getCause();
- String message = nse.getMessage();
- return message != null && message.toLowerCase().contains("smart http push");
- }
- return false;
- }
-
- private static boolean isNoRemoteWithoutDotGitError(Throwable e, String url) {
- Throwable cause = e.getCause();
- if (cause == null || (!(cause instanceof NoRemoteRepositoryException) && !(cause.getCause() instanceof NoRemoteRepositoryException))) {
- return false;
- }
- return !url.toLowerCase().endsWith(GitUtil.DOT_GIT);
- }
-
- private static boolean isTransportExceptionForHttp(@NotNull JGitInternalException e, @NotNull String url) {
- if (!(e.getCause() instanceof TransportException)) {
- return false;
- }
- return url.toLowerCase().startsWith("http") && !url.toLowerCase().startsWith("https");
- }
-
- private static void rememberPassword(@NotNull GitHttpCredentialsProvider credentialsProvider) {
- if (!credentialsProvider.wasDialogShown()) { // the dialog is not shown => everything is already stored
- return;
- }
- final PasswordSafeImpl passwordSafe = (PasswordSafeImpl)PasswordSafe.getInstance();
- if (passwordSafe.getSettings().getProviderType() == PasswordSafeSettings.ProviderType.DO_NOT_STORE) {
- return;
- }
- String login = credentialsProvider.getUserName();
- if (login == null || credentialsProvider.getPassword() == null) {
- return;
- }
-
- String url = adjustHttpUrl(credentialsProvider.getUrl());
- String key = keyForUrlAndLogin(url, login);
- try {
- // store in memory always
- storePassword(passwordSafe.getMemoryProvider(), credentialsProvider, key);
- if (credentialsProvider.isRememberPassword()) {
- storePassword(passwordSafe.getMasterKeyProvider(), credentialsProvider, key);
- }
- GitRememberedInputs.getInstance().addUrl(url, login);
- }
- catch (PasswordSafeException e) {
- LOG.info("Couldn't store the password for key [" + key + "]", e);
- }
- }
-
- private static void storePassword(PasswordSafeProvider passwordProvider, GitHttpCredentialsProvider credentialsProvider, String key) throws PasswordSafeException {
- passwordProvider.storePassword(credentialsProvider.getProject(), GitHttpCredentialsProvider.class, key, credentialsProvider.getPassword());
- }
-
- @Nullable
- private static AuthData getUsernameAndPassword(Project project, String url) {
- url = adjustHttpUrl(url);
- String userName = GitRememberedInputs.getInstance().getUserNameForUrl(url);
- if (userName == null) {
- return trySavedAuthDataFromProviders(url);
- }
- String key = keyForUrlAndLogin(url, userName);
- final PasswordSafe passwordSafe = PasswordSafe.getInstance();
- try {
- String password = passwordSafe.getPassword(project, GitHttpCredentialsProvider.class, key);
- if (password != null) {
- return new AuthData(userName, password);
- }
- return null;
- }
- catch (PasswordSafeException e) {
- LOG.info("Couldn't get the password for key [" + key + "]", e);
- return null;
- }
- }
-
- @Nullable
- private static AuthData trySavedAuthDataFromProviders(@NotNull String url) {
- GitHttpAuthDataProvider[] extensions = GitHttpAuthDataProvider.EP_NAME.getExtensions();
- for (GitHttpAuthDataProvider provider : extensions) {
- AuthData authData = provider.getAuthData(url);
- if (authData != null) {
- return authData;
- }
- }
- return null;
- }
-
- /**
- * If url is HTTPS, store it as HTTP in the password database, not to make user enter and remember same credentials twice.
- */
- @NotNull
- private static String adjustHttpUrl(@NotNull String url) {
- if (url.startsWith("https")) {
- return url.replaceFirst("https", "http");
- }
- return url;
- }
-
- @NotNull
- private static String keyForUrlAndLogin(@NotNull String stringUrl, @NotNull String login) {
- return login + ":" + stringUrl;
- }
-
- private static boolean authError(@NotNull JGitInternalException e) {
- Throwable cause = e.getCause();
- return (cause instanceof TransportException && cause.getMessage().contains("not authorized"));
- }
-
- /**
- * Converts {@link GitRepository} to JGit's {@link Repository}.
- */
- @NotNull
- private static Repository convert(@NotNull GitRepository repository) throws IOException {
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
- return builder.setGitDir(new File(repository.getRoot().getPath(), GitUtil.DOT_GIT))
- .readEnvironment() // scan environment GIT_* variables
- .findGitDir() // scan up the file system tree
- .build();
- }
-
- /**
- * Converts {@link GitRepository} to JGit's {@link Git} object.
- */
- private static Git convertToGit(@NotNull GitRepository repository) throws IOException {
- return Git.wrap(convert(repository));
- }
-
- private static class SmartPushNotSupportedException extends NotSupportedException {
- private SmartPushNotSupportedException(String message) {
- super(message);
- }
- }
-}
diff --git a/plugins/git4idea/src/git4idea/jgit/GitHttpCredentialsProvider.java b/plugins/git4idea/src/git4idea/jgit/GitHttpCredentialsProvider.java
deleted file mode 100644
index e37796636ea2..000000000000
--- a/plugins/git4idea/src/git4idea/jgit/GitHttpCredentialsProvider.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright 2000-2011 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package git4idea.jgit;
-
-import com.intellij.openapi.project.Project;
-import com.intellij.util.ui.UIUtil;
-import com.intellij.vcsUtil.AuthDialog;
-import org.eclipse.jgit.errors.UnsupportedCredentialItem;
-import org.eclipse.jgit.transport.CredentialItem;
-import org.eclipse.jgit.transport.CredentialsProvider;
-import org.eclipse.jgit.transport.URIish;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * @author Kirill Likhodedov
- */
-public class GitHttpCredentialsProvider extends CredentialsProvider {
-
- private static final Pattern HTTP_URL_PATTERN = Pattern.compile("http(?:s?)://(?:([\\S^@\\.]*)@)?.*");
-
- private final Project myProject;
- private String myRemoteUrl;
-
- private boolean myCancelled;
- private boolean myRememberPassword;
- private String myPassword;
- private String myUserName;
- private boolean myShowDialog;
- private boolean myDialogShown;
-
- public GitHttpCredentialsProvider(@NotNull Project project, @NotNull String remoteUrl) {
- myProject = project;
- myRemoteUrl = remoteUrl;
- }
-
- @Override
- public boolean isInteractive() {
- return true;
- }
-
- @Override
- public boolean supports(CredentialItem... items) {
- for (CredentialItem item : items) {
- if (item instanceof CredentialItem.Password) {
- continue;
- }
- if (item instanceof CredentialItem.Username) {
- continue;
- }
- return false;
- }
- return true;
- }
-
- @Override
- public boolean get(URIish uri, CredentialItem... items) throws UnsupportedCredentialItem {
- CredentialItem.Username userNameItem = null;
- CredentialItem.Password passwordItem = null;
- for (CredentialItem item : items) {
- if (item instanceof CredentialItem.Username) {
- userNameItem = (CredentialItem.Username)item;
- } else if (item instanceof CredentialItem.Password) {
- passwordItem = (CredentialItem.Password)item;
- }
- }
-
- if (userNameItem != null || passwordItem != null) {
- String username = getUserNameFromUrl(myRemoteUrl);
- String password = null;
- if (username == null) { // username is not in the url => reading pre-filled value from the password storage
- username = myUserName;
- password = myPassword;
- } else if (username.equals(myUserName)) { // username is in url => read password only if it is for the same user
- password = myPassword;
- }
-
- boolean rememberPassword = myRememberPassword;
- boolean ok;
- if (username != null && password != null && !myShowDialog) {
- ok = true;
- myDialogShown = false;
- } else {
- final AuthDialog dialog = new AuthDialog(myProject, "Login required", "Login to " + myRemoteUrl, username, password, false);
- UIUtil.invokeAndWaitIfNeeded(new Runnable() {
- @Override
- public void run() {
- dialog.show();
- }
- });
- ok = dialog.isOK();
- myDialogShown = true;
- if (ok) {
- username = dialog.getUsername();
- password = dialog.getPassword();
- rememberPassword = dialog.isRememberPassword();
- }
- }
-
- if (ok) {
- if (userNameItem != null) {
- userNameItem.setValue(username);
- }
- if (passwordItem != null) {
- passwordItem.setValue(password.toCharArray());
- }
- myRememberPassword = rememberPassword;
- myPassword = password;
- myUserName = username;
- }
- else {
- myCancelled = true;
- myRememberPassword = false; // in case of re-usage of the provider
- }
- return ok;
- }
- return true;
- }
-
- public boolean isRememberPassword() {
- return myRememberPassword;
- }
-
- @NotNull
- public Project getProject() {
- return myProject;
- }
-
- @Nullable
- public String getPassword() {
- return myPassword;
- }
-
- @Nullable
- public String getUserName() {
- return myUserName;
- }
-
- @NotNull
- public String getUrl() {
- return myRemoteUrl;
- }
-
- public void setUrl(@NotNull String url) {
- myRemoteUrl = url;
- }
-
- public void fillAuthDataIfNotFilled(@NotNull String login, @Nullable String password) {
- if (myUserName == null) {
- myUserName = login;
- myPassword = password;
- } else if (myPassword != null) {
- myPassword = password;
- }
- }
-
- public void setAlwaysShowDialog(boolean showDialog) {
- myShowDialog = showDialog;
- }
-
- public boolean wasDialogShown() {
- return myDialogShown;
- }
-
- @Nullable
- private static String getUserNameFromUrl(@NotNull String url) {
- Matcher matcher = HTTP_URL_PATTERN.matcher(url);
- if (matcher.matches()) {
- return matcher.group(1);
- }
- return null;
- }
-
- public boolean wasCancelled() {
- return myCancelled;
- }
-}
diff --git a/plugins/git4idea/src/git4idea/jgit/GitHttpRemoteCommand.java b/plugins/git4idea/src/git4idea/jgit/GitHttpRemoteCommand.java
deleted file mode 100644
index 3d2a9653440b..000000000000
--- a/plugins/git4idea/src/git4idea/jgit/GitHttpRemoteCommand.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Copyright 2000-2011 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package git4idea.jgit;
-
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.Function;
-import git4idea.push.GitSimplePushResult;
-import org.eclipse.jgit.api.CloneCommand;
-import org.eclipse.jgit.api.FetchCommand;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.PushCommand;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.InvalidRemoteException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.errors.NotSupportedException;
-import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.*;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.text.MessageFormat;
-import java.util.*;
-
-/**
- * @author Kirill Likhodedov
- */
-interface GitHttpRemoteCommand {
-
- String getUrl();
- void setUrl(String url);
- void run() throws GitAPIException, URISyntaxException, TransportException;
- void cleanup();
- GitHttpCredentialsProvider getCredentialsProvider();
- String getLogString();
- String getCommandString();
-
- class Fetch implements GitHttpRemoteCommand {
-
- private final Git myGit;
- private final GitHttpCredentialsProvider myCredentialsProvider;
- private String myUrl;
- private final List<RefSpec> myRefSpecs;
-
- Fetch(@NotNull Git git, @NotNull GitHttpCredentialsProvider credentialsProvider, @NotNull String url, @NotNull List<RefSpec> refSpecs) {
- myGit = git;
- myCredentialsProvider = credentialsProvider;
- myUrl = url;
- myRefSpecs = refSpecs;
- }
-
- @Override
- public void run() throws GitAPIException {
- FetchCommand fetchCommand = myGit.fetch();
- fetchCommand.setRemote(myUrl);
- fetchCommand.setRefSpecs(myRefSpecs);
- fetchCommand.setCredentialsProvider(myCredentialsProvider);
- fetchCommand.call();
- }
-
- @Override
- public void setUrl(@NotNull String url) {
- myUrl = url;
- }
-
- @Override
- public String getUrl() {
- return myUrl;
- }
-
- @Override
- public GitHttpCredentialsProvider getCredentialsProvider() {
- return myCredentialsProvider;
- }
-
- @Override
- public String getLogString() {
- return getCommandString();
- }
-
- @Override
- public String getCommandString() {
- return String.format("git fetch %s %s", myUrl, getRefspecsAsString(myRefSpecs));
- }
-
- static String getRefspecsAsString(@NotNull List<RefSpec> refSpecs) {
- return StringUtil.join(refSpecs, new Function<RefSpec, String>() {
- @Override
- public String fun(RefSpec spec) {
- return spec.toString();
- }
- }, " ");
- }
-
- @Override
- public void cleanup() {
- }
- }
-
- class Clone implements GitHttpRemoteCommand {
-
- private final File myTargetDirectory;
- private final GitHttpCredentialsProvider myCredentialsProvider;
- private String myUrl;
- @Nullable private Git myGit;
-
- Clone(@NotNull File targetDirectory, @NotNull GitHttpCredentialsProvider credentialsProvider, String url) {
- myTargetDirectory = targetDirectory;
- myCredentialsProvider = credentialsProvider;
- myUrl = url;
- }
-
- @Override
- public void run() throws GitAPIException {
- CloneCommand cloneCommand = Git.cloneRepository();
- cloneCommand.setDirectory(myTargetDirectory);
- cloneCommand.setURI(myUrl);
- cloneCommand.setCredentialsProvider(myCredentialsProvider);
- myGit = cloneCommand.call();
- }
-
- @Override
- public void setUrl(@NotNull String url) {
- myUrl = url;
- }
-
- @Override
- public String getUrl() {
- return myUrl;
- }
-
- @Override
- public GitHttpCredentialsProvider getCredentialsProvider() {
- return myCredentialsProvider;
- }
-
- @Override
- public String getLogString() {
- return getCommandString();
- }
-
- @Override
- public String getCommandString() {
- return String.format("git clone %s %s", myUrl, myTargetDirectory.getPath());
- }
-
- @Override
- public void cleanup() {
- if (myTargetDirectory.exists()) {
- FileUtil.delete(myTargetDirectory);
- }
- }
-
- @Nullable
- public Git getGit() {
- return myGit;
- }
- }
-
- class Push implements GitHttpRemoteCommand {
-
- private final Git myGit;
- private final GitHttpCredentialsProvider myCredentialsProvider;
- private GitSimplePushResult myPushResult;
- private String myRemoteName;
- private String myUrl;
- private final List<RefSpec> myPushSpecs;
-
- Push(@NotNull Git git, @NotNull GitHttpCredentialsProvider credentialsProvider, @NotNull String remoteName, @NotNull String url, @NotNull List<RefSpec> pushSpecs) {
- myGit = git;
- myCredentialsProvider = credentialsProvider;
- myRemoteName = remoteName;
- myUrl = url;
- myPushSpecs = pushSpecs;
- }
-
- @Override
- public void run() throws InvalidRemoteException, URISyntaxException, org.eclipse.jgit.api.errors.TransportException {
- PushCommand pushCommand = myGit.push();
- pushCommand.setRemote(myRemoteName);
- pushCommand.setRefSpecs(myPushSpecs);
- pushCommand.setCredentialsProvider(myCredentialsProvider);
-
- /*
- Need to push to remote NAME (to let push update the remote reference), but to probably another URL.
- So constructing RemoteConfig based on the original config for the remote, but with other url.
- No need in fetch urls => just removing them.
- Remove all push urls (we don't support pushing to multiple urls anyway yet), leaving only single correct url.
- Then pass the url to the push command.
- */
- RemoteConfig rc = new RemoteConfig(myGit.getRepository().getConfig(), myRemoteName);
- List<URIish> uris = new ArrayList<URIish>(rc.getURIs());
- for (URIish uri : uris) {
- rc.removeURI(uri);
- }
- uris = new ArrayList<URIish>(rc.getPushURIs());
- for (URIish uri : uris) {
- rc.removePushURI(uri);
- }
- rc.addPushURI(new URIish(myUrl));
-
- Iterable<PushResult> results = call(pushCommand, rc);
- myPushResult = analyzeResults(results);
- }
-
- @Override
- public void setUrl(@NotNull String url) {
- myUrl = url;
- }
-
- @Override
- public String getUrl() {
- return myUrl;
- }
-
- @Override
- public GitHttpCredentialsProvider getCredentialsProvider() {
- return myCredentialsProvider;
- }
-
- @Override
- public String getLogString() {
- return String.format("git push %s (%s) %s", myRemoteName, myUrl, GitHttpRemoteCommand.Fetch.getRefspecsAsString(myPushSpecs));
- }
-
- @Override
- public String getCommandString() {
- return String.format("git push %s %s", myRemoteName, GitHttpRemoteCommand.Fetch.getRefspecsAsString(myPushSpecs));
- }
-
- @Override
- public void cleanup() {
- }
-
- @Nullable
- GitSimplePushResult getResult() {
- return myPushResult;
- }
-
- @NotNull
- private static GitSimplePushResult analyzeResults(@NotNull Iterable<PushResult> results) {
- Collection<String> rejectedBranches = new ArrayList<String>();
- StringBuilder errorReport = new StringBuilder();
-
- for (PushResult result : results) {
- for (RemoteRefUpdate update : result.getRemoteUpdates()) {
- switch (update.getStatus()) {
- case REJECTED_NONFASTFORWARD:
- rejectedBranches.add(update.getSrcRef());
- // no break: add reject to the output
- case NON_EXISTING:
- case REJECTED_NODELETE:
- case REJECTED_OTHER_REASON:
- case REJECTED_REMOTE_CHANGED:
- errorReport.append(update.getSrcRef() + ": " + update.getStatus() + "<br/>");
- default:
- // on success do nothing
- }
- }
- }
-
- if (!rejectedBranches.isEmpty()) {
- return GitSimplePushResult.reject(rejectedBranches);
- }
- else if (errorReport.toString().isEmpty()) {
- return GitSimplePushResult.success();
- }
- else {
- return GitSimplePushResult.error(errorReport.toString());
- }
- }
-
-
- /*
- A copy-paste from org.eclipse.jgit.api.PushCommand#call with the following differences:
- 1. Fields are not accessible, so they are substituted by getters, except for credentialsProvider, which we have stored as an instance field.
- 2. checkCallable() won't fail (according to the PushCommand code), so it's safe to remove it.
- 3. Actual push is performed via
- Transport.openAll(repo, remoteConfig, Transport.Operation.PUSH)
- instead of
- Transport.openAll(repo, remote, Transport.Operation.PUSH)
- where remoteConfig is passed to the method.
- Original code constructs the remoteConfig based on .git/config.
- */
- @NotNull
- private Iterable<PushResult> call(PushCommand pushCommand, RemoteConfig remoteConfig)
- throws JGitInternalException, InvalidRemoteException, org.eclipse.jgit.api.errors.TransportException
- {
- ArrayList<PushResult> pushResults = new ArrayList<PushResult>(3);
-
- List<RefSpec> refSpecs = pushCommand.getRefSpecs();
- Repository repo = pushCommand.getRepository();
- boolean force = pushCommand.isForce();
- int timeout = pushCommand.getTimeout();
- CredentialsProvider credentialsProvider = myCredentialsProvider;
- String receivePack = pushCommand.getReceivePack();
- boolean thin = pushCommand.isThin();
- boolean dryRun = pushCommand.isDryRun();
- String remote = pushCommand.getRemote();
- ProgressMonitor monitor = pushCommand.getProgressMonitor();
-
- try {
- if (refSpecs.isEmpty()) {
- RemoteConfig config = new RemoteConfig(repo.getConfig(), pushCommand.getRemote());
- refSpecs.addAll(config.getPushRefSpecs());
- }
- if (refSpecs.isEmpty()) {
- Ref head = repo.getRef(Constants.HEAD);
- if (head != null && head.isSymbolic()) {
- refSpecs.add(new RefSpec(head.getLeaf().getName()));
- }
- }
-
- if (force) {
- for (int i = 0; i < refSpecs.size(); i++) {
- refSpecs.set(i, refSpecs.get(i).setForceUpdate(true));
- }
- }
-
- final List<Transport> transports;
- transports = Transport.openAll(repo, remoteConfig, Transport.Operation.PUSH);
- for (final Transport transport : transports) {
- if (0 <= timeout) {
- transport.setTimeout(timeout);
- }
- transport.setPushThin(thin);
- if (receivePack != null) {
- transport.setOptionReceivePack(receivePack);
- }
- transport.setDryRun(dryRun);
- if (credentialsProvider != null) {
- transport.setCredentialsProvider(credentialsProvider);
- }
-
- final Collection<RemoteRefUpdate> toPush = transport
- .findRemoteRefUpdatesFor(refSpecs);
-
- try {
- PushResult result = transport.push(monitor, toPush);
- pushResults.add(result);
- }
- catch (TransportException e) {
- throw new org.eclipse.jgit.api.errors.TransportException(e.getMessage(), e);
- }
- finally {
- transport.close();
- }
- }
- }
- catch (URISyntaxException e) {
- throw new InvalidRemoteException(MessageFormat.format(
- JGitText.get().invalidRemote, remote));
- } catch (TransportException e) {
- throw new org.eclipse.jgit.api.errors.TransportException(
- e.getMessage(), e);
- }
- catch (NotSupportedException e) {
- throw new JGitInternalException(
- JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
- e);
- }
- catch (IOException e) {
- throw new JGitInternalException(
- JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
- e);
- }
-
- return pushResults;
- }
- }
-
- class LsRemote implements GitHttpRemoteCommand {
-
- private final Git myGit;
- private final GitHttpCredentialsProvider myCredentialsProvider;
- private String myUrl;
- private Collection<Ref> myResultRefs;
-
- public LsRemote(@NotNull Git git, @NotNull GitHttpCredentialsProvider credentialsProvider, @NotNull String url) {
- myGit = git;
- myCredentialsProvider = credentialsProvider;
- myUrl = url;
- }
-
- @Override
- public void run() throws InvalidRemoteException, TransportException {
- myResultRefs = call();
- }
-
- @Override
- public void cleanup() {
- }
-
- @Override
- public GitHttpCredentialsProvider getCredentialsProvider() {
- return myCredentialsProvider;
- }
-
- @Override
- public String getLogString() {
- return getCommandString();
- }
-
- @Override
- public String getCommandString() {
- return String.format("git ls-remote --heads %s ", myUrl);
- }
-
- @Override
- public String getUrl() {
- return myUrl;
- }
-
- @Override
- public void setUrl(@NotNull String url) {
- myUrl = url;
- }
-
- @NotNull
- public Collection<Ref> getRefs() {
- return myResultRefs == null ? Collections.<Ref>emptyList() : myResultRefs;
- }
-
- /*
- Copy-paste of org.eclipse.jgit.api.LsRemote#call with the following changes:
- 1. More specific exceptions declaration.
- 2. Use CredentialsProvider.
- 3. We don't need --tags, we always need --heads.
- */
- private Collection<Ref> call() throws TransportException, InvalidRemoteException {
- try {
- Transport transport = Transport.open(myGit.getRepository(), myUrl);
-
- try {
- Collection<RefSpec> refSpecs = new ArrayList<RefSpec>(1);
- refSpecs.add(new RefSpec("refs/heads/*:refs/remotes/origin/*"));
- Collection<Ref> refs;
- Map<String, Ref> refmap = new HashMap<String, Ref>();
- transport.setCredentialsProvider(myCredentialsProvider);
- FetchConnection fc = transport.openFetch();
- try {
- refs = fc.getRefs();
- if (refSpecs.isEmpty()) {
- for (Ref r : refs) {
- refmap.put(r.getName(), r);
- }
- }
- else {
- for (Ref r : refs) {
- for (RefSpec rs : refSpecs) {
- if (rs.matchSource(r)) {
- refmap.put(r.getName(), r);
- break;
- }
- }
- }
- }
- }
- finally {
- fc.close();
- }
- return refmap.values();
- }
- catch (TransportException e) {
- throw new JGitInternalException(
- JGitText.get().exceptionCaughtDuringExecutionOfLsRemoteCommand,
- e);
- }
- finally {
- transport.close();
- }
- }
- catch (URISyntaxException e) {
- throw new InvalidRemoteException(MessageFormat.format(
- JGitText.get().invalidRemote, myUrl));
- }
- catch (NotSupportedException e) {
- throw new JGitInternalException(
- JGitText.get().exceptionCaughtDuringExecutionOfLsRemoteCommand,
- e);
- }
- }
- }
-}
-
-
diff --git a/plugins/git4idea/src/git4idea/merge/GitMergeDialog.java b/plugins/git4idea/src/git4idea/merge/GitMergeDialog.java
index 294227718670..f8a05816ce26 100644
--- a/plugins/git4idea/src/git4idea/merge/GitMergeDialog.java
+++ b/plugins/git4idea/src/git4idea/merge/GitMergeDialog.java
@@ -27,6 +27,7 @@ import git4idea.commands.GitSimpleHandler;
import git4idea.i18n.GitBundle;
import git4idea.util.GitUIUtil;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -88,15 +89,6 @@ public class GitMergeDialog extends DialogWrapper {
@NotNull private final Project myProject;
private final GitVcs myVcs;
-
-
- /**
- * A constructor
- *
- * @param project a project to select
- * @param roots a git repository roots for the project
- * @param defaultRoot a guessed default root
- */
public GitMergeDialog(@NotNull Project project, List<VirtualFile> roots, VirtualFile defaultRoot) {
super(project, true);
setTitle(GitBundle.getString("merge.branch.title"));
@@ -125,9 +117,6 @@ public class GitMergeDialog extends DialogWrapper {
init();
}
- /**
- * Initialize {@link #myBranchChooser} component
- */
private void initBranchChooser() {
myBranchChooser = new ElementsChooser<String>(true);
myBranchChooser.setToolTipText(GitBundle.getString("merge.branches.tooltip"));
@@ -149,6 +138,11 @@ public class GitMergeDialog extends DialogWrapper {
myBranchChooser.addElementsMarkListener(listener);
}
+ @Nullable
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myBranchChooser.getComponent();
+ }
/**
* Setup branches for git root, this method should be called when root is changed.
@@ -204,32 +198,20 @@ public class GitMergeDialog extends DialogWrapper {
}
- /**
- * {@inheritDoc}
- */
protected JComponent createCenterPanel() {
return myPanel;
}
- /**
- * {@inheritDoc}
- */
@Override
protected String getDimensionServiceKey() {
return getClass().getName();
}
- /**
- * {@inheritDoc}
- */
@Override
protected String getHelpId() {
return "reference.VersionControl.Git.MergeBranches";
}
- /**
- * @return selected root
- */
public VirtualFile getSelectedRoot() {
return (VirtualFile)myGitRoot.getSelectedItem();
}
diff --git a/plugins/git4idea/src/git4idea/push/GitPusher.java b/plugins/git4idea/src/git4idea/push/GitPusher.java
index 31e289dba806..778d2dcd0033 100644
--- a/plugins/git4idea/src/git4idea/push/GitPusher.java
+++ b/plugins/git4idea/src/git4idea/push/GitPusher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2011 JetBrains s.r.o.
+ * 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.
@@ -36,7 +36,6 @@ import git4idea.config.GitConfigUtil;
import git4idea.config.GitVcsSettings;
import git4idea.config.UpdateMethod;
import git4idea.history.GitHistoryUtils;
-import git4idea.jgit.GitHttpAdapter;
import git4idea.repo.GitBranchTrackInfo;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
@@ -48,7 +47,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Collects information to push and performs the push.
@@ -321,13 +320,8 @@ public final class GitPusher {
}
String url = pushUrls.iterator().next();
GitSimplePushResult pushResult;
- if (GitHttpAdapter.shouldUseJGit(url)) {
- pushResult = GitHttpAdapter.push(repository, remote.getName(), url, formPushSpec(pushSpec, remote));
- }
- else {
- pushResult = pushNatively(repository, pushSpec, url);
- }
-
+ pushResult = pushNatively(repository, pushSpec, url);
+
if (pushResult.getType() == GitSimplePushResult.Type.SUCCESS) {
setUpstream(repository, pushSpec.getSource(), pushSpec.getRemote(), pushSpec.getDest());
}
@@ -478,9 +472,7 @@ public final class GitPusher {
// and don't show the dialog again if user has chosen not to ask again
updateSettings = readUpdateSettings();
if (!mySettings.autoUpdateIfPushRejected()) {
- final GitRejectedPushUpdateDialog dialog = new GitRejectedPushUpdateDialog(myProject, rejectedPushesForCurrentBranch.keySet(), updateSettings);
- final int exitCode = showDialogAndGetExitCode(dialog);
- updateSettings = new UpdateSettings(dialog.shouldUpdateAll(), getUpdateMethodFromDialogExitCode(exitCode));
+ updateSettings = showDialogAndGetExitCode(rejectedPushesForCurrentBranch, updateSettings);
saveUpdateSettings(updateSettings);
}
}
@@ -519,20 +511,23 @@ public final class GitPusher {
return new UpdateSettings(updateAllRoots, updateMethod);
}
- private int showDialogAndGetExitCode(@NotNull final GitRejectedPushUpdateDialog dialog) {
- final AtomicInteger exitCode = new AtomicInteger();
+ private UpdateSettings showDialogAndGetExitCode(final Map<GitRepository, GitBranch> rejectedPushesForCurrentBranch,
+ final UpdateSettings initialSettings) {
+ final AtomicReference<UpdateSettings> updateSettings = new AtomicReference<UpdateSettings>();
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
+ final GitRejectedPushUpdateDialog dialog = new GitRejectedPushUpdateDialog(myProject, rejectedPushesForCurrentBranch.keySet(), initialSettings);
dialog.show();
- exitCode.set(dialog.getExitCode());
- }
+ final int exitCode = dialog.getExitCode();
+ if (exitCode != DialogWrapper.CANCEL_EXIT_CODE) {
+ mySettings.setAutoUpdateIfPushRejected(dialog.shouldAutoUpdateInFuture());
+ }
+ updateSettings.set(new UpdateSettings(dialog.shouldUpdateAll(), getUpdateMethodFromDialogExitCode(exitCode)));
+
+ }
});
- int code = exitCode.get();
- if (code != DialogWrapper.CANCEL_EXIT_CODE) {
- mySettings.setAutoUpdateIfPushRejected(dialog.shouldAutoUpdateInFuture());
- }
- return code;
+ return updateSettings.get();
}
/**
diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaser.java b/plugins/git4idea/src/git4idea/rebase/GitRebaser.java
index e0421b7bf67e..a85a328fc114 100644
--- a/plugins/git4idea/src/git4idea/rebase/GitRebaser.java
+++ b/plugins/git4idea/src/git4idea/rebase/GitRebaser.java
@@ -128,8 +128,8 @@ public class GitRebaser {
return allMerged ? GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS : GitUpdateResult.INCOMPLETE;
} else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) {
LOG.info("handleRebaseFailure: untracked files would be overwritten by checkout");
- UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject,
- untrackedWouldBeOverwrittenDetector.getFiles(), "rebase", null);
+ UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root,
+ untrackedWouldBeOverwrittenDetector.getRelativeFilePaths(), "rebase", null);
return GitUpdateResult.ERROR;
} else {
LOG.info("handleRebaseFailure error " + pullHandler.errors());
@@ -382,8 +382,8 @@ public class GitRebaser {
return allMerged ? GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS : GitUpdateResult.INCOMPLETE;
} else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) {
LOG.info("handleRebaseFailure: untracked files would be overwritten by checkout");
- UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject,
- untrackedWouldBeOverwrittenDetector.getFiles(), "rebase", null);
+ UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root,
+ untrackedWouldBeOverwrittenDetector.getRelativeFilePaths(), "rebase", null);
return GitUpdateResult.ERROR;
} else {
LOG.info("handleRebaseFailure error " + handler.errors());
diff --git a/plugins/git4idea/src/git4idea/jgit/GitHttpAuthDataProvider.java b/plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java
index af4229d3ac25..6c0860ccf20a 100644
--- a/plugins/git4idea/src/git4idea/jgit/GitHttpAuthDataProvider.java
+++ b/plugins/git4idea/src/git4idea/remote/GitHttpAuthDataProvider.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package git4idea.jgit;
+package git4idea.remote;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.util.AuthData;
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
- * Provides authentication information to the {@link GitHttpAdapter} on attempt to connect an HTTP remote.
+ * Provides authentication information to the {@link git4idea.commands.GitHttpAuthenticator} on attempt to connect an HTTP remote.
* Useful for reusing Github credentials stored in the settings to connect the github remote (IDEA-87530).
*
* @author Kirill Likhodedov
diff --git a/plugins/git4idea/src/git4idea/repo/GitConfig.java b/plugins/git4idea/src/git4idea/repo/GitConfig.java
index a88e7754b78e..4b0bdeadc8f9 100644
--- a/plugins/git4idea/src/git4idea/repo/GitConfig.java
+++ b/plugins/git4idea/src/git4idea/repo/GitConfig.java
@@ -149,7 +149,7 @@ public class GitConfig {
ini.load(configFile);
}
catch (IOException e) {
- LOG.error(new RepoStateException("Couldn't load .git/config file at " + configFile.getPath(), e));
+ LOG.warn(new RepoStateException("Couldn't load .git/config file at " + configFile.getPath(), e));
return emptyConfig;
}
diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryReader.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryReader.java
index 366bbde88050..5109d0600b69 100644
--- a/plugins/git4idea/src/git4idea/repo/GitRepositoryReader.java
+++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryReader.java
@@ -221,7 +221,8 @@ class GitRepositoryReader {
return hashAndName.name.endsWith(ref);
}
});
- return hashAndNames.get(0).hash;
+ HashAndName item = ContainerUtil.getFirstItem(hashAndNames);
+ return item == null ? null : item.hash;
}
/**
diff --git a/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java
index a0ae58667c70..6bfa7aa090eb 100644
--- a/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java
+++ b/plugins/git4idea/src/git4idea/stash/GitShelveChangesSaver.java
@@ -107,7 +107,7 @@ public class GitShelveChangesSaver extends GitChangesSaver {
@Override
protected boolean wereChangesSaved() {
- return myShelvedLists != null;
+ return myShelvedLists != null && !myShelvedLists.isEmpty();
}
@Override
diff --git a/plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java b/plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java
new file mode 100644
index 000000000000..a33a7d13704a
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/ui/ChangesBrowserWithRollback.java
@@ -0,0 +1,67 @@
+/*
+ * 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.ui;
+
+import com.intellij.openapi.actionSystem.EmptyAction;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.actions.RollbackDialogAction;
+import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * {@link ChangesBrowser} extension with Rollback/Revert action added to the toolbar.
+ * After the revert completes, the changes list is automatically refreshed according to the actual changes
+ * retrieved from the {@link ChangeListManager}.
+ */
+public class ChangesBrowserWithRollback extends ChangesBrowser {
+ private final List<Change> myOriginalChanges;
+
+ public ChangesBrowserWithRollback(@NotNull Project project, @NotNull List<Change> changes) {
+ super(project, null, changes, null, false, true, null, MyUseCase.LOCAL_CHANGES, null);
+ myOriginalChanges = changes;
+ RollbackDialogAction rollback = new RollbackDialogAction();
+ EmptyAction.setupAction(rollback, IdeActions.CHANGES_VIEW_ROLLBACK, this);
+ addToolbarAction(rollback);
+ setChangesToDisplay(changes);
+ }
+
+ @Override
+ public void rebuildList() {
+ if (myOriginalChanges != null) { // null is possible because rebuildList is called during initialization
+ myChangesToDisplay = filterActualChanges(myProject, myOriginalChanges);
+ }
+ super.rebuildList();
+ }
+
+ @NotNull
+ private static List<Change> filterActualChanges(@NotNull Project project, @NotNull List<Change> originalChanges) {
+ final Collection<Change> allChanges = ChangeListManager.getInstance(project).getAllChanges();
+ return ContainerUtil.filter(originalChanges, new Condition<Change>() {
+ @Override
+ public boolean value(Change change) {
+ return allChanges.contains(change);
+ }
+ });
+ }
+}
diff --git a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java
index 63b51a8c4b56..3c8d7100c50c 100644
--- a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java
+++ b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java
@@ -29,7 +29,9 @@ import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
@@ -49,6 +51,7 @@ import git4idea.merge.GitConflictResolver;
import git4idea.repo.GitRepository;
import git4idea.stash.GitStashUtils;
import git4idea.util.GitUIUtil;
+import git4idea.util.UntrackedFilesNotifier;
import git4idea.validators.GitBranchNameValidator;
import org.jetbrains.annotations.NotNull;
@@ -397,7 +400,7 @@ public class GitUnstashDialog extends DialogWrapper {
@Override
protected void doOKAction() {
VirtualFile root = getGitRoot();
- GitLineHandler h = handler();
+ final GitLineHandler h = handler();
final AtomicBoolean conflict = new AtomicBoolean();
h.addLineListener(new GitLineHandlerAdapter() {
@@ -407,13 +410,27 @@ public class GitUnstashDialog extends DialogWrapper {
}
}
});
- int rc = GitHandlerUtil.doSynchronously(h, GitBundle.getString("unstash.unstashing"), h.printableCommandLine(), false);
- ServiceManager.getService(myProject, GitPlatformFacade.class).hardRefresh(root);
+ GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root);
+ h.addLineListener(untrackedFilesDetector);
+ 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) {
+ h.addLineListener(new GitHandlerUtil.GitLineHandlerListenerProgress(indicator, h, "stash", false));
+ Git git = ServiceManager.getService(Git.class);
+ result.set(git.runCommand(new Computable.PredefinedValueComputable<GitLineHandler>(h)));
+ }
+ });
+
+ ServiceManager.getService(myProject, GitPlatformFacade.class).hardRefresh(root);
+ GitCommandResult res = result.get();
if (conflict.get()) {
boolean conflictsResolved = new UnstashConflictResolver(myProject, root, getSelectedStash()).merge();
LOG.info("loadRoot " + root + ", conflictsResolved: " + conflictsResolved);
- } else if (rc != 0) {
+ } else if (untrackedFilesDetector.wasMessageDetected()) {
+ UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root, untrackedFilesDetector.getRelativeFilePaths(),
+ "unstash", null);
+ } else if (!res.success()) {
GitUIUtil.showOperationErrors(myProject, h.errors(), h.printableCommandLine());
}
super.doOKAction();
diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java
index 645fb24bcd1e..bd9c8fb3a1b3 100644
--- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java
+++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java
@@ -29,6 +29,8 @@ import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.ui.popup.list.ListPopupImpl;
+import com.intellij.util.containers.ContainerUtil;
+import git4idea.GitLocalBranch;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.branch.GitBranchUtil;
@@ -184,7 +186,7 @@ class GitBranchPopup {
GitRepositoryManager repositoryManager = myRepositoryManager;
if (repositoryManager.moreThanOneRoot()) {
- if (!myMultiRootBranchConfig.diverged() && userWantsSyncControl()) {
+ if (userWantsSyncControl()) {
fillWithCommonRepositoryActions(popupGroup, repositoryManager);
}
else {
@@ -204,26 +206,37 @@ class GitBranchPopup {
}
private void fillWithCommonRepositoryActions(DefaultActionGroup popupGroup, GitRepositoryManager repositoryManager) {
- List<GitRepository> repositories = repositoryManager.getRepositories();
- String currentBranch = myMultiRootBranchConfig.getCurrentBranch();
- assert currentBranch != null : "Current branch can't be null if branches have not diverged";
- popupGroup.add(new GitBranchPopupActions.GitNewBranchAction(myProject, repositories));
+ List<GitRepository> allRepositories = repositoryManager.getRepositories();
+ popupGroup.add(new GitBranchPopupActions.GitNewBranchAction(myProject, allRepositories));
popupGroup.addAll(createRepositoriesActions());
popupGroup.addSeparator("Common Local Branches");
for (String branch : myMultiRootBranchConfig.getLocalBranches()) {
- if (!branch.equals(currentBranch)) {
+ List<GitRepository> repositories = filterRepositoriesNotOnThisBranch(branch, allRepositories);
+ if (!repositories.isEmpty()) {
popupGroup.add(new GitBranchPopupActions.LocalBranchActions(myProject, repositories, branch, myCurrentRepository));
}
}
popupGroup.addSeparator("Common Remote Branches");
for (String branch : myMultiRootBranchConfig.getRemoteBranches()) {
- popupGroup.add(new GitBranchPopupActions.RemoteBranchActions(myProject, repositories, branch, myCurrentRepository));
+ popupGroup.add(new GitBranchPopupActions.RemoteBranchActions(myProject, allRepositories, branch, myCurrentRepository));
}
}
+ @NotNull
+ private static List<GitRepository> filterRepositoriesNotOnThisBranch(@NotNull final String branch,
+ @NotNull List<GitRepository> allRepositories) {
+ return ContainerUtil.filter(allRepositories, new Condition<GitRepository>() {
+ @Override
+ public boolean value(GitRepository repository) {
+ GitLocalBranch currentBranch = repository.getCurrentBranch();
+ return currentBranch == null || !branch.equals(currentBranch.getName());
+ }
+ });
+ }
+
private void warnThatBranchesDivergedIfNeeded() {
if (myRepositoryManager.moreThanOneRoot() && myMultiRootBranchConfig.diverged() && userWantsSyncControl()) {
myPopup.setWarning("Branches have diverged");
diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java b/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java
index d9cbbd781f1c..89ff9f47268f 100644
--- a/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java
+++ b/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java
@@ -18,7 +18,7 @@ package git4idea.ui.branch;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.FrameWrapper;
import com.intellij.ui.TabbedPaneImpl;
import git4idea.GitUtil;
import git4idea.repo.GitRepository;
@@ -30,25 +30,22 @@ import javax.swing.*;
/**
* Dialog for comparing two Git branches.
- * @author Kirill Likhodedov
*/
-public class GitCompareBranchesDialog extends DialogWrapper {
+public class GitCompareBranchesDialog extends FrameWrapper {
- private final Project myProject;
- private final String myBranchName;
- private final String myCurrentBranchName;
- private final GitCommitCompareInfo myCompareInfo;
- private final GitRepository myInitialRepo;
- private JPanel myLogPanel;
+ @NotNull private final Project myProject;
+ @NotNull private final String myBranchName;
+ @NotNull private final String myCurrentBranchName;
+ @NotNull private final GitCommitCompareInfo myCompareInfo;
+ @NotNull private final JPanel myLogPanel;
public GitCompareBranchesDialog(@NotNull Project project, @NotNull String branchName, @NotNull String currentBranchName,
@NotNull GitCommitCompareInfo compareInfo, @NotNull GitRepository initialRepo) {
- super(project, false);
+ super(project, GitCompareBranchesDialog.class.getName());
myCurrentBranchName = currentBranchName;
myCompareInfo = compareInfo;
myProject = project;
myBranchName = branchName;
- myInitialRepo = initialRepo;
String rootString;
if (compareInfo.getRepositories().size() == 1 && GitUtil.getRepositoryManager(myProject).moreThanOneRoot()) {
@@ -58,13 +55,15 @@ public class GitCompareBranchesDialog extends DialogWrapper {
rootString = "";
}
setTitle(String.format("Comparing %s with %s%s", currentBranchName, branchName, rootString));
- setModal(false);
- init();
+
+ myLogPanel = new GitCompareBranchesLogPanel(myProject, myBranchName, myCurrentBranchName, myCompareInfo, initialRepo);
+ setPreferredFocusedComponent(myLogPanel);
+ setComponent(createCenterPanel());
+ closeOnEsc();
}
- @Override
+ @NotNull
protected JComponent createCenterPanel() {
- myLogPanel = new GitCompareBranchesLogPanel(myProject, myBranchName, myCurrentBranchName, myCompareInfo, myInitialRepo);
JPanel diffPanel = new GitCompareBranchesDiffPanel(myProject, myBranchName, myCurrentBranchName, myCompareInfo);
TabbedPaneImpl tabbedPane = new TabbedPaneImpl(SwingConstants.TOP);
@@ -74,20 +73,4 @@ public class GitCompareBranchesDialog extends DialogWrapper {
return tabbedPane;
}
- // it is information dialog - no need to OK or Cancel. Close the dialog by clicking the cross button or pressing Esc.
- @NotNull
- @Override
- protected Action[] createActions() {
- return new Action[0];
- }
-
- @Override
- protected String getDimensionServiceKey() {
- return GitCompareBranchesDialog.class.getName();
- }
-
- @Override
- public JComponent getPreferredFocusedComponent() {
- return myLogPanel;
- }
}
diff --git a/plugins/git4idea/src/git4idea/update/GitFetcher.java b/plugins/git4idea/src/git4idea/update/GitFetcher.java
index 44dbb54343a3..f9c65632d9be 100644
--- a/plugins/git4idea/src/git4idea/update/GitFetcher.java
+++ b/plugins/git4idea/src/git4idea/update/GitFetcher.java
@@ -34,7 +34,6 @@ import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitLineHandlerAdapter;
import git4idea.commands.GitLineHandlerListener;
-import git4idea.jgit.GitHttpAdapter;
import git4idea.repo.GitBranchTrackInfo;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
@@ -132,9 +131,6 @@ public class GitFetcher {
@NotNull GitRemote remote,
@NotNull String url,
@Nullable String branch) {
- if (GitHttpAdapter.shouldUseJGit(url)) {
- return GitHttpAdapter.fetch(repository, remote, url, branch);
- }
return fetchNatively(repository, remote, url, branch);
}
@@ -150,9 +146,6 @@ public class GitFetcher {
GitRemote remote = fetchParams.getRemote();
String remoteBranch = fetchParams.getRemoteBranch().getNameForRemoteOperations();
String url = fetchParams.getUrl();
- if (GitHttpAdapter.shouldUseJGit(url)) {
- return GitHttpAdapter.fetch(repository, remote, url, remoteBranch);
- }
return fetchNatively(repository, remote, url, remoteBranch);
}
@@ -191,22 +184,11 @@ public class GitFetcher {
LOG.error("URL is null for remote " + remote.getName());
continue;
}
- if (GitHttpAdapter.shouldUseJGit(url)) {
- GitFetchResult res = GitHttpAdapter.fetch(repository, remote, url, null);
- res.addPruneInfo(fetchResult.getPrunedRefs());
- fetchResult = res;
- myErrors.addAll(fetchResult.getErrors());
- if (!fetchResult.isSuccess()) {
- break;
- }
- }
- else {
- GitFetchResult res = fetchNatively(repository, remote, url, null);
- res.addPruneInfo(fetchResult.getPrunedRefs());
- fetchResult = res;
- if (!fetchResult.isSuccess()) {
- break;
- }
+ GitFetchResult res = fetchNatively(repository, remote, url, null);
+ res.addPruneInfo(fetchResult.getPrunedRefs());
+ fetchResult = res;
+ if (!fetchResult.isSuccess()) {
+ break;
}
}
return fetchResult;
diff --git a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java
index 4815eb781fce..4315e65227a3 100644
--- a/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java
+++ b/plugins/git4idea/src/git4idea/update/GitMergeUpdater.java
@@ -119,15 +119,15 @@ public class GitMergeUpdater extends GitUpdater {
LOG.info("Local changes would be overwritten by merge");
final List<FilePath> paths = getFilesOverwrittenByMerge(mergeLineListener.getOutput());
final Collection<Change> changes = getLocalChangesFilteredByFiles(paths);
- final ChangeListViewerDialog dialog = new ChangeListViewerDialog(myProject, changes, false) {
- @Override protected String getDescription() {
- return "Your local changes to the following files would be overwritten by merge.<br/>" +
- "Please, commit your changes or stash them before you can merge.";
- }
- };
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
+ ChangeListViewerDialog dialog = new ChangeListViewerDialog(myProject, changes, false) {
+ @Override protected String getDescription() {
+ return "Your local changes to the following files would be overwritten by merge.<br/>" +
+ "Please, commit your changes or stash them before you can merge.";
+ }
+ };
dialog.show();
}
});
@@ -135,8 +135,9 @@ public class GitMergeUpdater extends GitUpdater {
}
else if (untrackedFilesWouldBeOverwrittenByMergeDetector.wasMessageDetected()) {
LOG.info("handleMergeFailure: untracked files would be overwritten by merge");
- UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject,
- untrackedFilesWouldBeOverwrittenByMergeDetector.getFiles(), "merge", null);
+ UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, myRoot,
+ untrackedFilesWouldBeOverwrittenByMergeDetector.getRelativeFilePaths(),
+ "merge", null);
return GitUpdateResult.ERROR;
}
else {
diff --git a/plugins/git4idea/src/git4idea/util/GitSimplePathsBrowser.java b/plugins/git4idea/src/git4idea/util/GitSimplePathsBrowser.java
new file mode 100644
index 000000000000..fc37dcce3cd7
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/util/GitSimplePathsBrowser.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util;
+
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vcs.FilePathImpl;
+import com.intellij.openapi.vcs.changes.ui.FilePathChangesTreeList;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+public class GitSimplePathsBrowser extends JPanel {
+
+ public GitSimplePathsBrowser(@NotNull Project project, @NotNull Collection<String> absolutePaths) {
+ super(new BorderLayout());
+
+ FilePathChangesTreeList browser = createBrowser(project, absolutePaths);
+ ActionToolbar toolbar = createToolbar(browser);
+
+ add(toolbar.getComponent(), BorderLayout.NORTH);
+ add(browser);
+ }
+
+ @NotNull
+ private static FilePathChangesTreeList createBrowser(@NotNull Project project, @NotNull Collection<String> absolutePaths) {
+ List<FilePath> filePaths = toFilePaths(absolutePaths);
+ FilePathChangesTreeList browser = new FilePathChangesTreeList(project, filePaths, false, false, null, null);
+ browser.setChangesToDisplay(filePaths);
+ return browser;
+ }
+
+ @NotNull
+ private static ActionToolbar createToolbar(@NotNull FilePathChangesTreeList browser) {
+ DefaultActionGroup actionGroup = new DefaultActionGroup(browser.getTreeActions());
+ return ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actionGroup, true);
+ }
+
+ @NotNull
+ private static List<FilePath> toFilePaths(@NotNull Collection<String> absolutePaths) {
+ return ContainerUtil.map(absolutePaths, new Function<String, FilePath>() {
+ @Override
+ public FilePath fun(String path) {
+ return new FilePathImpl(new File(path), false);
+ }
+ });
+ }
+}
diff --git a/plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java b/plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java
new file mode 100644
index 000000000000..021ce824b9a7
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java
@@ -0,0 +1,85 @@
+/*
+ * 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.util;
+
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationListener;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogBuilder;
+import com.intellij.openapi.ui.ex.MultiLineLabel;
+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.VirtualFile;
+import git4idea.GitUtil;
+import git4idea.ui.ChangesBrowserWithRollback;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.event.HyperlinkEvent;
+import java.util.Collection;
+import java.util.List;
+
+public class LocalChangesWouldBeOverwrittenHelper {
+
+ @NotNull
+ public static String getErrorNotificationDescription() {
+ return getErrorDescription(true);
+ }
+
+ @NotNull
+ public static String getErrorDialogDescription() {
+ return getErrorDescription(false);
+ }
+
+ @NotNull
+ private static String getErrorDescription(boolean forNotification) {
+ String line1 = "Your local changes would be overwritten by merge.";
+ String line2 = "Commit, stash or revert them to proceed.";
+ if (forNotification) {
+ return line1 + "<br/>" + line2 + " <a href='view'>View them</a>";
+ }
+ else {
+ return line1 + "\n" + line2;
+ }
+ }
+
+ public static void showErrorNotification(@NotNull final Project project, @NotNull VirtualFile root, @NotNull final String operationName,
+ @NotNull final Collection<String> relativeFilePaths) {
+ final Collection<String> absolutePaths = GitUtil.toAbsolute(root, relativeFilePaths);
+ final List<Change> changes = GitUtil.findLocalChangesForPaths(project, root, absolutePaths, false);
+ String notificationTitle = "Git " + StringUtil.capitalize(operationName) + " Failed";
+ VcsNotifier.getInstance(project).notifyError(notificationTitle, getErrorNotificationDescription(),
+ new NotificationListener.Adapter() {
+ @Override
+ protected void hyperlinkActivated(@NotNull Notification notification,
+ @NotNull HyperlinkEvent e) {
+ String title = "Local Changes Prevent from " + StringUtil.capitalize(operationName);
+ String description = getErrorDialogDescription();
+ if (changes.isEmpty()) {
+ GitUtil.showPathsInDialog(project, absolutePaths, title, description);
+ }
+ else {
+ DialogBuilder builder = new DialogBuilder(project);
+ builder.setNorthPanel(new MultiLineLabel(description));
+ builder.setCenterPanel(new ChangesBrowserWithRollback(project, changes));
+ builder.addOkAction();
+ builder.setTitle(title);
+ builder.show();
+ }
+ }
+ });
+ }
+}
diff --git a/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java b/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java
index 6a867933e6aa..382d51b42032 100644
--- a/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java
+++ b/plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java
@@ -18,10 +18,14 @@ package git4idea.util;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.changes.ui.SelectFilesDialog;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import git4idea.GitUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -29,6 +33,7 @@ import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
public class UntrackedFilesNotifier {
@@ -38,24 +43,40 @@ public class UntrackedFilesNotifier {
/**
* Displays notification about {@code untracked files would be overwritten by checkout} error.
* Clicking on the link in the notification opens a simple dialog with the list of these files.
+ * @param root
+ * @param relativePaths
* @param operation the name of the Git operation that caused the error: {@code rebase, merge, checkout}.
* @param description the content of the notification or null if the deafult content is to be used.
*/
public static void notifyUntrackedFilesOverwrittenBy(@NotNull final Project project,
- @NotNull final Collection<VirtualFile> untrackedFiles,
+ @NotNull final VirtualFile root, @NotNull Collection<String> relativePaths,
@NotNull final String operation, @Nullable String description) {
final String notificationTitle = StringUtil.capitalize(operation) + " failed";
final String notificationDesc = description == null ? createUntrackedFilesOverwrittenDescription(operation, true) : description;
- VcsNotifier.getInstance(project).notifyError(notificationTitle, notificationDesc,
- new NotificationListener() {
+ final Collection<String> absolutePaths = GitUtil.toAbsolute(root, relativePaths);
+ final List<VirtualFile> untrackedFiles = ContainerUtil.mapNotNull(absolutePaths, new Function<String, VirtualFile>() {
+ @Override
+ public VirtualFile fun(String absolutePath) {
+ return GitUtil.findRefreshFileOrLog(absolutePath);
+ }
+ });
+
+ VcsNotifier.getInstance(project).notifyError(notificationTitle, notificationDesc, new NotificationListener() {
@Override
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
final String dialogDesc = createUntrackedFilesOverwrittenDescription(operation, false);
- SelectFilesDialog dlg = new UntrackedFilesDialog(project, untrackedFiles, dialogDesc);
- dlg.setTitle("Untracked Files Preventing " + StringUtil.capitalize(operation));
- dlg.show();
+ String title = "Untracked Files Preventing " + StringUtil.capitalize(operation);
+ if (untrackedFiles.isEmpty()) {
+ GitUtil.showPathsInDialog(project, absolutePaths, title, dialogDesc);
+ }
+ else {
+ DialogWrapper dialog;
+ dialog = new UntrackedFilesDialog(project, untrackedFiles, dialogDesc);
+ dialog.setTitle(title);
+ dialog.show();
+ }
}
}
});
diff --git a/plugins/git4idea/src/org/hanuna/gitalk/git/reader/util/GitException.java b/plugins/git4idea/src/org/hanuna/gitalk/git/reader/util/GitException.java
deleted file mode 100644
index fae6082502c9..000000000000
--- a/plugins/git4idea/src/org/hanuna/gitalk/git/reader/util/GitException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.hanuna.gitalk.git.reader.util;
-
-/**
- * @author erokhins
- */
-public class GitException extends RuntimeException {
- public GitException(String message) {
- super(message);
- }
-
-}
diff --git a/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy b/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy
index e00f0ddafe19..35acc41fc3cc 100644
--- a/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy
+++ b/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy
@@ -75,10 +75,6 @@ class GitBranchWorkerTest extends GitPlatformTest {
}
}
- public void tearDown() {
- super.tearDown();
- }
-
public void "test create new branch without problems"() {
checkoutNewBranch "feature", [ ]
@@ -205,7 +201,7 @@ class GitBranchWorkerTest extends GitPlatformTest {
boolean notificationShown = false;
checkoutOrMerge operation, "feature", [
- showUntrackedFilesNotification : { String s, Collection c -> notificationShown = true }
+ showUntrackedFilesNotification: { String s, VirtualFile root, Collection c -> notificationShown = true }
]
assertTrue "Untracked files notification was not shown", notificationShown
@@ -224,15 +220,15 @@ class GitBranchWorkerTest extends GitPlatformTest {
def untracked = ["untracked.txt"]
untrackedFileOverwrittenBy(myCommunity, "feature", untracked)
- Collection<VirtualFile> untrackedFiles = null;
+ Collection<String> untrackedPaths = null;
checkoutOrMerge operation, "feature", [
- showUntrackedFilesDialogWithRollback : { String s, String p, Collection files -> untrackedFiles = files; false }
+ showUntrackedFilesDialogWithRollback: {
+ String s, String p, VirtualFile root, Collection<String> files -> untrackedPaths = files; false
+ }
]
- assertTrue "Untracked files dialog was not shown", untrackedFiles != null
- assertEquals "Incorrect set of untracked files was shown in the dialog",
- untracked,
- untrackedFiles.collect { FileUtil.getRelativePath(myCommunity.root.path, it.path, '/'.toCharacter()) }
+ assertTrue "Untracked files dialog was not shown", untrackedPaths != null
+ assertEquals "Incorrect set of untracked files was shown in the dialog", untracked.asList(), untrackedPaths.asList()
}
public void "test checkout with local changes overwritten by checkout should show smart checkout dialog"() {
@@ -252,7 +248,7 @@ class GitBranchWorkerTest extends GitPlatformTest {
List<Change> changes = null;
checkoutOrMerge(operation, "feature", [
- showSmartOperationDialog: { Project p, List<Change> cs, String op, boolean force ->
+ showSmartOperationDialog: { Project p, List<Change> cs, Collection<String> paths, String op, boolean force ->
changes = cs
DialogWrapper.CANCEL_EXIT_CODE
}
@@ -349,7 +345,9 @@ class GitBranchWorkerTest extends GitPlatformTest {
prepareLocalChangesOverwrittenBy(myUltimate)
checkoutOrMerge(operation, "feature", [
- showSmartOperationDialog : { Project p, List<Change> cs, String op, boolean f -> GitSmartOperationDialog.CANCEL_EXIT_CODE },
+ showSmartOperationDialog: { Project p, List<Change> cs, Collection<String> paths, String op, boolean f
+ -> GitSmartOperationDialog.CANCEL_EXIT_CODE
+ },
] as GitBranchUiHandler )
assertNull "Notification was unexpectedly shown:" + myVcsNotifier.lastNotification, myVcsNotifier.lastNotification
@@ -372,8 +370,10 @@ class GitBranchWorkerTest extends GitPlatformTest {
def rollbackMsg = null
checkoutOrMerge(operation, "feature", [
- showSmartOperationDialog : { Project p, List<Change> cs, String op, boolean f -> GitSmartOperationDialog.CANCEL_EXIT_CODE },
- notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m ; false }
+ showSmartOperationDialog : {
+ Project p, List<Change> cs, Collection<String> paths, String op, boolean f -> GitSmartOperationDialog.CANCEL_EXIT_CODE
+ },
+ notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m; false }
] as GitBranchUiHandler )
assertNotNull "Rollback proposal was not shown", rollbackMsg
@@ -384,7 +384,7 @@ class GitBranchWorkerTest extends GitPlatformTest {
prepareLocalChangesOverwrittenBy(myUltimate)
def uiHandler = [
- showSmartOperationDialog: { Project p, List<Change> cs, String op, boolean force ->
+ showSmartOperationDialog: { Project p, List<Change> cs, Collection<String> paths, String op, boolean force ->
GitSmartOperationDialog.FORCE_EXIT_CODE;
},
] as GitBranchUiHandler
@@ -711,7 +711,12 @@ class GitBranchWorkerTest extends GitPlatformTest {
}
@Override
- int showSmartOperationDialog(@NotNull Project project, @NotNull List<Change> changes, @NotNull String operation, boolean force) {
+ int showSmartOperationDialog(
+ @NotNull Project project,
+ @NotNull List<Change> changes,
+ @NotNull Collection<String> paths,
+ @NotNull String operation,
+ boolean isForcePossible) {
GitSmartOperationDialog.SMART_EXIT_CODE
}
@@ -741,12 +746,13 @@ class GitBranchWorkerTest extends GitPlatformTest {
}
@Override
- void showUntrackedFilesNotification(@NotNull String operationName, @NotNull Collection<VirtualFile> untrackedFiles) {
+ void showUntrackedFilesNotification(@NotNull String operationName, @NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
throw new UnsupportedOperationException()
}
@Override
- boolean showUntrackedFilesDialogWithRollback(@NotNull String operationName, @NotNull String rollbackProposal, @NotNull Collection<VirtualFile> untrackedFiles) {
+ boolean showUntrackedFilesDialogWithRollback(
+ @NotNull String operationName, @NotNull String rollbackProposal, @NotNull VirtualFile root, @NotNull Collection<String> relativePaths) {
throw new UnsupportedOperationException()
}
diff --git a/plugins/git4idea/tests/git4idea/checkin/GitCommitAuthorCorrectorTest.java b/plugins/git4idea/tests/git4idea/checkin/GitCommitAuthorCorrectorTest.java
new file mode 100644
index 000000000000..9b0789d21809
--- /dev/null
+++ b/plugins/git4idea/tests/git4idea/checkin/GitCommitAuthorCorrectorTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.checkin;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class GitCommitAuthorCorrectorTest {
+
+ @Test
+ public void add_space_before_email_in_brackets() {
+ assertCorrection("foo<foo@email.com>", "foo <foo@email.com>");
+ }
+
+ @Test
+ public void add_brackets() {
+ String expected = "John Smith <john.smith@email.com>";
+ assertCorrection("John Smith john.smith@email.com", expected);
+ assertCorrection("John Smith <john.smith@email.com", expected);
+ assertCorrection("John Smith john.smith@email.com>", expected);
+ }
+
+ @Test
+ public void no_correction_needed() {
+ assertDoNothing("John Smith <john.smith@email.com>");
+ }
+
+ @Test
+ public void correction_not_possible() {
+ assertDoNothing("foo");
+ assertDoNothing("foo bar");
+ }
+
+ private static void assertCorrection(String source, String expected) {
+ assertEquals(expected, GitCommitAuthorCorrector.correct(source));
+ }
+
+ private static void assertDoNothing(String source) {
+ assertCorrection(source, source);
+ }
+
+} \ No newline at end of file
diff --git a/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java b/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java
index a073ceda0c96..ecef2b2cdcf5 100644
--- a/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java
+++ b/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java
@@ -125,6 +125,10 @@ public abstract class GitPlatformTest extends UsefulTestCase {
myDialogManager.cleanup();
myVcsNotifier.cleanup();
myProjectFixture.tearDown();
+
+ String tempTestIndicator = myTestStartedIndicator;
+ clearFields(this);
+ myTestStartedIndicator = tempTestIndicator;
}
finally {
super.tearDown();