Объединение нескольких хранилищ git

Скажем, у меня есть настройка, которая выглядит примерно так:

phd/code/
phd/figures/
phd/thesis/

По историческим причинам все они имеют свои репозитории git. Но я хотел бы объединить их в один, чтобы немного упростить ситуацию. Например, прямо сейчас я могу сделать два набора изменений и сделать что-то вроде

cd phd/code
git commit 
cd ../figures
git commit

Было бы (сейчас) приятно просто выполнить

cd phd
git commit

Кажется, есть несколько способов сделать это, используя подмодули или вытаскивая из моих субрепозиториев, но это немного сложнее, чем я ищу. По крайней мере, я был бы счастлив

cd phd
git init
git add [[everything that already in my other repositories]]

но это не похоже на однострочный. Есть что-нибудь в git, которое может помочь мне?

Ответ 1

Вот решение, которое я дал здесь:

  1. Сначала сделайте полную резервную копию вашего каталога phd: я не хочу нести ответственность за ваши упущенные годы тяжелой работы! ;-)

    $ cp -r phd phd-backup
    
  2. Переместите содержимое phd/code в phd/code/code и исправьте историю, чтобы она выглядела так, как будто она всегда была там (для этого используется команда git filter-branch):

    $ cd phd/code
    $ git filter-branch --index-filter \
        'git ls-files -s | sed "s#\t#&code/#" |
         GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
         git update-index --index-info &&
         mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
    
  3. То же самое для содержания phd/figures и phd/thesis (просто замените code с figures и thesis).

    Теперь ваша структура каталогов должна выглядеть так:

    phd
      |_code
      |    |_.git
      |    |_code
      |         |_(your code...)
      |_figures
      |    |_.git
      |    |_figures
      |         |_(your figures...)
      |_thesis
           |_.git
           |_thesis
                |_(your thesis...)
    
  4. Затем создайте репозиторий git в корневом каталоге, вытяните в него все и удалите старые репозитории:

    $ cd phd
    $ git init
    
    $ git pull code
    $ rm -rf code/code
    $ rm -rf code/.git
    
    $ git pull figures --allow-unrelated-histories
    $ rm -rf figures/figures
    $ rm -rf figures/.git
    
    $ git pull thesis --allow-unrelated-histories
    $ rm -rf thesis/thesis
    $ rm -rf thesis/.git
    

    Наконец, теперь у вас должно быть то, что вы хотели:

    phd
      |_.git
      |_code
      |    |_(your code...)
      |_figures
      |    |_(your figures...)
      |_thesis
           |_(your thesis...)
    

Одной из приятных сторон этой процедуры является то, что она оставит на месте не версионные файлы и каталоги.

Надеюсь это поможет.


Только одно предупреждение: если в вашем каталоге code уже есть подкаталог или файл code, все может пойти не так (как в случае с figures и thesis конечно). Если это так, просто переименуйте этот каталог или файл, прежде чем пройти всю эту процедуру:

$ cd phd/code
$ git mv code code-repository-migration
$ git commit -m "preparing the code directory for migration"

И когда процедура будет завершена, добавьте этот последний шаг:

$ cd phd
$ git mv code/code-repository-migration code/code
$ git commit -m "final step for code directory migration"

Конечно, если подкаталог или файл code не имеют версий, просто используйте mv вместо git mv и забудьте о git commit.

Ответ 2

git-stitch-repo обработает вывод git-fast-export --all --date-order в репозиториях git, указанных в командной строке, и создаст поток, подходящий для git-fast-import, который создаст новый репозиторий, содержащий все коммиты в новом дереве команд, который учитывает историю всех исходных репозиториев.

Ответ 3

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

$ cd phd/code
$ mkdir code
# This won't work literally, because * would also match the new code/ subdir, but you understand what I mean:
$ git mv * code/
$ git commit -m "preparing the code directory for migration"

а затем слияние трех отдельных репозиториев в одно новое, сделав smth вроде:

$ cd ../..
$ mkdir phd.all
$ cd phd.all
$ git init
$ git pull ../phd/code
...

Затем вы сохраните свои истории, но продолжите с одним репо.

Ответ 4

Вы можете попробовать стратегию слияния поддеревьев. Это позволит вам объединить репо B с репо A. Преимущество перед git-filter-branch заключается в том, что вам не нужно переписывать историю репо A (ломая суммы SHA1).

Ответ 5

Решение git -filter-branch работает хорошо, но обратите внимание, что если ваш ретранслятор git поступает из импорта SVN, он может выйти из строя с сообщением типа:

