// the RevWalk must be reset by the caller upon return! private ArrayList<RevCommit> findCommits( RevWalk walk, ArrayList<RevCommit> included, ArrayList<RevCommit> excluded, boolean getBody, long after) throws MissingObjectException, IncorrectObjectTypeException, IOException { ArrayList<RevCommit> commits = new ArrayList<RevCommit>(); commits.ensureCapacity(allBranches.size()); // heuristic workaround walk.sort(RevSort.COMMIT_TIME_DESC, true); walk.sort(RevSort.TOPO, true); walk.setRetainBody(getBody); if (after > 0) walk.setRevFilter(CommitTimeRevFilter.after(after)); else walk.setRevFilter(null); walk.markStart(included); RevCommit c; Iterator<RevCommit> it = excluded.iterator(); while (it.hasNext()) { walk.markUninteresting(it.next()); } it = walk.iterator(); while (it.hasNext()) { c = it.next(); if (getBody) walk.parseBody(c); // addUnique(commits, c); // commits are naturally ordered by SHA-1 commits.add(c); } return commits.size() > 0 ? commits : null; }
private void negotiateBegin() throws IOException { walk.resetRetain(REACHABLE, ADVERTISED); walk.markStart(reachableCommits); walk.sort(RevSort.COMMIT_TIME_DESC); walk.setRevFilter( new RevFilter() { @Override public RevFilter clone() { return this; } @Override public boolean include(final RevWalk walker, final RevCommit c) { final boolean remoteKnowsIsCommon = c.has(COMMON); if (c.has(ADVERTISED)) { // Remote advertised this, and we have it, hence common. // Whether or not the remote knows that fact is tested // before we added the flag. If the remote doesn't know // we have to still send them this object. // c.add(COMMON); } return !remoteKnowsIsCommon; } }); }
/** * Parses an array of Git commits and returns an array of commits that affected the specified * object. * * @param repository the Git repository * @param commitIds the Git commit IDs to parse * @param path the object affected * @return an array of {@link GitCommit} objects representing the commits */ public NSArray<GitCommit> commitsWithIds(NSArray<ObjectId> commitIds, String path) { try { RevWalk rw = new RevWalk(repository); try { rw.sort(RevSort.COMMIT_TIME_DESC); if (path != null) { rw.setTreeFilter( AndTreeFilter.create(PathSuffixFilter.create(path), TreeFilter.ANY_DIFF)); } else { rw.setTreeFilter(TreeFilter.ALL); } for (ObjectId commitId : commitIds) { rw.markStart(rw.parseCommit(commitId)); } NSMutableArray<GitCommit> commits = new NSMutableArray<GitCommit>(); for (RevCommit commit : rw) { commits.add(new GitCommit(commit)); } return commits; } finally { rw.release(); } } catch (Exception e) { log.error("An exception occurred while parsing the commit: ", e); return null; } }
/** * Returns a list of commits since the minimum date starting from the specified object id. * * @param repository * @param objectId if unspecified, HEAD is assumed. * @param minimumDate * @return list of commits */ public static List<RevCommit> getRevLog( Repository repository, String objectId, Date minimumDate) { List<RevCommit> list = new ArrayList<RevCommit>(); if (!hasCommits(repository)) { return list; } try { // resolve branch ObjectId branchObject; if (objectId == null) { branchObject = getDefaultBranch(repository); } else { branchObject = repository.resolve(objectId); } RevWalk rw = new RevWalk(repository); rw.markStart(rw.parseCommit(branchObject)); rw.setRevFilter(CommitTimeRevFilter.after(minimumDate)); Iterable<RevCommit> revlog = rw; for (RevCommit rev : revlog) { list.add(rev); } rw.dispose(); } catch (Throwable t) { // todo Logger.error(t, t.getMessage()); } return list; }
public void markCleanMerges( final RevWalk rw, final RevFlag canMergeFlag, final CodeReviewCommit mergeTip, final Set<RevCommit> alreadyAccepted) throws IntegrationException { if (mergeTip == null) { // If mergeTip is null here, branchTip was null, indicating a new branch // at the start of the merge process. We also elected to merge nothing, // probably due to missing dependencies. Nothing was cleanly merged. // return; } try { rw.resetRetain(canMergeFlag); rw.sort(RevSort.TOPO); rw.sort(RevSort.REVERSE, true); rw.markStart(mergeTip); for (RevCommit c : alreadyAccepted) { rw.markUninteresting(c); } CodeReviewCommit c; while ((c = (CodeReviewCommit) rw.next()) != null) { if (c.getPatchsetId() != null) { c.setStatusCode(CommitMergeStatus.CLEAN_MERGE); } } } catch (IOException e) { throw new IntegrationException("Cannot mark clean merges", e); } }
protected List<RevCommit> getRemoteLog(Project.NameKey project, String branch) throws IOException { try (Repository repo = repoManager.openRepository(project); RevWalk rw = new RevWalk(repo)) { rw.markStart(rw.parseCommit(repo.getRef("refs/heads/" + branch).getObjectId())); return Lists.newArrayList(rw); } }
/** * Returns a list of commits for the repository or a path within the repository. Caller may * specify ending revision with objectId. Caller may specify offset and maxCount to achieve * pagination of results. If the repository does not exist or is empty, an empty list is returned. * * @param repository * @param objectId if unspecified, HEAD is assumed. * @param path if unspecified, commits for repository are returned. If specified, commits for the * path are returned. * @param offset * @param maxCount if < 0, all commits are returned. * @return a paged list of commits */ public static List<RevCommit> getRevLog( Repository repository, String objectId, String path, int offset, int maxCount) { List<RevCommit> list = new ArrayList<RevCommit>(); if (maxCount == 0) { return list; } if (!hasCommits(repository)) { return list; } try { // resolve branch ObjectId branchObject; if (objectId == null) { branchObject = getDefaultBranch(repository); } else { branchObject = repository.resolve(objectId); } RevWalk rw = new RevWalk(repository); rw.markStart(rw.parseCommit(branchObject)); if (!(path == null)) { TreeFilter filter = AndTreeFilter.create( PathFilterGroup.createFromStrings(Collections.singleton(path)), TreeFilter.ALL); // TreeFilter.ANY_DIFF rw.setTreeFilter(filter); } Iterable<RevCommit> revlog = rw; if (offset > 0) { int count = 0; for (RevCommit rev : revlog) { count++; if (count > offset) { list.add(rev); if (maxCount > 0 && list.size() == maxCount) { break; } } } } else { for (RevCommit rev : revlog) { list.add(rev); if (maxCount > 0 && list.size() == maxCount) { break; } } } rw.dispose(); } catch (Throwable t) { // todo Logger.error(t, t.getMessage()); } return list; }
private static boolean isReachable(Repository repo, AnyObjectId id) throws IOException { try (RevWalk rw = new RevWalk(repo)) { for (Ref ref : repo.getAllRefs().values()) { rw.markStart(rw.parseCommit(ref.getObjectId())); } for (RevCommit next; (next = rw.next()) != null; ) { if (AnyObjectId.equals(next, id)) { return true; } } } return false; }
private void markReachable(final Set<ObjectId> have, final int maxTime) throws IOException { for (final Ref r : local.getAllRefs().values()) { try { final RevCommit o = walk.parseCommit(r.getObjectId()); o.add(REACHABLE); reachableCommits.add(o); } catch (IOException readError) { // If we cannot read the value of the ref skip it. } } for (final ObjectId id : have) { try { final RevCommit o = walk.parseCommit(id); o.add(REACHABLE); reachableCommits.add(o); } catch (IOException readError) { // If we cannot read the value of the ref skip it. } } if (maxTime > 0) { // Mark reachable commits until we reach maxTime. These may // wind up later matching up against things we want and we // can avoid asking for something we already happen to have. // final Date maxWhen = new Date(maxTime * 1000L); walk.sort(RevSort.COMMIT_TIME_DESC); walk.markStart(reachableCommits); walk.setRevFilter(CommitTimeRevFilter.after(maxWhen)); for (; ; ) { final RevCommit c = walk.next(); if (c == null) break; if (c.has(ADVERTISED) && !c.has(COMMON)) { // This is actually going to be a common commit, but // our peer doesn't know that fact yet. // c.add(COMMON); c.carry(COMMON); reachableCommits.add(c); } } } }
/** * Configure the generator to compute reverse blame (history of deletes). * * <p>This method is expensive as it immediately runs a RevWalk over the history spanning the * expression {@code start..end} (end being more recent than start) and then performs the * equivalent operation as {@link #push(String, AnyObjectId)} to begin blame traversal from the * commit named by {@code start} walking forwards through history until {@code end} blaming line * deletions. * * <p>A reverse blame may produce multiple sources for the same result line, each of these is a * descendant commit that removed the line, typically this occurs when the same deletion appears * in multiple side branches such as due to a cherry-pick. Applications relying on reverse should * use {@link BlameResult} as it filters these duplicate sources and only remembers the first * (oldest) deletion. * * @param start oldest commit to traverse from. The result file will be loaded from this commit's * tree. * @param end most recent commits to stop traversal at. Usually an active branch tip, tag, or * HEAD. * @return {@code this} * @throws IOException the repository cannot be read. */ public BlameGenerator reverse(AnyObjectId start, Collection<? extends ObjectId> end) throws IOException { initRevPool(true); ReverseCommit result = (ReverseCommit) revPool.parseCommit(start); if (!find(result, resultPath)) return this; revPool.markUninteresting(result); for (ObjectId id : end) revPool.markStart(revPool.parseCommit(id)); while (revPool.next() != null) { // just pump the queue } ReverseCandidate c = new ReverseCandidate(result, resultPath); c.sourceBlob = idBuf.toObjectId(); c.loadText(reader); c.regionList = new Region(0, 0, c.sourceText.size()); remaining = c.sourceText.size(); push(c); return this; }
protected static RevCommit resolveCommitIdByTagName(Repository repository, String tagName) throws IOException, GitAPIException { if (tagName == null || tagName.isEmpty()) return null; RevCommit revCommit = null; Map<String, Ref> tagMap = repository.getTags(); Ref ref = tagMap.get(tagName); if (ref != null) { RevWalk walk = new RevWalk(repository); // some reduce memory effors as described in jgit user guide walk.setRetainBody(false); ObjectId from; from = repository.resolve("refs/heads/master"); if (from == null) { Git git = new Git(repository); String lastTagName = git.describe().call(); from = repository.resolve("refs/tags/" + lastTagName); } ObjectId to = repository.resolve("refs/remotes/origin/master"); if (from == null) { throw new IllegalStateException("cannot determinate start commit"); } walk.markStart(walk.parseCommit(from)); walk.markUninteresting(walk.parseCommit(to)); try { RevObject revObject = walk.parseAny(ref.getObjectId()); if (revObject != null) { revCommit = walk.parseCommit(revObject.getId()); } } finally { walk.close(); } } return revCommit; }
@NotNull public SortedSet<RevCommit> calculate(@NotNull String branchName, @Nullable RevFilter revFilter) throws IOException { Ref head = myRepository.getRef(branchName); if (head == null) { throw new IllegalArgumentException("Branch does not exist: [branchName: " + branchName + "]"); } myRevWalk.markStart(myRevWalk.parseCommit(head.getObjectId())); myRevWalk.setRevFilter(revFilter); SortedSet<RevCommit> roots = new TreeSet<>(REV_COMMIT_COMPARATOR); for (RevCommit commit : myRevWalk) { roots.add(commit); } myRevWalk.reset(); myRevWalk.dispose(); return roots; }
/** * Create an iterator to walk the merge base of two commits. * * @param aIdx index of the first commit in {@link #sourceObjects}. * @param bIdx index of the second commit in {@link #sourceObjects}. * @return the new iterator * @throws IncorrectObjectTypeException one of the input objects is not a commit. * @throws IOException objects are missing or multiple merge bases were found. */ protected AbstractTreeIterator mergeBase(final int aIdx, final int bIdx) throws IOException { if (sourceCommits[aIdx] == null) throw new IncorrectObjectTypeException(sourceObjects[aIdx], Constants.TYPE_COMMIT); if (sourceCommits[bIdx] == null) throw new IncorrectObjectTypeException(sourceObjects[bIdx], Constants.TYPE_COMMIT); walk.reset(); walk.setRevFilter(RevFilter.MERGE_BASE); walk.markStart(sourceCommits[aIdx]); walk.markStart(sourceCommits[bIdx]); final RevCommit base = walk.next(); if (base == null) return new EmptyTreeIterator(); final RevCommit base2 = walk.next(); if (base2 != null) { throw new IOException( MessageFormat.format( JGitText.get().multipleMergeBasesFor, sourceCommits[aIdx].name(), sourceCommits[bIdx].name(), base.name(), base2.name())); } return openTree(base.getTree()); }
// printout all commit messages in a given range -> expensive: creates a one-time only RevWalk void printCommits(String outFile, String from_ref, String to_ref) throws IOException, NoHeadException, GitAPIException { AnyObjectId from = git.getRepository().resolve(from_ref); AnyObjectId to = (to_ref == null || to_ref.equals("")) ? null : git.getRepository().resolve(to_ref); RevWalk walk = new RevWalk(git.getRepository()); walk.sort(RevSort.COMMIT_TIME_DESC, true); walk.sort(RevSort.TOPO, true); walk.markStart(walk.parseCommit(from)); if (to != null) walk.markUninteresting(walk.parseCommit(to)); printCommits(outFile, walk); walk.dispose(); // PrintWriter pout = new PrintWriter(new FileWriter(outFile), true); // Iterator<RevCommit> itc = git.log().add(from).call().iterator(); // RevCommit c; // while (itc.hasNext()) { // c = itc.next(); // pout.println("===========\n" + printCommit(c)); // if (to != null && c.equals(to)) break; // } // pout.close(); }
public String getProjectVersion(File repoDir) throws IOException, GitAPIException { Git git = Git.open(repoDir); Repository repo = git.getRepository(); // Find base commit between current branch and "master": String branch = repo.getBranch(); RevCommit base = CommitUtils.getBase(repo, "master", branch); CommitCountFilter count = new CommitCountFilter(); CommitFinder finder = new CommitFinder(repo).setFilter(count); finder.findBetween(branch, base); long commitsSinceBase = count.getCount(); // Find tags in "master" before base commit: RevWalk rw = new RevWalk(repo); rw.markStart(base); rw.setRetainBody(false); Ref master = repo.getRef("master"); List<Ref> masterAsList = Arrays.asList(master); List<Ref> tags = git.tagList().call(); Map<RevCommit, Ref> masterTags = new HashMap<RevCommit, Ref>(); for (Ref tag : tags) { tag = repo.peel(tag); ObjectId commitID = tag.getPeeledObjectId(); if (commitID == null) continue; RevCommit commit = rw.parseCommit(commitID); // Only remember tags reachable from "master": if (!RevWalkUtils.findBranchesReachableFrom(commit, rw, masterAsList).isEmpty()) { masterTags.put(commit, tag); } } // Find the shortest distance in commits between base tag in "master": long commitsBetweenBaseAndTag = Long.MAX_VALUE; String tagName = ""; for (RevCommit tagCommit : masterTags.keySet()) { count.reset(); finder.findBetween(base, tagCommit); if (count.getCount() < commitsBetweenBaseAndTag) { commitsBetweenBaseAndTag = count.getCount(); tagName = masterTags.get(tagCommit).getName(); } } if (commitsBetweenBaseAndTag == Long.MAX_VALUE) { // If no tag, get total number of commits: commitsBetweenBaseAndTag = repo.getRefDatabase().getRefs("").size(); } long commitsSinceLastMasterTag = commitsSinceBase + commitsBetweenBaseAndTag; // Construct version string: String version = branch.equals("master") ? "" : (branch + "-"); if (tagName.startsWith("refs/tags/")) { tagName = tagName.substring("refs/tags/".length()); } // v1.1 -> 1.1 if (tagName.matches("v\\d+.*")) { tagName = tagName.substring(1); } if (tagName.isEmpty()) { version = "0"; } version += tagName + ((!tagonly) ? "." + commitsSinceLastMasterTag : ""); return version; }
/** * Update the submodules in one branch of one repository. * * @param subscriber the branch of the repository which should be changed. * @param updates submodule updates which should be updated to. * @throws SubmoduleException */ private void updateGitlinks( ReviewDb db, Branch.NameKey subscriber, Collection<SubmoduleSubscription> updates) throws SubmoduleException { PersonIdent author = null; Repository pdb = null; RevWalk recRw = null; StringBuilder msgbuf = new StringBuilder("Updated git submodules\n\n"); try { boolean sameAuthorForAll = true; pdb = repoManager.openRepository(subscriber.getParentKey()); if (pdb.getRef(subscriber.get()) == null) { throw new SubmoduleException( "The branch was probably deleted from the subscriber repository"); } DirCache dc = readTree(pdb, pdb.getRef(subscriber.get())); DirCacheEditor ed = dc.editor(); for (SubmoduleSubscription s : updates) { try (Repository subrepo = repoManager.openRepository(s.getSubmodule().getParentKey()); RevWalk rw = CodeReviewCommit.newRevWalk(subrepo)) { Ref ref = subrepo.getRefDatabase().exactRef(s.getSubmodule().get()); if (ref == null) { ed.add(new DeletePath(s.getPath())); continue; } final ObjectId updateTo = ref.getObjectId(); RevCommit newCommit = rw.parseCommit(updateTo); if (author == null) { author = newCommit.getAuthorIdent(); } else if (!author.equals(newCommit.getAuthorIdent())) { sameAuthorForAll = false; } DirCacheEntry dce = dc.getEntry(s.getPath()); ObjectId oldId = null; if (dce != null) { if (!dce.getFileMode().equals(FileMode.GITLINK)) { log.error( "Requested to update gitlink " + s.getPath() + " in " + s.getSubmodule().getParentKey().get() + " but entry " + "doesn't have gitlink file mode."); continue; } oldId = dce.getObjectId(); } else { // This submodule did not exist before. We do not want to add // the full submodule history to the commit message, so omit it. oldId = updateTo; } ed.add( new PathEdit(s.getPath()) { @Override public void apply(DirCacheEntry ent) { ent.setFileMode(FileMode.GITLINK); ent.setObjectId(updateTo); } }); if (verboseSuperProject) { msgbuf.append("Project: " + s.getSubmodule().getParentKey().get()); msgbuf.append(" " + s.getSubmodule().getShortName()); msgbuf.append(" " + updateTo.getName()); msgbuf.append("\n\n"); try { rw.markStart(newCommit); if (oldId != null) { rw.markUninteresting(rw.parseCommit(oldId)); } for (RevCommit c : rw) { msgbuf.append(c.getFullMessage() + "\n\n"); } } catch (IOException e) { logAndThrowSubmoduleException( "Could not perform a revwalk to " + "create superproject commit message", e); } } } } ed.finish(); if (!sameAuthorForAll || author == null) { author = myIdent; } ObjectInserter oi = pdb.newObjectInserter(); ObjectId tree = dc.writeTree(oi); ObjectId currentCommitId = pdb.getRef(subscriber.get()).getObjectId(); CommitBuilder commit = new CommitBuilder(); commit.setTreeId(tree); commit.setParentIds(new ObjectId[] {currentCommitId}); commit.setAuthor(author); commit.setCommitter(myIdent); commit.setMessage(msgbuf.toString()); oi.insert(commit); oi.flush(); ObjectId commitId = oi.idFor(Constants.OBJ_COMMIT, commit.build()); final RefUpdate rfu = pdb.updateRef(subscriber.get()); rfu.setForceUpdate(false); rfu.setNewObjectId(commitId); rfu.setExpectedOldObjectId(currentCommitId); rfu.setRefLogMessage("Submit to " + subscriber.getParentKey().get(), true); switch (rfu.update()) { case NEW: case FAST_FORWARD: gitRefUpdated.fire(subscriber.getParentKey(), rfu); changeHooks.doRefUpdatedHook(subscriber, rfu, account); // TODO since this is performed "in the background" no mail will be // sent to inform users about the updated branch break; default: throw new IOException(rfu.getResult().name()); } recRw = new RevWalk(pdb); // Recursive call: update subscribers of the subscriber updateSuperProjects(db, Sets.newHashSet(subscriber)); } catch (IOException e) { throw new SubmoduleException("Cannot update gitlinks for " + subscriber.get(), e); } finally { if (recRw != null) { recRw.close(); } if (pdb != null) { pdb.close(); } } }
/** * Process the EGit history associated with a given project. * * @param selectedProject selected project, presumably an object contribution selection * @throws CoreException * @throws IOException */ public void processHistory(IProject selectedProject, IProgressMonitor monitor) throws CoreException, IOException { // find the repository mapping for the project // if none found, return RepositoryMapping repositoryMapping = RepositoryMapping.getMapping((IResource) selectedProject); if (repositoryMapping == null) { CertWareLog.logWarning( String.format("%s %s", "Missing repository for project", selectedProject.getName())); return; } // build the commit history model, load it from the tree walk final CommitHistory commitHistory = ScoFactory.eINSTANCE.createCommitHistory(); Repository repo = repositoryMapping.getRepository(); RevWalk revWalk = new RevWalk(repo); ObjectId headObject = repo.resolve("HEAD"); revWalk.markStart(revWalk.parseCommit(headObject)); final Set<String> repositoryPaths = Collections.singleton(repositoryMapping.getRepoRelativePath(selectedProject)); revWalk.setTreeFilter(PathFilterGroup.createFromStrings(repositoryPaths)); for (RevCommit commit : revWalk) { String commitName = commit.getName(); ArtifactCommit artifactCommit = ScoFactory.eINSTANCE.createArtifactCommit(); artifactCommit.setCommitIdentifier(commitName); commitHistory.getCommitRecord().add(artifactCommit); } revWalk.dispose(); // use the Git provider to find the file history, then converge into the model GitProvider provider = (GitProvider) RepositoryProvider.getProvider(selectedProject); IFileHistoryProvider fileHistoryProvider = provider.getFileHistoryProvider(); IResource[] projectMembers = selectedProject.members(); monitor.beginTask("Processing project resources", projectMembers.length); for (IResource resource : projectMembers) { processResource(resource, fileHistoryProvider, commitHistory, monitor); monitor.worked(1); if (monitor.isCanceled()) { return; } } // model complete with commit history and associated file sizes // write the resulting model to an SCO file // expecting preference to have no extension, so add it if necessary IPreferenceStore store = Activator.getDefault().getPreferenceStore(); String fileName = store.getString(PreferenceConstants.P_FILENAME_SCO); if (fileName.endsWith(ICertWareConstants.SCO_EXTENSION) == false) { fileName = fileName + '.' + ICertWareConstants.SCO_EXTENSION; } // fully specify the path to the new file given the container project final String modelFile = selectedProject.getFullPath().toPortableString() + IPath.SEPARATOR + fileName; // create the resource in a workspace modify operation WorkspaceModifyOperation operation = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor progressMonitor) { try { // create a resource set and resource for a new file ResourceSet resourceSet = new ResourceSetImpl(); URI fileURI = URI.createPlatformResourceURI(modelFile, true); Resource resource = resourceSet.createResource(fileURI); resource.getContents().add(commitHistory); // save the contents of the resource to the file system Map<Object, Object> options = new HashMap<Object, Object>(); options.put(XMLResource.OPTION_ENCODING, FILE_ENCODING); resource.save(options); } catch (Exception e) { CertWareLog.logError(String.format("%s %s", "Saving SCO file", modelFile), e); } } }; // modify the workspace try { operation.run(monitor); } catch (Exception e) { CertWareLog.logError( String.format("%s %s", "Modifying workspace for", selectedProject.getName()), e); } monitor.done(); }
@Override public void execute() throws BuildException { if (_property == null) { throw new BuildException("Property attribute is required", getLocation()); } if (_path == null) { throw new BuildException("Path attribute is required", getLocation()); } File gitDir = PathUtil.getGitDir(_gitDir, getProject(), getLocation()); String relativePath = PathUtil.toRelativePath(gitDir, _path); if (_useCache) { String hash = _hashes.get(relativePath); if (hash != null) { Project currentProject = getProject(); currentProject.setNewProperty(_property, hash); return; } } try (Repository repository = RepositoryCache.open(FileKey.exact(gitDir, FS.DETECTED))) { RevWalk revWalk = new RevWalk(repository); revWalk.setRetainBody(false); revWalk.markStart(revWalk.parseCommit(repository.resolve(Constants.HEAD))); if (_ignoreFileName == null) { revWalk.setRevFilter(MaxCountRevFilter.create(1)); } else { revWalk.setRevFilter(MaxCountRevFilter.create(2)); } revWalk.setTreeFilter( AndTreeFilter.create(PathFilter.create(relativePath), TreeFilter.ANY_DIFF)); RevCommit revCommit = revWalk.next(); if (revCommit == null) { throw new IllegalStateException("Unable to find any commit under " + _path); } if (hasIgnoreFile(repository, revCommit, relativePath)) { RevCommit secondRevCommit = revWalk.next(); if (secondRevCommit != null) { revCommit = secondRevCommit; } } Project currentProject = getProject(); String hash = revCommit.name(); currentProject.setNewProperty(_property, hash); if (_useCache) { _hashes.put(relativePath, hash); } revWalk.dispose(); } catch (Exception e) { throw new BuildException("Unable to get head hash for path " + _path, e); } }