git-svn: allow UUID to be manually remapped via rewriteUUID
[gitweb.git] / git-svn.perl
index 650c9e5f02ead07351629d6572e82c3a9ac7ef92..f7226714fadc59a501a536451ea66334f7baa404 100755 (executable)
@@ -115,6 +115,7 @@ BEGIN
                  '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,
@@ -155,12 +156,16 @@ BEGIN
                    { '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, } ],
@@ -708,7 +713,21 @@ 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:/;
+       }
 
        my $ctx = SVN::Client->new(
                auth    => Git::SVN::Ra::_auth_providers(),
@@ -2189,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:
@@ -2470,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}) .
@@ -3052,12 +3089,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 {
@@ -3134,10 +3195,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(
@@ -3253,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;
@@ -3283,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;
@@ -3368,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)$} ) {