【问题标题】:`git log --follow --graph` skips commits`git log --follow --graph` 跳过提交
【发布时间】:2018-03-11 06:42:15
【问题描述】:

设置

git version 2.11.0.windows.1

这是一个 bash sn-p 来重现我的测试存储库:

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 提交未列出,即使它确实修改了文件。

我想我已经追踪到--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 git-log


    【解决方案1】:

    你被git log--follow 的廉价和低级实现所困扰,再加上git log 通常甚至不查看内部合并这一事实。

    从根本上说,--follow 在内部通过更改它要查找的文件的名称来工作。它不记得两个名字,所以当线性化算法(通过优先级队列的广度优先搜索)沿着合并的另一条腿向下时,它的名字是错误的。你是正确的,提交访问的顺序很重要,因为当 Git 推断出重命名时,Git 会更改它正在搜索的文件的名称。

    在此图中(看起来您多次运行脚本,因为哈希值发生了变化——这里的哈希值来自第一个示例):

    *   06b5bb7 Merge branch 'feature'
    |\
    | * 07ccfb6 Change
    * | 448ad99 Move
    |/
    * 31eae74 First commit
    

    git log 将访问提交06b5bb7,并将448ad9907ccfb6 放入队列中。使用默认的拓扑顺序,它将接下来访问448ad99,检查差异,并查看重命名。它现在正在寻找a.txt 而不是b.txt。 Commit 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),差异 1a07e4836c80a8,看到对 b.txt 的更改并选择提交,然后将1a07e48 在访问队列中。接下来,它访问 commit 36c80a8 (from 05116f1),diffs 05116f1 vs 36c80a8,并将 05116f1 放入访问队列。其余的从这里就很明显了。

    如何通过重命名清晰地显示更改文件的所有提交?

    Git 的答案是你不能,至少不能使用 Git 的内置功能。

    您可以(有时)通过在您的git log 命令中添加--cc-c 来更接近一点。这使得git log 查看合并提交,执行 Git 所称的combined diff。但这并不一定有效,因为,hidden away in a different part of the documentation 就是这个关键句:

    请注意,combined diff 仅列出从所有父级修改的文件。

    这是我添加 --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 个单独的差异,每个父项一个,以便找到哪些腿有哪些重命名——然后保留一个使用哪个名称的列表向下合并的哪些分支。但是当叉子重新组合在一起时,即当合并的多个分支(在我们的相反方向变成叉子)重新加入时,不清楚哪个名称是正确的名称!

    【讨论】:

    • 射击。好吧,无论如何,感谢您对问题的全面分析:)
    猜你喜欢
    • 2021-02-24
    • 1970-01-01
    • 2012-08-03
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 2019-07-28
    相关资源
    最近更新 更多