mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-87116 Cancel push if merge conflicts occur during auto-update while pushing
* Introduce several new GitUpdateResults for different situations, including SUCCESS_WITH_RESOLVED_CONFLICTS for the one from the bugreport. * Return GitUpdateResult from GitUpdateProcess. * In GitPusher if update happened with conflict resolving, don't proceed with push but show a notification.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package git4idea.push;
|
||||
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -45,6 +46,7 @@ import git4idea.repo.GitRepository;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
import git4idea.settings.GitPushSettings;
|
||||
import git4idea.update.GitUpdateProcess;
|
||||
import git4idea.update.GitUpdateResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -546,12 +548,31 @@ public final class GitPusher {
|
||||
|
||||
private boolean update(@NotNull Collection<GitRepository> rootsToUpdate, @NotNull UpdateMethod updateMethod) {
|
||||
GitUpdateProcess.UpdateMethod um = updateMethod == UpdateMethod.MERGE ? GitUpdateProcess.UpdateMethod.MERGE : GitUpdateProcess.UpdateMethod.REBASE;
|
||||
boolean updateResult = new GitUpdateProcess(myProject, myProgressIndicator, new HashSet<GitRepository>(rootsToUpdate),
|
||||
GitUpdateResult updateResult = new GitUpdateProcess(myProject, myProgressIndicator, new HashSet<GitRepository>(rootsToUpdate),
|
||||
UpdatedFiles.create()).update(um);
|
||||
for (GitRepository repository : rootsToUpdate) {
|
||||
repository.getRoot().refresh(true, true);
|
||||
}
|
||||
return updateResult;
|
||||
if (updateResult == GitUpdateResult.SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
else if (updateResult == GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS || updateResult == GitUpdateResult.INCOMPLETE) {
|
||||
String title = "Push cancelled";
|
||||
String description;
|
||||
if (updateResult == GitUpdateResult.INCOMPLETE) {
|
||||
description = "Push has been cancelled, because not all conflicts were resolved during update.<br/>" +
|
||||
"Resolve the conflicts and invoke push again.";
|
||||
}
|
||||
else {
|
||||
description = "Push has been cancelled, because there were conflicts during update.<br/>" +
|
||||
"Check that conflicts were resolved correctly, and invoke push again.";
|
||||
}
|
||||
new Notification(GitVcs.MINOR_NOTIFICATION.getDisplayId(), title, description, NotificationType.WARNING).notify(myProject);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ public class GitMergeUpdater extends GitUpdater {
|
||||
LOG.info("Conflict detected");
|
||||
final boolean allMerged =
|
||||
new MyConflictResolver(myProject, myGit, merger, myRoot).merge();
|
||||
return allMerged ? GitUpdateResult.SUCCESS : GitUpdateResult.INCOMPLETE;
|
||||
return allMerged ? GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS : GitUpdateResult.INCOMPLETE;
|
||||
}
|
||||
else if (error == MergeError.LOCAL_CHANGES) {
|
||||
LOG.info("Local changes would be overwritten by merge");
|
||||
|
||||
@@ -105,7 +105,7 @@ public class GitRebaseUpdater extends GitUpdater {
|
||||
if (rebaseConflictDetector.isMergeConflict()) {
|
||||
LOG.info("handleRebaseFailure merge conflict");
|
||||
final boolean allMerged = new MyConflictResolver(myProject, myGit, myRoot, myRebaser).merge();
|
||||
return allMerged ? GitUpdateResult.SUCCESS : GitUpdateResult.INCOMPLETE;
|
||||
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, ServiceManager.getService(myProject, PlatformFacade.class),
|
||||
|
||||
@@ -66,7 +66,7 @@ public class GitUpdateEnvironment implements UpdateEnvironment {
|
||||
GitRepositoryManager repositoryManager = getRepositoryManager(myProject);
|
||||
final GitUpdateProcess gitUpdateProcess = new GitUpdateProcess(myProject, progressIndicator,
|
||||
getRepositoriesFromRoots(repositoryManager, roots), updatedFiles);
|
||||
boolean result = gitUpdateProcess.update(GitUpdateProcess.UpdateMethod.READ_FROM_SETTINGS);
|
||||
boolean result = gitUpdateProcess.update(GitUpdateProcess.UpdateMethod.READ_FROM_SETTINGS).isSuccess();
|
||||
return new GitUpdateSession(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import git4idea.repo.GitBranchTrackInfo;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.stash.GitChangesSaver;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -67,7 +68,7 @@ public class GitUpdateProcess {
|
||||
private final GitChangesSaver mySaver;
|
||||
|
||||
private final Map<VirtualFile, GitBranchPair> myTrackedBranches = new HashMap<VirtualFile, GitBranchPair>();
|
||||
private boolean myResult;
|
||||
private GitUpdateResult myResult;
|
||||
private final Map<VirtualFile, GitUpdater> myUpdaters;
|
||||
private final Collection<VirtualFile> myRootsToSave;
|
||||
|
||||
@@ -94,12 +95,7 @@ public class GitUpdateProcess {
|
||||
/**
|
||||
* Checks if update is possible, saves local changes and updates all roots.
|
||||
* In case of error shows notification and returns false. If update completes without errors, returns true.
|
||||
*/
|
||||
public boolean update() {
|
||||
return update(UpdateMethod.READ_FROM_SETTINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Perform update on all roots.
|
||||
* 0. Blocks reloading project on external change, saving/syncing on frame deactivation.
|
||||
* 1. Checks if update is possible (rebase/merge in progress, no tracked branches...) and provides merge dialog to solve problems.
|
||||
@@ -110,18 +106,19 @@ public class GitUpdateProcess {
|
||||
* local changes are not restored.
|
||||
*
|
||||
*/
|
||||
public boolean update(final UpdateMethod updateMethod) {
|
||||
@NotNull
|
||||
public GitUpdateResult update(final UpdateMethod updateMethod) {
|
||||
LOG.info("update started|" + updateMethod);
|
||||
String oldText = myProgressIndicator.getText();
|
||||
myProgressIndicator.setText("Updating...");
|
||||
|
||||
// check if update is possible
|
||||
if (checkRebaseInProgress() || isMergeInProgress() || areUnmergedFiles() || !checkTrackedBranchesConfigured()) {
|
||||
return false;
|
||||
return GitUpdateResult.NOT_READY;
|
||||
}
|
||||
|
||||
if (!fetchAndNotify()) {
|
||||
return false;
|
||||
return GitUpdateResult.NOT_READY;
|
||||
}
|
||||
|
||||
GitComplexProcess.Operation updateOperation = new GitComplexProcess.Operation() {
|
||||
@@ -135,7 +132,8 @@ public class GitUpdateProcess {
|
||||
return myResult;
|
||||
}
|
||||
|
||||
private boolean updateImpl(UpdateMethod updateMethod, ContinuationContext context) {
|
||||
@NotNull
|
||||
private GitUpdateResult updateImpl(UpdateMethod updateMethod, ContinuationContext context) {
|
||||
// define updaters for roots
|
||||
LOG.info("updateImpl: defining updaters...");
|
||||
try {
|
||||
@@ -158,10 +156,10 @@ public class GitUpdateProcess {
|
||||
} catch (VcsException e) {
|
||||
LOG.info(e);
|
||||
notifyError(myProject, "Git update failed", e.getMessage(), true, e);
|
||||
return false;
|
||||
return GitUpdateResult.ERROR;
|
||||
}
|
||||
|
||||
if (myUpdaters.isEmpty()) return true;
|
||||
if (myUpdaters.isEmpty()) return GitUpdateResult.NOTHING_TO_UPDATE;
|
||||
|
||||
// save local changes if needed (update via merge may perform without saving).
|
||||
LOG.info("updateImpl: identifying if save is needed...");
|
||||
@@ -182,13 +180,13 @@ public class GitUpdateProcess {
|
||||
notifyError(myProject, "Git update failed",
|
||||
"Tried to save uncommitted changes in " + mySaver.getSaverName() + " before update, but failed with an error.<br/>" +
|
||||
"Update was cancelled.", true, e);
|
||||
return false;
|
||||
return GitUpdateResult.ERROR;
|
||||
}
|
||||
|
||||
// update each root
|
||||
LOG.info("updateImpl: updating...");
|
||||
boolean incomplete = false;
|
||||
boolean success = true;
|
||||
GitUpdateResult compoundResult = null;
|
||||
VirtualFile currentlyUpdatedRoot = null;
|
||||
try {
|
||||
for (Map.Entry<VirtualFile, GitUpdater> entry : myUpdaters.entrySet()) {
|
||||
@@ -199,7 +197,7 @@ public class GitUpdateProcess {
|
||||
if (res == GitUpdateResult.INCOMPLETE) {
|
||||
incomplete = true;
|
||||
}
|
||||
success &= res.isSuccess();
|
||||
compoundResult = joinResults(compoundResult, res);
|
||||
}
|
||||
} catch (VcsException e) {
|
||||
String rootName = (currentlyUpdatedRoot == null) ? "" : currentlyUpdatedRoot.getName();
|
||||
@@ -207,7 +205,7 @@ public class GitUpdateProcess {
|
||||
notifyImportantError(myProject, "Error updating " + rootName,
|
||||
"Updating " + rootName + " failed with an error: " + e.getLocalizedMessage());
|
||||
} finally {
|
||||
if (incomplete || !success) {
|
||||
if (incomplete || !!compoundResult.isSuccess()) {
|
||||
mySaver.notifyLocalChangesAreNotRestored();
|
||||
}
|
||||
else {
|
||||
@@ -215,7 +213,15 @@ public class GitUpdateProcess {
|
||||
restoreLocalChanges(context);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
return compoundResult;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GitUpdateResult joinResults(@Nullable GitUpdateResult compoundResult, GitUpdateResult result) {
|
||||
if (compoundResult == null) {
|
||||
return result;
|
||||
}
|
||||
return compoundResult.join(result);
|
||||
}
|
||||
|
||||
private void restoreLocalChanges(ContinuationContext context) {
|
||||
|
||||
@@ -15,19 +15,43 @@
|
||||
*/
|
||||
package git4idea.update;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public enum GitUpdateResult {
|
||||
SUCCESS,
|
||||
/** User cancelled update, everything that has changed was rolled back (git rebase/merge --abort) */
|
||||
CANCEL,
|
||||
/** exception happened during update */
|
||||
ERROR,
|
||||
/** Nothing to update. */
|
||||
NOTHING_TO_UPDATE(1),
|
||||
/** Successful update, without merge conflict resolution during update. */
|
||||
SUCCESS(2),
|
||||
/** Update introduced a merge conflict, that was immediately resolved by user. */
|
||||
SUCCESS_WITH_RESOLVED_CONFLICTS(3),
|
||||
/** Update introduced a merge conflict that wasn't immediately resolved. */
|
||||
INCOMPLETE;
|
||||
INCOMPLETE(4),
|
||||
/** User cancelled update, everything that has changed was rolled back (git rebase/merge --abort) */
|
||||
CANCEL(5),
|
||||
/** An error happened during update */
|
||||
ERROR(6),
|
||||
/** Update is not possible due to a configuration error or because of a failed fetch. */
|
||||
NOT_READY(7);
|
||||
|
||||
private final int myPriority;
|
||||
|
||||
GitUpdateResult(int priority) {
|
||||
myPriority = priority;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return this == SUCCESS || this == INCOMPLETE;
|
||||
return this == SUCCESS || this == SUCCESS_WITH_RESOLVED_CONFLICTS || this == INCOMPLETE || this == NOTHING_TO_UPDATE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitUpdateResult join(@NotNull GitUpdateResult next) {
|
||||
if (myPriority >= next.myPriority) {
|
||||
return this;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user