Измените родительский родительский коммит, чтобы он указывал на другой коммит (соединяя два независимых репозитория git)
У меня есть проект с более чем 3-летней историей в хранилище SVN. Он был перенесен в git, но парень, который сделал это, просто взял последнюю версию и выбросил все эти 3 года истории.
Теперь у проекта есть последние 3-4 месяца истории в одном репозитории, а я импортировал остальные 3 года истории SVN в новый репозиторий git.
Есть ли какой-нибудь способ соединить корневой коммит второго репозитория с последним коммитом первого?
Это что-то вроде этого:
* 2017-04-21 - last commit on master
|
* 2017-03-20 - merge branch Y into master
|\
| * 2017-03-19 - commit on branch Y
| |
* | 2017-03-18 - merge branch X into master
/| * 2017-02-17 - commit on another new branch Y
* |/ 2017-02-16 - commit on branch X
| * 2017-02-15 - commit on master branch
* | 2017-01-14 - commit on new branch X
\|
* 2017-01-13 - first commit on new repository
|
* 2017-01-12 - init new git project with the last version of the code in svn repository
.
.
There is no relationship between the two different repositories yet, this is what I wanna
do. I want to connect the root commit of 2nd repository with the last commit of the first
one.
.
.
* 2017-01-09 - commit
|
* 2017-01-08 - commit
|
* 2017-01-07 - merge
/|
* | 2016-01-06 - 2nd commit the other branch
| * 2016-01-05 - commit on trunk
* | 2016-01-04 - commit on new branch
\|
* 2015-01-03 - first commit
|
* 2015-01-02 - beggining of the project
Обновить:
Я просто узнаю, что мне нужно сделать git rebase
, но как? Пожалуйста, давайте рассмотрим даты фиксации, как если бы это были коды SHA-1... Ответ был на использование git filter-branch
с --parent-filter
вариант, а не git rebase
,
Обновление 2:
Я попробовал команду git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD
и это не сработало
PS D:\git\rebase-test\rep2cc> git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD
fatal: ambiguous argument '98e2b95e07b84ad1e40c3231e66840ea910e9d66 || cat': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Обновление 3:
Он не работал в Windows CMD или PowerShell, но работал в Git Bash для Windows.
1 ответ
Перво-наперво: вам нужен один репо со всей доступной историей.
Сделайте клон репо с недавней историей. Добавьте репо со старой историей в качестве удаленного. Я рекомендую, чтобы этот клон был "зеркалом", и вы закончите, заменив исходное репо этим. Но поочередно вы можете оставить --mirror
выключите, и вы закончите, нажав (возможно принудительное нажатие в зависимости от того, какой подход вы используете), все ссылки возвращаются к исходному.
git clone --mirror url/of/current/repo
cd repo
git remote add history url/of/historical/repo
git fetch history
Следующее, что вам нужно сделать, это выяснить, где вы будете склеивать историю. Терминология, описывающая это, немного нечеткая, я думаю... вам нужно найти два коммита, которые соответствуют самой последней ревизии SVN, для которой обе истории имеют коммит. Например, ваш репозиторий SVN содержал версии 1, 2, 3 и 4. Теперь у вас есть
Recent-History Repo
C --- D --- E --- F <--(master)
Old-History Repo
A --- B --- C' --- D'
где A
представляет версию 1, B
представляет версию 2, C
а также C'
представляют версию 3 и D
а также D'
представляют версию 4. E
а также F
являются работой, созданной после первоначальной миграции. Итак, вы хотите объединить коммиты, чей родитель D
(E
в этом примере) на D'
,
Теперь я могу придумать два подхода, каждый из которых имеет свои плюсы и минусы.
Переписывая недавнюю историю
IMO - лучший способ, если вы можете согласовать срез всех разработчиков с новым репо (то есть вы договариваетесь о времени, когда все они соглашаются, что вся выдающаяся работа отбрасывается, поэтому они отбрасывают своих клонов; затем вы выполняете преобразование; затем они все повторное клонирование) состоит в том, чтобы (эффективно) перенести недавнюю историю в старую.
Если на самом деле есть только одна ветка, то вы можете буквально использовать rebase
git rebase --onto D' D master
(где D
а также D'
заменяются идентификатором SHA коммитов).
Скорее всего, у вас есть некоторые ветви и слияния в недавней истории; в этом случае операция rebase станет проблемой очень быстро. С другой стороны, вы можете воспользоваться тем, что D
имеет то же дерево, что и D'
Таким образом, ребаз и повторный родитель более или менее эквивалентны.
Так что вы можете использовать git filter-branch
с --parent-filter
сделать переписать. Основываясь на примерах в документации на https://git-scm.com/docs/git-filter-branch вы бы сделали что-то вроде
git filter-branch --parent-filter 'test $GIT_COMMIT = D && echo "-p D'" || cat' HEAD
(где опять D
а также D'
заменяются идентификатором SHA коммитов).
Это создает "резервные" ссылки, которые вам нужно очистить. В конце концов вы получите
A --- B --- C' --- D' --- E' --- F' <--(master)
Это факт, что F
был заменен F'
что создает необходимость в жестком сокращении (более или менее).
Теперь, если вы сделали зеркальный клон обратно на шаге 1, вы можете стереть reflog, сбросить пульты и запустить gc
, а затем это новый готовый репо.
Если вы сделали обычный клон, то вам нужно push -f
все ссылки на происхождение, и это, вероятно, оставит позади некоторый беспорядок в репо происхождения.
Использование "замены коммита"
Другой вариант не создает жестких сокращений, но он оставляет вас с небольшими головными болями, чтобы иметь дело с навсегда. Ты можешь использовать git replace
, В вашем комбинированном репо
git replace `D` `D'`
По умолчанию, когда генерируется вывод журнала или что-то еще, если git находит D
, это заменит D'
(и его история) в выводе.
Есть несколько известных глюков. Там могут быть неизвестные глюки. И по умолчанию "заменяющие ссылки", которые заставляют все это работать, не являются общими, так что вам нужно сознательно их загружать и извлекать.