blob: 76e5487e77d494721c87f5e7175b0ddd6a7da684 [file] [log] [blame]
/*
* 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.DvcsUtil;
import com.intellij.openapi.application.AccessToken;
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.vcs.changes.VcsDirtyScopeManager;
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();
DvcsUtil.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);
VcsDirtyScopeManager.getInstance(myProject).dirDirtyRecursively(root);
}
}
finally {
DvcsUtil.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(DvcsUtil.sortRepositories(repositories), ", ");
}
private static void saveAllDocuments() {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
FileDocumentManager.getInstance().saveAllDocuments();
}
});
}
}