git version 2.11.0.windows.1
这是一个重现我的测试存储库的bash片段:
git init
# Create a file
echo Hello > a.txt
git add a.txt
git commit -m 'First commit'
# Change it on one branch
git checkout -b feature
echo Hi > a.txt
git commit -am 'Change'
# Rename it on the other
git checkout master
git mv a.txt b.txt
git commit -m 'Move'
# Merge both changes
git merge --no-edit feature
最后,git log --graph --pretty=oneline --abbrev-commit
打印:
* 06b5bb7 Merge branch 'feature'
|\
| * 07ccfb6 Change
* | 448ad99 Move
|/
* 31eae74 First commit
现在,我想获取b.txt
的完整日志(前 - b.txt
)
git log --graph --pretty=oneline --abbrev-commit --follow -- b.txt
打印:
...
* | 1a07e48 Move
|/
* 5ff73f6 First commit
如您所见,Change
提交未列出,即使 修改了该文件。
我认为我已将其跟踪--topo-order
隐式使用--graph
,因为添加--date-order
会使提交恢复,但这可能是机会。
此外,添加-m
显示合并提交(很好)和Change
提交,但随后合并提交重复:
* 36c80a8 (from 1a07e48) Merge branch 'feature'
|\
| | 36c80a8 (from 05116f1) Merge branch 'feature'
| * 05116f1 Change
* | 1a07e48 Move
|/
* 5ff73f6 First commit
我想要解释我目睹的奇怪行为是什么? 如何通过重命名来干净地显示所有更改文件的提交?
答案 0 :(得分:7)
git log
--follow
的{{1}}便宜而低俗的实施,以及git log
通常甚至看不到的事实汇合。
从根本上说,--follow
通过更改其正在查找的文件的名称在内部工作。它不记得这两个名称,所以当线性化算法(通过优先级队列的广度优先搜索)下降到合并的另一条腿时,它的名称错误。你是正确的,提交访问的顺序很重要,因为当Git推断重命名时Git改变了它正在搜索的文件的名称。
在此图表中(看起来您已经多次运行脚本,因为哈希值已更改 - 此处的哈希值来自第一个样本):
* 06b5bb7 Merge branch 'feature'
|\
| * 07ccfb6 Change
* | 448ad99 Move
|/
* 31eae74 First commit
git log
将访问提交06b5bb7
,并将448ad99
和07ccfb6
放在队列中。使用默认的拓扑订单,接下来将访问448ad99
,检查差异,并查看重命名。它现在正在寻找a.txt
而不是b.txt
。已选中提交448ad99
,因此git log
会将其打印到输出中;并且Git将31eae74
添加到访问队列中。接下来,Git访问07ccfb6
,但现在正在查找a.txt
,因此未选中此提交。 Git将31eae74
添加到访问队列(但它已经存在,所以这是一个无操作)。最后,Git访问了31eae74
;将该提交树与空树进行比较,Git找到添加的a.txt
,以便选择此提交。
请注意,如果Git在 07ccfb6
之前访问了448ad99
,它会选择两者,因为在开始时它会查找b.txt
。
-m
标志通过"拆分"合并为两个独立的内部虚拟提交" (使用相同的树,但将(from ...)
添加到他们的"名称"以便能够分辨哪个虚拟提交来自哪个父级)。这具有保留两个拆分合并并查看它们的差异的副作用(因为拆分此合并的结果是两个普通的非合并提交)。现在请注意,这会在第二个示例中使用您的新存储库及其新的不同哈希 - Git访问提交36c80a8 (from 1a07e48)
,差异1a07e48
与36c80a8
,看到对{{1}的更改并选择提交,并将b.txt
放在访问队列中。接下来,它访问提交1a07e48
,差异36c80a8 (from 05116f1)
与05116f1
,并将36c80a8
置于访问队列中。其余的在这里相当明显。
如何通过重命名来干净地显示所有更改文件的提交?
Git的答案是你不能,至少不使用Git中内置的东西。
您可以(有时)通过向05116f1
命令添加--cc
或-c
来更近距离。这使得git log
查看内部合并提交,执行Git称为combined diff的操作。但这无论如何都不一定有效,因为hidden away in a different part of the documentation是这个关键句子:
请注意,组合差异仅列出从所有父级修改过的文件。
以下是我添加git log
的内容(注意,--cc
确实存在于...
的输出中:
git log
但是,从根本上说,您需要$ git log --graph --oneline --follow --cc -- b.txt
* e5a17d7 (HEAD -> master) Merge branch 'feature'
|\
| |
...
* | 52e75c9 Move
|/
| diff --git a/a.txt b/b.txt
| similarity index 100%
| rename from a.txt
| rename to b.txt
* 7590cfd First commit
diff --git a/a.txt b/a.txt
new file mode 100644
index 0000000..e965047
--- /dev/null
+++ b/a.txt
@@ -0,0 +1 @@
+Hello
更多地了解合并提交时的文件重命名,并让它使用旧文件名在任何一条腿上查找旧名称,并且使用新名称的任何一条腿的新名称。这将要求git log
在每个合并内部使用(大部分)git log
选项 - 即,将每个合并拆分为N个单独的差异,每个父级一个,以便找到哪些分支具有重命名 - 然后保留一个列表,列出要使用哪个合并分支的名称。但是当叉子一起回来时,即当合并的多个支腿(它在我们的反方向上成为一个叉子)重新加入时,它不清楚哪个名称是正确的名称!