git diff переименованный файл

105

У меня есть файл a.txt.

cat a.txt
> hello

Содержимое a.txt- «привет».

Я беру на себя обязательство.

git add a.txt
git commit -m "first commit"

Затем я перехожу a.txtв testкаталог.

mkdir test
mv a.txt test

Затем я совершаю вторую фиксацию.

git add -A
git commit -m "second commit"

Наконец, я редактирую, a.txtчтобы вместо этого сказать «до свидания».

cat a.txt
> goodbye

Я делаю последний коммит.

git add a.txt
git commit -m "final commit"

Вот мой вопрос:

Как мне различать содержимое a.txtмежду моей последней фиксацией и моей первой фиксацией?

Я пробовал:, git diff HEAD^^..HEAD -M a.txtно это не сработало. git log --follow a.txtправильно определяет переименование, но я не могу найти эквивалент для git diff. Есть один?

Кен Хиракава
источник
Полагаю, было бы то же самое, если бы вы сделали «тест git mv a.txt»? IE вы просто переименовали файл вместо того, чтобы переместить его в подкаталог.
Макс Уотерман,

Ответы:

107

Проблема с разницей между HEAD^^и HEADзаключается в том, что у вас есть a.txtкоммиты в обоих, поэтому, просто учитывая эти два коммита (что и делает diff), переименования нет, есть копия и изменение.

Для обнаружения копий вы можете использовать -C:

git diff -C HEAD^^ HEAD

Результат:

index ce01362..dd7e1c6 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1 @@
-hello
+goodbye
diff --git a/a.txt b/test/a.txt
similarity index 100%
copy from a.txt
copy to test/a.txt

Между прочим, если вы ограничиваете свое различие только одним путем (как и в случае с git diff HEAD^^ HEAD a.txtвами, вы никогда не увидите переименований или копий, потому что вы исключили все, кроме одного пути, и переименовываете или копируете - по определению - включаете два пути.

CB Bailey
источник
2
Допустим, фиксация содержала несколько изменений файла. Как мне сузить его до разницы в одном файле?
Кен Хиракава,
3
@KenHirakawa используйте -- <old-path> <new-path>... см. Мой ответ.
Нолан Эми
В моем случае я хотел показать детали фиксации, в которой файл был переименован, но это просто показывало мне, что файл был удален и файл был добавлен ... Я побежал, git show -C [commit]и он распознал переименование файла и показал мне diff между файлами. Отлично.
Джейсон
83

Чтобы различать переименование определенного файла, используйте -M -- <old-path> <new-path>( -Cтакже работает).

Итак, если вы переименовали и изменили файл в последней фиксации, вы можете увидеть изменения с помощью:

git diff HEAD^ HEAD -M -- a.txt test/a.txt

Это производит:

diff --git a/a.txt b/test/a.txt
similarity index 55%
rename from a.txt
rename to test/a.txt
index 3f855b5..949dd15 100644
--- a/a.txt
+++ b/test/a.txt
@@ -1,3 +1,3 @@
 // a.txt

-hello
+goodbye

( // a.txtдобавлены строки, чтобы помочь git обнаружить переименование)


Если git не обнаруживает переименование, вы можете указать низкий порог схожести -M[=n], скажем, 1%:

git diff HEAD^ HEAD -M01 -- a.txt test/a.txt

Из документации git diff :

-M [<n>] --find-renames [= <n>]

Обнаружение переименований. Если nуказан, то это порог индекса сходства (т. Е. Количества добавлений / удалений по сравнению с размером файла). Например, это -M90%означает, что Git должен рассматривать пару удаления / добавления как переименование, если более 90% файла не изменилось. Без %знака число следует читать как дробь с десятичной точкой перед ним. То есть -M5становится 0,5 и, таким образом, то же самое, что -M50%. Точно так -M05же, как -M5%. Чтобы ограничить обнаружение точным переименованием, используйте -M100%. Индекс подобия по умолчанию составляет 50%.

Нолан Эми
источник
... Конечно, также работает, когда изменение и переименование находятся в отдельных коммитах, как в исходном примере:git diff HEAD^^ HEAD -M -- a.txt test/a.txt
Нолан Эми
1
Только тогда, когда содержимое файлов достаточно близко для того, чтобы diff дал индекс подобия. Использование обоих параметров (-M и -C) показывает разницу в / dev / null и из / dev / null в файле, который был переименован и полностью изменился (включая отступы), он даже не улавливает его при игнорировании пробел.
DavidG
37

Вы также можете:

git diff rev1:file1 rev2:file2

который, для вашего примера, будет

git diff HEAD^^:./a.txt HEAD:./test/a.txt

Обратите внимание на явный ./- этот формат в противном случае предполагает, что пути будут относиться к корню репо. (Если вы находитесь в корне репо, вы, конечно, можете опустить это.)

Это вообще не зависит от обнаружения переименования, поскольку пользователь явно указывает, что именно сравнивать. (Следовательно, это также может быть полезно в некоторых других обстоятельствах, например, при сравнении файлов между разными ветвями svn в среде git-svn.)

Benkc
источник
1
Ооо, это здорово!
Нолан Эми
Для переименования с дополнительными изменениями файла, внесенными через слияние, это единственный ответ, который сработал для меня. Спасибо!
Лейн Реттиг
1

Если ваша фиксация переименования поставлена, но еще не зафиксирована, вы можете использовать:

git diff --cached -M -- file.txt renamed_file.txt
Г-н-IDE
источник