Как объединить две ветки без общего предка?

Я начал использовать 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.