Как мне разнести один и тот же файл между двумя разными коммитами в одной ветке?

1144

В Git, как я могу сравнить один и тот же файл между двумя разными коммитами (не смежными) в одной и той же ветке (например, master)?

Я ищу функцию сравнения, например, в Visual SourceSafe (VSS) или Team Foundation Server (TFS).
Возможно ли это в Git?

systempuntoout
источник

Ответы:

1477

Из git-diffсправочной страницы:

git diff [--options] <commit> <commit> [--] [<path>...]

Например, чтобы увидеть разницу для файла "main.c" между now и двумя коммитами назад, вот три эквивалентные команды:

$ git diff HEAD^^ HEAD main.c
$ git diff HEAD^^..HEAD -- main.c
$ git diff HEAD~2 HEAD -- main.c
mipadi
источник
43
На ..самом деле в этом нет необходимости, хотя он будет работать с ним (за исключением довольно старых версий, может быть). Вы также можете использовать git logили, gitkчтобы найти SHA1 для использования, если два коммита будут очень далеко друг от друга. gitkтакже имеет «diff selected -> this» и «diff this -> selected» в контекстном меню.
Каскабель
17
Будет ли это работать, даже если имя файла было изменено между 2 коммитами?
reubenjohn
26
Так в чем же цель "-"
user64141
29
@ user64141 Это --полезно, например, когда у вас есть файл с именем -p. Хорошо использовать в сценариях, только в редких случаях, необходимых на практике.
Палек
13
Примечание: вам нужно использовать пути относительно корня репо. Пути относительно текущего рабочего каталога не будут работать.
Кевин Уилер
282

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

git diff <revision_1>:<file_1> <revision_2>:<file_2>

Якуб Наребски
источник
25
обратите внимание, что это выглядит так, как если бы <file_1>и <file_2>находились в текущем каталоге, а не в верхнем уровне каталога, управляемого git, в ./Unix нужно добавить :<revision_1>:./filename_1
Andre Holzner
7
<revision>: может быть опущен, так что вы можете сравнить с файлом, который еще не был зафиксирован.
Ярослав Никитенко
2
Обратите внимание, что в Windows нужно использовать «/» для путей к файлам, а не «\».
np8
88

Если вы настроили «difftool», вы можете использовать

git difftool revision_1:file_1 revision_2:file_2

Пример: сравнение файла из его последнего коммита с его предыдущим коммитом в той же ветке: при условии, что если вы находитесь в корневой папке вашего проекта

$git difftool HEAD:src/main/java/com.xyz.test/MyApp.java HEAD^:src/main/java/com.xyz.test/MyApp.java

У вас должны быть следующие записи в вашем ~ / .gitconfig или в файле проекта / .git / config. Установите p4merge [Это мой любимый инструмент сравнения и слияния]

[merge]
    tool = p4merge
    keepBackup = false
[diff]
    tool = p4merge
    keepBackup = false
[difftool "p4merge"]
    path = C:/Program Files (x86)/Perforce/p4merge.exe
[mergetool]
    keepBackup = false
[difftool]
    keepBackup = false
[mergetool "p4merge"]
    path = C:/Program Files (x86)/Perforce/p4merge.exe
    cmd = p4merge.exe \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"
Анвер Садхат
источник
50

Проверьте $ git log, скопируйте идентификатор SHA-1 двух разных коммитов и выполните git diffкоманду с этими идентификаторами. например:

$ git diff (sha-id-one) (sha-id-two)
Вибхути
источник
18
Если вы хотите использовать diff для определенного файла, добавьте путь к нему в конце команды.
hBrent
Также выполните «git pull», чтобы загрузить полную информацию о дереве, если два коммита находятся в разных ветвях. В противном случае вы получите ошибку «fatal: bad object».
user238607
4
git diff (sha-id-one) (sha-id-two) -- filename.ext без имени файла он будет отображать различия всех файлов в этих двух коммитах.
SherylHohman
40

Если вы хотите увидеть все изменения в файле между двумя коммитами на основе коммитов, вы также можете сделать

git log -u $start_commit..$end_commit -- path/to/file

