考虑以下场景:

我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。

我如何将A合并为B,而不丢失任何方面的历史?


当前回答

如果要将项目a合并到项目b中:

cd path/to/project-b
git remote add project-a /path/to/project-a
git fetch project-a --tags
git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge
git remote remove project-a

摘自:git合并不同的存储库?

这种方法对我来说效果很好,它更短,在我看来更干净。

如果您想将project-a放到子目录中,可以使用gitfilter repo(不建议使用过滤器分支)。在上述命令之前运行以下命令:

cd path/to/project-a
git filter-repo --to-subdirectory-filter project-a

合并两个大型存储库,将其中一个放在子目录中的示例:https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

注意:--allow非相关历史参数仅在git>=2.9之后存在。请参阅Git-Git merge文档/-允许不相关的历史记录

更新:按照@jstadler的建议添加了--tags,以便保留标签。

其他回答

我遇到了类似的挑战,但在我的情况下,我们在回购协议a中开发了一个版本的代码库,然后将其克隆到新的回购协议B中,用于新版本的产品。修复了回购协议A中的一些错误后,我们需要将更改FI到回购协议B中

向指向回购a的回购B添加远程(git remote add…)拉动当前分支(我们没有使用master修复错误)(git pull-remoteForRepabugFixBranch)将合并推送到github

工作愉快:)

如果您想将来自存储库B分支的文件放在存储库a的子树中,并保留历史记录,请继续阅读。(在下面的示例中,我假设我们希望回购协议B的主分支合并为回购协议A的主分支。)

在回购协议A中,首先执行以下操作以使回购协议B可用:

git remote add B ../B # Add repo B as a new remote.
git fetch B

现在我们在回购a中创建了一个全新的分支(只有一个提交),我们称之为new_b_root。生成的提交将包含在repo B的主分支的第一次提交中提交的文件,但这些文件放在名为path/to/B-files/的子目录中。

git checkout --orphan new_b_root master
git rm -rf . # Remove all files.
git cherry-pick -n `git rev-list --max-parents=0 B/master`
mkdir -p path/to/b-files
git mv README path/to/b-files/
git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"

解释:checkout命令的--孤儿选项从A的主分支检出文件,但不创建任何提交。我们可以选择任何提交,因为接下来我们无论如何都要清除所有文件。然后,在尚未提交(-n)的情况下,我们从B的主分支中选择第一个提交。(cherry pick保留了原始的提交消息,而直接签出似乎无法做到这一点。)然后我们创建一个子树,将所有来自repo B的文件放在那里。然后我们必须将cherry stick中引入的所有文件移动到子树中。在上面的示例中,只有一个README文件可以移动。然后我们提交B-repo根提交,同时,我们还保留原始提交的时间戳。

现在,我们将在新创建的new_B_root上创建一个新的B/master分支。我们称新分支为b:

git checkout -b b B/master
git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root

现在,我们将b分支合并为A/master:

git checkout master
git merge --allow-unrelated-histories --no-commit b
git commit -m 'Merge repo B into repo A.'

最后,您可以删除B个远程和临时分支:

git remote remove B
git branch -D new_b_root b

最终图形的结构如下:

除了使用远程add->fetch->merge策略的所有答案之外:如果您想保留其他存储库中的标记,但不想将它们全部泄漏到一个公共命名空间中(可能会发生冲突),您可能需要稍微更改fetch命令:

git fetch --no-tags other_repo
git fetch --no-tags other_repo 'refs/tags/*:refs/tags/other_repo/*'

第一个命令像往常一样获取所有分支,但省略了附加到提交的标记,第二个命令也省略了通常的标记获取机制(git help fetch了解更多信息),并使用git的refspec功能获取将它们从X映射到other_repo/X的所有标记。

引用(分支、标记)只是git中的文件,您可以使用目录来命名。上面的两个命令将保留第一个存储库中的标记,而另一个存储库的标记将以other_repo作为前缀/

操作完成后,最好移除另一个遥控器,这样您就不会意外地以正常方式获取标签并造成混乱。

我今天必须按如下方式解决:项目A在bitbucket中,项目B在代码提交中。。这两个项目都是相同的,但必须将A到B的更改合并。(诀窍是在项目A中创建同名分支,与在项目B中相同)

git签出项目Agit远程删除原点git远程添加源项目Bgit结帐分行git加法*gitcommit-m“我们已经移动了代码”数字推送

与@Smar类似,但使用在PRIMARY和SECONDARY中设置的文件系统路径:

PRIMARY=~/Code/project1
SECONDARY=~/Code/project2
cd $PRIMARY
git remote add test $SECONDARY && git fetch test
git merge test/master

然后手动合并。

(改编自阿纳尔·马纳福夫的帖子)