@Override
  @NotNull
  public LogData readAllHashes(
      @NotNull VirtualFile root, @NotNull final Consumer<TimedVcsCommit> commitConsumer)
      throws VcsException {
    if (!isRepositoryReady(root)) {
      return LogDataImpl.empty();
    }

    List<String> parameters = new ArrayList<String>(GitHistoryUtils.LOG_ALL);
    parameters.add("--date-order");

    final GitBekParentFixer parentFixer = GitBekParentFixer.prepare(root, this);
    Set<VcsUser> userRegistry = newHashSet();
    Set<VcsRef> refs = newHashSet();
    GitHistoryUtils.readCommits(
        myProject,
        root,
        parameters,
        new CollectConsumer<VcsUser>(userRegistry),
        new CollectConsumer<VcsRef>(refs),
        new Consumer<TimedVcsCommit>() {
          @Override
          public void consume(TimedVcsCommit commit) {
            commitConsumer.consume(parentFixer.fixCommit(commit));
          }
        });
    return new LogDataImpl(refs, userRegistry);
  }
  @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;
  }
 @NotNull
 private DetailedLogData loadSomeCommitsOnTaggedBranches(
     @NotNull VirtualFile root, int commitCount, @NotNull Collection<String> unmatchedTags)
     throws VcsException {
   List<String> params = new ArrayList<String>();
   params.add("--max-count=" + commitCount);
   params.addAll(unmatchedTags);
   return GitHistoryUtils.loadMetadata(myProject, root, true, ArrayUtil.toStringArray(params));
 }
 @NotNull
 private DetailedLogData loadSomeCommitsOnTaggedBranches(
     @NotNull VirtualFile root, int commitCount, @NotNull Collection<String> unmatchedTags)
     throws VcsException {
   StopWatch sw = StopWatch.start("loading commits on tagged branch in " + root.getName());
   List<String> params = new ArrayList<String>();
   params.add("--max-count=" + commitCount);
   params.addAll(unmatchedTags);
   sw.report();
   return GitHistoryUtils.loadMetadata(myProject, root, true, ArrayUtil.toStringArray(params));
 }
  @NotNull
  private DiffInfo doLoadDiffInfo(@NotNull final BranchInfo branch) throws VcsException {
    // TODO: make cancelable and abort old speculative requests (when git4idea will allow to do so)
    String currentBranch = myCurrentBranch;
    String targetBranch = branch.getForkInfo().getRemoteName() + "/" + branch.getRemoteName();

    List<GitCommit> commits1 =
        GitHistoryUtils.history(myProject, myGitRepository.getRoot(), ".." + targetBranch);
    List<GitCommit> commits2 =
        GitHistoryUtils.history(myProject, myGitRepository.getRoot(), targetBranch + "..");
    Collection<Change> diff =
        GitChangeUtils.getDiff(
            myProject, myGitRepository.getRoot(), targetBranch, myCurrentBranch, null);
    GitCommitCompareInfo info =
        new GitCommitCompareInfo(GitCommitCompareInfo.InfoType.BRANCH_TO_HEAD);
    info.put(myGitRepository, diff);
    info.put(myGitRepository, Couple.of(commits1, commits2));

    return new DiffInfo(info, currentBranch, targetBranch);
  }
