git svn fetch error: 182135 = 1234abcdef already exists! Why are we refetching it?

2018年9月14日

症状

我有一个SVN仓库和Git-SVN仓库,两者进行同步。SVN仓库里面有几个提交时间戳不正确,同步到Git-SVN仓库后,时间戳也是不正确的。我手动修复了SVN仓库里的提交时间戳,如图1,然后如何修复Git-SVN仓库里的提交时间戳?

图1:r182136的时间戳已经被修复,现在要修改63a958bd的时间戳

影响

提交时间戳错误可能是微不足道的,但它会影响Git时间搜索功能。例如,在TortoiseGit里搜索1/1/2018到9/14/2018的提交,TortoiseGit就不会显示63a958bd之前的提交。因为Git发现63a958bd的时间戳突然变成1969年,便认为之后的提交都是不感兴趣的,作为优化,便不再继续搜索了。

解决方法

运行git svn reset -p 182135,182135是最早的要修复的提交,所以把git-svn设置成182135的parent,即182134。然后运行git svn fetch

按道理应该能正确运行,但是上述命令却提示错误

gqqnbig MINGW64 /c/LoansPQ2-Git-SVN (master)
$ git svn reset 182134
r182134 = c6a305c34c3aef88f086f9b00c91dfd840f9e573 (refs/remotes/git-svn)

gqqnbig MINGW64 /c/LoansPQ2-Git-SVN (master)
$ git svn fetch
Index mismatch: 5570880225692ca3ab9ff3d9d2b2859e37653249 != d10fc78290e99d8e263a647787c6826c39866125
rereading c6a305c34c3aef88f086f9b00c91dfd840f9e573
182135 = 4261a2eb954c1a2907dc2ec4c4691e0a417ad729 already exists! Why are we refetching it?
at C:/Program Files/Git/mingw64/share/perl5/Git/SVN/Ra.pm line 476.

尝试解决

具体错误原因我不是很了解,我一开始以为因为存在4261a2eb这个提交,但发现实际上不存在。我猜可能需要垃圾回收,运行了git gcgit svn gc都没用。

我尝试设置svn.ignore-path忽略r182135修改的文件,制造一个SHA1不同的提交,但也没用。

成功解决

我研究一下Git源代码看看到底是怎么回事。”Why are we refetching it?”存在于C:\Program Files\Git\mingw64\share\perl5\Git\SVN.pm,

sub do_git_commit {
	my ($self, $log_entry) = @_;
	my $lr = $self->last_rev;
	if (defined $lr && $lr >= $log_entry->{revision}) {
		die "Last fetched revision of ", $self->refname,
		    " was r$lr, but we are about to fetch: ",
		    "r$log_entry->{revision}!\n";
	}
	if (my $c = $self->rev_map_get($log_entry->{revision})) {
		croak "$log_entry->{revision} = $c already exists! ",
		      "Why are we refetching it?\n";
	}
	my $old_env = set_commit_header_env($log_entry);
	my $tree = $log_entry->{tree};
	if (!defined $tree) {
		$tree = $self->tmp_index_do(sub {
		                            command_oneline('write-tree') });
	}
	......
}

我把抛出错误的那几行注释掉,再运行git svn fetch,就可以成功获取提交了。然后,记得把文件改回来。

反思

我的这个方法可以算是奇技淫巧,问题的本质应该是git-svn在检查重复对象时没有考虑时间戳。正确的方法应该要考虑时间戳,再计算SHA1。鉴于Git是开源项目,应该有空修改一下并发起一个拉取请求。