Combining multiple git repositories
假设我有一个类似
1 2 3 | phd/code/ phd/figures/ phd/thesis/ |
出于历史原因,它们都有自己的Git存储库。但我想把它们合并成一个单一的,以简化一些事情。例如,现在我可能要做两组更改,并且必须做类似的事情
1 2 3 4 | cd phd/code git commit cd ../figures git commit |
只是表演一下就好了
1 2 | cd phd git commit |
似乎有两种方法可以使用子模块或从我的子存储库中提取,但这比我所寻找的要复杂一些。至少,我会很高兴
1 2 3 | cd phd git init git add [[everything that's already in my other repositories]] |
但这似乎不是一条直线。
我在这里给出了一个解决方案:
首先做一个完整的博士目录备份:我不想为你多年的辛勤工作负责任!;-)
1 | $ cp -r phd phd-backup |
将
1 2 3 4 5 6 | $ 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 |
与
现在您的目录结构应该如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 | phd |_code | |_.git | |_code | |_(your code...) |_figures | |_.git | |_figures | |_(your figures...) |_thesis |_.git |_thesis |_(your thesis...) |
然后在根目录中创建一个Git存储库,将所有内容拉入其中并删除旧的存储库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ 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 |
最后,你现在应该得到你想要的:
1 2 3 4 5 6 7 8 | phd |_.git |_code | |_(your code...) |_figures | |_(your figures...) |_thesis |_(your thesis...) |
这个过程的一个好方面是它将保留非版本化的文件和目录。
希望这有帮助。
不过,只要一句警告:如果您的
1 2 3 | $ cd phd/code $ git mv code code-repository-migration $ git commit -m"preparing the code directory for migration" |
当程序完成后,添加最后一步:
1 2 3 | $ cd phd $ git mv code/code-repository-migration code/code $ git commit -m"final step for code directory migration" |
当然,如果
git-stitch-repo will process the output ofgit-fast-export --all --date-order on the git repositories given on the command-line, and create a stream suitable forgit-fast-import that will create a new repository containing all the commits in a new commit tree that respects the history of all the source repositories.
也许,简单地(类似于前面的答案,但使用更简单的命令)在每个单独的旧存储库中进行提交,将内容移动到适当命名的子目录中,例如:
1 2 3 4 5 | $ 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" |
然后通过如下步骤将三个独立的回购合并为一个新的回购:
1 2 3 4 5 6 | $ cd ../.. $ mkdir phd.all $ cd phd.all $ git init $ git pull ../phd/code ... |
然后你会保存你的历史记录,但会继续进行单一回购。
您可以尝试子树合并策略。它将允许您将回购B合并到回购A中。与
Git过滤器分支解决方案工作良好,但请注意,如果Git repo来自SVN导入,它可能会失败,并显示如下消息:
1 | Rewrite 422a38a0e9d2c61098b98e6c56213ac83b7bacc2 (1/42)mv: cannot stat `/home/.../wikis/nodows/.git-rewrite/t/../index.new': No such file or directory |
在这种情况下,您需要从过滤器分支中排除初始版本,即将末尾的
http://www.git.code-experials.com/blog/2010/03/merging-git-stores.html
亚里士多德·帕戈尔茨的答案中的git stitch repo只适用于具有简单线性历史的存储库。
MiniQuark的答案适用于所有存储库,但它不处理标记和分支。
我创建了一个程序,其工作方式与MiniQuark描述的相同,但它使用一个合并提交(带有n个父级),并重新创建所有标记和分支以指向这些合并提交。
有关如何使用它的示例,请参见git合并repos存储库。
@MiniQuark解决方案帮助了我很多,但不幸的是它没有考虑到源存储库中的标签(至少在我的情况下)。下面是我对@miniquark答案的改进。
首先创建包含组合repo和合并repo的目录,为每个合并的目录创建目录。
$ mkdir new_phd
$ mkdir new_phd/code
$ mkdir new_phd/figures
$ mkdir new_phd/thesis
对每个存储库进行拉取并获取所有标记。(仅为
$ cd new_phd/code
$ git init
$ git pull ../../original_phd/code master
$ git fetch ../../original_phd/code refs/tags/*:refs/tags/*
(这是对miniquark答案第2点的改进)将
$ 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
这样做之后,标记的数量将是进行筛选分支之前的两倍。旧标签仍保留在repo中,并添加带有
$ git tag
mytag1
code_mytag1
手动删除旧标签:
$ ls .git/refs/tags/* | grep -v"/code_" | xargs rm
对其他子目录重复第2、3、4点
现在我们有了目录结构,如@miniquark anwser点3所示。
按照MiniQuark Anwser的第4点操作,但在执行拉操作之后和删除
$ git fetch catalog refs/tags/*:refs/tags/*
继续…
这只是另一个解决方案。希望它能帮助别人,它能帮助我:)
实际上,git-stitch repo现在支持分支和标签,包括带注释的标签(我发现有一个bug,我报告了它,它得到了修复)。我发现有用的是标签。因为标签附在提交上,而一些解决方案(如EricLee的方法)无法处理标签。您尝试在导入的标记上创建一个分支,它将撤消任何Git合并/移动,并像合并存储库与标记来自的存储库几乎相同一样将您送回。此外,如果在多个"合并/合并"的存储库中使用相同的标记,则会出现问题。例如,如果您有repo的a ad b,两个都有标签rel_1.0。您将repo a和repo b合并到repo a b中。由于rel_1.0标记在两个不同的提交上(一个用于a,一个用于b),哪个标记在a b中可见?从导入的回购A或从导入的回购B中选择标签,但不能同时选择两者。
git-stitch repo通过创建rel_1.0-a和rel_1.0-b标签来帮助解决这个问题。您可能无法签出rel_1.0标记并期望两者同时存在,但至少您可以看到两者,并且理论上,您可以将它们合并到一个公共的本地分支,然后在合并的分支上创建一个rel_1.0标记(假设您只是合并而不更改源代码)。最好是与分支机构合作,因为您可以像从每个回购中合并分支机构一样将其合并到本地分支机构中。(可以将dev-a和dev-b合并到本地dev分支中,然后将其推送到源站)。
我已经创建了一个完成此任务的工具。所使用的方法类似(在内部生成一些东西,如过滤器分支),但更友好。是GPL 2
http://github.com/geppo12/gitcombinerepo
你建议的顺序
1 2 3 | git init git add * git commit -a -m"import everything" |
会有效,但您将丢失提交历史记录。
要在主项目中合并第二个项目,请执行以下操作:
a)在第二个项目中
1 | git fast-export --all --date-order > /tmp/secondProjectExport |
b)在主项目中:
1 2 | git checkout -b secondProject git fast-import --force < /tmp/secondProjectExport |
在这个分支中,完成您需要做的所有繁重的转换并提交它们。
c)然后返回主节点,并在两个分支之间进行经典合并:
1 2 | git checkout master git merge secondProject |
我也会把我的解决方案放在这里。它基本上是围绕
https://github.com/oakleon/git-join-repos