Example #6
0
 @NotNull
 @Override
 public List<GitCommit> history(@NotNull GitRepository repository, @NotNull String range) {
   try {
     return GitHistoryUtils.history(repository.getProject(), repository.getRoot(), range);
   } catch (VcsException e) {
     // this is critical, because we need to show the list of unmerged commits, and it shouldn't
     // happen => inform user and developer
     throw new GitExecutionException(
         "Couldn't get [git log " + range + "] on repository [" + repository.getRoot() + "]", e);
   }
 }
  @NotNull
  @Override
  public List<TimedVcsCommit> readAllHashes(
      @NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry) throws VcsException {
    if (!isRepositoryReady(root)) {
      return Collections.emptyList();
    }

    List<String> parameters = new ArrayList<String>(GitHistoryUtils.LOG_ALL);
    parameters.add("--sparse");
    return GitHistoryUtils.readCommits(myProject, root, userRegistry, parameters);
  }
 @NotNull
 @Override
 public List<? extends VcsFullCommitDetails> readFullDetails(
     @NotNull VirtualFile root, @NotNull List<String> hashes) throws VcsException {
   String noWalk =
       GitVersionSpecialty.NO_WALK_UNSORTED.existsIn(myVcs.getVersion())
           ? "--no-walk=unsorted"
           : "--no-walk";
   List<String> params = new ArrayList<String>();
   params.add(noWalk);
   params.addAll(hashes);
   return GitHistoryUtils.history(myProject, root, ArrayUtil.toStringArray(params));
 }
    private static void showDiffWithBranch(
        @NotNull Project project,
        @NotNull VirtualFile file,
        @NotNull String head,
        @NotNull String branchToCompare)
        throws VcsException {
      final FilePath filePath = new FilePathImpl(file);
      // we could use something like GitRepository#getCurrentRevision here,
      // but this way we can easily identify if the file is available in the branch
      final GitRevisionNumber currentRevisionNumber =
          (GitRevisionNumber) GitHistoryUtils.getCurrentRevision(project, filePath, head);
      final GitRevisionNumber compareRevisionNumber =
          (GitRevisionNumber)
              GitHistoryUtils.getCurrentRevision(project, filePath, branchToCompare);

      if (compareRevisionNumber == null) {
        fileDoesntExistInBranchError(project, file, branchToCompare);
        return;
      }
      LOG.assertTrue(
          currentRevisionNumber != null,
          String.format(
              "Current revision number is null for file [%s] and branch [%s]", filePath, head));

      // constructing the revision with human readable name (will work for files comparison
      // however).
      final VcsFileRevision compareRevision =
          new GitFileRevision(
              project,
              filePath,
              new GitRevisionNumber(branchToCompare, compareRevisionNumber.getTimestamp()));
      CurrentRevision currentRevision =
          new CurrentRevision(
              file, new GitRevisionNumber(head, currentRevisionNumber.getTimestamp()));
      new GitDiffFromHistoryHandler(project)
          .showDiffForTwo(new FilePathImpl(file), compareRevision, currentRevision);
    }
 @Nullable
 private static DiffInfo loadDiffInfo(
     @NotNull final Project project,
     @NotNull final GitRepository repository,
     @NotNull final String currentBranch,
     @NotNull final String targetBranch) {
   try {
     List<GitCommit> commits1 =
         GitHistoryUtils.history(project, repository.getRoot(), ".." + targetBranch);
     List<GitCommit> commits2 =
         GitHistoryUtils.history(project, repository.getRoot(), targetBranch + "..");
     Collection<Change> diff =
         GitChangeUtils.getDiff(
             repository.getProject(), repository.getRoot(), targetBranch, currentBranch, null);
     GitCommitCompareInfo info =
         new GitCommitCompareInfo(GitCommitCompareInfo.InfoType.BRANCH_TO_HEAD);
     info.put(repository, diff);
     info.put(repository, Pair.create(commits1, commits2));
     return new DiffInfo(info, currentBranch, targetBranch);
   } catch (VcsException e) {
     LOG.info(e);
     return null;
   }
 }
 @NotNull
 private List<VcsCommitMetadata> loadSomeCommitsOnTaggedBranches(
     @NotNull VirtualFile root, int commitCount, @NotNull List<VcsRef> unmatchedHeads)
     throws VcsException {
   List<String> params =
       new ArrayList<String>(
           ContainerUtil.map(
               unmatchedHeads,
               new Function<VcsRef, String>() {
                 @Override
                 public String fun(VcsRef ref) {
                   return ref.getCommitHash().asString();
                 }
               }));
   params.add("--max-count=" + commitCount);
   return GitHistoryUtils.loadMetadata(myProject, root, ArrayUtil.toStringArray(params));
 }
  @NotNull
  @Override
  public DetailedLogData readFirstBlock(
      @NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException {
    if (!isRepositoryReady(root)) {
      return LogDataImpl.empty();
    }
    GitRepository repository =
        ObjectUtils.assertNotNull(myRepositoryManager.getRepositoryForRoot(root));

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

    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 handled below.

    boolean refresh =
        requirements instanceof VcsLogProviderRequirementsEx
            && ((VcsLogProviderRequirementsEx) requirements).isRefresh();

    DetailedLogData data = GitHistoryUtils.loadMetadata(myProject, root, true, params);

    Set<VcsRef> safeRefs = data.getRefs();
    Set<VcsRef> allRefs = new OpenTHashSet<VcsRef>(safeRefs, DONT_CONSIDER_SHA);
    Set<VcsRef> branches = readBranches(repository);
    addNewElements(allRefs, branches);

    Collection<VcsCommitMetadata> allDetails;
    Set<String> currentTagNames = null;
    DetailedLogData commitsFromTags = null;
    if (!refresh) {
      allDetails = data.getCommits();
    } else {
      // 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.
      VcsLogProviderRequirementsEx rex = (VcsLogProviderRequirementsEx) requirements;

      currentTagNames = readCurrentTagNames(root);
      addOldStillExistingTags(allRefs, currentTagNames, rex.getPreviousRefs());

      allDetails = newHashSet(data.getCommits());

      Set<String> previousTags =
          newHashSet(ContainerUtil.mapNotNull(rex.getPreviousRefs(), GET_TAG_NAME));
      Set<String> safeTags = newHashSet(ContainerUtil.mapNotNull(safeRefs, GET_TAG_NAME));
      Set<String> newUnmatchedTags = remove(currentTagNames, previousTags, safeTags);

      if (!newUnmatchedTags.isEmpty()) {
        commitsFromTags = loadSomeCommitsOnTaggedBranches(root, commitCount, newUnmatchedTags);
        addNewElements(allDetails, commitsFromTags.getCommits());
        addNewElements(allRefs, commitsFromTags.getRefs());
      }
    }

    StopWatch sw = StopWatch.start("sorting commits in " + root.getName());
    List<VcsCommitMetadata> sortedCommits = VcsLogSorter.sortByDateTopoOrder(allDetails);
    sortedCommits =
        sortedCommits.subList(0, Math.min(sortedCommits.size(), requirements.getCommitCount()));
    sw.report();

    if (LOG.isDebugEnabled()) {
      validateDataAndReportError(
          root, allRefs, sortedCommits, data, branches, currentTagNames, commitsFromTags);
    }

    return new LogDataImpl(allRefs, sortedCommits);
  }
  @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;
  }
 @NotNull
 @Override
 public List<? extends VcsShortCommitDetails> readShortDetails(
     @NotNull VirtualFile root, @NotNull List<String> hashes) throws VcsException {
   return GitHistoryUtils.readMiniDetails(myProject, root, hashes);
 }