我在一个名为XXX的文件夹中有一个Git存储库,还有一个名为YYY的Git存储库。
我想将XXX存储库作为名为ZZZ的子目录导入到YYY存储库中,并将所有XXX的更改历史添加到YYY中。
之前的文件夹结构:
├── XXX
│ ├── .git
│ └── (project files)
└── YYY
├── .git
└── (project files)
文件夹结构后:
YYY
├── .git <-- This now contains the change history from XXX
├── ZZZ <-- This was originally XXX
│ └── (project files)
└── (project files)
这可以做到吗,或者我必须使用子模块?
Git仓库本身就有一个著名的实例,在Git社区中被统称为“有史以来最酷的合并”(以Linus Torvalds在给Git邮件列表中描述这次合并的电子邮件的主题行命名)。在这种情况下,gitk Git GUI现在是Git的一部分,实际上曾经是一个单独的项目。Linus设法将该存储库合并到Git存储库中
它出现在Git存储库中,就好像它一直是作为Git的一部分开发的一样,
所有的历史都保存完好
它仍然可以在旧的存储库中独立开发,只需通过git提取更改即可。
电子邮件包含了复制所需的步骤,但不适合胆小的人:首先,Linus写了Git,所以他可能比你我知道得多一些;其次,这是近5年前的事情了,Git从那时起已经有了很大的改进,所以现在可能要容易得多。
特别是,我猜现在人们会在这种特定情况下使用gitk子模块。
下面是脚本,将立即工作。
#!/bin/bash -xe
# script name: merge-repo.sh
# To merge repositories into the current.
# To see the log of the new repo use 'git log --follow -- unprefixed-filename'
# So if the file is repo/test.cpp use 'git log --follow -- test.cpp'
# I'm not sure how this will work when two files have the same name.
#
# `git branch -a` will show newly created branches.
# You can delete them if you want.
merge_another() {
repo="$1" # url of the remote repo
rn="$2" # new name of the repo, you can keep the same name as well.
git remote add ${rn} ${repo}
git fetch ${rn}
git merge -s ours --no-commit --allow-unrelated-histories ${rn}/master
git read-tree --prefix=${rn}/ -u ${rn}/master
git commit -m "Imported ${rn} as a subtree."
git pull -s subtree ${rn} master
}
merge_another $1 $2
运行脚本。转到您希望合并另一个repo的repo,并运行脚本。
cd base-repo
./merge-repo.sh git@github.com:username/repo-to-be-merged.git repo-to-be-merged-new-name
现在将主分支上的更改推到remote/origin。根据您要做的事情,可能不需要此步骤。
git push origin master
简单的方法是使用git format-patch。
假设我们有两个git存储库foo和bar。
foo包含:
福.txt
。去
栏包含:
酒吧.txt
。去
我们希望以foo结束,其中包含bar历史记录和这些文件:
foo . txt。
git。
雷丁酒吧。. txt。
要做到这一点:
1. create a temporary directory eg PATH_YOU_WANT/patch-bar
2. go in bar directory
3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
4. go in foo directory
5. git am PATH_YOU_WANT/patch-bar/*
如果我们想重写所有从bar提交的消息,我们可以这样做,例如在Linux上:
git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD
这将在每条提交消息的开头添加“[bar]”。
Git-subtree正是为这样的用例设计的脚本:将多个存储库合并为一个,同时保留历史(和/或分割子树的历史,尽管这似乎与这个问题无关)。从1.7.11版开始,它作为git树的一部分发布。
要合并一个<repo> at revision <rev> as subdirectory <prefix>的仓库,使用git子树add,如下所示:
git subtree add -P <prefix> <repo> <rev>
Git-subtree以一种更用户友好的方式实现子树合并策略。
对于您的情况,在存储库YYY中,您将运行:
git subtree add -P ZZZ /path/to/XXX.git master
缺点是在合并的历史中文件没有前缀(不在子目录中)。因此,git log ZZZ/a会显示除了合并历史之外的所有更改(如果有的话)。你可以:
git log --follow -- a
但这不会显示合并历史中其他的变化。
换句话说,如果不更改存储库XXX中的ZZZ文件,则需要指定——follow和一个无前缀路径。如果在两个存储库中都更改它们,则有两个命令,其中没有一个显示所有更改。
这里有更多。
没有足够的代表给x-yuri的回答加上评论,但它工作得很漂亮,保存了历史。
我正在与两个工作的本地回购工作,并收到这个错误:
中止:拒绝破坏性地覆盖此后的回购历史
这看起来不像一个新的克隆体。
(预计新包装的回购)
请改用一个新的克隆体做手术。如果你想继续,那就用武力。
与其担心——force标志的含义,我先在本地克隆了这个repo:
cd tempDir
git clone <location of repo to be merged> --no-local
用这个新克隆的拷贝来执行x-尤里布置的一系列命令。
最后,在:git filter-repo——to-subdirectory-filter a中,a是你要导入的repo根文件夹的名称。
可能最简单的方法是将XXX的东西拉到YYY的分支中,然后合并到master中:
多:
git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master
git merge ZZZ --allow-unrelated-histories # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ # to get rid of the extra branch before pushing
git push # if you have a remote, that is
实际上,我刚刚用我的几个回购尝试了这个,它是有效的。不像Jörg的答案,它不会让你继续使用另一个回购,但我不认为你指定无论如何。
注意:由于这篇文章最初写于2009年,git添加了下面答案中提到的子树合并。我今天可能会用这个方法,当然这个方法仍然有效。