如何使用git(或gitk)获取/列出/查看提交的所有后代?

16
如果您使用gitk --all,则可以查看来自所有分支的存储库的所有提交。我想要的是类似于这样的东西,但只包括给定提交的后代。

1
虽然manojlds的第一句话是正确的,但并不是全部真相:可以通过拼凑一些东西来检查每个引用是否在其祖先中具有给定提交,并从这些引用开始显示历史记录。 - Cascabel
2
可能是重复的问题,参见获取给定提交的所有子级列表 - jthill
4个回答

19

我认为这可能是你想要的。所有分支中,具有 A 作为祖先的提交:

gitk --all --ancestry-path A..

9

简而言之:

git log --all BRANCH~1..

详细来说,举个例子:这是我刚创建的仓库的完整树形结构:

$ 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

除了使用--all,另一件很明显的事情就是:master 等同于 HEAD

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

所以我尝试将它们结合起来,这个方法几乎可以得到我们想要的结果:

$ 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

但不幸的是,这并不显示分支之间的关系,因为我们询问的分支被省略了。因此,我们必须像这样从master的父级使用日志:

$ 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!(我不知道以前这是否有效,但以防万一:我正在使用git版本1.7.1)
编辑2017-11-17 - 感谢STW实际展示了这个问题:独立的分支将混淆这个输出。完全独立于master的提交将被包含在此输出中。从上述repo的副本开始,这是我的最后一个命令将产生的输出:
$ 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

“z”分支是以孤儿形式创建的,与“master”没有共同的历史,因此应该排除“z-1”和“z-2”,但未被排除。这就是“--ancestry-path”的作用,我现在明白了。包含它将排除分支“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

为了完整起见,即使已经有了--ancestry-path,当前的最佳答案也没有正确显示分支关系,因为它排除了master上的提交本身:

$ 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

我尝试过对此进行各种调整,但似乎并没有完全展示我想要的效果[使用gitk]。例如,假设你创建了一个中间子分支,然后从那里创建了3个孙子分支。有什么方法可以只显示该分支家族的gitk,而不包括master或其他任何内容呢? - Pistos
@Pistos 所以使用上述方法,当我使用 gitk --all master.. 时,我在 gitk 中得到与 log 相同的输出(而不是 git log --all master~1..)。因此,如果要使用除 master 以外的分支,请尝试 gitk --all branchname..(包括两个点)。 - Izkata
1
foo.. 不是指 foo..HEAD 或类似的吗?也就是说,这取决于你当前所在的分支,我想要一个不依赖于特定分支的解决方案。 - Pistos
我正在尝试使用本地仓库,而--all选项会显示太多内容,其中包括与我想要获取后代的单个提交/分支无关的提交。你能否尝试在一个成熟的仓库中进行测试,该仓库有许多(数百个)提交和许多(20+)分支? - Pistos
@Izkata -- branch~1.. 似乎排除了较旧的提交,但并未过滤无关的提交。http://tpcg.io/K09v2Z - STW
显示剩余6条评论

4

一个提交只知道它的父级(一直到最上层),但是不知道它的子孙后代。你必须使用类似 A..B 的符号来找到它。

例如,如果您想要查找自给定提交 A 以来当前分支中的提交,可以执行以下操作:

git rev-list A..

1
gitk --all 必须以某种方式工作。看起来至少可以编写代码,首先运行gitk --all,然后省略没有特定提交作为其父提交的每个提交。 - Pistos

0

我能够使用bash/git为所有分支完成这个操作。

这将列出后代提交(和给定的根):

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
}

您可以使用以下方式调用:

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

如果后代有多个父节点(即来自合并提交),它也会显示其非根祖先。我对此感到满意,所以没有费心找出如何摆脱它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接