【问题标题】:How can I get/list/see all the descendants of a commit with git (or gitk)?如何使用 git(或 gitk)获取/列出/查看提交的所有后代?
【发布时间】:2012-01-17 16:46:58
【问题描述】:

如果您使用gitk --all,您可以从所有分支查看您的 repo 的所有提交。我想要类似的东西,除了给定提交的后代。

【问题讨论】:

  • 虽然 manojlds 的第一句话是正确的,但这并不是全部事实:可以将检查每个 ref 是否在其祖先中具有给定提交,然后显示从那些参考。

标签: git parent commit gitk descendant


【解决方案1】:

我能够使用 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})
}

如果后代有多个父母(即来自合并提交),它也会显示其非根祖先。这是我很满意的事情,所以我没有费心去想如何摆脱它。

【讨论】:

    【解决方案2】:

    简而言之:

    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
    

    哒哒! (我不知道这在过去是否根本不起作用,但以防万一:我使用的是 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-1z-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
    

    【讨论】:

    • 我试着摆弄它的变体,但它似乎并没有显示出我想要的 [with gitk]。例如,假设您创建了一个中间子分支,并从那里创建了 3 个孙子分支。只显示那个分支家族的 gitk 的方式是什么,而不是 master 或其他任何东西?
    • @Pistos 所以使用上面的方法,当我使用log 时,我在gitk 中得到与log 相同的输出(而不是git log --all master~1.. - 所以,使用除大师,试试gitk --all branchname..(包括两个点)
    • @Pistos 我现在还注意到,我的评论最终与 jsvnm 的答案几乎相同,除了我不太明白 --ancestry-path 以及为什么有必要
    • foo.. 不是指foo..HEAD 之类的吗?也就是说,这不取决于您当前的分支吗?即我想要一个不依赖于当前分支的解决方案。
    • @Pistos 是的,但这就是我们也使用--all 的原因。如果您查看我回答中的最后一个示例,尽管HEAD 位于分支a,但分支bc 也是可见的,因为它们当前是master 的子分支
    【解决方案3】:

    我认为这可能会满足您的需求。以 A 为祖先的所有分支中的所有提交:

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

    【讨论】:

      【解决方案4】:

      提交只知道它的父级(因此一直向上),但不知道它的子级/后代。您必须使用 A..B 之类的符号才能找到它。

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

      git rev-list A..
      

      【讨论】:

      • gitk --all 必须以某种方式工作。似乎至少可以先将其编码为gitk --all,然后省略每个没有将给定提交作为其父提交的提交。
      猜你喜欢
      • 1970-01-01
      • 2012-02-21
      • 2013-03-13
      • 2023-03-15
      • 2011-06-14
      • 2014-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多