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