ref^指的是在ref之前的提交,那么在ref之后的提交呢?
例如,如果我签出12345,我如何签出下一次提交?
是的,Git是一个DAG节点指针结构树。我如何找到这个之后的提交?
ref^指的是在ref之前的提交,那么在ref之后的提交呢?
例如,如果我签出12345,我如何签出下一次提交?
是的,Git是一个DAG节点指针结构树。我如何找到这个之后的提交?
当前回答
我尝试过许多不同的解决方案,但没有一个对我有效。我得自己想办法。
查找下一个提交
function n() {
git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}
查找之前的提交
function p() {
git checkout HEAD^1
}
其他回答
Git的所有内部箭头都是单向的,向后指向。因此,没有简单方便的语法来向前推进:这是不可能的。
“逆箭头移动”是可能的,但如果你以前没有见过,那么这样做的方式是令人惊讶的,然后是显而易见的。假设我们有:
A <-B <-C <-D <-E <-- last
^
|
\--------- middle
使用middle~2跟随箭头两次从C回到a,那么我们如何从C移动到D呢?答案是:我们从E开始,最后使用名字,向后工作直到中间,记录我们一路访问的点。然后我们只需要在最后一个方向上移动我们想要的距离:向D移动一步,或向E移动两步。
当我们有分支时,这一点尤其重要:
D--E <-- feature1
/
...--B--C <-- master
\
F--G <-- feature2
哪个提交比C晚一步?没有正确答案,除非你在问题上加上:在特征___的方向上(填空)。
为了枚举C(不包括C)自身和G之间的提交,我们使用:
git rev-list --topo-order --ancestry-path master..feature2
——topo-order确保即使存在复杂的分支和合并,提交也会按照拓扑排序。只有当链不是线性的时候才需要这样做。——ancestry-path约束意味着当我们从feature2向后工作时,我们只列出那些将commit C作为自己祖先之一的提交。也就是说,如果这个图——或者它的相关部分——实际上是这样的:
A--B--C <-- master
\ \
\ F--G--J <-- feature2
\ /
H-------I <-- feature3
然后对表单feature2进行简单的请求。master按一定顺序枚举提交J、G和I以及F和H。使用——ancestor -path,我们剔除H和I:它们不是C的后代,只是a的后代。使用——topo-order,我们确保实际的枚举顺序是J,然后是G,然后是F。
git rev-list命令在其标准输出中溢出这些哈希id,每行一个。为了向feature2的方向前进一步,我们只需要最后一行。
可以使用add——reverse(这很诱人,也很有用),这样git rev-list就可以在生成提交后以相反的顺序打印提交。这确实有用,但如果你在这样的管道中使用它:
git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1
为了获得“id2方向的下一个提交”,并且有一个非常长的提交列表,git rev-list命令在试图写入head时可能会得到一个破裂的管道,因为head已经停止读取其输入并退出。由于shell通常会忽略管道破裂错误,因此这通常是可行的。只是要确保在使用时忽略它们。
在git rev-list命令中添加-n 1和——reverse也很诱人。不要这样做!这使得git rev-list在后退一步后停止,然后反转所访问的(一项)提交列表。所以每次都产生<id2>。
重要的边注
注意带有“菱形”或“苯环”的图形片段:
I--J
/ \
...--H M--... <-- last
\ /
K--L
将一次提交从H“向前”移到最后会得到I或k。对此你无能为力:两次提交都向前一步!如果您从结果的提交开始,并执行另一个步骤,那么您现在已经提交到您开始的路径。
解决这个问题的方法是避免一次只移动一步,从而被锁定在路径依赖链中。相反,如果你计划访问一个完整的祖先路径链,在做其他事情之前,做一个链中所有提交的完整列表:
git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits
然后,访问这个列表中的每个提交,一次一个,您将得到整个链。topo-order将确保你按这个顺序命中i -和j,以及按这个顺序命中k -和l(尽管没有简单的方法来预测你会在K-L对之前还是之后命中I-J对)。
这显示了当前HEAD的所有子元素的列表。
git rev-list --parents --all | awk -v h="$(git rev-parse HEAD)" 'index($0,h)>1{print$1}'
它只打印所有以HEAD为父文件的提交。
你可以稍微加快一下速度,把^HEAD放在|之前,那么HEAD的祖先就不会被搜索。
如果你想打印另一个提交或分支的子文件,只需把它放在HEAD的位置(在更快的版本中,在两个HEAD位置)。
我在~/.gitconfig中有这个别名
first-child = "!f() { git log --reverse --ancestry-path --pretty=%H $1..${2:-HEAD} | head -1; }; f"
每次提交都存储一个指向其父(在merge(标准)提交的情况下为父)的指针。
因此,没有任何方法可以从父对象指向子提交(如果有的话)。
如果你心中没有特定的“目的地”提交,而是想查看可能在任何分支上的子提交,你可以使用这个命令:
git rev-list --children --all | grep ^${COMMIT}
如果你想看到所有的子结点和孙子结点,你必须递归地使用rev-list——children,如下所示:
git rev-list --children --all | \
egrep ^\($(git rev-list --children --all | \
grep ^${COMMIT} | \
sed 's/ /|/g')\)
(只提供孙辈的版本将使用更复杂的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')\))
注意:上面的命令都假设你已经将shell变量${COMMIT}设置为你感兴趣的子提交的某个引用(分支,标签,sha1)。