Внесение изменений в историю с помощью слияний без ее выравнивания - PullRequest
0 голосов
/ 29 октября 2019

Допустим, у меня есть история, похожая на эту.
Develop периодически объединяется с master.

a---b---c---d---i---j---k---o  master
 \             /           /
  e---f---g---h---l---m---n    develop

Как изменить b без полного выравнивания истории?
Если я интерактивно перебазируюи исправив b, я получу историю, похожую на эту:

(верхний регистр означает, что фиксация изменилась)

a---B---C---E---F---G---H---J---K---L---M---N  master
 \
  e---f---g---h---l---m---n                    develop

Как сохранить структуру истории ивместо этого это выглядит так:

a---B---C---D---I---J---K---O  master
 \             /           /
  e---f---g---h---l---m---n    develop

1 Ответ

4 голосов
/ 29 октября 2019

Технически, вы не можете ничего изменить ни в одном существующем коммите.

Это означает, что для того, чтобы "изменить" b на B, вам действительно нужно сделать новый и улучшенныйB. Старый b будет продолжать существовать (как долго, это совсем другой вопрос). Обычный git rebase, интерактивный или нет, работает путем копирования коммитов в новые и улучшенные. Регулярная перебазировка также удаляет слияний и выравниваний, как вы заметили.

Начиная с Git 2.18, git rebase имеет режим, называемый --rebase-merges. Это сохраняет слияния - или, точнее, повторно создает их как новые коммиты слияния, буквально снова запуская git merge. До версии 2.18, git rebase имеет -p, который использует механизм интерактивной перебазировки, а также сохраняет слияния (повторяя их). Он несколько неисправен по сравнению с более привлекательной новой версией, но - я думаю (не проверял!) - должен работать в этом случае.

Следовательно, используйте git rebase -i --rebase-merges так же, как и git rebase -i. Если вам не хватает --rebase-merges, обновите версию Git или используйте git rebase -i -p и будьте очень осторожны, чтобы не нарушать порядок различных операций или не создавайте новую цепочку вручную.

Чтобы построить ее с помощьюрукой, запустите git checkout на коммите B. Вы можете назначить новое имя ветви здесь, если хотите, или просто выполнить всю операцию с отсоединенным HEAD способом git rebase:

git checkout -b temp-rebuild <hash-of-b>

для использования нового временного имени ветви, дляэкземпляр. Затем используйте git commit --amend (возможно, сначала с дополнительным материалом), чтобы создать новый B:

  B   <-- temp-rebuild
 /
a---b---c---d---i---j---k---o   <-- master
 \             /           /
  e---f---g---h---l---m---n   <-- develop

Теперь запустите git cherry-pick для хеша коммита c, чтобы скопировать его в новый коммит C и повторите для d:

  B---C---D   <-- temp-rebuild
 /
a---b---c---d---i---j---k---o   <-- master
 \             /           /
  e---f---g---h---l---m---n   <-- develop

Теперь запустите git merge для хэша коммита h, i второго родителя, чтобы произвести новое слияниеI, при необходимости разрешая любые конфликты слияния:

  B---C---D------I   <-- temp-rebuild
 /              /
a---b---c---d--/i---j---k---o   <-- master
 \            |/           /
  e---f---g---h---l---m---n   <-- develop

Используйте больше команд cherry-pick и merge для завершения процесса:

  B---C---D------I---J---K--O   <-- temp-rebuild
 /              /          /
a---b---c---d--/i---j---k-|-o   <-- master
 \            |/          |/
  e---f---g---h---l---m---n   <-- develop

Теперь, когда созданы новые коммиты,заставить имя master указывать на окончательный коммит и прекратить рисовать старую цепочку b-c-d-i-j-k-o, и вы получите то, что хотели. Вот что делают git rebase --rebase-merges и git rebase -p: -p просто использует хрупкий алгоритм, который, в то время как --rebase-merges использует новый формат интерактивного листа инструкций, который позволяет вам указывать новый график таким образом, чтобы он не ломался привы двигаетесь коммиты.

...