From c69700fe042ad2ecf2904a8b7d8eddc6d52b790b Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Wed, 6 May 2009 16:18:52 -0400 Subject: git-svn: Fix for svn paths removed > log-window-size revisions ago Instead of trying to find the end of the commit history only in the last window, track if we have seen commits yet, and use that to judge if we need to backtrack and look for a tail. Otherwise, conversion can silently lose up to 100 revisions of a branch if it was deleted >100 revisions ago. Signed-off-by: Alex Vandiver Acked-by: Eric Wong --- git-svn.perl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index ef1d30db38..5836ddec8a 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -4438,6 +4438,7 @@ sub gs_fetch_loop_common { my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc); my $longest_path = longest_common_path($gsv, $globs); my $ra_url = $self->{url}; + my $find_trailing_edge; while (1) { my %revs; my $err; @@ -4455,8 +4456,10 @@ sub gs_fetch_loop_common { sub { $revs{$_[1]} = _cb(@_) }); if ($err) { print "Checked through r$max\r"; + } else { + $find_trailing_edge = 1; } - if ($err && $max >= $head) { + if ($err and $find_trailing_edge) { print STDERR "Path '$longest_path' ", "was probably deleted:\n", $err->expanded_message, @@ -4475,6 +4478,7 @@ sub gs_fetch_loop_common { last; } } + $find_trailing_edge = 0; } $SVN::Error::handler = $err_handler; -- cgit v1.2.3 From b6c61778d4fa57904d5b818e0ca04292aa1d0335 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Wed, 6 May 2009 16:18:53 -0400 Subject: git-svn: Correctly report max revision when following deleted paths Report the maximum found revision in the range, instead of the minimum changed revision. Signed-off-by: Alex Vandiver Acked-by: Eric Wong --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 5836ddec8a..eebcf0f20e 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -4471,7 +4471,7 @@ sub gs_fetch_loop_common { my $ok; $self->get_log([$longest_path], $min, $hi, 0, 1, 1, sub { - $ok ||= $_[1]; + $ok = $_[1]; $revs{$_[1]} = _cb(@_) }); if ($ok) { print STDERR "r$min .. r$ok OK\n"; -- cgit v1.2.3 From 42a5da1806fd6891d4fcc0ad9c0e550c12f75df5 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Wed, 6 May 2009 16:19:45 -0400 Subject: git-svn: Set svn.authorsfile if it is passed to git svn clone Signed-off-by: Alex Vandiver Acked-by: Eric Wong --- git-svn.perl | 1 + 1 file changed, 1 insertion(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index eebcf0f20e..e927965ac4 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -361,6 +361,7 @@ sub cmd_clone { $path = basename($url) if !defined $path || !length $path; cmd_init($url, $path); Git::SVN::fetch_all($Git::SVN::default_repo_id); + command_oneline('config', 'svn.authorsfile', $_authors) if $_authors; } sub cmd_init { -- cgit v1.2.3 From 36db1eddf972c88edf402b99e2366ad533635ab8 Mon Sep 17 00:00:00 2001 From: Mark Lodato Date: Thu, 14 May 2009 21:27:15 -0400 Subject: git-svn: add --authors-prog option Add a new option, --authors-prog, to git-svn that allows a more flexible alternative (or supplement) to --authors-file. This allows more advanced username operations than the authors file will allow. For example, one may look up Subversion users via LDAP, or may generate the name and email address from the Subversion username. Notes: * If both --authors-name and --authors-prog are given, the former is tried first, falling back to the later. * The program is called once per unique SVN username, and the result is cached. * The command-line argument must be the path to a program, not a generic shell command line. The absolute path to this program is taken at startup since the git-svn script changes directory during operation. * The option is not enabled for `git svn log'. [ew: fixed case where neither --authors-(name|prog) were defined] Signed-off-by: Mark Lodato Acked-by: Eric Wong --- Documentation/git-svn.txt | 8 +++++ git-svn.perl | 32 +++++++++++++++++-- t/t9138-git-svn-authors-prog.sh | 69 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) create mode 100755 t/t9138-git-svn-authors-prog.sh (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 1c40894669..ca3fc3de1f 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -398,6 +398,14 @@ after the authors-file is modified should continue operation. config key: svn.authorsfile +--authors-prog=:: + +If this option is specified, for each SVN committer name that does not +exist in the authors file, the given file is executed with the committer +name as the first argument. The program is expected to return a single +line of the form "Name ", which will be treated as if included in +the authors file. + -q:: --quiet:: Make 'git-svn' less verbose. Specify a second time to make it diff --git a/git-svn.perl b/git-svn.perl index e927965ac4..a70c7d7b2c 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -5,7 +5,7 @@ use warnings; use strict; use vars qw/ $AUTHOR $VERSION $sha1 $sha1_short $_revision $_repository - $_q $_authors %users/; + $_q $_authors $_authors_prog %users/; $AUTHOR = 'Eric Wong '; $VERSION = '@@GIT_VERSION@@'; @@ -39,6 +39,7 @@ use Digest::MD5; use IO::File qw//; use File::Basename qw/dirname basename/; use File::Path qw/mkpath/; +use File::Spec; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; use IPC::Open3; use Git; @@ -76,6 +77,7 @@ my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex ); my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'authors-file|A=s' => \$_authors, + 'authors-prog=s' => \$_authors_prog, 'repack:i' => \$Git::SVN::_repack, 'noMetadata' => \$Git::SVN::_no_metadata, 'useSvmProps' => \$Git::SVN::_use_svm_props, @@ -263,6 +265,9 @@ usage(0) if $_help; version() if $_version; usage(1) unless defined $cmd; load_authors() if $_authors; +if (defined $_authors_prog) { + $_authors_prog = "'" . File::Spec->rel2abs($_authors_prog) . "'"; +} unless ($cmd =~ /^(?:clone|init|multi-init|commit-diff)$/) { Git::SVN::Migration::migration_check(); @@ -2664,12 +2669,33 @@ sub other_gs { $gs } +sub call_authors_prog { + my ($orig_author) = @_; + my $author = `$::_authors_prog $orig_author`; + if ($? != 0) { + die "$::_authors_prog failed with exit code $?\n" + } + if ($author =~ /^\s*(.+?)\s*<(.*)>\s*$/) { + my ($name, $email) = ($1, $2); + $email = undef if length $2 == 0; + return [$name, $email]; + } else { + die "Author: $orig_author: $::_authors_prog returned " + . "invalid author format: $author\n"; + } +} + sub check_author { my ($author) = @_; if (!defined $author || length $author == 0) { $author = '(no author)'; - } elsif (defined $::_authors && ! defined $::users{$author}) { - die "Author: $author not defined in $::_authors file\n"; + } + if (!defined $::users{$author}) { + if (defined $::_authors_prog) { + $::users{$author} = call_authors_prog($author); + } elsif (defined $::_authors) { + die "Author: $author not defined in $::_authors file\n"; + } } $author; } diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh new file mode 100755 index 0000000000..a4b00f2a3f --- /dev/null +++ b/t/t9138-git-svn-authors-prog.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong, Mark Lodato +# + +test_description='git svn authors prog tests' + +. ./lib-git-svn.sh + +cat > svn-authors-prog <<'EOF' +#!/usr/bin/perl +$_ = shift; +if (s/-sub$//) { + print "$_ <$_\@sub.example.com>\n"; +} +else { + print "$_ <$_\@example.com>\n"; +} +EOF +chmod +x svn-authors-prog + +cat > svn-authors <<'EOF' +ff = FFFFFFF FFFFFFF +EOF + +test_expect_success 'setup svnrepo' ' + for i in aa bb cc-sub dd-sub ee-foo ff + do + svn mkdir -m $i --username $i "$svnrepo"/$i + done + ' + +test_expect_success 'import authors with prog and file' ' + git svn clone --authors-prog=./svn-authors-prog \ + --authors-file=svn-authors "$svnrepo" x + ' + +test_expect_success 'imported 6 revisions successfully' ' + ( + cd x + test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 6 + ) + ' + +test_expect_success 'authors-prog ran correctly' ' + ( + cd x + git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \ + grep "^author ee-foo " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~2 | \ + grep "^author dd " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~3 | \ + grep "^author cc " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~4 | \ + grep "^author bb " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~5 | \ + grep "^author aa " + ) + ' + +test_expect_success 'authors-file overrode authors-prog' ' + ( + cd x + git rev-list -1 --pretty=raw refs/remotes/git-svn | \ + grep "^author FFFFFFF FFFFFFF " + ) + ' + +test_done -- cgit v1.2.3 From b510df8af2c86d441de87fa4be09f786b5411527 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 28 May 2009 00:56:23 -0700 Subject: git-svn: refuse to dcommit non-UTF-8 messages ...without i18n.commitencoding set in the config. SVN tries to store all commit messages in UTF-8, however it is up to the job of the clients to enforce this rule. SVN servers themselves do not always enforce this; allowing clients to commit malformed UTF-8 messages and break repositories. So git-svn will enforce this and tell the user to set i18n.commitencoding when a git commit is is not in UTF-8. Signed-off-by: Eric Wong --- git-svn.perl | 17 +++++++++-- t/t9139-git-svn-non-utf8-commitencoding.sh | 47 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100755 t/t9139-git-svn-non-utf8-commitencoding.sh (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index a70c7d7b2c..33017974d0 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1178,16 +1178,27 @@ sub get_commit_entry { } rename $commit_editmsg, $commit_msg or croak $!; { + require Encode; # SVN requires messages to be UTF-8 when entering the repo local $/; open $log_fh, '<', $commit_msg or croak $!; binmode $log_fh; chomp($log_entry{log} = <$log_fh>); - if (my $enc = Git::config('i18n.commitencoding')) { - require Encode; - Encode::from_to($log_entry{log}, $enc, 'UTF-8'); + my $enc = Git::config('i18n.commitencoding') || 'UTF-8'; + my $msg = $log_entry{log}; + + eval { $msg = Encode::decode($enc, $msg, 1) }; + if ($@) { + die "Could not decode as $enc:\n", $msg, + "\nPerhaps you need to set i18n.commitencoding\n"; } + + eval { $msg = Encode::encode('UTF-8', $msg, 1) }; + die "Could not encode as UTF-8:\n$msg\n" if $@; + + $log_entry{log} = $msg; + close $log_fh or croak $!; } unlink $commit_msg; diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh new file mode 100755 index 0000000000..2b1db97337 --- /dev/null +++ b/t/t9139-git-svn-non-utf8-commitencoding.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong + +test_description='git svn refuses to dcommit non-UTF8 messages' + +. ./lib-git-svn.sh + +# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test + +for H in ISO-8859-1 EUCJP +do + test_expect_success "$H setup" ' + mkdir $H && + svn_cmd import -m "$H test" $H "$svnrepo"/$H && + git svn clone "$svnrepo"/$H $H + ' +done + +for H in ISO-8859-1 EUCJP +do + test_expect_success "$H commit on git side" ' + ( + cd $H && + git config i18n.commitencoding $H && + git checkout -b t refs/remotes/git-svn && + echo $H >F && + git add F && + git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt && + E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") && + test "z$E" = "z$H" + ) + ' +done + +for H in ISO-8859-1 EUCJP +do + test_expect_success "$H dcommit to svn" ' + ( + cd $H && + git config --unset i18n.commitencoding && + ! git svn dcommit + ) + ' +done + +test_done -- cgit v1.2.3 From 5eec27e35f0a6231d2b0c50d94c726a67f14b23e Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Fri, 29 May 2009 17:09:42 +0200 Subject: git-svn: let 'dcommit $rev' work on $rev instead of HEAD 'git svn dcommit' takes an optional revision argument, but the meaning of it was rather scary. It completely ignored the current state of the HEAD, only looking at the revisions between SVN and $rev. If HEAD was attached to $branch, the branch lost all commits $rev..$branch in the process. Considering that 'git svn dcommit HEAD^' has the intuitive meaning "dcommit all changes on my branch except the last one", we change the meaning of the revision argument. git-svn temporarily checks out $rev for its work, meaning that * if a branch is specified, that branch (_not_ the HEAD) is rebased as part of the dcommit, * if some other revision is specified, as in the example, all work happens on a detached HEAD and no branch is affected. Signed-off-by: Thomas Rast Acked-by: Eric Wong --- Documentation/git-svn.txt | 5 +++-- git-svn.perl | 34 ++++++++++++++++++++++++++++++++-- t/t9100-git-svn-basic.sh | 19 +++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index bb22d8e712..5027f9fbd7 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -170,8 +170,9 @@ and have no uncommitted changes. It is recommended that you run 'git-svn' fetch and rebase (not pull or merge) your commits against the latest changes in the SVN repository. - An optional command-line argument may be specified as an - alternative to HEAD. + An optional revision or branch argument may be specified, and + causes 'git-svn' to do all work on that revision/branch + instead of HEAD. This is advantageous over 'set-tree' (below) because it produces cleaner, more linear history. + diff --git a/git-svn.perl b/git-svn.perl index 33017974d0..33fe34cbac 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -454,8 +454,22 @@ sub cmd_dcommit { 'Cannot dcommit with a dirty index. Commit your changes first, ' . "or stash them with `git stash'.\n"; $head ||= 'HEAD'; + + my $old_head; + if ($head ne 'HEAD') { + $old_head = eval { + command_oneline([qw/symbolic-ref -q HEAD/]) + }; + if ($old_head) { + $old_head =~ s{^refs/heads/}{}; + } else { + $old_head = eval { command_oneline(qw/rev-parse HEAD/) }; + } + command(['checkout', $head], STDERR => 0); + } + my @refs; - my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs); + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD', \@refs); unless ($gs) { die "Unable to determine upstream SVN information from ", "$head history.\nPerhaps the repository is empty."; @@ -541,7 +555,7 @@ sub cmd_dcommit { if (@diff) { @refs = (); my ($url_, $rev_, $uuid_, $gs_) = - working_head_info($head, \@refs); + working_head_info('HEAD', \@refs); my ($linear_refs_, $parents_) = linearize_history($gs_, \@refs); if (scalar(@$linear_refs) != @@ -579,6 +593,22 @@ sub cmd_dcommit { } } } + + if ($old_head) { + my $new_head = command_oneline(qw/rev-parse HEAD/); + my $new_is_symbolic = eval { + command_oneline(qw/symbolic-ref -q HEAD/); + }; + if ($new_is_symbolic) { + print "dcommitted the branch ", $head, "\n"; + } else { + print "dcommitted on a detached HEAD because you gave ", + "a revision argument.\n", + "The rewritten commit is: ", $new_head, "\n"; + } + command(['checkout', $old_head], STDERR => 0); + } + unlink $gs->{index}; } diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index 64aa7e2f10..570e0359e4 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -231,6 +231,25 @@ test_expect_success \ "^:refs/${remotes_git_svn}$" ' +test_expect_success 'dcommit $rev does not clobber current branch' ' + git svn fetch -i bar && + git checkout -b my-bar refs/remotes/bar && + echo 1 > foo && + git add foo && + git commit -m "change 1" && + echo 2 > foo && + git add foo && + git commit -m "change 2" && + old_head=$(git rev-parse HEAD) && + git svn dcommit -i bar HEAD^ && + test $old_head = $(git rev-parse HEAD) && + test refs/heads/my-bar = $(git symbolic-ref HEAD) && + git log refs/remotes/bar | grep "change 1" && + ! git log refs/remotes/bar | grep "change 2" && + git checkout master && + git branch -D my-bar + ' + test_expect_success 'able to dcommit to a subdirectory' " git svn fetch -i bar && git checkout -b my-bar refs/remotes/bar && -- cgit v1.2.3 From 9a8c92ac9e4698da150a413ce80f185005447f4c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 30 May 2009 18:17:06 -0700 Subject: Add 'git svn help [cmd]' which works outside a repo. Previously there was no explicit 'help' command, but 'git svn help' still printed the usage message (as an invalid command), provided you got past the initialization steps that required a valid repo. Signed-off-by: Ben Jackson Acked-by: Eric Wong --- git-svn.perl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 33fe34cbac..da1e1f64e9 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -219,6 +219,9 @@ for (my $i = 0; $i < @ARGV; $i++) { $cmd = $ARGV[$i]; splice @ARGV, $i, 1; last; + } elsif ($ARGV[$i] eq 'help') { + $cmd = $ARGV[$i+1]; + usage(0); } }; -- cgit v1.2.3 From ca5e880ec2172f33dd0113129775f6eb65f2f678 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 3 Jun 2009 20:45:51 -0700 Subject: git-svn: speed up find_rev_before By limiting start revision of find_rev_before to max existing revision. This avoids a long wait if you do 'git svn reset -r 9999999'. The linear search within the contiguous revisions doesn't seem to be a problem. [ew: expanded commit message] Signed-off-by: Ben Jackson Acked-by: Eric Wong --- git-svn.perl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index da1e1f64e9..f9a672d772 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3171,6 +3171,8 @@ sub find_rev_before { my ($self, $rev, $eq_ok, $min_rev) = @_; --$rev unless $eq_ok; $min_rev ||= 1; + my $max_rev = $self->rev_map_max; + $rev = $max_rev if ($rev > $max_rev); while ($rev >= $min_rev) { if (my $c = $self->rev_map_get($rev)) { return ($rev, $c); -- cgit v1.2.3 From 195643f2fc80b4d06a75b954b9a8ef2300976755 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 3 Jun 2009 20:45:52 -0700 Subject: 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 Acked-by: Eric Wong --- Documentation/git-svn.txt | 59 +++++++++++++++++++++++++++++++++++++++++- git-svn.perl | 45 ++++++++++++++++++++++++++++---- t/t9139-git-svn-reset.sh | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 6 deletions(-) create mode 100755 t/t9139-git-svn-reset.sh (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 5027f9fbd7..3f0fa5e6f1 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -216,7 +216,7 @@ config key: svn.commiturl (overwrites all svn-remote..commiturl options) The following features from `svn log' are supported: + -- ---revision=[:];; +-r/--revision=[:];; is supported, non-numeric args are not: HEAD, NEXT, BASE, PREV, etc ... -v/--verbose;; @@ -314,6 +314,63 @@ Any other arguments are passed directly to 'git-log' Shows the Subversion externals. Use -r/--revision to specify a specific revision. +'reset':: + Undoes the effects of 'fetch' back to the specified revision. + This allows you to re-'fetch' an SVN revision. Normally the + contents of an SVN revision should never change and 'reset' + should not be necessary. However, if SVN permissions change, + or if you alter your --ignore-paths option, a 'fetch' may fail + with "not found in commit" (file not previously visible) or + "checksum mismatch" (missed a modification). If the problem + file cannot be ignored forever (with --ignore-paths) the only + way to repair the repo is to use 'reset'. + +Only the rev_map and refs/remotes/git-svn are changed. Follow 'reset' +with a 'fetch' and then 'git-reset' or 'git-rebase' to move local +branches onto the new tree. + +-r/--revision=;; + Specify the most recent revision to keep. All later revisions + are discarded. +-p/--parent;; + Discard the specified revision as well, keeping the nearest + parent instead. +Example:;; +Assume you have local changes in "master", but you need to refetch "r2". + +------------ + r1---r2---r3 remotes/git-svn + \ + A---B master +------------ + +Fix the ignore-paths or SVN permissions problem that caused "r2" to +be incomplete in the first place. Then: + +[verse] +git svn reset -r2 -p +git svn fetch + +------------ + r1---r2'--r3' remotes/git-svn + \ + r2---r3---A---B master +------------ + +Then fixup "master" with 'git-rebase'. +Do NOT use 'git-merge' or your history will not be compatible with a +future 'dcommit'! + +[verse] +git rebase --onto remotes/git-svn A^ master + +------------ + r1---r2'--r3' remotes/git-svn + \ + A'--B' master +------------ + + -- OPTIONS diff --git a/git-svn.perl b/git-svn.perl index f9a672d772..b1245cbab2 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -211,6 +211,10 @@ my %cmd = ( 'blame' => [ \&Git::SVN::Log::cmd_blame, "Show what revision and author last modified each line of a file", { 'git-format' => \$_git_format } ], + 'reset' => [ \&cmd_reset, + "Undo fetches back to the specified SVN revision", + { 'revision|r=s' => \$_revision, + 'parent|p' => \$_fetch_parent } ], ); my $cmd; @@ -1054,6 +1058,20 @@ sub cmd_info { print $result, "\n"; } +sub cmd_reset { + my $target = shift || $_revision or die "SVN revision required\n"; + $target = $1 if $target =~ /^r(\d+)$/; + $target =~ /^\d+$/ or die "Numeric SVN revision expected\n"; + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); + unless ($gs) { + die "Unable to determine upstream SVN information from ". + "history\n"; + } + my ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent); + $gs->rev_map_set($r, $c, 'reset', $uuid); + print "r$r = $c ($gs->{ref_id})\n"; +} + ########################### utility functions ######################### sub rebase_cmd { @@ -3023,6 +3041,14 @@ sub _rev_map_set { croak "write: $!"; } +sub _rev_map_reset { + my ($fh, $rev, $commit) = @_; + my $c = _rev_map_get($fh, $rev); + $c eq $commit or die "_rev_map_reset(@_) commit $c does not match!\n"; + my $offset = sysseek($fh, 0, SEEK_CUR) or croak "seek: $!"; + truncate $fh, $offset or croak "truncate: $!"; +} + sub mkfile { my ($path) = @_; unless (-e $path) { @@ -3039,6 +3065,7 @@ sub rev_map_set { my $db = $self->map_path($uuid); my $db_lock = "$db.lock"; my $sig; + $update_ref ||= 0; if ($update_ref) { $SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} = $SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] }; @@ -3062,7 +3089,8 @@ sub rev_map_set { sysopen(my $fh, $db_lock, O_RDWR | O_CREAT) or croak "Couldn't open $db_lock: $!\n"; - _rev_map_set($fh, $rev, $commit); + $update_ref eq 'reset' ? _rev_map_reset($fh, $rev, $commit) : + _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"; @@ -3070,7 +3098,9 @@ sub rev_map_set { close $fh or croak $!; if ($update_ref) { $_head = $self; - command_noisy('update-ref', '-m', "r$rev", + my $note = ""; + $note = " ($update_ref)" if ($update_ref !~ /^\d*$/); + command_noisy('update-ref', '-m', "r$rev$note", $self->refname, $commit); } rename $db_lock, $db or die "rev_map_set(@_): ", "Failed to rename: ", @@ -3132,12 +3162,19 @@ sub rev_map_get { return undef unless -e $map_path; sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!"; + my $c = _rev_map_get($fh, $rev); + close($fh) or croak "close: $!"; + $c +} + +sub _rev_map_get { + my ($fh, $rev) = @_; + binmode $fh or croak "binmode: $!"; my $size = (stat($fh))[7]; ($size % 24) == 0 or croak "inconsistent size: $size"; if ($size == 0) { - close $fh or croak "close: $fh"; return undef; } @@ -3155,11 +3192,9 @@ sub rev_map_get { } elsif ($r > $rev) { $u = $i - 24; } else { # $r == $rev - close($fh) or croak "close: $!"; return $c eq ('0' x 40) ? undef : $c; } } - close($fh) or croak "close: $!"; undef; } diff --git a/t/t9139-git-svn-reset.sh b/t/t9139-git-svn-reset.sh new file mode 100755 index 0000000000..0735526d4b --- /dev/null +++ b/t/t9139-git-svn-reset.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# +# Copyright (c) 2009 Ben Jackson +# + +test_description='git svn reset' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository' ' + svn_cmd co "$svnrepo" s && + ( + cd s && + mkdir vis && + echo always visible > vis/vis.txt && + svn_cmd add vis && + svn_cmd commit -m "create visible files" && + mkdir hid && + echo initially hidden > hid/hid.txt && + svn_cmd add hid && + svn_cmd commit -m "create initially hidden files" && + svn_cmd up && + echo mod >> vis/vis.txt && + svn_cmd commit -m "modify vis" && + svn_cmd up + ) +' + +test_expect_success 'clone SVN repository with hidden directory' ' + git svn init "$svnrepo" g && + ( cd g && git svn fetch --ignore-paths="^hid" ) +' + +test_expect_success 'modify hidden file in SVN repo' ' + ( cd s && + echo mod hidden >> hid/hid.txt && + svn_cmd commit -m "modify hid" && + svn_cmd up + ) +' + +test_expect_success 'fetch fails on modified hidden file' ' + ( cd g && + git svn find-rev refs/remotes/git-svn > ../expect && + ! git svn fetch 2> ../errors && + git svn find-rev refs/remotes/git-svn > ../expect2 ) && + fgrep "not found in commit" errors && + test_cmp expect expect2 +' + +test_expect_success 'reset unwinds back to r1' ' + ( cd g && + git svn reset -r1 && + git svn find-rev refs/remotes/git-svn > ../expect2 ) && + echo 1 >expect && + test_cmp expect expect2 +' + +test_expect_success 'refetch succeeds not ignoring any files' ' + ( cd g && + git svn fetch && + git svn rebase && + fgrep "mod hidden" hid/hid.txt + ) +' + +test_done -- cgit v1.2.3 From 6224406914c3d3fecec73e32ed271dbdc0c8736a Mon Sep 17 00:00:00 2001 From: Marc Branchaud Date: Tue, 23 Jun 2009 13:02:08 -0400 Subject: git svn: Support multiple branch and tag paths in the svn repository. This enables git-svn.perl to read multiple 'branches' and 'tags' entries in svn-remote config sections. The init and clone subcommands also support multiple --branches and --tags arguments. The branch (and tag) subcommand gets a new argument: --destination (or -d). This argument is required if there are multiple branches (or tags) entries configured for the remote Subversion repository. The argument's value specifies which branch (or tag) path to use to create the branch (or tag). The specified value must match the left side (without wildcards) of one of the branches (or tags) refspecs in the svn-remote's config. [ew: avoided explicit loop when combining globs with "push"] Signed-off-by: Marc Branchaud Acked-by: Eric Wong --- git-svn.perl | 78 +++++++++++++++++------- t/t9138-git-svn-multiple-branches.sh | 114 +++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 22 deletions(-) create mode 100755 t/t9138-git-svn-multiple-branches.sh (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index b1245cbab2..48e8aad000 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -63,7 +63,7 @@ my ($SVN); $sha1 = qr/[a-f\d]{40}/; $sha1_short = qr/[a-f\d]{4,40}/; my ($_stdin, $_help, $_edit, - $_message, $_file, + $_message, $_file, $_branch_dest, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_fetch_parent, $_merge, $_strategy, $_dry_run, $_local, @@ -92,11 +92,11 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'localtime' => \$Git::SVN::_localtime, %remote_opts ); -my ($_trunk, $_tags, $_branches, $_stdlayout); +my ($_trunk, @_tags, @_branches, $_stdlayout); my %icv; my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared, - 'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags, - 'branches|b=s' => \$_branches, 'prefix=s' => \$_prefix, + 'trunk|T=s' => \$_trunk, 'tags|t=s@' => \@_tags, + 'branches|b=s@' => \@_branches, 'prefix=s' => \$_prefix, 'stdlayout|s' => \$_stdlayout, 'minimize-url|m' => \$Git::SVN::_minimize_url, 'no-metadata' => sub { $icv{noMetadata} = 1 }, @@ -141,11 +141,13 @@ my %cmd = ( branch => [ \&cmd_branch, 'Create a branch in the SVN repository', { 'message|m=s' => \$_message, + 'destination|d=s' => \$_branch_dest, 'dry-run|n' => \$_dry_run, 'tag|t' => \$_tag } ], tag => [ sub { $_tag = 1; cmd_branch(@_) }, 'Create a tag in the SVN repository', { 'message|m=s' => \$_message, + 'destination|d=s' => \$_branch_dest, 'dry-run|n' => \$_dry_run } ], 'set-tree' => [ \&cmd_set_tree, "Set an SVN repository to a git tree-ish", @@ -365,7 +367,7 @@ sub init_subdir { sub cmd_clone { my ($url, $path) = @_; if (!defined $path && - (defined $_trunk || defined $_branches || defined $_tags || + (defined $_trunk || @_branches || @_tags || defined $_stdlayout) && $url !~ m#^[a-z\+]+://#) { $path = $url; @@ -379,10 +381,10 @@ sub cmd_clone { sub cmd_init { if (defined $_stdlayout) { $_trunk = 'trunk' if (!defined $_trunk); - $_tags = 'tags' if (!defined $_tags); - $_branches = 'branches' if (!defined $_branches); + @_tags = 'tags' if (! @_tags); + @_branches = 'branches' if (! @_branches); } - if (defined $_trunk || defined $_branches || defined $_tags) { + if (defined $_trunk || @_branches || @_tags) { return cmd_multi_init(@_); } my $url = shift or die "SVN repository location required ", @@ -630,7 +632,31 @@ sub cmd_branch { my ($src, $rev, undef, $gs) = working_head_info($head); my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}}; - my $glob = $remote->{ $_tag ? 'tags' : 'branches' }; + my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' }; + my $glob; + if ($#{$allglobs} == 0) { + $glob = $allglobs->[0]; + } else { + unless(defined $_branch_dest) { + die "Multiple ", + $_tag ? "tag" : "branch", + " paths defined for Subversion repository.\n", + "You must specify where you want to create the ", + $_tag ? "tag" : "branch", + " with the --destination argument.\n"; + } + foreach my $g (@{$allglobs}) { + if ($_branch_dest eq $g->{path}->{left}) { + $glob = $g; + last; + } + } + unless (defined $glob) { + die "Unknown ", + $_tag ? "tag" : "branch", + " destination $_branch_dest\n"; + } + } my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/}; my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ()); @@ -837,7 +863,7 @@ sub cmd_proplist { sub cmd_multi_init { my $url = shift; - unless (defined $_trunk || defined $_branches || defined $_tags) { + unless (defined $_trunk || @_branches || @_tags) { usage(1); } @@ -862,10 +888,14 @@ sub cmd_multi_init { undef, $trunk_ref); } } - return unless defined $_branches || defined $_tags; + return unless @_branches || @_tags; my $ra = $url ? Git::SVN::Ra->new($url) : undef; - complete_url_ls_init($ra, $_branches, '--branches/-b', $_prefix); - complete_url_ls_init($ra, $_tags, '--tags/-t', $_prefix . 'tags/'); + foreach my $path (@_branches) { + complete_url_ls_init($ra, $path, '--branches/-b', $_prefix); + } + foreach my $path (@_tags) { + complete_url_ls_init($ra, $path, '--tags/-t', $_prefix.'tags/'); + } } sub cmd_multi_fetch { @@ -1150,6 +1180,7 @@ sub complete_url_ls_init { die "--prefix='$pfx' must have a trailing slash '/'\n"; } command_noisy('config', + '--add', "svn-remote.$gs->{repo_id}.$n", "$remote_path:refs/remotes/$pfx*" . ('/*' x (($remote_path =~ tr/*/*/) - 1)) ); @@ -1616,7 +1647,8 @@ sub fetch_all { # read the max revs for wildcard expansion (branches/*, tags/*) foreach my $t (qw/branches tags/) { defined $remote->{$t} or next; - push @globs, $remote->{$t}; + push @globs, @{$remote->{$t}}; + my $max_rev = eval { tmp_config(qw/--int --get/, "svn-remote.$repo_id.${t}-maxRev") }; if (defined $max_rev && ($max_rev < $base)) { @@ -1663,15 +1695,16 @@ sub read_all_remotes { } elsif (m!^(.+)\.(branches|tags)= (.*):refs/remotes/(.+)\s*$/!x) { my ($p, $g) = ($3, $4); - my $rs = $r->{$1}->{$2} = { - t => $2, - remote => $1, - path => Git::SVN::GlobSpec->new($p), - ref => Git::SVN::GlobSpec->new($g) }; + my $rs = { + t => $2, + remote => $1, + path => Git::SVN::GlobSpec->new($p), + ref => Git::SVN::GlobSpec->new($g) }; if (length($rs->{ref}->{right}) != 0) { die "The '*' glob character must be the last ", "character of '$g'\n"; } + push @{ $r->{$1}->{$2} }, $rs; } } @@ -1811,9 +1844,10 @@ sub find_by_url { # repos_root and, path are optional next if defined $repos_root && $repos_root ne $u; my $fetch = $remotes->{$repo_id}->{fetch} || {}; - foreach (qw/branches tags/) { - resolve_local_globs($u, $fetch, - $remotes->{$repo_id}->{$_}); + foreach my $t (qw/branches tags/) { + foreach my $globspec (@{$remotes->{$repo_id}->{$t}}) { + resolve_local_globs($u, $fetch, $globspec); + } } my $p = $path; my $rwr = rewrite_root({repo_id => $repo_id}); diff --git a/t/t9138-git-svn-multiple-branches.sh b/t/t9138-git-svn-multiple-branches.sh new file mode 100755 index 0000000000..9725ccf9d0 --- /dev/null +++ b/t/t9138-git-svn-multiple-branches.sh @@ -0,0 +1,114 @@ +#!/bin/sh +# +# Copyright (c) 2009 Marc Branchaud +# + +test_description='git svn multiple branch and tag paths in the svn repo' +. ./lib-git-svn.sh + +test_expect_success 'setup svnrepo' ' + mkdir project \ + project/trunk \ + project/b_one \ + project/b_two \ + project/tags_A \ + project/tags_B && + echo 1 > project/trunk/a.file && + svn import -m "$test_description" project "$svnrepo/project" && + rm -rf project && + svn cp -m "Branch 1" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_one/first" && + svn cp -m "Tag 1" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/1.0" && + svn co "$svnrepo/project" svn_project && + cd svn_project && + . + echo 2 > trunk/a.file && + svn ci -m "Change 1" trunk/a.file && + svn cp -m "Branch 2" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_one/second" && + svn cp -m "Tag 2" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/2.0" && + echo 3 > trunk/a.file && + svn ci -m "Change 2" trunk/a.file && + svn cp -m "Branch 3" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_two/1" && + svn cp -m "Tag 3" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/3.0" && + echo 4 > trunk/a.file && + svn ci -m "Change 3" trunk/a.file && + svn cp -m "Branch 4" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_two/2" && + svn cp -m "Tag 4" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/4.0" && + svn up && + echo 5 > b_one/first/a.file && + svn ci -m "Change 4" b_one/first/a.file && + svn cp -m "Tag 5" "$svnrepo/project/b_one/first" \ + "$svnrepo/project/tags_B/v5" && + echo 6 > b_one/second/a.file && + svn ci -m "Change 5" b_one/second/a.file && + svn cp -m "Tag 6" "$svnrepo/project/b_one/second" \ + "$svnrepo/project/tags_B/v6" && + echo 7 > b_two/1/a.file && + svn ci -m "Change 6" b_two/1/a.file && + svn cp -m "Tag 7" "$svnrepo/project/b_two/1" \ + "$svnrepo/project/tags_B/v7" && + echo 8 > b_two/2/a.file && + svn ci -m "Change 7" b_two/2/a.file && + svn cp -m "Tag 8" "$svnrepo/project/b_two/2" \ + "$svnrepo/project/tags_B/v8" && + cd .. + ' + +test_expect_success 'clone multiple branch and tag paths' ' + git svn clone -T trunk \ + -b b_one/* --branches b_two/* \ + -t tags_A/* --tags tags_B \ + "$svnrepo/project" git_project && + cd git_project && + git rev-parse refs/remotes/first && + git rev-parse refs/remotes/second && + git rev-parse refs/remotes/1 && + git rev-parse refs/remotes/2 && + git rev-parse refs/remotes/tags/1.0 && + git rev-parse refs/remotes/tags/2.0 && + git rev-parse refs/remotes/tags/3.0 && + git rev-parse refs/remotes/tags/4.0 && + git rev-parse refs/remotes/tags/v5 && + git rev-parse refs/remotes/tags/v6 && + git rev-parse refs/remotes/tags/v7 && + git rev-parse refs/remotes/tags/v8 && + cd .. + ' + +test_expect_success 'Multiple branch or tag paths require -d' ' + cd git_project && + test_must_fail git svn branch -m "No new branch" Nope && + test_must_fail git svn tag -m "No new tag" Tagless && + test_must_fail git rev-parse refs/remotes/Nope && + test_must_fail git rev-parse refs/remotes/tags/Tagless && + cd ../svn_project && + svn up && + test_must_fail test -d b_one/Nope && + test_must_fail test -d b_two/Nope && + test_must_fail test -d tags_A/Tagless && + test_must_fail test -d tags_B/Tagless && + cd .. + ' + +test_expect_success 'create new branches and tags' ' + ( cd git_project && git svn branch -m "New branch 1" -d project/b_one New1 ) && + ( cd svn_project && svn up && test -e b_one/New1/a.file ) && + + ( cd git_project && git svn branch -m "New branch 2" -d project/b_two New2 ) && + ( cd svn_project && svn up && test -e b_two/New2/a.file ) && + + ( cd git_project && git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) && + ( cd svn_project && svn up && test -e tags_A/Tag1/a.file ) + + ( cd git_project && git svn tag -m "New tag 2" -d project/tags_B Tag2 ) && + ( cd svn_project && svn up && test -e tags_B/Tag2/a.file ) + ' + +test_done -- cgit v1.2.3 From f7050599310c18bd67b35b8d59486116b30ff1f6 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 25 Jun 2009 02:28:15 -0700 Subject: git-svn: convert globs to regexps for branch destinations Marc Branchaud wrote: > I'm fairly happy with this, except for the way the branch > subcommand matches refspecs. The patch does a simple string > comparison, but it'd be better to do an actual glob. I just > couldn't track down the right function for that, so I left it as > a strcmp and hope that a gitizen can tell me how to glob here. Signed-off-by: Eric Wong --- git-svn.perl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 48e8aad000..6c42e2afca 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -646,7 +646,9 @@ sub cmd_branch { " with the --destination argument.\n"; } foreach my $g (@{$allglobs}) { - if ($_branch_dest eq $g->{path}->{left}) { + # SVN::Git::Editor could probably be moved to Git.pm.. + my $re = SVN::Git::Editor::glob2pat($g->{path}->{left}); + if ($_branch_dest =~ /$re/) { $glob = $g; last; } -- cgit v1.2.3 From 50ff23667020768fa18dcffd154406fc41ebaa84 Mon Sep 17 00:00:00 2001 From: Ulrich Dangel Date: Fri, 26 Jun 2009 16:52:09 +0200 Subject: git-svn: Canonicalize svn urls to prevent libsvn assertion Cloning/initializing svn repositories with an uncanonicalize url does not work as libsvn throws an assertion. This patch canonicalize svn uris for the clone and init command from git-svn. [ew: fixed trailing whitespace] Signed-off-by: Ulrich Dangel Acked-by: Eric Wong --- git-svn.perl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 6c42e2afca..d1af1a3d2f 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -389,6 +389,7 @@ sub cmd_init { } my $url = shift or die "SVN repository location required ", "as a command-line argument\n"; + $url = canonicalize_url($url); init_subdir(@_); do_git_init_db(); @@ -806,6 +807,12 @@ sub canonicalize_path { return $path; } +sub canonicalize_url { + my ($url) = @_; + $url =~ s#^([^:]+://[^/]*/)(.*)$#$1 . canonicalize_path($2)#e; + return $url; +} + # get_svnprops(PATH) # ------------------ # Helper for cmd_propget and cmd_proplist below. @@ -875,7 +882,7 @@ sub cmd_multi_init { $_prefix = '' unless defined $_prefix; if (defined $url) { - $url =~ s#/+$##; + $url = canonicalize_url($url); init_subdir(@_); } do_git_init_db(); -- cgit v1.2.3 From 3c49a03524b686c7b575e0a667736217e5445447 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Tue, 7 Jul 2009 01:39:52 +0200 Subject: git-svn: Always duplicate paths returned from get_log This makes get_log more safe to use because callers cannot run into path clobbering any more. The additional overhead will not affect performance since the critical calls from the fetch loop need the path duplication anyway and the rest of the call sites is not performance critical. Signed-off-by: Mattias Nissler Acked-by: Eric Wong --- git-svn.perl | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index d1af1a3d2f..b4e8d44351 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2538,9 +2538,8 @@ sub find_parent_branch { unless (defined $paths) { my $err_handler = $SVN::Error::handler; $SVN::Error::handler = \&Git::SVN::Ra::skip_unknown_revs; - $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1, sub { - $paths = - Git::SVN::Ra::dup_changed_paths($_[0]) }); + $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1, + sub { $paths = $_[0] }); $SVN::Error::handler = $err_handler; } return undef unless defined $paths; @@ -4431,6 +4430,26 @@ sub get_log { my ($self, @args) = @_; my $pool = SVN::Pool->new; + # svn_log_changed_path_t objects passed to get_log are likely to be + # overwritten even if only the refs are copied to an external variable, + # so we should dup the structures in their entirety. Using an + # externally passed pool (instead of our temporary and quickly cleared + # pool in Git::SVN::Ra) does not help matters at all... + my $receiver = pop @args; + push(@args, sub { + my ($paths) = $_[0]; + return &$receiver(@_) unless $paths; + $_[0] = (); + foreach my $p (keys %$paths) { + my $i = $paths->{$p}; + my %s = map { $_ => $i->$_ } + qw/copyfrom_path copyfrom_rev action/; + $_[0]{$p} = \%s; + } + &$receiver(@_); + }); + + # the limit parameter was not supported in SVN 1.1.x, so we # drop it. Therefore, the receiver callback passed to it # is made aware of this limitation by being wrapped if @@ -4600,7 +4619,7 @@ sub gs_fetch_loop_common { }; sub _cb { my ($paths, $r, $author, $date, $log) = @_; - [ dup_changed_paths($paths), + [ $paths, { author => $author, date => $date, log => $log } ]; } $self->get_log([$longest_path], $min, $max, 0, 1, 1, @@ -4823,24 +4842,6 @@ sub skip_unknown_revs { die "Error from SVN, ($errno): ", $err->expanded_message,"\n"; } -# svn_log_changed_path_t objects passed to get_log are likely to be -# overwritten even if only the refs are copied to an external variable, -# so we should dup the structures in their entirety. Using an externally -# passed pool (instead of our temporary and quickly cleared pool in -# Git::SVN::Ra) does not help matters at all... -sub dup_changed_paths { - my ($paths) = @_; - return undef unless $paths; - my %ret; - foreach my $p (keys %$paths) { - my $i = $paths->{$p}; - my %s = map { $_ => $i->$_ } - qw/copyfrom_path copyfrom_rev action/; - $ret{$p} = \%s; - } - \%ret; -} - package Git::SVN::Log; use strict; use warnings; -- cgit v1.2.3 From 0b2af457a49e3b00d47d556d5301934d27909db8 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Tue, 7 Jul 2009 01:40:02 +0200 Subject: git-svn: Fix branch detection when repository root is inaccessible For the case of multiple projects sharing a single SVN repository, it is common practice to create the standard SVN directory layout within a subdirectory for each project. In such setups, access control is often used to limit what projects a given user may access. git-svn failed to detect branches (e.g. when passing --stdlayout to clone) because it relied on having access to the root directory in the repository. This patch solves this problem by making git-svn use paths relative to the given repository URL instead of the repository root. Signed-off-by: Mattias Nissler Acked-by: Eric Wong --- git-svn.perl | 43 ++++++++++++++---------------------- t/t9138-git-svn-multiple-branches.sh | 8 +++---- 2 files changed, 21 insertions(+), 30 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index b4e8d44351..ec847580d6 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -876,10 +876,6 @@ sub cmd_multi_init { usage(1); } - # there are currently some bugs that prevent multi-init/multi-fetch - # setups from working well without this. - $Git::SVN::_minimize_url = 1; - $_prefix = '' unless defined $_prefix; if (defined $url) { $url = canonicalize_url($url); @@ -1180,7 +1176,7 @@ sub complete_url_ls_init { "wanted to set to: $gs->{url}\n"; } command_oneline('config', $k, $gs->{url}) unless $orig_url; - my $remote_path = "$ra->{svn_path}/$repo_path"; + my $remote_path = "$gs->{path}/$repo_path"; $remote_path =~ s#/+#/#g; $remote_path =~ s#^/##g; $remote_path .= "/*" if $remote_path !~ /\*/; @@ -2177,16 +2173,6 @@ sub ra { $ra; } -sub rel_path { - my ($self) = @_; - my $repos_root = $self->ra->{repos_root}; - return $self->{path} if ($self->{url} eq $repos_root); - my $url = $self->{url} . - (length $self->{path} ? "/$self->{path}" : $self->{path}); - $url =~ s!^\Q$repos_root\E(?:/+|$)!!g; - $url; -} - # prop_walk(PATH, REV, SUB) # ------------------------- # Recursively traverse PATH at revision REV and invoke SUB for each @@ -2512,10 +2498,7 @@ sub match_paths { if (my $path = $paths->{"/$self->{path}"}) { return ($path->{action} eq 'D') ? 0 : 1; } - my $repos_root = $self->ra->{repos_root}; - my $extended_path = $self->{url} . '/' . $self->{path}; - $extended_path =~ s#^\Q$repos_root\E(/|$)##; - $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//; + $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//; if (grep /$self->{path_regex}/, keys %$paths) { return 1; } @@ -2545,7 +2528,7 @@ sub find_parent_branch { return undef unless defined $paths; # look for a parent from another branch: - my @b_path_components = split m#/#, $self->rel_path; + my @b_path_components = split m#/#, $self->{path}; my @a_path_components; my $i; while (@b_path_components) { @@ -2563,11 +2546,11 @@ sub find_parent_branch { my $r = $i->{copyfrom_rev}; my $repos_root = $self->ra->{repos_root}; my $url = $self->ra->{url}; - my $new_url = $repos_root . $branch_from; + my $new_url = $url . $branch_from; print STDERR "Found possible branch point: ", "$new_url => ", $self->full_url, ", $r\n"; $branch_from =~ s#^/##; - my $gs = $self->other_gs($new_url, $url, $repos_root, + my $gs = $self->other_gs($new_url, $url, $branch_from, $r, $self->{ref_id}); my ($r0, $parent) = $gs->find_rev_before($r, 1); { @@ -2752,9 +2735,9 @@ sub parse_svn_date { } sub other_gs { - my ($self, $new_url, $url, $repos_root, + my ($self, $new_url, $url, $branch_from, $r, $old_ref_id) = @_; - my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from); + my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from); unless ($gs) { my $ref_id = $old_ref_id; $ref_id =~ s/\@\d+$//; @@ -4436,14 +4419,22 @@ sub get_log { # externally passed pool (instead of our temporary and quickly cleared # pool in Git::SVN::Ra) does not help matters at all... my $receiver = pop @args; + my $prefix = "/".$self->{svn_path}; + $prefix =~ s#/+($)##; + my $prefix_regex = qr#^\Q$prefix\E#; push(@args, sub { my ($paths) = $_[0]; return &$receiver(@_) unless $paths; $_[0] = (); foreach my $p (keys %$paths) { my $i = $paths->{$p}; - my %s = map { $_ => $i->$_ } - qw/copyfrom_path copyfrom_rev action/; + # Make path relative to our url, not repos_root + $p =~ s/$prefix_regex//; + my %s = map { $_ => $i->$_; } + qw/copyfrom_path copyfrom_rev action/; + if ($s{'copyfrom_path'}) { + $s{'copyfrom_path'} =~ s/$prefix_regex//; + } $_[0]{$p} = \%s; } &$receiver(@_); diff --git a/t/t9138-git-svn-multiple-branches.sh b/t/t9138-git-svn-multiple-branches.sh index cb9a6d229d..3cd06718eb 100755 --- a/t/t9138-git-svn-multiple-branches.sh +++ b/t/t9138-git-svn-multiple-branches.sh @@ -99,22 +99,22 @@ test_expect_success 'Multiple branch or tag paths require -d' ' test_expect_success 'create new branches and tags' ' ( cd git_project && - git svn branch -m "New branch 1" -d project/b_one New1 ) && + git svn branch -m "New branch 1" -d b_one New1 ) && ( cd svn_project && svn_cmd up && test -e b_one/New1/a.file ) && ( cd git_project && - git svn branch -m "New branch 2" -d project/b_two New2 ) && + git svn branch -m "New branch 2" -d b_two New2 ) && ( cd svn_project && svn_cmd up && test -e b_two/New2/a.file ) && ( cd git_project && - git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) && + git svn branch -t -m "New tag 1" -d tags_A Tag1 ) && ( cd svn_project && svn_cmd up && test -e tags_A/Tag1/a.file ) && ( cd git_project && - git svn tag -m "New tag 2" -d project/tags_B Tag2 ) && + git svn tag -m "New tag 2" -d tags_B Tag2 ) && ( cd svn_project && svn_cmd up && test -e tags_B/Tag2/a.file ) ' -- cgit v1.2.3 From b3e95936274a7f5d516e7c37b39e1a56b2d09709 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 11 Jul 2009 14:13:12 -0700 Subject: git svn: allow uppercase UUIDs from SVN SVN allows uppercase A-F characters in repositories. Although `svnadmin' does not create UUIDs with uppercase by default, it is possible to change the UUID of a SVN repository and SVN itself will make no attempt to normalize them. Thanks to Esben Skovenborg for discovering this issue. Signed-off-by: Eric Wong --- git-svn.perl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index ec847580d6..cfade63459 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1359,11 +1359,11 @@ sub read_repo_config { sub extract_metadata { my $id = shift or return (undef, undef, undef); my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+) - \s([a-f\d\-]+)$/x); + \s([a-f\d\-]+)$/ix); if (!defined $rev || !$uuid || !$url) { # some of the original repositories I made had # identifiers like this: - ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/); + ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/i); } return ($url, $rev, $uuid); } @@ -2010,7 +2010,7 @@ sub _set_svm_vars { chomp($src, $uuid); - $uuid =~ m{^[0-9a-f\-]{30,}$} + $uuid =~ m{^[0-9a-f\-]{30,}$}i or die "doesn't look right - svm:uuid is '$uuid'\n"; # the '!' is used to mark the repos_root!/relative/path @@ -2096,7 +2096,7 @@ sub svnsync { die "doesn't look right - svn:sync-from-url is '$url'\n"; my $uuid = tmp_config('--get', "$section.svnsync-uuid"); - ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or + ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or die "doesn't look right - svn:sync-from-uuid is '$uuid'\n"; $svnsync = { url => $url, uuid => $uuid } @@ -2114,7 +2114,7 @@ sub svnsync { die "doesn't look right - svn:sync-from-url is '$url'\n"; my $uuid = $rp->{'svn:sync-from-uuid'} or die $err . "uuid\n"; - ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or + ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or die "doesn't look right - svn:sync-from-uuid is '$uuid'\n"; my $section = "svn-remote.$self->{repo_id}"; @@ -2130,7 +2130,7 @@ sub ra_uuid { unless ($self->{ra_uuid}) { my $key = "svn-remote.$self->{repo_id}.uuid"; my $uuid = eval { tmp_config('--get', $key) }; - if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/) { + if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/i) { $self->{ra_uuid} = $uuid; } else { die "ra_uuid called without URL\n" unless $self->{url}; @@ -2848,7 +2848,7 @@ sub make_log_entry { die "Can't have both 'useSvmProps' and 'rewriteRoot' ", "options set!\n"; } - my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}; + my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}i; # we don't want "SVM: initializing mirror for junk" ... return undef if $r == 0; my $svm = $self->svm; -- cgit v1.2.3 From 2a679c7a3148978a3f58f1c12100383638e744c5 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 19 Jul 2009 22:08:45 -0700 Subject: git svn: fix reparenting when ugly http(s) URLs are used Mishandling of http(s) in need of escaping was causing t9118-git-svn-funky-branch-names to fail when SVN_HTTPD_PORT was defined. This bug was exposed in (but not caused by) commit 0b2af457a49e3b00d47d556d5301934d27909db8 (Fix branch detection when repository root is inaccessible) Signed-off-by: Eric Wong --- git-svn.perl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index cfade63459..43c86e85a1 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -4525,10 +4525,12 @@ sub gs_do_switch { my $full_url = $self->{url}; my $old_url = $full_url; - $full_url .= '/' . escape_uri_only($path) if length $path; + $full_url .= '/' . $path if length $path; my ($ra, $reparented); - if ($old_url =~ m#^svn(\+ssh)?://#) { + if ($old_url =~ m#^svn(\+ssh)?://# || + ($full_url =~ m#^https?://# && + escape_url($full_url) ne $full_url)) { $_[0] = undef; $self = undef; $RA = undef; -- cgit v1.2.3 From 4aacaeb3dc82bb6479e70e120053dc27a399460e Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 20 Jul 2009 02:06:24 -0700 Subject: git svn: fix shallow clone when upstream revision is too new Thanks to Ka-Hing Cheung for the initial bug report and patch: > git-svn uses $ra->get_latest_revnum to find out the latest > revision, but that can be problematic, because get_latest_revnum > returns the latest revnum in the entire repository, not > restricted by whatever URL you used to construct $ra. So if you > do git svn clone -r HEAD svn://blah/blah/trunk, it won't work if > the latest checkin is in one of the branches (it will try to > fetch a rev that doesn't exist in trunk, making the clone > useless). Relying on SVN::Core::INVALID_REVNUM (-1) as the "start" argument to SVN::Ra::get_log() proved unreliable with http(s) URLs so the result of SVN::Ra::get_latest_revnum() is used as the "start" argument instead. Signed-off-by: Eric Wong --- git-svn.perl | 1 + t/t9142-git-svn-shallow-clone.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100755 t/t9142-git-svn-shallow-clone.sh (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 43c86e85a1..9369acc4dc 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1647,6 +1647,7 @@ sub fetch_all { my $ra = Git::SVN::Ra->new($url); my $uuid = $ra->get_uuid; my $head = $ra->get_latest_revnum; + $ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] }); my $base = defined $fetch ? $head : 0; # read the max revs for wildcard expansion (branches/*, tags/*) diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh new file mode 100755 index 0000000000..fd5ad49471 --- /dev/null +++ b/t/t9142-git-svn-shallow-clone.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong +# + +test_description='git svn shallow clone' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository' ' + svn_cmd mkdir -m "create standard layout" \ + "$svnrepo"/trunk "$svnrepo"/branches "$svnrepo"/tags && + svn_cmd cp -m "branch off trunk" \ + "$svnrepo"/trunk "$svnrepo"/branches/a && + svn_cmd co "$svnrepo"/branches/a && + ( + cd a && + > foo && + svn_cmd add foo && + svn_cmd commit -m "add foo" + ) +' + +start_httpd + +test_expect_success 'clone trunk with "-r HEAD"' ' + git svn clone -r HEAD "$svnrepo/trunk" g && + ( cd g && git rev-parse --symbolic --verify HEAD ) +' + +test_done -- cgit v1.2.3 From 2da9ee0888a8570f9a85ef11f208556c5316e3d4 Mon Sep 17 00:00:00 2001 From: Robert Allan Zeh Date: Sun, 19 Jul 2009 18:00:52 -0500 Subject: git svn: add gc command Add a git svn gc command that gzips all unhandled.log files, and removes all index files under .git/svn. Signed-off-by: Robert Allan Zeh Signed-off-by: Eric Wong --- Documentation/git-svn.txt | 4 ++++ git-svn.perl | 33 +++++++++++++++++++++++++++++++++ t/t9143-git-svn-gc.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100755 t/t9143-git-svn-gc.sh (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 10af599b44..068aa58a5b 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -338,6 +338,10 @@ Any other arguments are passed directly to 'git log' Shows the Subversion externals. Use -r/--revision to specify a specific revision. +'gc':: + Compress $GIT_DIR/svn//unhandled.log files in .git/svn + and remove $GIT_DIR/svn/index files in .git/svn. + 'reset':: Undoes the effects of 'fetch' back to the specified revision. This allows you to re-'fetch' an SVN revision. Normally the diff --git a/git-svn.perl b/git-svn.perl index 9369acc4dc..70159204ce 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -31,6 +31,7 @@ require SVN::Delta; if ($SVN::Core::VERSION lt '1.1.0') { fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)"; } +my $can_compress = eval { require Compress::Zlib; 1}; push @Git::SVN::Ra::ISA, 'SVN::Ra'; push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor'; push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor'; @@ -40,6 +41,7 @@ use IO::File qw//; use File::Basename qw/dirname basename/; use File::Path qw/mkpath/; use File::Spec; +use File::Find; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; use IPC::Open3; use Git; @@ -217,6 +219,10 @@ my %cmd = ( "Undo fetches back to the specified SVN revision", { 'revision|r=s' => \$_revision, 'parent|p' => \$_fetch_parent } ], + 'gc' => [ \&cmd_gc, + "Compress unhandled.log files in .git/svn and remove " . + "index files in .git/svn", + {} ], ); my $cmd; @@ -1107,6 +1113,14 @@ sub cmd_reset { print "r$r = $c ($gs->{ref_id})\n"; } +sub cmd_gc { + if (!$can_compress) { + warn "Compress::Zlib could not be found; unhandled.log " . + "files will not be compressed.\n"; + } + find({ wanted => \&gc_directory, no_chdir => 1}, "$ENV{GIT_DIR}/svn"); +} + ########################### utility functions ######################### sub rebase_cmd { @@ -1527,6 +1541,25 @@ sub md5sum { return $md5->hexdigest(); } +sub gc_directory { + if ($can_compress && -f $_ && basename($_) eq "unhandled.log") { + my $out_filename = $_ . ".gz"; + open my $in_fh, "<", $_ or die "Unable to open $_: $!\n"; + binmode $in_fh; + my $gz = Compress::Zlib::gzopen($out_filename, "ab") or + die "Unable to open $out_filename: $!\n"; + + my $res; + while ($res = sysread($in_fh, my $str, 1024)) { + $gz->gzwrite($str) or + die "Unable to write: ".$gz->gzerror()."!\n"; + } + unlink $_ or die "unlink $File::Find::name: $!\n"; + } elsif (-f $_ && basename($_) eq "index") { + unlink $_ or die "unlink $_: $!\n"; + } +} + package Git::SVN; use strict; use warnings; diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh new file mode 100755 index 0000000000..aaa3af014c --- /dev/null +++ b/t/t9143-git-svn-gc.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Copyright (c) 2009 Robert Allan Zeh + +test_description='git svn gc basic tests' + +. ./lib-git-svn.sh + +test_expect_success 'setup directories and test repo' ' + mkdir import && + mkdir tmp && + echo "Sample text for Subversion repository." > import/test.txt && + svn_cmd import -m "import for git svn" import "$svnrepo" > /dev/null + ' + +test_expect_success 'checkout working copy from svn' \ + 'svn_cmd co "$svnrepo" test_wc' + +test_expect_success 'set some properties to create an unhandled.log file' ' + ( + cd test_wc && + svn_cmd propset foo bar test.txt && + svn_cmd commit -m "property set" + )' + +test_expect_success 'Setup repo' 'git svn init "$svnrepo"' + +test_expect_success 'Fetch repo' 'git svn fetch' + +test_expect_success 'make backup copy of unhandled.log' ' + cp .git/svn/git-svn/unhandled.log tmp + ' + +test_expect_success 'git svn gc runs' 'git svn gc' + +test_expect_success 'git svn gc produces a valid gzip file' ' + gunzip .git/svn/git-svn/unhandled.log.gz + ' + +test_expect_success 'git svn gc does not change unhandled.log files' ' + test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log + ' + +test_done -- cgit v1.2.3 From 6b48829dbbe06798eded1e7c5f70810940591f79 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 25 Jul 2009 00:00:50 -0700 Subject: git svn: revert default behavior for --minimize-url This reverts the --minimize-url behavior change that appeared recently in commit 0b2af457a49e3b00d47d556d5301934d27909db8 ("Fix branch detection when repository root is inaccessible"). However, we now allow the option to be turned off by allowing "--no-minimize-url" so people with limited-access setups can still take advantage of the fix in 0b2af457a49e3b00d47d556d5301934d27909db8. Also document the behavior and default settings of minimize-url in the manpage for the first time. This introduces a temporary UI regression to allow t9141 to pass that will be reverted (fixed) in the next commit. Signed-off-by: Eric Wong --- Documentation/git-svn.txt | 11 +++++++++++ git-svn.perl | 7 ++++++- t/t9141-git-svn-multiple-branches.sh | 8 ++++---- 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 068aa58a5b..22a0389f1e 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -80,6 +80,17 @@ COMMANDS When passed to 'init' or 'clone' this regular expression will be preserved as a config key. See 'fetch' for a description of '--ignore-paths'. +--no-minimize-url;; + When tracking multiple directories (using --stdlayout, + --branches, or --tags options), git svn will attempt to connect + to the root (or highest allowed level) of the Subversion + repository. This default allows better tracking of history if + entire projects are moved within a repository, but may cause + issues on repositories where read access restrictions are in + place. Passing '--no-minimize-url' will allow git svn to + accept URLs as-is without attempting to connect to a higher + level directory. This option is off by default when only + one URL/branch is tracked (it would do little good). 'fetch':: Fetch unfetched revisions from the Subversion remote we are diff --git a/git-svn.perl b/git-svn.perl index 70159204ce..10b77ad34c 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -19,6 +19,7 @@ $ENV{GIT_DIR} ||= '.git'; $Git::SVN::default_repo_id = 'svn'; $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn'; $Git::SVN::Ra::_log_window_size = 100; +$Git::SVN::_minimize_url = 'unset'; $Git::SVN::Log::TZ = $ENV{TZ}; $ENV{TZ} = 'UTC'; @@ -100,7 +101,7 @@ my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared, 'trunk|T=s' => \$_trunk, 'tags|t=s@' => \@_tags, 'branches|b=s@' => \@_branches, 'prefix=s' => \$_prefix, 'stdlayout|s' => \$_stdlayout, - 'minimize-url|m' => \$Git::SVN::_minimize_url, + 'minimize-url|m!' => \$Git::SVN::_minimize_url, 'no-metadata' => sub { $icv{noMetadata} = 1 }, 'use-svm-props' => sub { $icv{useSvmProps} = 1 }, 'use-svnsync-props' => sub { $icv{useSvnsyncProps} = 1 }, @@ -399,6 +400,10 @@ sub cmd_init { init_subdir(@_); do_git_init_db(); + if ($Git::SVN::_minimize_url eq 'unset') { + $Git::SVN::_minimize_url = 0; + } + Git::SVN->init($url); } diff --git a/t/t9141-git-svn-multiple-branches.sh b/t/t9141-git-svn-multiple-branches.sh index 3cd06718eb..cb9a6d229d 100755 --- a/t/t9141-git-svn-multiple-branches.sh +++ b/t/t9141-git-svn-multiple-branches.sh @@ -99,22 +99,22 @@ test_expect_success 'Multiple branch or tag paths require -d' ' test_expect_success 'create new branches and tags' ' ( cd git_project && - git svn branch -m "New branch 1" -d b_one New1 ) && + git svn branch -m "New branch 1" -d project/b_one New1 ) && ( cd svn_project && svn_cmd up && test -e b_one/New1/a.file ) && ( cd git_project && - git svn branch -m "New branch 2" -d b_two New2 ) && + git svn branch -m "New branch 2" -d project/b_two New2 ) && ( cd svn_project && svn_cmd up && test -e b_two/New2/a.file ) && ( cd git_project && - git svn branch -t -m "New tag 1" -d tags_A Tag1 ) && + git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) && ( cd svn_project && svn_cmd up && test -e tags_A/Tag1/a.file ) && ( cd git_project && - git svn tag -m "New tag 2" -d tags_B Tag2 ) && + git svn tag -m "New tag 2" -d project/tags_B Tag2 ) && ( cd svn_project && svn_cmd up && test -e tags_B/Tag2/a.file ) ' -- cgit v1.2.3 From eaa14ff8c7dbe51dfdfe7e2d9ede839fc62dcf52 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 25 Jul 2009 01:36:06 -0700 Subject: git svn: the branch command no longer needs the full path This was introduced in 0b2af457a49e3b00d47d556d5301934d27909db8 ("Fix branch detection when repository root is inaccessible") but reintroduced in the previous commit. Signed-off-by: Eric Wong --- git-svn.perl | 19 ++++++++++++++++--- t/t9141-git-svn-multiple-branches.sh | 8 ++++---- 2 files changed, 20 insertions(+), 7 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 10b77ad34c..9808597a18 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -666,9 +666,22 @@ sub cmd_branch { } } unless (defined $glob) { - die "Unknown ", - $_tag ? "tag" : "branch", - " destination $_branch_dest\n"; + my $dest_re = qr/\b\Q$_branch_dest\E\b/; + foreach my $g (@{$allglobs}) { + $g->{path}->{left} =~ /$dest_re/ or next; + if (defined $glob) { + die "Ambiguous destination: ", + $_branch_dest, "\nmatches both '", + $glob->{path}->{left}, "' and '", + $g->{path}->{left}, "'\n"; + } + $glob = $g; + } + unless (defined $glob) { + die "Unknown ", + $_tag ? "tag" : "branch", + " destination $_branch_dest\n"; + } } } my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/}; diff --git a/t/t9141-git-svn-multiple-branches.sh b/t/t9141-git-svn-multiple-branches.sh index cb9a6d229d..3cd06718eb 100755 --- a/t/t9141-git-svn-multiple-branches.sh +++ b/t/t9141-git-svn-multiple-branches.sh @@ -99,22 +99,22 @@ test_expect_success 'Multiple branch or tag paths require -d' ' test_expect_success 'create new branches and tags' ' ( cd git_project && - git svn branch -m "New branch 1" -d project/b_one New1 ) && + git svn branch -m "New branch 1" -d b_one New1 ) && ( cd svn_project && svn_cmd up && test -e b_one/New1/a.file ) && ( cd git_project && - git svn branch -m "New branch 2" -d project/b_two New2 ) && + git svn branch -m "New branch 2" -d b_two New2 ) && ( cd svn_project && svn_cmd up && test -e b_two/New2/a.file ) && ( cd git_project && - git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) && + git svn branch -t -m "New tag 1" -d tags_A Tag1 ) && ( cd svn_project && svn_cmd up && test -e tags_A/Tag1/a.file ) && ( cd git_project && - git svn tag -m "New tag 2" -d project/tags_B Tag2 ) && + git svn tag -m "New tag 2" -d tags_B Tag2 ) && ( cd svn_project && svn_cmd up && test -e tags_B/Tag2/a.file ) ' -- cgit v1.2.3 From 884cce5bd042e67a0d2a1a9317f8435634486ad1 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 25 Jul 2009 02:29:28 -0700 Subject: git svn: avoid escaping '/' when renaming/copying files Timothy Schaeffer reported the following: > Git-svn has been giving me the following error for some time > when calling "git svn dcommit": > > RA layer request failed: PROPFIND request failed on > '/svn/stf/branches/dev/sw%2Fdpemu%2Finclude%2FNetCnxn.h': PROPFIND of > '/svn/stf/branches/dev/sw%2Fdpemu%2Finclude%2FNetCnxn.h': 302 Found > (https://oursvnrepo.net) at /usr/local/libexec/git-core/git-svn line 508 > > This only occurred when git detected a rename or copy. > > Following the lead into git-svn.perl, > and noticing that some of the '/'s in the path were hex-encoded > and some were not, > I changed the regex used to find chars > to hex-encode in the relative part of the path > to exclude '/'. > It works, so far. > I have included a patch. While this has previous not been a problem in my experience, newer versions of SVN may be stricter and this does not introduce regressions in t9115. Signed-off-by: Eric Wong --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 9808597a18..fd7232cf6d 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -4005,7 +4005,7 @@ sub repo_path { sub url_path { my ($self, $path) = @_; if ($self->{url} =~ m#^https?://#) { - $path =~ s/([^~a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg; + $path =~ s!([^~a-zA-Z0-9_./-])!uc sprintf("%%%02x",ord($1))!eg; } $self->{url} . '/' . $self->repo_path($path); } -- cgit v1.2.3 From 5f8b2cbacd3bad38d0ae4d358fe9b34e98190935 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 25 Jul 2009 13:14:16 -0700 Subject: git svn: make minimize URL more reliable over http(s) In addition to path-based restrictions, Subversion servers over http(s) may have access controls implemented via the LimitExcept directive in Apache. In some cases, LimitExcept may be (arguably) misconfigured to not allow REPORT requests while allowing OPTIONS and PROPFIND. This caused problems with our existing minimize_url logic that only issued OPTIONS and PROPFIND requests when connecting and using SVN::Ra::get_latest_revnum. We now call SVN::Ra::get_log if get_latest_revnum succeeds, resulting in a REPORT request being sent. This will increase our chances of tripping access controls before we start attempting to fetch history. Signed-off-by: Eric Wong --- git-svn.perl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index fd7232cf6d..d075810724 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -4831,7 +4831,11 @@ sub minimize_url { my $c = ''; do { $url .= "/$c" if length $c; - eval { (ref $self)->new($url)->get_latest_revnum }; + eval { + my $ra = (ref $self)->new($url); + my $latest = $ra->get_latest_revnum; + $ra->get_log("", $latest, 0, 1, 0, 1, sub {}); + }; } while ($@ && ($c = shift @components)); $url; } -- cgit v1.2.3 From 63de84ad60984105b1cd60fb888968b352539313 Mon Sep 17 00:00:00 2001 From: Adam Brewster Date: Mon, 3 Aug 2009 21:40:38 -0400 Subject: svn: Honor --prefix option in init without --stdlayout Most users who type git svn init file:///tmp/repo --prefix=my-svn/ would expect the root of the svn repository to be tracked by refs/remotes/my-svn/git-svn. Acked-by: Eric Wong --- git-svn.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index d075810724..fa8583963b 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3317,7 +3317,8 @@ sub _new { $repo_id = $Git::SVN::default_repo_id; } unless (defined $ref_id && length $ref_id) { - $_[2] = $ref_id = $Git::SVN::default_ref_id; + $_prefix = '' unless defined($_prefix); + $_[2] = $ref_id = $_prefix . $Git::SVN::default_ref_id; } $_[1] = $repo_id; my $dir = "$ENV{GIT_DIR}/svn/$ref_id"; -- cgit v1.2.3 From a7d72544b112f093b6189a86d05b198b2be73828 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 7 Aug 2009 21:21:21 +0200 Subject: git-svn: ignore leading blank lines in svn:ignore Subversion ignores all blank lines in svn:ignore properties. The old git-svn code ignored blank lines everywhere except for the first line of the svn:ignore property. This patch makes the "git svn show-ignore" and "git svn create-ignore" commands ignore leading blank lines, too. Also include leading blank lines in the test suite. Signed-off-by: Michael Haggerty Acked-by: Eric Wong --- git-svn.perl | 2 ++ t/t9101-git-svn-props.sh | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index fa8583963b..b0bfb74792 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -764,6 +764,7 @@ sub cmd_show_ignore { print STDOUT "\n# $path\n"; my $s = $props->{'svn:ignore'} or return; $s =~ s/[\r\n]+/\n/g; + $s =~ s/^\n+//; chomp $s; $s =~ s#^#$path#gm; print STDOUT "$s\n"; @@ -801,6 +802,7 @@ sub cmd_create_ignore { open(GITIGNORE, '>', $ignore) or fatal("Failed to open `$ignore' for writing: $!"); $s =~ s/[\r\n]+/\n/g; + $s =~ s/^\n+//; chomp $s; # Prefix all patterns so that the ignore doesn't apply # to sub-directories. diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 9da4178c94..929499e996 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -142,7 +142,9 @@ test_expect_success 'test show-ignore' " touch deeply/nested/directory/.keep && svn_cmd add deeply && svn_cmd up && - svn_cmd propset -R svn:ignore 'no-such-file*' . + svn_cmd propset -R svn:ignore ' +no-such-file* +' . svn_cmd commit -m 'propset svn:ignore' cd .. && git svn show-ignore > show-ignore.got && @@ -171,6 +173,7 @@ test_expect_success 'test create-ignore' " " cat >prop.expect <<\EOF + no-such-file* EOF -- cgit v1.2.3 From b186a261b1f7ec1fbda8c5f6d84595f3a7716d92 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 12 Aug 2009 16:01:59 -0700 Subject: svn: initial "master" points to trunk if possible Since "trunk" is a convention for the main development branch in the SVN world, try to make that the master branch upon initial checkout if it exists. This is probably less surprising based on user requests. t9135 was the only test which relied on the previous behavior and thus needed to be modified. Signed-off-by: Eric Wong --- git-svn.perl | 11 +++++++++++ t/t9135-git-svn-moved-branch-empty-file.sh | 7 ++++++- t/t9145-git-svn-master-branch.sh | 25 +++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100755 t/t9145-git-svn-master-branch.sh (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index b0bfb74792..fad29605db 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1156,6 +1156,17 @@ sub post_fetch_checkout { my $gs = $Git::SVN::_head or return; return if verify_ref('refs/heads/master^0'); + # look for "trunk" ref if it exists + my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}}; + my $fetch = $remote->{fetch}; + if ($fetch) { + foreach my $p (keys %$fetch) { + basename($fetch->{$p}) eq 'trunk' or next; + $gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p); + last; + } + } + my $valid_head = verify_ref('HEAD^0'); command_noisy(qw(update-ref refs/heads/master), $gs->refname); return if ($valid_head || !verify_ref('HEAD^0')); diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh index 03705fa4ce..5280e5f1e4 100755 --- a/t/t9135-git-svn-moved-branch-empty-file.sh +++ b/t/t9135-git-svn-moved-branch-empty-file.sh @@ -10,7 +10,12 @@ test_expect_success 'load svn dumpfile' ' test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x' test_expect_success 'test that b1 exists and is empty' ' - (cd x && test -f b1 && ! test -s b1) + ( + cd x && + git reset --hard branch-c && + test -f b1 && + ! test -s b1 + ) ' test_done diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh new file mode 100755 index 0000000000..16852d26ae --- /dev/null +++ b/t/t9145-git-svn-master-branch.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong +# +test_description='git svn initial master branch is "trunk" if possible' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository' ' + mkdir i && + > i/a && + svn_cmd import -m trunk i "$svnrepo/trunk" && + svn_cmd import -m b/a i "$svnrepo/branches/a" && + svn_cmd import -m b/b i "$svnrepo/branches/b" +' + +test_expect_success 'git svn clone --stdlayout sets up trunk as master' ' + git svn clone -s "$svnrepo" g && + ( + cd g && + test x`git rev-parse --verify refs/remotes/trunk^0` = \ + x`git rev-parse --verify refs/heads/master^0` + ) +' + +test_done -- cgit v1.2.3 From 6f5748e14cc5bb0a836b649fb8e2d6a5eb166f1d Mon Sep 17 00:00:00 2001 From: Adam Brewster Date: Tue, 11 Aug 2009 23:14:27 -0400 Subject: svn: allow branches outside of refs/remotes It may be convenient for some users to store svn remote tracking branches outside of the refs/remotes/ heirarchy. To accomplish this feat, this patch includes the entire path to the ref in $r->{'refname'} in &read_all_remotes and tries to change references to this entry so the new value makes sense. [ew: fixed backwards compatibility, long lines] Signed-off-by: Adam Brewster Signed-off-by: Eric Wong --- git-svn.perl | 83 +++++++++++++++++++++++----------------- t/lib-git-svn.sh | 2 +- t/t9104-git-svn-follow-parent.sh | 10 ++--- t/t9107-git-svn-migrate.sh | 14 +++---- t/t9143-git-svn-gc.sh | 10 ++--- t/t9144-git-svn-old-rev_map.sh | 31 +++++++++++++++ 6 files changed, 96 insertions(+), 54 deletions(-) create mode 100755 t/t9144-git-svn-old-rev_map.sh (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index fad29605db..a2934e8651 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -909,7 +909,7 @@ sub cmd_multi_init { } do_git_init_db(); if (defined $_trunk) { - my $trunk_ref = $_prefix . 'trunk'; + my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk'; # try both old-style and new-style lookups: my $gs_trunk = eval { Git::SVN->new($trunk_ref) }; unless ($gs_trunk) { @@ -1654,23 +1654,23 @@ sub resolve_local_globs { return unless defined $glob_spec; my $ref = $glob_spec->{ref}; my $path = $glob_spec->{path}; - foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) { - next unless m#^refs/remotes/$ref->{regex}$#; + foreach (command(qw#for-each-ref --format=%(refname) refs/#)) { + next unless m#^$ref->{regex}$#; my $p = $1; my $pathname = desanitize_refname($path->full_path($p)); my $refname = desanitize_refname($ref->full_path($p)); if (my $existing = $fetch->{$pathname}) { if ($existing ne $refname) { die "Refspec conflict:\n", - "existing: refs/remotes/$existing\n", - " globbed: refs/remotes/$refname\n"; + "existing: $existing\n", + " globbed: $refname\n"; } - my $u = (::cmt_metadata("refs/remotes/$refname"))[0]; + my $u = (::cmt_metadata("$refname"))[0]; $u =~ s!^\Q$url\E(/|$)!! or die - "refs/remotes/$refname: '$url' not found in '$u'\n"; + "$refname: '$url' not found in '$u'\n"; if ($pathname ne $u) { warn "W: Refspec glob conflict ", - "(ref: refs/remotes/$refname):\n", + "(ref: $refname):\n", "expected path: $pathname\n", " real path: $u\n", "Continuing ahead with $u\n"; @@ -1748,33 +1748,35 @@ sub read_all_remotes { my $use_svm_props = eval { command_oneline(qw/config --bool svn.useSvmProps/) }; $use_svm_props = $use_svm_props eq 'true' if $use_svm_props; + my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*}; foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) { - if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) { - my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3); - die("svn-remote.$remote: remote ref '$_remote_ref' " - . "must start with 'refs/remotes/'\n") - unless $_remote_ref =~ m{^refs/remotes/(.+)}; - my $remote_ref = $1; - $local_ref =~ s{^/}{}; + if (m!^(.+)\.fetch=$svn_refspec$!) { + my ($remote, $local_ref, $remote_ref) = ($1, $2, $3); + die("svn-remote.$remote: remote ref '$remote_ref' " + . "must start with 'refs/'\n") + unless $remote_ref =~ m{^refs/}; $r->{$remote}->{fetch}->{$local_ref} = $remote_ref; $r->{$remote}->{svm} = {} if $use_svm_props; } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) { $r->{$1}->{svm} = {}; } elsif (m!^(.+)\.url=\s*(.*)\s*$!) { $r->{$1}->{url} = $2; - } elsif (m!^(.+)\.(branches|tags)= - (.*):refs/remotes/(.+)\s*$/!x) { - my ($p, $g) = ($3, $4); + } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) { + my ($remote, $t, $local_ref, $remote_ref) = + ($1, $2, $3, $4); + die("svn-remote.$remote: remote ref '$remote_ref' ($t) " + . "must start with 'refs/'\n") + unless $remote_ref =~ m{^refs/}; my $rs = { - t => $2, - remote => $1, - path => Git::SVN::GlobSpec->new($p), - ref => Git::SVN::GlobSpec->new($g) }; + t => $t, + remote => $remote, + path => Git::SVN::GlobSpec->new($local_ref), + ref => Git::SVN::GlobSpec->new($remote_ref) }; if (length($rs->{ref}->{right}) != 0) { die "The '*' glob character must be the last ", - "character of '$g'\n"; + "character of '$remote_ref'\n"; } - push @{ $r->{$1}->{$2} }, $rs; + push @{ $r->{$remote}->{$t} }, $rs; } } @@ -1882,9 +1884,9 @@ sub init_remote_config { } } my ($xrepo_id, $xpath) = find_ref($self->refname); - if (defined $xpath) { + if (!$no_write && defined $xpath) { die "svn-remote.$xrepo_id.fetch already set to track ", - "$xpath:refs/remotes/", $self->refname, "\n"; + "$xpath:", $self->refname, "\n"; } unless ($no_write) { command_noisy('config', @@ -1959,7 +1961,7 @@ sub find_ref { my ($ref_id) = @_; foreach (command(qw/config -l/)) { next unless m!^svn-remote\.(.+)\.fetch= - \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x; + \s*/?(.*?)\s*:\s*(.+?)\s*$!x; my ($repo_id, $path, $ref) = ($1, $2, $3); if ($ref eq $ref_id) { $path = '' if ($path =~ m#^\./?#); @@ -1976,16 +1978,16 @@ sub new { if (!defined $repo_id) { die "Could not find a \"svn-remote.*.fetch\" key ", "in the repository configuration matching: ", - "refs/remotes/$ref_id\n"; + "$ref_id\n"; } } my $self = _new($class, $repo_id, $ref_id, $path); if (!defined $self->{path} || !length $self->{path}) { my $fetch = command_oneline('config', '--get', "svn-remote.$repo_id.fetch", - ":refs/remotes/$ref_id\$") or + ":$ref_id\$") or die "Failed to read \"svn-remote.$repo_id.fetch\" ", - "\":refs/remotes/$ref_id\$\" in config\n"; + "\":$ref_id\$\" in config\n"; ($self->{path}, undef) = split(/\s*:\s*/, $fetch); } $self->{url} = command_oneline('config', '--get', @@ -1996,7 +1998,7 @@ sub new { } sub refname { - my ($refname) = "refs/remotes/$_[0]->{ref_id}" ; + my ($refname) = $_[0]->{ref_id} ; # It cannot end with a slash /, we'll throw up on this because # SVN can't have directories with a slash in their name, either: @@ -3331,12 +3333,23 @@ sub _new { } unless (defined $ref_id && length $ref_id) { $_prefix = '' unless defined($_prefix); - $_[2] = $ref_id = $_prefix . $Git::SVN::default_ref_id; + $_[2] = $ref_id = + "refs/remotes/$_prefix$Git::SVN::default_ref_id"; } $_[1] = $repo_id; my $dir = "$ENV{GIT_DIR}/svn/$ref_id"; + + # Older repos imported by us used $GIT_DIR/svn/foo instead of + # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo + if ($ref_id =~ m{^refs/remotes/(.*)}) { + my $old_dir = "$ENV{GIT_DIR}/svn/$1"; + if (-d $old_dir && ! -d $dir) { + $dir = $old_dir; + } + } + $_[3] = $path = '' unless (defined $path); - mkpath(["$ENV{GIT_DIR}/svn"]); + mkpath([$dir]); bless { ref_id => $ref_id, dir => $dir, index => "$dir/index", path => $path, config => "$ENV{GIT_DIR}/svn/config", @@ -5509,7 +5522,7 @@ sub minimize_connections { my $pfx = "svn-remote.$x->{old_repo_id}"; my $old_fetch = quotemeta("$x->{old_path}:". - "refs/remotes/$x->{ref_id}"); + "$x->{ref_id}"); command_noisy(qw/config --unset/, "$pfx.fetch", '^'. $old_fetch . '$'); delete $r->{$x->{old_repo_id}}-> @@ -5578,7 +5591,7 @@ sub new { my ($class, $glob) = @_; my $re = $glob; $re =~ s!/+$!!g; # no need for trailing slashes - $re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!; + $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!; my $temp = $re; my ($left, $right) = ($1, $3); $re = $2; diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 5654962343..fd8631f906 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -14,7 +14,7 @@ if ! test_have_prereq PERL; then fi GIT_DIR=$PWD/.git -GIT_SVN_DIR=$GIT_DIR/svn/git-svn +GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn SVN_TREE=$GIT_SVN_DIR/svn-tree svn >/dev/null 2>&1 diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index 78610b61e6..bbfd7f4793 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -172,11 +172,11 @@ test_expect_success "follow-parent is atomic" ' git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 && git update-ref -d refs/remotes/stunk && git config --unset svn-remote.svn.fetch stunk && - mkdir -p "$GIT_DIR"/svn/flunk@18 && - rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) && - dd if="$GIT_DIR"/svn/stunk/$rev_map \ - of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 && - rm -rf "$GIT_DIR"/svn/stunk && + mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 && + rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) && + dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \ + of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 && + rm -rf "$GIT_DIR"/svn/refs/remotes/stunk && git svn init --minimize-url -i flunk "$svnrepo"/flunk && git svn fetch -i flunk && git svn init --minimize-url -i stunk "$svnrepo"/stunk && diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index c0098d9808..901b8e09fb 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -16,9 +16,7 @@ test_expect_success 'setup old-looking metadata' ' cd .. && git svn init "$svnrepo" && git svn fetch && - mv "$GIT_DIR"/svn/* "$GIT_DIR"/ && - mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ && - rmdir "$GIT_DIR"/svn && + rm -rf "$GIT_DIR"/svn && git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} && git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} && git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn} @@ -87,7 +85,7 @@ test_expect_success 'migrate --minimize on old inited layout' ' rm -rf "$GIT_DIR"/svn && for i in `cat fetch.out`; do path=`expr $i : "\([^:]*\):.*$"` - ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"` + ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"` if test -z "$ref"; then continue; fi if test -n "$path"; then path="/$path"; fi ( mkdir -p "$GIT_DIR"/svn/$ref/info/ && @@ -107,16 +105,16 @@ test_expect_success 'migrate --minimize on old inited layout' ' test_expect_success ".rev_db auto-converted to .rev_map.UUID" ' git svn fetch -i trunk && - test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && - expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" && + test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" && + expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" && test -n "$expect" && rev_db="$(echo $expect | sed -e "s,_map,_db,")" && convert_to_rev_db "$expect" "$rev_db" && rm -f "$expect" && test -f "$rev_db" && git svn fetch -i trunk && - test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && - test ! -e "$GIT_DIR"/svn/trunk/.rev_db && + test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" && + test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db && test -f "$expect" ' diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh index f2ba2d1da3..99f69c6a0b 100755 --- a/t/t9143-git-svn-gc.sh +++ b/t/t9143-git-svn-gc.sh @@ -28,26 +28,26 @@ test_expect_success 'Setup repo' 'git svn init "$svnrepo"' test_expect_success 'Fetch repo' 'git svn fetch' test_expect_success 'make backup copy of unhandled.log' ' - cp .git/svn/git-svn/unhandled.log tmp + cp .git/svn/refs/remotes/git-svn/unhandled.log tmp ' -test_expect_success 'create leftover index' '> .git/svn/git-svn/index' +test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index' test_expect_success 'git svn gc runs' 'git svn gc' -test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index' +test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index' if perl -MCompress::Zlib -e 0 2>/dev/null then test_expect_success 'git svn gc produces a valid gzip file' ' - gunzip .git/svn/git-svn/unhandled.log.gz + gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz ' else say "Perl Compress::Zlib unavailable, skipping gunzip test" fi test_expect_success 'git svn gc does not change unhandled.log files' ' - test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log + test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log ' test_done diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh new file mode 100755 index 0000000000..7600a35cd4 --- /dev/null +++ b/t/t9144-git-svn-old-rev_map.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong + +test_description='git svn old rev_map preservd' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository with old layout' ' + mkdir i && + (cd i && > a) && + svn_cmd import -m- i "$svnrepo" && + git svn init "$svnrepo" && + git svn fetch && + test -d .git/svn/refs/remotes/git-svn/ && + ! test -e .git/svn/git-svn/ && + mv .git/svn/refs/remotes/git-svn .git/svn/ && + rm -r .git/svn/refs +' + +test_expect_success 'old layout continues to work' ' + svn_cmd import -m- i "$svnrepo/b" && + git svn rebase && + echo a >> b/a && + git add b/a && + git commit -m- -a && + git svn dcommit && + ! test -d .git/svn/refs/ && + test -e .git/svn/git-svn/ +' + +test_done -- cgit v1.2.3 From c83f4e686445c83edbe9c1d3055c0c43921e1ab8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 12 Aug 2009 22:20:02 -0700 Subject: svn: (cleanup) use predefined constant for rev_map_fmt This makes life easier in case we ever need to change the internal format of the rev_maps. Signed-off-by: Eric Wong --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index a2934e8651..1da9f0781c 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3277,7 +3277,7 @@ sub _rev_map_get { my $i = int(($l/24 + $u/24) / 2) * 24; sysseek($fh, $i, SEEK_SET) or croak "seek: $!"; sysread($fh, my $buf, 24) == 24 or croak "read: $!"; - my ($r, $c) = unpack('NH40', $buf); + my ($r, $c) = unpack(rev_map_fmt, $buf); if ($r < $rev) { $l = $i + 24; -- cgit v1.2.3 From 5268f9edc3c86b07a64fcc2679e5ffe39be28d97 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 16 Aug 2009 14:22:12 -0700 Subject: svn: assume URLs from the command-line are URI-encoded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And then unescape them when writing to $GIT_CONFIG. SVN has different rules for repository URLs (usually the root) and for paths within that repository (below the HTTP layer). Thus, for the request URI path at the HTTP level, the URI needs to be encoded. However, in the body of the HTTP request (the with underlying SVN XML protocol), those paths should not be URI-encoded[1]. For non-HTTP(S) requests, SVN appears to be more flexible and will except weird characters in the URL as well as URI-encoded ones. Since users are used to using URLs being entirely URI-encoded, git svn will now attempt to unescape the path portion of URLs while leaving the actual repository URL untouched. This change will be reflected in newly-created $GIT_CONFIG files only. This allows users to switch between svn(+ssh)://, file:// and http(s):// urls without changing the fetch/branches/tags config keys. This won't affect existing imports at all (since things didn't work before this commit anyways), and will allow users to force escaping into repository paths that look like they're escaped (but are not). Thanks to Mike Smullin for the original bug report and Björn Steinbrink for summarizing it into testable cases for me. [1] Except when committing copies/renames, see commit 29633bb91c7bcff31ff3bb59378709e3e3ef627d Signed-off-by: Eric Wong --- git-svn.perl | 2 ++ t/t9120-git-svn-clone-with-percent-escapes.sh | 52 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 1da9f0781c..5515e3ea54 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1222,6 +1222,7 @@ sub complete_url_ls_init { } command_oneline('config', $k, $gs->{url}) unless $orig_url; my $remote_path = "$gs->{path}/$repo_path"; + $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg; $remote_path =~ s#/+#/#g; $remote_path =~ s#^/##g; $remote_path .= "/*" if $remote_path !~ /\*/; @@ -1892,6 +1893,7 @@ sub init_remote_config { command_noisy('config', "svn-remote.$self->{repo_id}.url", $url); $self->{path} =~ s{^/}{}; + $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg; command_noisy('config', '--add', "svn-remote.$self->{repo_id}.fetch", "$self->{path}:".$self->refname); diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh index f159ab689b..9d9ebd533c 100755 --- a/t/t9120-git-svn-clone-with-percent-escapes.sh +++ b/t/t9120-git-svn-clone-with-percent-escapes.sh @@ -10,6 +10,10 @@ test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && svn_cmd import -m "$test_description" project "$svnrepo/pr ject" && + svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \ + "$svnrepo/pr ject/branches/b" && + svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \ + "$svnrepo/pr ject/tags/v1" && rm -rf project && start_httpd ' @@ -21,6 +25,54 @@ test_expect_success 'test clone with percent escapes' ' cd .. ' +# SVN works either way, so should we... + +test_expect_success 'svn checkout with percent escapes' ' + svn_cmd checkout "$svnrepo/pr%20ject" svn.percent && + svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk +' + +test_expect_success 'svn checkout with space' ' + svn_cmd checkout "$svnrepo/pr ject" svn.space && + svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk +' + +test_expect_success 'test clone trunk with percent escapes and minimize-url' ' + git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize && + ( + cd minimize && + git rev-parse refs/${remotes_git_svn} + ) +' + +test_expect_success 'test clone trunk with percent escapes' ' + git svn clone "$svnrepo/pr%20ject/trunk" trunk && + ( + cd trunk && + git rev-parse refs/${remotes_git_svn} + ) +' + +test_expect_success 'test clone --stdlayout with percent escapes' ' + git svn clone --stdlayout "$svnrepo/pr%20ject" percent && + ( + cd percent && + git rev-parse refs/remotes/trunk^0 && + git rev-parse refs/remotes/b^0 && + git rev-parse refs/remotes/tags/v1^0 + ) +' + +test_expect_success 'test clone -s with unescaped space' ' + git svn clone -s "$svnrepo/pr ject" space && + ( + cd space && + git rev-parse refs/remotes/trunk^0 && + git rev-parse refs/remotes/b^0 && + git rev-parse refs/remotes/tags/v1^0 + ) +' + stop_httpd test_done -- cgit v1.2.3 From f3a87d922a0333a56b5a912440aa324883019d81 Mon Sep 17 00:00:00 2001 From: Karthik R Date: Tue, 18 Aug 2009 18:54:40 -0500 Subject: git-svn: Use GIT_SSH setting if SVN_SSH is not set If SVN_SSH is defined, it will be used. Else value in GIT_SSH is copied to SVN_SSH & then, only on Windows, the \s are escaped. On Windows, the shell-variables must be set as follows GIT_SSH="C:\Program Files\PuTTY\plink.exe" SVN_SSH="C:\\Program Files\\PuTTY\\plink.exe" See http://code.google.com/p/msysgit/issues/detail?id=305 [ew: fixed indentation to use tabs] Acked-by: Eric Wong Signed-off-by: Karthik Rajagopalan --- git-svn.perl | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 5515e3ea54..ce4fef9d34 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -21,6 +21,15 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn'; $Git::SVN::Ra::_log_window_size = 100; $Git::SVN::_minimize_url = 'unset'; +if (! exists $ENV{SVN_SSH}) { + if (exists $ENV{GIT_SSH}) { + $ENV{SVN_SSH} = $ENV{GIT_SSH}; + if ($^O eq 'msys') { + $ENV{SVN_SSH} =~ s/\\/\\\\/g; + } + } +} + $Git::SVN::Log::TZ = $ENV{TZ}; $ENV{TZ} = 'UTC'; $| = 1; # unbuffer STDOUT -- cgit v1.2.3 From d3d7d47e6e0c3077fa39ffcca2b7f5f48ea97812 Mon Sep 17 00:00:00 2001 From: Mark Lodato Date: Sat, 12 Sep 2009 20:33:23 -0400 Subject: svn: properly escape arguments for authors-prog Previously, the call to authors-prog was not properly escaped, so any special characters in the Subversion username, such as spaces and semi-colons, would be interpreted by the shell rather than being passed in as the first argument. Now all unsafe characters are escaped using "git rev-parse --sq-quote" [ew: switched from "\Q..\E" to "rev-parse --sq-quote"] Signed-off-by: Mark Lodato Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 1 + t/t9138-git-svn-authors-prog.sh | 14 ++++++++++++++ 2 files changed, 15 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index d075810724..24bdbf5b81 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2810,6 +2810,7 @@ sub other_gs { sub call_authors_prog { my ($orig_author) = @_; + $orig_author = command_oneline('rev-parse', '--sq-quote', $orig_author); my $author = `$::_authors_prog $orig_author`; if ($? != 0) { die "$::_authors_prog failed with exit code $?\n" diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh index a4b00f2a3f..83cc5fc9d1 100755 --- a/t/t9138-git-svn-authors-prog.sh +++ b/t/t9138-git-svn-authors-prog.sh @@ -66,4 +66,18 @@ test_expect_success 'authors-file overrode authors-prog' ' ) ' +git --git-dir=x/.git config --unset svn.authorsfile +git --git-dir=x/.git config --unset svn.authorsprog + +test_expect_success 'authors-prog handled special characters in username' ' + svn mkdir -m bad --username "xyz; touch evil" "$svnrepo"/bad && + ( + cd x && + git svn --authors-prog=../svn-authors-prog fetch && + git rev-list -1 --pretty=raw refs/remotes/git-svn | + grep "^author xyz; touch evil " && + ! test -f evil + ) +' + test_done -- cgit v1.2.3 From c03c1f798d5f865331428e88c7d19b15471d25a8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 9 Oct 2009 11:01:04 +0400 Subject: git-svn: Avoid spurious errors when rewriteRoot is used. After doing a rebase, git-svn checks that the SVN URL is what it expects. However, it does not account for rewriteRoot, which is a legitimate way for the URL to change. This produces a lot of spurious errors. [ew: fixed line wrapping] Signed-off-by: Alexander Gavrilov Acked-by: Eric Wong --- git-svn.perl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index e0ec258e33..ab65d688fe 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -603,8 +603,15 @@ sub cmd_dcommit { "\nBefore dcommitting"; } if ($url_ ne $expect_url) { - fatal "URL mismatch after rebase: ", - "$url_ != $expect_url"; + if ($url_ eq $gs->metadata_url) { + print + "Accepting rewritten URL:", + " $url_\n"; + } else { + fatal + "URL mismatch after rebase:", + " $url_ != $expect_url"; + } } if ($uuid_ ne $uuid) { fatal "uuid mismatch after rebase: ", -- cgit v1.2.3 From 85886162f1149bb1db48e6a4c8e1c4a4a726173f Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Fri, 9 Oct 2009 13:21:13 +0100 Subject: git-svn: hide find_parent_branch output in double quiet mode Hide find_parent_branch logging when -qq is specified. This eliminates more unnecessary output when run from cron, e.g.: Found possible branch point: http://undernet-ircu.svn.sourceforge.net/svnroot/undernet-ircu/ircu2/trunk => http://undernet-ircu.svn.sourceforge.net/svnroot/undernet-ircu/ircu2/branches/authz, 1919 Found branch parent: (authz) ea061d76aea985dc0208d36fa5e0b2249b698557 Following parent with do_switch Successfully followed parent Acked-by: Eric Wong Signed-off-by: Simon Arlott Signed-off-by: Junio C Hamano --- git-svn.perl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index ab65d688fe..eb4b75aff3 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2633,7 +2633,8 @@ sub find_parent_branch { my $url = $self->ra->{url}; my $new_url = $url . $branch_from; print STDERR "Found possible branch point: ", - "$new_url => ", $self->full_url, ", $r\n"; + "$new_url => ", $self->full_url, ", $r\n" + unless $::_q > 1; $branch_from =~ s#^/##; my $gs = $self->other_gs($new_url, $url, $branch_from, $r, $self->{ref_id}); @@ -2654,11 +2655,13 @@ sub find_parent_branch { ($r0, $parent) = $gs->find_rev_before($r, 1); } if (defined $r0 && defined $parent) { - print STDERR "Found branch parent: ($self->{ref_id}) $parent\n"; + print STDERR "Found branch parent: ($self->{ref_id}) $parent\n" + unless $::_q > 1; my $ed; if ($self->ra->can_do_switch) { $self->assert_index_clean($parent); - print STDERR "Following parent with do_switch\n"; + print STDERR "Following parent with do_switch\n" + unless $::_q > 1; # do_switch works with svn/trunk >= r22312, but that # is not included with SVN 1.4.3 (the latest version # at the moment), so we can't rely on it @@ -2673,18 +2676,20 @@ sub find_parent_branch { print STDERR "Trees match:\n", " $new_url\@$r0\n", " ${\$self->full_url}\@$rev\n", - "Following parent with no changes\n"; + "Following parent with no changes\n" + unless $::_q > 1; $self->tmp_index_do(sub { command_noisy('read-tree', $parent); }); $self->{last_commit} = $parent; } else { - print STDERR "Following parent with do_update\n"; + print STDERR "Following parent with do_update\n" + unless $::_q > 1; $ed = SVN::Git::Fetcher->new($self); $self->ra->gs_do_update($rev, $rev, $self, $ed) or die "SVN connection failed somewhere...\n"; } - print STDERR "Successfully followed parent\n"; + print STDERR "Successfully followed parent\n" unless $::_q > 1; return $self->make_log_entry($rev, [$parent], $ed); } return undef; @@ -2829,7 +2834,7 @@ sub other_gs { $ref_id .= "\@$r"; # just grow a tail if we're not unique enough :x $ref_id .= '-' while find_ref($ref_id); - print STDERR "Initializing parent: $ref_id\n"; + print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1; my ($u, $p, $repo_id) = ($new_url, '', $ref_id); if ($u =~ s#^\Q$url\E(/|$)##) { $p = $u; -- cgit v1.2.3 From ffd5c8e457f8139ee89006083d97d47e17a066b8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 22 Oct 2009 23:39:04 -0700 Subject: git svn: fix fetch where glob is on the top-level URL In cases where the top-level URL we're tracking is the path we glob against, we can once again track odd repositories that keep branches/tags at the top level. This regression was introduced in commit 6f5748e14cc5bb0a836b649fb8e2d6a5eb166f1d. Thanks to Daniel Cordero for the original bug report and bisection. Signed-off-by: Eric Wong --- git-svn.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index eb4b75aff3..56af2216a9 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1765,7 +1765,7 @@ sub read_all_remotes { my $use_svm_props = eval { command_oneline(qw/config --bool svn.useSvmProps/) }; $use_svm_props = $use_svm_props eq 'true' if $use_svm_props; - my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*}; + my $svn_refspec = qr{\s*(.*?)\s*:\s*(.+?)\s*}; foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) { if (m!^(.+)\.fetch=$svn_refspec$!) { my ($remote, $local_ref, $remote_ref) = ($1, $2, $3); @@ -1979,7 +1979,7 @@ sub find_ref { my ($ref_id) = @_; foreach (command(qw/config -l/)) { next unless m!^svn-remote\.(.+)\.fetch= - \s*/?(.*?)\s*:\s*(.+?)\s*$!x; + \s*(.*?)\s*:\s*(.+?)\s*$!x; my ($repo_id, $path, $ref) = ($1, $2, $3); if ($ref eq $ref_id) { $path = '' if ($path =~ m#^\./?#); -- cgit v1.2.3 From f1264bd65451a68e87e0f99c9601cfa1b3244db6 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Tue, 20 Oct 2009 15:42:01 +1300 Subject: git-svn: convert SVK merge tickets to extra parents SVK is a simple case to start with, as its idea of merge parents matches git's one. When a svk:merge ticket is encountered, check each of the listed merged revisions to see if they are in the history of this commit; if not, then we have encountered a merge - record it. [ew: minor formatting cleanups] Signed-off-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 52 ++++++++++++++++++++++++++++++++++++++++++++- t/t9150-svk-mergetickets.sh | 23 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 t/t9150-svk-mergetickets.sh (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 56af2216a9..1b918aef71 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2878,14 +2878,64 @@ sub check_author { $author; } +sub find_extra_svk_parents { + my ($self, $ed, $tickets, $parents) = @_; + # aha! svk:merge property changed... + my @tickets = split "\n", $tickets; + my @known_parents; + for my $ticket ( @tickets ) { + my ($uuid, $path, $rev) = split /:/, $ticket; + if ( $uuid eq $self->ra_uuid ) { + my $url = $self->rewrite_root || $self->{url}; + my $repos_root = $url; + my $branch_from = $path; + $branch_from =~ s{^/}{}; + my $gs = $self->other_gs($repos_root."/".$branch_from, + $url, + $branch_from, + $rev, + $self->{ref_id}); + if ( my $commit = $gs->rev_map_get($rev, $uuid) ) { + # wahey! we found it, but it might be + # an old one (!) + push @known_parents, $commit; + } + } + } + for my $parent ( @known_parents ) { + my @cmd = ('rev-list', $parent, map { "^$_" } @$parents ); + my ($msg_fh, $ctx) = command_output_pipe(@cmd); + my $new; + while ( <$msg_fh> ) { + $new=1;last; + } + command_close_pipe($msg_fh, $ctx); + if ( $new ) { + print STDERR + "Found merge parent (svk:merge ticket): $parent\n"; + push @$parents, $parent; + } + } +} + sub make_log_entry { my ($self, $rev, $parents, $ed) = @_; my $untracked = $self->get_untracked($ed); + my @parents = @$parents; + my $ps = $ed->{path_strip} || ""; + for my $path ( grep { m/$ps/ } %{$ed->{dir_prop}} ) { + my $props = $ed->{dir_prop}{$path}; + if ( $props->{"svk:merge"} ) { + $self->find_extra_svk_parents + ($ed, $props->{"svk:merge"}, \@parents); + } + } + open my $un, '>>', "$self->{dir}/unhandled.log" or croak $!; print $un "r$rev\n" or croak $!; print $un $_, "\n" foreach @$untracked; - my %log_entry = ( parents => $parents || [], revision => $rev, + my %log_entry = ( parents => \@parents, revision => $rev, log => ''); my $headrev; diff --git a/t/t9150-svk-mergetickets.sh b/t/t9150-svk-mergetickets.sh new file mode 100644 index 0000000000..8000c347b4 --- /dev/null +++ b/t/t9150-svk-mergetickets.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright (c) 2007 Sam Vilain +# + +test_description='git-svn svk merge tickets' + +. ./lib-git-svn.sh + +test_expect_success 'load svk depot' " + svnadmin load -q '$rawsvnrepo' < '../t9150/svk-merge.dump' && + git svn init --minimize-url -R svkmerge \ + -T trunk -b branches '$svnrepo' && + git svn fetch --all + " + +uuid=b48289b2-9c08-4d72-af37-0358a40b9c15 + +test_expect_success 'svk merges were represented coming in' " + [ `git-cat-file commit HEAD | grep parent | wc -l` -eq 2 ] + " + +test_done -- cgit v1.2.3 From dff589ef949e97cbec5363b105ba27f6e85594fb Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Tue, 20 Oct 2009 15:42:03 +1300 Subject: git-svn: convert SVN 1.5+ / svnmerge.py svn:mergeinfo props to parents This feature is long overdue; convert SVN's merge representation to git's as revisions are imported. This works by converting the list of revisions in each line of the svn:mergeinfo into git revision ranges, and then checking the latest of each of these revision ranges for A) being new and B) now being completely merged. Signed-off-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ t/t9151-svn-mergeinfo.sh | 21 +++++++++++ 2 files changed, 114 insertions(+) create mode 100644 t/t9151-svn-mergeinfo.sh (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 1b918aef71..6a3b501d24 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2918,6 +2918,93 @@ sub find_extra_svk_parents { } } +# note: this function should only be called if the various dirprops +# have actually changed +sub find_extra_svn_parents { + my ($self, $ed, $mergeinfo, $parents) = @_; + # aha! svk:merge property changed... + + # We first search for merged tips which are not in our + # history. Then, we figure out which git revisions are in + # that tip, but not this revision. If all of those revisions + # are now marked as merge, we can add the tip as a parent. + my @merges = split "\n", $mergeinfo; + my @merge_tips; + my @merged_commit_ranges; + my $url = $self->rewrite_root || $self->{url}; + for my $merge ( @merges ) { + my ($source, $revs) = split ":", $merge; + my $path = $source; + $path =~ s{^/}{}; + my $gs = Git::SVN->find_by_url($url.$source, $url, $path); + if ( !$gs ) { + warn "Couldn't find revmap for $url$source\n"; + next; + } + my @ranges = split ",", $revs; + my ($tip, $tip_commit); + # find the tip + for my $range ( @ranges ) { + my ($bottom, $top) = split "-", $range; + $top ||= $bottom; + my $bottom_commit = + $gs->rev_map_get($bottom, $self->ra_uuid) || + $gs->rev_map_get($bottom+1, $self->ra_uuid); + my $top_commit = + $gs->rev_map_get($top, $self->ra_uuid); + + unless ($top_commit and $bottom_commit) { + warn "W:unknown path/rev in svn:mergeinfo " + ."dirprop: $source:$range\n"; + next; + } + + push @merged_commit_ranges, + "$bottom_commit..$top_commit"; + + if ( !defined $tip or $top > $tip ) { + $tip = $top; + $tip_commit = $top_commit; + } + } + unless (!$tip_commit or + grep { $_ eq $tip_commit } @$parents ) { + push @merge_tips, $tip_commit; + } else { + push @merge_tips, undef; + } + } + for my $merge_tip ( @merge_tips ) { + my $spec = shift @merges; + next unless $merge_tip; + my @cmd = ('rev-list', "-1", $merge_tip, + "--not", @$parents ); + my ($msg_fh, $ctx) = command_output_pipe(@cmd); + my $new; + while ( <$msg_fh> ) { + $new=1;last; + } + command_close_pipe($msg_fh, $ctx); + if ( $new ) { + push @cmd, @merged_commit_ranges; + my ($msg_fh, $ctx) = command_output_pipe(@cmd); + my $unmerged; + while ( <$msg_fh> ) { + $unmerged=1;last; + } + command_close_pipe($msg_fh, $ctx); + if ( $unmerged ) { + warn "W:svn cherry-pick ignored ($spec)\n"; + } else { + warn + "Found merge parent (svn:mergeinfo prop): ", + $merge_tip, "\n"; + push @$parents, $merge_tip; + } + } + } +} + sub make_log_entry { my ($self, $rev, $parents, $ed) = @_; my $untracked = $self->get_untracked($ed); @@ -2930,6 +3017,12 @@ sub make_log_entry { $self->find_extra_svk_parents ($ed, $props->{"svk:merge"}, \@parents); } + if ( $props->{"svn:mergeinfo"} ) { + $self->find_extra_svn_parents + ($ed, + $props->{"svn:mergeinfo"}, + \@parents); + } } open my $un, '>>', "$self->{dir}/unhandled.log" or croak $!; diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh new file mode 100644 index 0000000000..7eb36e599c --- /dev/null +++ b/t/t9151-svn-mergeinfo.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Copyright (c) 2007, 2009 Sam Vilain +# + +test_description='git-svn svn mergeinfo properties' + +. ./lib-git-svn.sh + +test_expect_success 'load svn dump' " + svnadmin load -q '$rawsvnrepo' < '../t9151/svn-mergeinfo.dump' && + git svn init --minimize-url -R svnmerge \ + -T trunk -b branches '$svnrepo' && + git svn fetch --all + " + +test_expect_success 'svn merges were represented coming in' " + [ `git cat-file commit HEAD | grep parent | wc -l` -eq 2 ] + " + +test_done -- cgit v1.2.3 From b4479f074760a788dd4e353b8c86a7d735afc53e Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 30 Oct 2009 20:42:34 -0500 Subject: add -i, send-email, svn, p4, etc: use "git var GIT_EDITOR" Use the new "git var GIT_EDITOR" feature to decide what editor to use, instead of duplicating its logic elsewhere. This should make the behavior of commands in edge cases (e.g., editor names with spaces) a little more consistent. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 +--- Documentation/git-commit.txt | 2 +- Documentation/git-send-email.txt | 4 ++-- contrib/fast-import/git-p4 | 5 +---- git-add--interactive.perl | 3 +-- git-send-email.perl | 3 ++- git-sh-setup.sh | 19 ++++++------------- git-svn.perl | 5 ++--- 8 files changed, 16 insertions(+), 29 deletions(-) (limited to 'git-svn.perl') diff --git a/Documentation/config.txt b/Documentation/config.txt index d1e2120e15..5181b77629 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -387,9 +387,7 @@ core.editor:: Commands such as `commit` and `tag` that lets you edit messages by launching an editor uses the value of this variable when it is set, and the environment variable - `GIT_EDITOR` is not set. The order of preference is - `GIT_EDITOR` environment, `core.editor`, `VISUAL` and - `EDITOR` environment variables and then finally `vi`. + `GIT_EDITOR` is not set. See linkgit:git-var[1]. core.pager:: The command that git will use to paginate output. Can diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 0578a40d84..3ea80c820f 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -323,7 +323,7 @@ ENVIRONMENT AND CONFIGURATION VARIABLES The editor used to edit the commit log message will be chosen from the GIT_EDITOR environment variable, the core.editor configuration variable, the VISUAL environment variable, or the EDITOR environment variable (in that -order). +order). See linkgit:git-var[1] for details. HOOKS ----- diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 767cf4d4bd..c85d7f4385 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -60,8 +60,8 @@ The --bcc option must be repeated for each user you want on the bcc list. The --cc option must be repeated for each user you want on the cc list. --compose:: - Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an - introductory message for the patch series. + Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1]) + to edit an introductory message for the patch series. + When '--compose' is used, git send-email will use the From, Subject, and In-Reply-To headers specified in the message. If the body of the message diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e710219ca5..48059d0aa7 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -729,13 +729,10 @@ class P4Submit(Command): tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() mtime = os.stat(fileName).st_mtime - defaultEditor = "vi" - if platform.system() == "Windows": - defaultEditor = "notepad" if os.environ.has_key("P4EDITOR"): editor = os.environ.get("P4EDITOR") else: - editor = os.environ.get("EDITOR", defaultEditor); + editor = read_pipe("git var GIT_EDITOR") system(editor + " " + fileName) response = "y" diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 69aeaf03ec..0c74e5c14a 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -987,8 +987,7 @@ sub edit_hunk_manually { EOF close $fh; - my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") - || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; + chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR))); system('sh', '-c', $editor.' "$@"', $editor, $hunkfile); if ($? != 0) { diff --git a/git-send-email.perl b/git-send-email.perl index a0279de687..4f5da4ecf2 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -162,7 +162,8 @@ my $compose_filename; # Handle interactive edition of files. my $multiedit; -my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; +my $editor = Git::command_oneline('var', 'GIT_EDITOR'); + sub do_edit { if (defined($multiedit) && !$multiedit) { map { diff --git a/git-sh-setup.sh b/git-sh-setup.sh index c41c2f7439..99cceeb858 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -99,19 +99,12 @@ set_reflog_action() { } git_editor() { - : "${GIT_EDITOR:=$(git config core.editor)}" - : "${GIT_EDITOR:=${VISUAL:-${EDITOR}}}" - case "$GIT_EDITOR,$TERM" in - ,dumb) - echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL," - echo >&2 "or EDITOR. Tried to fall back to vi but terminal is dumb." - echo >&2 "Please set one of these variables to an appropriate" - echo >&2 "editor or run $0 with options that will not cause an" - echo >&2 "editor to be invoked (e.g., -m or -F for git-commit)." - exit 1 - ;; - esac - eval "${GIT_EDITOR:=vi}" '"$@"' + if test -z "${GIT_EDITOR:+set}" + then + GIT_EDITOR="$(git var GIT_EDITOR)" || return $? + fi + + eval "$GIT_EDITOR" '"$@"' } is_bare_repository () { diff --git a/git-svn.perl b/git-svn.perl index 6a3b501d24..42c9a728ad 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1321,9 +1321,8 @@ sub get_commit_entry { close $log_fh or croak $!; if ($_edit || ($type eq 'tree')) { - my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi'; - # TODO: strip out spaces, comments, like git-commit.sh - system($editor, $commit_editmsg); + chomp(my $editor = command_oneline(qw(var GIT_EDITOR))); + system('sh', '-c', $editor.' "$@"', $editor, $commit_editmsg); } rename $commit_editmsg, $commit_msg or croak $!; { -- cgit v1.2.3 From dec543e62dbc47be0c06805330a20f7fa9f699a3 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 30 Oct 2009 20:43:19 -0500 Subject: am -i, git-svn: use "git var GIT_PAGER" Use the new "git var GIT_PAGER" command to ask what pager to use. Without this change, the core.pager configuration is ignored by these commands. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- git-am.sh | 5 ++++- git-svn.perl | 6 ++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'git-svn.perl') diff --git a/git-am.sh b/git-am.sh index c132f50da5..26494877f4 100755 --- a/git-am.sh +++ b/git-am.sh @@ -649,7 +649,10 @@ do [eE]*) git_editor "$dotest/final-commit" action=again ;; [vV]*) action=again - LESS=-S ${PAGER:-less} "$dotest/patch" ;; + : ${GIT_PAGER=$(git var GIT_PAGER)} + : ${LESS=-FRSX} + export LESS + $GIT_PAGER "$dotest/patch" ;; *) action=again ;; esac done diff --git a/git-svn.perl b/git-svn.perl index 42c9a728ad..c4ca5487f3 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -5171,10 +5171,8 @@ sub git_svn_log_cmd { # adapted from pager.c sub config_pager { - $pager ||= $ENV{GIT_PAGER} || $ENV{PAGER}; - if (!defined $pager) { - $pager = 'less'; - } elsif (length $pager == 0 || $pager eq 'cat') { + chomp(my $pager = command_oneline(qw(var GIT_PAGER))); + if ($pager eq 'cat') { $pager = undef; } $ENV{GIT_PAGER_IN_USE} = defined($pager); -- cgit v1.2.3 From 753dc384dc2c4ab3e1049f695425cebf41ff7e6b Mon Sep 17 00:00:00 2001 From: Toby Allsopp Date: Sat, 14 Nov 2009 13:26:47 -0800 Subject: git svn: handle SVN merges from revisions past the tip of the branch When recording the revisions that it has merged, SVN sets the top revision to be the latest revision in the repository, which is not necessarily a revision on the branch that is being merged from. When it is not on the branch, git-svn fails to add the extra parent to represent the merge because it relies on finding the commit on the branch that corresponds to the top of the SVN merge range. In order to correctly handle this case, we look for the maximum revision less than or equal to the top of the SVN merge range that is actually on the branch being merged from. [ew: This includes the following (squashed) commit to prevent errors during bisect:] Author: Toby Allsopp Date: Fri Nov 13 09:48:39 2009 +1300 git-svn: add (failing) test for SVN 1.5+ merge with intervening commit This test exposes a bug in git-svn's handling of SVN 1.5+ mergeinfo properties. The problematic case is when there is some commit on an unrelated branch after the last commit on the merged-from branch. When SVN records the mergeinfo property, it records the latest revision in the whole repository, which, in the problematic case, is not on the branch it is merging from. To trigger the git-svn bug, we modify t9151 to include two SVN merges, the second of which has an intervening commit. The SVN dump was generated using SVN 1.6.6 (on Debian squeeze amd64). Signed-off-by: Toby Allsopp Acked-by: Eric Wong --- git-svn.perl | 7 +- t/t9151-svn-mergeinfo.sh | 6 +- t/t9151/make-svnmerge-dump | 32 +++- t/t9151/svn-mergeinfo.dump | 386 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 371 insertions(+), 60 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 6a3b501d24..27fbe3063b 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2950,8 +2950,11 @@ sub find_extra_svn_parents { my $bottom_commit = $gs->rev_map_get($bottom, $self->ra_uuid) || $gs->rev_map_get($bottom+1, $self->ra_uuid); - my $top_commit = - $gs->rev_map_get($top, $self->ra_uuid); + my $top_commit; + for (; !$top_commit && $top >= $bottom; --$top) { + $top_commit = + $gs->rev_map_get($top, $self->ra_uuid); + } unless ($top_commit and $bottom_commit) { warn "W:unknown path/rev in svn:mergeinfo " diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh index 9bee516358..f57daf401a 100755 --- a/t/t9151-svn-mergeinfo.sh +++ b/t/t9151-svn-mergeinfo.sh @@ -15,7 +15,11 @@ test_expect_success 'load svn dump' " git svn fetch --all " -test_expect_success 'svn merges were represented coming in' " +test_expect_success 'represent svn merges without intervening commits' " + [ `git cat-file commit HEAD^1 | grep parent | wc -l` -eq 2 ] + " + +test_expect_success 'represent svn merges with intervening commits' " [ `git cat-file commit HEAD | grep parent | wc -l` -eq 2 ] " diff --git a/t/t9151/make-svnmerge-dump b/t/t9151/make-svnmerge-dump index e35d64d585..7e3da75f86 100644 --- a/t/t9151/make-svnmerge-dump +++ b/t/t9151/make-svnmerge-dump @@ -28,6 +28,10 @@ svn cp trunk branches/left echo "Committing BRANCH POINT" svn commit -m "make left branch" +svn cp trunk branches/right + +echo "Committing other BRANCH POINT" +svn commit -m "make right branch" cd branches/left/ #$sm init @@ -64,7 +68,33 @@ git cat-file blob b51ad431:Makefile > Makefile svn resolved Makefile -svn commit -m "Merge trunk" +svn commit -m "Merge trunk 1" + +# create commits on both branches + +cd ../branches/left +git cat-file blob ff5ebe39:Makefile > Makefile +echo "Committing BRANCH UPDATE 4" +svn commit -m "left update 4" + +cd ../right +git cat-file blob b5039db6:Makefile > Makefile +echo "Committing other BRANCH UPDATE 1" +svn commit -m "right update 1" + +# merge to trun again + +cd ../.. +svn update +cd trunk + +svn merge ../branches/left --accept postpone + +git cat-file blob b51ad431:Makefile > Makefile + +svn resolved Makefile + +svn commit -m "Merge trunk 2" cd ../.. diff --git a/t/t9151/svn-mergeinfo.dump b/t/t9151/svn-mergeinfo.dump index 2153187c9b..11a883fda9 100644 --- a/t/t9151/svn-mergeinfo.dump +++ b/t/t9151/svn-mergeinfo.dump @@ -1,6 +1,6 @@ SVN-fs-dump-format-version: 2 -UUID: 1ce241d1-ba54-4eb9-bded-03057fe48a33 +UUID: 1530d5a2-a1dc-4438-8ad5-d95e96db8945 Revision-number: 0 Prop-content-length: 56 @@ -9,12 +9,12 @@ Content-length: 56 K 8 svn:date V 27 -2009-10-20T01:33:37.692723Z +2009-11-12T20:29:38.812226Z PROPS-END Revision-number: 1 -Prop-content-length: 123 -Content-length: 123 +Prop-content-length: 127 +Content-length: 127 K 7 svn:log @@ -22,12 +22,12 @@ V 24 Setup trunk and branches K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:38.159933Z +2009-11-12T20:29:39.045856Z PROPS-END Node-path: branches @@ -49,8 +49,8 @@ PROPS-END Revision-number: 2 -Prop-content-length: 106 -Content-length: 106 +Prop-content-length: 110 +Content-length: 110 K 7 svn:log @@ -58,12 +58,12 @@ V 8 ancestor K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:39.160059Z +2009-11-12T20:29:40.079587Z PROPS-END Node-path: trunk/Makefile @@ -72,6 +72,7 @@ Node-action: add Prop-content-length: 10 Text-content-length: 2401 Text-content-md5: bfd8ff778d1492dc6758567373176a89 +Text-content-sha1: 103205ce331f7d64086dba497574734f78439590 Content-length: 2411 PROPS-END @@ -155,8 +156,8 @@ backup: clean Revision-number: 3 -Prop-content-length: 115 -Content-length: 115 +Prop-content-length: 119 +Content-length: 119 K 7 svn:log @@ -164,12 +165,12 @@ V 16 make left branch K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:41.148192Z +2009-11-12T20:29:42.084439Z PROPS-END Node-path: branches/left @@ -177,27 +178,54 @@ Node-kind: dir Node-action: add Node-copyfrom-rev: 1 Node-copyfrom-path: trunk -Prop-content-length: 34 -Content-length: 34 -K 13 -svn:mergeinfo -V 0 +Node-path: branches/left/Makefile +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/Makefile +Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89 +Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590 + + +Revision-number: 4 +Prop-content-length: 120 +Content-length: 120 + +K 7 +svn:log +V 17 +make right branch +K 10 +svn:author +V 8 +tallsopp +K 8 +svn:date +V 27 +2009-11-12T20:29:44.065452Z PROPS-END +Node-path: branches/right +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + -Node-path: branches/left/Makefile +Node-path: branches/right/Makefile Node-kind: file Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: trunk/Makefile Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89 +Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590 -Revision-number: 4 -Prop-content-length: 112 -Content-length: 112 +Revision-number: 5 +Prop-content-length: 116 +Content-length: 116 K 7 svn:log @@ -205,12 +233,12 @@ V 13 left update 1 K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:42.148773Z +2009-11-12T20:29:45.066262Z PROPS-END Node-path: branches/left/Makefile @@ -218,6 +246,7 @@ Node-kind: file Node-action: change Text-content-length: 2465 Text-content-md5: 16e38d9753b061731650561ce01b1195 +Text-content-sha1: 36da4b84ea9b64218ab48171dfc5c48ae025f38b Content-length: 2465 # -DCOLLISION_CHECK if you believe that SHA1's @@ -299,9 +328,9 @@ backup: clean cd .. ; tar czvf dircache.tar.gz dir-cache -Revision-number: 5 -Prop-content-length: 111 -Content-length: 111 +Revision-number: 6 +Prop-content-length: 115 +Content-length: 115 K 7 svn:log @@ -309,12 +338,12 @@ V 12 trunk update K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:43.159959Z +2009-11-12T20:29:46.278498Z PROPS-END Node-path: trunk/Makefile @@ -322,6 +351,7 @@ Node-kind: file Node-action: change Text-content-length: 2521 Text-content-md5: 0668418a621333f4aa8b6632cd63e2a0 +Text-content-sha1: 4f29afd038e52f45acb5ef8c41acfc70062a741a Content-length: 2521 # -DCOLLISION_CHECK if you believe that SHA1's @@ -406,9 +436,9 @@ backup: clean cd .. ; tar czvf dircache.tar.gz dir-cache -Revision-number: 6 -Prop-content-length: 112 -Content-length: 112 +Revision-number: 7 +Prop-content-length: 116 +Content-length: 116 K 7 svn:log @@ -416,12 +446,12 @@ V 13 left update 2 K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:44.164175Z +2009-11-12T20:29:47.069090Z PROPS-END Node-path: branches/left/Makefile @@ -429,6 +459,7 @@ Node-kind: file Node-action: change Text-content-length: 2529 Text-content-md5: f6b197cc3f2e89a83e545d4bb003de73 +Text-content-sha1: 2f656677cfec0bceec85e53036ffb63e25126f8e Content-length: 2529 # -DCOLLISION_CHECK if you believe that SHA1's @@ -510,9 +541,9 @@ backup: clean cd .. ; tar czvf dircache.tar.gz dir-cache -Revision-number: 7 -Prop-content-length: 112 -Content-length: 112 +Revision-number: 8 +Prop-content-length: 116 +Content-length: 116 K 7 svn:log @@ -520,12 +551,12 @@ V 13 left update 3 K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:45.144214Z +2009-11-12T20:29:48.053835Z PROPS-END Node-path: branches/left/Makefile @@ -533,6 +564,7 @@ Node-kind: file Node-action: change Text-content-length: 2593 Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba +Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10 Content-length: 2593 # -DCOLLISION_CHECK if you believe that SHA1's @@ -614,22 +646,22 @@ backup: clean cd .. ; tar czvf dircache.tar.gz dir-cache -Revision-number: 8 -Prop-content-length: 110 -Content-length: 110 +Revision-number: 9 +Prop-content-length: 116 +Content-length: 116 K 7 svn:log -V 11 -Merge trunk +V 13 +Merge trunk 1 K 10 svn:author -V 4 -samv +V 8 +tallsopp K 8 svn:date V 27 -2009-10-20T01:33:48.176135Z +2009-11-12T20:29:51.098306Z PROPS-END Node-path: trunk @@ -641,7 +673,7 @@ Content-length: 53 K 13 svn:mergeinfo V 18 -/branches/left:2-7 +/branches/left:2-8 PROPS-END @@ -650,6 +682,7 @@ Node-kind: file Node-action: change Text-content-length: 2713 Text-content-md5: 0afbe34f244cd662b1f97d708c687f90 +Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14 Content-length: 2713 # -DCOLLISION_CHECK if you believe that SHA1's @@ -734,3 +767,244 @@ backup: clean cd .. ; tar czvf dircache.tar.gz dir-cache +Revision-number: 10 +Prop-content-length: 116 +Content-length: 116 + +K 7 +svn:log +V 13 +left update 4 +K 10 +svn:author +V 8 +tallsopp +K 8 +svn:date +V 27 +2009-11-12T20:29:52.081644Z +PROPS-END + +Node-path: branches/left/Makefile +Node-kind: file +Node-action: change +Text-content-length: 2529 +Text-content-md5: f6b197cc3f2e89a83e545d4bb003de73 +Text-content-sha1: 2f656677cfec0bceec85e53036ffb63e25126f8e +Content-length: 2529 + +# -DCOLLISION_CHECK if you believe that SHA1's +# 1461501637330902918203684832716283019655932542976 hashes do not give you +# enough guarantees about no collisions between objects ever hapenning. +# +# -DNSEC if you want git to care about sub-second file mtimes and ctimes. +# Note that you need some new glibc (at least >2.2.4) for this, and it will +# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly +# break unless your underlying filesystem supports those sub-second times +# (my ext3 doesn't). +CFLAGS=-g -O3 -Wall + +CC=gcc + + +PROG= update-cache show-diff init-db write-tree read-tree commit-tree \ + cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \ + check-files ls-tree merge-base + +all: $(PROG) + +install: $(PROG) + install $(PROG) $(HOME)/bin/ + +LIBS= -lssl -lz + +init-db: init-db.o + +update-cache: update-cache.o read-cache.o + $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS) + +show-diff: show-diff.o read-cache.o + $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS) + +write-tree: write-tree.o read-cache.o + $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS) + +read-tree: read-tree.o read-cache.o + $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS) + +commit-tree: commit-tree.o read-cache.o + $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS) + +cat-file: cat-file.o read-cache.o + $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS) + +fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o + $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS) + +checkout-cache: checkout-cache.o read-cache.o + $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS) + +diff-tree: diff-tree.o read-cache.o + $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS) + +rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o + $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS) + +show-files: show-files.o read-cache.o + $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS) + +check-files: check-files.o read-cache.o + $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS) + +ls-tree: ls-tree.o read-cache.o + $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS) + +merge-base: merge-base.o read-cache.o + $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS) + +read-cache.o: cache.h +show-diff.o: cache.h + +clean: + rm -f *.o $(PROG) + +backup: clean + cd .. ; tar czvf dircache.tar.gz dir-cache + + +Revision-number: 11 +Prop-content-length: 117 +Content-length: 117 + +K 7 +svn:log +V 14 +right update 1 +K 10 +svn:author +V 8 +tallsopp +K 8 +svn:date +V 27 +2009-11-12T20:29:53.059636Z +PROPS-END + +Node-path: branches/right/Makefile +Node-kind: file +Node-action: change +Text-content-length: 2593 +Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba +Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10 +Content-length: 2593 + +# -DCOLLISION_CHECK if you believe that SHA1's +# 1461501637330902918203684832716283019655932542976 hashes do not give you +# enough guarantees about no collisions between objects ever hapenning. +# +# -DNSEC if you want git to care about sub-second file mtimes and ctimes. +# Note that you need some new glibc (at least >2.2.4) for this, and it will +# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly +# break unless your underlying filesystem supports those sub-second times +# (my ext3 doesn't). +CFLAGS=-g -O3 -Wall + +CC=gcc + + +PROG= update-cache show-diff init-db write-tree read-tree commit-tree \ + cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \ + check-files ls-tree merge-base + +all: $(PROG) + +install: $(PROG) + install $(PROG) $(HOME)/bin/ + +LIBS= -lssl -lz + +init-db: init-db.o + +update-cache: update-cache.o read-cache.o + $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS) + +show-diff: show-diff.o read-cache.o + $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS) + +write-tree: write-tree.o read-cache.o + $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS) + +read-tree: read-tree.o read-cache.o + $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS) + +commit-tree: commit-tree.o read-cache.o + $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS) + +cat-file: cat-file.o read-cache.o + $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS) + +fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o + $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS) + +checkout-cache: checkout-cache.o read-cache.o + $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS) + +diff-tree: diff-tree.o read-cache.o + $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS) + +rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o + $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS) + +show-files: show-files.o read-cache.o + $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS) + +check-files: check-files.o read-cache.o + $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS) + +ls-tree: ls-tree.o read-cache.o + $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS) + +merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o + $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS) + +read-cache.o: cache.h +show-diff.o: cache.h + +clean: + rm -f *.o $(PROG) + +backup: clean + cd .. ; tar czvf dircache.tar.gz dir-cache + + +Revision-number: 12 +Prop-content-length: 116 +Content-length: 116 + +K 7 +svn:log +V 13 +Merge trunk 2 +K 10 +svn:author +V 8 +tallsopp +K 8 +svn:date +V 27 +2009-11-12T20:29:56.083003Z +PROPS-END + +Node-path: trunk +Node-kind: dir +Node-action: change +Prop-content-length: 54 +Content-length: 54 + +K 13 +svn:mergeinfo +V 19 +/branches/left:2-11 +PROPS-END + + -- cgit v1.2.3 From 1a30582b43e137e16b3486d83bb86b0eb090e13d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 14 Nov 2009 14:25:11 -0800 Subject: git svn: read global+system config for clone+init Since $GIT_DIR does not exist when initializing new repositories, we can follow back to the global and system config files for git. The logic for this was originally introduced when $GIT_DIR/config was the only config file git could read (back when "git config" was "git repo-config"), so the function is renamed to "read_git_config" instead of "read_repo_config". Signed-off-by: Eric Wong --- git-svn.perl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 27fbe3063b..ea922ace1d 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -274,7 +274,7 @@ unless ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) { my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); -read_repo_config(\%opts); +read_git_config(\%opts); if ($cmd && ($cmd eq 'log' || $cmd eq 'blame')) { Getopt::Long::Configure('pass_through'); } @@ -1390,8 +1390,7 @@ sub load_authors { } # convert GetOpt::Long specs for use by git-config -sub read_repo_config { - return unless -d $ENV{GIT_DIR}; +sub read_git_config { my $opts = shift; my @config_only; foreach my $o (keys %$opts) { -- cgit v1.2.3 From 6111b934991f3ea670ac2442806c976defc7b61c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 15 Nov 2009 18:57:16 -0800 Subject: git svn: attempt to create empty dirs on clone+rebase We parse unhandled.log files for empty_dir statements and make a best effort attempt to recreate empty directories on fresh clones and rebase. This should cover the majority of cases where users work off a single branch or for projects where branches do not differ in empty directories. Since this cannot affect "normal" git commands like "checkout" or "reset", so users switching between branches in a single working directory should use the new "git svn mkdirs" command after switching branches. Signed-off-by: Eric Wong --- Documentation/git-svn.txt | 7 +++ git-svn.perl | 45 +++++++++++++++++ t/t9115-git-svn-dcommit-funky-renames.sh | 4 +- t/t9146-git-svn-empty-dirs.sh | 85 ++++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 2 deletions(-) create mode 100755 t/t9146-git-svn-empty-dirs.sh (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 1812890a7e..db00ed43b7 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -320,6 +320,13 @@ Any other arguments are passed directly to 'git log' directories. The output is suitable for appending to the $GIT_DIR/info/exclude file. +'mkdirs':: + Attempts to recreate empty directories that core git cannot track + based on information in $GIT_DIR/svn//unhandled.log files. + Empty directories are automatically recreated when using + "git svn clone" and "git svn rebase", so "mkdirs" is intended + for use after commands like "git checkout" or "git reset". + 'commit-diff':: Commits the diff of two tree-ish arguments from the command-line. This command does not rely on being inside an `git svn diff --git a/git-svn.perl b/git-svn.perl index ea922ace1d..ab0a8dd099 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -168,6 +168,9 @@ my %cmd = ( 'Create a .gitignore per svn:ignore', { 'revision|r=i' => \$_revision } ], + 'mkdirs' => [ \&cmd_mkdirs , + "recreate empty directories after a checkout", + { 'revision|r=i' => \$_revision } ], 'propget' => [ \&cmd_propget, 'Print the value of a property on a file or directory', { 'revision|r=i' => \$_revision } ], @@ -769,6 +772,7 @@ sub cmd_rebase { $_fetch_all ? $gs->fetch_all : $gs->fetch; } command_noisy(rebase_cmd(), $gs->refname); + $gs->mkemptydirs; } sub cmd_show_ignore { @@ -830,6 +834,12 @@ sub cmd_create_ignore { }); } +sub cmd_mkdirs { + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); + $gs ||= Git::SVN->new; + $gs->mkemptydirs($_revision); +} + sub canonicalize_path { my ($path) = @_; my $dot_slash_added = 0; @@ -1196,6 +1206,7 @@ sub post_fetch_checkout { command_noisy(qw/read-tree -m -u -v HEAD HEAD/); print STDERR "Checked out HEAD:\n ", $gs->full_url, " r", $gs->last_rev, "\n"; + $gs->mkemptydirs($gs->last_rev); } sub complete_svn_url { @@ -2724,6 +2735,34 @@ sub do_fetch { $self->make_log_entry($rev, \@parents, $ed); } +sub mkemptydirs { + my ($self, $r) = @_; + my %empty_dirs = (); + + open my $fh, '<', "$self->{dir}/unhandled.log" or return; + binmode $fh or croak "binmode: $!"; + while (<$fh>) { + if (defined $r && /^r(\d+)$/) { + last if $1 > $r; + } elsif (/^ \+empty_dir: (.+)$/) { + $empty_dirs{$1} = 1; + } elsif (/^ \-empty_dir: (.+)$/) { + delete $empty_dirs{$1}; + } + } + close $fh; + foreach my $d (sort keys %empty_dirs) { + $d = uri_decode($d); + next if -d $d; + if (-e _) { + warn "$d exists but is not a directory\n"; + } else { + print "creating empty directory: $d\n"; + mkpath([$d]); + } + } +} + sub get_untracked { my ($self, $ed) = @_; my @out; @@ -3556,6 +3595,12 @@ sub uri_encode { $f } +sub uri_decode { + my ($f) = @_; + $f =~ s#%([0-9a-fA-F]{2})#chr(hex($1))#eg; + $f +} + sub remove_username { $_[0] =~ s{^([^:]*://)[^@]+@}{$1}; } diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh index 9be7aefaee..767799e7a7 100755 --- a/t/t9115-git-svn-dcommit-funky-renames.sh +++ b/t/t9115-git-svn-dcommit-funky-renames.sh @@ -19,7 +19,7 @@ test_expect_success 'init and fetch repository' ' ' test_expect_success 'create file in existing ugly and empty dir' ' - mkdir "#{bad_directory_name}" && + mkdir -p "#{bad_directory_name}" && echo hi > "#{bad_directory_name}/ foo" && git update-index --add "#{bad_directory_name}/ foo" && git commit -m "new file in ugly parent" && @@ -37,7 +37,7 @@ test_expect_success 'rename pretty file' ' git update-index --add pretty && git commit -m "pretty :x" && git svn dcommit && - mkdir regular_dir_name && + mkdir -p regular_dir_name && git mv pretty regular_dir_name/pretty && git commit -m "moved pretty file" && git svn dcommit diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh new file mode 100755 index 0000000000..5948544ec5 --- /dev/null +++ b/t/t9146-git-svn-empty-dirs.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong + +test_description='git svn creates empty directories' +. ./lib-git-svn.sh + +test_expect_success 'initialize repo' ' + for i in a b c d d/e d/e/f "weird file name" + do + svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" + done +' + +test_expect_success 'clone' 'git svn clone "$svnrepo" cloned' + +test_expect_success 'empty directories exist' ' + ( + cd cloned && + for i in a b c d d/e d/e/f "weird file name" + do + if ! test -d "$i" + then + echo >&2 "$i does not exist" + exit 1 + fi + done + ) +' + +test_expect_success 'more emptiness' ' + svn_cmd mkdir -m "bang bang" "$svnrepo"/"! !" +' + +test_expect_success 'git svn rebase creates empty directory' ' + ( cd cloned && git svn rebase ) + test -d cloned/"! !" +' + +test_expect_success 'git svn mkdirs recreates empty directories' ' + ( + cd cloned && + rm -r * && + git svn mkdirs && + for i in a b c d d/e d/e/f "weird file name" "! !" + do + if ! test -d "$i" + then + echo >&2 "$i does not exist" + exit 1 + fi + done + ) +' + +test_expect_success 'git svn mkdirs -r works' ' + ( + cd cloned && + rm -r * && + git svn mkdirs -r7 && + for i in a b c d d/e d/e/f "weird file name" + do + if ! test -d "$i" + then + echo >&2 "$i does not exist" + exit 1 + fi + done + + if test -d "! !" + then + echo >&2 "$i should not exist" + exit 1 + fi + + git svn mkdirs -r8 && + if ! test -d "! !" + then + echo >&2 "$i not exist" + exit 1 + fi + ) +' + +test_done -- cgit v1.2.3 From 4d0157d6995925ea55ff181ea94d058583333f90 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 22 Nov 2009 12:37:06 -0800 Subject: git svn: always reuse existing remotes on fetch The internal no_reuse_existing flag is set to allow initializing multiple remotes with the same URL, common with SVM users. Unfortunately, this flag caused misbehavior when used with the -R command-line flag for fetching. Signed-off-by: Eric Wong --- git-svn.perl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 2746895ae6..7f7a56fbea 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -428,6 +428,7 @@ sub cmd_fetch { if (@_ > 1) { die "Usage: $0 fetch [--all] [--parent] [svn-remote]\n"; } + $Git::SVN::no_reuse_existing = undef; if ($_fetch_parent) { my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); unless ($gs) { @@ -956,6 +957,7 @@ sub cmd_multi_init { } sub cmd_multi_fetch { + $Git::SVN::no_reuse_existing = undef; my $remotes = Git::SVN::read_all_remotes(); foreach my $repo_id (sort keys %$remotes) { if ($remotes->{$repo_id}->{url}) { -- cgit v1.2.3 From 9be30eed61993a6f2d04a1609723e64e7632a64e Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 22 Nov 2009 18:11:32 -0800 Subject: git svn: strip leading path when making empty dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since unhandled.log stores paths relative to the repository root, we need to strip out leading path components if the directories we're tracking are not the repository root. Reported-by: Björn Steinbrink Signed-off-by: Eric Wong --- git-svn.perl | 3 +++ t/t9146-git-svn-empty-dirs.sh | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 7f7a56fbea..957d44e630 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2752,8 +2752,11 @@ sub mkemptydirs { } } close $fh; + + my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/; foreach my $d (sort keys %empty_dirs) { $d = uri_decode($d); + $d =~ s/$strip//; next if -d $d; if (-e _) { warn "$d exists but is not a directory\n"; diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh index 5948544ec5..70c52c1f97 100755 --- a/t/t9146-git-svn-empty-dirs.sh +++ b/t/t9146-git-svn-empty-dirs.sh @@ -82,4 +82,27 @@ test_expect_success 'git svn mkdirs -r works' ' ) ' +test_expect_success 'initialize trunk' ' + for i in trunk trunk/a trunk/"weird file name" + do + svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" + done +' + +test_expect_success 'clone trunk' 'git svn clone -s "$svnrepo" trunk' + +test_expect_success 'empty directories in trunk exist' ' + ( + cd trunk && + for i in a "weird file name" + do + if ! test -d "$i" + then + echo >&2 "$i does not exist" + exit 1 + fi + done + ) +' + test_done -- cgit v1.2.3 From e9e4c8b7473f6ee21f452c66430a0763681243d7 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Sun, 29 Nov 2009 02:20:21 -0500 Subject: git-svn: sort svk merge tickets to account for minimal parents When merging branches based on svk:merge properties, a single merge can have updated or added multiple svk:merge lines. Attempt to include the minimal set of parents by sorting the merge properties in order of revision, highest to lowest. Signed-off-by: Alex Vandiver Acked-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 957d44e630..51f03adb9b 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2940,10 +2940,14 @@ sub find_extra_svk_parents { if ( my $commit = $gs->rev_map_get($rev, $uuid) ) { # wahey! we found it, but it might be # an old one (!) - push @known_parents, $commit; + push @known_parents, [ $rev, $commit ]; } } } + # Ordering matters; highest-numbered commit merge tickets + # first, as they may account for later merge ticket additions + # or changes. + @known_parents = map {$_->[1]} sort {$b->[0] <=> $a->[0]} @known_parents; for my $parent ( @known_parents ) { my @cmd = ('rev-list', $parent, map { "^$_" } @$parents ); my ($msg_fh, $ctx) = command_output_pipe(@cmd); -- cgit v1.2.3 From 33f2a3108eeacbd937021470aa6e67c01ceec960 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 7 Dec 2009 22:28:32 -0500 Subject: git svn: Don't create empty directories whose parents were deleted Commit 6111b93 "git svn: attempt to create empty dirs on clone+rebase" will create empty directories 'a/b' and 'a/c' if they were previously created in SVN, even if their parent directory 'a' was deleted. For example, unhandled.log may contain lines like this: r32 +empty_dir: packages/sipb-xen-remctl-auto/sipb-xen-remctl-auto/files/etc/remctl/sipb-xen-auto/acl +empty_dir: packages/sipb-xen-remctl-auto/sipb-xen-remctl-auto/files/etc/remctl/sipb-xen-auto/machine.d +empty_dir: packages/sipb-xen-remctl-auto/sipb-xen-remctl-auto/files/etc/remctl/sipb-xen-auto/moira-acl [...] r314 -empty_dir: packages/sipb-xen-remctl-auto [ew: rewrote to be line-wrapped at <= 80-columns] Reported-by: Evan Broder Signed-off-by: Greg Price Acked-by: Eric Wong --- git-svn.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 51f03adb9b..bdd1f966c3 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2748,7 +2748,8 @@ sub mkemptydirs { } elsif (/^ \+empty_dir: (.+)$/) { $empty_dirs{$1} = 1; } elsif (/^ \-empty_dir: (.+)$/) { - delete $empty_dirs{$1}; + my @d = grep {m[^\Q$1\E(/|$)]} (keys %empty_dirs); + delete @empty_dirs{@d}; } } close $fh; -- cgit v1.2.3 From f9ad77a739c0d012ee58b64eda2d7ec0d4e1df9d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 7 Dec 2009 20:49:38 -0800 Subject: git svn: log removals of empty directories This also adds a test case for: "git svn: Don't create empty directories whose parents were deleted" which was the reason we found this bug in the first place. Signed-off-by: Eric Wong --- git-svn.perl | 2 +- t/t9146-git-svn-empty-dirs.sh | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index bdd1f966c3..5a52068b12 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3891,11 +3891,11 @@ sub delete_entry { } print "\tD\t$gpath/\n" unless $::_q; command_close_pipe($ls, $ctx); - $self->{empty}->{$path} = 0 } else { $self->{gii}->remove($gpath); print "\tD\t$gpath\n" unless $::_q; } + $self->{empty}->{$path} = 0; undef; } diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh index 70c52c1f97..9b8d0463fa 100755 --- a/t/t9146-git-svn-empty-dirs.sh +++ b/t/t9146-git-svn-empty-dirs.sh @@ -105,4 +105,14 @@ test_expect_success 'empty directories in trunk exist' ' ) ' +test_expect_success 'remove a top-level directory from svn' ' + svn_cmd rm -m "remove d" "$svnrepo"/d +' + +test_expect_success 'removed top-level directory does not exist' ' + git svn clone "$svnrepo" removed && + test ! -e removed/d + +' + test_done -- cgit v1.2.3 From 2bc35dcbf7ed702e92b925f8e50afe3632dd211b Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 8 Dec 2009 15:54:10 -0500 Subject: git-svn: Set svn.authorsfile to an absolute path when cloning If --authors-file is passed a relative path, cloning will work, but future `git svn fetch`es will fail to locate the authors file correctly. Thus, use File::Spec->rel2abs to determine an absolute path for the authors file before setting it. Signed-off-by: Alex Vandiver Acked-by: Eric Wong --- git-svn.perl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 5a52068b12..dc0e711593 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -392,9 +392,11 @@ sub cmd_clone { $path = $url; } $path = basename($url) if !defined $path || !length $path; + my $authors_absolute = $_authors ? File::Spec->rel2abs($_authors) : ""; cmd_init($url, $path); Git::SVN::fetch_all($Git::SVN::default_repo_id); - command_oneline('config', 'svn.authorsfile', $_authors) if $_authors; + command_oneline('config', 'svn.authorsfile', $authors_absolute) + if $_authors; } sub cmd_init { -- cgit v1.2.3 From f58415096632946b723fa73216ec1d49b4c6bb55 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 8 Dec 2009 15:54:11 -0500 Subject: git-svn: set svn.authorsfile earlier when cloning If a clone errors out because of a missing author, or user interrupt, this allows `git svn fetch` to resume seamlessly, rather than forcing the user to re-provide the path to the authors file. [ew: shortened subject] Signed-off-by: Alex Vandiver Acked-by: Eric Wong --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index dc0e711593..a4b052c71e 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -394,9 +394,9 @@ sub cmd_clone { $path = basename($url) if !defined $path || !length $path; my $authors_absolute = $_authors ? File::Spec->rel2abs($_authors) : ""; cmd_init($url, $path); - Git::SVN::fetch_all($Git::SVN::default_repo_id); command_oneline('config', 'svn.authorsfile', $authors_absolute) if $_authors; + Git::SVN::fetch_all($Git::SVN::default_repo_id); } sub cmd_init { -- cgit v1.2.3 From a5b80d92632a2d76325d4b6e6161022171a9fee4 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 19 Dec 2009 13:49:00 -0800 Subject: git svn: make empty directory creation gc-aware The "git svn gc" command creates and appends to unhandled.log.gz files which should be parsed before the uncompressed unhandled.log files. Reported-by: Robert Zeh Signed-off-by: Eric Wong --- git-svn.perl | 45 ++++++++++++++++++++++++++++++++----------- t/t9146-git-svn-empty-dirs.sh | 24 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 11 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index a4b052c71e..d362de7364 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2740,21 +2740,44 @@ sub do_fetch { sub mkemptydirs { my ($self, $r) = @_; + + sub scan { + my ($r, $empty_dirs, $line) = @_; + if (defined $r && $line =~ /^r(\d+)$/) { + return 0 if $1 > $r; + } elsif ($line =~ /^ \+empty_dir: (.+)$/) { + $empty_dirs->{$1} = 1; + } elsif ($line =~ /^ \-empty_dir: (.+)$/) { + my @d = grep {m[^\Q$1\E(/|$)]} (keys %$empty_dirs); + delete @$empty_dirs{@d}; + } + 1; # continue + }; + my %empty_dirs = (); + my $gz_file = "$self->{dir}/unhandled.log.gz"; + if (-f $gz_file) { + if (!$can_compress) { + warn "Compress::Zlib could not be found; ", + "empty directories in $gz_file will not be read\n"; + } else { + my $gz = Compress::Zlib::gzopen($gz_file, "rb") or + die "Unable to open $gz_file: $!\n"; + my $line; + while ($gz->gzreadline($line) > 0) { + scan($r, \%empty_dirs, $line) or last; + } + $gz->gzclose; + } + } - open my $fh, '<', "$self->{dir}/unhandled.log" or return; - binmode $fh or croak "binmode: $!"; - while (<$fh>) { - if (defined $r && /^r(\d+)$/) { - last if $1 > $r; - } elsif (/^ \+empty_dir: (.+)$/) { - $empty_dirs{$1} = 1; - } elsif (/^ \-empty_dir: (.+)$/) { - my @d = grep {m[^\Q$1\E(/|$)]} (keys %empty_dirs); - delete @empty_dirs{@d}; + if (open my $fh, '<', "$self->{dir}/unhandled.log") { + binmode $fh or croak "binmode: $!"; + while (<$fh>) { + scan($r, \%empty_dirs, $_) or last; } + close $fh; } - close $fh; my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/; foreach my $d (sort keys %empty_dirs) { diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh index 9b8d0463fa..3f2d719827 100755 --- a/t/t9146-git-svn-empty-dirs.sh +++ b/t/t9146-git-svn-empty-dirs.sh @@ -114,5 +114,29 @@ test_expect_success 'removed top-level directory does not exist' ' test ! -e removed/d ' +unhandled=.git/svn/refs/remotes/git-svn/unhandled.log +test_expect_success 'git svn gc-ed files work' ' + ( + cd removed && + git svn gc && + : Compress::Zlib may not be available && + if test -f "$unhandled".gz + then + svn mkdir -m gz "$svnrepo"/gz && + git reset --hard $(git rev-list HEAD | tail -1) && + git svn rebase && + test -f "$unhandled".gz && + test -f "$unhandled" && + for i in a b c "weird file name" gz "! !" + do + if ! test -d "$i" + then + echo >&2 "$i does not exist" + exit 1 + fi + done + fi + ) +' test_done -- cgit v1.2.3 From 577e9fcad2c8968846b365226b89778050496a78 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 21 Dec 2009 02:06:04 -0800 Subject: git svn: fix --revision when fetching deleted paths When using the -r/--revision argument to fetch deleted history, calling SVN::Ra::get_log() from an SVN::Ra object initialized to track the deleted URL will fail. This regression was introduced in: commit 4aacaeb3dc82bb6479e70e120053dc27a399460e "fix shallow clone when upstream revision is too new" We now ignore errors from SVN::Ra::get_log() here because using --revision will always override the value of $head here if (and only if) we're tracking deleted directories. Signed-off-by: Eric Wong --- git-svn.perl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index d362de7364..a6f5061c3c 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1741,7 +1741,11 @@ sub fetch_all { my $ra = Git::SVN::Ra->new($url); my $uuid = $ra->get_uuid; my $head = $ra->get_latest_revnum; - $ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] }); + + # ignore errors, $head revision may not even exist anymore + eval { $ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] }) }; + warn "W: $@\n" if $@; + my $base = defined $fetch ? $head : 0; # read the max revs for wildcard expansion (branches/*, tags/*) -- cgit v1.2.3 From 7d944c3399c4145293d599e1ddc76d1ac0c1e118 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Sun, 20 Dec 2009 00:55:13 +1300 Subject: git-svn: memoize conversion of SVN merge ticket info to git commit ranges Each time the svn mergeinfo ticket changes, we look it up in the rev_map; when there are a lot of merged branches, this will result in many repeated lookups of the same information for subsequent commits. Arrange the slow part of the function so that it may be memoized, and memoize it. The more expensive revision walking operation can be memoized separately. [ew: changed "next" to "return" for function exit] Signed-off-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 91 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 37 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index a6f5061c3c..1e106f064b 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1634,6 +1634,7 @@ use Carp qw/croak/; use File::Path qw/mkpath/; use File::Copy qw/copy/; use IPC::Open3; +use Memoize; # core since 5.8.0, Jul 2002 my ($_gc_nr, $_gc_period); @@ -2994,6 +2995,55 @@ sub find_extra_svk_parents { } } +sub lookup_svn_merge { + my $uuid = shift; + my $url = shift; + my $merge = shift; + + my ($source, $revs) = split ":", $merge; + my $path = $source; + $path =~ s{^/}{}; + my $gs = Git::SVN->find_by_url($url.$source, $url, $path); + if ( !$gs ) { + warn "Couldn't find revmap for $url$source\n"; + return; + } + my @ranges = split ",", $revs; + my ($tip, $tip_commit); + my @merged_commit_ranges; + # find the tip + for my $range ( @ranges ) { + my ($bottom, $top) = split "-", $range; + $top ||= $bottom; + my $bottom_commit = + $gs->rev_map_get($bottom, $uuid) || + $gs->rev_map_get($bottom+1, $uuid); + my $top_commit; + for (; !$top_commit && $top >= $bottom; --$top) { + $top_commit = + $gs->rev_map_get($top, $uuid); + } + + unless ($top_commit and $bottom_commit) { + warn "W:unknown path/rev in svn:mergeinfo " + ."dirprop: $source:$range\n"; + next; + } + + push @merged_commit_ranges, + "$bottom_commit..$top_commit"; + + if ( !defined $tip or $top > $tip ) { + $tip = $top; + $tip_commit = $top_commit; + } + } + return ($tip_commit, @merged_commit_ranges); +} +BEGIN { + memoize 'lookup_svn_merge'; +} + # note: this function should only be called if the various dirprops # have actually changed sub find_extra_svn_parents { @@ -3008,44 +3058,11 @@ sub find_extra_svn_parents { my @merge_tips; my @merged_commit_ranges; my $url = $self->rewrite_root || $self->{url}; + my $uuid = $self->ra_uuid; for my $merge ( @merges ) { - my ($source, $revs) = split ":", $merge; - my $path = $source; - $path =~ s{^/}{}; - my $gs = Git::SVN->find_by_url($url.$source, $url, $path); - if ( !$gs ) { - warn "Couldn't find revmap for $url$source\n"; - next; - } - my @ranges = split ",", $revs; - my ($tip, $tip_commit); - # find the tip - for my $range ( @ranges ) { - my ($bottom, $top) = split "-", $range; - $top ||= $bottom; - my $bottom_commit = - $gs->rev_map_get($bottom, $self->ra_uuid) || - $gs->rev_map_get($bottom+1, $self->ra_uuid); - my $top_commit; - for (; !$top_commit && $top >= $bottom; --$top) { - $top_commit = - $gs->rev_map_get($top, $self->ra_uuid); - } - - unless ($top_commit and $bottom_commit) { - warn "W:unknown path/rev in svn:mergeinfo " - ."dirprop: $source:$range\n"; - next; - } - - push @merged_commit_ranges, - "$bottom_commit..$top_commit"; - - if ( !defined $tip or $top > $tip ) { - $tip = $top; - $tip_commit = $top_commit; - } - } + my ($tip_commit, @ranges) = + lookup_svn_merge( $uuid, $url, $merge ); + push @merged_commit_ranges, @ranges; unless (!$tip_commit or grep { $_ eq $tip_commit } @$parents ) { push @merge_tips, $tip_commit; -- cgit v1.2.3 From 33973a5b179278c563624fcb45c223f03031b242 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Sun, 20 Dec 2009 05:22:42 +1300 Subject: git-svn: fix some mistakes with interpreting SVN mergeinfo commit ranges SVN's list of commit ranges in mergeinfo tickets is inclusive, whereas git commit ranges are exclusive on the left hand side. Also, the end points of the commit ranges may not exist; they simply delineate ranges of commits which may or may not exist. Fix these two mistakes. Signed-off-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 12 +++--------- t/t9151-svn-mergeinfo.sh | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 1e106f064b..a4a45ef398 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3015,14 +3015,8 @@ sub lookup_svn_merge { for my $range ( @ranges ) { my ($bottom, $top) = split "-", $range; $top ||= $bottom; - my $bottom_commit = - $gs->rev_map_get($bottom, $uuid) || - $gs->rev_map_get($bottom+1, $uuid); - my $top_commit; - for (; !$top_commit && $top >= $bottom; --$top) { - $top_commit = - $gs->rev_map_get($top, $uuid); - } + my $bottom_commit = $gs->find_rev_after( $bottom, 1, $top ); + my $top_commit = $gs->find_rev_before( $top, 1, $bottom ); unless ($top_commit and $bottom_commit) { warn "W:unknown path/rev in svn:mergeinfo " @@ -3031,7 +3025,7 @@ sub lookup_svn_merge { } push @merged_commit_ranges, - "$bottom_commit..$top_commit"; + "$bottom_commit^..$top_commit"; if ( !defined $tip or $top > $tip ) { $tip = $top; diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh index dc3478fbbc..f6e00ea30b 100755 --- a/t/t9151-svn-mergeinfo.sh +++ b/t/t9151-svn-mergeinfo.sh @@ -33,7 +33,7 @@ test_expect_success 'svn non-merge merge commits did not become git merge commit [ -z "$bad_non_merges" ] ' -test_expect_failure 'everything got merged in the end' ' +test_expect_success 'everything got merged in the end' ' unmerged=$(git rev-list --all --not master) [ -z "$unmerged" ] ' -- cgit v1.2.3 From ea020cbd6aeb769d95e5c2dbffee4c81d6f92796 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Sun, 20 Dec 2009 05:25:31 +1300 Subject: git-svn: exclude already merged tips using one rev-list call The old function would have to check all mentioned merge tips, every time that the mergeinfo ticket changed. This involved 1-2 rev-list operation for each listed mergeinfo line. If there are a lot of feature branches being merged into a trunk, this makes for a very expensive operation for detecting the new parents on every merge. This new version first uses a single 'rev-list' to figure out which commit ranges are already reachable from the parents. This is used to eliminate the already merged branches from the list. Signed-off-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index a4a45ef398..07d40ba81f 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3038,6 +3038,41 @@ BEGIN { memoize 'lookup_svn_merge'; } +sub parents_exclude { + my $parents = shift; + my @commits = @_; + return unless @commits; + + my @excluded; + my $excluded; + do { + my @cmd = ('rev-list', "-1", @commits, "--not", @$parents ); + $excluded = command_oneline(@cmd); + if ( $excluded ) { + my @new; + my $found; + for my $commit ( @commits ) { + if ( $commit eq $excluded ) { + push @excluded, $commit; + $found++; + last; + } + else { + push @new, $commit; + } + } + die "saw commit '$excluded' in rev-list output, " + ."but we didn't ask for that commit (wanted: @commits --not @$parents)" + unless $found; + @commits = @new; + } + } + while ($excluded and @commits); + + return @excluded; +} + + # note: this function should only be called if the various dirprops # have actually changed sub find_extra_svn_parents { @@ -3050,23 +3085,32 @@ sub find_extra_svn_parents { # are now marked as merge, we can add the tip as a parent. my @merges = split "\n", $mergeinfo; my @merge_tips; - my @merged_commit_ranges; my $url = $self->rewrite_root || $self->{url}; my $uuid = $self->ra_uuid; + my %ranges; for my $merge ( @merges ) { my ($tip_commit, @ranges) = lookup_svn_merge( $uuid, $url, $merge ); - push @merged_commit_ranges, @ranges; unless (!$tip_commit or grep { $_ eq $tip_commit } @$parents ) { push @merge_tips, $tip_commit; + $ranges{$tip_commit} = \@ranges; } else { push @merge_tips, undef; } } + + my %excluded = map { $_ => 1 } + parents_exclude($parents, grep { defined } @merge_tips); + + # check merge tips for new parents + my @new_parents; for my $merge_tip ( @merge_tips ) { my $spec = shift @merges; - next unless $merge_tip; + next unless $merge_tip and $excluded{$merge_tip}; + + my $ranges = $ranges{$merge_tip}; + my @cmd = ('rev-list', "-1", $merge_tip, "--not", @$parents ); my ($msg_fh, $ctx) = command_output_pipe(@cmd); @@ -3076,7 +3120,7 @@ sub find_extra_svn_parents { } command_close_pipe($msg_fh, $ctx); if ( $new ) { - push @cmd, @merged_commit_ranges; + push @cmd, @$ranges; my ($msg_fh, $ctx) = command_output_pipe(@cmd); my $unmerged; while ( <$msg_fh> ) { -- cgit v1.2.3 From 7a955a5365d9ebd5e12c12ed926b2b51b61c02ee Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Sun, 20 Dec 2009 05:26:26 +1300 Subject: git-svn: detect cherry-picks correctly. The old function was incorrect; in some instances it marks a cherry picked range as a merged branch (because of an incorrect assumption that 'rev-list COMMIT --not RANGE' would work). This is replaced with a function which should detect them correctly, memoized to limit the expense of dealing with branches with many cherry picks to one 'merge-base' call per merge, per branch which used cherry picking. Signed-off-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 87 +++++++++++++++++++++++++++++++++++------------- t/t9151-svn-mergeinfo.sh | 4 +-- 2 files changed, 66 insertions(+), 25 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 07d40ba81f..4ea3ac63da 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3034,8 +3034,35 @@ sub lookup_svn_merge { } return ($tip_commit, @merged_commit_ranges); } + +sub _rev_list { + my ($msg_fh, $ctx) = command_output_pipe( + "rev-list", @_, + ); + my @rv; + while ( <$msg_fh> ) { + chomp; + push @rv, $_; + } + command_close_pipe($msg_fh, $ctx); + @rv; +} + +sub check_cherry_pick { + my $base = shift; + my $tip = shift; + my @ranges = @_; + my %commits = map { $_ => 1 } + _rev_list("--no-merges", $tip, "--not", $base); + for my $range ( @ranges ) { + delete @commits{_rev_list($range)}; + } + return (keys %commits); +} + BEGIN { memoize 'lookup_svn_merge'; + memoize 'check_cherry_pick'; } sub parents_exclude { @@ -3111,32 +3138,46 @@ sub find_extra_svn_parents { my $ranges = $ranges{$merge_tip}; - my @cmd = ('rev-list', "-1", $merge_tip, - "--not", @$parents ); - my ($msg_fh, $ctx) = command_output_pipe(@cmd); - my $new; - while ( <$msg_fh> ) { - $new=1;last; - } - command_close_pipe($msg_fh, $ctx); - if ( $new ) { - push @cmd, @$ranges; - my ($msg_fh, $ctx) = command_output_pipe(@cmd); - my $unmerged; - while ( <$msg_fh> ) { - $unmerged=1;last; - } - command_close_pipe($msg_fh, $ctx); - if ( $unmerged ) { - warn "W:svn cherry-pick ignored ($spec)\n"; - } else { - warn - "Found merge parent (svn:mergeinfo prop): ", - $merge_tip, "\n"; - push @$parents, $merge_tip; + # check out 'new' tips + my $merge_base = command_oneline( + "merge-base", + @$parents, $merge_tip, + ); + + # double check that there are no missing non-merge commits + my (@incomplete) = check_cherry_pick( + $merge_base, $merge_tip, + @$ranges, + ); + + if ( @incomplete ) { + warn "W:svn cherry-pick ignored ($spec) - missing " + .@incomplete." commit(s) (eg $incomplete[0])\n"; + } else { + warn + "Found merge parent (svn:mergeinfo prop): ", + $merge_tip, "\n"; + push @new_parents, $merge_tip; + } + } + + # cater for merges which merge commits from multiple branches + if ( @new_parents > 1 ) { + for ( my $i = 0; $i <= $#new_parents; $i++ ) { + for ( my $j = 0; $j <= $#new_parents; $j++ ) { + next if $i == $j; + next unless $new_parents[$i]; + next unless $new_parents[$j]; + my $revs = command_oneline( + "rev-list", "-1", "$i..$j", + ); + if ( !$revs ) { + undef($new_parents[$i]); + } } } } + push @$parents, grep { defined } @new_parents; } sub make_log_entry { diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh index f6e00ea30b..359eeaa738 100755 --- a/t/t9151-svn-mergeinfo.sh +++ b/t/t9151-svn-mergeinfo.sh @@ -15,13 +15,13 @@ test_expect_success 'load svn dump' " git svn fetch --all " -test_expect_failure 'all svn merges became git merge commits' ' +test_expect_success 'all svn merges became git merge commits' ' unmarked=$(git rev-list --parents --all --grep=Merge | grep -v " .* " | cut -f1 -d" ") [ -z "$unmarked" ] ' -test_expect_failure 'cherry picks did not become git merge commits' ' +test_expect_success 'cherry picks did not become git merge commits' ' bad_cherries=$(git rev-list --parents --all --grep=Cherry | grep " .* " | cut -f1 -d" ") [ -z "$bad_cherries" ] -- cgit v1.2.3 From 063681d72e60e80cd14576037728b395ed42e330 Mon Sep 17 00:00:00 2001 From: Andrew Myrick Date: Mon, 21 Dec 2009 14:22:54 -0800 Subject: git-svn: Remove obsolete MAXPARENT check Change git-svn not to impose a limit of 16 parents on a merge. This limit in git-svn artificially prevents cloning svn repositories that contain commits with more than 16 merge parents. The limit was removed from builtin-commit-tree.c for git v1.6.0 in commit ef98c5cafb3e799b1568bb843fcd45920dc62f16, so there is no need to check for it it in git-svn. Signed-off-by: Andrew Myrick Acked-by: Eric Wong --- git-svn.perl | 6 ------ 1 file changed, 6 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 4ea3ac63da..36709607f2 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2451,12 +2451,6 @@ sub get_commit_parents { next if $seen{$p}; $seen{$p} = 1; push @ret, $p; - # MAXPARENT is defined to 16 in commit-tree.c: - last if @ret >= 16; - } - if (@tmp) { - die "r$log_entry->{revision}: No room for parents:\n\t", - join("\n\t", @tmp), "\n"; } @ret; } -- cgit v1.2.3 From 0fe19753f26e2747f48d1ec6a1761006d75c2d71 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 22 Dec 2009 12:15:40 -0800 Subject: git svn: lookup new parents correctly from svn:mergeinfo This appears to be a trivial case where array indices were being passed to git rev-list, instead of the contents stored in the array itself. Signed-off-by: Eric Wong --- git-svn.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 36709607f2..dba0d12b00 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3163,7 +3163,8 @@ sub find_extra_svn_parents { next unless $new_parents[$i]; next unless $new_parents[$j]; my $revs = command_oneline( - "rev-list", "-1", "$i..$j", + "rev-list", "-1", + "$new_parents[$i]..$new_parents[$j]", ); if ( !$revs ) { undef($new_parents[$i]); -- cgit v1.2.3 From 150d38c4f3733b38c2c212469afa162a55e0e99d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 22 Dec 2009 22:40:18 -0800 Subject: git svn: branch/tag commands detect username in URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit svn+ssh:// repositories often have userinfo embedded in the URL which were stripped out of the "git-svn-id:" trailers. Since the SVN::Client::copy function takes userinfo into account when matching URLs for SVN repositories, we need to retrieve the full URL with embedded userinfo in it to avoid mismatched URLs. Tested-by: Florian Köberle Signed-off-by: Eric Wong --- git-svn.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index dba0d12b00..650c9e5f02 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -663,7 +663,8 @@ sub cmd_branch { } $head ||= 'HEAD'; - my ($src, $rev, undef, $gs) = working_head_info($head); + my (undef, $rev, undef, $gs) = working_head_info($head); + my $src = $gs->full_url; my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}}; my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' }; -- cgit v1.2.3 From 1cef6500a9edc7997bd0aa4cd1f739f5106ab0a9 Mon Sep 17 00:00:00 2001 From: Andrew Myrick Date: Wed, 6 Jan 2010 16:25:21 -0800 Subject: git-svn: ignore changeless commits when checking for a cherry-pick Update git-svn to ignore commits that do not change the tree when it is deciding if an svn merge ticket represents a real branch merge or just a cherry-pick. Consider the following integration model in the svn repository: F---G branch1 / \ D tag1 \ E tag2 / \ / A---B C trunk branch1 is merged to trunk in commit C. With this patch, git-svn will correctly identify branch1 as a proper merge parent, instead of incorrectly ignoring it as a cherry-pick. Signed-off-by: Andrew Myrick Acked-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 650c9e5f02..947184afcf 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3052,12 +3052,36 @@ sub check_cherry_pick { for my $range ( @ranges ) { delete @commits{_rev_list($range)}; } + for my $commit (keys %commits) { + if (has_no_changes($commit)) { + delete $commits{$commit}; + } + } return (keys %commits); } +sub has_no_changes { + my $commit = shift; + + my @revs = split / /, command_oneline( + qw(rev-list --parents -1 -m), $commit); + + # Commits with no parents, e.g. the start of a partial branch, + # have changes by definition. + return 1 if (@revs < 2); + + # Commits with multiple parents, e.g a merge, have no changes + # by definition. + return 0 if (@revs > 2); + + return (command_oneline("rev-parse", "$commit^{tree}") eq + command_oneline("rev-parse", "$commit~1^{tree}")); +} + BEGIN { memoize 'lookup_svn_merge'; memoize 'check_cherry_pick'; + memoize 'has_no_changes'; } sub parents_exclude { -- cgit v1.2.3 From 41c01693ac13846c73a31c8f5c3a60206e1643be Mon Sep 17 00:00:00 2001 From: Andrew Myrick Date: Wed, 6 Jan 2010 16:25:22 -0800 Subject: git-svn: handle merge-base failures Change git-svn to warn and continue when merge-base fails while processing svn merge tickets. merge-base can fail when a partial branch is created and merged back to trunk in svn, because it cannot find a common ancestor between the partial branch and trunk. Signed-off-by: Andrew Myrick Acked-by: Sam Vilain Acked-by: Eric Wong --- git-svn.perl | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 947184afcf..e0773eff4e 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3158,10 +3158,21 @@ sub find_extra_svn_parents { my $ranges = $ranges{$merge_tip}; # check out 'new' tips - my $merge_base = command_oneline( - "merge-base", - @$parents, $merge_tip, - ); + my $merge_base; + eval { + $merge_base = command_oneline( + "merge-base", + @$parents, $merge_tip, + ); + }; + if ($@) { + die "An error occurred during merge-base" + unless $@->isa("Git::Error::Command"); + + warn "W: Cannot find common ancestor between ". + "@$parents and $merge_tip. Ignoring merge info.\n"; + next; + } # double check that there are no missing non-merge commits my (@incomplete) = check_cherry_pick( -- cgit v1.2.3 From a83b91e7246fed3df5686c29a3c64eed8670fd98 Mon Sep 17 00:00:00 2001 From: Igor Mironov Date: Tue, 12 Jan 2010 03:20:43 +1100 Subject: git-svn: fix mismatched src/dst errors for branch/tag This fixes the following issue: $ git svn branch -t --username=svnuser \ --commit-url=https://myproj.domain.com/svn mytag Copying http://myproj.domain.com/svn/trunk at r26 to https://myproj.domain.com/svn/tags/mytag... Trying to use an unsupported feature: Source and dest appear not to be in the same repository (src: 'http://myproj.domain.com/svn/trunk'; dst: 'https://myproj.domain.com/svn/tags/mytag') [ew: shortened subject] Signed-off-by: Igor Mironov Acked-by: Eric Wong --- git-svn.perl | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index e0773eff4e..e05bf366cc 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -710,6 +710,10 @@ sub cmd_branch { my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/}; my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ()); + if ($dst =~ /^https:/ && $src =~ /^http:/) { + $src=~s/^http:/https:/; + } + my $ctx = SVN::Client->new( auth => Git::SVN::Ra::_auth_providers(), log_msg => sub { -- cgit v1.2.3 From 99bacd6c25d30766921468d09323b2c34cf62cb8 Mon Sep 17 00:00:00 2001 From: Igor Mironov Date: Tue, 12 Jan 2010 03:21:23 +1100 Subject: git-svn: respect commiturl option for branch/tag When constructing a destination URL, use the property 'commiturl' if it is specified in the configuration file; otherwise take 'url' as usual. This accommodates the scenario where a user only wants to involve the writable repository in operations performing a commit and defaults everything else to a read-only URL. [ew: shortened subject] Signed-off-by: Igor Mironov Acked-by: Eric Wong --- git-svn.perl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index e05bf366cc..2e14b22c89 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -708,7 +708,17 @@ sub cmd_branch { } } my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/}; - my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ()); + my $url; + if (defined $_commit_url) { + $url = $_commit_url; + } else { + $url = eval { command_oneline('config', '--get', + "svn-remote.$gs->{repo_id}.commiturl") }; + if (!$url) { + $url = $remote->{url}; + } + } + my $dst = join '/', $url, $lft, $branch_name, ($rgt || ()); if ($dst =~ /^https:/ && $src =~ /^http:/) { $src=~s/^http:/https:/; -- cgit v1.2.3 From 6594f0b793507a1e00f79c9884b8911c6d047c1c Mon Sep 17 00:00:00 2001 From: Igor Mironov Date: Tue, 12 Jan 2010 03:21:51 +1100 Subject: git-svn: add --username/commit-url options for branch/tag Add ability to specify on the command line the username to perform the operation as and the writable URL of the repository to perform it on. [ew: shortened subject] Signed-off-by: Igor Mironov Acked-by: Eric Wong --- git-svn.perl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 2e14b22c89..0fca1be224 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -155,12 +155,16 @@ my %cmd = ( { 'message|m=s' => \$_message, 'destination|d=s' => \$_branch_dest, 'dry-run|n' => \$_dry_run, - 'tag|t' => \$_tag } ], + 'tag|t' => \$_tag, + 'username=s' => \$Git::SVN::Prompt::_username, + 'commit-url=s' => \$_commit_url } ], tag => [ sub { $_tag = 1; cmd_branch(@_) }, 'Create a tag in the SVN repository', { 'message|m=s' => \$_message, 'destination|d=s' => \$_branch_dest, - 'dry-run|n' => \$_dry_run } ], + 'dry-run|n' => \$_dry_run, + 'username=s' => \$Git::SVN::Prompt::_username, + 'commit-url=s' => \$_commit_url } ], 'set-tree' => [ \&cmd_set_tree, "Set an SVN repository to a git tree-ish", { 'stdin' => \$_stdin, %cmt_opts, %fc_opts, } ], -- cgit v1.2.3 From 3e18ce1ac3034b1562ec748523aa7636e1b58b52 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Sat, 23 Jan 2010 03:30:00 -0500 Subject: git-svn: allow UUID to be manually remapped via rewriteUUID In certain situations it may be necessary to manually remap an svn repostitory UUID. For example: o--- [git-svn clone] / [origin svn repo] \ o--- [svnsync clone] Imagine that only "git-svn clone" and "svnsync clone" are made available to external users. Furthur, "git-svn clone" contains only trunk, and for reasons unknown, "svnsync clone" is missing the revision properties that normally provide the origin svn repo's UUID. A git user who has cloned the "git-svn clone" repo now wishes to use git-svn to pull in the missing branches from the "synsync clone" repo. In order for git-svn to get the history correct for those branches, it needs to know the origin svn repo's UUID. Hence rewriteUUID. Signed-off-by: Jay Soffian Acked-by: Eric Wong --- Documentation/git-svn.txt | 15 +++++++-- git-svn.perl | 33 +++++++++++++++--- t/t9153-git-svn-rewrite-uuid.sh | 25 ++++++++++++++ t/t9153/svn.dump | 75 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 8 deletions(-) create mode 100755 t/t9153-git-svn-rewrite-uuid.sh create mode 100644 t/t9153/svn.dump (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 8dbf9d1cb2..5df30596de 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -62,6 +62,8 @@ COMMANDS Set the 'useSvnsyncProps' option in the [svn-remote] config. --rewrite-root=;; Set the 'rewriteRoot' option in the [svn-remote] config. +--rewrite-uuid=;; + Set the 'rewriteUUID' option in the [svn-remote] config. --username=;; For transports that SVN handles authentication for (http, https, and plain svn), specify the username. For other @@ -629,6 +631,12 @@ svn-remote..rewriteRoot:: the repository with a public http:// or svn:// URL in the metadata so users of it will see the public URL. +svn-remote..rewriteUUID:: + Similar to the useSvmProps option; this is for users who need + to remap the UUID manually. This may be useful in situations + where the original UUID is not available via either useSvmProps + or useSvnsyncProps. + svn.brokenSymlinkWorkaround:: This disables potentially expensive checks to workaround broken symlinks checked into SVN by broken clients. Set this @@ -638,13 +646,14 @@ svn.brokenSymlinkWorkaround:: revision fetched. If unset, 'git svn' assumes this option to be "true". -Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps +Since the noMetadata, rewriteRoot, rewriteUUID, useSvnsyncProps and useSvmProps options all affect the metadata generated and used by 'git svn'; they *must* be set in the configuration file before any history is imported and these settings should never be changed once they are set. -Additionally, only one of these four options can be used per-svn-remote -section because they affect the 'git-svn-id:' metadata line. +Additionally, only one of these options can be used per svn-remote +section because they affect the 'git-svn-id:' metadata line, except +for rewriteRoot and rewriteUUID which can be used together. BASIC EXAMPLES diff --git a/git-svn.perl b/git-svn.perl index 0fca1be224..f7226714fa 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -115,6 +115,7 @@ my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared, 'use-svm-props' => sub { $icv{useSvmProps} = 1 }, 'use-svnsync-props' => sub { $icv{useSvnsyncProps} = 1 }, 'rewrite-root=s' => sub { $icv{rewriteRoot} = $_[1] }, + 'rewrite-uuid=s' => sub { $icv{rewriteUUID} = $_[1] }, %remote_opts ); my %cmt_opts = ( 'edit|e' => \$_edit, 'rmdir' => \$SVN::Git::Editor::_rmdir, @@ -2207,6 +2208,10 @@ sub svnsync { die "Can't have both 'useSvnsyncProps' and 'rewriteRoot' ", "options set!\n"; } + if ($self->rewrite_uuid) { + die "Can't have both 'useSvnsyncProps' and 'rewriteUUID' ", + "options set!\n"; + } my $svnsync; # see if we have it in our config, first: @@ -2488,6 +2493,20 @@ sub rewrite_root { $self->{-rewrite_root} = $rwr; } +sub rewrite_uuid { + my ($self) = @_; + return $self->{-rewrite_uuid} if exists $self->{-rewrite_uuid}; + my $k = "svn-remote.$self->{repo_id}.rewriteUUID"; + my $rwid = eval { command_oneline(qw/config --get/, $k) }; + if ($rwid) { + $rwid =~ s#/+$##; + if ($rwid !~ m#^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$#) { + die "$rwid is not a valid UUID (key: $k)\n"; + } + } + $self->{-rewrite_uuid} = $rwid; +} + sub metadata_url { my ($self) = @_; ($self->rewrite_root || $self->{url}) . @@ -3306,6 +3325,10 @@ sub make_log_entry { die "Can't have both 'useSvmProps' and 'rewriteRoot' ", "options set!\n"; } + if ($self->rewrite_uuid) { + die "Can't have both 'useSvmProps' and 'rewriteUUID' ", + "options set!\n"; + } my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}i; # we don't want "SVM: initializing mirror for junk" ... return undef if $r == 0; @@ -3336,10 +3359,10 @@ sub make_log_entry { } else { my $url = $self->metadata_url; remove_username($url); - $log_entry{metadata} = "$url\@$rev " . - $self->ra->get_uuid; - $email ||= "$author\@" . $self->ra->get_uuid; - $commit_email ||= "$author\@" . $self->ra->get_uuid; + my $uuid = $self->rewrite_uuid || $self->ra->get_uuid; + $log_entry{metadata} = "$url\@$rev " . $uuid; + $email ||= "$author\@" . $uuid; + $commit_email ||= "$author\@" . $uuid; } $log_entry{name} = $name; $log_entry{email} = $email; @@ -3421,7 +3444,7 @@ sub rebuild { '--'); my $metadata_url = $self->metadata_url; remove_username($metadata_url); - my $svn_uuid = $self->ra_uuid; + my $svn_uuid = $self->rewrite_uuid || $self->ra_uuid; my $c; while (<$log>) { if ( m{^commit ($::sha1)$} ) { diff --git a/t/t9153-git-svn-rewrite-uuid.sh b/t/t9153-git-svn-rewrite-uuid.sh new file mode 100755 index 0000000000..88a2cfa233 --- /dev/null +++ b/t/t9153-git-svn-rewrite-uuid.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2010 Jay Soffian +# + +test_description='git svn --rewrite-uuid test' + +. ./lib-git-svn.sh + +uuid=6cc8ada4-5932-4b4a-8242-3534ed8a3232 + +test_expect_success 'load svn repo' " + svnadmin load -q '$rawsvnrepo' < '$TEST_DIRECTORY/t9153/svn.dump' && + git svn init --minimize-url --rewrite-uuid='$uuid' '$svnrepo' && + git svn fetch + " + +test_expect_success 'verify uuid' " + git cat-file commit refs/remotes/git-svn~0 | \ + grep '^${git_svn_id}: .*@2 $uuid$' && + git cat-file commit refs/remotes/git-svn~1 | \ + grep '^${git_svn_id}: .*@1 $uuid$' + " + +test_done diff --git a/t/t9153/svn.dump b/t/t9153/svn.dump new file mode 100644 index 0000000000..0ddfe7025d --- /dev/null +++ b/t/t9153/svn.dump @@ -0,0 +1,75 @@ +SVN-fs-dump-format-version: 2 + +UUID: b4885626-c94f-4a6c-b179-00c030fc68e8 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2010-01-23T06:41:03.908576Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 109 +Content-length: 109 + +K 7 +svn:log +V 11 +initial foo +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T06:41:48.353776Z +PROPS-END + +Node-path: foo +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 4 +Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 +Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 +Content-length: 14 + +PROPS-END +foo + + +Revision-number: 2 +Prop-content-length: 110 +Content-length: 110 + +K 7 +svn:log +V 12 +now with bar +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T06:42:14.214640Z +PROPS-END + +Node-path: foo +Node-kind: file +Node-action: change +Text-content-length: 8 +Text-content-md5: f47c75614087a8dd938ba4acff252494 +Text-content-sha1: 4e48e2c9a3d2ca8a708cb0cc545700544efb5021 +Content-length: 8 + +foo +bar + + -- cgit v1.2.3 From 075762085c6668f11c4ea165ecec17f69245ef09 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Sat, 23 Jan 2010 03:30:01 -0500 Subject: git-svn: allow subset of branches/tags to be specified in glob spec For very large projects it is useful to be able to clone a subset of the upstream SVN repo's branches. Allow for this by letting the left-side of the branches and tags glob specs contain a brace-delineated comma-separated list of names. e.g.: branches = branches/{red,green}/src:refs/remotes/branches/* Signed-off-by: Jay Soffian Acked-by: Eric Wong --- Documentation/git-svn.txt | 16 +++ git-svn.perl | 60 ++++++++---- t/t9154-git-svn-fancy-glob.sh | 42 ++++++++ t/t9154/svn.dump | 222 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 320 insertions(+), 20 deletions(-) create mode 100755 t/t9154-git-svn-fancy-glob.sh create mode 100644 t/t9154/svn.dump (limited to 'git-svn.perl') diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 5df30596de..99f3c1ea6c 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -838,6 +838,22 @@ independent path component (surrounded by '/' or EOL). This type of configuration is not automatically created by 'init' and should be manually entered with a text-editor or using 'git config'. +It is also possible to fetch a subset of branches or tags by using a +comma-separated list of names within braces. For example: + +------------------------------------------------------------------------ +[svn-remote "huge-project"] + url = http://server.org/svn + fetch = trunk/src:refs/remotes/trunk + branches = branches/{red,green}/src:refs/remotes/branches/* + tags = tags/{1.0,2.0}/src:refs/remotes/tags/* +------------------------------------------------------------------------ + +Note that git-svn keeps track of the highest revision in which a branch +or tag has appeared. If the subset of branches or tags is changed after +fetching, then .git/svn/.metadata must be manually edited to remove (or +reset) branches-maxRev and/or tags-maxRev as appropriate. + SEE ALSO -------- linkgit:git-rebase[1] diff --git a/git-svn.perl b/git-svn.perl index f7226714fa..b321c968af 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1825,8 +1825,8 @@ sub read_all_remotes { my $rs = { t => $t, remote => $remote, - path => Git::SVN::GlobSpec->new($local_ref), - ref => Git::SVN::GlobSpec->new($remote_ref) }; + path => Git::SVN::GlobSpec->new($local_ref, 1), + ref => Git::SVN::GlobSpec->new($remote_ref, 0) }; if (length($rs->{ref}->{right}) != 0) { die "The '*' glob character must be the last ", "character of '$remote_ref'\n"; @@ -5233,6 +5233,7 @@ sub match_globs { next if (length $g->{path}->{right} && ($self->check_path($p, $r) != $SVN::Node::dir)); + next unless $p =~ /$g->{path}->{regex}/; $exists->{$p} = Git::SVN->init($self->{url}, $p, undef, $g->{ref}->full_path($de), 1); } @@ -6006,29 +6007,48 @@ use strict; use warnings; sub new { - my ($class, $glob) = @_; + my ($class, $glob, $pattern_ok) = @_; my $re = $glob; $re =~ s!/+$!!g; # no need for trailing slashes - $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!; - my $temp = $re; - my ($left, $right) = ($1, $3); - $re = $2; - my $depth = $re =~ tr/*/*/; - if ($depth != $temp =~ tr/*/*/) { - die "Only one set of wildcard directories " . - "(e.g. '*' or '*/*/*') is supported: '$glob'\n"; + my (@left, @right, @patterns); + my $state = "left"; + my $die_msg = "Only one set of wildcard directories " . + "(e.g. '*' or '*/*/*') is supported: '$glob'\n"; + for my $part (split(m|/|, $glob)) { + if ($part =~ /\*/ && $part ne "*") { + die "Invalid pattern in '$glob': $part\n"; + } elsif ($pattern_ok && $part =~ /[{}]/ && + $part !~ /^\{[^{}]+\}/) { + die "Invalid pattern in '$glob': $part\n"; + } + if ($part eq "*") { + die $die_msg if $state eq "right"; + $state = "pattern"; + push(@patterns, "[^/]*"); + } elsif ($pattern_ok && $part =~ /^\{(.*)\}$/) { + die $die_msg if $state eq "right"; + $state = "pattern"; + my $p = quotemeta($1); + $p =~ s/\\,/|/g; + push(@patterns, "(?:$p)"); + } else { + if ($state eq "left") { + push(@left, $part); + } else { + push(@right, $part); + $state = "right"; + } + } } + my $depth = @patterns; if ($depth == 0) { - die "One '*' is needed for glob: '$glob'\n"; - } - $re =~ s!\*!\[^/\]*!g; - $re = quotemeta($left) . "($re)" . quotemeta($right); - if (length $left && !($left =~ s!/+$!!g)) { - die "Missing trailing '/' on left side of: '$glob' ($left)\n"; - } - if (length $right && !($right =~ s!^/+!!g)) { - die "Missing leading '/' on right side of: '$glob' ($right)\n"; + die "One '*' is needed in glob: '$glob'\n"; } + my $left = join('/', @left); + my $right = join('/', @right); + $re = join('/', @patterns); + $re = join('\/', + grep(length, quotemeta($left), "($re)", quotemeta($right))); my $left_re = qr/^\/\Q$left\E(\/|$)/; bless { left => $left, right => $right, left_regex => $left_re, regex => qr/$re/, glob => $glob, depth => $depth }, $class; diff --git a/t/t9154-git-svn-fancy-glob.sh b/t/t9154-git-svn-fancy-glob.sh new file mode 100755 index 0000000000..a6a56a6cb9 --- /dev/null +++ b/t/t9154-git-svn-fancy-glob.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright (c) 2010 Jay Soffian +# + +test_description='git svn fancy glob test' + +. ./lib-git-svn.sh + +test_expect_success 'load svn repo' " + svnadmin load -q '$rawsvnrepo' < '$TEST_DIRECTORY/t9154/svn.dump' && + git svn init --minimize-url -T trunk '$svnrepo' && + git svn fetch + " + +test_expect_success 'add red branch' " + git config svn-remote.svn.branches 'branches/{red}:refs/remotes/*' && + git svn fetch && + git rev-parse refs/remotes/red && + test_must_fail git rev-parse refs/remotes/green && + test_must_fail git rev-parse refs/remotes/blue + " + +test_expect_success 'add green branch' " + GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev && + git config svn-remote.svn.branches 'branches/{red,green}:refs/remotes/*' && + git svn fetch && + git rev-parse refs/remotes/red && + git rev-parse refs/remotes/green && + test_must_fail git rev-parse refs/remotes/blue + " + +test_expect_success 'add all branches' " + GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev && + git config svn-remote.svn.branches 'branches/*:refs/remotes/*' && + git svn fetch && + git rev-parse refs/remotes/red && + git rev-parse refs/remotes/green && + git rev-parse refs/remotes/blue + " + +test_done diff --git a/t/t9154/svn.dump b/t/t9154/svn.dump new file mode 100644 index 0000000000..3dfabb67db --- /dev/null +++ b/t/t9154/svn.dump @@ -0,0 +1,222 @@ +SVN-fs-dump-format-version: 2 + +UUID: a18093a0-5f0b-44e0-8d88-8911ac7078db + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2010-01-23T07:40:25.660053Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 104 +Content-length: 104 + +K 7 +svn:log +V 7 +initial +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T07:41:33.636365Z +PROPS-END + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk/foo +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 4 +Text-content-md5: d3b07384d113edec49eaa6238ad5ff00 +Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 +Content-length: 14 + +PROPS-END +foo + + +Revision-number: 2 +Prop-content-length: 110 +Content-length: 110 + +K 7 +svn:log +V 12 +add branches +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T07:42:37.290694Z +PROPS-END + +Node-path: branches +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: branches/blue +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Node-path: branches/green +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Node-path: branches/red +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Revision-number: 3 +Prop-content-length: 108 +Content-length: 108 + +K 7 +svn:log +V 10 +red change +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T07:43:02.208918Z +PROPS-END + +Node-path: branches/red/foo +Node-kind: file +Node-action: change +Text-content-length: 8 +Text-content-md5: 64c3c8cf7d0233ab7627623a68888bd1 +Text-content-sha1: 95a0492027876adfd3891ec71ee37b79ee44d640 +Content-length: 8 + +foo +red + + +Revision-number: 4 +Prop-content-length: 110 +Content-length: 110 + +K 7 +svn:log +V 12 +green change +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T07:43:15.746586Z +PROPS-END + +Node-path: branches/green/foo +Node-kind: file +Node-action: change +Text-content-length: 10 +Text-content-md5: 0209b6450891abc033d5eaaa9d3a8023 +Text-content-sha1: 87fc3bef9faeec48c0cd61dfc9851db377fdccf7 +Content-length: 10 + +foo +green + + +Revision-number: 5 +Prop-content-length: 109 +Content-length: 109 + +K 7 +svn:log +V 11 +blue change +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T07:43:29.364811Z +PROPS-END + +Node-path: branches/blue/foo +Node-kind: file +Node-action: change +Text-content-length: 9 +Text-content-md5: 9fbe4c13d0bae86386ae5209b2e6b275 +Text-content-sha1: cc4575083459a16f9aaef796c4a2456d64691ba0 +Content-length: 9 + +foo +blue + + +Revision-number: 6 +Prop-content-length: 110 +Content-length: 110 + +K 7 +svn:log +V 12 +trunk change +K 10 +svn:author +V 3 +jay +K 8 +svn:date +V 27 +2010-01-23T07:44:01.313130Z +PROPS-END + +Node-path: trunk/foo +Node-kind: file +Node-action: change +Text-content-length: 10 +Text-content-md5: 1c4db977d7a57c3bae582aab87948516 +Text-content-sha1: 469c08df449e702cf2a1fe746244a9ef3f837fad +Content-length: 10 + +foo +trunk + + -- cgit v1.2.3 From a004fb923d74c1d5500d493506b1462eb04cf017 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 23 Jan 2010 15:20:28 +0100 Subject: If deriving SVN_SSH from GIT_SSH on msys, also add quotes In contrast to GIT_SSH, SVN_SSH requires quotes for paths that contain spaces. As GIT_SSH will not work if it contains quotes, it is safe to assume it never contains quotes. Also, adding quotes to SVN_SSH for paths that do not contain spaces does no harm. So we always add quotes when deriving SVN_SSH from GIT_SSH on msys. This fixes msysGit issue 385, see http://code.google.com/p/msysgit/issues/detail?id=385 Signed-off-by: Sebastian Schuberth Signed-off-by: Junio C Hamano --- git-svn.perl | 1 + 1 file changed, 1 insertion(+) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index b321c968af..4f7dc2b73b 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -26,6 +26,7 @@ if (! exists $ENV{SVN_SSH}) { $ENV{SVN_SSH} = $ENV{GIT_SSH}; if ($^O eq 'msys') { $ENV{SVN_SSH} =~ s/\\/\\\\/g; + $ENV{SVN_SSH} =~ s/(.*)/"$1"/; } } } -- cgit v1.2.3 From 8bff7c5383ed833bd1df9c8d85c00a27af3e5b02 Mon Sep 17 00:00:00 2001 From: Andrew Myrick Date: Sat, 30 Jan 2010 03:14:22 +0000 Subject: git-svn: persistent memoization Make memoization of the svn:mergeinfo processing functions persistent with Memoize::Storable so that the memoization tables don't need to be regenerated every time the user runs git-svn fetch. The Memoize::Storable hashes are stored in ENV{GIT_DIR}/svn/.caches. [ew: changed caches path to avoid conflicts with old repos] [ew: File::Path::{make_path => mkpath} for compatibility] [ew: line-wrapped at 80 chars] Acked-by: Eric Wong Signed-off-by: Andrew Myrick --- git-svn.perl | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'git-svn.perl') diff --git a/git-svn.perl b/git-svn.perl index 4f7dc2b73b..265852f459 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1656,6 +1656,7 @@ use File::Path qw/mkpath/; use File::Copy qw/copy/; use IPC::Open3; use Memoize; # core since 5.8.0, Jul 2002 +use Memoize::Storable; my ($_gc_nr, $_gc_period); @@ -3116,10 +3117,39 @@ sub has_no_changes { command_oneline("rev-parse", "$commit~1^{tree}")); } -BEGIN { - memoize 'lookup_svn_merge'; - memoize 'check_cherry_pick'; - memoize 'has_no_changes'; +# The GIT_DIR environment variable is not always set until after the command +# line arguments are processed, so we can't memoize in a BEGIN block. +{ + my $memoized = 0; + + sub memoize_svn_mergeinfo_functions { + return if $memoized; + $memoized = 1; + + my $cache_path = "$ENV{GIT_DIR}/svn/.caches/"; + mkpath([$cache_path]) unless -d $cache_path; + + tie my %lookup_svn_merge_cache => 'Memoize::Storable', + "$cache_path/lookup_svn_merge.db", 'nstore'; + memoize 'lookup_svn_merge', + SCALAR_CACHE => 'FAULT', + LIST_CACHE => ['HASH' => \%lookup_svn_merge_cache], + ; + + tie my %check_cherry_pick_cache => 'Memoize::Storable', + "$cache_path/check_cherry_pick.db", 'nstore'; + memoize 'check_cherry_pick', + SCALAR_CACHE => 'FAULT', + LIST_CACHE => ['HASH' => \%check_cherry_pick_cache], + ; + + tie my %has_no_changes_cache => 'Memoize::Storable', + "$cache_path/has_no_changes.db", 'nstore'; + memoize 'has_no_changes', + SCALAR_CACHE => ['HASH' => \%has_no_changes_cache], + LIST_CACHE => 'FAULT', + ; + } } sub parents_exclude { @@ -3163,6 +3193,8 @@ sub find_extra_svn_parents { my ($self, $ed, $mergeinfo, $parents) = @_; # aha! svk:merge property changed... + memoize_svn_mergeinfo_functions(); + # We first search for merged tips which are not in our # history. Then, we figure out which git revisions are in # that tip, but not this revision. If all of those revisions -- cgit v1.2.3