@Override
  public <S> List<S> filterUniqueRoots(
      final List<S> in, final Convertor<S, VirtualFile> convertor) {
    Collections.sort(
        in, new ComparatorDelegate<S, VirtualFile>(convertor, FilePathComparator.getInstance()));

    for (int i = 1; i < in.size(); i++) {
      final S sChild = in.get(i);
      final VirtualFile child = convertor.convert(sChild);
      final VirtualFile childRoot = HgUtil.getHgRootOrNull(myProject, child);
      if (childRoot == null) {
        continue;
      }
      for (int j = i - 1; j >= 0; --j) {
        final S sParent = in.get(j);
        final VirtualFile parent = convertor.convert(sParent);
        // if the parent is an ancestor of the child and that they share common root, the child is
        // removed
        if (VfsUtil.isAncestor(parent, child, false)
            && VfsUtil.isAncestor(childRoot, parent, false)) {
          in.remove(i);
          //noinspection AssignmentToForLoopParameter
          --i;
          break;
        }
      }
    }
    return in;
  }
  @Test
  public void updateKeepsWorkingAfterPull() throws Exception {

    changeFile_A_AndCommitInRemoteRepository();

    // do a simple pull without an update
    HgPullCommand pull = new HgPullCommand(myProject, projectRepoVirtualFile);
    pull.setSource(HgUtil.getRepositoryDefaultPath(myProject, projectRepoVirtualFile));
    pull.setUpdate(false);
    pull.execute();

    assertEquals(
        determineNumberOfIncomingChanges(projectRepo),
        0,
        "The update operation should have pulled the incoming changes from the default repository.");

    updateThroughPlugin();

    HgRevisionNumber parentRevision =
        new HgWorkingCopyRevisionsCommand(myProject).firstParent(projectRepoVirtualFile);
    assertEquals(
        parentRevision.getRevision(),
        "1",
        "The working directory should have been updated to the latest version");
  }
 @Override
 public void conflictResolvedForFile(VirtualFile file) {
   try {
     new HgResolveCommand(myProject).markResolved(HgUtil.getHgRootOrThrow(myProject, file), file);
   } catch (VcsException e) {
     LOG.error("Couldn't mark file resolved, because it is not under Mercurial root.");
   }
 }
 @Nullable
 @Override
 protected HgRepository guessCurrentRepository(@NotNull Project project) {
   return DvcsUtil.guessCurrentRepositoryQuick(
       project,
       HgUtil.getRepositoryManager(project),
       HgProjectSettings.getInstance(project).getRecentRootPath());
 }
 public static HgFile getOriginalHgFile(Project project, VirtualFile root) {
   HgFile hgFile = new HgFile(root, VcsUtil.getFilePath(root.getPath()));
   if (project.isDisposed()) {
     return hgFile;
   }
   FilePath originalFileName =
       HgUtil.getOriginalFileName(hgFile.toFilePath(), ChangeListManager.getInstance(project));
   return new HgFile(hgFile.getRepo(), originalFileName);
 }
 /**
  * @return the prompthooks.py extension used for capturing prompts from Mercurial and requesting
  *     IDEA's user about authentication.
  */
 @NotNull
 public File getPromptHooksExtensionFile() {
   if (myPromptHooksExtensionFile == null) {
     // check that hooks are available
     myPromptHooksExtensionFile = HgUtil.getTemporaryPythonFile("prompthooks");
     if (myPromptHooksExtensionFile == null || !myPromptHooksExtensionFile.exists()) {
       LOG.error(
           "prompthooks.py Mercurial extension is not found. Please reinstall "
               + ApplicationNamesInfo.getInstance().getProductName());
     }
   }
   return myPromptHooksExtensionFile;
 }
 private HgCommandExitCode pull(VirtualFile repo, ProgressIndicator indicator, boolean isRebase)
     throws VcsException {
   indicator.setText2(HgVcsMessages.message("hg4idea.progress.pull.with.update"));
   HgPullCommand hgPullCommand = new HgPullCommand(project, repo);
   final String defaultPath = HgUtil.getRepositoryDefaultPath(project, repo);
   hgPullCommand.setSource(defaultPath);
   if (isRebase) {
     hgPullCommand.setRebase(true);
   } else {
     hgPullCommand.setUpdate(true);
   }
   return hgPullCommand.execute();
 }
 private void processRebase(final UpdatedFiles updatedFiles) throws VcsException {
   HgRepository repository = HgUtil.getRepositoryManager(project).getRepositoryForRoot(repoRoot);
   HgCommandResult result;
   do {
     resolvePossibleConflicts(updatedFiles);
     if (repository == null || !HgConflictResolver.findConflicts(project, repoRoot).isEmpty()) {
       return;
     }
     HgRebaseCommand rebaseCommand = new HgRebaseCommand(project, repository);
     result = rebaseCommand.continueRebase();
     if (HgErrorUtil.isAbort(result)) {
       new HgCommandResultNotifier(project)
           .notifyError(result, "Hg Error", "Couldn't continue rebasing");
       return;
     }
   } while (result.getExitValue() == 1);
 }
  public void showDialogAndPush(@Nullable final VirtualFile selectedRepo) {
    HgUtil.executeOnPooledThreadIfNeeded(
        new Runnable() {
          public void run() {
            final List<VirtualFile> repositories = HgUtil.getHgRepositories(myProject);
            if (repositories.isEmpty()) {
              VcsBalloonProblemNotifier.showOverChangesView(
                  myProject, "No Mercurial repositories in the project", MessageType.ERROR);
              return;
            }
            VirtualFile firstRepo = repositories.get(0);
            final List<HgTagBranch> branches = getBranches(myProject, firstRepo);
            if (branches.isEmpty()) {
              return;
            }
            final AtomicReference<HgPushCommand> pushCommand = new AtomicReference<HgPushCommand>();
            UIUtil.invokeAndWaitIfNeeded(
                new Runnable() {
                  @Override
                  public void run() {
                    final HgPushDialog dialog =
                        new HgPushDialog(myProject, repositories, branches, selectedRepo);
                    dialog.show();
                    if (dialog.isOK()) {
                      dialog.rememberSettings();
                      pushCommand.set(preparePushCommand(myProject, dialog));
                    }
                  }
                });

            if (pushCommand.get() != null) {
              push(myProject, pushCommand.get());
            }
          }
        });
  }
  @Override
  public void activate() {
    super.activate();

    // validate hg executable on start
    if (!ApplicationManager.getApplication().isUnitTestMode()) {
      getExecutableValidator().checkExecutableAndNotifyIfNeeded();
    }

    // status bar
    myStatusWidget = new HgStatusWidget(this, getProject(), projectSettings);
    myStatusWidget.activate();

    myIncomingWidget = new HgIncomingOutgoingWidget(this, getProject(), projectSettings, true);
    myIncomingWidget.activate();

    myOutgoingWidget = new HgIncomingOutgoingWidget(this, getProject(), projectSettings, false);
    myOutgoingWidget.activate();

    // updaters and listeners
    myHgRemoteStatusUpdater =
        new HgRemoteStatusUpdater(
            this,
            myIncomingWidget.getChangesetStatus(),
            myOutgoingWidget.getChangesetStatus(),
            projectSettings);
    myHgRemoteStatusUpdater.activate();

    myHgCurrentBranchStatusUpdater =
        new HgCurrentBranchStatusUpdater(this, myStatusWidget.getCurrentBranchStatus());
    myHgCurrentBranchStatusUpdater.activate();

    messageBusConnection = myProject.getMessageBus().connect();
    messageBusConnection.subscribe(
        FileEditorManagerListener.FILE_EDITOR_MANAGER,
        new FileEditorManagerAdapter() {
          @Override
          public void selectionChanged(FileEditorManagerEvent event) {
            Project project = event.getManager().getProject();
            project.getMessageBus().syncPublisher(BRANCH_TOPIC).update(project, null);
          }
        });

    myVFSListener = new HgVFSListener(myProject, this);

    // ignore temporary files
    final String ignoredPattern = FileTypeManager.getInstance().getIgnoredFilesList();
    if (!ignoredPattern.contains(ORIG_FILE_PATTERN)) {
      final String newPattern =
          ignoredPattern + (ignoredPattern.endsWith(";") ? "" : ";") + ORIG_FILE_PATTERN;
      HgUtil.runWriteActionLater(
          new Runnable() {
            public void run() {
              FileTypeManager.getInstance().setIgnoredFilesList(newPattern);
            }
          });
    }

    // Force a branch topic update
    myProject.getMessageBus().syncPublisher(BRANCH_TOPIC).update(myProject, null);
  }
 @Override
 public boolean isVersionedDirectory(VirtualFile dir) {
   return HgUtil.getNearestHgRoot(dir) != null;
 }
 @Override
 protected boolean isMultiRoot(@NotNull Project project) {
   return HgUtil.getRepositoryManager(project).moreThanOneRoot();
 }
 @NotNull
 @Override
 protected String getFullBranchName(@NotNull HgRepository repository) {
   return HgUtil.getDisplayableBranchOrBookmarkText(repository);
 }
  public boolean update(
      final UpdatedFiles updatedFiles, ProgressIndicator indicator, List<VcsException> warnings)
      throws VcsException {
    indicator.setText(HgVcsMessages.message("hg4idea.progress.updating", repoRoot.getPath()));

    String defaultPath = HgUtil.getRepositoryDefaultPath(project, repoRoot);

    if (StringUtil.isEmptyOrSpaces(defaultPath)) {
      throw new VcsException(
          HgVcsMessages.message("hg4idea.warning.no-default-update-path", repoRoot.getPath()));
    }

    List<HgRevisionNumber> branchHeadsBeforePull = new HgHeadsCommand(project, repoRoot).execute();

    if (branchHeadsBeforePull.size() > 1) {
      reportWarning(
          warnings,
          HgVcsMessages.message(
              "hg4idea.update.warning.multipleHeadsBeforeUpdate", repoRoot.getPath()));
    }

    // TODO perhaps report a warning in this case ?
    //    //if the parent of the working dir is not the tip of the current branch, the user has
    //    //manually brought his working copy to some specific revision. In that case we won't touch
    //    //his setup
    //    if (!parentRevision.equals(currentBranchHead)) {
    //      throw new VcsException("working dir not at branch tip (use \"Update to...\" to check out
    // branch tip)");
    //    }

    HgRevisionNumber parentBeforeUpdate =
        new HgWorkingCopyRevisionsCommand(project).firstParent(repoRoot);
    HgCommandExitCode pullResult = pull(repoRoot, indicator, shouldRebase());
    if (pullResult == HgCommandExitCode.ERROR) {
      return false;
    }

    if (pullResult == HgCommandExitCode.SUCCESS) {
      HgRevisionNumber parentAfterUpdate =
          new HgWorkingCopyRevisionsCommand(project).firstParent(repoRoot);
      addUpdatedFiles(repoRoot, updatedFiles, parentBeforeUpdate, parentAfterUpdate);
      return true;
    }

    if (shouldMerge()) {

      indicator.setText2(HgVcsMessages.message("hg4idea.progress.countingHeads"));

      List<HgRevisionNumber> branchHeadsAfterPull = new HgHeadsCommand(project, repoRoot).execute();
      List<HgRevisionNumber> pulledBranchHeads =
          determinePulledBranchHeads(branchHeadsBeforePull, branchHeadsAfterPull);
      List<HgRevisionNumber> remainingOriginalBranchHeads =
          determingRemainingOriginalBranchHeads(branchHeadsBeforePull, branchHeadsAfterPull);

      if (branchHeadsAfterPull.size() > 1) {
        abortOnLocalChanges();
        abortOnMultiplePulledHeads(pulledBranchHeads);
        abortOnMultipleLocalHeads(remainingOriginalBranchHeads);

        HgCommandResult mergeResult = doMerge(indicator);

        if (shouldCommitAfterMerge()) {
          commitOrWarnAboutConflicts(warnings, mergeResult);
        }
      }
      // any kind of update could have resulted in merges and merge conflicts, so run the resolver
      resolvePossibleConflicts(updatedFiles);
    } else {
      processRebase(updatedFiles);
    }

    return true;
  }