Объединение двух отдельных хранилищ SVN в один репозиторий Git

У нас есть два репозитория Subversion, каждый из которых имеет один проект. Итак:

svn://server/svn/project_a
svn://server/svn/project_b

Они являются отдельными проектами и находятся в отдельных хранилищах с полностью раздельными историями фиксации. Проект A имеет r1, r2, ... r100, а проект B имеет r1, r2, ... r400

В конечном итоге мы хотели бы объединить эти два SVN-репозитория в один репозиторий Git. Независимо от того, произойдет ли слияние в Git или должно произойти в третьем временном хранилище SVN, мы в конечном итоге хотим увидеть:

git://server/svn/projects/

Это репозиторий как с проектами A, так и с Project B. Они будут храниться в отдельных папках, например:

git://server/svn/projects/project_a
git://server/svn/projects/project_b

Таким образом, конфликтов не будет "слияние" двух. Мы смогли без проблем использовать этот ответ, чтобы перенести один проект SVN в один проект Git с включенной историей фиксации.

Мы хотели бы объединить наши два проекта SVN A и B в один репозиторий Git, но мы хотим, чтобы коммиты были объединены по дате. то есть:

8b8dad: Project A, r1 (first commit in Git)
dbdffe: Project B, r1 (child of previous)
0ae7f7: Project B, r2 ...
615b51: Project A, r2 ...
916e59: Project A, r3 ...
85f241: Project B, r3 ...

Возможно ли это? Должны ли мы объединить два репозитория SVN в один, а затем импортировать в Git? Или проще оставить их отдельно и выполнить слияние во время импорта Git?

Ответ 1

Вот что мы закончили:

Шаг 1: Объединение репозиториев SVN во временный репозиторий SVN

Для этого требуется доступ к репозиторию SVN (НЕ рабочие копии):

Сначала создайте файлы дампов каждого репозитория, который вы хотите объединить:

svnadmin dump project_a > dumps/a.dmp
svnadmin dump project_b > dumps/b.dmp
svnadmin dump project_c > dumps/c.dmp

Затем создайте новый репозиторий, в котором будут размещены объединенные репозитории:

svnadmin create svn-temp-project

Обратите внимание, что вы ДОЛЖНЫ проверить этот репозиторий в рабочей копии и создать подкаталоги проекта, или загрузка ваших дампов не будет работать:

svn co file:///var/svn/svn-temp-project svn-temp-project-wc
cd svn-temp-project-wc
mkdir project_a
mkdir project_b
mkdir project_c
svn add . --force
svn ci -m "Added initial project directories."

Затем вы можете загрузить каждый отдельный файл дампа в свой собственный каталог (!!) проекта:

svnadmin load svn-temp-project --parent-dir project_a < dumps/a.dmp
svnadmin load svn-temp-project --parent-dir project_b < dumps/b.dmp
svnadmin load svn-temp-project --parent-dir project_c < dumps/c.dmp

Теперь у вас есть 3-объединенный репозиторий SVN.

Шаг 2: Перенос 3-объединенного репозитория SVN в репозиторий Git

Следующие действия могут выполняться на локальном компьютере - не обязательно на вашем сервере.

Сначала создайте файл authors.txt, который git -svn может использовать для определения автора каждой фиксации. Я использовал:

someguy = Some Guy <[email protected]>
...
(no author) = no_author <[email protected]_author>

С помощью этого файла авторов вы можете:

cd projects/
mkdir my-git-repository
cd my-git-repository
git svn init https://svn.mycompany.com/svn/svn-temp-project --no-metadata
git config svn.authorsfile ../authors.txt
git svn fetch

Шаг 3: Очистка

Этот метод хорошо работает для слияния истории фиксации, , но вы получаете каталоги, подобные SVN:

repo/project_a/trunk
repo/project_a/branches
repo/project_a/tags
repo/project_b/trunk
repo/project_b/branches
repo/project_b/tags
...

Таким образом, перед нажатием вы должны перенести любые теги/ветки на Git. Мы этого не делали. Наши теги не нужны, чтобы обойти, так как у нас были другие источники, чтобы их получить, и у нас не было веток для этих проектов.

После удаления каталогов branches и tags мы затем опустили содержимое trunk/ на один уровень, так что все было на уровне "root", специфичном для проекта.

Ответ 2

Итак, я попробовал метод Craig, но в конце концов это оставило меня с несколько неудовлетворительной историей в объединенном хранилище. Я обнаружил, что выписываю все svn-репозитории в отдельные git -ные, а затем разветвляя их, создавая приятную историю, где встречаются три ветки.

Итак, сначала сделайте шаг "авторы" для создания authors.txt:

someguy = Some Guy <[email protected]>
...
(no author) = no_author <[email protected]_author>

Теперь вам нужно проверить все репозитории svn, используя git:

mkdir proja projb projc ...

Теперь вам нужно повторить следующее для каждого проекта, и поскольку ваши репозитории, вероятно, не являются одной отдельной папкой, выполните дополнительную фиксацию:

cd proja
git svn init https://svn.mycompany.com/svn/proja --no-metadata
git config svn.authorsfile ../authors.txt
git svn fetch

#here comes the additional part:
mkdir -p proja                  #proja/proja
git mv -k * proja               #move everything in there
git commit -m "subtree proja"

Затем я пошел и сделал свое новое комбинированное репо, в котором я использовал другую ветвь для каждого подпроекта:

mkdir ../superproj
cd ../supeproj
git init
git commit --allow-empty        #so that we have a master branch
git branch proja projb projc...

Для каждого подпроекта необходимо повторить следующее:

git checkout proja
git remote add proja_rm ../proja
git pull proja_rm              #probably add a branch (e.g. master)
git remote rm proja_rm         #cleanup

Наконец, вы можете объединить все это в свой мастер

git checkout master
git merge proja projb projc...  #it all comes together
git push whereeveryouwant

Ответ 3

Вот что я сделал бы в оболочке Linux (untested):

  • конвертировать каждый в свой собственный git repo
  • сделать третий git репо с пустой первой фиксацией

    git ci --allow-empty -m'Add empty, initial commit'

  • в пустом репо, добавьте каждое репо как удаленное

    git remote add repoA 'path/to/git/repoA'
    git remote add repoB 'path/to/git/repoB'

  • извлекает репозитории в пустую (это возвращает все объекты в одно репо)

    git fetch repoA
    git fetch repoB

  • получить список коммитов в каждом репо с префиксом временных интервалов Unix (секунды с 01.01.1970)

    git --no-pager log --format='%at %H' master >repoACommits
    git --no-pager log --format='%at %H' master >repoBCommits

  • cat оба из них в один, отсортированный (по метке времени) список, отбраковывая отметки времени:

    cat repoACommits repoBCommits | sort | cut -d' ' -f2 >orderedCommits

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

    git co master
    cat orderedCommits | while read commit; do git cherry-pick $commit; done

Это все теоретическое, но я думаю, что это сработает. Я не знаю, что произойдет, если у вас есть конфликт слияния между ними. Я не уверен, остановится ли while или продолжит попытки и не продолжит работу.

Я только заметил, что вы упоминали о том, что каждый из них должен работать в отдельных папках в последней папке. Вам понадобится таинственный и мощный git filter-branch, чтобы сначала запускать каждое репо отдельно, выполняя работу по перемещению добавленных вещей в папку, для каждой транзакции. Вероятно, стоит новый вопрос, если он еще не ответил на SO.