Skip to content

Commit 195643f

Browse files
bjjEric Wong
authored andcommitted
Add 'git svn reset' to unwind 'git svn fetch'
Add a command to unwind the effects of fetch by moving the rev_map and refs/remotes/git-svn back to an old SVN revision. This allows revisions to be re-fetched. Ideally SVN revs would be immutable, but permissions changes in the SVN repository or indiscriminate use of '--ignore-paths' can create situations where fetch cannot make progress. Signed-off-by: Ben Jackson <[email protected]> Acked-by: Eric Wong <[email protected]>
1 parent ca5e880 commit 195643f

File tree

3 files changed

+164
-6
lines changed

3 files changed

+164
-6
lines changed

Documentation/git-svn.txt

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
216216
The following features from `svn log' are supported:
217217
+
218218
--
219-
--revision=<n>[:<n>];;
219+
-r/--revision=<n>[:<n>];;
220220
is supported, non-numeric args are not:
221221
HEAD, NEXT, BASE, PREV, etc ...
222222
-v/--verbose;;
@@ -314,6 +314,63 @@ Any other arguments are passed directly to 'git-log'
314314
Shows the Subversion externals. Use -r/--revision to specify a
315315
specific revision.
316316

317+
'reset'::
318+
Undoes the effects of 'fetch' back to the specified revision.
319+
This allows you to re-'fetch' an SVN revision. Normally the
320+
contents of an SVN revision should never change and 'reset'
321+
should not be necessary. However, if SVN permissions change,
322+
or if you alter your --ignore-paths option, a 'fetch' may fail
323+
with "not found in commit" (file not previously visible) or
324+
"checksum mismatch" (missed a modification). If the problem
325+
file cannot be ignored forever (with --ignore-paths) the only
326+
way to repair the repo is to use 'reset'.
327+
328+
Only the rev_map and refs/remotes/git-svn are changed. Follow 'reset'
329+
with a 'fetch' and then 'git-reset' or 'git-rebase' to move local
330+
branches onto the new tree.
331+
332+
-r/--revision=<n>;;
333+
Specify the most recent revision to keep. All later revisions
334+
are discarded.
335+
-p/--parent;;
336+
Discard the specified revision as well, keeping the nearest
337+
parent instead.
338+
Example:;;
339+
Assume you have local changes in "master", but you need to refetch "r2".
340+
341+
------------
342+
r1---r2---r3 remotes/git-svn
343+
\
344+
A---B master
345+
------------
346+
347+
Fix the ignore-paths or SVN permissions problem that caused "r2" to
348+
be incomplete in the first place. Then:
349+
350+
[verse]
351+
git svn reset -r2 -p
352+
git svn fetch
353+
354+
------------
355+
r1---r2'--r3' remotes/git-svn
356+
\
357+
r2---r3---A---B master
358+
------------
359+
360+
Then fixup "master" with 'git-rebase'.
361+
Do NOT use 'git-merge' or your history will not be compatible with a
362+
future 'dcommit'!
363+
364+
[verse]
365+
git rebase --onto remotes/git-svn A^ master
366+
367+
------------
368+
r1---r2'--r3' remotes/git-svn
369+
\
370+
A'--B' master
371+
------------
372+
373+
317374
--
318375

319376
OPTIONS

git-svn.perl

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ BEGIN
211211
'blame' => [ \&Git::SVN::Log::cmd_blame,
212212
"Show what revision and author last modified each line of a file",
213213
{ 'git-format' => \$_git_format } ],
214+
'reset' => [ \&cmd_reset,
215+
"Undo fetches back to the specified SVN revision",
216+
{ 'revision|r=s' => \$_revision,
217+
'parent|p' => \$_fetch_parent } ],
214218
);
215219

216220
my $cmd;
@@ -1054,6 +1058,20 @@ sub cmd_info {
10541058
print $result, "\n";
10551059
}
10561060

1061+
sub cmd_reset {
1062+
my $target = shift || $_revision or die "SVN revision required\n";
1063+
$target = $1 if $target =~ /^r(\d+)$/;
1064+
$target =~ /^\d+$/ or die "Numeric SVN revision expected\n";
1065+
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
1066+
unless ($gs) {
1067+
die "Unable to determine upstream SVN information from ".
1068+
"history\n";
1069+
}
1070+
my ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent);
1071+
$gs->rev_map_set($r, $c, 'reset', $uuid);
1072+
print "r$r = $c ($gs->{ref_id})\n";
1073+
}
1074+
10571075
########################### utility functions #########################
10581076

10591077
sub rebase_cmd {
@@ -3023,6 +3041,14 @@ sub _rev_map_set {
30233041
croak "write: $!";
30243042
}
30253043

3044+
sub _rev_map_reset {
3045+
my ($fh, $rev, $commit) = @_;
3046+
my $c = _rev_map_get($fh, $rev);
3047+
$c eq $commit or die "_rev_map_reset(@_) commit $c does not match!\n";
3048+
my $offset = sysseek($fh, 0, SEEK_CUR) or croak "seek: $!";
3049+
truncate $fh, $offset or croak "truncate: $!";
3050+
}
3051+
30263052
sub mkfile {
30273053
my ($path) = @_;
30283054
unless (-e $path) {
@@ -3039,6 +3065,7 @@ sub rev_map_set {
30393065
my $db = $self->map_path($uuid);
30403066
my $db_lock = "$db.lock";
30413067
my $sig;
3068+
$update_ref ||= 0;
30423069
if ($update_ref) {
30433070
$SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
30443071
$SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] };
@@ -3062,15 +3089,18 @@ sub rev_map_set {
30623089

30633090
sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
30643091
or croak "Couldn't open $db_lock: $!\n";
3065-
_rev_map_set($fh, $rev, $commit);
3092+
$update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :
3093+
_rev_map_set($fh, $rev, $commit);
30663094
if ($sync) {
30673095
$fh->flush or die "Couldn't flush $db_lock: $!\n";
30683096
$fh->sync or die "Couldn't sync $db_lock: $!\n";
30693097
}
30703098
close $fh or croak $!;
30713099
if ($update_ref) {
30723100
$_head = $self;
3073-
command_noisy('update-ref', '-m', "r$rev",
3101+
my $note = "";
3102+
$note = " ($update_ref)" if ($update_ref !~ /^\d*$/);
3103+
command_noisy('update-ref', '-m', "r$rev$note",
30743104
$self->refname, $commit);
30753105
}
30763106
rename $db_lock, $db or die "rev_map_set(@_): ", "Failed to rename: ",
@@ -3132,12 +3162,19 @@ sub rev_map_get {
31323162
return undef unless -e $map_path;
31333163

31343164
sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
3165+
my $c = _rev_map_get($fh, $rev);
3166+
close($fh) or croak "close: $!";
3167+
$c
3168+
}
3169+
3170+
sub _rev_map_get {
3171+
my ($fh, $rev) = @_;
3172+
31353173
binmode $fh or croak "binmode: $!";
31363174
my $size = (stat($fh))[7];
31373175
($size % 24) == 0 or croak "inconsistent size: $size";
31383176

31393177
if ($size == 0) {
3140-
close $fh or croak "close: $fh";
31413178
return undef;
31423179
}
31433180

@@ -3155,11 +3192,9 @@ sub rev_map_get {
31553192
} elsif ($r > $rev) {
31563193
$u = $i - 24;
31573194
} else { # $r == $rev
3158-
close($fh) or croak "close: $!";
31593195
return $c eq ('0' x 40) ? undef : $c;
31603196
}
31613197
}
3162-
close($fh) or croak "close: $!";
31633198
undef;
31643199
}
31653200

t/t9139-git-svn-reset.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2009 Ben Jackson
4+
#
5+
6+
test_description='git svn reset'
7+
. ./lib-git-svn.sh
8+
9+
test_expect_success 'setup test repository' '
10+
svn_cmd co "$svnrepo" s &&
11+
(
12+
cd s &&
13+
mkdir vis &&
14+
echo always visible > vis/vis.txt &&
15+
svn_cmd add vis &&
16+
svn_cmd commit -m "create visible files" &&
17+
mkdir hid &&
18+
echo initially hidden > hid/hid.txt &&
19+
svn_cmd add hid &&
20+
svn_cmd commit -m "create initially hidden files" &&
21+
svn_cmd up &&
22+
echo mod >> vis/vis.txt &&
23+
svn_cmd commit -m "modify vis" &&
24+
svn_cmd up
25+
)
26+
'
27+
28+
test_expect_success 'clone SVN repository with hidden directory' '
29+
git svn init "$svnrepo" g &&
30+
( cd g && git svn fetch --ignore-paths="^hid" )
31+
'
32+
33+
test_expect_success 'modify hidden file in SVN repo' '
34+
( cd s &&
35+
echo mod hidden >> hid/hid.txt &&
36+
svn_cmd commit -m "modify hid" &&
37+
svn_cmd up
38+
)
39+
'
40+
41+
test_expect_success 'fetch fails on modified hidden file' '
42+
( cd g &&
43+
git svn find-rev refs/remotes/git-svn > ../expect &&
44+
! git svn fetch 2> ../errors &&
45+
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
46+
fgrep "not found in commit" errors &&
47+
test_cmp expect expect2
48+
'
49+
50+
test_expect_success 'reset unwinds back to r1' '
51+
( cd g &&
52+
git svn reset -r1 &&
53+
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
54+
echo 1 >expect &&
55+
test_cmp expect expect2
56+
'
57+
58+
test_expect_success 'refetch succeeds not ignoring any files' '
59+
( cd g &&
60+
git svn fetch &&
61+
git svn rebase &&
62+
fgrep "mod hidden" hid/hid.txt
63+
)
64+
'
65+
66+
test_done

0 commit comments

Comments
 (0)