Как мне объединить репозитории Git?
Я пытаюсь объединить 2 хранилища в 1, прививая историю. Я бы предположил, что это самый простой способ получить чистую линейную историю.
Я попытался сделать это, добавив другой в качестве удаленного в исходный репозиторий:
git init
echo "Hello" > Hello.txt
git add -A
git commit -m "initial commit"
git remote add b c:\pathToB
git replace --graft master b/master
Дерево выглядит хорошо, проблема в том, что я не получаю содержимое хранилища B в текущем каталоге.
Я также попробовал это (хеш коммита - подсказка b / master)
git filter-branch -f --parent-filter 'sed "s~^\$~-p b34fc98295273c41aeb203213ad5fe4f95ba205b~"'
Просматривая дерево, я вижу, что каждый коммит содержит свои изменения, но первый коммит в основном репо в основном удаляет все изменения, внесенные репо B:
Ни один из оригинальных коммитов не удаляет файлы.
Чего мне не хватает, я использую фильтр-ответвление и трансплантаты неправильно? Или мне просто нужно использовать cherry-pick или rebase, чтобы сохранить все изменения в текущем каталоге?
1 ответ
TL;DR
Вам нужно объединить деревья. Например, вы можете использовать git merge
, Если ваш Git достаточно новый, вам понадобится --allow-unrelated-histories
флаг. Такое слияние будет использовать пустое дерево в качестве базы слияния, поэтому оно думает, что переход с базы слияния на L - это "добавить все файлы в коммите L ", а переход с базы слияния на R - "добавить все файлы в коммите R". "(где L и R определены так, как мне нравится определять их для git merge
; см., например, этот ответ).
Долго
Коммиты это снимки. (Эта часть, я надеюсь, не противоречива.)
Git и git replace
объекты, в буквальном смысле, замены. То есть всякий раз, когда Git собирается искать объект по его хэш-идентификатору 1234567...
(или что-то еще), Git сначала проверяет: есть ли в списке замена 1234567...
в refs/replace/
? Если есть такая замена, Git считывает объект замены, решая refs/replace/1234567...
с другим идентификатором хэша и чтения этого объекта.
Так:
git init echo "Hello" > Hello.txt git add -A git commit -m "initial commit"
Эта последовательность сначала создает новый, полностью пустой репозиторий (при условии, что еще нет репозитория Git, так что git init
делает создание). echo
команда создает файл в рабочем дереве; git add -A
добавляет файл рабочего дерева в индекс (что имеет побочный эффект от сохранения данных файла в хранилище как объекта BLOB- объекта, хотя здесь это не критично). Последний шаг, git commit ...
создает объект дерева для хранения снимка, в котором содержится один файл, Hello.txt
с содержимым, которое вы вставили в него, а затем создает объект фиксации, такой как 1234567...
в нем указаны авторы и коммиттеры, имеется сообщение "начальная фиксация", используется дерево, созданное для хранения снимка, и - поскольку это первый коммит в истории - нет родительских коммитов: это новый корневой коммит.
Теперь у нас есть:
git remote add b c:\pathToB
Это просто добавляет URL (и fetch
настройка) для нового пульта b
,
Отсутствует шаг:
git fetch b
который вызывает другой Git (на вашей локальной машине, так как c:\pathToB
локальный - обычно мы вызываем Git на другом компьютере, через HTTPS или SSH или что-то подобное, но это нормально) и загружают с него объекты. В частности, он получает любые коммиты, которые у них есть (которые являются всеми их коммитами), и любые объекты, необходимые для выполнения этих коммитов (которые являются их остальными объектами), и копирует их в ваш репозиторий. Все они имеют некоторый идентификатор, который не 1234567...
, поскольку каждый коммит имеет гарантированно уникальный идентификатор хеша.
В заключение:
git replace --graft master b/master
Это говорит вашему Git настроить одну из этих замен. В частности, он говорит, что он должен скопировать коммит, указанный master
- что мы сказали выше 1234567...
- на новый коммит, который похож на оригинал, за исключением того, что у него есть родительский хеш, какой бы ни был коммит b/master
идентифицирует. Скажем так b/master
определяет совершить fedcba9...
,
Допустим, новый коммит, который git replace
фиксирует имеет идентификатор 8888888...
, Его содержимое:
- Вы как автор и коммиттер, скопированы с
1234567...
или создан заново (это не имеет значения); - штамп даты скопирован с
1234567...
или создан заново (это тоже не имеет значения); - сообщение скопировано с
1234567...
; - дерево (снимок) скопировано с
1234567...
(эта часть имеет решающее значение); а также - родительский хеш
fedcba9...
,
Ваш существующий master
все еще идентифицирует 1234567...
, но теперь, когда вы попросите Git показать вам 1234567...
твой мерзавец видит что refs/replace/1234567...
существует и говорит "не используйте это, используйте 8888888...
вместо ". Так ваш Git ищет объект 8888888...
и находит дерево, которое вы сохранили 1234567...
, который имеет только один файл в нем. Коммит до этого - замена, заменяющая 1234567...
- имеет разные файлы, поэтому изменение с того времени на настоящее время должно быть следующим: удалить все эти файлы и создать Hello.txt
вместо.
Чтобы ваш следующий сохраненный снимок каким-то образом использовал оба дерева, вам нужно объединить дерево для master
с деревом для b/master
, Это никогда не будет git replace
(хотя будь то git merge
или что-то другое / любитель зависит от вас).