Merge remote-tracking branch 'origin/master'

This commit is contained in:
Anna Kozlova
2012-06-06 19:03:29 +04:00
13 changed files with 134 additions and 79 deletions
@@ -27,6 +27,7 @@ import com.intellij.openapi.editor.markup.TextAttributes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.ArrayList;
/**
@@ -37,6 +38,7 @@ import java.util.ArrayList;
class ChangeHighlighterHolder {
private static final Logger LOG = Logger.getInstance(ChangeHighlighterHolder.class);
static final int APPLIED_CHANGE_TRANSPARENCY = 30;
private Editor myEditor;
private final ArrayList<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>(3);
@@ -55,28 +57,41 @@ class ChangeHighlighterHolder {
return myEditor.getMarkupModel();
}
private void highlighterCreated(RangeHighlighter highlighter, TextAttributes attrs) {
private void highlighterCreated(RangeHighlighter highlighter, TextAttributes attrs, boolean applied) {
if (attrs != null) {
highlighter.setErrorStripeMarkColor(attrs.getErrorStripeColor());
Color color = attrs.getErrorStripeColor();
if (applied) {
color = makeColorForApplied(color);
}
highlighter.setErrorStripeMarkColor(color);
}
myHighlighters.add(highlighter);
}
private static Color makeColorForApplied(Color color) {
return new Color(color.getRed(), color.getGreen(), color.getBlue(), APPLIED_CHANGE_TRANSPARENCY);
}
@Nullable
public RangeHighlighter addLineHighlighter(int line, int layer, TextDiffType diffType) {
public RangeHighlighter addLineHighlighter(int line, int layer, TextDiffType diffType, boolean applied) {
if (myEditor.getDocument().getTextLength() == 0) return null;
RangeHighlighter highlighter = getMarkupModel().addLineHighlighter(line, layer, null);
highlighter.setLineSeparatorColor(diffType.getTextBackground(myEditor));
highlighterCreated(highlighter, diffType.getTextAttributes(myEditor));
highlighterCreated(highlighter, diffType.getTextAttributes(myEditor), applied);
return highlighter;
}
@Nullable
public RangeHighlighter addRangeHighlighter(int start, int end, int layer, TextDiffType type, HighlighterTargetArea targetArea) {
public RangeHighlighter addRangeHighlighter(int start,
int end,
int layer,
TextDiffType type,
HighlighterTargetArea targetArea,
boolean applied) {
if (getMarkupModel().getDocument().getTextLength() == 0) return null;
TextAttributes attributes = type.getTextAttributes(myEditor);
RangeHighlighter highlighter = getMarkupModel().addRangeHighlighter(start, end, layer, attributes, targetArea);
highlighterCreated(highlighter, attributes);
highlighterCreated(highlighter, attributes, applied);
return highlighter;
}
@@ -98,7 +98,7 @@ public class ChangeType {
int length = text.length();
int start = changeSide.getStart();
int end = start + length;
RangeHighlighter highlighter = markup.addRangeHighlighter(start, end, LAYER, diffType, HighlighterTargetArea.EXACT_RANGE);
RangeHighlighter highlighter = markup.addRangeHighlighter(start, end, LAYER, diffType, HighlighterTargetArea.EXACT_RANGE, myApplied);
if (highlighter != null) {
highlighter.setLineSeparatorPlacement(SeparatorPlacement.TOP);
@@ -111,7 +111,7 @@ public class ChangeType {
end--;
}
highlighter = markup.addRangeHighlighter(start, end, LAYER, TextDiffType.NONE, HighlighterTargetArea.EXACT_RANGE);
highlighter = markup.addRangeHighlighter(start, end, LAYER, TextDiffType.NONE, HighlighterTargetArea.EXACT_RANGE, myApplied);
if (highlighter != null) {
highlighter.setLineSeparatorPlacement(SeparatorPlacement.BOTTOM);
highlighter.setLineSeparatorColor(separatorColor);
@@ -122,8 +122,8 @@ public class ChangeType {
}
@Nullable
private static RangeHighlighter addLine(ChangeHighlighterHolder markup, int line, TextDiffType type, SeparatorPlacement placement) {
RangeHighlighter highlighter = markup.addLineHighlighter(line, LAYER, type);
private RangeHighlighter addLine(ChangeHighlighterHolder markup, int line, TextDiffType type, SeparatorPlacement placement) {
RangeHighlighter highlighter = markup.addLineHighlighter(line, LAYER, type, myApplied);
if (highlighter == null) {
return null;
}
@@ -20,7 +20,6 @@ import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.i18n.GitBundle;
import git4idea.repo.GitRepositoryManager;
import git4idea.ui.GitUnstashDialog;
import org.jetbrains.annotations.NotNull;
@@ -50,6 +49,11 @@ public class GitUnstash extends GitRepositoryAction {
final List<VcsException> exceptions) throws VcsException {
final ChangeListManager changeListManager = ChangeListManager.getInstance(project);
if (changeListManager.isFreezedWithNotification("Can not unstash changes now")) return;
GitUnstashDialog.showUnstashDialog(project, gitRoots, defaultRoot, affectedRoots);
GitUnstashDialog.showUnstashDialog(project, gitRoots, defaultRoot);
}
@Override
protected boolean executeFinalTasksSynchronously() {
return false;
}
}
@@ -372,12 +372,6 @@ public class GitHandlerUtil {
}
}
}
/**
* Error indicators for the line
*/
@NonNls private static final String[] ERROR_INDICATORS =
{"ERROR:", "error", "FATAL:", "fatal", "Cannot apply", "Could not", "Interactive rebase already started", "refusing to pull",
"cannot rebase:"};
/**
* Check if the line is an error line
@@ -386,7 +380,7 @@ public class GitHandlerUtil {
* @return true if the error line
*/
protected static boolean isErrorLine(String text) {
for (String prefix : ERROR_INDICATORS) {
for (String prefix : GitImpl.ERROR_INDICATORS) {
if (text.startsWith(prefix)) {
return true;
}
@@ -339,14 +339,14 @@ public class GitImpl implements Git {
}
}
private GitCommandResult run(@NotNull GitLineHandler handler) {
private static GitCommandResult run(@NotNull GitLineHandler handler) {
return run(handler, false);
}
/**
* Runs the given {@link GitLineHandler} in the current thread and returns the {@link GitCommandResult}.
*/
private GitCommandResult run(@NotNull GitLineHandler handler, boolean remote) {
private static GitCommandResult run(@NotNull GitLineHandler handler, boolean remote) {
handler.setNoSSH(!remote);
final List<String> errorOutput = new ArrayList<String>();
@@ -387,7 +387,7 @@ public class GitImpl implements Git {
/**
* Check if the line looks line an error message
*/
private boolean isError(String text) {
private static boolean isError(String text) {
for (String indicator : ERROR_INDICATORS) {
if (text.startsWith(indicator.toLowerCase())) {
return true;
@@ -397,8 +397,9 @@ public class GitImpl implements Git {
}
// could be upper-cased, so should check case-insensitively
private final String[] ERROR_INDICATORS = {
"error", "fatal", "Cannot apply", "Could not", "Interactive rebase already started", "refusing to pull", "cannot rebase:", "conflict"
public static final String[] ERROR_INDICATORS = {
"error", "fatal", "Cannot apply", "Could not", "Interactive rebase already started", "refusing to pull", "cannot rebase:", "conflict",
"unable"
};
}
@@ -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;
}
}
}
@@ -99,7 +99,7 @@ public class GitStashChangesSaver extends GitChangesSaver {
@Override
protected void showSavedChanges() {
GitUnstashDialog.showUnstashDialog(myProject, new ArrayList<VirtualFile>(myStashedRoots), myStashedRoots.iterator().next(), new HashSet<VirtualFile>());
GitUnstashDialog.showUnstashDialog(myProject, new ArrayList<VirtualFile>(myStashedRoots), myStashedRoots.iterator().next());
}
@Override
@@ -216,8 +216,8 @@ public class GitStashChangesSaver extends GitChangesSaver {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
if (event.getDescription().equals("saver")) {
// we don't use #showSavedChanges to specify unmerged root first
GitUnstashDialog.showUnstashDialog(myProject, new ArrayList<VirtualFile>(myStashedRoots), myStashedRoots.iterator().next(),
new HashSet<VirtualFile>());
GitUnstashDialog.showUnstashDialog(myProject, new ArrayList<VirtualFile>(myStashedRoots), myStashedRoots.iterator().next()
);
} else if (event.getDescription().equals("resolve")) {
mergeNoProceed();
}
@@ -390,26 +390,10 @@ public class GitUnstashDialog extends DialogWrapper {
return myStashList;
}
/**
* Show unstash dialog and process its result
*
* @param project the context project
* @param gitRoots the git roots
* @param defaultRoot the default git root
* @param affectedRoots the affected roots
*/
public static void showUnstashDialog(Project project,
List<VirtualFile> gitRoots,
VirtualFile defaultRoot,
Set<VirtualFile> affectedRoots) {
GitUnstashDialog d = new GitUnstashDialog(project, gitRoots, defaultRoot);
d.show();
if (!d.isOK()) {
return;
}
VirtualFile root = d.getGitRoot();
affectedRoots.add(root);
GitLineHandler h = d.handler();
@Override
protected void doOKAction() {
VirtualFile root = getGitRoot();
GitLineHandler h = handler();
final AtomicBoolean conflict = new AtomicBoolean();
h.addLineListener(new GitLineHandlerAdapter() {
@@ -423,11 +407,17 @@ public class GitUnstashDialog extends DialogWrapper {
root.refresh(true, true);
if (conflict.get()) {
boolean conflictsResolved = new UnstashConflictResolver(project, root, d.getSelectedStash()).merge();
boolean conflictsResolved = new UnstashConflictResolver(myProject, root, getSelectedStash()).merge();
LOG.info("loadRoot " + root + ", conflictsResolved: " + conflictsResolved);
} else if (rc != 0) {
GitUIUtil.showOperationErrors(project, h.errors(), h.printableCommandLine());
GitUIUtil.showOperationErrors(myProject, h.errors(), h.printableCommandLine());
}
super.doOKAction();
}
public static void showUnstashDialog(Project project, List<VirtualFile> gitRoots, VirtualFile defaultRoot) {
new GitUnstashDialog(project, gitRoots, defaultRoot).show();
// d is not modal=> everything else in doOKAction.
}
private static class UnstashConflictResolver extends GitConflictResolver {
@@ -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;
}
}