Как объединить две ветки без общего предка?
Я начал использовать Git в середине моего проекта, где первые две коммиты - это только некоторые начальные настройки (.gitignore и .gitattributes), а третий commit M2 добавляет содержимое соединительной линии SVN:
I1 -- I2 -- M2 -- N -- .. -- Z
Я импортировал историю SVN в ветки с именем svn, где M1 - это соединительная линия SVN (с тем же содержимым, что и M2 кроме .gitignore и .gitattributes):
A -- B -- ... -- K -- L -- M1
Q: Каков наилучший подход при объединении обеих ветвей?
Я мог бы объединить M1 и M2 в M3, а затем rebase, но я не знаю, как удалить I1 и I2, и если я могу безопасно удалить фиксацию M3 (я нашел несколько советов, чтобы сохранить комманды слияния, но в этом случае M3 больше не нужно).
A -- B -- ... -- K -- L -- M1
\
M3 -- N' -- .. -- Z'
/
I1 -- I2 -- M2 -- N -- .. -- Z
Другой способ - выбрать N Z. Z совершает переход в ветвь svn, но я бы хотел избежать этот подход.
Наиболее элегантным решением было бы переустановить изменения, внесенные N.. Z завершается поверх ветки svn, но я didn 't нашел еще требуемый синтаксис для двух ветвей без общего предка.
Ответы
Ответ 1
Отказ от ответственности. Я только однажды использовал "точки трансплантата" в игрушечном хранилище. Но это неясная особенность, о которой вы, возможно, не слышали, и которая может помочь вам в вашей ситуации.
Вы можете использовать "точки трансплантата" для подделки информации о родословной. См. Например, Что такое .git/info/grafts для? или немедленно перейти к git wiki в пунктах трансплантации.
В сущности, вы должны создать файл .git/info/grafts
, который трюки git в мысли, что commit M1 является предком commit M2:
$ cat .git/info/grafts
<your M2 commit hash> <your M1 commit hash>
Впоследствии это выглядело бы так: M2 было пустой фиксацией, которая просто объединила I2 и M1 в общее дерево.
Основной недостаток: точка трансплантата не зафиксирована; поэтому он не проверяется, но должен быть добавлен в каждую локальную рабочую копию репозитория вручную.
Обновить: используйте git replace --graft
вместо этого.
Точки пересечения, как описано выше, были заменены. Run
git replace --graft <your M2 commit hash> <your M1 commit hash>
чтобы создать трансплантат. Это сохраняется в .git/refs/replace/
. Хотя git не извлекает или не нажимает эти ссылки по умолчанию, они могут быть синхронизированы между репозиториями, используя:
git push origin 'refs/replace/*'
git fetch origin 'refs/replace/*:refs/replace/*'
(fooobar.com/questions/213051/...)
Ответ 2
Я бы сделал следующее:
git checkout M1
git cherry-pick I1
git cherry-pick I2
Это добавляет .gitignore и .gitattributes к вашей ветке, содержащей более хорошую историю.
А затем просто установите новые коммиты поверх этого:
git filter-branch --parent-filter 'if $GIT_COMMIT = $hash_of_N; then printf -- '-p
$hash_of_cherrypicked_I2\n'; else cat; fi'
Недостатком этого является переписывание истории.
Таким образом, альтернативный подход заключается в создании сценария, аналогичного сценарию для ядра Linux, и его размещении в вашем хранилище.
Ответ 3
Наиболее элегантным решением было бы переустановить изменения, внесенные N.. Z, на вершине svn-ветки, но я еще не нашел требуемого синтаксиса для двух ветвей без общего предка.
Попробуйте сначала выбрать I1 и I2 для вишни на M1, а затем используйте команду git rebase --onto M1' M2 Z
(где M1 '- ветвь M1-I1-I2). Я не уверен, что rebase -onto работает, когда нет общих предков, но если это не так, есть возможность использовать патчи. Используйте git format-patch
для создания патчей M2..Z, а затем git am
, чтобы применить их поверх M1. Вот некоторые отчеты об опыте об использовании его при конвертации старых хранилищ SVN и CVS.