结合多个git仓库


207

假设我有一个看起来像

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's already in my other repositories]]

但这似乎不是单线的。有什么git可以帮助我的吗?


还要考虑这个伟大的方法:stackoverflow.com/questions/1425892/...
约翰·斯乔贝格


join-git-repos.py剧本做了很好的工作,如果你有独立的仓库,每个主分支要结合起来。
标记

Answers:


149

这是我在这里提供的解决方案:

  1. 首先,完整备份您的phd目录:我不想为您失去的辛苦岁月负责!;-)

    $ cp -r phd phd-backup
    
  2. 将的内容移动phd/codephd/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/figuresphd/thesis(只需更换codefiguresthesis)。

    现在您的目录结构应如下所示:

    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 commits。


13
感谢这个片段-这也正是我需要(有一次我占了Mac OS X中使用sed不处理“\ t”的(我不得不^ V ^ I),而不是使用。
克雷格交易

6
我最初无法使它工作,最终在另一个旧的留言板上找到了解决问题的方法。在最后一行,我不得不像这样在文件名两边加上引号:mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD然后效果很好!
Jorin 2011年

3
funky filter-branch命令来自git的filter-branch手册页。您应该这样说:a)应该正确地赋予属性b)我不会因为某人(即使声誉很高)将其发布在StackOverflow上而运行这样的命令。我会从手册页中知道它的。
tymtam

5
小心!MacOS X不使用sed的GNU扩展名,因此它不知道序列\ t。结果是混乱的历史!我的解决方案是将代码粘贴到脚本文件中,然后在其中写入真实的<TAB>字符。在终端中,可以按ctrl + v进入选项卡,然后编写<TAB>。我没有尝试过克雷格(Craig)的解决方案
吉尔·韦格利奇

4
注意(2)!还要注意,如果某些文件或目录包含连字符(-),则sed命令将失败。在这种情况下,您可以用“ s〜\ t〜&code /〜”代替它。在这里,应用相同的逻辑,注意名称中的“〜”
Gil Vegliach 2014年

75

git-stitch-repo将处理git-fast-export --all --date-order命令行上给定的git存储库上的输出,并创建一个适合的流,git-fast-import该流将创建一个新存储库,该存储库在尊重所有源存储库历史的新提交树中包含所有提交。


33
呃,它是第三方工具,不是git的一部分……:-)
Aristotle Pagaltzis

1
确实,现在您告诉我了:)哦,好吧,我想我必须学会一天如何安装CPAN软件包……
罗伯逊将

1
感谢您指出该命令。刚刚使用它来帮助将一些仓库从SVN转移到Git。
signine 2010年

1
如果您有分支机构/合并机构,则警告可能不起作用!在git-stich-repo页面上:“ git-stich-repo与具有线性历史记录(无合并)的存储库完美配合。..0.06版中添加的拼接算法的改进应该适合与具有以下内容的存储库一起使用分支和合并。”
Bryan P

6
这是一个外部脚本,答案太短且没有真正的帮助,此脚本在合并提交时遇到问题,没有多少人会处理Perl或CPAN,并且答案中没有很好地说明。所以... -1,对不起。
哈兰·杜波夫

20

也许,简单地(类似于先前的答案,但是使用更简单的命令)在每个单独的旧存储库中进行一次提交,该提交将内容移动到适当命名的子目录中,例如:

$ 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"

然后通过以下方式将三个单独的存储库合并为一个新的存储库:

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

然后,您将保存您的历史记录,但是将继续进行单个回购。


没关系,但是如果您将一个存储库合并到另一个存储库中(即phd是一个已经存在的不空存储库),那么如果phd的文件夹名称与代码目录中的子文件夹相同,则会遇到“ git pull ..”的问题。 / phd / code'提取具有原始路径的所有提交,并且仅在最后应用mv提交。
tymtam

1
@Tymek:但这在这种情况下仍然有效,没有问题。不好的是,历史记录中的路径不会“正确”(对应于新路径)。
imz-伊万·扎哈拉里谢夫(Ivan Zakharyaschev)2011年

19

您可以尝试子树合并策略。它将使您将存储库B合并到存储库A。优点git-filter-branch是它不需要您重写存储库A的历史记录(打破SHA1的总和)。


链接无效,因此无法保留历史记录,对吗?
tymtam

