Передача веток и коммитов между двумя GIT-репозиториями

 

Случилась такая задача. Есть два git-репозитория (один из которых ко всему прочему соединен с SVN через git-svn). Между ними необходимо передавать ветки и коммиты. Если бы они имели прямую связь через файловую систему или например через http, особых проблем бы не было. Однако, никакой связи кроме человека с флэшкой между этими репозиториями нет. Поэтому пришлось изобретать некий workflow который позволил бы эффективно эту проблему решить.

Вот картинка того, что требуется сделать:

branches pass between isolated repos

Решить эту проблему можно тремя способами:

Первый способ нам не подошел. Хотя бы потому, что репозиторий весит около 1.5 ГБ в сжатом виде. А заливать его приходилось в том числе через RDP соединение. Хотя если у вас есть такая возможность – это самый правильный и самый лучший вариант. Нам увы не подошел, поэтому идем дальше.

Второй способ мы даже активно использовали. Workflow там примерно следующий. С какого-то определенного коммита мы делаем набор патчей для каждого коммита выбранной ветки. На другом репозитории мы переключаемся (или создаем если такой ветки еще нет) на нужную ветку и делаем Apply Patch Serial. В принципе это все работает, но есть проблемы. Проблема первая – фактически в двух репозиториях мы имеем две разные ветки, хоть они одинаково и называются. И содержат разные коммиты, хоть и они содержат одно и то же. Кроме того, возникают нетривиальные вещи связанные с разрешением коллизий. Вообщем, достаточно громоздко и сложно, хотя и работает.

Третий способ – git bundles. Вот его и рассмотрим.

Начнем с того, что git bundle – это такой специальный файл. В который во-первых упакованы нужные ветки и нужные коммиты (вы их указываете сами). Во-вторых, он может представляться как удаленный git-репозиторий, который можно добавить в remotes и работать с ним как с полноценным удаленным репозиторием. Скажем так – этот подход – лайт версия первого подхода, когда вы тащите за собой весь репозиторий. Только здесь вы тащите один файл с тем, что надо. Места он правда может занять тоже весьма нехило, но в общем гораздо меньше (при правильном подходе), чем весь репозиторий.

Теперь давайте рассмотрим как это дело провернуть. Предположим у вас есть репозиторий A из которого необходимо перетащить ветку в репозиторий B (который пуст).

Имеем несколько файлов в репозитории A:

repo a

И ветку master:

master branch

Создание бандлов

Теперь нам нужно создать (пересоздать) бандл. Тут два пути – если он уже был создан когда-то, или же его еще не было.

Случай, когда мы создаем новый бандл

# Создаем новый бандл для ветки мастер (веток может быть несколько)
$ git bundle create ../master.bundle master
# Помечаем последний коммит на ветке мастер который мы себе забрали (чтобы в следующий раз не тащить все)
# lastR2bundle - просто некоторое имя
$ git tag -f lastR2bundle master

Случай, если бандл уже создавали (и появились новые изменения)

bundle been created

# Создаем бандл, но уже с указанием места, с которого его создавали в предыдущий раз
$ git bundle create ../master2.bundle lastR2bundle..master
# Обновляем указатель последнего коммита бандла
$ git tag -f lastR2bundle master

Разворачивание бандлов

Теперь бандл у нас есть, необходимо развернуть (обновить) его на другом репозитории RepoB.

В случае если бандл мы принесли первый раз и репозитория нет.

Тогда можно просто склонировать репозиторий прямо с бандла:

# Клонируем репозиторий с бандла в текущую папку
$ git clone -b master C:/Temp/gittest/master.bundle .

Ветка автоматически разворачивается до текущего состояния (флаг -b master указывает нужную ветку)

branch updated

Случай, когда бандл принесли первый раз, но репозиторий уже есть

Тут несколько сложнее, необходимо зайти в папку .git в корне репозитория и отредактировать там файл config

# создаем новый remote с именем RepoA
[remote "RepoA"]
# указываем путь до бандла
url = C:/Temp/gittest/master.bundle
# указываем способ получения веток из бандла
fetch = +refs/heads/*:refs/remotes/RepoA/*

После этого делаем

$ git pull RepoA master

И получаем выгруженную историю из бандла.

Случай, когда бандл уже приносили

В этом случае достаточно заменить старую версию бандла новым, и сделать git pull. Бандлы, сделанные не с самого «начала времен» а с определенного места занимают не так много места.

Надеюсь всем было все понятно и это поможет вам вести удобную разработку с использованием git’а.

P.S. Механизм бандлов работает в обе стороны, изменения в нашей схеме можно переносить не только из RepoA в RepoB, но и наоборот.