`git log --follow --graph` 跳过提交
`git log --follow --graph` skips commits
设置
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
(ex-b.txt
)的完整日志。
git log --graph --pretty=oneline --abbrev-commit --follow -- b.txt
打印:
...
* | 1a07e48 Move
|/
* 5ff73f6 First commit
如您所见,Change
提交未列出,即使它确实 修改了文件。
我想我已经追踪到 --graph
对 --topo-order
的隐式使用,因为添加 --date-order
会恢复提交,但这可能是偶然的。
此外,添加 -m
显示合并提交(很好)和 Change
提交,但是合并提交被复制:
* 36c80a8 (from 1a07e48) Merge branch 'feature'
|\
| | 36c80a8 (from 05116f1) Merge branch 'feature'
| * 05116f1 Change
* | 1a07e48 Move
|/
* 5ff73f6 First commit
问题
我错过了什么来解释我目睹的奇怪行为?
如何在重命名后清晰地显示所有更改文件的提交?
git log
对 --follow
的廉价和低级实现,再加上 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
。 Commit 448ad99
被选中,所以 git log
将它打印到输出; Git 将 31eae74
添加到访问队列中。接下来,Git 访问了 07ccfb6
,但它现在正在寻找 a.txt
,因此未选择此提交。 Git 将 31eae74
添加到访问队列(但它已经存在,所以这是一个空操作)。最后,Git 访问了 31eae74
;将该提交的树与空树进行比较,Git 发现添加了一个 a.txt
,因此该提交被选中。
请注意,Git 在 448ad99
之前访问了 07ccfb6
,它会选择两者,因为在开始时它正在寻找b.txt
.
-m
标志通过 "splitting" 合并为两个独立的内部 "virtual commits"(具有相同的树,但 (from ...)
添加到它们的 "names" 以便能够分辨出哪个虚拟提交是由哪个父代产生的)。这具有保留两个拆分合并并查看它们的差异的副作用(因为拆分此合并的结果是两个普通的非合并提交)。所以现在——请注意,这在第二个示例中使用了你的新存储库及其新的不同哈希值——Git 访问提交 36c80a8 (from 1a07e48)
,比较 1a07e48
与 36c80a8
,看到对b.txt
并选择提交,并将 1a07e48
放入访问队列。接下来,它访问提交 36c80a8 (from 05116f1)
、比较 05116f1
与 36c80a8
,并将 05116f1
放入访问队列。其余的从这里就很明显了。
How can I display cleanly all of the commits that changed a file, following through renames?
Git 的答案是你不能,至少不能使用 Git 中内置的内容。
您可以(有时)通过在 git log
命令中添加 --cc
或 -c
来更接近一点。这使得 git log
查看合并提交,做 Git 所谓的 combined diff. But this doesn't necessarily work anyway, because, hidden away in a different part of the documentation 是这个关键句子:
Note that combined diff lists only files which were modified from all parents.
这是我添加 --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
在每次合并时在内部使用(大部分) -m
选项——即将每次合并拆分为 N 个单独的差异,每个父项一个,以便找到哪些分支具有哪些重命名——然后保留一个列表,列出要使用哪个名称,然后进行 b运行 次合并。但是当分叉重新组合在一起时,即当合并的多个分支(在我们相反的方向上成为一个分叉)重新加入时,不清楚哪个名称是正确的名称!
设置
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
(ex-b.txt
)的完整日志。
git log --graph --pretty=oneline --abbrev-commit --follow -- b.txt
打印:
...
* | 1a07e48 Move
|/
* 5ff73f6 First commit
如您所见,Change
提交未列出,即使它确实 修改了文件。
我想我已经追踪到 --graph
对 --topo-order
的隐式使用,因为添加 --date-order
会恢复提交,但这可能是偶然的。
此外,添加 -m
显示合并提交(很好)和 Change
提交,但是合并提交被复制:
* 36c80a8 (from 1a07e48) Merge branch 'feature'
|\
| | 36c80a8 (from 05116f1) Merge branch 'feature'
| * 05116f1 Change
* | 1a07e48 Move
|/
* 5ff73f6 First commit
问题
我错过了什么来解释我目睹的奇怪行为?
如何在重命名后清晰地显示所有更改文件的提交?
git log
对 --follow
的廉价和低级实现,再加上 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
。 Commit 448ad99
被选中,所以 git log
将它打印到输出; Git 将 31eae74
添加到访问队列中。接下来,Git 访问了 07ccfb6
,但它现在正在寻找 a.txt
,因此未选择此提交。 Git 将 31eae74
添加到访问队列(但它已经存在,所以这是一个空操作)。最后,Git 访问了 31eae74
;将该提交的树与空树进行比较,Git 发现添加了一个 a.txt
,因此该提交被选中。
请注意,Git 在 448ad99
之前访问了 07ccfb6
,它会选择两者,因为在开始时它正在寻找b.txt
.
-m
标志通过 "splitting" 合并为两个独立的内部 "virtual commits"(具有相同的树,但 (from ...)
添加到它们的 "names" 以便能够分辨出哪个虚拟提交是由哪个父代产生的)。这具有保留两个拆分合并并查看它们的差异的副作用(因为拆分此合并的结果是两个普通的非合并提交)。所以现在——请注意,这在第二个示例中使用了你的新存储库及其新的不同哈希值——Git 访问提交 36c80a8 (from 1a07e48)
,比较 1a07e48
与 36c80a8
,看到对b.txt
并选择提交,并将 1a07e48
放入访问队列。接下来,它访问提交 36c80a8 (from 05116f1)
、比较 05116f1
与 36c80a8
,并将 05116f1
放入访问队列。其余的从这里就很明显了。
How can I display cleanly all of the commits that changed a file, following through renames?
Git 的答案是你不能,至少不能使用 Git 中内置的内容。
您可以(有时)通过在 git log
命令中添加 --cc
或 -c
来更接近一点。这使得 git log
查看合并提交,做 Git 所谓的 combined diff. But this doesn't necessarily work anyway, because, hidden away in a different part of the documentation 是这个关键句子:
Note that combined diff lists only files which were modified from all parents.
这是我添加 --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
在每次合并时在内部使用(大部分) -m
选项——即将每次合并拆分为 N 个单独的差异,每个父项一个,以便找到哪些分支具有哪些重命名——然后保留一个列表,列出要使用哪个名称,然后进行 b运行 次合并。但是当分叉重新组合在一起时,即当合并的多个分支(在我们相反的方向上成为一个分叉)重新加入时,不清楚哪个名称是正确的名称!