@NotNull
  public static <CommitInfo> List<CommitInfo> getCommitRecords(
      @NotNull Project project,
      @Nullable HgCommandResult result,
      @NotNull Function<String, CommitInfo> converter,
      boolean silent) {
    final List<CommitInfo> revisions = new LinkedList<CommitInfo>();
    if (result == null) {
      return revisions;
    }

    List<String> errors = result.getErrorLines();
    if (errors != null && !errors.isEmpty()) {
      if (result.getExitValue() != 0) {
        if (silent) {
          LOG.warn(errors.toString());
        } else {
          VcsNotifier.getInstance(project)
              .notifyError(
                  HgVcsMessages.message("hg4idea.error.log.command.execution"), errors.toString());
        }
        return Collections.emptyList();
      }
      LOG.warn(errors.toString());
    }
    String output = result.getRawOutput();
    List<String> changeSets = StringUtil.split(output, HgChangesetUtil.CHANGESET_SEPARATOR);
    return ContainerUtil.mapNotNull(changeSets, converter);
  }
 @NotNull
 public static List<String> prepareHashes(@NotNull List<String> hashes) {
   List<String> hashArgs = new ArrayList<String>();
   for (String hash : hashes) {
     hashArgs.add("-r");
     hashArgs.add(hash);
   }
   return hashArgs;
 }
  @NotNull
  public static List<VcsCommitMetadata> loadMetadata(
      @NotNull final Project project,
      @NotNull final VirtualFile root,
      int limit,
      @NotNull List<String> parameters)
      throws VcsException {

    final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project);
    if (factory == null) {
      return Collections.emptyList();
    }
    HgVcs hgvcs = HgVcs.getInstance(project);
    assert hgvcs != null;
    HgVersion version = hgvcs.getVersion();
    List<String> templateList = HgBaseLogParser.constructDefaultTemplate(version);
    templateList.add("{desc}");
    String[] templates = ArrayUtil.toStringArray(templateList);
    HgCommandResult result =
        getLogResult(
            project, root, version, limit, parameters, HgChangesetUtil.makeTemplate(templates));
    HgBaseLogParser<VcsCommitMetadata> baseParser =
        new HgBaseLogParser<VcsCommitMetadata>() {

          @Override
          protected VcsCommitMetadata convertDetails(
              @NotNull String rev,
              @NotNull String changeset,
              @NotNull SmartList<HgRevisionNumber> parents,
              @NotNull Date revisionDate,
              @NotNull String author,
              @NotNull String email,
              @NotNull List<String> attributes) {
            String message = parseAdditionalStringAttribute(attributes, MESSAGE_INDEX);
            int subjectIndex = message.indexOf('\n');
            String subject = subjectIndex == -1 ? message : message.substring(0, subjectIndex);
            List<Hash> parentsHash = new SmartList<Hash>();
            for (HgRevisionNumber parent : parents) {
              parentsHash.add(factory.createHash(parent.getChangeset()));
            }
            return factory.createCommitMetadata(
                factory.createHash(changeset),
                parentsHash,
                revisionDate.getTime(),
                root,
                subject,
                author,
                email,
                message,
                author,
                email,
                revisionDate.getTime());
          }
        };
    return getCommitRecords(project, result, baseParser);
  }
 @Nullable
 private static HgCommandResult getLogResult(
     @NotNull final Project project,
     @NotNull final VirtualFile root,
     @NotNull HgVersion version,
     int limit,
     @NotNull List<String> parameters,
     @NotNull String template) {
   HgFile originalHgFile = getOriginalHgFile(project, root);
   HgLogCommand hgLogCommand = new HgLogCommand(project);
   List<String> args = new ArrayList<String>(parameters);
   hgLogCommand.setLogFile(false);
   if (!version.isParentRevisionTemplateSupported()) {
     args.add("--debug");
   }
   return hgLogCommand.execute(root, template, limit, originalHgFile, args);
 }
 @NotNull
 public static Collection<String> getDescendingHeadsOfBranches(
     @NotNull Project project, @NotNull VirtualFile root, @NotNull Hash hash) throws VcsException {
   // hg log -r "descendants(659db54c1b6865c97c4497fa867194bcd759ca76) and head()" --template
   // "{branch}{bookmarks}"
   Set<String> branchHeads = new HashSet<String>();
   List<String> params = new ArrayList<String>();
   params.add("-r");
   params.add("descendants(" + hash.asString() + ") and head()");
   HgLogCommand hgLogCommand = new HgLogCommand(project);
   hgLogCommand.setLogFile(false);
   String template = HgChangesetUtil.makeTemplate("{branch}", "{bookmarks}");
   HgCommandResult logResult = hgLogCommand.execute(root, template, -1, null, params);
   if (logResult == null || logResult.getExitValue() != 0) {
     throw new VcsException("Couldn't get commit details: log command execution error.");
   }
   String output = logResult.getRawOutput();
   List<String> changeSets = StringUtil.split(output, HgChangesetUtil.CHANGESET_SEPARATOR);
   for (String line : changeSets) {
     List<String> attributes = StringUtil.split(line, HgChangesetUtil.ITEM_SEPARATOR);
     branchHeads.addAll(attributes);
   }
   return branchHeads;
 }
  public static List<? extends VcsFullCommitDetails> createFullCommitsFromResult(
      @NotNull Project project,
      @NotNull VirtualFile root,
      @Nullable HgCommandResult result,
      @NotNull HgVersion version,
      boolean silent) {
    final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project);
    if (factory == null) {
      return Collections.emptyList();
    }
    List<HgFileRevision> hgRevisions =
        getCommitRecords(
            project,
            result,
            new HgFileRevisionLogParser(project, getOriginalHgFile(project, root), version),
            silent);
    List<VcsFullCommitDetails> vcsFullCommitDetailsList = new ArrayList<VcsFullCommitDetails>();
    for (HgFileRevision revision : hgRevisions) {

      HgRevisionNumber vcsRevisionNumber = revision.getRevisionNumber();
      List<HgRevisionNumber> parents = vcsRevisionNumber.getParents();
      HgRevisionNumber firstParent =
          parents.isEmpty() ? null : parents.get(0); // can have no parents if it is a root
      List<Hash> parentsHash = new SmartList<Hash>();
      for (HgRevisionNumber parent : parents) {
        parentsHash.add(factory.createHash(parent.getChangeset()));
      }

      final Collection<Change> changes = new ArrayList<Change>();
      for (String file : revision.getModifiedFiles()) {
        changes.add(
            createChange(
                project, root, file, firstParent, file, vcsRevisionNumber, FileStatus.MODIFIED));
      }
      for (String file : revision.getAddedFiles()) {
        changes.add(
            createChange(project, root, null, null, file, vcsRevisionNumber, FileStatus.ADDED));
      }
      for (String file : revision.getDeletedFiles()) {
        changes.add(
            createChange(
                project, root, file, firstParent, null, vcsRevisionNumber, FileStatus.DELETED));
      }
      for (Map.Entry<String, String> copiedFile : revision.getCopiedFiles().entrySet()) {
        changes.add(
            createChange(
                project,
                root,
                copiedFile.getKey(),
                firstParent,
                copiedFile.getValue(),
                vcsRevisionNumber,
                FileStatus.ADDED));
      }

      vcsFullCommitDetailsList.add(
          factory.createFullDetails(
              factory.createHash(vcsRevisionNumber.getChangeset()),
              parentsHash,
              revision.getRevisionDate().getTime(),
              root,
              vcsRevisionNumber.getSubject(),
              vcsRevisionNumber.getAuthor(),
              vcsRevisionNumber.getEmail(),
              vcsRevisionNumber.getCommitMessage(),
              vcsRevisionNumber.getAuthor(),
              vcsRevisionNumber.getEmail(),
              revision.getRevisionDate().getTime(),
              new ThrowableComputable<Collection<Change>, Exception>() {
                @Override
                public Collection<Change> compute() throws Exception {
                  return changes;
                }
              }));
    }
    return vcsFullCommitDetailsList;
  }