Ответ 1
Использование git ветки фильтра
Используя трюк прямо с git-filter-branch man page:
Сначала создайте новый репозиторий с двумя оригинальными в качестве пультов, как и раньше. Я предполагаю, что оба используют имя ветки "master" .
git init repo
cd repo
git remote add R1 /vol/R1.git
git fetch R1
git remote add R2 /vol/R2.git
git fetch R2
Далее, укажите "ведущий" (текущая ветвь) на вершину R2 "master" .
git reset --hard R2/master
Теперь мы можем перенести историю R1 "master" в начало.
git filter-branch --parent-filter 'sed "s_^\$_-p R1/master_"' HEAD
Другими словами, мы вставляем фальшивое родительское соглашение между D
и K
, чтобы новая история выглядела следующим образом:
A---B---C---D---K---L---M---N
Единственное изменение в K
через N
заключается в том, что указатель родительского указателя K
изменяется, и, следовательно, все идентификаторы SHA-1 меняются. Сообщение фиксации, автор, метка времени и т.д. Остаются неизменными.
Объединение более двух репозиториев вместе с ветвью фильтров
Если у вас есть более двух репозиториев, скажите R1 (самый старый) через R5 (самый новый), просто повторяйте команды git reset
и git filter-branch
в хронологическом порядке.
PARENT_REPO=R1
for CHILD_REPO in R2 R3 R4 R5; do
git reset --hard $CHILD_REPO/master
git filter-branch --parent-filter 'sed "s_^\$_-p '$PARENT_REPO/master'"' HEAD
PARENT_REPO=$CHILD_REPO
done
Использование трансплантатов
В качестве альтернативы использованию опции --parent-filter
для filter-branch
вместо этого вы можете использовать механизм трансплантат.
Рассмотрим исходную ситуацию добавления R2/master
в качестве дочернего элемента (то есть более нового, чем) R1/master
. Как и раньше, начните с указания текущей ветки (master
) на вершину R2/master
.
git reset --hard R2/master
Теперь вместо запуска команды filter-branch
создайте в .git/info/grafts
"трансплантат" (фальшивый родитель), который связывает "root" (самый старый) commit R2/master
(K
) на вершине (новейшей) фиксации в R1/master
(D
). (Если есть несколько корней из R2/master
, следующее будет связывать только одно из них.)
ROOT_OF_R2=$(git rev-list R2/master | tail -n 1)
TIP_OF_R1=$(git rev-parse R1/master)
echo $ROOT_OF_R2 $TIP_OF_R1 >> .git/info/grafts
На этом этапе вы можете посмотреть свою историю (скажем, через gitk
), чтобы увидеть, правильно ли она выглядит. Если это так, вы можете сделать изменения постоянными через:
git filter-branch
Наконец, вы можете очистить все, удалив файл трансплантата.
rm .git/info/grafts
Использование графтов скорее всего будет работать, чем при использовании --parent-filter
, но у него есть то преимущество, что он может трансформировать более двух историй с помощью одного filter-branch
. (Вы можете сделать то же самое с --parent-filter
, но script станет очень уродливым очень быстро.) Это также имеет то преимущество, что позволяет вам видеть ваши изменения до того, как они станут постоянными; если он выглядит плохо, просто удалите файл трансплантата, чтобы прервать его.
Объединение более двух репозиториев вместе с трансплантатами
Чтобы использовать метод трансплантата с R1 (самый старый) через R5 (новейший), просто добавьте несколько строк в файл трансплантата. (Порядок, в котором вы выполняете команды echo
, не имеет значения.)
git reset --hard R5/master
PARENT_REPO=R1
for CHILD_REPO in R2 R3 R4 R5; do
ROOT_OF_CHILD=$(git rev-list $CHILD_REPO/master | tail -n 1)
TIP_OF_PARENT=$(git rev-parse $PARENT_REPO/master)
echo "$ROOT_OF_CHILD" "$TIP_OF_PARENT" >> .git/info/grafts
PARENT_REPO=$CHILD_REPO
done
Как насчет git rebase?
Несколько других предложили использовать git rebase R1/master
вместо команды git filter-branch
выше. Это приведет к различию между пустым фиксацией и K
, а затем попытается применить его к D
, в результате чего:
A---B---C---D---K'---L'---M'---N'
Это скорее всего вызовет конфликт слиянием и может привести к созданию ложных файлов в K'
, если файл был удален между D
и K
. Единственный случай, когда это будет работать, - это то, что деревья D
и K
идентичны.
(Еще одно небольшое отличие состоит в том, что git rebase
изменяет информацию коммиттера для K'
через N'
, тогда как git filter-branch
не делает.)