如何在 git 中找到下一个提交?(参考的孩子/孩子)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2263674/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 07:51:28  来源:igfitidea点击:

How do I find the next commit in git? (child/children of ref)

gitversion-control

提问by Schwern

ref^refers to the commit before ref, what about getting the commit afterref?

ref^指的是之前的提交ref,那么之后的提交ref呢?

For example, if I git checkout 12345how do I check out the next commit?

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

Thanks.

谢谢。

PS Yes, git's a DAG node pointer struct tree whatever. How do I find the commit after this one?

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

采纳答案by Tim Hunt

To list all the commits, starting from the current one, and then its child, and so on - basically standard git log, but going the other way in time, use something like

要列出所有提交,从当前提交开始,然后是它的子提交,依此类推 - 基本上是标准的 git log,但要及时使用其他方式,使用类似

git log --reverse --ancestry-path 894e8b4e93d8f3^..master

where 894e8b4e93d8f3 is the first commit you want to show.

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

回答by VonC

The creator for Hudson (now Jenkins) Kohsuke Kawaguchijust published (November 2013):
kohsuke / git-children-of:

Hudson(现为 Jenkins)Kohsuke Kawaguchi的创建者刚刚发布(2013 年 11 月):
kohsuke / git-children-of

Given a commit, find immediate children of that commit.

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

#!/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


As illustrated by this thread, in a VCS based on history represented by a DAG (Directed Acyclic Graph), there is not "one parent" or "one child".

该线程所示,在基于由DAG(有向无环图)表示的历史记录的 VCS 中,不存在“一个父级”或“一个子级”。

        C1 -> C2 -> C3
      /               \
A -> B                  E -> F
      \               /
        D1 -> D2 ----/

The ordering of commits is done by "topo-order" or "date-order" (see GitPro book)

