Как мне объединить репозитории 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 или что-то другое / любитель зависит от вас).

Другие вопросы по тегам