With this patch, users can specify colon-seperated lists of directories containing tags and branches, respectively. This makes git-svnimport much more usable in the real world because there are Subversion repositories that use a deeper tag and branch hierarchy than the recommended hierarchy of trunk/ tags/ branches/ The patch assumes my memleak fix patch from http://marc.info/?l=git&m=118554191513822&w=2 but applies cleanly to current vanilla git head as well. I'm not subscribed to the list so please Cc me in replies, thanks. diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt index e97d15e..2cfc407 100644 --- a/Documentation/git-svnimport.txt +++ b/Documentation/git-svnimport.txt @@ -12,11 +12,11 @@ SYNOPSIS [verse] 'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ] [ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev] - [ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ] - [ -s start_chg ] [ -m ] [ -r ] [ -M regex ] - [ -I <ignorefile_name> ] [ -A <author_file> ] - [ -R <repack_each_revs>] [ -P <path_from_trunk> ] - <SVN_repository_URL> [ <path> ] + [ -b branch_subdir[:branch_subdir:...] ] [ -T trunk_subdir ] + [ -t tag_subdir[:tag_subdir:...] ] [ -s start_chg ] [ -m ] + [ -r ] [ -M regex ] [ -I <ignorefile_name> ] + [ -A <author_file> ] [ -R <repack_each_revs>] + [ -P <path_from_trunk> ] <SVN_repository_URL> [ <path> ] DESCRIPTION @@ -26,11 +26,6 @@ repository, or incrementally import into an existing one. SVN access is done by the SVN::Perl module. -git-svnimport assumes that SVN repositories are organized into one -"trunk" directory where the main development happens, "branches/FOO" -directories for branches, and "/tags/FOO" directories for tags. -Other subdirectories are ignored. - git-svnimport creates a file ".git/svn2git", which is required for incremental SVN imports. @@ -53,11 +48,13 @@ When importing incrementally, you might need to edit the .git/svn2git file. -T <trunk_subdir>:: Name the SVN trunk. Default "trunk". --t <tag_subdir>:: - Name the SVN subdirectory for tags. Default "tags". +-t <tag_subdir[:tag_subdir:..]>:: + Colon-seperated list of names of subdirectories containing tags. + Default "tags". --b <branch_subdir>:: - Name the SVN subdirectory for branches. Default "branches". +-b <branch_subdir[:branch_subdir:...]>:: + Colon-seperated list of names of subdirectories containing branches. + Default "branches". -o <branch-for-HEAD>:: The 'trunk' branch from SVN is imported to the 'origin' branch within diff --git a/git-svnimport.perl b/git-svnimport.perl index fc9ea71..96f0926 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -38,9 +38,10 @@ sub usage() { print STDERR <<END; Usage: ${\basename $0} # fetch/update GIT from SVN [-o branch-for-HEAD] [-h] [-v] [-l max_rev] [-R repack_each_revs] - [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname] - [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg] - [-m] [-M regex] [-A author_file] [-S] [-F] [-P project_name] [SVN_URL] + [-C GIT_repository] [-t tag_subdir[:tag_subdir:...]] [-T trunk_subdir] + [-b branch_subdir[:branch_subdir:...]] [-d|-D] [-i] [-u] [-r] + [-I ignorefilename] [-s start_chg] [-m] [-M regex] + [-A author_file] [-S] [-F] [-P project_name] [SVN_URL] END exit(1); } @@ -48,9 +49,9 @@ END getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:R:uv") or usage(); usage if $opt_h; -my $tag_name = $opt_t || "tags"; -my $trunk_name = defined $opt_T ? $opt_T : "trunk"; -my $branch_name = $opt_b || "branches"; +my @tag_dirs = defined $opt_t ? split /:/,$opt_t : "tags"; +my $trunk_dir = defined $opt_T ? $opt_T : "trunk"; +my @branch_dirs = defined $opt_b ? split /:/,$opt_b : "branches"; my $project_name = $opt_P || ""; $project_name = "/" . $project_name if ($project_name); my $repack_after = $opt_R || 1000; @@ -68,14 +69,18 @@ my $svn_dir = $ARGV[1]; our @mergerx = (); if ($opt_m) { - my $branch_esc = quotemeta ($branch_name); - my $trunk_esc = quotemeta ($trunk_name); - @mergerx = - ( - qr!\b(?:merg(?:ed?|ing))\b.*?\b((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, - qr!\b(?:from|of)\W+((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, - qr!\b(?:from|of)\W+(?:the )?([\w\.\-]+)[-\s]branch\b!i - ); + my @branch_escs; + foreach (@branch_dirs) { push @branch_escs, quotemeta ($_); } + my $trunk_esc = quotemeta ($trunk_dir); + + foreach my $branch_esc (@branch_escs) { + push (@mergerx, + ( + qr!\b(?:merg(?:ed?|ing))\b.*?\b((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, + qr!\b(?:from|of)\W+((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, + qr!\b(?:from|of)\W+(?:the )?([\w\.\-]+)[-\s]branch\b!i + )); + } } if ($opt_M) { unshift (@mergerx, qr/$opt_M/); @@ -452,29 +457,39 @@ sub project_path($$) sub split_path($$) { my($rev,$path) = @_; - my $branch; + my $dir; # tag or branch dir + + OUTER: foreach my $tag_dir (@tag_dirs) { + foreach my $branch_dir (@branch_dirs) { + if ($path =~ s#^/\Q$tag_dir\E/([^/]+)/?##) { + $dir = "/$1"; + } elsif ($path =~ s#^/\Q$trunk_dir\E/?##) { + $dir = "/"; + } elsif ($path =~ s#^/\Q$branch_dir\E/([^/]+)/?##) { + $dir = $1; + } + last OUTER if $dir; + } + } - if($path =~ s#^/\Q$tag_name\E/([^/]+)/?##) { - $branch = "/$1"; - } elsif($path =~ s#^/\Q$trunk_name\E/?##) { - $branch = "/"; - } elsif($path =~ s#^/\Q$branch_name\E/([^/]+)/?##) { - $branch = $1; - } else { - my %no_error = ( - "/" => 1, - "/$tag_name" => 1, - "/$branch_name" => 1 - ); - print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path}); - return () + if (! $dir) { + my %no_error = ( "/" => 1, "/tags" => 1, "/branches" => 1 ); + foreach (@tag_dirs) { + $no_error{"/$_"} = 1; + } + foreach (@branch_dirs) { + $no_error{"/$_"} = 1; + } + print STDERR "$rev: Could not determine tag or branch ", + "directory for path '$path'\n", + unless (defined $no_error{$path}); } if ($path eq "") { $path = "/"; } elsif ($project_name) { $path = project_path($path, $project_name); } - return ($branch,$path); + return ($dir,$path); } sub branch_rev($$) { @@ -874,9 +889,32 @@ sub commit { $dest =~ tr/_/\./ if $opt_u; - system('git-tag', $dest, $cid) == 0 - or die "Cannot create tag $dest: $!\n"; - + # Since we support multiple directories that host tags we + # must support repositories that contain the same tag name + # in different tag directories for whatever reason, + # e.g. tags/jim/1.0-rc3 and tags/huck/1.0-rc3 + # + # Also, because in Subversion a given tag directory could + # exist in revision A, be deleted in revision B and later + # recreated under the same name in revision C, we + # cannot guarantee that a tag is unique simply by + # looking at the tag's path. + # + # So if we fail to create the tag the first time, we try + # to create the tag with the same name except with the + # revision number of the current commit appended. + # Users can rename tags again later after the repository + # has been converted if they don't like this. + # It's still much better than failing to convert the + # repository alltogether. + if (system('git-tag', $dest, $cid) != 0) { + print STDERR "Could not create tag $dest, ", + "trying to create tag $dest-r$revision ", + "instead\n"; + $dest = "$dest-r$revision"; + system('git-tag', $dest, $cid) == 0 + or die "Cannot create tag $dest: $? $!\n"; + } print "Created tag '$dest' on '$branch'\n" if $opt_v; } $branches{$branch}{"LAST"} = $cid; -- Stefan Sperling <stsp@xxxxxxxx> Software Developer elego Software Solutions GmbH HRB 77719 Gustav-Meyer-Allee 25, Gebaeude 12 Tel: +49 30 23 45 86 96 13355 Berlin Fax: +49 30 23 45 86 95 http://www.elego.de Geschaeftsfuehrer: Olaf Wagner
Attachment:
pgpINRqtbeHQI.pgp
Description: PGP signature