Теперь предположим, что коммит C
был протестирован и готов к слиянию, поэтому мы используем git switch master; git merge feature-1 --squash
.
master A------C'
\ /
feature-1 B--C
\
feature-2 D--E
Этот рисунок не совсем правильный: он должен читаться так, как я нарисовал ниже. Обратите внимание: я также переместил имена вправо по причинам, которые вскоре станут понятнее. Я также назвал squa sh commit BC
, что является попыткой прояснить, что есть один коммит, который делает то, что B-и- C делали вместе.
То, что вы нарисовали, было настоящее слияние (хотя вы назвали фиксацию слияния C'
). Как сказал Мэтт , «squa sh merge» вовсе не является слиянием.
A--BC <-- master
\
B--C <-- feature-1
\
D--E <-- feature-2
На этом этапе почти нет причин для сохраните имя feature-1
. Если вы удалите его, мы можем перерисовать график следующим образом:
A--BC <-- master
\
B--C--D--E <-- feature-2
Обратите внимание, что все коммиты A-B-C-D-E
находятся в ветке feature-2
(независимо от того, удаляем ли мы имя feature-1
); commit BC
есть только на master
.
Основная причина сохранения имени feature-1
заключается в том, что оно идентифицирует фиксацию C
, что упрощает копирование коммитов D
и E
(и никакие другие) в новые и улучшенные коммиты D'-E'
.
Вопрос 1: Есть ли простой способ сквашить sh историю для feature-2
, чтобы соответствовать сжатое слияние?
Мне не совсем понятно, что вы подразумеваете под «squa sh история». Однако после выполнения приведенного выше git merge --squash
снимок в фиксации BC
будет соответствовать (точно) снимку в фиксации C
, поэтому выполняется:
git switch feature-2 && git rebase --onto master feature-1
(обратите внимание на --onto
здесь 1 ) сообщит Git о копировании коммитов D
и E
(только) с копиями, идущими после фиксации BC
, например:
D'-E' <-- feature-2 (HEAD)
/
A--BC <-- master
\
B--C <-- feature-1
\
D--E [abandoned]
Теперь это безопасно чтобы удалить имя feature-1
, так как нам больше не нужно что-то запоминать ha sh ID фиксации C
. Если мы перестанем рисовать в заброшенных коммитах, мы получим:
A--BC <-- master
\
D'-E' <-- feature-2
, что может быть тем, что вы хотели.
1 Обычно git rebase
занимает одно имя или фиксацию ha sh ID. Затем он:
- перечисляет некоторый набор коммитов для копирования, используя фиксацию ha sh ID в качестве ограничителя;
- делает эквивалент
git switch --detach
при фиксации ha sh ID; - копирует коммиты, перечисленные на шаге 1;
- перемещает имя ветки, которое вы использовали до шага 2, чтобы указать на последний коммит, скопированный на шаге 3; и
- выполняет эквивалент
git switch
обратно к имени ветки, только что перемещенному на шаге 4.
Когда не с использованием --onto
, фиксация ha sh Идентификаторы на шагах 1 и 2 - одинаковые . При использовании --onto
идентификаторы фиксации ha sh на шагах 1 и 2 являются или, по крайней мере, могут быть разными . Таким образом, с --onto
мы можем сказать Git: Только копировать некоторые коммиты, а не много коммитов.
В частности, без --onto
мы скопируйте все коммиты, которые достижимы из HEAD
, но не достижимы из (одиночного) аргумента, и копии будут go в (одиночный) аргумент. С помощью --onto
мы можем сказать: Копировать коммиты достижимы из HEAD
, но не из указанного мной ограничителя, в место, указанное моим отдельным аргументом --onto
. В этом случае это позволяет нам сказать не пытайтесь копировать B
и C
.
С другой стороны, вы также можете просто запустить:
git switch master # if needed - you're probably already there
git merge --squash feature-2
, если вам просто нужно одно сква sh -слияние цепочки DE:
A--BC--DE <-- master (HEAD)
\
B--C <-- feature-1
\
D--E <-- feature-2
Это git merge --squash
будет обычно go плавно, потому что, как и обычный git merge
, git merge --squash
начинается с:
- нахождения базы слияния (
A
в в этом случае); - сравнение базы слияния с текущей фиксацией (
BC
, потому что HEAD
равно master
, что определяет фиксацию BC
); и - сравнение базы слияния с указанной фиксацией (
E
, потому что feature-2
имена фиксации E
).
Первая разница показывает, что сделал B+C
, потому что снимок BC
совпадает с C
s, а второй показывает, что сделал B+C+D+E
, потому что снимок E
является результатом B
плюс C
плюс D
плюс E
. Таким образом, если D
и / или E
конкретно un не сделали что-то, что сделали B
и / или C
, два набора изменений, скорее всего, объединятся автоматически.
(Обратите внимание, что перебазирование здесь всегда происходит плавно, даже если D
и / или E
что-то отменит.)
Разница между squa sh -not-really-a-merge и реальным слияние ограничено окончательной фиксацией: у squa sh есть фиксация с единственным родителем, в данном случае BC
, в то время как реальное слияние имеет фиксацию с двумя родителями. В этом случае реальное слияние даст вам BC
в качестве одного родителя и E
в качестве другого. Вы, вероятно, захотите сначала удалить коммиты B
и C
, если вам нравится BC
squa sh -merge.
Что, если история немного сложнее и после точки ветвления C
на feature-1 было больше коммитов, которые были объединены squa sh в master?
Как всегда, фокус в том, чтобы нарисовать реальный график. Мы можем начать с этого:
A <-- master
\
B--C--F--G <-- feature-1
\
D--E <-- feature-2
, которое после git switch master && git merge --squash feature-1
дает:
A--BCFG <-- master
\
B--C--F--G <-- feature-1
\
D--E <-- feature-2
Теперь можно использовать:
git switch feature-2 && git rebase --onto master feature-1
Обратите внимание, что это та же команда, которую мы использовали в более ранней ситуации. В ней говорится (сравните с шагами в сноске 1 выше):
Вывести список коммитов доступен из feature-2
(где мы находимся после git switch
) , но не из feature-1
. Коммиты, доступные из feature-2
, - A-B-C-D-E
, а коммиты, доступные из feature-1
, - A-B-C-F-G
. Вычитание A-B-C-F-G
из A-B-C-D-E
оставляет D-E
.
Перейти на отдельную HEAD по адресу master
, т.е. зафиксировать BCFG
.
Скопируйте коммиты, перечисленные на шаге 1 , т.е. D
и E
.
Янк имя ветки (feature-2
) туда, где мы сейчас (commit E'
).
Сделайте эквивалент git switch feature-2
снова.
Результат:
D'-E' <-- feature-2 (HEAD)
/
A--BCFG <-- master
\
B--C--F--G <-- feature-1
\
D--E [abandoned]
, после чего можно безопасно удалить имя feature-1
: нам больше не нужен простой способ найти фиксацию C
через фиксацию G
больше.
Вопрос 2: Предполагая, что перезапись истории сложна (или может быть утомительно выполнена с помощью git rebase -i
; У меня более двух коммитов в каждой ветке) ...
Как вы можете видеть выше, это не обязательно правильное предположение. Насколько сложна перебазировка, зависит от того, сколько конфликтов слияния возникает с каждой копируемой фиксацией, что зависит от того, что произошло после последней общей фиксации (C
на рисунках выше). Тем не менее:
... есть ли способ просмотреть только коммиты в feature-2
, которые не были squa sh - объединены в master
?
Команда git log
имеет простой синтаксис для этого, если у вас все еще есть имя feature-1
, идентифицирующее соответствующую фиксацию, как на различных рисунках выше:
git log feature-1..feature-2
делает именно это. Этот синтаксис означает, что все коммиты достижимы, начиная с feature-2
и работая в обратном направлении, за вычетом всех коммитов, достижимых, начиная с feature-1
и работая в обратном направлении. Обратите внимание, что это тот же набор коммитов, который мы скопировали с нашим git rebase
операций в примерах выше. 2
При выполнении запроса на вытягивание на github или bitbucket для feature-2 -> master
, есть ли способ перечислить только эти действительно новые коммиты ?
Нет, потому что в этих системах нет эквивалентного синтаксиса. Однако, если вы используете rebase для копирования только желаемых коммитов и force-pu sh, чтобы репозиторий GitHub или Bitbucket совпадал, они покажут то, что вы хотели.
2 Выше не упоминается тот факт, что git rebase
намеренно пропускает определенные коммиты на шаге 1 по умолчанию. В вашем случае нет никаких коммитов, которые следует здесь опускать, поэтому это не совсем актуально, но стоит упомянуть:
- По умолчанию
git rebase
пропускает все слияние совершает. - По замыслу (и нет возможности остановить это),
git rebase
также использует те же вычисления, которые git cherry
или git log --cherry-pick
использовали бы для исключения из копирования любых коммитов, чьи patch-id соответствует фиксации в вышестоящем наборе коммитов. (Этот набор трудно определить, не вдаваясь в подробности того, как работает разностная нотация A...B
symri c.) В вашем случае это тоже не имеет значения, потому что подобный вид сопоставления идентификатора патча здесь крайне маловероятен. Это больше предназначено для случая, когда кто-то из апстрима намеренно использовал git cherry-pick
для копирования одного или нескольких ваших коммитов в ветку, на которой вы собираетесь выполнить перебазирование. - В некоторых случаях - но опять же не ваш -
git rebase
по умолчанию запускает git merge --fork-point
, чтобы найти коммиты, которые следует пропустить, и это может дать удивительные результаты.
Исторически документация по перебазированию не упоминала их, вероятно, потому, что они не все так часто. В вашем случае они не должны всплывать. Последняя версия документации по перебазированию значительно улучшена.