我们希望永远从svn迁移到git,以便能够在分支和协作方面使用git的更好function。
我们目前的svn仓库看起来像这样
svnrepo/ frontend/ trunk branches/ ng/ ... tags/ 1.x ... backend/ trunk branches/ ng/ ... tags/ 1.x ...
工作布局是我们签出前端项目,在这里,我们创build一个后端文件夹,并签出后端项目。
我们现在想要迁移到git,并放弃前端和后端之间的分裂(就分开的项目而言),因为这给我们带来了更多的问题而不是优点。 我们希望他们都在一个单一的仓库。
我想使用svn2git进行转换。 不幸的是最新的发展发生在一个分支,而不是在树干,但我认为这不应该是svn2git的问题。 所以新的git仓库布局应该如下所示:
/ => svnrepo/frontend/branches/ng /backend => svnrepo/backend/branches/ng
其中=>表示“从…迁移/转换”。
对于转换,我们没有必要将所有标签和分支从svn仓库转换到git。 这对我们并不重要。 然而重要的是,我们拥有所有提交到分支/ ng目录中的所有文件的完整历史logging,并返回到主干中的分支以及在主干中发生的所有提交。 我们希望所有这些提交都是在单个git存储库中提到的布局。 这甚至有可能吗? 我们将如何做到这一点?
我已经search谷歌,也在1,2 ,但无法find我们的问题的确切解决scheme。
一个解决scheme是用svn2git或者git svn (这是一个已经embeddedgit的漂亮的小工具)分别生成每个版本库,然后用git filter-branch它们连接在一起。
git filter-branch上执行git filter-branch ,使用索引filter为它们生成一个新的子目录。 master (或任何你想要的分支)上。 完整的历史将被保留。 第3步的命令看起来像这样:
git filter-branch --index-filter ' git ls-files -s | perl -pe "s{\t\"?}{$&newsubdir/}" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE ' HEAD
神奇的,每次我必须这样做,它感觉有点像魔术,是perl声明。 git filter-branch 在每次提交时过滤索引,并用'newsubdir'前置所有blobpath(即改变工作树的文件path)。 您可能需要尝试四处获取path。 从以前走过这条路的人那里学到了一些教训:
git filter-branch是具有破坏性的历史。 一旦你改变它,你不能轻易地改回它。 请务必备份您使用的所有存储库副本。 没有什么比这更糟糕的是,完成一个复杂的操作,发现你错过了path。 git filter-branch非常耗费CPU资源。 深层历史logging上的索引filter可能需要几个小时才能在您的本地环境上运行,但在AWS 群集计算实例上只需要几分之一的时间。 当然,他们每小时花费超过2美元,但你只需要几个小时。 节省自己的痛苦,并使用在硬件上编写的脚本,使操作变得微不足道。 这是一个不错的午餐的代价。 其中一个解决scheme是将两个SVN项目库转换为2个Git仓库,然后将一个Git仓库作为另一个仓库的Git子模块 。
要将您的SVN仓库转换为Git仓库,您可以使用任何基于git-svn的脚本或SubGit 。 用最新的工具运行一个命令
$ subgit install path/to/svn/repository
转换的git仓库将在path / to / svn / repository / git。
然后你build立一个对Git仓库的访问权限,并添加一个作为另一个子仓库的子模块:
$ git clone <frontend_GitURL> frontend $ git co $ cd frontend $ git submodule add -b ng <backend_GitURL> backend
我能想到的是,除非svn2git (我不是专家)本地支持这个,否则这将需要一些极端的svn2git 。
问题是frontend的提交完全独立于backend的提交。 没有真正的方法来告诉哪个提交将映射到单个存储库中的哪个提交。 这使我们只有一个真正的select:历史将由两个分支合并在一起,代表了原来的项目的历史,然后一旦合并,新的分支是“更好的模式”。
从现在开始,我将假定你已经在svn-frontend分支中导入了svn-frontend ,在svn-backend导入了svn-backend分支,并且都包含了它们自己的历史logging。
第一个问题是修复svn-backend在backend/目录中:
git checkout svn-backend git filter-branch --index-filter ' git ls-files -s | perl -pe "s{\t\"?}{$&newsubdir/}" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
(请参阅本文档 ,以及@Christopher的答案)
现在,除非这些以某种方式包含相同的提交作为基础(不太可能,除非svn2git创build一些预定义的基本提交或…),我们必须做一个。 你应该从哪个分支开始。
git symbolic-ref HEAD refs/heads/svn-base rm .git/index git clean -dxf
Git不能跟踪空目录。 我从来没有testing过,看看这是否适用于根目录,但我的假设不是,所以less创build一个空的git忽略文件并提交:
touch .gitignore git add .gitignore git commit -m "Base for SVN branches"
让我们改写历史:
git rebase svn-base svn-frontend git rebase svn-base svn-backend
我们差不多完成了。 让我们现在创build主分支。 如果它已经存在:
git update-ref master "$head"
除此以外:
git branch master
让我们来看看:
git checkout master
最后,合并:
git merge svn-backend
这是一个好主意,标记旧的分支,然后删除它们:
git checkout svn-frontend git tag svn-frontend git branch -d svn-frontend git checkout svn-backend git tag svn-backend git branch -d svn-backend git checkout master git branch -d svn-base