git разница между мастером и инсценировкой - PullRequest
0 голосов
/ 15 января 2020

Если я делаю

git diff --staged

, он сравнивает поэтапно с HEAD.

Если я делаю

git diff master...HEAD

, он сравнивает мастера с моей веткой (в GitHub PR fashion)

Как объединить оба, чтобы сравнить мастер и инсценировку?

Ответы [ 3 ]

1 голос
/ 15 января 2020

TL; DR

Невозможно сравнить третий коммит, который вы не указали напрямую, с областью подготовки без использования хотя бы одной дополнительной команды Git. Если вы хотите выбрать базу слияния между коммитом подсказки ветви, заданным именем ветви B, и содержимым области index / staging, вы должны использовать две команды Git. Например, в качестве двухстрочного сценария оболочки вы можете сделать:

base=$(git merge-base <B> HEAD)
git diff --cached $base            # or --staged

Вы можете создать псевдоним Git, который делает это в одной строке, например:

git config --global alias.bvs='!f() { git diff --staged $(git merge-base $1 HEAD); } f'

(с именем bvs, обозначающим Base Vs Staged), если вы хотите сократить, сколько вы должны набрать. (Этот псевдоним слегка ошибочен, так как он не проверяет, что вы фактически дали ровно один аргумент. Он также не проверяет, существует ли ровно одна такая фиксация базы слияния, но не проверяет git diff; см. Ниже.)

Long-i sh

Будьте осторожны с пунктирным синтаксисом: git diff обрабатывает его особенно.

Вы можете:

git diff
git diff --staged                # or --cached, exactly the same thing
git diff <commit>
git diff --cached <commit>
git diff <commit1> <commit2>
git diff <commit1>..<commit2>    # note two dots
git diff <commit1>...<commit2>   # note three dots

(и даже больше! - но «больше» случаев не попадают в эту основную модель). В каждом из этих различных случаев вы выбираете две вещи для сравнения:

  • commit1 vs commit2, для случаев, которые действительно используют два спецификатора коммита, но не используют три точки
  • commit vs work-tree, для случаев, когда вы называете один коммит и не используете --staged
  • commit против index / staging-area, для случаев, когда вы называете один коммит и используете --staged или --cache

Пока что все они используют указанный вами коммит или оба коммита, которые вы указали . Но:

git diff <commit1>...<commit2>   # note three dots

фактически сравнивает некоторые третий коммит с commit2.

Третий коммит, выбранный Git, основан на результат выполнения:

git merge-base --all <commit1> <commit2>

или (ближе к тому, что на самом деле делает git diff):

git rev-parse <commit1>...<commit2>

Команда git merge-base находит все базы слияния из двух указанных коммитов, основанных на графе коммитов. График коммитов является результатом интерпретации всех сохраненных родителей коммитов. Диаграммы в принятом ответе на вопрос, на который вы ссылались в комментарии , помогают визуально объяснить основы слияния. (Эта ссылка, вероятно, должна была быть в вашем исходном вопросе 1 , поскольку она помогает определить проблему.)

Если вы запустите фактический git rev-parse, показанный здесь, вы увидите вывод как this:

$ git rev-parse master...origin/maint
da72936f544fec5a335e66432610e4cef4430991
083378cc35c4dbcc607e4cdd24a5fca440163d17
^da72936f544fec5a335e66432610e4cef4430991

Это текстовое представление rev-parse для:

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

База слияния является Коммит, достижимый из обоих коммитов. Он более ограничен: это лучший такой коммит . 2 Может быть более одного "лучшего" коммита, и в этом случае git merge-base --all найдет их все. Будет действовать и команда git rev-parse: каждая из них должна быть исключена с префиксом ^.

Что git diff A...B с тремя точками делает для вызова внутреннего эквивалента git rev-parse, чтобы найти все базы слияния. (Вы можете сделать это самостоятельно с помощью git merge-base --all или с помощью git rev-parse, хотя вы не получаете коммиты со всеми внутренними флагами, которые использует внутренний интерфейс.) Затем, заметив, что есть некоторые положительно выбранные коммиты и в хотя бы один отрицательно выбранный коммит, git diff выбирает один из отрицательно выбранных коммитов, по-видимому, случайным образом, из списка. Поэтому, если git rev-parse производит одну базу слияния, git diff выбирает эту одну базу слияния. Если git rev-parse перечисляет два или более, git diff все равно просто выбирает один, вместо того, чтобы выдавать предупреждение или ошибку.

Чтобы сделать это вручную, просто запустите git merge-base без --all, что по сути делает то же самое. 3 Так вот, что мы должны сделать.

Найдя базу слияния, git diff теперь сравнивает коммит базы слияния с правой стороной commit ha sh (вторая строка вывода git rev-parse). Так что это тоже то, что мы должны делать.

Это приводит к двухэтапной операции, которую мы выписали, а затем оболочка выполнила одну команду командной строки (которая запускает две команды Git) в псевдониме.


1 Обратите внимание, что в своем вопросе вы сказали:

git diff master...HEAD ... сравнивает мастер с моей веткой (в мода GitHub PR)

На самом деле сравнение GitHub не совсем не так, и я понятия не имею, как они производят именно то, что производят. Но этот, то есть трехточечный синтаксис, использует базу слияния.

2 Определение best немного сложнее. Есть два хороших теоретических определения графа самого низкого общего предка графа, оба из статьи в Журнале алгоритмов , написанной Бендером, Майклом А и Фарах-Колтоном, Мартином и Пеммасани, Гиридхаром и Шиеной, Стивеном и Сумазин, Павел под названием Самые низкие общие предки на деревьях и направленные ациклы c графы:

Определение 1. Пусть G = (V, E) - DAG, и пусть x , y V . Пусть G x, y будет подграфом G , индуцированным множеством всех общих предков x и y . Определите SLCA ( x , y ) как набор узлов (листков) нулевой степени в G x, y . Самые низкие общие предки x и y являются элементами SLCA ( x , y ).

Определение 2. Для любого DAG G = (V, E) мы определим частично упорядоченный набор S = (V, ≼) следующим образом: элемент i ≼ j тогда и только тогда, когда i = j или (i, j) находится в переходном замыкании G tr из G . Пусть SLA C ( x , y ) будет набором максимальных элементов набора общих предков { z | z x z y } ⊆ V . Самые низкие общие предки x и y являются элементами SLA C ( x , y ).

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

1 голос
/ 15 января 2020
0 голосов
/ 15 января 2020

git diff master, git diff --staged master и git diff --cached master даст тот же результат из другой ветви, что и master, он примет во внимание ваши поэтапные изменения.

Если, наоборот, вы хотели diff без с учетом поэтапных изменений, тогда вам придется использовать git diff HEAD...master

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