16

If you use gitk --all, you can see all the commits of your repo, from all branches. I want something like that except only the descendants of a given commit.

Pistos
  • 23,070
  • 14
  • 64
  • 77
  • 1
    Though manojlds' first sentence is correct, it's not the entire truth: it would be possible to kludge something which checks to see if each ref has the given commit in its ancestry, and then display history starting from those refs. – Cascabel Dec 11 '11 at 03:44
  • 2
    possible duplicate of [Getting a list of all children of a given commit](http://stackoverflow.com/questions/2613935/getting-a-list-of-all-children-of-a-given-commit) – jthill Jun 15 '14 at 16:15

4 Answers4

19

I think this might do what you want. All commits in all branches, that have A as ancestor:

gitk --all --ancestry-path A..
jsvnm
  • 676
  • 6
  • 10
9

In short:

git log --all BRANCH~1..

In detail, with examples: This is the complete tree of a repository I just created:

$ git log --graph --oneline --decorate --all
* e3972be (HEAD, a) a-6
* 2707d79 a-5
* cdea9a7 a-4
| * 65b716e (c) c-5
| * ebe2a0e c-4
|/  
| * 2ed9abe (b) b-4
|/  
* ace558e (master) 3
* 20db61f 2
* 3923af1 1

Aside from --all, another thing is obvious: master -> HEAD:

$ git log --graph --oneline --decorate master..
* e3972be (HEAD, a) a-6
* 2707d79 a-5
* cdea9a7 a-4

So I tried combining them, and it almost got me what we wanted:

$ git log --graph --oneline --decorate --all master..
* e3972be (HEAD, a) a-6
* 2707d79 a-5
* cdea9a7 a-4
* 65b716e (c) c-5
* ebe2a0e c-4
* 2ed9abe (b) b-4

But unfortunately, this doesn't show the relationship between the branches, since the branch we're asking about was omitted. So we have to use log from the parent of master like so:

$ git log --graph --oneline --decorate --all master~1..
* e3972be (HEAD, a) a-6
* 2707d79 a-5
* cdea9a7 a-4
| * 65b716e (c) c-5
| * ebe2a0e c-4
|/  
| * 2ed9abe (b) b-4
|/  
* ace558e (master) 3

Ta-da! (I don't know if this simply didn't work in the past, but just in case: I'm on git version 1.7.1)

EDIT 2017-11-17 - Thanks to STW for actually showing the problem with this: Independent trees would mess this up. Commits that are entirely independent of master would be included in this output. Starting from a copy of the above repo, this is what my last command would output:

$ git checkout --orphan z
Switched to a new branch 'z'
$ git commit --allow-empty -m'z-1'
[z (root-commit) bc0c0bb] z-1
$ git commit --allow-empty -m'z-2'
[z 1183713] z-2

$ git log --graph --oneline --decorate --all master~1..
* 1183713 (HEAD -> z) z-2
* bc0c0bb z-1
* 6069f73 (a) a-6
* 654d106 a-5
* a218c59 a-4
| * 338432a (c) c-5
| * 2115318 c-4
|/  
| * 43a34dc (b) b-4
|/  
* ce05471 (master) 3

The z branch, being created as an orphan, has no common history with master, so z-1 and z-2 should have been excluded but were not. This is what --ancestry-path is for, I now get. Including it will exclude branch z:

$ git log --graph --oneline --decorate --all --ancestry-path master~1..
* 6069f73 (a) a-6
* 654d106 a-5
* a218c59 a-4
| * 338432a (c) c-5
| * 2115318 c-4
|/  
| * 43a34dc (b) b-4
|/  
* ce05471 (master) 3

For completeness, even given that it already had --ancestry-path, the current top answer does not show the branch relationship correctly because it excludes the commit on master itself:

$ git log --graph --oneline --decorate --all --ancestry-path master..
* 6069f73 (a) a-6
* 654d106 a-5
* a218c59 a-4
* 338432a (c) c-5
* 2115318 c-4
* 43a34dc (b) b-4
Izkata
  • 8,961
  • 2
  • 40
  • 50
  • I tried fiddling with variations of this, but it doesn't quite seem to show what I want [with gitk]. For example, suppose you made an intermediary child branch, and from there made 3 grandchildren branches. What would be the way to show the gitk for just that family of branches, and none of master, or anything else? – Pistos May 13 '14 at 19:55
  • @Pistos So using the above, I get the same output in `gitk` as `log` when I use `gitk --all master..` (as opposed to `git log --all master~1..` - So, to use a branch other than master, try `gitk --all branchname..` (include the two dots) – Izkata May 13 '14 at 20:47
  • @Pistos I also now note that my comment has ended up almost the same as jsvnm's answer, except I don't quite understand `--ancestry-path` and why it would be necessary – Izkata May 14 '14 at 03:46
  • 1
    Doesn't `foo..` mean `foo..HEAD` or such? i.e. isn't that dependent on your current branch? i.e. I want a solution that doesn't depend on the current branch being something in particular. – Pistos May 14 '14 at 14:11
  • @Pistos Yes, but that's why we also use `--all`. If you look at the final example in my answer, despite `HEAD` being at branch `a`, branches `b` and `c` are also visible because they are currently child branches of `master` – Izkata May 14 '14 at 14:45
  • I'm trying this with a local repo, and --all is making it show way too much, with commits that have nothing to do with the one commit/branch that I'm trying to get descendants of. Can you try it with a mature repo with many (hundreds) of commits, and many (20+) branches? – Pistos May 15 '14 at 14:34
  • `--all` shows you all commits in the repository,including completely unrelated commits. – STW Nov 17 '17 at 16:25
  • @STW Yes, and the question asked for all descendants, so `--all` is necessary to include those instead of only doing the path from branch to HEAD. `branch~1..` then removes ancestors and unrelated descendants of those ancestors. – Izkata Nov 17 '17 at 17:33
  • @Izkata -- `branch~1..` appears to exclude *older* commits, but doesn't filter out unrelated commits. http://tpcg.io/K09v2Z – STW Nov 17 '17 at 18:11
  • @STW Ahh, an independent tree. I knew git could do that, but have personally never seen it in the wild. – Izkata Nov 17 '17 at 19:46
  • They don't need to be independent, this command depends on chronology rather than parentage. – STW Nov 17 '17 at 22:46
4

A commit only knows about its parent ( and hence all the way up) but has no clue about its children / descendants. You must use a notation like A..B to find it.

For example if you want to find commits in current branch since the given commit A, you can do something like this:

git rev-list A..
manojlds
  • 290,304
  • 63
  • 469
  • 417
  • 1
    `gitk --all` must work somehow. It seems possible to at least code it to first `gitk --all` and then omit every commit that doesn't have a given commit as its parent. – Pistos Dec 11 '11 at 04:49
0

I was able to do this for all branches using bash/git.

This lists the descendant commits (and the given root):

git_list_all_descendant_hashes() {
    COMMIT=$1
    if [[ ${#COMMIT} -lt 40 ]]; then # short commit hash, need full hash
        COMMIT=`git rev-parse ${COMMIT}`
    fi

    declare -A ALL_HASHES

    while IFS= read -r string; do
        IFS=' ' read -r -a array <<< $string
        key=${array[0]}
        value=${array[@]:1}
        ALL_HASHES[${key}]=$value
    done <<< $(git rev-list --children --all)

    declare -A HASHES # subset of ALL_HASHES that are descendants

    HASHES[${COMMIT}]=1

    added_hashes=1
    while [[ ${added_hashes} -gt 0 ]]; do
        added_hashes=0
        # this loop is inefficient, as it will iterate over all collected hashes each time a hash is found to have new children
        for hash in ${!HASHES[@]}; do
            for child_hash in ${ALL_HASHES[${hash}]}; do
                if [[ ! -v HASHES[$child_hash] ]]; then
                    added_hashes=1
                    HASHES[${child_hash}]=1
                fi
            done
        done
    done

    for hash in ${!HASHES[@]}; do
        echo ${hash}
    done
}

You can invoke it with:

git_descendants() {
    # the --short flag is a hack that'll return an error code if no argument is given, but git_list_all_descendant_hashes actually needs to turn it into a full hash
    COMMIT=`git rev-parse --short $1 2>/dev/null || git rev-parse HEAD`
    git log --graph --oneline --decorate ^${COMMIT}^@ $(git_list_all_descendant_hashes ${COMMIT})
}

If a descendant has multiple parents (i.e. from a merge commit), it'll show its non-root ancestry as well. This is something I was happy with, so I didn't bother figuring out how to get rid of it.

Sean
  • 1
  • 1