Почему git rebase игнорирует коммиты слияния? - PullRequest
2 голосов
/ 26 апреля 2019

Недавно мне пришлось сделать некоторую перебазировку, чтобы разрешить некоторые конфликты слияния, используя git rebase master. Git, к моему удивлению, проигнорировал коммиты слияния, вызвавшие много головной боли, когда код просто исчезал. В конце концов я обнаружил, что -p был тем, что я искал, но почему поведение по умолчанию git rebase игнорирует коммиты слияния?

Ответы [ 2 ]

4 голосов
/ 26 апреля 2019

С man git-rebase сказано:

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

Я хотел бы добавить, что если вы используете git rebase --interactive со слиянием, вы должны (также согласно man git-rebase) использовать --rebase-merges вместо --preserve-merges. Это избавит вас от многих других головных болей.

3 голосов
/ 26 апреля 2019

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

Первая заключается в том, что первоначальная идея перебазирования заключается в том, что это то, что индивид сделал бы непосредственно перед или во время слияния с каким-то более авторитетным хранилищем.

Давайте изобретем двух игроков, Алису и Боба. У Алисы есть авторитетная версия: любой, кто хочет самую последнюю и лучшую версию программного обеспечения, идет к Алисе.

В репозитории Алисы есть несколько направлений развития:

...--o--o--o--o--o   <-- master
            \
             o--o--o   <-- feature/tall

и т. Д. Боб клонировал хранилище Алисы в какой-то момент, возможно, в этом пункте:

...--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

перед последними двумя коммитами, которые Алиса добавила к своему авторитетному master.

Затем Боб разработал свою особенность, feature/short, из его master, поэтому у него есть:

             A--B   <-- feature/short
            /
...--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

Он думает, что готов передать свой результат Алисе. Таким образом, он запускает git fetch origin, чтобы получить любое из ее обновлений, и теперь у него есть это:

             A--B   <-- feature/short
            /
...--o--o--o   <-- master
           |\
           | o--o   <-- origin/master
            \
             o--o--o   <-- origin/feature/tall

Теперь он может обновить свой собственный master, чтобы он указывал на тот же коммит, что и его origin/master (текущий совет Алисы master):

             A--B   <-- feature/short
            /
...--o--o--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

Прежде чем доставить свою серию коммитов A--B Алисе, он должен убедиться, что они работают. Таким образом, он может git checkout master && git merge feature/short, который производит:

             A---B
            /     \
           |       M   <-- feature/short
           |      /
...--o--o--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

Боб может проверить M и убедиться, что он работает - так что теперь безопасно перебазировать A и B на вершине master, давая:

           [old commits, no longer in use]
           |
           |       A'-B'  <-- feature/short
           |      /
...--o--o--o--o--o   <-- master, origin/master
            \
             o--o--o   <-- origin/feature/tall

Обратите внимание, что коммит M ушел из перебазированного feature/short: теперь Боб должен доставить новую и улучшенную цепочку коммитов A'-B' Алисе, и Алиса может выбрать, объединить их или выполнить быструю перемотку вперед. до B' или как ей угодно.

Вторая идея заключается в том, что на самом деле невозможно скопировать коммит слияния. Копирование commit A в A' - это просто внесение тех же изменений, что и A, по сравнению с его родителем. Копирование B в B' - это просто внесение изменений, которые B внес в A. Но вы не можете скопировать слияние; Вы должны сделать совершенно новое слияние. Это, конечно, возможно; и это то, что на самом деле делают старые -p или новые фантазии --rebase-merges: они просто определяют, где слияние происходило раньше, и делают новое - с, возможно, совершенно разными результатами, если новая база слияния отличается - везде, где это имеет смысл .

Один из двух родителей нового слияния очевиден: это перебазированный коммит копии из некоторого оригинального коммита, который был родителем исходного слияния. Родитель other - предполагающий слияние с двумя родителями, во всяком случае - на самом деле не так очевиден: иногда это неизменный исходный коммит, а иногда это перебазированный коммит. Так что эта работа сложнее, чем может показаться на первый взгляд. Старый, не -p код перебазировки просто сказал: Это сложно, и мы в любом случае вообще не хотим делать это вообще, поэтому давайте не будем пытаться.

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

...