提交的顺序由“topo-order”或“date-order”完成(参见GitPro book

But since Git1.6.0, you can list the children of a commit.

但是从Git1.6.0 开始,您可以列出提交的子项。

git rev-list --children
git log --children


Note: for parent commits, you have the same issue, with the suffix ^to a revision parameter meaning the firstparent of that commit object. ^<n>means the <n>th parent (i.e. rev^is equivalent to rev^1).

注意:对于parent commits,您有同样的问题,^修订参数的后缀表示该提交对象的第一个父级。^<n>表示第<n>th 个父级(即rev^相当于rev^1)。

If you are on branch fooand issue "git merge bar" then foowill be the first parent.
I.e: The first parent is the branch you were on when you merged, and the second is the commit on the branch that you merged in.

如果您在分支上foo并发出“ git merge bar”,foo则将是第一个父级。
即:第一个父级是您合并时所在的分支,第二个是您合并到的分支上的提交。

回答by white_gecko

what I found is

我发现的是

git rev-list --ancestry-path commit1..commit2

where I set commit1as the current commit and commit2to the current head. This returns me a list of all commits which build a path between commit1and commit2.

我设置commit1为当前提交和commit2当前头部。这会返回一个在commit1和之间构建路径的所有提交的列表commit2

The last line of the output is the child of commit1 (on the path to commit2).

输出的最后一行是 commit1 的子项(在 commit2 的路径上)。

回答by vontrapp

I know what you mean. It's frustrating to have plentiful syntax for going to previous commits, but none to go to the next ones. In a complex history, the problem of "what is the next commit" becomes rather hard, but then in complex merging the same hardness emerges with 'previous' commits as well. In the simple case, inside a single branch with a linear history (even just locally for some limited number of commits) it would be nice and make sense to go forward and backward.

我明白你的意思。有很多语法可以转到以前的提交,但没有转到下一个提交,这令人沮丧。在复杂的历史中,“下一次提交是什么”的问题变得相当困难,但是在复杂的合并中,同样的困难也会出现在“上一次”提交中。在简单的情况下,在具有线性历史记录的单个分支内(即使只是在本地进行一些有限数量的提交),前进和后退会很好并且有意义。

The real problem with this, however, is that the children commits are not referenced, it's a backwards-linked list only. Finding the child commit takes a search, which isn't too bad, but probably not something git wants to put into the refspec logic.

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

At any rate, I came upon this question because I simply want to step forward in the history one commit at a time, doing tests, and sometimes you have to step forward and not backward. Well, with some more thought I came up with this solution:

无论如何,我之所以遇到这个问题,是因为我只是想在一次提交的历史中前进,做测试,有时您必须前进而不是后退。好吧,经过更多思考,我想出了这个解决方案:

Pick a commit ahead of where you're at. This could probably be a branch head. If you're at branch~10, then "git checkout branch~9" then "git checkout branch~8" to get the next after that, then "git checkout branch~7" and so on.

在你所处的位置之前选择一个提交。这可能是一个分支负责人。如果你在分支~10,然后“git checkout branch~9”然后“git checkout branch~8”得到下一个,然后是“git checkout branch~7”等等。

Decrementing the number should be really easy in a script if you need it. A lot easier than parsing git rev-list.

如果需要,在脚本中减少数字应该很容易。比解析 git rev-list 容易得多。

回答by Tom Hale

Two practical answers:

两个实用的答案:

One Child

一个小孩

Based on @Michael's answer, I hacked up the childalias in my .gitconfig.

根据@Michael 的回答,我child在我的.gitconfig.

It works as expected in the default case, and is also versatile.

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

# Get the child commit of the current commit.
# Use  instead of 'HEAD' if given. Use  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' -"

It defaults to giving the child of HEAD (unless another commit-ish argument is given) by following the ancestry one step toward the tip of the current branch (unless another commit-ish is given as second argument).

它默认为 HEAD 的子级(除非给出另一个 commit-ish 参数)通过跟随祖先朝着当前分支的尖端一步(除非另一个 commit-ish 作为第二个参数给出)。

Use %hinstead of %Hif you want the short hash form.

如果您想要短散列形式,请使用%h代替%H

Multiple children

多个孩子

With a detached HEAD (there is no branch) or to get all children regardless of branches:

使用分离的 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 ' -"

Change the $1to $*to print all the children.

$1to更改$*为打印所有子项。

You can also change --allto a commit-ish to display only the children which are ancestors of that commit—in other words, to display only the children “in the direction of” the given commit. This may help you narrow the output down from many children to just one.

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

回答by Phil Miller

There is no unique "next commit". Because history in Git is a DAG, and not a line, many commits can have a common parent (branches), and commits can have more than one parent (merges).

没有唯一的“下一次提交”。因为 Git 中的历史是一个 DAG,而不是一条线,所以许多提交可以有一个共同的父级(分支),并且提交可以有多个父级(合并)。

If you have a particular branch in mind, you can look at its log and see what commit lists the present one as its parent.

如果您有一个特定的分支,您可以查看它的日志并查看哪些提交将当前分支列为其父分支。

回答by M K

I've tried many different solutions and none worked for me. Had to come up with my own.

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

find next commit

找到下一个提交

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

find previous commit

找到以前的提交

function p() {
    git checkout HEAD^1
}

回答by Matt McHenry

In the case where you don't have a particular "destination" commit in mind, but instead want to see child commits that might be on anybranch, you can use this command:

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

git rev-list --children --all | grep ^${COMMIT}

If you want to see all children and grand-children, you have to use rev-list --childrenrecursively, like so:

如果你想看到所有的 children和 grand-children,你必须rev-list --children递归地使用,像这样:

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

(The version that gives onlygrand-children would use a more complex sedand/or cut.)

仅提供孙辈的版本将使用更复杂的sed和/或cut.)

Finally, you can feed that into a log --graphcommand to see the tree structure, like so:

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

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