cxreg
источник
Что такое "$ start_commit" и "$ end_commit"? Являются ли они буквальными или нет, можете ли вы привести пример?
Питер Мортенсен
Это переменные оболочки, содержащие начальную и конечную ревизии, которые могут быть литералами sha1 или refs
cxreg
21

Вот скрипт Perl, который выводит команды Git diff для данного файла, как это указано в команде журнала Git.

Например

git log pom.xml | perl gldiff.pl 3 pom.xml

Урожайность:

git diff 5cc287:pom.xml e8e420:pom.xml
git diff 3aa914:pom.xml 7476e1:pom.xml
git diff 422bfd:pom.xml f92ad8:pom.xml

который затем может быть вырезан и вставлен в сеансе окна оболочки или передан по трубопроводу /bin/sh.

Ноты:

  1. число (в данном случае 3) указывает, сколько строк будет напечатано
  2. файл (в данном случае pom.xml) должен совпадать в обоих местах (вы можете обернуть его в функцию оболочки, чтобы обеспечить одинаковый файл в обоих местах) или поместить его в двоичный каталог как скрипт оболочки

Код:

# gldiff.pl
use strict;

my $max  = shift;
my $file = shift;

die "not a number" unless $max =~ m/\d+/;
die "not a file"   unless -f $file;

my $count;
my @lines;

while (<>) {
    chomp;
    next unless s/^commit\s+(.*)//;
    my $commit = $1;
    push @lines, sprintf "%s:%s", substr($commit,0,6),$file;
    if (@lines == 2) {
        printf "git diff %s %s\n", @lines;
        @lines = ();
    }
    last if ++$count >= $max *2;
}
user2520657
источник
14

Если вы хотите сделать diff с более чем одним файлом, используя метод, указанный в @mipadi:

Например, diff между HEADи your master, чтобы найти все .coffeeфайлы:

git diff master..HEAD -- `find your_search_folder/ -name '*.coffee'`

Это будет рекурсивно искать your_search_folder/все ваши .coffeeфайлы и проводить различие между ними и их masterверсиями.

Андрей-Никулае Петре
источник
13

Если у вас есть несколько файлов или каталогов и вы хотите сравнить непостоянные коммиты, вы можете сделать это:

Создайте временную ветку ( в этом примере "revision" )

git checkout -b revision

Перемотать на первую цель коммита

git reset --hard <commit_target>

Вишня ковыряется заинтересованным

git cherry-pick <commit_interested> ...

Применить diff

git diff <commit-target>^

Когда вы сделали

git branch -D revision
dvdvck
источник
2
Спасибо за это решение. Это хорошо сработало для моего варианта использования. Единственное, что я хотел бы обновить, это то, что когда вы закончите, вы не сможете удалить ветку, пока не отключите ее.
Стивен Дикс
9

Просто еще один способ использовать удивительность Git ...

git difftool HEAD HEAD@{N} /PATH/FILE.ext
Эдди Б
источник
Я определил псевдоним из этого ответа, который работает с bash:difftool-file = "!git difftool HEAD@{\"$2\"} HEAD \"$1\" #"
blueogive
2

Если вам нужно простое визуальное сравнение в Windows, например, в Visual SourceSafe или Team Foundation Server (TFS), попробуйте следующее:

  • щелкните правой кнопкой мыши файл в проводнике
  • выберите «История Git»

Примечание: после обновления до Windows 10 я потерял опции контекстного меню Git. Тем не менее, вы можете добиться того же, используя «gitk» или «gitk filename» в командном окне.

Как только вы вызовете «Git History», запустится инструмент Git GUI с историей файла в верхней левой панели. Выберите одну из версий, которую вы хотите сравнить. Затем щелкните правой кнопкой мыши на второй версии и выберите либо

Diff this -> selected

или

Diff selected -> this

Различия с цветовой кодировкой появятся в нижней левой панели.

Ресурс
источник
Примечание для downvoters: это простое решение, простое в реализации и решающее проблему ОП. Он работает на Windows, которую явно использует OP (см. Ссылки на TFS и VSS в вопросе).
Ресурс