private @NotNull GitUpdateResult updateImpl(@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, false, () -> {
        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();
    }
  }
private @NotNull GitUpdateResult updateImpl(@NotNull UpdateMethod updateMethod)