GitUpdateResult (@NotNull UpdateMethod updateMethod) { // re-read after fetch, remote branch might have been deleted Map<GitRepository, GitBranchPair> trackedBranches = myUpdateConfig != null ? myUpdateConfig : checkTrackedBranchesConfiguration(); if (trackedBranches == null) { return GitUpdateResult.NOT_READY; } Map<GitRepository, GitUpdater> updaters; try { updaters = defineUpdaters(updateMethod, trackedBranches); } catch (VcsException e) { LOG.info(e); VcsNotifier.getInstance(myProject) .notifyError(UPDATE_ERROR, GitBundle.message("notification.title.update.failed"), e.getMessage(), Collections.singleton(e) ); return GitUpdateResult.ERROR; } if (updaters.isEmpty()) { return GitUpdateResult.NOTHING_TO_UPDATE; } GitUpdatedRanges updatedRanges = GitUpdatedRanges.calcInitialPositions(myProject, trackedBranches); try { updaters = tryFastForwardMergeForRebaseUpdaters(updaters); if (updaters.isEmpty()) { // everything was updated via the fast-forward merge return GitUpdateResult.SUCCESS; } if (myCheckRebaseOverMergeProblem) { Collection<GitRepository> problematicRoots = findRootsRebasingOverMerge(updaters); if (!problematicRoots.isEmpty()) { GitRebaseOverMergeProblem.Decision decision = GitRebaseOverMergeProblem.showDialog(); if (decision == GitRebaseOverMergeProblem.Decision.MERGE_INSTEAD) { for (GitRepository repo : problematicRoots) { GitBranchPair branchAndTracked = trackedBranches.get(repo); if (branchAndTracked == null) { LOG.error("No tracked branch information for root " + repo.getRoot()); continue; } updaters.put(repo, new GitMergeUpdater(myProject, myGit, repo, branchAndTracked, myProgressIndicator, myUpdatedFiles)); } } else if (decision == GitRebaseOverMergeProblem.Decision.CANCEL_OPERATION) { return GitUpdateResult.CANCEL; } } } // save local changes if needed (update via merge may perform without saving). final Collection<VirtualFile> myRootsToSave = new ArrayList<>(); LOG.info("updateImpl: identifying if save is needed..."); for (Map.Entry<GitRepository, GitUpdater> entry : updaters.entrySet()) { GitRepository repo = entry.getKey(); GitUpdater updater = entry.getValue(); if (updater.isSaveNeeded()) { myRootsToSave.add(repo.getRoot()); LOG.info("update| root " + repo + " needs save"); } } LOG.info("updateImpl: saving local changes..."); final Ref<Boolean> incomplete = Ref.create(false); final Ref<GitUpdateResult> compoundResult = Ref.create(); final Map<GitRepository, GitUpdater> finalUpdaters = updaters; new GitPreservingProcess(myProject, myGit, myRootsToSave, GitBundle.message("git.update.operation"), GitBundle.message("progress.update.destination.remote"), GitVcsSettings.getInstance(myProject).getSaveChangesPolicy(), myProgressIndicator, () -> { LOG.info("updateImpl: updating..."); GitRepository currentlyUpdatedRoot = null; try { for (GitRepository repo : finalUpdaters.keySet()) { GitUpdater updater = finalUpdaters.get(repo); if (updater == null) continue; currentlyUpdatedRoot = repo; GitUpdateResult res = updater.update(); LOG.info("updating root " + currentlyUpdatedRoot + " finished: " + res); if (res == GitUpdateResult.INCOMPLETE) { incomplete.set(true); } compoundResult.set(joinResults(compoundResult.get(), res)); } } catch (VcsException e) { String rootName = (currentlyUpdatedRoot == null) ? "" : getShortRepositoryName(currentlyUpdatedRoot); LOG.info("Error updating changes for root " + currentlyUpdatedRoot, e); VcsNotifier.getInstance(myProject) .notifyError(UPDATE_ERROR, GitBundle.message("notification.title.error.updating.root", rootName), GitBundle.message("notification.content.updating.root.failed.with.error", rootName, e.getLocalizedMessage())); } }).execute(() -> { // Note: compoundResult normally should not be null, because the updaters map was checked for non-emptiness. // But if updater.update() fails with exception for the first root, then the value would not be assigned. // In this case we don't restore local changes either, because update failed. return !incomplete.get() && !compoundResult.isNull() && compoundResult.get().isSuccess(); }); // GitPreservingProcess#save may fail due index.lock presence return ObjectUtils.notNull(compoundResult.get(), GitUpdateResult.ERROR); } finally { myUpdatedRanges = updatedRanges.calcCurrentPositions(); } }