git: 在两个 repo 之间 merge
事情是这样,有一个叫 scripts 的 git 库,里面放了一些所属不明的脚本。我希望把这些零散脚本管理起来,它们不属于任何一个项目,为每个脚本都建一个库又太小题大做,于是有了一个『脚本孤儿院』。
与此同时,另外有个项目在进行,到一定程度时,它需要引用前面脚本里的一个。我突然明白,那个脚本就是这个项目的一部分,它『找到组织』,该『认祖归宗』了!
剪切,粘贴?
最简单的方法当然是,从原库 剪切 相关文件, 粘贴 到新库,然后两边分别提交变更。
确实,文件管理权移交这件事完成了。但结果是,新库历史里这些文件是凭空出现的,之前的提交历史都丢失了。你当然可以在新库提交时,在message里提一下,这些文件原本是在哪个库,详细历史请参考该库历史。可如果有一天旧库不再维护呢?你无法保证两个库生命周期一致。
merge ?
怎么连带提交历史一起移动呢?Merge !Merge 就是为这个目的存在的。不过等等,merge 只能在 branch 之间进行吧,两个 repo 怎么做?(严格说,是 commit 之间,branch 也好 tag 也罢,在 git 里最后都是指向 commit 的指针)
fetch & merge
当然可以,只要把要合并来源,当做远程库,获取到目标库生成一个远程分支,再合并即可。
假设要做的是 repo-from
=> repo-target
的合并,按以下操作即可:
$ cd repo-target
$ git remote add other /local/path/to/repo-from # 不要忘了 git 支持的协议非常广泛,除了常用的http(s)和ssh,也支持文件夹作为远程库
$ git fetch other
$ git merge other/master # 这里假定你要合并master,别的分支改一下就好
完事之后再 git remote rm other
, 然后 git rebase
稍微调整一下提交历史就好(因为这样合并,会把完整历史引进来,但实际上你可能只要部分文件和相关的提交历史)。或者 fetch 后直接选择 rebase,把需要的 commit 挑选出来,不再赘述。
fast-export & fast-import
上述方法基本可以解决问题,非要挑剔的话:
- 要先 添加 远程库 再删掉
- 引入的历史可能太多
其实 git 自带了相关工具:
$ cd repo-from
$ git fast-export master~5..master | sed "s|refs/heads/master|refs/heads/other|" | (cd /path/to/repo-target && git fast-import)
$ cd repo-target
$ git merge other
通过 fast-export
+ pipe + fast-import
从一个库导入到另一个库。注意的地方有几个:
- 因为两边都是本地分支,所以为了避免重名,中间可以用 sed 改名
- fast-export 可以指定导入的范围,譬如上面就是获取 master 最后5个commit。不过注意的是,这个只是限制了commit 历史,文件还是会整个分支(也就是最新那个 commit 的所有祖先 commit)都包含进来。文件的筛选就要在merge 阶段做了。
介绍到这,内容并不复杂。 对于 Git 的使用,不排斥甚至推荐用 TortoiseGit 和 SourceTree 这样的 GUI 工具,为常规操作节省时间。但是了解命令可以做到更多!