我的提交历史记录如下:
* 7a5841d - (6 hours ago) commit messageA (master)
| * 1552e99 - (6 hours ago) commit messageB (HEAD -> feature1)
| * 2d4a3bd - (6 hours ago) commit messageC
| * 31513f7 - (6 hours ago) commit messageD
| * 3335afb - (7 hours ago) commit messageE
|/
* 5be4cf7 - (27 hours ago) commit messageF (origin/master, origin/HEAD)
| * 870f217 - (27 hours ago) commit messageG (origin/develop)
| * 70f1973 - (27 hours ago) commit messageH
|/
* 50e3bba - (27 hours ago) commit messageI
$ git log --oneline master ^feature1 --no-merges
7a5841d commit messageA
$ git log --all --oneline master ^feature1 --no-merges
7a5841d commit messageA
870f217 commit messageG
70f1973 commit messageH
git log --all
在这做什么?为什么它会返回额外的两次提交?
答案 0 :(得分:2)
我假设第一个确实是所有提交的完整集合(我没有理由相信它不是,但可能会有更多我们无法看到,例如,在reflogs中 - 不会影响这个答案,但无论如何,它都是一个微妙的点。)
--all
的含义是所有引用(加HEAD
)。但这不是全部的故事。 (我也会遗漏--no-merges
,因为无论如何都没有合并。)
让我们从 references 的概念开始,区别于分支,标签和远程跟踪分支。参考是完全一般的形式:所有三种特定品种(分支,标签,远程跟踪分支)都是一种参考。像master
这样的分支名称大多只是说出完整引用名称refs/heads/master
的简短方法。像v1.3
这样的标记名称只是refs/tags/v1.3
的缩写,而origin/develop
之类的远程跟踪分支名称只是refs/remotes/origin/develop
的缩写。
还有更多可能的引用:例如,stash
是refs/stash
的缩写。如果您有活动存储(来自git stash save
),则会有refs/stash
。那会出现在这里,并没有;所以你没有。但如果你这样做,--all
会包括它。还有可选的Git"笔记"在refs/notes
下,可能会有更多。以refs/
开头的任何内容都是引用,--all
标记会全部获取! (另外,正如我所提到的,HEAD
,但通常并不重要。请注意,HEAD
并非以refs/
开头,所以它名义上并非如此一个引用,即使它是Git默认使用的那个!)
您可以使用--branches
表示"所有分支" (refs/heads/*
)中的所有内容,--tags
表示"所有标记" (refs/tags/*
)和--remotes
表示"所有远程跟踪分支" (refs/remotes/*
)。使用其中的部分或全部内容,您可以省略额外的--all
项(如果存在)。
master
几乎在任何时候使用master
之类的名称时,Git都会经历the gitrevisions documentation中描述的六步参考查找过程。点击该链接,找到六步列表,并注意从master
到refs/heads/master
的翻译实际上是步骤四。如果有一个refs/tags/master
,则会在步骤三中发生,这是您将获得的参考。
(我在这里任何时候都说几乎因为某些命令,例如git checkout
,试图先将名称视为分支名称,然后才进行六步骤处理解析名称。如果同时存在分支git checkout master
和标记master
,则master
会检出分支,而不是标记。git log
命令没有&#39} ; t:如果两者都存在,那么你可以在这里获得标签!短版本是"不要这样做" :-) - 不要用一个名为 X的分支设置自己和一个名为 X 的标签。这太令人困惑了。)
你的最终命令是:
git log --all --oneline master ^feature1 --no-merges
您在这里不需要master
,因为--all
表示所有引用,而master
表示"找到{{1}参考" (可能是分支)。它已被发现。
master
至关重要。当Git枚举所有可达提交时,它会考虑你告诉它开始的所有地方 - 在这种情况下,^feature1
表示"所有引用" -but它会覆盖你告诉它停止的所有地方。前缀帽--all
否定了引用:它表示"将其放入停止列表中。" Git找到^
命名的特定提交;我们可以在上面看到这是feature1
。
所以Git将1552e99
放入"停止"名单。但它并不止于此:它发现所有提交可从 1552e99
(messageB)到达。这从它的第一个也是唯一的父提交开始,即1552e99
(messageC)。但是,提交2d4a3bd
有一个父级:提交2d4a3bd
(messageD);并且它有一个父31513f7
(messageE),它有父3335afb
(messageF)。
请注意,5be4cf7
的父级是5be4cf7
(messageI),跳过其他两次提交。 (按照连接50e3bba
输出中的星号的行。如果没有git log --graph
,这可能会更容易,至少在开始时;在--oneline
这样的图形查看器中几乎肯定更容易或者一些GUI。)
当我们到达gitk
时,我们停止:它根本没有父母。它是 root commit (这意味着"没有父母的提交")。这样就完成了我们的停止"停止"提交。
现在50e3bba
可以继续查看所有引用。总共有四个:git log --all
,master
,feature1
和origin/master
。 (或者可能有五六个:我们也应该考虑origin/develop
和HEAD
。但是,最后两个是符号引用,因为它们只是命名其他现有引用所以他们被折叠起来,我们又回到了四个。)
这四个名称决定提交origin/HEAD
(7a5841d
),master
(1552e99
),HEAD -> feature1
(5be4cf7
)和{ {1}}(origin/master, origin/HEAD
)。因此,870f217
将 - 或尝试将这四个提交放入要显示给您的事物列表中。
origin/develop
位于停止列表中。所以Git没有表现出来;它会从"中弹出它以显示"列表(或者从不把它放在那里,具有相同的效果)。 Git选择剩下的三个提交之一并显示它。我们可以看到它选择了git log
。 1
Git尝试将1552e99
的父提交添加到"显示列表",但7a5841d
的父级是{{1 }}。那已经在"停止"列表,通过7a5841d
。它要么被抛出,要么永远不会进入。
只在显示列表中留下7a5841d
。 Git接下来显示它,然后将其父项添加到显示列表中。它只有一个父5be4cf7
。所以现在在节目列表中; Git向您展示了那个。 Git尝试将其父级添加到节目列表中,但是^feature1
位于停止列表中。
显示列表现在为空,因此870f217
已完成。
1 这里还有另一个棘手的问题。当Git处于从未经排序的一堆可能提交中选择一个提交的位置时,它需要具有数字最大提交日期的提交。如果你做了一个过去未来的提交,把它放在一个分支或标签上,以便它可以从70f1973
到达,并运行50e3bba
,那将来的提交来了每次都先出去。