{"thread":{"id":"19608","subject":"[PATCH 1/2] Add 'git svn help [cmd]' which works outside a repo.","startedAt":"2009-05-31T01:17:06Z","lastAt":"2009-06-04T02:48:39Z","messageCount":5,"participants":["Ben Jackson","Eric Wong"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"115105","messageId":"1243732627-82282-1-git-send-email-ben@ben.com","threadId":"19608","inReplyTo":null,"subject":"[PATCH 1/2] Add 'git svn help [cmd]' which works outside a repo.","fromName":"Ben Jackson","fromEmail":"ben@ben.com","sentAt":"2009-05-31T01:17:06Z","receivedAt":"2009-05-31T01:17:06Z","isPatch":true,"sender":{"key":"ben@ben.com","avatar":"https://gravatar.com/avatar/df49904dd23b03a5f57d9d53c0bf9fb6f69a14fac075c98f54f26cf1ce960794?d=mp&s=160"},"body":"Previously there was no explicit 'help' command, but 'git svn help'\nstill printed the usage message (as an invalid command), provided you\ngot past the initialization steps that required a valid repo.\n\nSigned-off-by: Ben Jackson <ben@ben.com>\n---\n\nThis was just a minor nit I noticed while working on the 'git-svn reset'\npatch.\n\n git-svn.perl |    3 +++\n 1 files changed, 3 insertions(+), 0 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex a70c7d7..20bf828 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -219,6 +219,9 @@ for (my $i = 0; $i < @ARGV; $i++) {\n \t\t$cmd = $ARGV[$i];\n \t\tsplice @ARGV, $i, 1;\n \t\tlast;\n+\t} elsif ($ARGV[$i] eq 'help') {\n+\t\t$cmd = $ARGV[$i+1];\n+\t\tusage(0);\n \t}\n };\n \n-- \n1.6.3.GIT\n"},{"id":"115106","messageId":"1243732627-82282-2-git-send-email-ben@ben.com","threadId":"19608","inReplyTo":"1243732627-82282-1-git-send-email-ben@ben.com","subject":"[PATCH 2/2] Add 'git svn reset' to unwind 'git svn fetch'","fromName":"Ben Jackson","fromEmail":"ben@ben.com","sentAt":"2009-05-31T01:17:07Z","receivedAt":"2009-05-31T01:17:07Z","isPatch":true,"sender":{"key":"ben@ben.com","avatar":"https://gravatar.com/avatar/df49904dd23b03a5f57d9d53c0bf9fb6f69a14fac075c98f54f26cf1ce960794?d=mp&s=160"},"body":"Add a command to unwind the effects of fetch by moving the rev_map\nand refs/remotes/git-svn back to an old SVN revision.  This allows\nrevisions to be re-fetched.  Ideally SVN revs would be immutable,\nbut permissions changes in the SVN repository or indiscriminate use\nof '--ignore-paths' can create situations where fetch cannot make\nprogress.\n\nSigned-off-by: Ben Jackson <ben@ben.com>\n---\n\nI ran into a situation at work where a directory in our SVN repo was\nhidden from me using SVN permissions.  Many, many revisions later the\nsecurity settings were changed which exposed those files to me.  The\npermissions change is NOT a revision-controlled event, so from the\nperspective of git-svn there were ollld revisions which actually\nchanged content.  As soon as someone checked in a change to one of the\nnewly visible files, 'git-svn fetch' would fail with the dreaded \"file\nnot found in commit\" errors.\n\nI solved my problem by modifying my ignore-paths config to restore the\nstatus quo.  I did look into what would be necessary to fix it, though,\nand 'git-svn reset' is the result.  The code changes are actually pretty\nminor.  They're dwarfed by the docs and the tests.\n\n Documentation/git-svn.txt |   59 +++++++++++++++++++++++++++++++++++++++-\n git-svn.perl              |   44 ++++++++++++++++++++++++++---\n t/t9139-git-svn-reset.sh  |   66 +++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 163 insertions(+), 6 deletions(-)\n create mode 100755 t/t9139-git-svn-reset.sh\n\ndiff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt\nindex ca3fc3d..e209145 100644\n--- a/Documentation/git-svn.txt\n+++ b/Documentation/git-svn.txt\n@@ -215,7 +215,7 @@ config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)\n The following features from `svn log' are supported:\n +\n --\n---revision=<n>[:<n>];;\n+-r/--revision=<n>[:<n>];;\n \tis supported, non-numeric args are not:\n \tHEAD, NEXT, BASE, PREV, etc ...\n -v/--verbose;;\n@@ -313,6 +313,63 @@ Any other arguments are passed directly to 'git-log'\n \tShows the Subversion externals.  Use -r/--revision to specify a\n \tspecific revision.\n \n+'reset'::\n+\tUndoes the effects of 'fetch' back to the specified revision.\n+\tThis allows you to re-'fetch' an SVN revision.  Normally the\n+\tcontents of an SVN revision should never change and 'reset'\n+\tshould not be necessary.  However, if SVN permissions change,\n+\tor if you alter your --ignore-paths option, a 'fetch' may fail\n+\twith \"not found in commit\" (file not previously visible) or\n+\t\"checksum mismatch\" (missed a modification).  If the problem\n+\tfile cannot be ignored forever (with --ignore-paths) the only\n+\tway to repair the repo is to use 'reset'.\n+\n+Only the rev_map and refs/remotes/git-svn are changed.  Follow 'reset'\n+with a 'fetch' and then 'git-reset' or 'git-rebase' to move local\n+branches onto the new tree.\n+\n+-r/--revision=<n>;;\n+\tSpecify the most recent revision to keep.  All later revisions\n+\tare discarded.\n+-p/--parent;;\n+\tDiscard the specified revision as well, keeping the nearest\n+\tparent instead.\n+Example:;;\n+Assume you have local changes in \"master\", but you need to refetch \"r2\".\n+\n+------------\n+    r1---r2---r3 remotes/git-svn\n+                \\\n+                 A---B master\n+------------\n+\n+Fix the ignore-paths or SVN permissions problem that caused \"r2\" to\n+be incomplete in the first place.  Then:\n+\n+[verse]\n+git svn reset -r2 -p\n+git svn fetch\n+\n+------------\n+    r1---r2'--r3' remotes/git-svn\n+      \\\n+       r2---r3---A---B master\n+------------\n+\n+Then fixup \"master\" with 'git-rebase'.\n+Do NOT use 'git-merge' or your history will not be compatible with a\n+future 'dcommit'!\n+\n+[verse]\n+git rebase --onto remotes/git-svn A^ master\n+\n+------------\n+    r1---r2'--r3' remotes/git-svn\n+                \\\n+                 A'--B' master\n+------------\n+\n+\n --\n \n OPTIONS\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 20bf828..2ff6bb0 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -211,6 +211,10 @@ my %cmd = (\n \t'blame' => [ \\&Git::SVN::Log::cmd_blame,\n \t            \"Show what revision and author last modified each line of a file\",\n \t\t    { 'git-format' => \\$_git_format } ],\n+\t'reset' => [ \\&cmd_reset,\n+\t\t     \"Undo fetches back to the specified SVN revision\",\n+\t\t     { 'revision|r=s' => \\$_revision,\n+\t\t       'parent|p' => \\$_fetch_parent } ],\n );\n \n my $cmd;\n@@ -1024,6 +1028,19 @@ sub cmd_info {\n \tprint $result, \"\\n\";\n }\n \n+sub cmd_reset {\n+\tmy $target = shift || $_revision or die \"SVN revision required\\n\";\n+\t$target = $1 if $target =~ /^r(\\d+)/;\n+\tmy ($url, $rev, $uuid, $gs) = working_head_info('HEAD');\n+\tunless ($gs) {\n+\t\tdie \"Unable to determine upstream SVN information from \".\n+\t\t    \"history\\n\";\n+\t}\n+\tmy ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent);\n+\t$gs->rev_map_set($r, $c, 'reset', $uuid);\n+\tprint \"r$r = $c ($gs->{ref_id})\\n\";\n+}\n+\n ########################### utility functions #########################\n \n sub rebase_cmd {\n@@ -2982,6 +2999,14 @@ sub _rev_map_set {\n \t  croak \"write: $!\";\n }\n \n+sub _rev_map_reset {\n+\tmy ($fh, $rev, $commit) = @_;\n+\tmy $c = _rev_map_get($fh, $rev);\n+\t$c eq $commit or die \"_rev_map_reset(@_) commit $c does not match!\\n\";\n+\tmy $offset = sysseek($fh, 0, SEEK_CUR) or croak \"seek: $!\";\n+\ttruncate $fh, $offset or croak \"truncate: $!\";\n+}\n+\n sub mkfile {\n \tmy ($path) = @_;\n \tunless (-e $path) {\n@@ -2998,6 +3023,7 @@ sub rev_map_set {\n \tmy $db = $self->map_path($uuid);\n \tmy $db_lock = \"$db.lock\";\n \tmy $sig;\n+\t$update_ref ||= 0;\n \tif ($update_ref) {\n \t\t$SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =\n \t\t            $SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] };\n@@ -3021,7 +3047,8 @@ sub rev_map_set {\n \n \tsysopen(my $fh, $db_lock, O_RDWR | O_CREAT)\n \t     or croak \"Couldn't open $db_lock: $!\\n\";\n-\t_rev_map_set($fh, $rev, $commit);\n+\t$update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) :\n+\t\t\t\t _rev_map_set($fh, $rev, $commit);\n \tif ($sync) {\n \t\t$fh->flush or die \"Couldn't flush $db_lock: $!\\n\";\n \t\t$fh->sync or die \"Couldn't sync $db_lock: $!\\n\";\n@@ -3029,7 +3056,9 @@ sub rev_map_set {\n \tclose $fh or croak $!;\n \tif ($update_ref) {\n \t\t$_head = $self;\n-\t\tcommand_noisy('update-ref', '-m', \"r$rev\",\n+\t\tmy $note = \"\";\n+\t\t$note = \" ($update_ref)\" if ($update_ref !~ /^\\d*$/);\n+\t\tcommand_noisy('update-ref', '-m', \"r$rev$note\",\n \t\t              $self->refname, $commit);\n \t}\n \trename $db_lock, $db or die \"rev_map_set(@_): \", \"Failed to rename: \",\n@@ -3091,12 +3120,19 @@ sub rev_map_get {\n \treturn undef unless -e $map_path;\n \n \tsysopen(my $fh, $map_path, O_RDONLY) or croak \"open: $!\";\n+\tmy $c = _rev_map_get($fh, $rev);\n+\tclose($fh) or croak \"close: $!\";\n+\t$c\n+}\n+\n+sub _rev_map_get {\n+\tmy ($fh, $rev) = @_;\n+\n \tbinmode $fh or croak \"binmode: $!\";\n \tmy $size = (stat($fh))[7];\n \t($size % 24) == 0 or croak \"inconsistent size: $size\";\n \n \tif ($size == 0) {\n-\t\tclose $fh or croak \"close: $fh\";\n \t\treturn undef;\n \t}\n \n@@ -3114,11 +3150,9 @@ sub rev_map_get {\n \t\t} elsif ($r > $rev) {\n \t\t\t$u = $i - 24;\n \t\t} else { # $r == $rev\n-\t\t\tclose($fh) or croak \"close: $!\";\n \t\t\treturn $c eq ('0' x 40) ? undef : $c;\n \t\t}\n \t}\n-\tclose($fh) or croak \"close: $!\";\n \tundef;\n }\n \ndiff --git a/t/t9139-git-svn-reset.sh b/t/t9139-git-svn-reset.sh\nnew file mode 100755\nindex 0000000..0735526\n--- /dev/null\n+++ b/t/t9139-git-svn-reset.sh\n@@ -0,0 +1,66 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2009 Ben Jackson\n+#\n+\n+test_description='git svn reset'\n+. ./lib-git-svn.sh\n+\n+test_expect_success 'setup test repository' '\n+\tsvn_cmd co \"$svnrepo\" s &&\n+\t(\n+\t\tcd s &&\n+\t\tmkdir vis &&\n+\t\techo always visible > vis/vis.txt &&\n+\t\tsvn_cmd add vis &&\n+\t\tsvn_cmd commit -m \"create visible files\" &&\n+\t\tmkdir hid &&\n+\t\techo initially hidden > hid/hid.txt &&\n+\t\tsvn_cmd add hid &&\n+\t\tsvn_cmd commit -m \"create initially hidden files\" &&\n+\t\tsvn_cmd up &&\n+\t\techo mod >> vis/vis.txt &&\n+\t\tsvn_cmd commit -m \"modify vis\" &&\n+\t\tsvn_cmd up\n+\t)\n+'\n+\n+test_expect_success 'clone SVN repository with hidden directory' '\n+\tgit svn init \"$svnrepo\" g &&\n+\t( cd g && git svn fetch --ignore-paths=\"^hid\" )\n+'\n+\n+test_expect_success 'modify hidden file in SVN repo' '\n+\t( cd s &&\n+\t  echo mod hidden >> hid/hid.txt &&\n+\t  svn_cmd commit -m \"modify hid\" &&\n+\t  svn_cmd up\n+\t)\n+'\n+\n+test_expect_success 'fetch fails on modified hidden file' '\n+\t( cd g &&\n+\t  git svn find-rev refs/remotes/git-svn > ../expect &&\n+\t  ! git svn fetch 2> ../errors &&\n+\t  git svn find-rev refs/remotes/git-svn > ../expect2 ) &&\n+\tfgrep \"not found in commit\" errors &&\n+\ttest_cmp expect expect2\n+'\n+\n+test_expect_success 'reset unwinds back to r1' '\n+\t( cd g &&\n+\t  git svn reset -r1 &&\n+\t  git svn find-rev refs/remotes/git-svn > ../expect2 ) &&\n+\techo 1 >expect &&\n+\ttest_cmp expect expect2\n+'\n+\n+test_expect_success 'refetch succeeds not ignoring any files' '\n+\t( cd g &&\n+\t  git svn fetch &&\n+\t  git svn rebase &&\n+\t  fgrep \"mod hidden\" hid/hid.txt\n+\t)\n+'\n+\n+test_done\n-- \n1.6.3.GIT\n"},{"id":"115212","messageId":"20090601174943.GA80846@kronos.home.ben.com","threadId":"19608","inReplyTo":"1243732627-82282-2-git-send-email-ben@ben.com","subject":"Re: [PATCH 2/2] Add 'git svn reset' to unwind 'git svn fetch'","fromName":"Ben Jackson","fromEmail":"ben@ben.com","sentAt":"2009-06-01T17:49:44Z","receivedAt":"2009-06-01T17:49:44Z","isPatch":true,"sender":{"key":"ben@ben.com","avatar":"https://gravatar.com/avatar/df49904dd23b03a5f57d9d53c0bf9fb6f69a14fac075c98f54f26cf1ce960794?d=mp&s=160"},"body":"On Sat, May 30, 2009 at 06:17:07PM -0700, Ben Jackson wrote:\n> Add a command to unwind the effects of fetch by moving the rev_map\n> and refs/remotes/git-svn back to an old SVN revision.\n\nI just realized this morning that I didn't do any special handling for\nbad revision input.  I will submit a revised patch.  In the mean time\nI would still like feedback on the concept and the docs.\n\nKnown issues:\n\n1.  Garbage revision input is accepted and prints several internal errors\nbefore bombing out.  The repo is not harmed, though.\n\n2.  Huge revision numbers cause git-svn to spin for quite a while because\nI used the pre-existing find_rev_before which is a linear search.\n\n3.  Without --parent it should probably fail for a nonexistent rev rather\nthan automatically find_rev_before..\n\n-- \nBen Jackson AD7GD\n<ben@ben.com>\nhttp://www.ben.com/\n"},{"id":"115402","messageId":"20090604002406.GB8882@dcvr.yhbt.net","threadId":"19608","inReplyTo":"20090601174943.GA80846@kronos.home.ben.com","subject":"Re: [PATCH 2/2] Add 'git svn reset' to unwind 'git svn fetch'","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2009-06-04T00:24:06Z","receivedAt":"2009-06-04T00:24:06Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Ben Jackson <ben@ben.com> wrote:\n> On Sat, May 30, 2009 at 06:17:07PM -0700, Ben Jackson wrote:\n> > Add a command to unwind the effects of fetch by moving the rev_map\n> > and refs/remotes/git-svn back to an old SVN revision.\n> \n> I just realized this morning that I didn't do any special handling for\n> bad revision input.  I will submit a revised patch.  In the mean time\n> I would still like feedback on the concept and the docs.\n\nHi Ben,\n\nSorry for the late response, I've been distracted with other projects.\nI like the overall idea of it, it would actually be useful for\ndeveloping/debugging git-svn as well :)\n\n> Known issues:\n> \n> 1.  Garbage revision input is accepted and prints several internal errors\n> before bombing out.  The repo is not harmed, though.\n> \n> 2.  Huge revision numbers cause git-svn to spin for quite a while because\n> I used the pre-existing find_rev_before which is a linear search.\n\nfind_rev_before can certainly be improved.  I haven't noticed\nperformance issues with it myself, but you should be able to improve it.\n\n> 3.  Without --parent it should probably fail for a nonexistent rev rather\n> than automatically find_rev_before..\n\nProbably best to ignore the --parent flag and just pass 1 as the second\nargument to find_rev_before.  That's how SVN operations work when a\nnon-exact revision is specified..\n\n-- \nEric Wong\n"},{"id":"115407","messageId":"20090604024839.GB19622@dcvr.yhbt.net","threadId":"19608","inReplyTo":"1243732627-82282-1-git-send-email-ben@ben.com","subject":"Re: [PATCH 1/2] Add 'git svn help [cmd]' which works outside a repo.","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2009-06-04T02:48:39Z","receivedAt":"2009-06-04T02:48:39Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Ben Jackson <ben@ben.com> wrote:\n> Previously there was no explicit 'help' command, but 'git svn help'\n> still printed the usage message (as an invalid command), provided you\n> got past the initialization steps that required a valid repo.\n> \n> Signed-off-by: Ben Jackson <ben@ben.com>\n> ---\n> \n> This was just a minor nit I noticed while working on the 'git-svn reset'\n> patch.\n\nThanks Ben,\n\nAcked and pushed out to git://git.bogomips.org/git-svn\n\n-- \nEric Wong\n"}]}