Isaac Betesh
Isaac Betesh

Reputation: 3000

How can I list all branches that are ancestors of a commit?

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.

Upvotes: 10

Views: 2087

Answers (3)

sschuberth
sschuberth

Reputation: 29866

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.

Upvotes: 10

torek
torek

Reputation: 489848

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.)

Upvotes: 2

Dan
Dan

Reputation: 4189

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.

Upvotes: 1

Related Questions