Часть этого вопроса о Git Расширения , графический интерфейс пользователя. Я не использовал это и не могу ничего ответить об этом. Другая часть этого вопроса, однако, заключается в том, что означают / показывают эти различия, и это основа c командная строка Git.
Помните, каждый коммит в Git содержит полный снимок всех ваших файлов. Для регулярных (без слияния) коммитов достаточно просто увидеть, как это работает. Каждый коммит имеет родительский коммит, и поэтому коммиты соединяются друг с другом в одну линию, которую мы можем нарисовать:
...---F--G--H <-- master
или:
* 9fadedd637 (master) a commit message subject
|
* 3bab5d5625 some other commit message subject
|
* 1c56d6f57a this commit does something or other
|
:
как показано, например, git log --graph
(вышеприведенный вывод моделируется и не совсем то, что показывал бы --graph
, но достаточно близко).
К представлению коммита, мы можем либо проверьте его, чтобы у нас были все файлы в рабочем дереве, где мы можем их видеть; или мы можем попросить Git до показать коммит, сравнив его с его родителем. Чтобы увидеть коммит H
, на кончике master
мы имеем Git, извлекающие G
и H
во временную область (в памяти) и , сравни им.
Некоторые файлы в G
соответствуют своим аналогам в H
. Git вообще ничего не говорит об этих файлах. Некоторые файлы не не совпадают; для них Git показывает нам рецепт - список различий - по которому мы могли бы изменить файл в G
так, чтобы он совпадал с копией в H
.
Коммиты слияния имеют более одного родителя. При рисовании их с новыми коммитами вправо, как я обычно делаю в StackOverflow, мы получаем что-то вроде этого:
I--J
/ \
...--G--H M--N--...
\ /
K--L
Commit M
- это слияние с родителями J
и L
.
Слияние похоже на любой другой коммит: оно содержит полный снимок, а не набор изменений. Но мы можем просмотреть это при изменении , сравнив снимок в M
со снимком в J
:
git diff <hash-of-J> <hash-of-M>
делает именно это, для instance.
Мы также можем рассматривать это как изменения, сравнивая L
против M
:
git diff <hash-of-L> <hash-of-M>
Любой diff покажет изменения . Изменения, которые они покажут, разные.
Так как M
- это коммит слияния, то, что мы увидим, глядя на J
-vs- M
, это изменения, которые мы внесли из работы, выполненной вдоль строки bottom , в коммитах K
и L
. Если мы сравним L
-vs- M
, то увидим изменения, которые мы внесли из работы, проделанной вдоль строки top , в коммиты I
и J
. Обратите внимание, что, возможно, L
- это, например, вишня I
, так что работа дублируется. В этом случае объединение заняло только одну копию этих изменений, а не две копии, поэтому не будет видеть, что в или этих двух различий.
A комбинированный diff - это нечто совершенно другое. Это, на мой взгляд, попытка быть полезной, которая не так успешна, как хотелось бы. Git комбинированный дифференциал сначала запускает каждый отдельный дифференциал - в данном случае J
против M
и L
против M
, а затем выбрасывает из этот diff любой файл, который не вообще изменился в другом diff.
То есть, предположим, что в H
у нас было четыре файла, каждый из которых также находится в I
, J
, K
, L
и M
, и имеют место следующие свойства:
same.txt
одинаково во всех шести коммитах. top-only.txt
изменяется в I
и / или J
, но не в K
и / или L
. bot-only.txt
изменяется в K
и / или L
, но не в I
и / или J
. both.txt
изменяется в J
, а также в L
.
Мы также будем предполагать, что M
не является злым слиянием (см. Зло сливается в git? ). То есть, H
-vs- M
, если мы запустим этот diff, не покажет любые изменения, которые не были получены через I
, J
, K
или L
. Более того, мы предполагаем, что изменения в both.txt
не перекрывались вообще, поэтому объединение объединило оба изменения: никаких конфликтов, никаких других проблем.
Очевидно, что разница J
против M
ничего не показывает для same.txt
: он не был затронут ни в одном из пяти коммитов, следующих после H
.
Различение J
против M
не показывает top-only.txt
потому что копия в M
совпадает с копией в J
. Изменения в этом файле произошли в I
и / или J
, т. Е. Сравнение H
с J
показывает изменения, но эти изменения в H
версии top-only.txt
, что приводит к версии J
top-only.txt
, применяются к версии H
для получения версии M
.
Diffing L
против M
не показывает bot-only.txt
для той же причине. Опять же, M
копия bot-only.txt
соответствует копии L
.
Только для both.txt
есть различия между J
и M
и различиями L
и M
.
Если мы спросим Git о комбинированном diff представлении M
, Git теперь будет:
- diff
J
vs M
: изменены два файла: top-only.txt
и both.txt
; и - diff
L
против M
: два файла изменены: bot-only.txt
и both.txt
.
Таким образом, в списке есть три файла, по одному в обоих файлах. и один только в одном каждый. Комбинированный diff теперь выбрасывает файлы only-in-one-diff, оставляя только один файл both.txt
.
На данный момент, у вас есть возможность двух разных видов комбинированный дифференциал:
git diff --cc
(по умолчанию: обратите внимание на две черточки и две c
с): это печатает ничто . git diff -c
( обратите внимание на один da sh и один c
): это показывает разницу для both.txt
.
Значение --cc
действительно имеет смысл. Идея в том, чтобы показать вам, где произошел конфликт слияния . Поскольку не было конфликтом слияния, он ничего не показывает. (Для случая, когда он действительно что-то показывает, см. раздел комбинированного различий в документации git diff
.)
-c
тоже имеет смысл, за исключением одного: он не показывает ни top-only.txt
, ни bot-only.txt
. Что нам действительно нужно здесь, но у Git нет, так это способ сравнить снимок результата слияния со снимком base merge . То есть мы могли бы посмотреть H
против M
. Нет встроенной операции для этого, несмотря на то, что я думаю, что, вероятно, должно быть.
Вывод git show -c
, который запускает этот вид комбинированного diff, в тестовом репозитории, который я сделал здесь, это:
commit 54627dbf51ab9b27c8e5486e4755651688dd5d21 (HEAD -> merge)
Merge: da92a96 e77c224
[snip]
diff --combined both.txt
index 5d51eed,506867f..1f3f391
--- a/both.txt
+++ b/both.txt
@@@ -2,10 -2,10 +2,11 @@@ this fil
will be
modified in
both branches.
+here is the top-branch change
The changes
will be
combined into
one file
in the merge result.
+ here is the bottom-branch change
Мы можем сделать это вручную. Смотрите скрипт ниже (с именем git-show-merge
, чтобы я мог запустить его как git show-merge
). Это в основном находит базу слияния от родителей указанного слияния (или HEAD
по умолчанию), затем запускает требуемый diff.
Git также имеет опцию -m
, доступную для git show
и git log
оба. Этот флаг эффективно «разбивает» слияние на отдельные «виртуальные коммиты». Каждый из них является коммитом с одним родителем, один из родителей которого является одним из родителей фактического слияния. В этом случае механизм внутренних различий Git хорошо справляется с дифференцированием, которое фиксируется как обычный дифференциал, чтобы показать вам, какие файлы были изменены.
В этом случае git show -m <hash-of-M>
будет дифференцировать J
-vs- M
сначала, затем L
-vs- M
секунд и покажем вам оба различия.
Похоже, Git Расширения не имеют опции -m
, но имеют возможность показывать либо J
-vs- M
diff, либо L
-vs- M
diff, либо - если вы выберете его - один из форматов комбинированного diff, вероятно, --cc
вариант.
Обратите внимание, что git log -p
по умолчанию не пытается , чтобы показать слияние. Используйте явную опцию -c
или --cc
, чтобы заставить git log
показывать комбинированную разницу.
#! /bin/sh
USAGE="[commit] the merge commit to show - default is HEAD"
. git-sh-setup
case $# in
0) set HEAD;;
1) ;;
*) usage;;
esac
commit=$1
hash=$(git rev-parse ${commit}^{commit}) || exit
set -- $(git rev-parse $hash^@)
[ $# -ge 2 ] || die "$commit ($hash) is not a merge"
set -- $(git merge-base --all $@)
case $# in
0) die "parents of ${commit} are unrelated";;
1) ;;
*) echo "warning: more than one merge base, diffing against $1";;
esac
git diff $1 $hash