@NotNull
 private Set<VcsRef> readBranches(@NotNull GitRepository repository) {
   StopWatch sw = StopWatch.start("readBranches in " + repository.getRoot().getName());
   VirtualFile root = repository.getRoot();
   repository.update();
   Collection<GitLocalBranch> localBranches = repository.getBranches().getLocalBranches();
   Collection<GitRemoteBranch> remoteBranches = repository.getBranches().getRemoteBranches();
   Set<VcsRef> refs = new THashSet<VcsRef>(localBranches.size() + remoteBranches.size());
   for (GitLocalBranch localBranch : localBranches) {
     refs.add(
         myVcsObjectsFactory.createRef(
             localBranch.getHash(), localBranch.getName(), GitRefManager.LOCAL_BRANCH, root));
   }
   for (GitRemoteBranch remoteBranch : remoteBranches) {
     refs.add(
         myVcsObjectsFactory.createRef(
             remoteBranch.getHash(),
             remoteBranch.getNameForLocalOperations(),
             GitRefManager.REMOTE_BRANCH,
             root));
   }
   String currentRevision = repository.getCurrentRevision();
   if (currentRevision != null) { // null => fresh repository
     refs.add(
         myVcsObjectsFactory.createRef(
             HashImpl.build(currentRevision), "HEAD", GitRefManager.HEAD, root));
   }
   sw.report();
   return refs;
 }
  @NotNull
  @Override
  public List<? extends VcsCommitMetadata> readFirstBlock(
      @NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException {
    if (!isRepositoryReady(root)) {
      return Collections.emptyList();
    }

    int commitCount = requirements.getCommitCount();
    if (requirements.isOrdered()) {
      commitCount *=
          2; // need to query more to sort them manually; this doesn't affect performance: it is
      // equal for -1000 and -2000
    }

    String[] params =
        new String[] {"HEAD", "--branches", "--remotes", "--max-count=" + commitCount};
    // NB: not specifying --tags, because it introduces great slowdown if there are many tags,
    // but makes sense only if there are heads without branch or HEAD labels (rare case). Such cases
    // are partially handles below.
    List<VcsCommitMetadata> firstBlock = GitHistoryUtils.loadMetadata(myProject, root, params);

    if (requirements instanceof VcsLogProviderRequirementsEx) {
      VcsLogProviderRequirementsEx rex = (VcsLogProviderRequirementsEx) requirements;
      // on refresh: get new tags, which point to commits not from the first block; then get
      // history, walking down just from these tags
      // on init: just ignore such tagged-only branches. The price for speed-up.
      if (!rex.isOrdered()) {
        Collection<VcsRef> newTags = getNewTags(rex.getCurrentRefs(), rex.getPreviousRefs());
        if (!newTags.isEmpty()) {
          final Set<Hash> firstBlockHashes =
              ContainerUtil.map2Set(
                  firstBlock,
                  new Function<VcsCommitMetadata, Hash>() {
                    @Override
                    public Hash fun(VcsCommitMetadata metadata) {
                      return metadata.getHash();
                    }
                  });
          List<VcsRef> unmatchedHeads = getUnmatchedHeads(firstBlockHashes, newTags);
          if (!unmatchedHeads.isEmpty()) {
            List<VcsCommitMetadata> detailsFromTaggedBranches =
                loadSomeCommitsOnTaggedBranches(root, commitCount, unmatchedHeads);
            Collection<VcsCommitMetadata> unmatchedCommits =
                getUnmatchedCommits(firstBlockHashes, detailsFromTaggedBranches);
            firstBlock.addAll(unmatchedCommits);
          }
        }
      }
    }

    if (requirements.isOrdered()) {
      firstBlock = VcsLogSorter.sortByDateTopoOrder(firstBlock);
      firstBlock =
          new ArrayList<VcsCommitMetadata>(
              firstBlock.subList(0, Math.min(firstBlock.size(), requirements.getCommitCount())));
    }
    return firstBlock;
  }
 private static <T> void addNewElements(
     @NotNull Collection<T> original, @NotNull Collection<T> toAdd) {
   for (T item : toAdd) {
     if (!original.contains(item)) {
       original.add(item);
     }
   }
 }
  private static Groups prepareGroups(
      @NotNull VcsLogDataPack dataPack,
      @Nullable Collection<VirtualFile> visibleRoots,
      @Nullable List<List<String>> recentItems) {
    Groups filteredGroups = new Groups();
    Collection<VcsRef> allRefs = dataPack.getRefs().getBranches();
    for (Map.Entry<VirtualFile, Set<VcsRef>> entry :
        VcsLogUtil.groupRefsByRoot(allRefs).entrySet()) {
      VirtualFile root = entry.getKey();
      if (visibleRoots != null && !visibleRoots.contains(root)) continue;
      Collection<VcsRef> refs = entry.getValue();
      VcsLogProvider provider = dataPack.getLogProviders().get(root);
      VcsLogRefManager refManager = provider.getReferenceManager();
      List<RefGroup> refGroups = refManager.group(refs);

      putActionsForReferences(refGroups, filteredGroups);
    }

    if (recentItems != null) {
      for (List<String> recentItem : recentItems) {
        if (recentItem.size() == 1) {
          final String item = ContainerUtil.getFirstItem(recentItem);
          if (filteredGroups.singletonGroups.contains(item)
              || ContainerUtil.find(
                      filteredGroups.expandedGroups.values(), strings -> strings.contains(item))
                  != null) {
            continue;
          }
        }
        filteredGroups.recentGroups.add(recentItem);
      }
    }

    return filteredGroups;
  }
 // TODO this is to be removed when tags will be supported by the GitRepositoryReader
 private Collection<? extends VcsRef> readTags(@NotNull VirtualFile root) throws VcsException {
   GitSimpleHandler tagHandler = new GitSimpleHandler(myProject, root, GitCommand.LOG);
   tagHandler.setSilent(true);
   tagHandler.addParameters(
       "--tags", "--no-walk", "--format=%H%d" + GitLogParser.RECORD_START_GIT, "--decorate=full");
   String out = tagHandler.run();
   Collection<VcsRef> refs = new ArrayList<VcsRef>();
   try {
     for (String record : out.split(GitLogParser.RECORD_START)) {
       if (!StringUtil.isEmptyOrSpaces(record)) {
         refs.addAll(new RefParser(myVcsObjectsFactory).parseCommitRefs(record.trim(), root));
       }
     }
   } catch (Exception e) {
     LOG.error(
         "Error during tags parsing",
         new Attachment("stack_trace.txt", ExceptionUtil.getThrowableText(e)),
         new Attachment("git_output.txt", out));
   }
   return refs;
 }
  @NotNull
  @Override
  public Collection<VcsRef> readAllRefs(@NotNull VirtualFile root) throws VcsException {
    if (!isRepositoryReady(root)) {
      return Collections.emptyList();
    }

    GitRepository repository = getRepository(root);
    repository.update();
    Collection<GitLocalBranch> localBranches = repository.getBranches().getLocalBranches();
    Collection<GitRemoteBranch> remoteBranches = repository.getBranches().getRemoteBranches();
    Collection<VcsRef> refs = new ArrayList<VcsRef>(localBranches.size() + remoteBranches.size());
    for (GitLocalBranch localBranch : localBranches) {
      refs.add(
          myVcsObjectsFactory.createRef(
              HashImpl.build(localBranch.getHash()),
              localBranch.getName(),
              GitRefManager.LOCAL_BRANCH,
              root));
    }
    for (GitRemoteBranch remoteBranch : remoteBranches) {
      refs.add(
          myVcsObjectsFactory.createRef(
              HashImpl.build(remoteBranch.getHash()),
              remoteBranch.getNameForLocalOperations(),
              GitRefManager.REMOTE_BRANCH,
              root));
    }
    String currentRevision = repository.getCurrentRevision();
    if (currentRevision != null) { // null => fresh repository
      refs.add(
          myVcsObjectsFactory.createRef(
              HashImpl.build(currentRevision), "HEAD", GitRefManager.HEAD, root));
    }

    refs.addAll(readTags(root));
    return refs;
  }
  @NotNull
  @Override
  public List<TimedVcsCommit> getCommitsMatchingFilter(
      @NotNull final VirtualFile root,
      @NotNull VcsLogFilterCollection filterCollection,
      int maxCount)
      throws VcsException {
    if (!isRepositoryReady(root)) {
      return Collections.emptyList();
    }

    List<String> filterParameters = ContainerUtil.newArrayList();

    if (filterCollection.getBranchFilter() != null
        && !filterCollection.getBranchFilter().getBranchNames().isEmpty()) {
      GitRepository repository = getRepository(root);
      assert repository != null
          : "repository is null for root " + root + " but was previously reported as 'ready'";

      boolean atLeastOneBranchExists = false;
      for (String branchName : filterCollection.getBranchFilter().getBranchNames()) {
        if (branchName.equals("HEAD")
            || repository.getBranches().findBranchByName(branchName) != null) {
          filterParameters.add(branchName);
          atLeastOneBranchExists = true;
        }
      }
      if (!atLeastOneBranchExists) { // no such branches in this repository => filter matches
        // nothing
        return Collections.emptyList();
      }
    } else {
      filterParameters.addAll(GitHistoryUtils.LOG_ALL);
    }

    if (filterCollection.getUserFilter() != null) {
      String authorFilter =
          StringUtil.join(
              ContainerUtil.map(
                  filterCollection.getUserFilter().getUserNames(root), UserNameRegex.INSTANCE),
              "|");
      filterParameters.add(prepareParameter("author", StringUtil.escapeBackSlashes(authorFilter)));
      filterParameters.add(
          "--extended-regexp"); // extended regexp required for correctly filtering user names
    }

    if (filterCollection.getDateFilter() != null) {
      // assuming there is only one date filter, until filter expressions are defined
      VcsLogDateFilter filter = filterCollection.getDateFilter();
      if (filter.getAfter() != null) {
        filterParameters.add(prepareParameter("after", filter.getAfter().toString()));
      }
      if (filter.getBefore() != null) {
        filterParameters.add(prepareParameter("before", filter.getBefore().toString()));
      }
    }

    if (filterCollection.getTextFilter() != null) {
      String textFilter = filterCollection.getTextFilter().getText();
      filterParameters.add(prepareParameter("grep", StringUtil.escapeChars(textFilter, '[', ']')));
    }

    filterParameters.add(
        "--regexp-ignore-case"); // affects case sensitivity of any filter (except file filter)
    if (maxCount > 0) {
      filterParameters.add(prepareParameter("max-count", String.valueOf(maxCount)));
    }

    // note: structure filter must be the last parameter, because it uses "--" which separates
    // parameters from paths
    if (filterCollection.getStructureFilter() != null) {
      Collection<VirtualFile> files = filterCollection.getStructureFilter().getFiles();
      if (!files.isEmpty()) {
        filterParameters.add("--full-history");
        filterParameters.add("--simplify-merges");
        filterParameters.add("--");
        for (VirtualFile file : files) {
          filterParameters.add(file.getPath());
        }
      }
    }

    List<TimedVcsCommit> commits = ContainerUtil.newArrayList();
    GitHistoryUtils.readCommits(
        myProject,
        root,
        filterParameters,
        EmptyConsumer.<VcsUser>getInstance(),
        EmptyConsumer.<VcsRef>getInstance(),
        new CollectConsumer<TimedVcsCommit>(commits));
    return commits;
  }
  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;
  }