Note: the above commands all assume that you've set the shell variable ${COMMIT}to some reference (branch, tag, sha1) of the commit whose children you're interested in.

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

回答by torek

(This started as an answer to a duplicate question. I've done a bit of light editing to clean it up.)

(这开始是对重复问题的回答。我做了一些轻微的编辑来清理它。)

All of Git's internal arrows are one-way, pointing backwards. There is therefore no short convenient syntax for moving forwards: it's just not possible.

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

It ispossible to "move against the arrows", but the way to do it is surprising if you haven't seen it before, and then obvious afterward. Let's say we have:

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

A <-B <-C <-D <-E   <-- last
        ^
        |
         \--------- middle

Using middle~2follows the arrows twice from Cback to A. So how do we move from Cto D? The answer is: we start at E, using the name last, and work backwards until we get to middle, recording the points we visit along the way. Then we just move as far as we want in the direction of last: move one step to D, or two to E.

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

This is particularly important when we have branches:

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

          D--E   <-- feature1
         /
...--B--C   <-- master
         \
          F--G   <-- feature2

Which commit is one step after C? There's no correct answer until you add to the question: in the direction of feature___(fill in the blank).

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

To enumerate the commits between C(excluding C) itself and, say, G, we use:

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

git rev-list --topo-order --ancestry-path master..feature2

The --topo-ordermakes sure that even in the presence of complex branching-and-merging, the commits come out in topologically-sorted order. This is only required if the chain isn't linear. The --ancestry-pathconstraint means that when we work backwards from feature2, we only list commits that have commit Cas one of their own ancestors. That is, if the graph—or the relevant chunk of it anyway—actually looks like this:

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

A--B--C   <-- master
 \     \
  \     F--G--J   <-- feature2
   \         /
    H-------I   <-- feature3

a simple request of the form feature2..masterenumerates commits J, Gand I, and Fand Hin some order. With --ancestry-pathwe knock out Hand I: they are not descendants of C, only of A. With --topo-orderwe make sure that the actual enumeration order is J, then G, then F.

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

The git rev-listcommand spills these hash IDs out on its standard output, one per line. To move one step forward in the direction of feature2, then, we just want the lastline.

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

It's possible (and tempting and can be useful) to add --reverseso that git rev-listprints the commits in reversed order after generating them. This does work, but if you use it in a pipeline like this:

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

git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1

to just get the "next commit in the direction of id2", and there is a very long list of commits, the git rev-listcommand can get a broken pipe when it tries to write to headwhich has stopped reading its input and exited. Since broken-pipe errors are normally ignored by the shell, this mostly works. Just make sure they're ignored in yourusage.

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

It's also tempting to add -n 1to the git rev-listcommand, along with --reverse. Don't do it! That makes git rev-liststop after walking one step back, and then reverse the (one-entry) list of commits visited. So this just produces <id2>every time.

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

Important side note

重要的旁注

Note that with "diamond" or "benzene ring" graph fragments:

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

       I--J
      /    \
...--H      M--...  <-- last
      \    /
       K--L

moving one commit "forward" from Htowards lastwill get you eitherIorK. There is nothing you can do about that: both commits are one step forward! If you then start from the resulting commit and go another step, you're now committed to whichever path you started on.

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

The cure for this is to avoid moving one step at a time and getting locked into path-dependent chains. Instead, if you plan to visit an entire ancestry-path chain, before doing anything else, make a complete list of all commits in the chain:

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

git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits

Then, visit each commit in this list, one at a time, and you'll get the entire chain. The --topo-orderwill make sure you hit I-and-Jin that order, and K-and-Lin that order (though there's no easy way to predict whether you'll do the I-J pair before or after the K-L pair).

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

回答by weakish

I have this alias in ~/.gitconfig

我有这个别名 ~/.gitconfig

first-child = "!f() { git log  --reverse --ancestry-path --pretty=%H ..${2:-HEAD} | head -1; }; f"