@NotNull
 private Map<VirtualFile, GitUpdater> defineUpdaters(@NotNull UpdateMethod updateMethod)
     throws VcsException {
   final Map<VirtualFile, GitUpdater> updaters = new HashMap<VirtualFile, GitUpdater>();
   LOG.info("updateImpl: defining updaters...");
   for (GitRepository repository : myRepositories) {
     VirtualFile root = repository.getRoot();
     GitUpdater updater =
         GitUpdater.getUpdater(
             myProject,
             myGit,
             myTrackedBranches,
             root,
             myProgressIndicator,
             myUpdatedFiles,
             updateMethod);
     if (updater.isUpdateNeeded()) {
       updaters.put(root, updater);
     }
     LOG.info("update| root=" + root + " ,updater=" + updater);
   }
   return updaters;
 }
  @NotNull
  private GitUpdateResult updateImpl(@NotNull UpdateMethod updateMethod) {
    Map<VirtualFile, GitUpdater> updaters;
    try {
      updaters = defineUpdaters(updateMethod);
    } catch (VcsException e) {
      LOG.info(e);
      notifyError(myProject, "Git update failed", e.getMessage(), true, e);
      return GitUpdateResult.ERROR;
    }

    if (updaters.isEmpty()) {
      return GitUpdateResult.NOTHING_TO_UPDATE;
    }

    updaters = tryFastForwardMergeForRebaseUpdaters(updaters);

    if (updaters.isEmpty()) {
      // everything was updated via the fast-forward merge
      return GitUpdateResult.SUCCESS;
    }

    if (myCheckRebaseOverMergeProblem) {
      Collection<VirtualFile> problematicRoots = findRootsRebasingOverMerge(updaters);
      if (!problematicRoots.isEmpty()) {
        GitRebaseOverMergeProblem.Decision decision = GitRebaseOverMergeProblem.showDialog();
        if (decision == GitRebaseOverMergeProblem.Decision.MERGE_INSTEAD) {
          for (VirtualFile root : problematicRoots) {
            updaters.put(
                root,
                new GitMergeUpdater(
                    myProject,
                    myGit,
                    root,
                    myTrackedBranches,
                    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 = ContainerUtil.newArrayList();
    LOG.info("updateImpl: identifying if save is needed...");
    for (Map.Entry<VirtualFile, GitUpdater> entry : updaters.entrySet()) {
      VirtualFile root = entry.getKey();
      GitUpdater updater = entry.getValue();
      if (updater.isSaveNeeded()) {
        myRootsToSave.add(root);
        LOG.info("update| root " + root + " needs save");
      }
    }

    LOG.info("updateImpl: saving local changes...");
    final Ref<Boolean> incomplete = Ref.create(false);
    final Ref<GitUpdateResult> compoundResult = Ref.create();
    final Map<VirtualFile, GitUpdater> finalUpdaters = updaters;

    new GitPreservingProcess(
            myProject,
            myGit,
            myRootsToSave,
            "Update",
            "Remote",
            GitVcsSettings.getInstance(myProject).updateChangesPolicy(),
            myProgressIndicator,
            new Runnable() {
              @Override
              public void run() {
                LOG.info("updateImpl: updating...");
                VirtualFile currentlyUpdatedRoot = null;
                try {
                  for (Map.Entry<VirtualFile, GitUpdater> entry : finalUpdaters.entrySet()) {
                    currentlyUpdatedRoot = entry.getKey();
                    GitUpdater updater = entry.getValue();
                    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) ? "" : currentlyUpdatedRoot.getName();
                  LOG.info("Error updating changes for root " + currentlyUpdatedRoot, e);
                  notifyImportantError(
                      myProject,
                      "Error updating " + rootName,
                      "Updating " + rootName + " failed with an error: " + e.getLocalizedMessage());
                }
              }
            })
        .execute(
            new Computable<Boolean>() {
              @Override
              public Boolean compute() {
                // 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);
  }