From c096b42e3e6714f3df3dec317cf1826c43734eb1 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Tue, 14 Jan 2014 17:38:40 +0100 Subject: [PATCH] Add setContains to ListBranchCommand (branch --contains) To correspond to the behavior of "git branch", also return HEAD in case it is detached. Bug: 425678 Change-Id: Ie615731434d70b99bd18c7a02e832c0a2c3ceef3 Signed-off-by: Robin Stocker --- .../eclipse/jgit/api/BranchCommandTest.java | 14 ++++ .../eclipse/jgit/api/ListBranchCommand.java | 78 ++++++++++++++++--- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java index 91ced0a59..c0e2e2ca8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java @@ -196,6 +196,20 @@ public class BranchCommandTest extends RepositoryTestCase { git.branchList().setListMode(ListMode.ALL).call(); } + @Test + public void testListBranchesWithContains() throws Exception { + git.branchCreate().setName("foo").setStartPoint(secondCommit).call(); + + List refs = git.branchList().call(); + assertEquals(2, refs.size()); + + List refsContainingSecond = git.branchList() + .setContains(secondCommit.name()).call(); + assertEquals(1, refsContainingSecond.size()); + // master is on initial commit, so it should not be returned + assertEquals("refs/heads/foo", refsContainingSecond.get(0).getName()); + } + @Test public void testCreateFromCommit() throws Exception { Ref branch = git.branchCreate().setName("FromInitial").setStartPoint( diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java index ea6f34b41..32ee65148 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2010, Mathias Kinzler * Copyright (C) 2010, Chris Aniszczyk + * Copyright (C) 2014, Robin Stocker * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -44,21 +45,30 @@ package org.eclipse.jgit.api; import java.io.IOException; +import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.api.errors.RefNotFoundException; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.revwalk.RevWalkUtils; /** * Used to obtain a list of branches. + *

+ * In case HEAD is detached (it points directly to a commit), it is also + * returned in the results. * * @see > { private ListMode listMode; + private String containsCommitish; + /** * The modes available for listing branches (corresponding to the -r and -a * options) @@ -91,23 +103,28 @@ public class ListBranchCommand extends GitCommand> { public List call() throws GitAPIException { checkCallable(); - Map refList; + List resultRefs; try { + Collection refs = new ArrayList(); + + // Also return HEAD if it's detached + Ref head = repo.getRef(Constants.HEAD); + if (head != null && head.getLeaf().getName().equals(Constants.HEAD)) + refs.add(head); + if (listMode == null) { - refList = repo.getRefDatabase().getRefs(Constants.R_HEADS); + refs.addAll(getRefs(Constants.R_HEADS)); } else if (listMode == ListMode.REMOTE) { - refList = repo.getRefDatabase().getRefs(Constants.R_REMOTES); + refs.addAll(getRefs(Constants.R_REMOTES)); } else { - refList = new HashMap(repo.getRefDatabase().getRefs( - Constants.R_HEADS)); - refList.putAll(repo.getRefDatabase().getRefs( - Constants.R_REMOTES)); + refs.addAll(getRefs(Constants.R_HEADS)); + refs.addAll(getRefs(Constants.R_REMOTES)); } + resultRefs = new ArrayList(filterRefs(refs)); } catch (IOException e) { throw new JGitInternalException(e.getMessage(), e); } - List resultRefs = new ArrayList(); - resultRefs.addAll(refList.values()); + Collections.sort(resultRefs, new Comparator() { public int compare(Ref o1, Ref o2) { return o1.getName().compareTo(o2.getName()); @@ -117,6 +134,26 @@ public class ListBranchCommand extends GitCommand> { return resultRefs; } + private Collection filterRefs(Collection refs) + throws RefNotFoundException, IOException { + if (containsCommitish == null) + return refs; + + RevWalk walk = new RevWalk(repo); + try { + ObjectId resolved = repo.resolve(containsCommitish); + if (resolved == null) + throw new RefNotFoundException(MessageFormat.format( + JGitText.get().refNotResolved, containsCommitish)); + + RevCommit containsCommit = walk.parseCommit(resolved); + return RevWalkUtils.findBranchesReachableFrom(containsCommit, walk, + refs); + } finally { + walk.release(); + } + } + /** * @param listMode * optional: corresponds to the -r/-a options; by default, only @@ -128,4 +165,23 @@ public class ListBranchCommand extends GitCommand> { this.listMode = listMode; return this; } + + /** + * If this is set, only the branches that contain the specified commit-ish + * as an ancestor are returned. + * + * @param containsCommitish + * a commit ID or ref name + * @return this instance + * @since 3.3 + */ + public ListBranchCommand setContains(String containsCommitish) { + checkCallable(); + this.containsCommitish = containsCommitish; + return this; + } + + private Collection getRefs(String prefix) throws IOException { + return repo.getRefDatabase().getRefs(prefix).values(); + } }