@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); }
@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); }