【问题标题】:How do I "git blame" a deleted line?我如何“git责备”删除的行?
【发布时间】:2011-05-23 05:04:32
【问题描述】:

git blame 非常适合修改和添加的行,但是我如何才能找到存在于特定先前提交中的行最终被删除的时间。我在想bisect,但我希望有更方便的东西。

(在你问之前:在这种情况下,我只是做了一个git log -p 并搜索了代码行,并且(a)一些白痴只是删除了上一次提交中的重要行并且(b) 我就是那个白痴。)

【问题讨论】:

  • 有一个 followup 的答案澄清了 git log -S<string> /path/to/file 还想要一个 -c-cc 以显示合并期间的删除(冲突)
  • 应该是-c--cc。 @Steen:正确,感谢您指出!愚蠢的监督。希望我可以编辑评论。添加一个新的,然后删除我的,然后删除你的,我想这太麻烦了:)
  • 我希望 git blame 可以选择显示已删除的行(可能带有 strikethrough 或红色文本)以及删除它们的修订版本。
  • 那会很难写吗?我不太了解 Git 内部结构。

标签: git


【解决方案1】:

对于隐藏在合并提交中的更改

合并提交会自动将其更改隐藏在 Git 日志输出中。 pickaxereverse-blame 都没有发现变化。所以我想要的行已经被添加然后被删除,我想找到删除它的合并。文件git log -p -- path/file 历史仅显示它被添加。这是我找到的最佳方法:

git log -p -U9999 -- path/file

搜索更改,然后向后搜索“^commit” - 第一个“^commit”是文件最后包含该行的提交。第二个“^commit”是在它消失之后。第二个提交可能是删除它的那个。 -U9999 旨在显示整个文件内容(每次更改文件后),假设您的文件都是最多 9999 行。

通过蛮力查找任何相关的合并(将每个可能的合并提交与其第一个父级进行比较,针对大量提交运行)

git log --merges --pretty=format:"git diff %h^...%h | grep target_text" HEAD ^$(git merge-base A B) | sh -v 2>&1 | less

(我尝试更多地限制修订过滤器,但遇到了问题,不推荐这样做。我正在寻找的添加/删除更改位于不同的分支上,这些分支在不同的时间合并到 A...B不包括更改实际合并到主线的时间。)

显示包含这两个提交的 Git 树(并删除了许多复杂的 Git 历史记录):

git log --graph --oneline A B ^$(git merge-base A B) (A 是上面的第一次提交,B 是上面的第二次提交)

显示 A 的历史和 B 的历史减去 A 和 B 的历史。

替代版本(似乎更线性地显示路径而不是常规的 Git 历史树 - 但我更喜欢常规的 git 历史树):

git log --graph --oneline A...B

三个,而不是两个点 - 三个点的意思是“r1 r2 --not $(git merge-base --all r1 r2)。它是可以从 r1(左侧)或r2(右侧),但不是来自两者。” - 来源:“man gitrevisions”

【讨论】:

    【解决方案2】:

    只是为了完成Cascabel's answer

    git log --full-history -S <string> path/to/file
    

    我遇到了与这里提到的相同的问题,但是结果发现该行丢失了,因为来自分支的合并提交被还原然后合并回它,有效地删除了题。 --full-history 标志防止跳过这些提交。

    【讨论】:

      【解决方案3】:

      git blame --reverse 可以让您接近到删除该行的位置。但它实际上 指向删除该行的修订版。它指向该行存在最后修订版。然后,如果 后续修订 是一个普通的提交,那么你很幸运,你得到了删除修订。 OTOH,如果以下修订是合并提交,那么事情可能会变得有点疯狂。

      作为创建 difflame 工作的一部分,我解决了这个问题,所以如果你已经在你的机器上安装了 Python 并且你愿意尝试一下,那就不要再等了并告诉我进展如何。

      https://github.com/eantoranz/difflame

      【讨论】:

      • 你能解释一下在下面的修订是合并提交的情况下的逻辑吗?
      • 现在不记得细节了*(我什至认为我需要更正 difflame 中的一些极端情况)但是如果以下修订是合并提交,那么您需要追踪合并修订的其他父级以查看该行被删除的位置(并考虑它可能已在合并修订本身中被删除,与父级的内容无关)。
      【解决方案4】:

      我认为你真正想要的是

      git blame --reverse START..END filename
      

      来自the manpage

      向前而不是向后走历史。这不是显示出现行的版本,而是显示存在行的最后一个版本。这需要一系列的修订,例如 START..END,其中责备路径存在于 START 中。

      使用git blame reverse,您可以找到该行出现的最后一个提交。您仍然需要获取后面的提交。

      您可以使用以下命令来显示反向的 git 日志。显示的第一个提交将是该行的最后一次出现,下一次提交将是它被更改或删除的时间。

      git log --reverse --ancestry-path COMMIT^..master
      

      【讨论】:

      • 如果从添加行的分支到缺少行的分支中有多个合并(或任何其他从 START 到 END 的沿袭中有多个路径的情况),@987654325 @ 将显示按时间顺序最后的合并之前的修订,而不是在初始合并之前做出决定不采取该行的修订。有什么方法可以找到该行停止存在的最早版本而不是最新版本?
      • @rakslice ,因为你可以使用blame --reverse --first-parent ,稍微好一点。
      【解决方案5】:

      如果您知道该行的内容,这是一个理想的用例:

      git log -S <string> path/to/file
      

      它向您显示引入或删除该字符串的实例的提交。还有-G&lt;regex&gt; 对正则表达式做同样的事情!有关详细信息,请参阅 man git-log 并搜索 -G-S 选项或镐(这些功能的友好名称)。

      -S 选项实际上也在 git-blame 手册页的标题中提到,在描述部分中,它给出了一个使用 git log -S... 的示例。

      【讨论】:

      • 在使用 Git 1 年多之后,我仍然惊讶地发现 Git always 在某处有一个命令/选项来解决我所拥有的几乎所有使用场景。感谢分享这个,正是我现在需要的!
      • 这个方法以前对我有用,但是刚才我看到一个案例,它没有找到删除该行的提交。原来有问题的行在合并提交中被删除了——这能解释失败吗? (git blame --reverse 方法找到了它。)
      • @antinome 要显示来自合并的提交,请另外使用 -c 选项。
      • 我在联机帮助页上的“-s”上按了 ctrl+f,但一无所获。你在页面的什么地方看到它??我正在使用 git 1.8.5.2
      • 请注意,您不必对单个文件运行命令。 git log -S ... path/to/directory 工作正常。
      猜你喜欢
      • 2020-12-12
      • 2020-01-25
      • 2023-03-03
      • 2021-07-29
      • 1970-01-01
      • 2017-04-27
      • 2017-04-17
      • 2011-07-03
      相关资源
      最近更新 更多