如何在git中找到下一个提交?(参考儿童/儿童)

Sch*_*ern 218 git version-control

ref^是指之前ref的提交,之后 获取提交ref怎么样?

例如,如果我git checkout 12345如何检查下一次提交?

谢谢.

PS是的,git是一个DAG节点指针结构树,无论如何. 如何在此之后找到提交?

小智 172

要列出所有提交,从当前提交,然后是其子代,依此类推 - 基本上是标准的git log,但是以其他方式进行,请使用类似的东西

git log --reverse --ancestry-path 894e8b4e93d8f3^..master
Run Code Online (Sandbox Code Playgroud)

其中894e8b4e93d8f3是您要显示的第一个提交.

  • 只有当`master`位于当前提交的祖先路径上时,这才有效.请参阅[我的回答](http://stackoverflow.com/a/39566420/5353461)的第二个代码片段,以获得适用于所有情况的解决方案. (5认同)
  • 对于原始问题中的具体情况,只需将`HEAD ^`替换为`894e8b4e93d8f3 ^`. (2认同)
  • 也许,添加--oneline是一个更好的简短输出结果. (2认同)

Von*_*onC 34

哈德森(现詹金斯)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
Run Code Online (Sandbox Code Playgroud)

该线程所示,在基于由DAG(有向无环图)表示的历史的VCS中,不存在"一个父"或"一个子".

        C1 -> C2 -> C3
      /               \
A -> B                  E -> F
      \               /
        D1 -> D2 ----/
Run Code Online (Sandbox Code Playgroud)

提交的顺序由"拓扑订单"或"日期订单"完成(参见GitPro书)

但是从Git1.6.0开始,你可以列出提交的子代.

git rev-list --children
git log --children
Run Code Online (Sandbox Code Playgroud)

注意:对于父提交,您遇到相同的问题,其后缀^为revision参数,表示该提交对象的第一个父项.^<n><n>父母(即rev^ 相当于rev^1).

如果你在分支机构foo并发出" git merge bar",那么foo将是第一个父母.
即:第一个父项是合并时所在的分支,第二个是您合并的分支上的提交.

  • `git rev-list --children`肯定看起来像我想要的,但它不是DWIM.它似乎列出了所有父母及其子女.我想我可以列出所有内容并通过它们解析......嗯,但它的东西. (5认同)
  • `git-children-of` 的唯一问题是它使用 [git describe](https://git-scm.com/docs/git-describe) 尝试将 SHA 格式化为人类可读的格式,这可能会失败带有@TomHale 的错误,并给出像 `v1.0.4-14-g2414721` 这样的结果,如果您期望使用 SHA,这些结果会令人困惑。用一个简单的 `echo` 替换它使它成为一个很好的工具,谢谢! (2认同)

whi*_*cko 18

我发现的是

git rev-list --ancestry-path commit1..commit2
Run Code Online (Sandbox Code Playgroud)

我将其设置commit1为当前提交和commit2当前头部.这将返回一个列表,其中列出了构建commit1和之间路径的所有提交commit2.

输出的最后一行是commit1的子节点(在commit2的路径上).

  • 所以只需添加`| 尾巴-1`让孩子. (3认同)

von*_*app 8

我明白你的意思.拥有丰富的语法来进行之前的提交是令人沮丧的,但没有一个可以转到下一个提交.在复杂的历史中,"下一次提交是什么"的问题变得相当困难,但是在复杂的合并中,同样的硬度也出现在"先前的"提交中.在简单的情况下,在具有线性历史的单个分支内部(即使只是在本地进行一些有限数量的提交),前进和后退也很有意义.

然而,真正的问题在于没有引用子提交,它只是一个向后链接的列表.查找子提交需要搜索,这不是太糟糕,但可能不是git想要放入refspec逻辑的东西.

无论如何,我遇到了这个问题,因为我只是想在历史上一步提出,做一次测试,有时你必须前进而不是后退.好吧,有一些想法,我提出了这个解决方案:

在您所在的位置之前选择一个提交.这可能是一个分支头.如果你在分支~10,那么"git checkout branch~9"然后"git checkout branch~8"以获得下一个,然后"git checkout branch~7"等等.

如果需要,在脚本中减少数字应该非常简单.比解析git rev-list容易得多.


Tom*_*ale 7

两个实际答案:

一个小孩

根据@ Michael的回答,我child在我的文章中修改了别名.gitconfig.

它在默认情况下按预期工作,也是多功能的.

# 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' -"
Run Code Online (Sandbox Code Playgroud)

它默认为给孩子HEAD(除非给出了另一个commit-ish参数),方法是将祖先跟随当前分支的一步(除非给出另一个commit-ish作为第二个参数).

如果您想要短哈希表单,请使用%h而不是%H.

多个孩子

使用分离的HEAD(没有分支)或者让所有孩子都不管分支:

# 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' -"
Run Code Online (Sandbox Code Playgroud)

更改$1$*以打印所有孩子.

您还可以更改--all为commit-ish以仅显示作为该提交的祖先的子项 - 换句话说,仅显示"在给定提交的方向上"的子项.这可以帮助您将输出从许多孩子缩小到只有一个.


Phi*_*ler 6

没有独特的"下一次提交".因为Git中的历史是DAG而不是行,所以许多提交可以具有公共父(分支),并且提交可以具有多个父(合并).

如果你有一个特定的分支,你可以查看它的日志,看看哪个提交列出了当前的一个作为其父项.

  • 通过这种逻辑,也没有"先前提交",但是有足够的语法来获取父级. (33认同)
  • @Schwern:也没有"先前提交"; `<rev> ^`是"parent commit"(合并提交的'first parent'). (7认同)

Mat*_*nry 6

如果您没有特定的“目标”提交,但想查看可能在任何分支上的子提交,则可以使用以下命令:

git rev-list --children --all | grep ^${COMMIT}
Run Code Online (Sandbox Code Playgroud)

如果要查看所有子代和孙子代,则必须rev-list --children递归使用,如下所示:

git rev-list --children --all | \
egrep ^\($(git rev-list --children --all | \
           grep ^${COMMIT} | \
           sed 's/ /|/g')\)
Run Code Online (Sandbox Code Playgroud)

仅提供孙子代的版本将使用更复杂的sed和/或cut。)

最后,您可以将其输入log --graph命令以查看树结构,如下所示:

git log --graph --oneline --decorate \
\^${COMMIT}^@ \
$(git rev-list --children --all | \
  egrep ^\($(git rev-list --children --all | \
             grep ^${COMMIT} | \
             sed 's/ /|/g')\))
Run Code Online (Sandbox Code Playgroud)

注意:以上命令均假定您已将shell变量设置为${COMMIT}您感兴趣的子提交的某个引用(分支,标签,sha1)。

  • 这回答了我不知道如何为谷歌和stackoverflow制定公式的问题,但是试图问这个问题。感谢您主动认识到这一需求 (2认同)

tor*_*rek 6

Git 的所有内部箭头都是单向的,指向后方。因此,前进没有简短的方便语法:这是不可能的。

可能的“反箭头移动”,但做到这一点是令人惊讶的方式,如果你以前没有见过它,然后明显之后。假设我们有:

A <-B <-C <-D <-E   <-- last
        ^
        |
         \--------- middle
Run Code Online (Sandbox Code Playgroud)

Usingmiddle~2跟随箭头两次从Cback 到A。那么我们如何从CD?答案是:我们从 开始E,使用名称last,然后向后工作,直到到达middle记录我们沿途访问的点。然后我们只要朝着我们想要的方向last移动:向 移动一步D,或向移动两步E

当我们有分支时,这一点尤其重要:

          D--E   <-- feature1
         /
...--B--C   <-- master
         \
          F--G   <-- feature2
Run Code Online (Sandbox Code Playgroud)

哪个提交是一步之后C?在您添加问题之前,没有正确答案:在特征___的方向上(填空)。

要枚举C(排除C) 本身和例如之间的提交G,我们使用:

git rev-list --topo-order --ancestry-path master..feature2
Run Code Online (Sandbox Code Playgroud)

--topo-order确保即使存在复杂的分支和合并,提交也会以拓扑排序的顺序出现。仅当链不是线性时才需要这样做。该--ancestry-path约束的手段,当我们从反向工作feature2,我们只是有名单提交承诺C作为自己的祖先之一。也就是说,如果图——或者它的相关部分——实际上看起来像这样:

A--B--C   <-- master
 \     \
  \     F--G--J   <-- feature2
   \         /
    H-------I   <-- feature3
Run Code Online (Sandbox Code Playgroud)

然后表单的简单请求以某种顺序feature2..master枚举 commits J, Gand I, and Fand H。用--ancestry-path我们打倒HI:他们不是后裔C,只有后裔A。随着--topo-order我们确保实际的枚举顺序J,然后G,然后F

git rev-list命令将这些哈希 ID 溢出到其标准输出中,每行一个。为了向 的方向前进一步feature2,那么,我们只需要最后一行。

这是可能的(和诱人的和有用的)添加--reverse,使git rev-list打印出提交相反的顺序生成它们之后。这确实有效,但如果您在这样的管道中使用它:

git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1
Run Code Online (Sandbox Code Playgroud)

为了获得“在 id2 方向上的下一次提交”,并且有很长的提交列表,该git rev-list命令在尝试写入时可能会得到一个损坏的管道,该管道head已停止读取其输入并退出。由于断管错误通常会被 shell 忽略,所以这主要是有效的。只要确保它们在您的使用中被忽略。

添加-n 1git rev-list命令中也很诱人,与--reverse. 不要这样做!这使得git rev-list在后退一步停止,然后反转访问的(单项)提交列表。所以这只是<id2>每次都会产生。

重要的旁注

注意带有“钻石”或“苯环”的图形片段:

       I--J
      /    \
...--H      M--...  <-- last
      \    /
       K--L
Run Code Online (Sandbox Code Playgroud)

移动一个承诺“前进”从Hlast将让你无论是 I K。你对此无能为力:两次提交都是向前迈出的一步!如果您然后从结果提交开始并再走一步,您现在将致力于您开始的任何路径。

解决这个问题的方法是避免一次移动一步并被锁定在依赖路径的链中。相反,如果您打算访问整个祖先路径链,在做任何其他事情之前,请制作链中所有提交完整列表:

git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits
Run Code Online (Sandbox Code Playgroud)

然后,一次访问此列表中的每个提交,您将获得整个链。该--topo-order会确保你打I-和-J的顺序,和K-和-L的顺序(虽然有没有简单的方法来预测你是否会在KL对之前或之后做IJ对)。


M K*_*M K 5

我尝试了许多不同的解决方案,但没有一个对我有用。不得不拿出我自己的。

查找下一个提交

function n() {
    git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}
Run Code Online (Sandbox Code Playgroud)

查找先前的提交

function p() {
    git checkout HEAD^1
}
Run Code Online (Sandbox Code Playgroud)