【问题标题】:Referencing the child of a commit in Git在 Git 中引用提交的子项
【发布时间】:2010-12-18 05:21:55
【问题描述】:

如果要将HEAD 移动到当前HEAD 的父级,这很简单:

git reset --hard HEAD^

但是有没有什么简单的方法可以做与这个操作完全相反的操作,即设置head为当前head的第一个子commit?

现在,我使用 gitk 作为解决方法(alt-tab、向上箭头、alt-tab、中键),但我想要一个更优雅的解决方案,也可以在 gitk 不可用时使用.

【问题讨论】:

标签: git


【解决方案1】:

仅移动 HEAD(如要求 - 这不会更新索引或工作树),请使用:

git reset --soft $(git child)

您需要使用下面列出的配置。

说明

基于@Michael's answer,我在.gitconfig 中破解了child 别名。

它在默认情况下按预期工作,而且用途广泛。

# Get the child commit of the current commit.
# Use $1 instead of 'HEAD' if given. Use $2 instead of curent branch if given.
child = "!bash -c 'git log --format=%H --reverse --ancestry-path ${1:-HEAD}..${2:\"$(git rev-parse --abbrev-ref HEAD)\"} | head -1' -"

它默认给 HEAD 的孩子(除非给出了另一个 commit-ish 参数),方法是按照祖先向当前分支的尖端迈出一步(除非另一个 commit-ish 作为第二个参数给出)。

如果您想要短哈希形式,请使用 %h 而不是 %H。

有了一个分离的头,没有分支,但是仍然可以使用这个别名来获得第一个孩子:

# For the current (or specified) commit-ish, get the all children, print the first child 
children = "!bash -c 'c=${1:-HEAD}; set -- $(git rev-list --all --not \"$c\"^@ --children | grep $(git rev-parse \"$c\") ); shift; echo $1' -"

将$1 更改为$* 以打印所有子项

【讨论】:

  • 如果您在错误的分支上,这将不起作用。有什么方法可以概括这一点?
  • 奇怪;如果您不在同一个分支上,您的 children 别名确实有效,但无法为具有多个子提交的提交打印多个提交。
【解决方案2】:

很可能不是最快的解决方案,但它可以满足我的需要:

#!/bin/bash REV=$1 如果 [[ -z "$REV" ]];然后 echo "用法:git-get-child []" 出口 菲 HASH=$(git rev-parse $REV) NUM=$2 如果 [[ -z "$NUM" ]];然后 数字=1 菲 git rev-list --all --parents | grep "$HASH" | sed -n "${NUM}s/\([^ ]*\) .*$/\\1/p"

git rev-list --all --parents 完全符合我的需要:它遍历所有可访问的提交,并为每个提交打印以下行:

SHA1_commit SHA1_parent1 SHA1_parent2 等

grep 表达式中的空格可确保仅找到相关 SHA1 为父级的那些行。然后我们得到第n个孩子的第n行,得到孩子的SHA1。

【讨论】:

  • 我相信它应该是 git rev-list --all --parents | grep -m 1 -B $(($NUM-1)) " $HASH" | head -1 | sed 's/ .*//' 否则它在 $NUM != 1 时不太有效
  • 请参阅下面的潜在重要performance improvement
  • 请注意,这 not 会发现悬空提交,即无法从任何分支访问提交。 git reset --hard HEAD^ 通常会使 HEAD 提交无法访问(除非它也在分支上)。所以这可能找不到所有提交。要查找所有提交,必须使用 git rev-list --walk-reflog 和/或 git fsck --unreachable。
  • 应该是 git rev-parse 而不是 git-rev-parse -- 不允许我编辑帖子。
【解决方案3】:

使用git rev-list --all 的above method 考虑了所有可用的提交,这可能很多而且通常没有必要。如果可以从 some 分支访问有趣的子提交,则可以减少对子提交感兴趣的脚本需要处理的提交数量:

branches=$(git branch --contains $commit| grep -v '[*] ('| sed -e 's+^..++')

将确定 $commit 是其祖先的分支集。 使用现代 git,至少版本 2.21+,这应该做同样的事情,而不需要 sed(未经测试):

branches=$(git branch --format='%(refname:short)' --contains $commit| grep -v '[*] (')

使用这个集合,git rev-list --parents ^$commit $branches 应该准确地产生 $commit 和它是其祖先的所有分支头之间的所有父子关系的集合。

