2025-03-16 09:00:01

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

但这似乎不是一句俏皮话。git中有什么可以帮助我的吗?


你建议的顺序

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

将工作,但您将丢失提交历史。


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


下面是我给出的一个解决方案:

First do a complete backup of your phd directory: I don't want to be held responsible for your losing years of hard work! ;-) $ cp -r phd phd-backup Move the content of phd/code to phd/code/code, and fix the history so that it looks like it has always been there (this uses git's filter-branch command): $ 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 Same for the content of phd/figures and phd/thesis (just replace code with figures and thesis).

现在你的目录结构应该是这样的:

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

然后在根目录中创建一个git存储库,将所有内容都拉到其中,并删除旧的存储库: $ CD博士 $ git init $ git拉码 $ rm -rf code/code $ rm -rf代码/.git $ git拉数据-允许不相关的历史 $ rm -rf figures/数字 $ rm -rf数字/.git $ git拉论文-允许不相关的历史 $ 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的提交。


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

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

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


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 second revision]。头部-见:

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


你可以尝试子树合并策略。它可以让你将回购B合并到回购A。相对于git-filter-branch的优点是它不需要你重写回购A的历史记录(破坏SHA1和)。


我创造了一个工具来完成这项任务。使用的方法是类似的(内部做一些东西,如——filter-branch),但更友好。是GPL 2.0

http://github.com/geppo12/GitCombineRepo


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

First create directory which will contain composed repo and merged repos, create directory for each merged one. $ mkdir new_phd $ mkdir new_phd/code $ mkdir new_phd/figures $ mkdir new_phd/thesis Do a pull of each repository and fetch all tags. (Presenting instructions only for code sub-directory) $ cd new_phd/code $ git init $ git pull ../../original_phd/code master $ git fetch ../../original_phd/code refs/tags/*:refs/tags/* (This is improvement to point 2 in MiniQuark answer) Move the content of new_phd/code to new_phd/code/code and add code_ prefeix before each tag $ 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 After doing so there will be twice as many tags as it was before doing filter-branch. Old tags remain in repo and new tags with code_ prefix are added. $ git tag mytag1 code_mytag1 Remove old tags manually: $ ls .git/refs/tags/* | grep -v "/code_" | xargs rm Repeat point 2,3,4 for other subdirectories Now we have structure of directories as in @MiniQuark anwser point 3. Do as in point 4 of MiniQuark anwser, but after doing a pull and before removing .git dir, fetch tags: $ git fetch catalog refs/tags/*:refs/tags/* Continue..

这是另一个解。希望它能帮助别人,它帮助了我:)


亚里士多德Pagaltzis回答的git-stitch-repo只适用于具有简单线性历史的存储库。

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

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

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


Actually, git-stitch-repo now supports branches and tags, including annotated tags (I found there was a bug which I reported, and it got fixed). What i found useful is with tags. Since tags are attached to commits, and some of the solutions (like Eric Lee's approach) fails to deal with tags. You try to create a branch off an imported tag, and it will undo any git merges/moves and sends you back like the consolidated repository being near identical to the repository that the tag came from. Also, there are issues if you use the same tag across multiple repositories that you 'merged/consolidated'. For example, if you have repo's A ad B, both having tag rel_1.0. You merge repo A and repo B into repo AB. Since rel_1.0 tags are on two different commits (one for A and one for B), which tag will be visible in AB? Either the tag from the imported repo A or from imported repo B, but not both.

git-stitch-repo helps to address that problem by creating rel_1.0-A and rel_1.0-B tags. You may not be able to checkout rel_1.0 tag and expect both, but at least you can see both, and theoretically, you can merge them into a common local branch then create a rel_1.0 tag on that merged branch (assuming you just merge and not change source code). It's better to work with branches, as you can merge like branches from each repo into local branches. (dev-a and dev-b can be merged into a local dev branch which can then be pushed to origin).


合并mainProject中的secondProject:

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

我把解也写在这里。它基本上是一个围绕git过滤器分支的相当简单的bash脚本包装器。像其他解决方案一样,它只迁移主分支,而不迁移标签。但是完整的主提交历史被迁移了,它是一个简短的bash脚本,因此用户应该相对容易检查或调整。

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


这个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

这是迷你夸克,马里乌斯-布图克和瑞恩的哨子的组合。为他们干杯!


借助IntelliJ IDEA Community Edition中的git集成,我手动将3个git存储库合并为一个。

Create a new repo, add a new commit to the master branch with an empty README.md file. Add three remotes for the new repo, using the name of the 3 repositories and the remote URL of them respectively. Run Git Fetch. Create a new local branch named temp based on the master branch, so we can start over without pollute the master branch. Checkout the temp branch. Select to only show commits of one remote branch(one repository). Select all the commits and right click to Cherry-Pick them. Create directory structure for this repository, then move the files into it and commit. Repeat the step 4 to 6 for the other 2 remote branch(repository). When everything is OK, merge all the changes in the temp branch into master branch.

然后添加主分支的原始远程URL并推送到它。