这不是一个真正的答案,但我需要使用格式和大量空间。我将尝试描述我认为两个最佳答案背后的理论:公认的答案和(至少目前)排名最高的答案。但是实际上,他们回答了不同的问题。
Git中的提交通常一次“在”多个分支上。确实,这就是问题的实质。鉴于:
...--F--G--H <-- master
\
I--J <-- develop
大写字母代表实际的Git哈希ID,我们通常在输出中只查找一个H或多个提交I-Jgit log。提交G都在两个分支上,因此我们希望将它们排除在外。
(请注意,在绘制这样的图表,较新提交是朝着正确的。名字中选择一个最右边的承诺在该行每个这些提交的有父提交,这是承诺他们的左:父HISG和母公司J是I的父,I是G再一次的家长。G是F,并F有简单的这里没有显示父:它的一部分的...部分)。
对于这种特别简单的情况,我们可以使用:
git log master..develop # note: two dots
查看I-J,或:
git log develop..master # note: two dots
H仅查看。在两个点后面的右侧名称告诉Git:是的,这些提交。在两个点之前的左侧名称告诉Git:不,不是这些commits。Git从末尾开始,即commitH或commit J,然后向后工作。有关此的更多信息,请参见思考(a)Git。
最初问题的表达方式是,希望找到可以从一个特定名称而不是同一通用类别中的任何其他名称到达的提交。也就是说,如果我们有一个更复杂的图:
O--P <-- name5
/
N <-- name4
/
...--F--G--H--I---M <-- name1
\ /
J-----K <-- name2
\
L <-- name3
我们可以选择其中一个名称,例如name4或name3,然后询问:可以通过该名称找到哪些提交,而不能通过其他任何名称找到? 如果我们选择name3答案,那就是commit L。如果我们选择name4,答案是根本没有提交:name4名称为commitN但N可以通过从头开始name5并向后工作来找到的提交。
接受的答案使用远程跟踪名称而不是分支名称,并且允许您将一个(拼写的一个)指定origin/merge-only为所选名称,并查看该命名空间中的所有其他名称。它还避免了显示合并:如果我们选择name1“有趣的名称”,并说显示可从中访问name1但没有其他任何名称的提交,则将看到合并提交M和常规提交I。
最受欢迎的答案是完全不同的。这是所有关于穿越提交图形,而不以下双腿合并,并没有表现出任何的是,提交的是合并。name1例如,如果我们以开头,则不会显示M(这是一个合并),但是假设合并的第一个父对象M是commit I,我们甚至不会查看commitJ和K。我们最终会显示提交I,并且还承诺H,G,F,等等,这些都不是合并的提交和所有可以连在开始M和向后工作,参观只有第一各合并提交的母公司。
最受欢迎的答案非常适合例如查看master何时master打算成为仅合并分支。如果所有“实际工作”都是在随后合并到的分支中完成的master,则我们将具有以下模式:
I---------M---------N <-- master
\ / \ /
o--o--o o--o--o
其中所有未命名的o提交都是普通(非合并)提交,M并且N是合并提交。提交I是初始提交:有史以来第一次提交,并且应该是在主服务器上的唯一提交(不是合并提交)。如果git log --first-parent --no-merges master显示除以外的 任何提交I,我们将遇到以下情况:
I---------M----*----N <-- master
\ / \ /
o--o--o o--o--o
我们希望看到*直接在上进行的提交,而master不是通过合并某些功能分支。
简而言之,流行的答案非常适合查看master何时master只应进行合并,而不适用于其他情况。 接受的答案适用于其他情况。
远程跟踪名称是否像origin/master 分支名称?
Git的某些部分说它们不是:
git checkout master
...
git status
说on branch master,但是:
git checkout origin/master
...
git status
说HEAD detached at origin/master。我更愿意同意git checkout/ git switch:origin/master不是分支名称,因为您无法“使用”它。
接受的答案使用远程跟踪名称origin/*作为“分支名称”:
git log --no-merges origin/merge-only \
--not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
grep -Fv refs/remotes/origin/merge-only)
中间的行调用git for-each-ref,对名为的远程对象的远程跟踪名称进行迭代origin。
之所以可以很好地解决原始问题,是因为我们对其他人的分支名称感兴趣,而不是我们的分支名称。但这意味着我们已经将分支定义为分支名称以外的东西。很好:请注意您在执行此操作。
git log 遍历提交图的某些部分
我们真正要在这里搜索的是所谓的daglet系列:请参阅“分支”到底是什么意思? 也就是说,我们正在整体提交图的某些子集中寻找片段。
每当我们让Git查看分支名称(例如)master,标签名称(例如v2.1)或远程跟踪名称(例如)时origin/master,我们往往希望让Git告诉我们该提交以及从该提交中可以得到的每个提交: ,然后向后工作。
在数学中,这称为走图。Git的提交图是有向无环图或DAG,这种图特别适合步行。在浏览这样的图形时,将访问通过使用的路径可到达的每个图形顶点。Git图中的顶点是提交,边缘是从每个孩子到每个父对象的弧形(单向链接)。(这就是“想像(a)Git”出现的地方。弧线的单向性质意味着Git必须从子级到父级都向后工作。)
用于图形遍历的两个主要Git命令是git log和git rev-list。这些命令极为相似-实际上它们大多是从相同的源文件构建的-但它们的输出是不同的:git log产生供人类读取的git rev-list输出,而产生供其他Git程序读取的输出。1 这两个命令都执行这种图遍历。
他们具体执行的图形遍历是:给定一组起点提交(也许只有一个提交,也许一堆哈希ID,也许一堆解析为哈希ID的名称),遍历图形,访问commit。特殊指令(例如--not或前缀^,或--ancestry-path或)以某种方式--first-parent修改了图形遍历。
在进行图行走时,他们会访问每个提交。但是它们仅打印部分已提交的选定子集。指令如--no-merges或--before <date>告诉图形行走代码提交到打印。
为了进行一次访问,一次提交一次,这两个命令使用优先级队列。您运行git log或git rev-list给它一些起点提交。他们将这些提交放入优先级队列。例如,一个简单的:
git log master
将名称master转换为原始哈希ID,并将该哈希ID放入队列。要么:
git log master develop
将两个名称都转换为哈希ID,并假设它们是两个不同的哈希ID,则将两者都放入队列。
此队列中提交的优先级由更多参数确定。例如,参数--author-date-order告诉git log或git rev-list使用作者时间戳,而不是提交者时间戳。默认设置是使用提交者时间戳,并选择最新的提交:具有最高数字日期的提交。因此,与master develop假设这些决心两种不同的提交,Git将显示哪一个来了以后第一个,因为这将是在队列的前面。
无论如何,修订版本遍历代码现在都在循环中运行:
- 当队列中有提交时:
- 删除第一个队列条目。
- 确定是否完全打印此提交。例如
--no-merges::如果是合并提交,则不打印任何内容;--before:如果日期不早于指定时间,则不打印任何内容。如果不禁止打印,则打印commit:git log显示;为git rev-list,打印其哈希ID。
- 将一些或所有此提交的父提交提交到队列中(只要它现在不存在,并且尚未被访问2)。正常的默认值是放入所有父项。使用
--first-parent禁止除每个合并的第一个父对象外的所有对象。
(两者git log并git rev-list可以做历史的简化带或不带父改写在这一点为好,但我们会跳过这里。)
对于简单的链,例如在HEAD没有合并提交的情况下从头开始并向后工作,队列在循环的顶部始终始终有一个提交。有一个提交,因此我们将其弹出并打印,然后将其(单个)父级放入队列中,然后再次进行遍历,然后沿着链条向后移动,直到到达第一个提交,否则用户会厌倦git log输出并退出该程序。在这种情况下,排序选项都不重要:仅显示一次提交。
如果存在合并,并且我们遵循父母双方(合并的“两条腿”),或者当您给出一个git log或git rev-list多个起始提交时,排序选项就很重要。
最后,考虑提交说明符的作用--not或^在提交说明符之前。这些有几种编写方式:
git log master --not develop
要么:
git log ^develop master
要么:
git log develop..master
都是同一件事。该--not像前缀^不同之处在于它适用于多个名称:
git log ^branch1 ^branch2 branch3
表示不是branch1,不是branch2,是branch3;但:
git log --not branch1 branch2 branch3
表示不是branch1,不是branch2,不是branch3,并且您必须使用一秒钟--not将其关闭:
git log --not branch1 branch2 --not branch3
这有点尴尬。这两个“非”指令通过XOR组合在一起,因此,如果您确实需要,可以编写:
git log --not branch1 branch2 ^branch3
如果要混淆,则表示不是branch1,不是branch2,是branch3。
这些都通过影响图形游走而起作用。作为git log或git rev-list散步的图形,它确保不投入的优先级队列中的任何承诺是从任何可达否定引用。(实际上,它们也影响启动设置:否定的提交不能直接从命令行进入优先级队列,因此git log master ^master什么也不显示。)
gitrevisions文档中描述的所有精美语法都使用了此语法,您可以通过简单调用暴露它git rev-parse。例如:
$ git rev-parse origin/pu...origin/master # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306
三点语法意味着可从左侧或右侧访问的提交,但不包括可从两者访问的提交。在这种情况下,origin/master提交 ()b34789c0b本身可从origin/pu(fc307aa37...)到达,因此origin/master散列出现两次,一次带有否定,但实际上Git通过放入两个正引用(两个非否定的哈希ID)来实现三点语法。一个负数,以^前缀表示。
相似:
$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c
该^@语法意味着所有给定的父母承诺,以及master^本身-在此提交的分支名称选定的第一个父master-is合并提交,所以它有两个家长。这是两个父母。和:
$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c
该^!后缀手段的承诺本身,但没有其父母。在这种情况下,master^是0b07eecf6...。我们已经看到父母双方都有^@后缀。他们又来了,但是这次却被否定了。
1许多Git程序实际上git rev-list使用各种选项运行,并读取其输出,以了解要使用哪些提交和/或其他Git对象。
2因为该图是非循环的,所以可以保证没有访问过图,如果我们添加约束,则在将其所有子级显示为优先级之前,从不显示父级。 --date-order,,--author-date-order并--topo-order添加此约束。没有默认的排序顺序(没有名称)。如果提交时间戳很麻烦(例如,某些提交是由时钟关闭的计算机“将来”执行的),则在某些情况下可能会导致输出看起来很奇怪。
如果到目前为止,您已经了解很多 git log
概要:
git log 是关于在遍历图的某些或全部时显示一些选定的提交。
--no-merges在接受的答案和当前排名最高的答案中都存在该参数,它会抑制显示某些已提交的提交。
- 该
--first-parent说法,从当前排名第一的回答,压制行走图形的某些部分,在图形的走本身。
- 该
--not前缀的命令行参数,在接受的答案是使用,压制曾经访问图形的某些部分在所有的,从一开始。
使用这些功能,我们可以针对两个不同的问题获得所需的答案。
git log ^branch1 ^branch2 merge-only-branch语法呢?