Почему значения .. и ... переключаются между git diff и git log? - PullRequest
0 голосов
/ 09 мая 2018

Возьмем, к примеру, репозиторий с таким графиком истории:

$ git log --all --graph --oneline
* 691d454 (HEAD -> branch-b) branch-b-2
* c3bd488 branch-b
| * f5756ff (branch-a) branch-a-2
| * de00ec4 branch-a
|/  
* 5ad3b15 (root) root

Теперь сравните git diff branch-a..branch-b

diff --git a/f b/f
index af31a64..e7791f3 100644
--- a/f
+++ b/f
@@ -1 +1 @@
-branch-a-2
+branch-b-2

с git diff branch-a...branch-b

diff --git a/f b/f
index d8649da..e7791f3 100644
--- a/f
+++ b/f
@@ -1 +1 @@
-root
+branch-b-2

с git log --oneline branch-a..branch-b

691d454 (HEAD -> branch-b) branch-b-2
c3bd488 branch-b

с git log --oneline branch-a...branch-b

691d454 (HEAD -> branch-b) branch-b-2
c3bd488 branch-b
de00ec4 branch-a
f5756ff (branch-a) branch-a-2

По сути, мы видим это:

|------+--------------------------------------+--------------------------------------|
|      | branch-a..branch-b                   | branch-a...branch-b                  |
|------+--------------------------------------+--------------------------------------|
| log  | root -> branch-b                     | branch-a -> branch-b                 |
|      | (root, branch-b]                     | [branch-a, branch-b]                 |
|      | changes introduced in branch-b       | difference from branch-a to branch-b |
|------+--------------------------------------+--------------------------------------|
| diff | branch-a -> branch-b                 | root -> branch-b                     |
|      | [branch-a, branch-b]                 | (root, branch-b]                     |
|      | difference from branch-a to branch-b | changes introduced in branch-b       |
|------+--------------------------------------+--------------------------------------|

Что это означает, что если вы хотите увидеть изменения, сделанные в ветке, которая отходит от мастера в виде списка коммитов, git log master..branch покажет вам. Однако, если вы хотите увидеть те же самые изменения в виде diff, который объединяет эти коммиты, вам придется переключиться на ..., например, git diff master...branch. Это то, что я делаю очень часто, так же как и небольшое раздражение, о котором я давно задумывался.

Есть ли причина дизайна? Кажется, это лишь несоответствие.

РЕДАКТИРОВАТЬ: Чтобы уточнить, я знаю, что .. и ... переводить как в diff и log. Мой вопрос можно перефразировать следующим образом: не будет ли больше смысла переворачивать их, чтобы они соответствовали друг другу? Например, если git diff branch-a..branch-b означает git diff $(git merge-base branch-a branch-b) branch-b, а git diff branch-a...branch-b означает git diff branch-a branch-b, то diff будет соответствовать log. Так почему же это не так?

РЕДАКТИРОВАТЬ 2: В абзаце ниже таблицы я привел практический пример того, как git diff master...branch является аналогом git log master..branch. В другом случае, если вы хотите получить список коммитов, который отделяет master от branch (т.е. коммиты, которые представляют разницу между двумя), вы должны использовать git log master...branch, и если вы хотите diff это та же разница, вы бы использовали git diff master..branch.

РЕДАКТИРОВАТЬ 3: Я пытался сделать таблицу более понятной. В скобках и скобках используется математическая интервальная запись .

РЕДАКТИРОВАТЬ 4: транспонировать таблицу, чтобы она лучше сидела.

1 Ответ

0 голосов
/ 09 мая 2018

Они не (перевернутые). Скорее, .. в git diff имеет специальное значение, которое имеет значение no , а ... в git diff имеет специальное значение, уникальное для git diff, не встречающееся ни в одной другой команде Git. .

Ваш конкретный входной график - это ... был , до редактирования :-) ... слишком мал, чтобы показать реальную разницу. Если у вас было больше коммитов после того, как две ветви разошлись, вы могли бы увидеть больше.

Long

Давайте сначала посмотрим на git diff, потому что это проще. Что git diff делает в этих конкретных случаях - есть особые случаи для комбинированных различий , но они здесь не применяются; и мы можем различить коммит против нашего рабочего дерева или против нашего индекса, который мы здесь не делаем - это выбрать два отдельных коммита и сравнить их .

