10

I want to see all branches that are ancestors of commit abcdef1234.

This is sort of the inverse of:

git branch --contains abcdef1234

The above command would list all branches that are descendants of abcdef1234. I want to see a list of all branches that are ancestors of abcdef1234.

I am also interested the equivalent for tags.

UPDATE

To be extra clear, I mean I want to see a list of all commits that meet 2 criteria:

  1. They are ancestors of abcdef1234
  2. They are currently pointed to by (local or remote) branches.

Obviously most commits at some point had a branch pointing to them when they were brand new. I am only interested in whether they are branches at this particular moment.

sschuberth
  • 28,386
  • 6
  • 101
  • 146
Isaac Betesh
  • 2,935
  • 28
  • 37
  • 2
    Can you give an example of what it means for you to have a branch that's an ancestor of a commit? I'm having trouble understanding your question because branches are implemented as "pointers" to commits at the heads of the branches, so Git doesn't have any history of which commits did or did not belong to which branches in the past. – Kevin May 13 '16 at 18:39
  • 1
    Which is it? "To be extra clear, I mean I want to see a list of all commits" OR "I want to see all branches" You should edit your question and remove one or the other – Jeff Puckett May 13 '16 at 18:44
  • So, what you want is: (K is chosen commit) forall labels L: C = target(L); if C subseteq K: print L. – torek May 13 '16 at 18:45
  • @JeffPuckettII I'd rather see a list of the branch names, but if it displays a list of commit SHA's, I could get from there to branch names easily enough using `git show-ref` or something similar. – Isaac Betesh May 13 '16 at 18:46
  • @torek Is that something I can run on the command line, or is it pseudo-code? – Isaac Betesh May 13 '16 at 18:46
  • pseudocode, but turning into an answer, if that's what you wanted. – torek May 13 '16 at 18:47
  • Look at git describe. –  May 13 '16 at 18:49
  • @torek Looking forward to your answer – Isaac Betesh May 13 '16 at 18:55
  • Possible duplicate of [How can I know in git if a branch has been already merged into master?](http://stackoverflow.com/questions/226976/how-can-i-know-in-git-if-a-branch-has-been-already-merged-into-master) – Andrew C May 13 '16 at 21:50

3 Answers3

10

git branch --merged abcdef1234 should do what you want. It lists all branches that are merged into the named commit (and thus are ancestors of it), i.e. those whose tip commits are reachable from the named commit.

sschuberth
  • 28,386
  • 6
  • 101
  • 146
  • 1
    D'oh, I should have remembered this one. Run with `-r` to get remote-tracking branches or `-a` to get all branches. – torek May 13 '16 at 19:10
  • This led me to `git branch --no-merged origin/master`, which is also incredibly useful. – Isaac Betesh May 13 '16 at 20:01
  • Unfortunately, there doesn't seem to be an equivalent to `--merged` for `git tag` – Isaac Betesh May 13 '16 at 20:03
  • Newer versions of Git do have `git tag --merged` (and `--no-merged`). For those that don't, use `for-each-ref` as I showed, just use `refs/tags` as the label name space. – torek May 13 '16 at 20:51
2

Here's what I suggested as a comment, in expanded form. (I think this is what you're asking for.)

We set K to your chosen commit (abcdefg1234 in your example). Then, we want to iterate through all labels L where L has the form refs/heads/* or refs/remotes/* (all branches and remote-tracking branches). Each label L points to some specific commit C. If C is an ancestor of K, print label L.

[Edit: as sschuberth answered, this is just git branch --merged; the new feature I was thinking of is that git for-each-ref also now implements --merged, which means you can script things more easily, but if all you want is the names, git branch does it. If you do want tags, see git tag --merged, if available. If your version of Git is too old, read on to the script. :-) ]

Here is a shell script (untested!) that implements this. Note that git for-each-ref has new capabilities in later versions of Git that would simplify this, but this should work all the way back to 1.6-ish—maybe 1.7-ish; I forget when git merge-base acquired --is-ancestor.

#! /bin/sh

# find branches and remote-tracking branches that targt
# ancestors of $1.
showthem() {
    local tgt label lbltgt
    tgt=$(git rev-parse "$1") || return $?
    git for-each-ref --format='%(refname:short) %(objectname)' \
            refs/heads refs/remotes |
        while read label lbltgt; do
            if git merge-base --is-ancestor $lbltgt $tgt; then
                echo "$label"
            fi
        done
    return 0
}

case $# in
0) usage 1>&2; exit 1;;
1) showthem "$1";;
*) for i do "echo ${i}:"; showthem "$i" || exit; done
esac

(You may want to tweak this slightly, e.g., to discard symbolic refs like origin/HEAD.)

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
1
comm -23 <(git branch -a | sort) <(git branch -a --contains abcdefg1234 | sort)

This will give you all the branches that do not have commit abcdefg1234; it's the output of git branch -a minus the output of git branch -a --contains abcdefg1234.

Dan Fischer
  • 3,769
  • 1
  • 14
  • 10