Как добавить историю в репозиторий Git?

У меня есть проект, который существовал в двух репозиториях SVN. Второй репозиторий SVN был создан простым добавлением репозиториев из кассы старого репозитория SVN без удаления информации SCM. Содержимое файлов идентично по байтам, но нет связанных метаданных SCM.

Я взял новый репозиторий SVN и перенес его в репозиторий Git через git-svn. Теперь я хотел бы импортировать старый репозиторий и каким-то образом связать его с новым репозиторием, чтобы я мог видеть историю обоих. Есть ли простой способ сделать это без ручного сшивания двух репозиториев?


person shemnon    schedule 03.08.2009    source источник


Ответы (2)


См. также: Как воспроизвести мои коммиты из локального репозитория Git поверх проекта, который я создал на github.com? вопрос (и мой ответ там), хотя ситуация немного другая, я думаю.


У вас есть как минимум три возможности:

  • Используйте прививки, чтобы соединить две истории, но не переписывать историю. Это означает, что у вас (и любого, у кого есть такие же трансплантаты) будет полная история, в то время как у других пользователей будет меньший репозиторий. Это также позволяет избежать проблем с переписанной историей, если кто-то уже начал работать над преобразованным репозиторием с более короткой историей.

  • Используйте прививки, чтобы соединить две истории, и проверьте правильность с помощью «git log» или «gitk» (или другого браузера/просмотрщика истории Git), а затем перепишите историю с помощью git filter-branch; затем вы можете удалить файл графтов. Это означает, что каждый, кто клонирует (извлекает) из переписанного репозитория, получит полную объединенную историю. Но переписывать историю — это большое «нет», если кто-то уже основывал работу на конвертированном репозитории краткой истории (но этот случай может не относиться к вам).

  • Используйте git replace соединить две истории. Это позволит людям выбрать, хотят ли они полную историю или только текущую историю, выбрав получение refs/replace/ (тогда они получат полную историю) или нет (тогда они получат краткую историю). К сожалению, в настоящее время для этого требуется использовать еще не выпущенную версию Git, используя разрабатываемую («основную») версию или один из кандидатов на выпуск для 1.6.5. Иерархия refs/replace/ запланирована для следующей версии Git 1.6.5.


Ниже приведены пошаговые инструкции для всех этих методов: прививки (локальные), переписывание истории при помощи прививок и refs/replace/.

Во всех случаях я предполагаю, что у вас есть как текущая, так и историческая история репозитория в одном репозитории (вы можете добавить историю из другого репозитория, используя удаленное добавление git). Я также предполагаю, что (одна из) веток в репозитории краткой истории называется «мастер», а ветка (коммит) исторического репозитория, куда вы хотите прикрепить текущую историю, называется «история». Вам нужно будет заменить свои собственные имена веток (или идентификаторы коммитов).

Поиск коммита для прикрепления (корень краткой истории)

Во-первых, вам нужно найти коммит (идентификатор SHA-1) в краткой истории, который вы хотите прикрепить к полной истории. Это будет первая фиксация в короткой истории, то есть корневая фиксация (коммит без каких-либо родителей).

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

$ git rev-list --topo-order master | tail -n 1

(где tail -n 1 используется для получения последней строки вывода; вам не нужно использовать его, если у вас его нет.)

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

$ git rev-list --parents master | grep -v ' '

(где grep -v ' ', то есть пробел между одинарными кавычками, используется для фильтрации всех коммитов, у которых есть родители). Затем вы должны проверить (используя, например, «git show <commit>») эти коммиты, если их больше одного, и выбрать тот, который вы хотите прикрепить к более ранней истории.

Назовем этот коммит TAIL. Вы можете сохранить его в переменной оболочки, используя (при условии, что более простой метод работает для вас):

$ TAIL=$(git rev-list --topo-order master | tail -n 1)

В приведенном ниже описании я бы использовал $TAIL для обозначения того, что вы должны заменить SHA-1 самой нижней фиксации в текущей (короткой) истории... или позволить оболочке сделать замену за вас.

Поиск коммита для присоединения (верхняя часть исторического репозитория)

Эта часть проста. Нам нужно преобразовать символическое имя коммита в идентификатор SHA-1. Мы можем сделать это с помощью «git rev-parse»:

$ git rev-parse --verify history^0

(где «история ^ 0» используется вместо «истории» на тот случай, если «история» является тегом; нам нужен SHA-1 коммита, а не объекта тега). Точно так же, как и при поиске коммита для присоединения, давайте назовем этот идентификатор коммита TOP. Вы можете сохранить его в переменной оболочки, используя:

$ TOP=$(git rev-parse --verify history^0)

Присоединение истории с помощью файла графтов

Файл grafts, расположенный в .git/info/grafts (вам нужно создать этот файл, если он не существует, если вы хотите использовать этот механизм) используется для замены родительской информации для коммита. Это построчный формат, где каждая строка содержит SHA-1 коммита, который мы хотим изменить, за которым следует нуль или более списков коммитов, разделенных пробелами, которые мы хотим, чтобы данный коммит имел в качестве родителей; тот же формат, что и "git rev-list --parents <revision>".

Мы хотим, чтобы коммит $TAIL, у которого нет родителей, имел $TOP в качестве единственного родителя. Таким образом, в файле info/grafts должна быть строка с SHA-1 коммита $TAIL, разделенная пробелом SHA-1 коммита $TOP. Для этого вы можете использовать следующую однострочную строку (см. также примеры в git filter-branch документация):

