summaryrefslogtreecommitdiff
path: root/plugins/git4idea/src/git4idea/reset/GitResetOperation.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/git4idea/src/git4idea/reset/GitResetOperation.java')
-rw-r--r--plugins/git4idea/src/git4idea/reset/GitResetOperation.java191
1 files changed, 191 insertions, 0 deletions
diff --git a/plugins/git4idea/src/git4idea/reset/GitResetOperation.java b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java
new file mode 100644
index 000000000000..9c66a5225a52
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package git4idea.reset;
+
+import com.intellij.dvcs.repo.RepositoryUtil;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vcs.VcsNotifier;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.vcs.log.VcsFullCommitDetails;
+import git4idea.GitPlatformFacade;
+import git4idea.GitUtil;
+import git4idea.branch.GitBranchUiHandlerImpl;
+import git4idea.branch.GitSmartOperationDialog;
+import git4idea.commands.Git;
+import git4idea.commands.GitCommandResult;
+import git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector;
+import git4idea.repo.GitRepository;
+import git4idea.util.GitPreservingProcess;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.RESET;
+
+public class GitResetOperation {
+
+ @NotNull private final Project myProject;
+ @NotNull private final Map<GitRepository, VcsFullCommitDetails> myCommits;
+ @NotNull private final GitResetMode myMode;
+ @NotNull private final ProgressIndicator myIndicator;
+ @NotNull private final Git myGit;
+ @NotNull private final VcsNotifier myNotifier;
+ @NotNull private final GitPlatformFacade myFacade;
+ @NotNull private final GitBranchUiHandlerImpl myUiHandler;
+
+ public GitResetOperation(@NotNull Project project, @NotNull Map<GitRepository, VcsFullCommitDetails> targetCommits,
+ @NotNull GitResetMode mode, @NotNull ProgressIndicator indicator) {
+ myProject = project;
+ myCommits = targetCommits;
+ myMode = mode;
+ myIndicator = indicator;
+ myGit = ServiceManager.getService(Git.class);
+ myNotifier = VcsNotifier.getInstance(project);
+ myFacade = ServiceManager.getService(GitPlatformFacade.class);
+ myUiHandler = new GitBranchUiHandlerImpl(myProject, myFacade, myGit, indicator);
+ }
+
+ public void execute() {
+ saveAllDocuments();
+ GitUtil.workingTreeChangeStarted(myProject);
+ Map<GitRepository, GitCommandResult> results = ContainerUtil.newHashMap();
+ try {
+ for (Map.Entry<GitRepository, VcsFullCommitDetails> entry : myCommits.entrySet()) {
+ GitRepository repository = entry.getKey();
+ VirtualFile root = repository.getRoot();
+ String target = entry.getValue().getId().asString();
+ GitLocalChangesWouldBeOverwrittenDetector detector = new GitLocalChangesWouldBeOverwrittenDetector(root, RESET);
+
+ GitCommandResult result = myGit.reset(repository, myMode, target, detector);
+ if (!result.success() && detector.wasMessageDetected()) {
+ GitCommandResult smartResult = proposeSmartReset(detector, repository, target);
+ if (smartResult != null) {
+ result = smartResult;
+ }
+ }
+ results.put(repository, result);
+ repository.update();
+ VfsUtil.markDirtyAndRefresh(true, true, false, root);
+ }
+ }
+ finally {
+ GitUtil.workingTreeChangeFinished(myProject);
+ }
+ notifyResult(results);
+ }
+
+ private GitCommandResult proposeSmartReset(@NotNull GitLocalChangesWouldBeOverwrittenDetector detector,
+ @NotNull final GitRepository repository, @NotNull final String target) {
+ Collection<String> absolutePaths = GitUtil.toAbsolute(repository.getRoot(), detector.getRelativeFilePaths());
+ List<Change> affectedChanges = GitUtil.findLocalChangesForPaths(myProject, repository.getRoot(), absolutePaths, false);
+ int choice = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "reset", "&Hard Reset");
+ if (choice == GitSmartOperationDialog.SMART_EXIT_CODE) {
+ final Ref<GitCommandResult> result = Ref.create();
+ new GitPreservingProcess(myProject, myFacade, myGit, Collections.singleton(repository), "reset", target, myIndicator, new Runnable() {
+ @Override
+ public void run() {
+ result.set(myGit.reset(repository, myMode, target));
+ }
+ }).execute();
+ return result.get();
+ }
+ if (choice == GitSmartOperationDialog.FORCE_EXIT_CODE) {
+ return myGit.reset(repository, GitResetMode.HARD, target);
+ }
+ return null;
+ }
+
+ private void notifyResult(@NotNull Map<GitRepository, GitCommandResult> results) {
+ Map<GitRepository, GitCommandResult> successes = ContainerUtil.newHashMap();
+ Map<GitRepository, GitCommandResult> errors = ContainerUtil.newHashMap();
+ for (Map.Entry<GitRepository, GitCommandResult> entry : results.entrySet()) {
+ GitCommandResult result = entry.getValue();
+ GitRepository repository = entry.getKey();
+ if (result.success()) {
+ successes.put(repository, result);
+ }
+ else {
+ errors.put(repository, result);
+ }
+ }
+
+ if (errors.isEmpty()) {
+ myNotifier.notifySuccess("", "Reset successful");
+ }
+ else if (!successes.isEmpty()) {
+ myNotifier.notifyImportantWarning("Reset partially failed",
+ "Reset was successful for " + joinRepos(successes.keySet())
+ + "<br/>but failed for " + joinRepos(errors.keySet()) + ": <br/>" + formErrorReport(errors));
+ }
+ else {
+ myNotifier.notifyError("Reset Failed", formErrorReport(errors));
+ }
+ }
+
+ @NotNull
+ private static String formErrorReport(@NotNull Map<GitRepository, GitCommandResult> errorResults) {
+ MultiMap<String, GitRepository> grouped = groupByResult(errorResults);
+ if (grouped.size() == 1) {
+ return "<code>" + grouped.keySet().iterator().next() + "</code>";
+ }
+ return StringUtil.join(grouped.entrySet(), new Function<Map.Entry<String, Collection<GitRepository>>, String>() {
+ @NotNull
+ @Override
+ public String fun(@NotNull Map.Entry<String, Collection<GitRepository>> entry) {
+ return joinRepos(entry.getValue()) + ":<br/><code>" + entry.getKey() + "</code>";
+ }
+ }, "<br/>");
+ }
+
+ // to avoid duplicate error reports if they are the same for different repositories
+ @NotNull
+ private static MultiMap<String, GitRepository> groupByResult(@NotNull Map<GitRepository, GitCommandResult> results) {
+ MultiMap<String, GitRepository> grouped = MultiMap.create();
+ for (Map.Entry<GitRepository, GitCommandResult> entry : results.entrySet()) {
+ grouped.putValue(entry.getValue().getErrorOutputAsHtmlString(), entry.getKey());
+ }
+ return grouped;
+ }
+
+ @NotNull
+ private static String joinRepos(@NotNull Collection<GitRepository> repositories) {
+ return StringUtil.join(RepositoryUtil.sortRepositories(repositories), ", ");
+ }
+
+ private static void saveAllDocuments() {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ FileDocumentManager.getInstance().saveAllDocuments();
+ }
+ });
+ }
+
+}