代码之家  ›  专栏  ›  技术社区  ›  Will Robertson

组合多个git存储库

git
  •  201
  • Will Robertson  · 技术社区  · 17 年前

    假设我有一个类似

    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 那能帮我吗?

    12 回复  |  直到 17 年前
        1
  •  155
  •   Jaime Hablutzel    7 年前

    我给出了一个解决方案 here :

    1. 首先,对你的博士目录做一个完整的备份:我不想为你失去多年的努力负责!;-)

      $ 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...)
      

    这个过程的一个好处是它会离开 无版本 文件和目录就位。

    希望这有帮助。


    不过,只有一个警告:如果你的 密码 目录已经有一个 密码 子目录或文件,事情可能会非常糟糕(对于 数字 和 论文 当然)。如果是这种情况,只需在完成整个过程之前重命名该目录或文件:

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

    当然,如果 密码 子目录或文件没有版本控制,只需使用 mv 而不是 git mv ,忘了 git commit s

        2
  •  78
  •   Aristotle Pagaltzis    10 年前

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

        3
  •  20
  •   imz -- Ivan Zakharyaschev    17 年前

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

    $ 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
  •  20
  •   leif.gruenwoldt    7 年前

    你可以试试 subtree merge strategy 它将使你把回购协议B合并到回购协议A中 git-filter-branch 它不需要你重写回购A的历史(打破SHA1总和)。

        5
  •  9
  •   Gareth    16 年前

    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

        6
  •  6
  •   Community Mohan Dere    9 年前

    git stitch回购自 Aristotle Pagaltzis' answer 仅适用于具有简单线性历史的存储库。

    MiniQuark's answer 适用于所有存储库,但它不处理标记和分支。

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

    看到了吗 git-merge-repos repository 例如如何使用它。

        7
  •  5
  •   MichK    13 年前

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

    1. 首先创建包含组合回购和合并回购的目录,为每个合并回购创建目录。

      $mkdir新大学博士
      $mkdir新博士/代码
      $mkdir new_博士/数字
      $mkdir新_博士/论文

    2. 拉取每个存储库并获取所有标签。(仅为以下人员提供说明) code 子目录)

      $cd新博士/代码
      $git init
      $git pull..//原创博士/代码硕士
      $git fetch..//原始博士/代码参考/标签/*:参考/标签/*

    3. (这是对MiniQuark答案第2点的改进)移动 new_phd/code 到 new_phd/code/code 加上 code_ 在每一次之前 标签

      $git filter branch——索引过滤器的git ls文件-s | sed“s-\t\”*-&代码/“|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-”*-代码&-“‘头’

    4. 这样做之后,标签的数量将是执行过滤器分支之前的两倍。旧标签保留在回购协议中,新标签保留在 密码_ 添加前缀。

      $git标签
      mytag1
      代码_mytag1

      手动删除旧标签:

      1美元。git/refs/tags/*|grep-v“/code|xargs rm

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

    5. 现在我们有了@MiniQuark anwser point 3中的目录结构。

    6. 按照MiniQuark anwser的第4点进行操作,但在拉动之后和移除之前 .git 目录,获取标签:

      $git fetch catalog refs/tags/*:refs/tags/*

      持续

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

        8
  •  3
  •   Giuseppe Monteleone    15 年前

    我创建了一个工具来完成这项任务。所使用的方法类似(内部生成一些东西,比如--filter branch),但更友好。是GPL2.0吗

    http://github.com/geppo12/GitCombineRepo

        9
  •  3
  •   user3622356    12 年前

    实际上,git stitch repo现在支持分支和标记,包括带注释的标记(我发现我报告了一个bug,它得到了修复)。我发现标签很有用。因为标签附在提交上,而一些解决方案(比如Eric Lee的方法)无法处理标签。您尝试从导入的标记创建一个分支,它将撤消任何git合并/移动,并将您发回,就像合并存储库与标记来自的存储库几乎相同一样。此外,如果在“合并/合并”的多个存储库中使用相同的标记,也会出现问题。例如,如果你有回购A和B,两者都有标签rel_1.0。您将repo A和repo 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
  •  2
  •   Patrick_O    17 年前

    你建议的顺序

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

    会有用,但你会失去你的提交历史记录。

        11
  •  1
  •   user123568943685    11 年前

    要在主项目中合并第二个项目,请执行以下操作:

    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
  •  0
  •   chrishiestand    10 年前

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

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

        13
  •  0
  •   bue    7 年前

    这个bash脚本围绕 sed tab character 问题(例如在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
    

    这是 miniquark , marius-butuc 和 ryan 我的帖子。为他们干杯!