Сохраняйте историю событий после слияния git

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

Я объясню лучше. Когда я заканчиваю работу над Branch-A, я объединять ее в мастер. И это прекрасно, если я git log, я вижу все коммиты, которые я сделал в Branch-A.

Вместо, когда я завершаю работу с Branch-B, и я пытаюсь объединить его в master (после того, как Branch-A уже был объединен), я должен указать сообщение фиксации для слияния (в то время как для первой ветки меня ничего не спрашивали). И после слияния с мастером, если я наберу git log, я не могу видеть коммиты Branch-B в истории моей основной ветки

Скажем, у меня

**Branch A**

commit 09b2unfas9d781n2e
    Add more stuff

commit 8uj8masd89jas898a
    Add stuff

**Branch B**

commit 09b2unfas9d781n2e
    Add feature setting

commit 8uj8masd89jas898a
    Add feature

Я заканчиваю тем, что

**Master**

commit 6hf6h8hd871udjkdn
Merge: 09b2un 34osd6
    Merge branch 'Branch-B' into master

commit 09b2unfas9d781n2e
    Add more stuff

commit 8uj8masd89jas898a
    Add stuff

commit 34osd62dhc91123j8
    I'm a previous commit from 'master'.
    The last one before branching...

while Я хотел бы получить что-то вроде:

**Master**

commit 09b2unfas9d781n2e
    Add feature setting

commit 8uj8masd89jas898a
    Add feature

commit 09b2unfas9d781n2e
    Add more stuff

commit 8uj8masd89jas898a
    Add stuff

commit 34osd62dhc91123j8
    I'm a previous commit from 'master'.
    The last one before branching...

..., что более точно отражает историю выполненных коммитов.

Я не понимаю, почему я могу сохранить историю только из одной из двух ветвей.

Как я могу сохранить все ясно без этих коммитов, которые скрывают/пропускают реальную историю объединенных коммитов?

Ответ 1

Похоже, первое слияние было быстрым, а второе - трехсторонним слиянием.

Объяснение

Git имеет две версии слияния: ускоренную и трехстороннюю. (Существуют и другие версии, но это не то, что произошло здесь.) По умолчанию поведение заключается в том, чтобы выполнить быстрое слияние, когда это возможно, и в противном случае выполнить трехстороннее слияние.

Быстрое слияние (вы можете принудительно использовать это поведение с опцией --ff-only, что приведет к сбою слияния при невозможности ускоренной перемотки вперед) может иметь место, когда объединение, которое объединяется, имеет текущую позицию ветвь в ее истории. Например:

A - B - C - D <-master
             \
              E - F - G <- branch-a

Excecuting git merge (с настройками по умолчанию) приведет к

A - B - C - D - E - F - G <- branch-a <-master

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

A - B - C - D  - E - F - G <-master
                  \
                   E1 - E2 <- branch-b

Следовательно, Git не может просто переместить указатель мастера с G на E2, потому что это избавит вас от изменений, сделанных в F и G. Вместо этого происходит трехстороннее слияние, которое создает фиксацию, которая имеет двух родителей, а также имеет сообщение фиксации. Теперь мастер может быть перенесен на эту фиксацию. (Обратите внимание, что в этой ситуации master и branch-b НЕ указывают на одну и ту же фиксацию.

A - B - C - D  - E - F - G - H <-master
                  \       /
                   E1 - E2 <- branch-b

Если вы хотите иметь линейную историю, вам нужно использовать rebase, но следует предупредить, что, если кто-либо еще увидит, что ваш филиал совершает это, это может привести к проблемам, выходящим за рамки этого ответа. Использование rebase будет включать в себя два шага, перезагрузку и последующее слияние. Итак, вместо слияния вы сначала выполняете следующее: branch-b, git rebase master. Это создает новые коммиты, которые являются копиями старых коммитов, т.е. Того же набора изменений, информации об авторе и сообщения, но новой информации об участниках и родительской истории. (Я назову коммиты E1 'и E2' на иллюстрации, чтобы указать, что они являются просто копиями.) Старые коммиты будут существовать до тех пор, пока они не будут собраны в мусор, но не будут доступны, если вы не посмотрите на reflog.)

A - B - C - D  - E - F - G <-master
                  \       \
                   E1 - E2 \ 
                            E1' - E2' <- branch-b

Выполнение git checkout master; git merge --ff-only branch-b теперь ускорит переадресацию изменений в master, тем самым предоставив вам линейную историю.

A - B - C - D  - E - F - G - E1' -E2' <-master <- branch-b

Ответ 2

Используйте rebase вместо merge. Из учебника :

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

Я предполагаю, что изменения с вашего Branch-B не могут быть объединены с помощью быстрой перемотки вперед, слияния в master. В таких случаях выполняется трехстороннее слияние:

Вместо того, чтобы просто перемещать указатель на ветку вперед, Git создает новый моментальный снимок, который получается из этого трехстороннего слияния и автоматически создает новую фиксацию, указывающую на нее. Это называется слиянием commit и является особенным в том, что он имеет более одного родителя.

Я всегда буду переустанавливать свои коммиты, прежде чем отправлять их в master, чтобы сохранить линейную историю.