Чтобы назвать два коммита для сравнения, я предпочитаю написать:

git diff <commit1> <commit2>

Однако вы можете, если хотите, написать:

git diff <commit1>..<commit2>

Это означает точно то же самое , всегда. 1 Git просто делает вид, что вы не написали .. в конце концов. Это верно, используете ли вы здесь хэш-идентификаторы, или имена ветвей, такие как master, или имена для удаленного отслеживания, такие как origin/master, или любое их сочетание.

Обратите внимание, что порядок двух имен-или-хеш-идентификаторов коммитов имеет значение: вывод git diff представляет собой набор инструкций, сообщающих вам, как изменить первый коммит, чтобы получить второй коммит, если Вы следовали всем инструкциям.

Если вы напишите:

git diff <commit1>...<commit2>

Git будет все еще сравнить два коммита: 2 Git найдет базу слияния между двумя данными коммитами, затем сравнит этот базовый коммит слияния со вторым указанный коммит. То есть это то же самое, что и расширение в стиле оболочки:

git diff $(git merge-base <commit1> <commit2>) commit2

Здесь, как и прежде, порядок имеет значение: база слияния одна и та же, 3 независимо от порядка двух коммитов, но эта база слияния сравнивается со вторым в списке коммитом.


1 Ну, почти всегда! Есть несколько угловых случаев, которые вы можете отметить, если вы перечислите третий коммит в командной строке, где он действует так, как если бы вы использовали трехточечный синтаксис. Я бы сказал, что это ошибка в git diff, хотя всегда возможно, что кто-то зависит от них.

2 Здесь есть еще один угловой случай, и он определенно является ошибкой: если имеется более одной базы слияния, git diff создает комбинированный diff. Это не цель трехточечного синтаксиса, поэтому это ошибка.

3 Предполагается, что вы не отметите ошибку в сноске 2. Если для нескольких коммитов существует одна база слияния, порядок не имеет значения, но при наличии нескольких баз слияния порядок входы могут влиять на порядок выходов.


Для git log и большинства других команд Git, однако, .. и ... имеют совершенно другое значение. Как вы, без сомнения, знаете, <commit1>..<commit2> - это сокращение от <commit2> ^<commit1>. Мы можем видеть это с git rev-parse:

$ git rev-parse master..origin/maint
468165c1d8a442994a825f3684528361727cd8c0
^ccdcbd54c4475c2238b310f7113ab3075b5abc9c

Здесь origin/maint переводится в первый (неотрицательный) хэш-идентификатор, а master переводится во второй (отрицательный). Они сообщают Git, что во время обхода графика коммитов он должен выбирать коммиты, которые достижимы из 468165c1d8a442994a825f3684528361727cd8c0 (положительная ссылка), а отклонять коммиты, которые достижимы из ccdcbd54c4475c2238b310f7113ab3075b5abc9c (отрицательная ссылка). Это означает все таких коммитов, которых может быть много. Команда типа git log, проходящая по графику фиксации, покажет все выбранные фиксации.

Трехточечная запись сложнее, но внутренне переводит на один и тот же тип положительной и отрицательной ссылки:

$ git rev-parse master...origin/maint
468165c1d8a442994a825f3684528361727cd8c0
ccdcbd54c4475c2238b310f7113ab3075b5abc9c
^468165c1d8a442994a825f3684528361727cd8c0

Здесь две положительные ссылки - это идентификаторы хеша, связанные с origin/maint и master, как и раньше, а отрицательная ссылка - это результат точки (точек), в которой подграфы, выбранные этими начальными точками, воссоединяются. В этом конкретном случае есть только одна такая точка - так что это соответствует выводу git merge-base, как мы увидим через мгновение - хотя некоторые подграфы, которые расходятся и объединяются сложным образом, могут иметь несколько таких точек:

$ git merge-base master origin/maint
468165c1d8a442994a825f3684528361727cd8c0

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

Если у вас есть такой график:

     C--D   <-- br1
    /
A--B
    \
     E--F   <-- br2

синтаксис из трех точек br1...br2 выбирает коммиты C-D и E-F для команд, которые идут по графику, как git log, но выбирает коммиты B и F соответственно для git diffтолько для git diff). Синтаксис из двух точек br1..br2 выбирает коммиты E-F для git log и т.п. и сравнивает коммиты D и F соответственно для git diffтолько для git diff) .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...