/**
  * Returns proper updater based on the update policy (merge or rebase) selected by user or stored
  * in his .git/config
  *
  * @return {@link GitMergeUpdater} or {@link GitRebaseUpdater}.
  */
 @NotNull
 public static GitUpdater getUpdater(
     @NotNull Project project,
     @NotNull Git git,
     @NotNull Map<VirtualFile, GitBranchPair> trackedBranches,
     @NotNull VirtualFile root,
     @NotNull ProgressIndicator progressIndicator,
     @NotNull UpdatedFiles updatedFiles) {
   final GitVcsSettings settings = GitVcsSettings.getInstance(project);
   if (settings == null) {
     return getDefaultUpdaterForBranch(
         project, git, root, trackedBranches, progressIndicator, updatedFiles);
   }
   switch (settings.getUpdateType()) {
     case REBASE:
       return new GitRebaseUpdater(
           project, git, root, trackedBranches, progressIndicator, updatedFiles);
     case MERGE:
       return new GitMergeUpdater(
           project, git, root, trackedBranches, progressIndicator, updatedFiles);
     case BRANCH_DEFAULT:
       // use default for the branch
       return getDefaultUpdaterForBranch(
           project, git, root, trackedBranches, progressIndicator, updatedFiles);
   }
   return getDefaultUpdaterForBranch(
       project, git, root, trackedBranches, progressIndicator, updatedFiles);
 }
 /**
  * Updates the recently visited branch in the settings. This is to be performed after successful
  * checkout operation.
  */
 protected void updateRecentBranch() {
   if (getRepositories().size() == 1) {
     GitRepository repository = myRepositories.iterator().next();
     mySettings.setRecentBranchOfRepository(repository.getRoot().getPath(), myCurrentBranchOrRev);
   } else {
     mySettings.setRecentCommonBranch(myCurrentBranchOrRev);
   }
 }
  @Before
  public void setUp() throws Throwable {
    IdeaTestApplication.getInstance();

    myTestName = createTestName();
    myProjectFixture =
        IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(myTestName).getFixture();

    edt(
        new ThrowableRunnable<Exception>() {
          @Override
          public void run() throws Exception {
            myProjectFixture.setUp();
          }
        });

    myProject = myProjectFixture.getProject();

    myProjectRoot = myProject.getBasePath();
    myProjectDir = myProject.getBaseDir();
    myTestRoot = myProjectRoot;

    myGit = ServiceManager.getService(myProject, Git.class);
    mySettings = GitVcsSettings.getInstance(myProject);
    mySettings.getAppSettings().setPathToGit(GitExecutor.PathHolder.GIT_EXECUTABLE);

    // dynamic overriding is used instead of making it in plugin.xml,
    // because MockVcsHelper is not ready to be a full featured implementation for all tests.
    myVcsHelper =
        GitTestUtil.overrideService(myProject, AbstractVcsHelper.class, MockVcsHelper.class);
    myChangeListManager = ChangeListManagerImpl.getInstanceImpl(myProject);
    myNotificator = (TestVcsNotifier) ServiceManager.getService(myProject, VcsNotifier.class);
    myVcs = GitVcs.getInstance(myProject);
    myRepositoryManager = GitUtil.getRepositoryManager(myProject);

    virtualCommits = new GitTestVirtualCommitsHolder();
    myAsyncTasks = new ArrayList<>();

    cd(myProjectRoot);
    myRepository = GitTestUtil.createRepository(myProject, myProjectRoot);

    ProjectLevelVcsManagerImpl vcsManager =
        (ProjectLevelVcsManagerImpl) ProjectLevelVcsManager.getInstance(myProject);
    AbstractVcs vcs = vcsManager.findVcsByName("Git");
    Assert.assertEquals(1, vcsManager.getRootsUnderVcs(vcs).length);

    GitTestUtil.assumeSupportedGitVersion(myVcs);
    LOG.info(getStartTestMarker());
  }
    @NotNull
    private ReturnResult warnAboutCrlfIfNeeded() {
      GitVcsSettings settings = GitVcsSettings.getInstance(myProject);
      if (!settings.warnAboutCrlf()) {
        return ReturnResult.COMMIT;
      }

      final GitPlatformFacade platformFacade =
          ServiceManager.getService(myProject, GitPlatformFacade.class);
      final Git git = ServiceManager.getService(Git.class);

      final Collection<VirtualFile> files =
          myPanel
              .getVirtualFiles(); // deleted files aren't included, but for them we don't care about
                                  // CRLFs.
      final AtomicReference<GitCrlfProblemsDetector> crlfHelper =
          new AtomicReference<GitCrlfProblemsDetector>();
      ProgressManager.getInstance()
          .run(
              new Task.Modal(myProject, "Checking for line separator issues...", true) {
                @Override
                public void run(@NotNull ProgressIndicator indicator) {
                  crlfHelper.set(
                      GitCrlfProblemsDetector.detect(
                          GitCheckinHandlerFactory.MyCheckinHandler.this.myProject,
                          platformFacade,
                          git,
                          files));
                }
              });

      if (crlfHelper.get() == null) { // detection cancelled
        return ReturnResult.CANCEL;
      }

      if (crlfHelper.get().shouldWarn()) {
        final GitCrlfDialog dialog = new GitCrlfDialog(myProject);
        UIUtil.invokeAndWaitIfNeeded(
            new Runnable() {
              @Override
              public void run() {
                dialog.show();
              }
            });
        int decision = dialog.getExitCode();
        if (decision == GitCrlfDialog.CANCEL) {
          return ReturnResult.CANCEL;
        } else {
          if (decision == GitCrlfDialog.SET) {
            VirtualFile anyRoot =
                myPanel
                    .getRoots()
                    .iterator()
                    .next(); // config will be set globally => any root will do.
            setCoreAutoCrlfAttribute(anyRoot);
          } else {
            if (dialog.dontWarnAgain()) {
              settings.setWarnAboutCrlf(false);
            }
          }
          return ReturnResult.COMMIT;
        }
      }
      return ReturnResult.COMMIT;
    }
 @Override
 protected void rememberRecentRoot(@NotNull String path) {
   mySettings.setRecentRoot(path);
 }
 @Nullable
 @Override
 protected GitRepository guessCurrentRepository(@NotNull Project project) {
   return DvcsUtil.guessCurrentRepositoryQuick(
       project, GitUtil.getRepositoryManager(project), mySettings.getRecentRootPath());
 }
 public GitBranchWidget(@NotNull Project project) {
   super(project, "Git");
   mySettings = GitVcsSettings.getInstance(project);
 }
  @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);
  }
  /**
   * Constructs new dialog. Loads settings, registers listeners.
   *
   * @param project the project
   * @param vcsRoots the vcs roots
   * @param roots the loaded information about roots
   */
  private GitPushActiveBranchesDialog(
      final Project project, List<VirtualFile> vcsRoots, List<Root> roots) {
    super(project, true);
    myVcs = GitVcs.getInstance(project);
    myProject = project;
    myVcsRoots = vcsRoots;
    myGeneralSettings = GeneralSettings.getInstance();
    myProjectManager = ProjectManagerEx.getInstanceEx();

    updateTree(roots, null);
    updateUI();

    final GitVcsSettings settings = GitVcsSettings.getInstance(project);
    if (settings != null) {
      UpdatePolicyUtils.updatePolicyItem(
          settings.getPushActiveBranchesRebaseSavePolicy(),
          myStashRadioButton,
          myShelveRadioButton);
    }
    ChangeListener listener =
        new ChangeListener() {
          public void stateChanged(ChangeEvent e) {
            if (settings != null) {
              settings.setPushActiveBranchesRebaseSavePolicy(
                  UpdatePolicyUtils.getUpdatePolicy(myStashRadioButton, myShelveRadioButton));
            }
          }
        };
    myStashRadioButton.addChangeListener(listener);
    myShelveRadioButton.addChangeListener(listener);
    myCommitTree
        .getSelectionModel()
        .addTreeSelectionListener(
            new TreeSelectionListener() {
              public void valueChanged(TreeSelectionEvent e) {
                TreePath path = myCommitTree.getSelectionModel().getSelectionPath();
                if (path == null) {
                  myViewButton.setEnabled(false);
                  return;
                }
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                myViewButton.setEnabled(
                    node != null
                        && myCommitTree.getSelectionCount() == 1
                        && node.getUserObject() instanceof Commit);
              }
            });
    myViewButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            TreePath path = myCommitTree.getSelectionModel().getSelectionPath();
            if (path == null) {
              return;
            }
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
            if (node == null || !(node.getUserObject() instanceof Commit)) {
              return;
            }
            Commit c = (Commit) node.getUserObject();
            GitShowAllSubmittedFilesAction.showSubmittedFiles(
                project, c.revision.asString(), c.root.root);
          }
        });
    myFetchButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            fetch();
          }
        });
    myRebaseButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            rebase();
          }
        });

    myPushButton.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            push();
          }
        });

    setTitle(GitBundle.getString("push.active.title"));
    setOKButtonText(GitBundle.getString("push.active.rebase.and.push"));
    setCancelButtonText(GitBundle.getString("git.push.active.close"));
    init();
  }