*/
package git4idea.checkin;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationDisplayType;
+import com.intellij.ide.GeneralSettings;
import com.intellij.notification.NotificationType;
-import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
-import com.intellij.util.Function;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
-import static git4idea.ui.GitUIUtil.notifyError;
-import static git4idea.ui.GitUIUtil.notifyImportantError;
+import static git4idea.ui.GitUIUtil.*;
/**
* The dialog that allows pushing active branches.
private JRadioButton myShelveRadioButton;
private GitVcs myVcs;
private static final Logger LOG = Logger.getInstance(GitPushActiveBranchesDialog.class.getName());
+ private final GeneralSettings myGeneralSettings;
+ private final ProjectManagerEx myProjectManager;
/**
* A modification of Runnable with the roots-parameter.
myVcs = GitVcs.getInstance(project);
myProject = project;
myVcsRoots = vcsRoots;
+ myGeneralSettings = GeneralSettings.getInstance();
+ myProjectManager = ProjectManagerEx.getInstanceEx();
updateTree(roots, null);
updateUI();
final List<Root> roots = loadRoots(myProject, myVcsRoots, exceptions, true); // fetch
if (!exceptions.isEmpty()) {
- notifyException("Failed to fetch", exceptions);
+ notifyMessage(myProject, "Failed to fetch", null, NotificationType.ERROR, true, exceptions);
return;
}
updateTree(roots, rebaseInfo.uncheckedCommits);
if (isRebaseNeeded()) {
executeRebase(exceptions, rebaseInfo);
if (!exceptions.isEmpty()) {
- notifyException("Failed to rebase", exceptions);
+ notifyMessage(myProject, "Failed to rebase", null, NotificationType.ERROR, true, exceptions);
return;
}
GitUtil.refreshFiles(myProject, rebaseInfo.roots);
}
}
- notifyException("Failed to push", pushExceptions);
+ notifyMessage(myProject, "Failed to push", "Update project and push again", NotificationType.ERROR, true, pushExceptions);
}
});
}
- /**
- * Notifies about errors during background rebase & push tasks.
- */
- private void notifyException(String title, Collection<VcsException> exceptions) {
- String content = StringUtil.join(exceptions, new Function<VcsException, String>() {
- @Override public String fun(VcsException e) {
- return e.getLocalizedMessage();
- }
- }, "<br/>");
- if (StringUtil.isEmptyOrSpaces(content)) {
- content = title;
- }
- LOG.info(title + " || " + content);
- Notifications.Bus.notify(new Notification(GitVcs.IMPORTANT_ERROR_NOTIFICATION, title, content, NotificationType.ERROR),
- NotificationDisplayType.STICKY_BALLOON, myProject);
- }
-
/**
* Pushes selected commits synchronously in foreground.
*/
rebaseInfo = collectRebaseInfo();
return reorderCommitsIfNeeded(rebaseInfo);
} else {
- GitUIUtil.notifyMessage(myProject, "Commits weren't pushed", "Rebase failed.", NotificationType.WARNING, true, null);
+ notifyMessage(myProject, "Commits weren't pushed", "Rebase failed.", NotificationType.WARNING, true, null);
return false;
}
return true;
}
- ProjectManagerEx projectManager = ProjectManagerEx.getInstanceEx();
ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
if (progressIndicator == null) {
progressIndicator = new EmptyProgressIndicator();
}
String stashMessage = "Uncommitted changes before rebase operation at " + DateFormatUtil.formatDateTime(Clock.getTime());
GitChangesSaver saver = rebaseInfo.policy == GitVcsSettings.UpdateChangesPolicy.SHELVE ? new GitShelveChangesSaver(myProject, progressIndicator, stashMessage) : new GitStashChangesSaver(myProject, progressIndicator, stashMessage);
- projectManager.blockReloadingProjectOnExternalChanges();
+
+ final boolean saveOnFrameDeactivation = myGeneralSettings.isSaveOnFrameDeactivation();
+ final boolean syncOnFrameDeactivation = myGeneralSettings.isSyncOnFrameActivation();
+ myProjectManager.blockReloadingProjectOnExternalChanges();
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override public void run() {
+ FileDocumentManager.getInstance().saveAllDocuments();
+ myGeneralSettings.setSaveOnFrameDeactivation(false);
+ myGeneralSettings.setSyncOnFrameActivation(false);
+ }
+ });
+ }
+ });
+
try {
final Set<VirtualFile> rootsToReorder = rebaseInfo.reorderedCommits.keySet();
saver.saveLocalChanges(rootsToReorder);
}
} catch (VcsException e) {
- GitUIUtil.notifyMessage(myProject, "Commits weren't pushed", "Failed to reorder commits", NotificationType.WARNING, true,
- Collections.singleton(e));
+ notifyMessage(myProject, "Commits weren't pushed", "Failed to reorder commits", NotificationType.WARNING, true,
+ Collections.singleton(e));
} finally {
try {
saver.restoreLocalChanges();
"Tried to save uncommitted changes in " + saver.getSaverName() + " before update, but failed with an error.<br/>" +
"Update was cancelled.", true, e);
} finally {
- projectManager.unblockReloadingProjectOnExternalChanges();
+ myProjectManager.unblockReloadingProjectOnExternalChanges();
+ myGeneralSettings.setSaveOnFrameDeactivation(saveOnFrameDeactivation);
+ myGeneralSettings.setSyncOnFrameActivation(syncOnFrameDeactivation);
}
return false;
}
*/
package git4idea.update;
+import com.intellij.ide.GeneralSettings;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.text.DateFormatUtil;
+import com.intellij.util.ui.UIUtil;
import git4idea.GitBranch;
import git4idea.branch.GitBranchPair;
import git4idea.merge.GitMergeConflictResolver;
private final GitChangesSaver mySaver;
private final Map<VirtualFile, GitBranchPair> myTrackedBranches = new HashMap<VirtualFile, GitBranchPair>();
+ private GeneralSettings myGeneralSettings;
public GitUpdateProcess(@NotNull Project project,
@NotNull ProgressIndicator progressIndicator,
myMerger = new GitMerger(myProject);
mySaver = GitChangesSaver.getSaver(myProject, myProgressIndicator,
"Uncommitted changes before update operation at " + DateFormatUtil.formatDateTime(Clock.getTime()));
+ myGeneralSettings = GeneralSettings.getInstance();
}
/**
return update(false);
}
+ /**
+ * 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.
+ * 2. Finds updaters to use (merge or rebase).
+ * 3. Preserves local changes if needed (not needed for merge sometimes).
+ * 4. Updates via 'git pull' or equivalent.
+ * 5. Restores local changes if update completed or failed with error. If update is incomplete, i.e. some unmerged files remain,
+ * local changes are not restored.
+ * @param forceRebase
+ * @return
+ */
public boolean update(boolean forceRebase) {
- LOG.info("update started");
+ LOG.info("update started|" + (forceRebase ? " force rebase" : ""));
+ final boolean saveOnFrameDeactivation = myGeneralSettings.isSaveOnFrameDeactivation();
+ final boolean syncOnFrameDeactivation = myGeneralSettings.isSyncOnFrameActivation();
myProjectManager.blockReloadingProjectOnExternalChanges();
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override public void run() {
+ FileDocumentManager.getInstance().saveAllDocuments();
+ myGeneralSettings.setSaveOnFrameDeactivation(false);
+ myGeneralSettings.setSyncOnFrameActivation(false);
+ }
+ });
+ }
+ });
+
try {
// check if update is possible
if (checkRebaseInProgress() || checkMergeInProgress() || checkUnmergedFiles()) { return false; }
if (!checkTrackedBranchesConfigured()) { return false; }
- // define updaters for each root
- Collection<VirtualFile> rootsToSave = new HashSet<VirtualFile>(1);
+ // define updaters for roots
+ final Map<VirtualFile, GitUpdater> updaters = new HashMap<VirtualFile, GitUpdater>();
for (VirtualFile root : myRoots) {
- final GitUpdater updater = forceRebase ? GitUpdater.getUpdater(myProject, this, root, myProgressIndicator, myUpdatedFiles) :
- new GitRebaseUpdater(myProject, root, this, myProgressIndicator, myUpdatedFiles);
+ final GitUpdater updater = forceRebase
+ ? new GitRebaseUpdater(myProject, root, this, myProgressIndicator, myUpdatedFiles)
+ : GitUpdater.getUpdater(myProject, this, root, myProgressIndicator, myUpdatedFiles);
+ updaters.put(root, updater);
+ LOG.info("update| root=" + root + " ,updater=" + updater);
+ }
+
+ // save local changes if needed (update via merge may perform without saving).
+ final Collection<VirtualFile> rootsToSave = new HashSet<VirtualFile>(1);
+ for (Map.Entry<VirtualFile, GitUpdater> entry : updaters.entrySet()) {
+ VirtualFile root = entry.getKey();
+ GitUpdater updater = entry.getValue();
if (updater.isSaveNeeded()) {
rootsToSave.add(root);
+ LOG.info("update| root " + root + " needs save");
}
}
-
mySaver.saveLocalChanges(rootsToSave);
// update each root
boolean incomplete = false;
boolean success = true;
- for (final VirtualFile root : myRoots) {
+ for (Map.Entry<VirtualFile, GitUpdater> entry : updaters.entrySet()) {
+ VirtualFile root = entry.getKey();
+ GitUpdater updater = entry.getValue();
try {
- final GitUpdater updater = forceRebase ? GitUpdater.getUpdater(myProject, this, root, myProgressIndicator, myUpdatedFiles) :
- new GitRebaseUpdater(myProject, root, this, myProgressIndicator, myUpdatedFiles);
GitUpdateResult res = updater.update();
+ LOG.info("updating root " + root + " finished: " + res);
if (res == GitUpdateResult.INCOMPLETE) {
incomplete = true;
}
"Update was cancelled.", true, e);
} finally {
myProjectManager.unblockReloadingProjectOnExternalChanges();
+ myGeneralSettings.setSaveOnFrameDeactivation(saveOnFrameDeactivation);
+ myGeneralSettings.setSyncOnFrameActivation(syncOnFrameDeactivation);
}
return false;
}
try {
final GitBranch branch = GitBranch.current(myProject, root);
if (branch == null) {
+ LOG.info("checkTrackedBranchesConfigured current branch is null");
notifyImportantError(myProject, "Can't update: no current branch",
"You are in 'detached HEAD' state, which means that you're not on any branch.<br/>" +
"Checkout a branch to make update possible.");
final GitBranch tracked = branch.tracked(myProject, root);
if (tracked == null) {
final String branchName = branch.getName();
+ LOG.info("checkTrackedBranchesConfigured tracked branch is null for current branch " + branch);
notifyImportantError(myProject, "Can't update: no tracked branch",
"No tracked branch configured for branch " + branchName +
"<br/>To make your branch track a remote branch call, for example,<br/>" +
}
myTrackedBranches.put(root, new GitBranchPair(branch, tracked));
} catch (VcsException e) {
+ LOG.info("checkTrackedBranchesConfigured ", e);
notifyImportantError(myProject, "Can't update: error identifying tracked branch", e.getLocalizedMessage());
return false;
}
if (mergingRoots.isEmpty()) {
return false;
}
+ LOG.info("checkMergeInProgress mergingRoots: " + mergingRoots);
return !new GitMergeConflictResolver(myProject, false, "You have unfinished merge. These conflicts must be resolved before update.", "Can't update", "") {
@Override protected boolean proceedAfterAllMerged() throws VcsException {
myMerger.mergeCommit(mergingRoots);
return true;
}
- }.mergeFiles(mergingRoots);
+ }.merge(mergingRoots);
}
/**
try {
Collection<VirtualFile> unmergedFiles = GitMergeUtil.getUnmergedFiles(myProject, myRoots);
if (!unmergedFiles.isEmpty()) {
+ LOG.info("checkUnmergedFiles unmergedFiles: " + unmergedFiles);
return !new GitMergeConflictResolver(myProject, false, "Unmerged files detected. These conflicts must be resolved before update.", "Can't update", "") {
@Override protected boolean proceedAfterAllMerged() throws VcsException {
myMerger.mergeCommit(myRoots);
return true;
}
- }.mergeFiles(myRoots);
+ }.merge(myRoots);
}
} catch (VcsException e) {
LOG.info("areUnmergedFiles. Couldn't get unmerged files", e);
if (rebasingRoots.isEmpty()) {
return false;
}
+ LOG.info("checkRebaseInProgress rebasingRoots: " + rebasingRoots);
return !new GitMergeConflictResolver(myProject, true, "You have unfinished rebase process. These conflicts must be resolved before update.", "Can't update",
"Then you may <b>continue rebase</b>. <br/> You also may <b>abort rebase</b> to restore the original branch and stop rebasing.") {
@Override protected boolean proceedAfterAllMerged() {
return rebaser.continueRebase(rebasingRoots);
}
- }.mergeFiles(rebasingRoots);
+ }.merge(rebasingRoots);
}
}