Skip to content

Commit 61b472e

Browse files
Peter BaumannEric Wong
authored andcommitted
git svn: reset invalidates the memoized mergeinfo caches
Since v1.7.0-rc2~11 (git-svn: persistent memoization, 2010-01-30), git-svn has maintained some private per-repository caches in .git/svn/.caches to avoid refetching and recalculating some mergeinfo-related information with every 'git svn fetch'. This memoization can cause problems, e.g consider the following case: SVN repo: ... - a - b - c - m <- trunk \ / d - e <- branch1 The Git import of the above repo is at commit 'a' and doesn't know about the branch1. In case of an 'git svn rebase', only the trunk of the SVN repo is imported. During the creation of the git commit 'm', git svn uses the svn:mergeinfo property and tries to find the corresponding git commit 'e' to create 'm' with 'c' and 'e' as parents. But git svn rebase only imports the current branch so commit 'e' is not imported. Therefore git svn fails to create commit 'm' as a merge commit, because one of its parents is not known to git. The imported history looks like this: ... - a - b - c - m <- trunk A later 'git svn fetch' to import all branches can't rewrite the commit 'm' to add 'e' as a parent and to make it a real git merge commit, because it was already imported. That's why the imported history misses the merge and looks like this: ... - a - b - c - m <- trunk \ d - e <- branch1 Right now the only known workaround for importing 'm' as a merge is to force reimporting 'm' again from SVN, e.g. via $ git svn reset --revision $(git find-rev $c) $ git svn fetch Sadly, this is where the behavior has regressed: git svn reset doesn't invalidate the old mergeinfo cache, which is no longer valid for the reimport, which leads to 'm' beeing imprted with only 'c' as parent. As solution to this problem, this commit invalidates the mergeinfo cache to force correct recalculation of the parents. During development of this patch, several ways for invalidating the cache where considered. One of them is to use Memoize::flush_cache, which will call the CLEAR method on the underlying Memoize persistency implementation. Sadly, neither Memoize::Storable nor the newer Memoize::YAML module introduced in 68f532f could optionally be used implement the CLEAR method, so this is not an option. Reseting the internal hash used to store the memoized values has the same problem, because it calls the non-existing CLEAR method of the underlying persistency layer, too. Considering this and taking into account the different implementations of the memoization modules, where Memoize::Storable is not in our control, implementing the missing CLEAR method is not an option, at least not if Memoize::Storable is still used. Therefore the easiest solution to clear the cache is to delete the files on disk in 'git svn reset'. Normally, deleting the files behind the back of the memoization module would be problematic, because the in-memory representation would still exist and contain wrong data. Fortunately, the memoization is active in memory only for a small portion of the code. Invalidating the cache by deleting the files on disk if it isn't active should be safe. Signed-off-by: Peter Baumann <[email protected]> Signed-off-by: Steven Walter <[email protected]> Signed-off-by: Eric Wong <[email protected]>
1 parent e48fb75 commit 61b472e

File tree

2 files changed

+103
-2
lines changed

2 files changed

+103
-2
lines changed

perl/Git/SVN.pm

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,24 @@ sub tie_for_persistent_memoization {
16161616
Memoize::unmemoize 'has_no_changes';
16171617
}
16181618

1619+
sub clear_memoized_mergeinfo_caches {
1620+
die "Only call this method in non-memoized context" if ($memoized);
1621+
1622+
my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
1623+
return unless -d $cache_path;
1624+
1625+
for my $cache_file (("$cache_path/lookup_svn_merge",
1626+
"$cache_path/check_cherry_pick",
1627+
"$cache_path/has_no_changes")) {
1628+
for my $suffix (qw(yaml db)) {
1629+
my $file = "$cache_file.$suffix";
1630+
next unless -e $file;
1631+
unlink($file) or die "unlink($file) failed: $!\n";
1632+
}
1633+
}
1634+
}
1635+
1636+
16191637
Memoize::memoize 'Git::SVN::repos_root';
16201638
}
16211639

@@ -2107,8 +2125,13 @@ sub rev_map_set {
21072125

21082126
sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
21092127
or croak "Couldn't open $db_lock: $!\n";
2110-
$update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
2111-
_rev_map_set($fh, $rev, $commit);
2128+
if ($update_ref eq 'reset') {
2129+
clear_memoized_mergeinfo_caches();
2130+
_rev_map_reset($fh, $rev, $commit);
2131+
} else {
2132+
_rev_map_set($fh, $rev, $commit);
2133+
}
2134+
21122135
if ($sync) {
21132136
$fh->flush or die "Couldn't flush $db_lock: $!\n";
21142137
$fh->sync or die "Couldn't sync $db_lock: $!\n";
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2012 Peter Baumann
4+
#
5+
6+
test_description='git svn reset clears memoized caches'
7+
. ./lib-git-svn.sh
8+
9+
svn_ver="$(svn --version --quiet)"
10+
case $svn_ver in
11+
0.* | 1.[0-4].*)
12+
skip_all="skipping git-svn test - SVN too old ($svn_ver)"
13+
test_done
14+
;;
15+
esac
16+
17+
# ... a - b - m <- trunk
18+
# \ /
19+
# ... c <- branch1
20+
#
21+
# SVN Commits not interesting for this test are abbreviated with "..."
22+
#
23+
test_expect_success 'initialize source svn repo' '
24+
svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk &&
25+
svn_cmd mkdir -m "create branches" "$svnrepo/branches" &&
26+
svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
27+
(
28+
cd "$SVN_TREE" &&
29+
touch foo &&
30+
svn_cmd add foo &&
31+
svn_cmd commit -m "a" &&
32+
svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
33+
svn_cmd switch "$svnrepo"/branches/branch1 &&
34+
touch bar &&
35+
svn_cmd add bar &&
36+
svn_cmd commit -m b &&
37+
svn_cmd switch "$svnrepo"/trunk &&
38+
touch baz &&
39+
svn_cmd add baz &&
40+
svn_cmd commit -m c &&
41+
svn_cmd up &&
42+
svn_cmd merge "$svnrepo"/branches/branch1 &&
43+
svn_cmd commit -m "m"
44+
) &&
45+
rm -rf "$SVN_TREE"
46+
'
47+
48+
test_expect_success 'fetch to merge-base (a)' '
49+
git svn init -s "$svnrepo" &&
50+
git svn fetch --revision BASE:3
51+
'
52+
53+
# git svn rebase looses the merge commit
54+
#
55+
# ... a - b - m <- trunk
56+
# \
57+
# ... c
58+
#
59+
test_expect_success 'rebase looses SVN merge (m)' '
60+
git svn rebase &&
61+
git svn fetch &&
62+
test 1 = $(git cat-file -p master|grep parent|wc -l)
63+
'
64+
65+
# git svn fetch creates correct history with merge commit
66+
#
67+
# ... a - b - m <- trunk
68+
# \ /
69+
# ... c <- branch1
70+
#
71+
test_expect_success 'reset and fetch gets the SVN merge (m) correctly' '
72+
git svn reset -r 3 &&
73+
git reset --hard trunk &&
74+
git svn fetch &&
75+
test 2 = $(git cat-file -p trunk|grep parent|wc -l)
76+
'
77+
78+
test_done

0 commit comments

Comments
 (0)