* [PATCH] git svn: reset invalidates the memoized mergeinfo caches
@ 2012-08-07 20:02 Peter Baumann
2012-08-07 20:45 ` Eric Wong
0 siblings, 1 reply; 9+ messages in thread
From: Peter Baumann @ 2012-08-07 20:02 UTC (permalink / raw)
To: git
Cc: Eric Wong, Andrew Myrick, Jonathan Nieder, Steven Walter,
Junio C Hamano, git
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 68f532f4ba888 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 <waste.manager@gmx.de>
---
perl/Git/SVN.pm | 25 ++++++++++-
t/t9163-git-svn-reset-clears-caches.sh | 79 ++++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 2 deletions(-)
create mode 100755 t/t9163-git-svn-reset-clears-caches.sh
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 0889145..430b366 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -1634,6 +1634,22 @@ sub tie_for_persistent_memoization {
Memoize::unmemoize 'has_no_changes';
}
+ sub clear_memoized_mergeinfo_caches {
+ die "Only call this method in non-memoized context" if ($memoized);
+
+ my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
+ return unless -d $cache_path;
+
+ for my $cache_file (("$cache_path/lookup_svn_merge",
+ "$cache_path/check_cherry_pick",
+ "$cache_path/has_no_changes")) {
+ for my $suffix (qw(yaml db)) {
+ unlink("$cache_file.$suffix");
+ }
+ }
+ }
+
+
Memoize::memoize 'Git::SVN::repos_root';
}
@@ -2126,8 +2142,13 @@ sub rev_map_set {
sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
or croak "Couldn't open $db_lock: $!\n";
- $update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
- _rev_map_set($fh, $rev, $commit);
+ if ($update_ref eq 'reset') {
+ _rev_map_reset($fh, $rev, $commit);
+ clear_memoized_mergeinfo_caches();
+ } else {
+ _rev_map_set($fh, $rev, $commit);
+ }
+
if ($sync) {
$fh->flush or die "Couldn't flush $db_lock: $!\n";
$fh->sync or die "Couldn't sync $db_lock: $!\n";
diff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh
new file mode 100755
index 0000000..d604dfd
--- /dev/null
+++ b/t/t9163-git-svn-reset-clears-caches.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Peter Baumann
+#
+
+test_description='git svn reset clears memoized caches'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+ skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+ test_done
+ ;;
+esac
+
+# ... a - b - m <- trunk
+# \ /
+# ... c <- branch1
+#
+# ... = SVN Commits not interesting for this test, e.g. commits creating
+# the SVN repo layout
+#
+test_expect_success 'initialize source svn repo' '
+ svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk &&
+ svn_cmd mkdir -m "create branches" "$svnrepo/branches" &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ touch foo &&
+ svn add foo &&
+ svn commit -m "a" &&
+ svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+ svn switch "$svnrepo"/branches/branch1 &&
+ touch bar &&
+ svn add bar &&
+ svn commit -m b &&
+ svn switch "$svnrepo"/trunk &&
+ touch baz &&
+ svn add baz &&
+ svn commit -m c &&
+ svn up &&
+ svn merge "$svnrepo"/branches/branch1 &&
+ svn commit -m "m"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'fetch to merge-base (a)' '
+ git svn init -s "$svnrepo" &&
+ git svn fetch --revision BASE:3
+'
+
+# git svn rebase looses the merge commit
+#
+# ... a - b - m <- trunk
+# \
+# ... c
+#
+test_expect_success 'rebase looses SVN merge (m)' '
+ git svn rebase &&
+ git svn fetch &&
+ test 1 = $(git cat-file -p master|grep parent|wc -l)
+'
+
+# git svn fetch creates correct history with merge commit
+#
+# ... a - b - m <- trunk
+# \ /
+# ... c <- branch1
+#
+test_expect_success 'reset and fetch gets the SVN merge (m) correctly' '
+ git svn reset -r 3 &&
+ git reset --hard trunk &&
+ git svn fetch &&
+ test 2 = $(git cat-file -p trunk|grep parent|wc -l)
+'
+
+test_done
--
1.7.12.rc0.10.g476109f
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] git svn: reset invalidates the memoized mergeinfo caches
2012-08-07 20:02 [PATCH] git svn: reset invalidates the memoized mergeinfo caches Peter Baumann
@ 2012-08-07 20:45 ` Eric Wong
2012-08-08 5:41 ` Peter Baumann
0 siblings, 1 reply; 9+ messages in thread
From: Eric Wong @ 2012-08-07 20:45 UTC (permalink / raw)
To: Peter Baumann
Cc: git, Andrew Myrick, Jonathan Nieder, Steven Walter,
Junio C Hamano
Peter Baumann <waste.manager@gmx.de> wrote:
> 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.
Thanks for the patch and explanation. A few comments below:
> + sub clear_memoized_mergeinfo_caches {
> + die "Only call this method in non-memoized context" if ($memoized);
> +
> + my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
> + return unless -d $cache_path;
> +
> + for my $cache_file (("$cache_path/lookup_svn_merge",
> + "$cache_path/check_cherry_pick",
> + "$cache_path/has_no_changes")) {
> + for my $suffix (qw(yaml db)) {
> + unlink("$cache_file.$suffix");
Need to check for unlink() errors (and ignore ENOENT).
> @@ -2126,8 +2142,13 @@ sub rev_map_set {
>
> sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
> or croak "Couldn't open $db_lock: $!\n";
> - $update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
> - _rev_map_set($fh, $rev, $commit);
> + if ($update_ref eq 'reset') {
> + _rev_map_reset($fh, $rev, $commit);
> + clear_memoized_mergeinfo_caches();
Better to clear_memoized_mergeinfo_caches() before _rev_map_reset()
in case unlink() (or anything else) fails when clearing the cache.
> +test_expect_success 'initialize source svn repo' '
> + svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk &&
> + svn_cmd mkdir -m "create branches" "$svnrepo/branches" &&
> + svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
> + (
> + cd "$SVN_TREE" &&
> + touch foo &&
> + svn add foo &&
svn_cmd here, too.
> + svn commit -m "a" &&
> + svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
> + svn switch "$svnrepo"/branches/branch1 &&
> + touch bar &&
> + svn add bar &&
> + svn commit -m b &&
> + svn switch "$svnrepo"/trunk &&
> + touch baz &&
> + svn add baz &&
> + svn commit -m c &&
> + svn up &&
> + svn merge "$svnrepo"/branches/branch1 &&
> + svn commit -m "m"
> + ) &&
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] git svn: reset invalidates the memoized mergeinfo caches
2012-08-07 20:45 ` Eric Wong
@ 2012-08-08 5:41 ` Peter Baumann
2012-08-08 22:52 ` Eric Wong
0 siblings, 1 reply; 9+ messages in thread
From: Peter Baumann @ 2012-08-08 5:41 UTC (permalink / raw)
To: Eric Wong
Cc: git, Andrew Myrick, Jonathan Nieder, Steven Walter,
Junio C Hamano
On Tue, Aug 07, 2012 at 08:45:10PM +0000, Eric Wong wrote:
> Peter Baumann <waste.manager@gmx.de> wrote:
> > 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.
>
> Thanks for the patch and explanation. A few comments below:
>
> > + sub clear_memoized_mergeinfo_caches {
> > + die "Only call this method in non-memoized context" if ($memoized);
> > +
> > + my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
> > + return unless -d $cache_path;
> > +
> > + for my $cache_file (("$cache_path/lookup_svn_merge",
> > + "$cache_path/check_cherry_pick",
> > + "$cache_path/has_no_changes")) {
> > + for my $suffix (qw(yaml db)) {
> > + unlink("$cache_file.$suffix");
>
> Need to check for unlink() errors (and ignore ENOENT).
I'm not sure what you mean here: Aren't we screwed either way if unlinking
the file failed? There is nothhing we can do about it if e.g. the user doesn't
have the permissions to delete the file, besides terminating, e.g.
for my $cache_file (("$cache_path/lookup_svn_merge",
"$cache_path/check_cherry_pick",
"$cache_path/has_no_changes")) {
for my $suffix (qw(yaml db)) {
next unless (-e "$cache_file.$suffix");
unlink("$cache_file.$suffix") or
die "Failed to delete $cache_file.$suffix";
}
}
>
> > @@ -2126,8 +2142,13 @@ sub rev_map_set {
> >
> > sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
> > or croak "Couldn't open $db_lock: $!\n";
> > - $update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
> > - _rev_map_set($fh, $rev, $commit);
> > + if ($update_ref eq 'reset') {
> > + _rev_map_reset($fh, $rev, $commit);
> > + clear_memoized_mergeinfo_caches();
>
> Better to clear_memoized_mergeinfo_caches() before _rev_map_reset()
> in case unlink() (or anything else) fails when clearing the cache.
Will do.
>
> > +test_expect_success 'initialize source svn repo' '
> > + svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk &&
> > + svn_cmd mkdir -m "create branches" "$svnrepo/branches" &&
> > + svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
> > + (
> > + cd "$SVN_TREE" &&
> > + touch foo &&
> > + svn add foo &&
>
> svn_cmd here, too.
Will do.
>
> > + svn commit -m "a" &&
> > + svn cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
> > + svn switch "$svnrepo"/branches/branch1 &&
> > + touch bar &&
> > + svn add bar &&
> > + svn commit -m b &&
> > + svn switch "$svnrepo"/trunk &&
> > + touch baz &&
> > + svn add baz &&
> > + svn commit -m c &&
> > + svn up &&
> > + svn merge "$svnrepo"/branches/branch1 &&
> > + svn commit -m "m"
> > + ) &&
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] git svn: reset invalidates the memoized mergeinfo caches
2012-08-08 5:41 ` Peter Baumann
@ 2012-08-08 22:52 ` Eric Wong
2012-08-09 6:42 ` [PATCH v2] " Peter Baumann
0 siblings, 1 reply; 9+ messages in thread
From: Eric Wong @ 2012-08-08 22:52 UTC (permalink / raw)
To: Peter Baumann
Cc: git, Andrew Myrick, Jonathan Nieder, Steven Walter,
Junio C Hamano
Peter Baumann <waste.manager@gmx.de> wrote:
> On Tue, Aug 07, 2012 at 08:45:10PM +0000, Eric Wong wrote:
> > Peter Baumann <waste.manager@gmx.de> wrote:
> > > + for my $suffix (qw(yaml db)) {
> > > + unlink("$cache_file.$suffix");
> >
> > Need to check for unlink() errors (and ignore ENOENT).
>
> I'm not sure what you mean here: Aren't we screwed either way if unlinking
> the file failed? There is nothhing we can do about it if e.g. the user doesn't
> have the permissions to delete the file, besides terminating, e.g.
>
> for my $cache_file (("$cache_path/lookup_svn_merge",
> "$cache_path/check_cherry_pick",
> "$cache_path/has_no_changes")) {
> for my $suffix (qw(yaml db)) {
> next unless (-e "$cache_file.$suffix");
> unlink("$cache_file.$suffix") or
> die "Failed to delete $cache_file.$suffix";
> }
Yes we're screwed, but silent failure is the worst way to fail,
especially if it can lead us back to the problems your patch is meant to
address.
Perhaps something like this (with $! to show the error):
my $file = "$cache_file.$suffix";
next unless -e $file;
unlink($file) or die "unlink($file) failed: $!\n";
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] git svn: reset invalidates the memoized mergeinfo caches
2012-08-08 22:52 ` Eric Wong
@ 2012-08-09 6:42 ` Peter Baumann
2012-08-09 17:35 ` Steven Walter
2012-08-10 20:22 ` Eric Wong
0 siblings, 2 replies; 9+ messages in thread
From: Peter Baumann @ 2012-08-09 6:42 UTC (permalink / raw)
To: Eric Wong
Cc: git, Andrew Myrick, Jonathan Nieder, Steven Walter,
Junio C Hamano
On Wed, Aug 08, 2012 at 10:52:58PM +0000, Eric Wong wrote:
> Peter Baumann <waste.manager@gmx.de> wrote:
> > On Tue, Aug 07, 2012 at 08:45:10PM +0000, Eric Wong wrote:
> > > Peter Baumann <waste.manager@gmx.de> wrote:
> > > > + for my $suffix (qw(yaml db)) {
> > > > + unlink("$cache_file.$suffix");
> > >
> > > Need to check for unlink() errors (and ignore ENOENT).
> >
> > I'm not sure what you mean here: Aren't we screwed either way if unlinking
> > the file failed? There is nothhing we can do about it if e.g. the user doesn't
> > have the permissions to delete the file, besides terminating, e.g.
> >
> > for my $cache_file (("$cache_path/lookup_svn_merge",
> > "$cache_path/check_cherry_pick",
> > "$cache_path/has_no_changes")) {
> > for my $suffix (qw(yaml db)) {
> > next unless (-e "$cache_file.$suffix");
> > unlink("$cache_file.$suffix") or
> > die "Failed to delete $cache_file.$suffix";
> > }
>
> Yes we're screwed, but silent failure is the worst way to fail,
> especially if it can lead us back to the problems your patch is meant to
> address.
>
> Perhaps something like this (with $! to show the error):
>
> my $file = "$cache_file.$suffix";
> next unless -e $file;
> unlink($file) or die "unlink($file) failed: $!\n";
First, let me thank you for your review and your detailed explanation.
I really appreciate it.
I changed svn to svn_cmd in the test (and also some minor changes to
the comments and removing trailing whitespace) and switched the order of
clear_memoized_mergeinfo_caches and _rev_map_reset as you asked for, so
hopefully this is ready to go in.
-- 8< --
From: Peter Baumann <waste.manager@gmx.de>
Subject: [PATCH] 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 68f532f4ba888 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 <waste.manager@gmx.de>
---
perl/Git/SVN.pm | 27 +++++++++++-
t/t9163-git-svn-reset-clears-caches.sh | 78 ++++++++++++++++++++++++++++++++++
2 files changed, 103 insertions(+), 2 deletions(-)
create mode 100755 t/t9163-git-svn-reset-clears-caches.sh
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 0889145..acb2539 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -1634,6 +1634,24 @@ sub tie_for_persistent_memoization {
Memoize::unmemoize 'has_no_changes';
}
+ sub clear_memoized_mergeinfo_caches {
+ die "Only call this method in non-memoized context" if ($memoized);
+
+ my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
+ return unless -d $cache_path;
+
+ for my $cache_file (("$cache_path/lookup_svn_merge",
+ "$cache_path/check_cherry_pick",
+ "$cache_path/has_no_changes")) {
+ for my $suffix (qw(yaml db)) {
+ my $file = "$cache_file.$suffix";
+ next unless -e $file;
+ unlink($file) or die "unlink($file) failed: $!\n";
+ }
+ }
+ }
+
+
Memoize::memoize 'Git::SVN::repos_root';
}
@@ -2126,8 +2144,13 @@ sub rev_map_set {
sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
or croak "Couldn't open $db_lock: $!\n";
- $update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
- _rev_map_set($fh, $rev, $commit);
+ if ($update_ref eq 'reset') {
+ clear_memoized_mergeinfo_caches();
+ _rev_map_reset($fh, $rev, $commit);
+ } else {
+ _rev_map_set($fh, $rev, $commit);
+ }
+
if ($sync) {
$fh->flush or die "Couldn't flush $db_lock: $!\n";
$fh->sync or die "Couldn't sync $db_lock: $!\n";
diff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh
new file mode 100755
index 0000000..cd4c662
--- /dev/null
+++ b/t/t9163-git-svn-reset-clears-caches.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Peter Baumann
+#
+
+test_description='git svn reset clears memoized caches'
+. ./lib-git-svn.sh
+
+svn_ver="$(svn --version --quiet)"
+case $svn_ver in
+0.* | 1.[0-4].*)
+ skip_all="skipping git-svn test - SVN too old ($svn_ver)"
+ test_done
+ ;;
+esac
+
+# ... a - b - m <- trunk
+# \ /
+# ... c <- branch1
+#
+# SVN Commits not interesting for this test are abbreviated with "..."
+#
+test_expect_success 'initialize source svn repo' '
+ svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk &&
+ svn_cmd mkdir -m "create branches" "$svnrepo/branches" &&
+ svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
+ (
+ cd "$SVN_TREE" &&
+ touch foo &&
+ svn_cmd add foo &&
+ svn_cmd commit -m "a" &&
+ svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
+ svn_cmd switch "$svnrepo"/branches/branch1 &&
+ touch bar &&
+ svn_cmd add bar &&
+ svn_cmd commit -m b &&
+ svn_cmd switch "$svnrepo"/trunk &&
+ touch baz &&
+ svn_cmd add baz &&
+ svn_cmd commit -m c &&
+ svn_cmd up &&
+ svn_cmd merge "$svnrepo"/branches/branch1 &&
+ svn_cmd commit -m "m"
+ ) &&
+ rm -rf "$SVN_TREE"
+'
+
+test_expect_success 'fetch to merge-base (a)' '
+ git svn init -s "$svnrepo" &&
+ git svn fetch --revision BASE:3
+'
+
+# git svn rebase looses the merge commit
+#
+# ... a - b - m <- trunk
+# \
+# ... c
+#
+test_expect_success 'rebase looses SVN merge (m)' '
+ git svn rebase &&
+ git svn fetch &&
+ test 1 = $(git cat-file -p master|grep parent|wc -l)
+'
+
+# git svn fetch creates correct history with merge commit
+#
+# ... a - b - m <- trunk
+# \ /
+# ... c <- branch1
+#
+test_expect_success 'reset and fetch gets the SVN merge (m) correctly' '
+ git svn reset -r 3 &&
+ git reset --hard trunk &&
+ git svn fetch &&
+ test 2 = $(git cat-file -p trunk|grep parent|wc -l)
+'
+
+test_done
--
1.7.12.rc0.10.g476109f
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] git svn: reset invalidates the memoized mergeinfo caches
2012-08-09 6:42 ` [PATCH v2] " Peter Baumann
@ 2012-08-09 17:35 ` Steven Walter
2012-08-10 20:22 ` Eric Wong
1 sibling, 0 replies; 9+ messages in thread
From: Steven Walter @ 2012-08-09 17:35 UTC (permalink / raw)
To: Peter Baumann
Cc: Eric Wong, git, Andrew Myrick, Jonathan Nieder, Junio C Hamano
Signed-off-by: Steven Walter <stevenrwalter@gmail.com>
On Thu, Aug 9, 2012 at 2:42 AM, Peter Baumann <waste.manager@gmx.de> wrote:
> On Wed, Aug 08, 2012 at 10:52:58PM +0000, Eric Wong wrote:
>> Peter Baumann <waste.manager@gmx.de> wrote:
>> > On Tue, Aug 07, 2012 at 08:45:10PM +0000, Eric Wong wrote:
>> > > Peter Baumann <waste.manager@gmx.de> wrote:
>> > > > + for my $suffix (qw(yaml db)) {
>> > > > + unlink("$cache_file.$suffix");
>> > >
>> > > Need to check for unlink() errors (and ignore ENOENT).
>> >
>> > I'm not sure what you mean here: Aren't we screwed either way if unlinking
>> > the file failed? There is nothhing we can do about it if e.g. the user doesn't
>> > have the permissions to delete the file, besides terminating, e.g.
>> >
>> > for my $cache_file (("$cache_path/lookup_svn_merge",
>> > "$cache_path/check_cherry_pick",
>> > "$cache_path/has_no_changes")) {
>> > for my $suffix (qw(yaml db)) {
>> > next unless (-e "$cache_file.$suffix");
>> > unlink("$cache_file.$suffix") or
>> > die "Failed to delete $cache_file.$suffix";
>> > }
>>
>> Yes we're screwed, but silent failure is the worst way to fail,
>> especially if it can lead us back to the problems your patch is meant to
>> address.
>>
>> Perhaps something like this (with $! to show the error):
>>
>> my $file = "$cache_file.$suffix";
>> next unless -e $file;
>> unlink($file) or die "unlink($file) failed: $!\n";
>
> First, let me thank you for your review and your detailed explanation.
> I really appreciate it.
>
> I changed svn to svn_cmd in the test (and also some minor changes to
> the comments and removing trailing whitespace) and switched the order of
> clear_memoized_mergeinfo_caches and _rev_map_reset as you asked for, so
> hopefully this is ready to go in.
>
> -- 8< --
> From: Peter Baumann <waste.manager@gmx.de>
> Subject: [PATCH] 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 68f532f4ba888 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 <waste.manager@gmx.de>
> ---
> perl/Git/SVN.pm | 27 +++++++++++-
> t/t9163-git-svn-reset-clears-caches.sh | 78 ++++++++++++++++++++++++++++++++++
> 2 files changed, 103 insertions(+), 2 deletions(-)
> create mode 100755 t/t9163-git-svn-reset-clears-caches.sh
>
> diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
> index 0889145..acb2539 100644
> --- a/perl/Git/SVN.pm
> +++ b/perl/Git/SVN.pm
> @@ -1634,6 +1634,24 @@ sub tie_for_persistent_memoization {
> Memoize::unmemoize 'has_no_changes';
> }
>
> + sub clear_memoized_mergeinfo_caches {
> + die "Only call this method in non-memoized context" if ($memoized);
> +
> + my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
> + return unless -d $cache_path;
> +
> + for my $cache_file (("$cache_path/lookup_svn_merge",
> + "$cache_path/check_cherry_pick",
> + "$cache_path/has_no_changes")) {
> + for my $suffix (qw(yaml db)) {
> + my $file = "$cache_file.$suffix";
> + next unless -e $file;
> + unlink($file) or die "unlink($file) failed: $!\n";
> + }
> + }
> + }
> +
> +
> Memoize::memoize 'Git::SVN::repos_root';
> }
>
> @@ -2126,8 +2144,13 @@ sub rev_map_set {
>
> sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
> or croak "Couldn't open $db_lock: $!\n";
> - $update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
> - _rev_map_set($fh, $rev, $commit);
> + if ($update_ref eq 'reset') {
> + clear_memoized_mergeinfo_caches();
> + _rev_map_reset($fh, $rev, $commit);
> + } else {
> + _rev_map_set($fh, $rev, $commit);
> + }
> +
> if ($sync) {
> $fh->flush or die "Couldn't flush $db_lock: $!\n";
> $fh->sync or die "Couldn't sync $db_lock: $!\n";
> diff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh
> new file mode 100755
> index 0000000..cd4c662
> --- /dev/null
> +++ b/t/t9163-git-svn-reset-clears-caches.sh
> @@ -0,0 +1,78 @@
> +#!/bin/sh
> +#
> +# Copyright (c) 2012 Peter Baumann
> +#
> +
> +test_description='git svn reset clears memoized caches'
> +. ./lib-git-svn.sh
> +
> +svn_ver="$(svn --version --quiet)"
> +case $svn_ver in
> +0.* | 1.[0-4].*)
> + skip_all="skipping git-svn test - SVN too old ($svn_ver)"
> + test_done
> + ;;
> +esac
> +
> +# ... a - b - m <- trunk
> +# \ /
> +# ... c <- branch1
> +#
> +# SVN Commits not interesting for this test are abbreviated with "..."
> +#
> +test_expect_success 'initialize source svn repo' '
> + svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk &&
> + svn_cmd mkdir -m "create branches" "$svnrepo/branches" &&
> + svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
> + (
> + cd "$SVN_TREE" &&
> + touch foo &&
> + svn_cmd add foo &&
> + svn_cmd commit -m "a" &&
> + svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 &&
> + svn_cmd switch "$svnrepo"/branches/branch1 &&
> + touch bar &&
> + svn_cmd add bar &&
> + svn_cmd commit -m b &&
> + svn_cmd switch "$svnrepo"/trunk &&
> + touch baz &&
> + svn_cmd add baz &&
> + svn_cmd commit -m c &&
> + svn_cmd up &&
> + svn_cmd merge "$svnrepo"/branches/branch1 &&
> + svn_cmd commit -m "m"
> + ) &&
> + rm -rf "$SVN_TREE"
> +'
> +
> +test_expect_success 'fetch to merge-base (a)' '
> + git svn init -s "$svnrepo" &&
> + git svn fetch --revision BASE:3
> +'
> +
> +# git svn rebase looses the merge commit
> +#
> +# ... a - b - m <- trunk
> +# \
> +# ... c
> +#
> +test_expect_success 'rebase looses SVN merge (m)' '
> + git svn rebase &&
> + git svn fetch &&
> + test 1 = $(git cat-file -p master|grep parent|wc -l)
> +'
> +
> +# git svn fetch creates correct history with merge commit
> +#
> +# ... a - b - m <- trunk
> +# \ /
> +# ... c <- branch1
> +#
> +test_expect_success 'reset and fetch gets the SVN merge (m) correctly' '
> + git svn reset -r 3 &&
> + git reset --hard trunk &&
> + git svn fetch &&
> + test 2 = $(git cat-file -p trunk|grep parent|wc -l)
> +'
> +
> +test_done
> --
> 1.7.12.rc0.10.g476109f
>
--
-Steven Walter <stevenrwalter@gmail.com>
"The rotter who simpers that he sees no difference between the power
of the dollar and the power of the whip, ought to learn the difference
on his own hide."
-Francisco d'Anconia, Atlas Shrugged
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] git svn: reset invalidates the memoized mergeinfo caches
2012-08-09 6:42 ` [PATCH v2] " Peter Baumann
2012-08-09 17:35 ` Steven Walter
@ 2012-08-10 20:22 ` Eric Wong
2012-08-10 21:29 ` Junio C Hamano
1 sibling, 1 reply; 9+ messages in thread
From: Eric Wong @ 2012-08-10 20:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Andrew Myrick, Jonathan Nieder, Steven Walter, Peter Baumann
Peter Baumann <waste.manager@gmx.de> wrote:
> First, let me thank you for your review and your detailed explanation.
> I really appreciate it.
You're welcome, Peter. Thanks again for the patch. I've signed-off and
pushed for Junio.
The following changes since commit 034161a94e827ef05790b1c7ce5a6e3e740c864e:
Merge git://github.com/git-l10n/git-po (2012-08-09 10:51:46 -0700)
are available in the git repository at:
git://bogomips.org/git-svn for-git-master
for you to fetch changes up to 61b472ed8b090a3e9240590c85041120a54dd268:
git svn: reset invalidates the memoized mergeinfo caches (2012-08-10 19:53:18 +0000)
----------------------------------------------------------------
Peter Baumann (1):
git svn: reset invalidates the memoized mergeinfo caches
Robert Luberda (1):
git svn: handle errors and concurrent commits in dcommit
git-svn.perl | 74 ++++++++---
perl/Git/SVN.pm | 27 ++++-
t/t9163-git-svn-reset-clears-caches.sh | 78 ++++++++++++
t/t9164-git-svn-dcommit-concrrent.sh | 216 +++++++++++++++++++++++++++++++++
4 files changed, 374 insertions(+), 21 deletions(-)
create mode 100755 t/t9163-git-svn-reset-clears-caches.sh
create mode 100755 t/t9164-git-svn-dcommit-concrrent.sh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] git svn: reset invalidates the memoized mergeinfo caches
2012-08-10 20:22 ` Eric Wong
@ 2012-08-10 21:29 ` Junio C Hamano
2012-08-10 21:30 ` Eric Wong
0 siblings, 1 reply; 9+ messages in thread
From: Junio C Hamano @ 2012-08-10 21:29 UTC (permalink / raw)
To: Eric Wong
Cc: git, Andrew Myrick, Jonathan Nieder, Steven Walter, Peter Baumann
Eric Wong <normalperson@yhbt.net> writes:
> Peter Baumann <waste.manager@gmx.de> wrote:
>> First, let me thank you for your review and your detailed explanation.
>> I really appreciate it.
>
> You're welcome, Peter. Thanks again for the patch. I've signed-off and
> pushed for Junio.
>
> The following changes since commit 034161a94e827ef05790b1c7ce5a6e3e740c864e:
>
> Merge git://github.com/git-l10n/git-po (2012-08-09 10:51:46 -0700)
>
> are available in the git repository at:
>
>
> git://bogomips.org/git-svn for-git-master
>
> for you to fetch changes up to 61b472ed8b090a3e9240590c85041120a54dd268:
>
> git svn: reset invalidates the memoized mergeinfo caches (2012-08-10 19:53:18 +0000)
>
> ----------------------------------------------------------------
> Peter Baumann (1):
> git svn: reset invalidates the memoized mergeinfo caches
>
> Robert Luberda (1):
> git svn: handle errors and concurrent commits in dcommit
OK, so these two are fit for 1.7.12-rc3 and later?
Will pull.
Thanks.
>
> git-svn.perl | 74 ++++++++---
> perl/Git/SVN.pm | 27 ++++-
> t/t9163-git-svn-reset-clears-caches.sh | 78 ++++++++++++
> t/t9164-git-svn-dcommit-concrrent.sh | 216 +++++++++++++++++++++++++++++++++
> 4 files changed, 374 insertions(+), 21 deletions(-)
> create mode 100755 t/t9163-git-svn-reset-clears-caches.sh
> create mode 100755 t/t9164-git-svn-dcommit-concrrent.sh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] git svn: reset invalidates the memoized mergeinfo caches
2012-08-10 21:29 ` Junio C Hamano
@ 2012-08-10 21:30 ` Eric Wong
0 siblings, 0 replies; 9+ messages in thread
From: Eric Wong @ 2012-08-10 21:30 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Andrew Myrick, Jonathan Nieder, Steven Walter, Peter Baumann
Junio C Hamano <gitster@pobox.com> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
> > Peter Baumann (1):
> > git svn: reset invalidates the memoized mergeinfo caches
> >
> > Robert Luberda (1):
> > git svn: handle errors and concurrent commits in dcommit
>
> OK, so these two are fit for 1.7.12-rc3 and later?
Yes.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-08-10 21:30 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-08-07 20:02 [PATCH] git svn: reset invalidates the memoized mergeinfo caches Peter Baumann
2012-08-07 20:45 ` Eric Wong
2012-08-08 5:41 ` Peter Baumann
2012-08-08 22:52 ` Eric Wong
2012-08-09 6:42 ` [PATCH v2] " Peter Baumann
2012-08-09 17:35 ` Steven Walter
2012-08-10 20:22 ` Eric Wong
2012-08-10 21:29 ` Junio C Hamano
2012-08-10 21:30 ` Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).