介绍
遗留项目使用的版本控制系统是 Mercurial,为了与他人协作方便,需要将其转换为 Git。
因为 Mercurial 与 Git 有着非常相似的模型,同时 Git 拥有强大的灵活性,将一个 Mercurial 仓库转换为 Git 仓库是相当直接的。
方法
使用 hg-fast-export
工具:
1
|
$ git clone https://github.com/frej/fast-export.git /tmp/fast-export
|
转换的第一步就是要先得到想要转换的 Mercurial 仓库的完整克隆:
1
|
$ hg clone <remote repo URL> /tmp/hg-repo
|
下一步就是创建一个作者映射文件。 Mercurial 对放入到变更集作者字段的内容比 Git 更宽容一些,所以这是一个清理的好机会。 只需要用到 bash 终端下的一行命令:
1
2
|
$ cd /tmp/hg-repo
$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors
|
这会花费几秒钟,具体要看项目提交历史有多少,最终 /tmp/authors 文件看起来会像这样:
1
|
Old Name <old@example.com>
|
在这个例子中,同一个人(Bob)使用不同的名字创建变更集,其中一个实际上是正确的,另一个完全不符合 Git 提交的规范。Hg-fast-export 通过向我们想要修改的行尾添加 ={new name and email address} 来修正这个问题,移除任何我们想要保留的用户名所在的行。如果所有的用户名看起来都是正确的,那我们根本就不需要这个文件。在本例中,我们会使文件看起来像这样:
1
|
"Old Name <old@example.com>"="New Name <new@example.com>"
|
新版本 fast-export 需要的是有双引号括起的原始字段与目标字段,可以使用 vim 替换下:
1
|
:%s/\(.*\)=\(.*\)/"\1"="\2"/
|
下一步是创建一个新的 Git 仓库,然后运行导出脚本:
1
2
3
|
$ git init /tmp/converted
$ cd /tmp/converted
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
|
-r 选项告诉 hg-fast-export 去哪里寻找我们想要转换的 Mercurial 仓库,-A 标记告诉它在哪找到作者映射文件。 这个脚本会分析 Mercurial 变更集然后将它们转换成 Git“fast-import”功能(我们将在之后详细讨论)需要的脚本。 这会花一点时间(尽管它比通过网格 更 快),输出相当的冗长:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
Loaded 4 authors
master: Exporting full revision 1/22208 with 13/0/0 added/changed/removed files
master: Exporting simple delta revision 2/22208 with 1/1/0 added/changed/removed files
master: Exporting simple delta revision 3/22208 with 0/1/0 added/changed/removed files
[…]
master: Exporting simple delta revision 22206/22208 with 0/4/0 added/changed/removed files
master: Exporting simple delta revision 22207/22208 with 0/2/0 added/changed/removed files
master: Exporting thorough delta revision 22208/22208 with 3/213/0 added/changed/removed files
Exporting tag [0.4c] at [hg r9] [git :10]
Exporting tag [0.4d] at [hg r16] [git :17]
[…]
Exporting tag [3.1-rc] at [hg r21926] [git :21927]
Exporting tag [3.1] at [hg r21973] [git :21974]
Issued 22315 commands
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects: 120000
Total objects: 115032 ( 208171 duplicates )
blobs : 40504 ( 205320 duplicates 26117 deltas of 39602 attempts)
trees : 52320 ( 2851 duplicates 47467 deltas of 47599 attempts)
commits: 22208 ( 0 duplicates 0 deltas of 0 attempts)
tags : 0 ( 0 duplicates 0 deltas of 0 attempts)
Total branches: 109 ( 2 loads )
marks: 1048576 ( 22208 unique )
atoms: 1952
Memory total: 7860 KiB
pools: 2235 KiB
objects: 5625 KiB
---------------------------------------------------------------------
pack_report: getpagesize() = 4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit = 8589934592
pack_report: pack_used_ctr = 90430
pack_report: pack_mmap_calls = 46771
pack_report: pack_open_windows = 1 / 1
pack_report: pack_mapped = 340852700 / 340852700
---------------------------------------------------------------------
|
1
2
3
|
$ git shortlog -sn
369 Bob Jones
365 Joe Smith
|
那看起来非常好。 所有 Mercurial 标签都已被转换成 Git 标签,Mercurial 分支与书签都被转换成 Git 分支。 现在已经准备好将仓库推送到新的服务器那边:
1
2
|
$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all
|
额外处理
无法推送到服务器
服务器端仓库在推送时发生问题:
1
2
3
4
5
6
7
8
|
$ git push -u origin master
Counting objects: 10302, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3488/3488), done.
remote: error: object 4d6b0a35ecbd348ed42c464fe12125754b994497: hasDotgit: contains '.git'
remote: fatal: Error in object
error: pack-objects died of signal 13
error: failed to push some refs to 'git@github.com:xxxxx/xxxxx.git'
|
解决方法
前往 BFG Repo-Cleaner by rtyley 下载 BFG Repo-Cleaner
运行以下命令来清理错误提交的 .git
目录文件:
1
2
|
$ cd converted_git_repository
$ java -jar ~/bfg-1.13.0.jar --delete-folders .git --delete-files .git --no-blob-protection .git
|
补充,清理完 .git
目录及文件后,需要执行以下命令,防止添加 .git
目录下的其他文件:
1
|
$ git reset --hard head
|
修改忽略文件
.hgignore
改为 .gitignore
需要手动修改 regex 语法为 glob 语法。
1
2
|
$ git mv .hgignore .gitignore
$ git commit -m "Git: Rename .hgignore to .gitignore"
|
本地设置
设置文件模式,以解决非 master
分支中文件模式的问题
1
2
|
$ cd repository
$ git config core.filemode false
|
git gui - How do I remove files saying “old mode 100755 new mode 100644” from unstaged changes in Git? - Stack Overflow
设置本地的忽略文件,以解决非 master
分支中忽略文件不存在的问题
1
2
3
4
|
$ cd repository
$ git checkout master .gitignore
$ git reset .gitignore
$ cp .gitignore .git/info/exclude
|
导入裸仓库到 GitLab 实例中
参考资料