Присоединяйтесь к истории более старой версии того же репозитория - PullRequest
4 голосов
/ 19 июня 2020

У меня был репозиторий с этой историей:

A---B---C---D

Затем этот репозиторий был «разделен» (в основном, был создан другой репозиторий с его историей, начинающейся с 'D', с использованием git -subtrees).

Теперь у меня есть другое репо с этой историей:

# The same D as the other
D---E---F---G

Как я могу объединить эти две «части» одной и той же сюжетной линии проекта в один репозиторий? Конечный результат должен быть:

A---B---C---D---E---F---G

Я уже пробовал несколько вещей, но все они включают слияние, а это не то, что я намереваюсь, потому что слияние не сохраняет некоторые изменения, такие как удаленные файлы. Кроме того, я попытался сгенерировать патчи для всех изменений этой последней версии репозитория и применить их в старой, но получил множество error: <file> already exists in index ошибок.

Обновление

Я нашел этот другой вопрос о смене родителей на фиксацию, и это именно то, что решило мою проблему, комбинация обоих git replace --graft и git filter-branch.

Обновление 2

Сейчас моя задача выполнена, и я опубликовал полный и правильный ответ на проблему ниже.

Ответы [ 2 ]

1 голос
/ 19 июня 2020

Вы можете создать новое репо, добавить оба репозитория как удаленные и переназначить второй репозиторий на первый:

Вот репо 1:

repo1[master]/$ git log --oneline
b3ae047 D
5c68b5e C
4a0bfe9 B
0d88f30 A
repo1[master]/$ git grep -e .
a:a
b:b
c:c
d:d

А вот репо 2:

$ cd ../repo2/
repo2[master]/ $ git log --oneline
7b05da3 G
3a72ace F
acd2388 E
5bfa6b3 D
repo2[master]/$ git grep -e .
a:a
b:b
c:c
d:d
e:e
f:f
g:g

Что начинается с фиксации D, идентичной репо 1:

repo2[master]/$ git log --oneline HEAD~3
5bfa6b3 D
repo2[master]/$ git grep -e . HEAD~3
HEAD~3:a:a
HEAD~3:b:b
HEAD~3:c:c
HEAD~3:d:d

Теперь давайте создадим репо, которое их соединяет:

repo2[master]/$ mkdir ../repo3
repo2[master]/$ cd ../repo3
repo3$ git init
repo3[master]/$ git remote add r1 ../repo1
repo3[master]/$ git remote add r2 ../repo2
repo3[master]/$ git fetch r1 && git fetch r2
...boring output omitted...

Теперь мы хотим выполнить перебазирование с кончик репо 2:

fat:repo3[master]/$ git reset --hard r2/master
HEAD is now at 7b05da3 G

Вы захотите сделать git rebase -i r1/master и удалить первую фиксацию, D, потому что она дублирует r1/master. Если у вас go полная командная строка (и у вас нет редактора, настроенного в gitconfig):

repo3[master]/$ export EDITOR='sed -ibak 1d'
repo3[master]/$ git rebase -i r1/master
Successfully rebased and updated refs/heads/master.
repo3[master]/$ git log --oneline
fc2eb8e G
de5161b F
e85ce17 E
b3ae047 D
5c68b5e C
4a0bfe9 B
0d88f30 A
repo3[master]/ (INT)$ git grep -e .
a:a
b:b
c:c
d:d
e:e
f:f
g:g
0 голосов
/ 19 июня 2020

Обновление - Фактически идеальный подход:

# Inside the older repo
$ cd old_repo

# Add the remote to newer repo with updated content
$ git remote add <remote name> <new_repo>

# Fetch the remote
$ git fetch <remote name>

# Track all branches of the remote so you have all of it's history in your older git (be aware of the remote's name in the command)
$ for b in `git branch -r | grep -v -- '->'`; do git branch --track ${b##<remote name>/} $b; done

# Delete the remote so you avoid messing up with the newer repo
$ git remote remove <remote name>

Теперь я настоятельно рекомендую вам использовать визуальный инструмент с этим репозиторием (например, Gitkraken), так как теперь там какой-то беспорядок. У вас будет две истории, не привязанные друг к другу, с, возможно, множеством повторяющихся коммитов.

Теперь выберите коммиты, которыми вы будете управлять. Давайте вызовем фиксацию с ha sh A той, которая была в более старой истории, которая теперь будет родительской для фиксации B новейшей истории. Теперь вы можете использовать приведенный ниже сценарий (или запустить команды вручную), чтобы присоединиться к деревьям и очистить оставшийся беспорядок (обрезать новую историю прямо в коммите B, отбросив всех родителей, так как теперь у него есть новый родитель). (У вас должны быть установлены git -replace и git -filter-repo )

#!/bin/sh

# Argument "$1" is commit A, and argument "$2" is commit B

if [ -z "$1" ] || [ -z "$2" ]
then
        echo "error";
        exit 1;
fi

git replace --graft $1 $2
result="$?"

[ "$result" = "0" ] && git filter-repo --force

Более старые, неважные (они служат только для того, чтобы узнать, чего НЕ делать), ответ ниже.

Сначала я попробовал подход с git-rebase, который не сработал по ряду причин, самая большая из них - это было немного излишним для чего-то вроде простой смены родителя совершить другой, даже если он не имеет отношения к истории. Затем я попытался git cherry-pick повторно применить всю историю из точки E..G в старый репозиторий, тоже не сработало по ряду причин, но главная из них заключалась в том, что другие ветки рекурсивно не копировались.

Пробный подход

$ git replace --graft <commit> <new parent to this commit> Теперь поместите HEAD на кончик новой истории (самая последняя фиксация в основной строке, которую вы хотите сохранить), затем: $ git filter-branch <new parent to this commit>..HEAD

Вы можете потерять ветви, которые еще не слились с веткой, в которой находится HEAD, и я пока не мог найти способ обойти это.

...