3
@Tymek(安全漏洞后,kernel.org的抱歉部分仍处于关闭状态)。它破坏了入库B的SHA1。但是A保持不变。
Leif Gruenwoldt 2011年


1
@LeifGruenwoldt第一个链接现在正在工作。镜像链接消失了,我想应该将其删除。
瓦迪姆·科托夫

9

git-filter-branch解决方案效果很好,但是请注意,如果您的git repo来自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


2
谢谢!我一直在挠挠为什么不起作用!回购确实来自SVN。
亚瑟·马尔森

1
当我这样做时,同样的错误。振作起来 另外,链接现在已断开。
瑞安

您能否详细说明“将头转向……”的意思,我的回购来自SVN导入,我正面临着这个问题,非常感谢您的帮助!

5

@MiniQuark解决方案对我有很大帮助,但是不幸的是,它没有考虑源存储库中的标签(至少在我看来)。以下是我对@MiniQuark答案的改进。

  1. 首先创建目录,其中将包含组合的仓库和合并的仓库,为每个合并的仓库创建目录。

    $ mkdir new_phd
    $ mkdir new_phd /代码
    $ mkdir new_phd /图
    $ mkdir new_phd / thesis

  2. 提取每个存储库并获取所有标签。(仅针对code子目录提供说明)

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

  3. (这是对MiniQuark答案中第2点的改进),将的内容new_phd/code移至new_phd/code/codecode_在每个标签之前添加前缀

    $ 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 -。*-code _&-“'HEAD

  4. 这样做之后,标签将是过滤分支之前的两倍。旧标签保留在存储库中,并code_添加了带前缀的新标签。

    $ git标签
    mytag1
    code_mytag1

    手动删除旧标签:

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

    对其他子目录重复点2,3,4

  5. 现在我们有了目录结构,如@MiniQuark anwser点3所示。

  6. 按照MiniQuark anwser第4点的方法进行操作,但是在进行拉动之后并且在删除.git目录之前,请获取标签:

    $ git获取目录refs / tags / *:refs / tags / *

    继续..

这只是另一个解决方案。希望它对某人有帮助,对我有帮助:)


5

Aristotle Pagaltzis的答案中的 git-stitch-repo 仅适用于具有简单线性历史记录的存储库。

MiniQuark的答案适用于所有存储库,但不能处理标签和分支。

我创建了一个程序,该程序的工作方式与MiniQuark描述的相同,但它使用了一个合并提交(具有N个父级),并且还重新创建了所有标记和分支以指向这些合并提交。

有关如何使用它的示例,请参见git-merge-repos存储库



3

实际上,git-stitch-repo现在支持分支和标签,包括带注释的标签(我发现我报告了一个错误,并已修复)。我发现有用的是标签。由于标签附加在提交上,因此某些解决方案(例如Eric Lee的方法)无法处理标签。您尝试根据导入的标签创建一个分支,它将撤消所有git合并/移动并将其发送回给您,就像合并的存储库几乎与该标签所来自的存储库一样。另外,如果您在“合并/合并”的多个存储库中使用相同的标签,则会出现问题。例如,如果您有回购的A广告B,两者的标签均为rel_1.0。您将存储库A和存储库B合并到存储库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分支中,然后可以将其推送到原始位置)。


2

您建议的顺序

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

将起作用,但是您将丢失提交历史记录。


丢失历史记录并不是很糟糕,但是由于该存储库是我自己的工作(即,它是私有的),所以那里有很多我不想版本化或尚未版本化的东西。
罗伯逊

1

要在mainProject中合并secondProject:

A)在第二个项目中

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

B)在mainProject中:

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

在这个分支中,您需要进行所有繁重的转换并提交。

C)然后回到母版和两个分支之间的经典合并:

git checkout master
git merge secondProject

这会将两个git项目根目录下的所有文件和文件夹合并到一个项目中。我怀疑任何人都会希望这种情况发生。
克林特姆

0

我也将解决方案放在这里。基本上,这是一个相当简单的bash脚本包装器git filter-branch。与其他解决方案一样,它仅迁移主分支,而不迁移标签。但是完整的主提交历史记录已被迁移,并且它是一个简短的bash脚本,因此用户应该相对容易地对其进行查看或调整。

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


0

此bash脚本可解决sed选项卡字符问题(例如在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" HEAD

这是miniquarkmarius-butucryan的帖子的组合。为他们加油!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.