49
$ 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:それは不可能です。以下をお読みください。

メールリストからの回答by Junio C Hamano

git blame名前の変更に従いますが、git blame COMMIT^ -- <filename>

バージョン v1.0 にファイル A とファイル B があるとします。

6 か月後、コードは大幅にリファクタリングされたので、これら 2 つのファイルの内容を個別に取得する必要はありません。あなたは A と B を削除し、それらが持っていたものの多くは現在ファイル C にあります。それが現在の状態です。

git blame -C HEAD -- C

内容はどちらからでもいいのです 、言わせてもらえば

git blame v1.0 -- C

それはどういう意味ですか?C は v1.0 ではまったく存在しませんでした。当時のAの内容を踏襲するか、Bの内容を踏襲するか。このコマンドで C と言ったとき、B ではなく A を意味しているとどのように言いましたか?

「git Blame」はコンテンツの移動に従い、「名前の変更」を特別な方法で扱うことは決してありません。

コマンド ラインからどのコンテンツから掘り始めるかをコマンドに伝える方法は、開始点 commit (デフォルトは HEAD ですが、例として COMMIT^ を指定することもできます) とその開始点のパスを指定することです。C を Git に伝えてから、魔法のように A を意味する場合もあれば、B を意味する場合もあると推測させるのは意味がないためです。v1.0 に C が含まれていない場合、唯一賢明なことは、推測を行う代わりに終了することです (そして、ユーザーにどのように推測したかを伝えません)。

4

2 に答える 2

36

git blame 名前の変更に従います ( を指定しgit log場合と同様--follow)。問題は名前の変更に続く方法にありますが、これはあまり完全ではないハックです: 一度に 1 つのコミットを (各子から各親に) 戻すと、差分が作成されます。次を使用して手動で作成します。

git diff -M SHA1^ SHA1

—そして、この差分が名前の変更を検出したかどうかを確認します。1

これまでのところは問題ありませんがgit blame、名前の変更を検出するには、(a)git diff -Mがそれを検出できなければならず (幸いなことに、ここではそうです)、問題の原因は次のとおりです。名前を変更します。

たとえば、コミット グラフが次のようになっているとします。

A <-- B <-- ... Q <-- R <-- S <-- T

各大文字はコミットを表します。さらに、ファイルの名前が commit で変更されR、それをR介したコミットTでは namenewnameを持ち、コミットでは nameAを持つようになったとしQますoldname

を実行するgit blame -- newnameと、シーケンスは から始まりT、 と を比較Sし、 と をT比較RS、 と を比較QRます。 と比較するQR名前git blameの変更を発見しoldname、コミットQと以前に検索を開始するので、比較するPQファイルoldnameoldnameその2つのコミットで比較します。

一方、シーケンスが commit で始まるようにgit blame R^ -- newname(または) を実行すると、そのコミットにはファイルがなく、とを比較するときに名前の変更がなく、単にあきらめます。git blame Q -- newnameQnewnamePQgit blame

トリックは、ファイルに以前の名前があったコミットから開始する場合、git に古い名前を付ける必要があることです。

git blame R^ -- oldname

その後、すべてが再び機能します。


1git diffドキュメントには、名前の変更を検出する方法-Mを制御するオプションがあることがわかります。コードはこれを少し変更し (実際には 2 つのパスを実行します。1 つはオフに、もう 1 つはオンにします)、独自の (異なる)オプションを多少異なる目的で使用しますが、最終的にはこの同じコードを使用しています。 git diffblame-M-M-M


[コメントへの返信を追加するための編集(コメント自体としては適合しませんでした)]:

次のようなファイルの名前変更を表示できるツールはありますか: git renames <filename> SHA date oldname->newname

正確ではありませんが、git diff -Mかなり近いですし、十分に近いかもしれません。

ここでの「SHA 日付」の意味がわかりませんが、git diff -M2 つの SHA-1 を指定して左右を比較できます。--name-statusファイル名と性質だけを取得するには、追加します。したがって、 からに変換するには、ファイルの名前を変更する必要があると gitが判断し、古い名前を「新しい」名前として報告するgit diff -M --name-status HEAD oldsha1 可能性があります。たとえば、git リポジトリ自体には、以前はわずかに異なる名前だったファイルがあります。HEADoldsha1RDocumentation/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
[...]

それが気になるファイルであれば、問題ありません。ここでの 2 つの問題は次のとおりです。

  • SHA1 を見つける: どこ992cb206から来たの? すでに SHA-1 を持っている場合、それは簡単です。そうでない場合git rev-listは、SHA1 検出ツールです。そのドキュメントを読んでください。
  • そして、コミットごとに一連の名前変更を一度に 1 つずつ行うという事実は、そうであるように、ずっと後のコミット ( ) とずっと前のコミット (または何でも)git blameを比較するよりも、まったく異なる答えを生み出す可能性があります。この場合、結果は同じですが、ここでの「類似度指数」は 100 点中 97 です。中間ステップのいくつかでさらに変更された場合、その類似度指数は 50% を下回る可能性があります ...それでも、少し後のリビジョンを(そうであるように) 比較すると、おそらく、これら 2 つのファイル間の類似性指数が高くなる可能性があります。HEAD992cb206992cb206992cb206git blame

必要な (そして不足している) ことは、git rev-listそれ自体が を実装することです。これにより、内部--followで使用するすべてのコマンドgit rev-list(つまり、複数のリビジョンで動作するほとんどのコマンド) がそのトリックを実行できるようになります。途中で、それが別の方向に機能するならいいと思います (現在は--follow新しいものから古いものへのみです。つまり、 で問題なく動作し、 で最初に最も古い履歴を要求しない限り、 で問題なく動作します)。git blamegit log--reverse

于 2015-04-06T09:46:15.040 に答える