$ echo "$TAIL $TOP" >> .git/info/grafts

Теперь вы должны проверить, используя «git log», «git log --graph», «gitk» или другой браузер истории, что вы правильно объединили истории.

Переписывание истории по файлу графтов

Обратите внимание, что это изменит историю!

Чтобы сделать историю, записанную в файле grafts, постоянной, достаточно использовать «git filter-branch», чтобы переписать нужные вам ветки. Если есть только одна ветвь, которую нужно переписать («мастер»), это может быть так же просто, как:

$ git filter-branch $TOP..master

(Это будет обрабатывать только минимальный набор коммитов). Если есть больше ветвей, затронутых историей присоединения, вы можете просто использовать

$ git filter-branch --all

Теперь вы можете удалить файл графтов. Проверьте, все ли так, как вы хотели, и удалите резервную копию в refs/original/ (подробности см. в документации по "git filter-branch").

Использование механизма refs/replace/

Это альтернатива файлу графтов. У него есть то преимущество, что его можно передавать, поэтому, если вы опубликовали краткую историю и не можете ее переписать (потому что другие основывали свою работу на короткой истории), то использование refs/replace/ может быть хорошим решением... ну, по крайней мере когда выйдет Git версии 1.6.5.

Механизм refs/replace/ работает иначе, чем файл grafts: вместо изменения информации родителя вы заменяете объекты. Итак, сначала вам нужно создать объект фиксации, который имеет те же свойства, что и $TAIL, но имеет $TOP в качестве родителя.

Мы можем использовать

$ git cat-file commit $TAIL > TAIL_COMMIT

(Имя временного файла является только примером).

Теперь вам нужно отредактировать файл 'TAIL_COMMIT' (он будет выглядеть так):

tree 2b5bfdf7798569e0b59b16eb9602d5fa572d6038
author Joe R Hacker  1112911993 -0700
committer Joe R Hacker  1112911993 -0700

Initial revision of "project", after moving to new repository

Теперь вам нужно добавить $TOP в качестве родителя, поместив строку с «parent $TOP» (где $TOP должен быть расширен до идентификатора SHA-1!) между заголовком «дерево» и заголовком «автор». После редактирования TAIL_COMMIT это должно выглядеть так:

tree 2b5bfdf7798569e0b59b16eb9602d5fa572d6038
parent 0f6592e3c2f2fe01f7b717618e570ad8dff0bbb1
author Joe R Hacker  1112911993 -0700
committer Joe R Hacker  1112911993 -0700

Initial revision of "project", after moving to new repository

При желании вы можете отредактировать сообщение коммита.

Теперь вам нужно использовать git hash-object для создания новой фиксации в репозитории. Вам нужно сохранить результат этой команды, который представляет собой SHA-1 нового объекта фиксации, например, так:

$ NEW_TAIL=$(git hash-object -t commit -w TAIL_COMMIT)

(Где опция «-w» предназначена для фактической записи объекта в репозиторий).

Наконец, используйте git replace для замены $TAIL $NEW_TAIL:

$ git replace $TAIL $NEW_TAIL

Теперь осталось проверить (используя "git log" или какой-либо другой просмотрщик истории), верна ли история.

Теперь любой, кто хочет получить полную историю, должен добавить «+refs/replace/*:refs/replace/*» в качестве одной из спецификаций pull refspec.

Последнее примечание. Я не проверял это решение, поэтому ваше мнение может отличаться.

person Jakub Narębski    schedule 10.10.2009
comment
также есть возможность использовать git rebase вместо прививки и последующей фильтрации - person knittl; 10.10.2009
comment
Решение с использованием git rebase --root --onto возможно только в том случае, если фрагмент новой истории, который вы хотите присоединить (добавить) к старой истории, не содержит слияний, и возможно только в том случае, если указанная новая история не слишком длинная. - person Jakub Narębski; 10.10.2009
comment
Я дал этому чек, потому что он включал образцы кода. #showMeTehCodez - person shemnon; 25.10.2009
comment
Git rebase также имеет параметр --graft, поэтому вы можете просто запустить эту команду, чтобы получить желаемый результат напрямую, не создавая коммит замены вручную. git replace --graft $TAIL $TOP - person exstral; 12.06.2019

Сначала создайте точку прививки, чтобы связать две истории. Затем запустите git filter-branch над репозиторием, чтобы сделать изменение постоянным. Обратите внимание, что это изменит идентификаторы коммитов всех коммитов ниже по течению от трансплантата.

person bdonlan    schedule 03.08.2009
comment
Чтобы было ясно, вы делаете два разных репозитория git-svn, добавляете оба как удаленные, чтобы все коммиты оказались в одном месте. Точка прививки делает родителя самой старой фиксации более новой серии самой новой фиксацией более старой серии. Вы увидите это в журнале до того, как будете фильтровать ветку. - person Dustin; 03.08.2009
comment
kan U, пожалуйста, покажи мне кодез? (просто пытаюсь сделать ответы лучше) - person shemnon; 04.08.2009
comment
посмотрите мой ответ и ответьте на аналогичный вопрос: stackoverflow.com/ вопросы/1457248/ - person Jakub Narębski; 10.10.2009