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

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

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

Ответы

Ответ 1

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


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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

Поиск фиксации для присоединения к (началу исторического хранилища)

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

$ git rev-parse --verify history^0

(где "история ^ 0" используется вместо "истории" на всякий случай, если "история" - это тег, нам нужен SHA-1 commit, а не объект тега). Подобно тому, как найти фиксацию для прикрепления, давайте назовите этот идентификатор фиксации TOP. Вы можете сохранить его в переменной оболочки с помощью

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

Включение истории с использованием файла трансплантатов

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

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

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

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

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

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

Чтобы сделать историю, записанную в файле трансплантатов постоянной, достаточно использовать "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/работает иначе, чем файл графтов: вместо изменения информации о родителях вы заменяете объекты. Поэтому сначала вам нужно создать объект commit, который имеет те же свойства, что и $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 id!) между заголовком "tree" и заголовком "author". После редактирования "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 хэш-объект для создания нового коммита в репозитории. Вам нужно сохранить результат этой команды, которая является SHA-1 нового объекта фиксации, например, следующим образом:

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

(где опция < <220 > здесь предназначена для записи объекта в репозиторий).

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

$ git replace $TAIL $NEW_TAIL

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

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

Заключительное примечание: Я не проверял это решение, поэтому YMMV

Ответ 2

Сначала создайте точку трансплантата, чтобы прикрепить две истории. Затем запустите git ветвь фильтра через репозиторий, чтобы сделать изменение постоянным. Это изменит идентификаторы фиксации всех коммитов после трансплантата, обратите внимание.