Передача веток и коммитов между двумя GIT-репозиториями
10.12.2015Случилась такая задача. Есть два git-репозитория (один из которых ко всему прочему соединен с SVN через git-svn). Между ними необходимо передавать ветки и коммиты. Если бы они имели прямую связь через файловую систему или например через http, особых проблем бы не было. Однако, никакой связи кроме человека с флэшкой между этими репозиториями нет. Поэтому пришлось изобретать некий workflow который позволил бы эффективно эту проблему решить.
Вот картинка того, что требуется сделать:
Решить эту проблему можно тремя способами:
- Скопировать весь локальный репозиторий одной стороны (например, Repo A) и перенести его на флэшке рядом с репозиторием (Repo B) чтобы между ними можно было установить прямую связь через локальную файловую систему
- Использовать механизм патчей (git am)
- Использовать механизм пакетов git’а (git bundles)
Первый способ нам не подошел. Хотя бы потому, что репозиторий весит около 1.5 ГБ в сжатом виде. А заливать его приходилось в том числе через RDP соединение. Хотя если у вас есть такая возможность – это самый правильный и самый лучший вариант. Нам увы не подошел, поэтому идем дальше.
Второй способ мы даже активно использовали. Workflow там примерно следующий. С какого-то определенного коммита мы делаем набор патчей для каждого коммита выбранной ветки. На другом репозитории мы переключаемся (или создаем если такой ветки еще нет) на нужную ветку и делаем Apply Patch Serial. В принципе это все работает, но есть проблемы. Проблема первая – фактически в двух репозиториях мы имеем две разные ветки, хоть они одинаково и называются. И содержат разные коммиты, хоть и они содержат одно и то же. Кроме того, возникают нетривиальные вещи связанные с разрешением коллизий. Вообщем, достаточно громоздко и сложно, хотя и работает.
Третий способ – git bundles. Вот его и рассмотрим.
Начнем с того, что git bundle – это такой специальный файл. В который во-первых упакованы нужные ветки и нужные коммиты (вы их указываете сами). Во-вторых, он может представляться как удаленный git-репозиторий, который можно добавить в remotes и работать с ним как с полноценным удаленным репозиторием. Скажем так – этот подход – лайт версия первого подхода, когда вы тащите за собой весь репозиторий. Только здесь вы тащите один файл с тем, что надо. Места он правда может занять тоже весьма нехило, но в общем гораздо меньше (при правильном подходе), чем весь репозиторий.
Теперь давайте рассмотрим как это дело провернуть. Предположим у вас есть репозиторий A из которого необходимо перетащить ветку в репозиторий B (который пуст).
Имеем несколько файлов в репозитории A:
И ветку master:
Создание бандлов
Теперь нам нужно создать (пересоздать) бандл. Тут два пути – если он уже был создан когда-то, или же его еще не было.
Случай, когда мы создаем новый бандл
# Создаем новый бандл для ветки мастер (веток может быть несколько)
$ git bundle create ../master.bundle master
# Помечаем последний коммит на ветке мастер который мы себе забрали (чтобы в следующий раз не тащить все)
# lastR2bundle - просто некоторое имя
$ git tag -f lastR2bundle master
Случай, если бандл уже создавали (и появились новые изменения)
# Создаем бандл, но уже с указанием места, с которого его создавали в предыдущий раз
$ git bundle create ../master2.bundle lastR2bundle..master
# Обновляем указатель последнего коммита бандла
$ git tag -f lastR2bundle master
Разворачивание бандлов
Теперь бандл у нас есть, необходимо развернуть (обновить) его на другом репозитории RepoB.
В случае если бандл мы принесли первый раз и репозитория нет.
Тогда можно просто склонировать репозиторий прямо с бандла:
# Клонируем репозиторий с бандла в текущую папку
$ git clone -b master C:/Temp/gittest/master.bundle .
Ветка автоматически разворачивается до текущего состояния (флаг -b master указывает нужную ветку)
Случай, когда бандл принесли первый раз, но репозиторий уже есть
Тут несколько сложнее, необходимо зайти в папку .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, но и наоборот.