Что вызывает конфликты и как я могу распознать общую историю?
Нет общей истории (или, скорее, недостаточно).Нечего распознать, вот почему возникают конфликты.
Ключом является флаг --squash
:
$ git checkout B
$ git merge [release-commit-ID] --squash
Первый шаг присоединяет ваш HEAD
на имя ветви B
, проверяя, какой коммит имя B
идентифицирует:
...--*--C--D--E <-- B (HEAD)
\
F--G--H <-- somebranch
Здесь имя B
идентифицирует коммит E
(каждая из этих букв обозначает реальный хешидентификаторы).Коммит *
(который я бы назвал B
, но вы использовали это имя для своей ветви) - это точка, в которой расходятся два потока разработки, что означает, что когда мы работаем в обратном направлении (как это делает Git), это точка, в которойони собираются вместе.Теперь вы запускаете git merge --squash <hash>
, где <hash>
идентифицирует коммит F
, или G
, или H
, поэтому Git сравнивает содержимое коммита *
с содержимым коммита E
, чтобы выяснить, что вы изменили:
git diff --find-renames <hash-of-*> <hash-of-E> # what we changed
и повторяется, скажем, G
:
git diff --find-renames <hash-of-*> <hash-of-G> # what they changed
Git теперь объединяет эти два набора изменений, применяет объединенные изменения для фиксации содержимого *
и делает новый коммит.
Если вы не используете --squash
, Git записывает новый коммит с двумя родителями:
...--*--C--D--E--I <-- B (HEAD)
\ /
F-------G--H <-- somebranch
и теперь самая последняя общая отправная точка между двумя строками - это коммит G
.Но если вы do используете --squash
, Git записывает новый коммит только с одним parent:
...--*--C--D--E--I <-- B (HEAD)
\
F--G--H <-- somebranch
и теперь общая начальная точка остается неизменной.Вся работа через G
находится в коммите I
, но коммит I
не запоминает почему эта работа есть.Будущее git merge
с коммита H
должно начинаться с коммита *
.
Git не остановит вас от разработки на ветке somebranch
, но в целом после git merge --squash
,Вы должны считать ветвь, которую вы слили из , "мертвой" и просто прекратить ее использование.Как только мы объединили G
с git merge --squash
, нам следует прекратить использование F
и G
полностью.Это означает, что мы также должны полностью прекратить использование H
.Если H
полезен, мы должны скопировать его в новый, другой коммит:
$ git checkout -b newbranch B
, давая нам:
...--*--C--D--E--I <-- B, newbranch (HEAD)
\ /
F-------G--H <-- somebranch
, за которым следует:
$ git cherry-pick somebranch # or <hash of H>
скопировать H
в очень похожий, но не идентичный коммит H'
:
H' <-- newbranch (HEAD)
/
...--*--C--D--E--I <-- B
\ /
F-------G--H <-- somebranch
Теперь мы можем сбросить somebranch
:
$ git branch -D somebranch
и переименовать newbranch
до somebranch
, если нам нравится.
(Обратите внимание, что мы можем сделать это с помощью копирования имени с именем за один шаг, используя git rebase --onto
, с git checkout somebranch; git rebase --onto B <hash of G>
. Независимо от того, как вы это делаете,Тем не менее, имейте в виду, что git cherry-pick
не может копировать коммиты слияния, и любой , использующий коммиты, которые мы хотим уничтожить - здесь цепочка F-G-H
- должен выполнить это уничтожение при копировании.вещь для спасения. Поэтому перед использованием --squash
, убедитесь, что вы понимаете все последствия.)