$ 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^
正如您所注意到的,该文件位于该提交的不同目录中
$ git blame -L22,+1 99b7a802^ -- ../utils.js
c5105267 (user 2007-04-10 08:00:20 +0000 22) #comment 2
尽管有文件
The origin of lines is automatically followed across whole-file renames (currently there is no option to turn
the rename-following off)
责备不遵循重命名。为什么呢?
更新:简短回答
git blame
关注重命名,但不关注git blame COMMIT^ -- <filename>
但是,通过大量重命名和大量历史记录手动跟踪文件重命名太难了。
我认为,必须修复此行为以静默跟随git blame COMMIT^ -- <filename>
的重命名。或者,至少必须实现--follow
,因此我可以:git blame --follow COMMIT^ -- <filename>
UPDATE2:这是不可能的。请阅读以下内容。
来自MAILLIST的回答作者:Junio C Hamano
git blame
关注重命名,但不关注git blame COMMIT^ -- <filename>
假设您的v1.0版本中有文件A和文件B.
在接下来的六个月里,代码被重构了很多,你做到了 不需要分别对这两个文件的内容。你有 删除了A和B以及它们现在的大部分内容现在都在文件C中 现状。
git blame -C HEAD -- C
可能会跟随两者中的内容,但是如果你是 允许说
git blame v1.0 -- C
它甚至意味着什么? C根本不存在v1.0。你是 要求遵循A的内容,或者B?你好吗? 当你在这个命令中告诉它C时,告诉你的意思是A而不是B?
&#34; git blame&#34;遵循内容动作,从不对待&#34;重命名&#34;在 任何特殊的方式,因为认为重命名是一个愚蠢的事情 不知怎的特别; - )
您告诉从开始挖掘命令的内容的方式 从它的命令行是给出起点提交(默认为 HEAD,但您可以将COMMIT ^作为您的示例)以及其中的路径 初始点。因为将C告诉Git并没有任何意义 然后神奇地猜测你在某些情况下意味着A而在某些情况下意味着B. 其他。如果v1.0没有C,唯一明智的做法就是 退出而不是猜测(而不告诉用户它是如何做的) 猜到)。
答案 0 :(得分:30)
git blame
会重命名(如果您git log
提供--follow
,也会如此git diff -M SHA1^ SHA1
。问题在于方式它是否重命名,这是一个非常彻底的黑客攻击:因为它一次退回一个提交(从每个子节点到每个父节点),它会产生差异 - 您可以手动使用相同类型的差异:
git blame
- 并检查此差异是否检测到重命名。 1
这一切都很好,但这意味着git diff -M
要检测重命名,(a)A <-- B <-- ... Q <-- R <-- S <-- T
必须能检测它(幸运的是这里就是这种情况)并且 - 这就是导致你出现问题的原因 - 它必须跨越重命名。
例如,假设提交图看起来像这样:
R
其中每个大写字母代表一个提交。进一步假设在提交R
中重命名了一个文件,因此在提交T
到newname
时,它在提交A
到{{1}时具有名称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
然后它再次起作用。
1 在git diff
documentation中,您会看到有一个-M
选项可控制 git diff
如何检测重命名。 blame
代码会稍微修改一下(实际上会执行两次传递,一次关闭-M
,另一次开启-M
)并使用自己的(不同){{1 }}选项用于某些不同的目的,但最终它使用相同的代码。
[编辑以添加对评论的回复(不适合作为评论本身)]:
是否有任何可以显示文件的工具重命名为:git renames&lt; filename&gt; SHA date oldname-&gt; newname
不完全,但-M
接近,可能已经足够接近。
我不确定你的意思&#34; SHA日期&#34;在这里,git diff -M
允许您提供两个SHA-1并比较左右对比。添加git diff -M
以获取文件名和处置。因此,--name-status
可能报告要从git diff -M --name-status HEAD oldsha1
转换为HEAD
,git认为您应该oldsha1
对文件进行命名,并将旧名称报告为&#34;新&#34;名称。例如,在git存储库本身中,有一个当前名为R
的文件,其名称略有不同:
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
[...]
来自哪里?如果您已经拥有SHA-1,那很简单;如果没有,992cb206
是SHA1查找工具;阅读其文件; git rev-list
所做的那样,可能会产生完全不同的答案,而不是比较后期提交(git blame
)与更早提交(HEAD
或其他)。在这种情况下,它出现相同,但相似性指数&#34;如果在一些中间步骤中修改得更多,那么相似性指数可能会降到50%以下......但是,如果我们要比较修订版只是 little < / em>在992cb206
到992cb206
之后(如992cb206
所示),这两个文件之间的相似性索引可能更高。 git blame
本身需要(和缺少)实现git rev-list
的所有内容,以便所有在内部使用--follow
的命令 - 即大多数命令都可以使用git rev-list
而不只是一个修订 - 可以做到这一点。一路上,如果它在另一个方向上工作会很好(目前--follow
只是更新到更老,即,git blame
工作正常,并且git log
正常工作很久你不会先--reverse
)询问最早的历史。
答案 1 :(得分:1)
最新的git有趣的命令。在您的配置旁边添加:
[alias]
follow= "!sh -c 'git log --topo-order -u -L $2,${3:-$2}:"$1"'" -
现在你可以:
$git follow <filename> <linefrom> [<lineto>]
您将看到每个提交更改<filename>
中的指定行。
您也可以对--follow
命令的git log
选项感兴趣:
继续列出重命名以外的文件历史记录(仅适用于单个文件)。
如果您对复制检测感兴趣,请使用-C
:
检测副本以及重命名。另请参阅--find-copies-harder。如果指定了n,则其含义与-M相同。
-C
将在同一次提交中查看不同的文件。如果要检测代码是从此提交中未更改的不同文件中获取的。然后你应该提供--find-copies-harder
选项。
出于性能原因,默认情况下,-C选项仅在相同变更集中修改了副本的原始文件时才查找副本。此标志使命令检查未修改的文件作为副本源的候选者。对于大型项目来说,这是一项非常昂贵的操作,因此请谨慎使用。给予多个-C选项具有相同的效果。
<强> UPD 强>
我改进了这个别名:
[alias]
follow = "!bash -c ' \
if [[ $1 == \"/\"* ]]; then \
FILE=$1; \
else \
FILE=${GIT_PREFIX}$1; \
fi; \
echo \"git log --topo-order -u -L $2,${3:-$2}:\\\"$FILE\\\"\"; \
git log --topo-order -u -L $2,${3:-$2}:\"$FILE\"; \
' --"
现在,您可以跟踪指定的行范围的更改方式:
git follow file_name.c 30 35
注意:不幸的是,git没有考虑工作目录的变化。因此,如果您对文件进行本地更改,则必须先将其存储,然后才能follow
更改