【讨论】:

  • 这对我有用,但是我确实必须过滤掉“*(无分支)”行,因为那时我在一个分离的分支上。对于我的存储库,这大约是 10 倍,0.13 秒对 1.4 秒。
  • 考虑用--format='%(refname:short) 简化git branch 的输出(删除前导空格和*)
  • zsh(但不是 bash)在第二个命令行上遇到 $branches 问题,因为换行符的解释方式。如果将分支存储为数组,则 bash 和 zsh 都可以正常工作:branches=($(git branch --contains ... ))
  • @Paul Wagland:很好的收获。添加了grep 调用以删除此类行以及* (HEAD detached at $SHA1) 之类的行。 @Joshua Goldberg:添加了 sed 摆脱领先的 * ,如果有的话。 --format 在我的 LTS 系统上不可用,但很高兴知道。添加了您对那些有幸拥有它的人的建议。关于 zsh 与 bash,我更喜欢将我的脚本限制在 POSIX sh 所提供的范围内,感谢您为 zsh 用户提出解决方案。并感谢您将引用链接到“以上”答案。
【解决方案4】:

部分基于Paul Wagland's answer,部分基于his source,我是using the following:

git log --ancestry-path --format=%H ${commit}..master | tail -1

我发现 Paul 的回答给了我旧提交的错误输出(可能是由于合并?),主要区别是 --ancestry-path 标志。

【讨论】:

  • 使用--reverse 并使用head -1 进行适度加速
  • 查看my answer 以获得更通用的解决方案
【解决方案5】:

这篇文章 (http://www.jayway.com/2015/03/30/using-git-commits-to-drive-a-live-coding-session/#comment-282667) 展示了一个巧妙的方法,如果你可以在提交堆栈的末尾创建一个定义明确的标签。本质上 git config --global alias.next '!git checkout `git rev-list HEAD..demo-end | tail -1`' 其中“demo-end”是最后一个标签。

【讨论】:

    【解决方案6】:

    您可以使用 Hudson(现为 Jenkins)Kohsuke Kawaguchi(2013 年 11 月)的创建者的要点:
    kohsuke / git-children-of:

    给定一个提交,找到该提交的直接子节点。

    #!/bin/bash -e
    # given a commit, find immediate children of that commit.
    for arg in "$@"; do
      for commit in $(git rev-parse $arg^0); do
        for child in $(git log --format='%H %P' --all | grep -F " $commit" | cut -f1 -d' '); do
          git describe $child
        done
      done
    done
    

    将该脚本放在您的$PATH 引用的文件夹中,然后输入:

    git children-of <a-commit>
    

    【讨论】:

      【解决方案7】:

      根据How do I find the next commit in git? 中给出的答案,我有另一个适合我的解决方案。

      假设你想在“master”分支上找到下一个修订,那么你可以这样做:

      git log --reverse ${commit}..master | sed 's/commit //; q'
      

      这也假设有 一个 下一个修订版,但无论如何这都是问题所假设的。

      【讨论】:

        【解决方案8】:

        这取决于您的要求。在无限数量的分支中可能有无限数量的当前头部的子节点,一些是本地的,一些是远程的,还有许多已被重新定位并在您的存储库中,但不是您打算发布的历史记录的一部分。

        对于一个简单的案例,如果你刚刚重置为HEAD^,你可以将你刚刚扔掉的孩子找回为HEAD@{1}。

        【讨论】:

          【解决方案9】:

          严格来说不可能给出一个好的答案——因为 git 是分布式的,所以您询问的提交的大多数子项可能位于您本地计算机上没有的存储库中!这当然是一个愚蠢的答案,但需要考虑一些事情。 Git 很少实现它不能正确实现的操作。

          【讨论】:

          • 当然我只需要当前存储库中的孩子。
          【解决方案10】:

          您可以使用gitk ... 因为可以有多个孩子,所以可能没有像HEAD^ 这样的简单方法。

          如果您想撤消整个操作,您也可以使用 reflog。使用git reflog 查找提交的指针,您可以将其用于reset 命令。见here。

          【讨论】:

            猜你喜欢
            • 2014-11-04
            • 2015-08-17
            • 1970-01-01
            • 2011-04-06
            • 1970-01-01
            • 2013-12-15
            • 2012-06-06
            • 1970-01-01
            相关资源
            最近更新 更多