考虑以下场景:
我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。
我如何将A合并为B,而不丢失任何方面的历史?
考虑以下场景:
我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。
我如何将A合并为B,而不丢失任何方面的历史?
当前回答
https://github.com/hraban/tomono作为基于脚本的解决方案的另一个例子。
我不是作者,但使用了它,它完成了任务。
一个积极的方面是,你将所有分支机构和所有历史记录纳入最终回购。对于我的repo(repo中没有重复的文件夹-实际上,它们来自tfs2git迁移),没有冲突,一切都是自动运行的。
它主要用于(参见名称)创建monoreos。
对于Windows用户:gitbash可以执行.sh文件。它带有标准的git安装。
其他回答
以下是两种可能的解决方案:
子模块
要么将存储库A复制到较大项目B中的单独目录中,要么(也许更好)将存储库B克隆到项目B的子目录中。然后使用git子模块将此存储库设置为存储库B的子模块。
对于松散耦合的存储库来说,这是一个很好的解决方案,存储库a中的开发仍在继续,而开发的主要部分是a中的独立开发。另请参阅GitWiki上的SubmoduleSupport和GitSubmoduleTutorial页面。
子树合并
您可以使用子树合并策略将存储库A合并到项目B的子目录中。Markus Prinz在《子树合并与你》中描述了这一点。
git remote add -f Bproject /path/to/B
git merge -s ours --allow-unrelated-histories --no-commit Bproject/master
git read-tree --prefix=dir-B/ -u Bproject/master
git commit -m "Merge B project as our subdirectory"
git pull -s subtree Bproject master
(选项--Git>=2.9.0需要允许不相关的历史记录。)
或者你可以使用apenwarr(Avery Pennarun)的git子树工具(GitHub上的存储库),例如,在他的博客文章《git子模块的新替代方案:git子树》中宣布了这一点。
我认为在您的情况下(A是大型项目B的一部分),正确的解决方案是使用子树合并。
我遇到了类似的挑战,但在我的情况下,我们在回购协议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
最终图形的结构如下:
与@Smar类似,但使用在PRIMARY和SECONDARY中设置的文件系统路径:
PRIMARY=~/Code/project1
SECONDARY=~/Code/project2
cd $PRIMARY
git remote add test $SECONDARY && git fetch test
git merge test/master
然后手动合并。
(改编自阿纳尔·马纳福夫的帖子)
git子树很好,但它可能不是您想要的。
例如,如果projectA是在B中创建的目录,在git子树之后,
git log projectA
仅列出一个提交:合并。合并项目的提交针对不同的路径,因此不会显示。
格雷格·休吉尔(Greg Hewgill)的答案最接近,但实际上并没有说明如何重写路径。
解决方案出奇地简单。
(1) 在A中,
PREFIX=projectA #adjust this
git filter-branch --index-filter '
git ls-files -s |
sed "s,\t,&'"$PREFIX"'/," |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD
注意:这将重写历史;你可能想先备份a。
注意Bene:如果在文件名或路径中使用非ascii字符(或白色字符),则必须修改sed命令中的替代脚本。在这种情况下,“ls files-s”生成的记录中的文件位置以引号开头。
(2) 然后在B中,运行
git pull path/to/A
瞧!在B中有一个projectA目录。如果运行git-log-projectA,您将看到a中的所有提交。
在我的例子中,我需要两个子目录,projectA和projectB。在这种情况下,我也执行了步骤(1)到B。