Eug*_*kov 41 git blame git-blame
$ pwd
/data/mdi2/classes
$ git blame -L22,+1 -- utils.js
99b7a802 mdi2/utils.js (user 2015-03-26 21:54:57 +0200 22) #comment
$ git blame -L22,+1 99b7a802^ -- utils.js
fatal: no such path mdi2/classes/utils.js in 99b7a802^
Run Code Online (Sandbox Code Playgroud)
您已经注意到,该文件位于该提交的不同目录中
$ git blame -L22,+1 99b7a802^ -- ../utils.js
c5105267 (user 2007-04-10 08:00:20 +0000 22) #comment 2
Run Code Online (Sandbox Code Playgroud)
尽管如此
The origin of lines is automatically followed across whole-file renames (currently there is no option to turn
the rename-following off)
Run Code Online (Sandbox Code Playgroud)
责备不遵循重命名.为什么?
更新:简答
git blame 按照重命名但不是 git blame COMMIT^ -- <filename>
但是,通过批量重命名和大量历史记录手动跟踪文件重命名太难了.我认为,必须修复此行为以静默跟随重命名git blame COMMIT^ -- <filename>.或者,至少--follow必须实施,所以我可以:git blame --follow COMMIT^ -- <filename>
更新2:这是不可能的.参见下文.
来自 Junio C Hamano的MAILLIST的回答
git blame按照重命名但不是git blame COMMIT^ -- <filename>
假设您的v1.0版本中有文件A和文件B.
未来六个月,代码进行了大量重构,您不需要分别对这两个文件的内容进行重构.你已经删除了A和B以及它们现在在文件C中的大部分内容.这是当前的状态.
git blame -C HEAD -- C
Run Code Online (Sandbox Code Playgroud)
可以按照两者的内容就好了,但是如果你被 允许说的话
git blame v1.0 -- C
Run Code Online (Sandbox Code Playgroud)
它甚至意味着什么?C根本不存在v1.0.您是否要求遵循A的内容,或者B?当你在这个命令中告诉它C时,你是怎么告诉你的意思是A而不是B?
"git blame"遵循内容动作,从不以任何特殊的方式对待"重命名",因为认为重命名在某种程度上是特殊的,这是一个愚蠢的事情;-)
你从命令行告诉从命令开始挖掘什么内容的方法是给出起始点提交(默认为HEAD,但你可以给出COMMIT ^作为你的例子)和起始点的路径.因为将C告诉Git然后神奇地让它猜测你在某些情况下意味着A而在其他情况下意味着B是没有任何意义的.如果v1.0没有C,那么唯一明智的做法就是退出而不是猜测(并且不告诉用户它是如何猜测的).
tor*_*rek 33
git blame 确实遵循重命名(git log如果你给它的话--follow).问题在于它重命名的方式,这是一个不太彻底的黑客攻击:因为它一次退回一个提交(从每个子节点到每个父节点),它会产生差异 - 你可以做同样的差异手动制作:
git diff -M SHA1^ SHA1
Run Code Online (Sandbox Code Playgroud)
- 并检查此差异是否检测到重命名.1
这一切都很好,但它意味着git blame要检测重命名,(a)git diff -M必须能够检测到它(幸运的是这里就是这种情况)并且 - 这就是导致你出现问题的原因 - 它必须跨过重命名.
例如,假设提交图看起来像这样:
A <-- B <-- ... Q <-- R <-- S <-- T
Run Code Online (Sandbox Code Playgroud)
其中每个大写字母代表一个提交.进一步假设在commit中重命名了一个文件R,以便在提交时R通过T它具有名称,newname而在提交A时Q它具有名称oldname.
如果你运行git blame -- newname的程序开始于T,比较S和T,比较R和S,并比较Q和R. 当它进行比较Q和R,git blame发现名称变化,并开始寻找oldname在提交Q和更早,所以当它进行比较P和Q它比较文件oldname,并oldname在这两个提交.
如果,另一方面,你运行git blame R^ -- newname(或者git blame Q -- newname),使该序列开始于提交Q,没有任何文件newname在提交时,并没有进行比较时,没有重命名P和Q,和git blame简单地放弃.
诀窍在于,如果您从文件具有先前名称的提交开始,则必须为git指定旧名称:
git blame R^ -- oldname
Run Code Online (Sandbox Code Playgroud)
然后它再次起作用.
1在git diff文档中,您将看到有一个-M选项可控制如何 git diff检测重命名.该blame代码修改这一点(事实上的确两遍,一个与-M关闭,第二与-M开启),并使用它自己的(不同的)-M选项有所不同的目的,但最终它使用相同的代码.
[ 编辑以添加对评论的回复(不适合作为评论本身)]:
是否可以向我显示文件的任何工具重命名为:git renames <filename> SHA date oldname-> newname
不完全,但git diff -M接近,可能足够接近.
我不确定你的"SHA日期"是什么意思,但git diff -M允许你提供两个SHA-1并比较左右对比.添加--name-status以获取文件名和处置.因此git diff -M --name-status HEAD oldsha1 可能会报告要转换HEAD为oldsha1,git认为您应该R枚举文件并将旧名称报告为"新"名称.例如,在git存储库本身中,有一个当前名为的文件Documentation/giteveryday.txt,其名称略有不同:
$ git diff -M --name-status HEAD 992cb206
M .gitignore
M .mailmap
[...snip...]
M Documentation/diff-options.txt
R097 Documentation/giteveryday.txt Documentation/everyday.txt
D Documentation/everyday.txto
[...]
Run Code Online (Sandbox Code Playgroud)
如果这是你关心的文件,那你很好.这里的两个问题是:
992cb206来自哪里?如果您已经拥有SHA-1,那很容易; 如果没有,git rev-list是SHA1查找工具; 阅读其文件;git blame,可能会产生完全不同的答案,而不是将一个更晚的commit(HEAD)与更早的提交(992cb206或其他)进行比较.在这种情况下,它是相同的,但这里的"相似性指数"是97中的97.如果在一些中间步骤中被修改得更多,那么相似性指数可能会低于50%......然而,如果我们只是比较修订一点后992cb206,以992cb206(如git blame会),也许这两个文件之间的相似性指数可能会更高.需要(和缺少)的是git rev-list它自己实现--follow,所以所有在git rev-list内部使用的命令- 即大多数命令不仅仅在一个修订版上工作 - 都可以解决问题.一路上,如果它在另一个方向上工作将是很好的(目前--follow只是较新到较旧,即工作正常git blame并且git log只要您不要求最早的历史记录就可以正常工作--reverse).