Rewrite 422a38a0e9d2c61098b98e6c56213ac83b7bacc2 (1/42)mv: cannot stat `/home/.../wikis/nodows/.git-rewrite/t/../index.new': No such file or directory

В этом случае вам нужно исключить исходную ревизию из ветки фильтра - т.е. изменить HEAD в конце на [SHA of 2nd revision]..HEAD - см.

http://www.git.code-experiments.com/blog/2010/03/merging-git-repositories.html

Ответ 6

Решение @MiniQuark мне очень помогло, но, к сожалению, оно не учитывает теги, которые находятся в исходных репозиториях (по крайней мере, в моем случае). Ниже мое улучшение ответа @MiniQuark.

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

    $mkdir new_phd
    $ mkdir new_phd/код
    $ mkdir new_phd/цифры
    $ mkdir new_phd/thesis

  • Сделайте попытку каждого репозитория и извлеките все теги. (Представление инструкций только для подкаталога code)

    $cd new_phd/code
    $ git init
    $ git pull../../original_phd/code master
    $ git fetch../../original_phd/code refs/tags/*: refs/tags/*

  • (Это улучшение в пункте 2 в ответе MiniQuark) Переместите содержимое new_phd/code в new_phd/code/code и добавьте code_ prefeix перед каждым тегом

    $git filter-branch --index-filter 'git ls-files -s | sed "s-\t \" * - & code/- "| GIT_INDEX_FILE = $GIT_INDEX_FILE.new git update-index --index-info & mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE '--tag- name-filter 'sed' s -. * - code_ & -" 'HEAD

  • После этого будет два раза больше тегов, чем было, прежде чем делать ветвь фильтра. Старые теги остаются в репо и добавляются новые теги с префиксом code_.

    $git tag
    мойтег1
    code_mytag1

    Удалите старые теги вручную:

    $ls.git/refs/tags/* | grep -v "/code_" | xargs rm

    Точка повтора 2,3,4 для других подкаталогов

  • Теперь у нас есть структура каталогов, как в точке @MiniQuark anwser 3.

  • Сделайте так, как в пункте 4 файла MiniQuark anwser, но после выполнения команды pull и перед удалением .git dir, выберите теги:

    $git fetch каталог refs/tags/*: refs/tags/*

    Продолжить..

Это просто другое решение. Надеюсь, это поможет кому-то, это помогло мне:)

Ответ 7

git -stitch-repo из Ответ Аристотеля Пагальциса работает только для репозиториев с простой, линейной историей.

Ответ MiniQuark работает для всех репозиториев, но он не обрабатывает теги и ветки.

Я создал программу, которая работает так же, как описывает MiniQuark, но использует одно объединение слияния (с N родителями), а также воссоздает все теги и ветки, чтобы указать на эти комманды слияния.

См. git -merge-repository для примера, как его использовать.

Ответ 8

Я создал инструмент, который выполняет эту задачу. Используемый метод аналогичен (внутренне делайте некоторые вещи, такие как --filter-branch), но более дружелюбен. Является GPL 2.0

http://github.com/geppo12/GitCombineRepo

Ответ 9

На самом деле, git -stitch-repo теперь поддерживает ветки и теги, включая аннотированные теги (я обнаружил, что была ошибка, о которой я сообщал, и она была исправлена). То, что я нашел полезным, - это теги. Поскольку теги привязаны к фиксации, а некоторые из решений (например, подход Эрика Ли) не справляются с тегами. Вы пытаетесь создать ветку с импортированным тегом, и он отменит любой git слияние/перемещение и отправит вас назад, как консолидированный репозиторий, который почти идентичен репозиторию, из которого пришел тэг. Кроме того, существуют проблемы, если вы используете один и тот же тег для нескольких репозиториев, которые вы объединили/консолидировали ". Например, если у вас есть репо A объявление B, оба имеют тег rel_1.0. Вы объединяете репо A и репо B в repo AB. Поскольку теги rel_1.0 находятся на двух разных коммитах (один для A и один для B), какой тег будет виден в AB? Или тег из импортированного репо A или из импортированного репо B, но не оба.

git -stitch-repo помогает решить эту проблему, создав теги rel_1.0-A и rel_1.0-B. Возможно, вы не сможете проверить тег rel_1.0 и ожидать того и другого, но, по крайней мере, вы можете видеть и то и другое, и теоретически вы можете объединить их в общую локальную ветвь, а затем создать тег rel_1.0 на объединенной ветке (предполагая, что вы просто слияние и изменение исходного кода). Лучше работать с веткими, так как вы можете объединиться как ветки из каждого репо в локальные ветки. (dev-a и dev-b могут быть объединены в локальную ветвь dev, которая затем может быть перенесена в исходное состояние).

Ответ 10

Последовательность, которую вы предложили

git init
git add *
git commit -a -m "import everything"

будет работать, но вы потеряете историю фиксации.

Ответ 11

Чтобы слить второй проект в mainProject:

A) Во втором проекте

git fast-export --all --date-order > /tmp/secondProjectExport

B) В главном проекте:

git checkout -b secondProject
git fast-import --force < /tmp/secondProjectExport

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

C) Затем вернитесь к мастеру и классическому слиянию между двумя ветвями:

git checkout master
git merge secondProject

Ответ 12

Я тоже брошу свое решение. Это в основном довольно простая обертка bash script вокруг git filter-branch. Как и другие решения, он только переносит главные ветки и не переносит теги. Но полные истории фиксации хозяев переносятся, и это короткий bash script, поэтому пользователям будет достаточно легко просмотреть или настроить.

https://github.com/Oakleon/git-join-repos

Ответ 13

Этот bash-скрипт решает проблему с символами Sed Tab (например, в MacOS) и проблемой отсутствующих файлов.

export SUBREPO="subrepo"; # <= your subrepository name here
export TABULATOR='printf '\t'';
FILTER='git ls-files -s | sed "s#${TABULATOR}#&${SUBREPO}/#" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
  git update-index --index-info &&
  if [ -f "$GIT_INDEX_FILE.new" ]; then mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE; else echo "git filter skipped missing file: $GIT_INXEX_FILE.new"; fi'

git filter-branch --index-filter "$FILTER"

Это сочетание miniquark, Marius-butuc и Райан сообщений. Приветствую их!