X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-svn.perl;h=80b7b87f0f4f1933e551439ca4fb2cd68981a029;hb=d09e79cb1c474b3bb323356e6d1072922ab7ccb2;hp=0d58bb9b37944728baa7d8af17d714f6ee8b4509;hpb=bb266cb11842f76712ebff1d8b1bd086dc65337f;p=git.git diff --git a/git-svn.perl b/git-svn.perl index 0d58bb9b3..80b7b87f0 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -40,8 +40,22 @@ memoize('cmt_metadata'); memoize('get_commit_time'); my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib); + +sub nag_lib { + print STDERR < \$_no_ignore_ext, 'repack:i' => \$_repack, 'no-metadata' => \$_no_metadata, 'quiet|q' => \$_q, + 'ignore-nodate' => \$_ignore_nodate, 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); my ($_trunk, $_tags, $_branches); @@ -112,11 +128,18 @@ my %cmd = ( 'incremental' => \$_incremental, 'oneline' => \$_oneline, 'show-commit' => \$_show_commit, + 'non-recursive' => \$_non_recursive, 'authors-file|A=s' => \$_authors, } ], 'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees', { 'message|m=s' => \$_message, 'file|F=s' => \$_file, + 'revision|r=s' => \$_revision, + %cmt_opts } ], + dcommit => [ \&dcommit, 'Commit several diffs to merge with upstream', + { 'merge|m|M' => \$_merge, + 'strategy|s=s' => \$_strategy, + 'dry-run|n' => \$_dry_run, %cmt_opts } ], ); @@ -161,11 +184,11 @@ Usage: $0 [options] [arguments]\n foreach (sort keys %cmd) { next if $cmd && $cmd ne $_; - print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n"; + print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; foreach (keys %{$cmd{$_}->[2]}) { # prints out arguments as they should be passed: my $x = s#[:=]s$## ? '' : s#[:=]i$## ? '' : ''; - print $fd ' ' x 17, join(', ', map { length $_ > 1 ? + print $fd ' ' x 21, join(', ', map { length $_ > 1 ? "--$_" : "-$_" } split /\|/,$_)," $x\n"; } @@ -500,6 +523,8 @@ sub commit_lib { my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; + my $repo; + ($repo, $SVN_PATH) = repo_path_split($SVN_URL); set_svn_commit_env(); foreach my $c (@revs) { my $log_msg = get_commit_message($c, $commit_msg); @@ -508,9 +533,11 @@ sub commit_lib { # can't track down... (it's probably in the SVN code) defined(my $pid = open my $fh, '-|') or croak $!; if (!$pid) { + $SVN_LOG = libsvn_connect($repo); + $SVN = libsvn_connect($repo); my $ed = SVN::Git::Editor->new( { r => $r_last, - ra => $SVN, + ra => $SVN_LOG, c => $c, svn_path => $SVN_PATH }, @@ -557,6 +584,43 @@ sub commit_lib { unlink $commit_msg; } +sub dcommit { + my $gs = "refs/remotes/$GIT_SVN"; + chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..HEAD")); + my $last_rev; + foreach my $d (reverse @refs) { + unless (defined $last_rev) { + (undef, $last_rev, undef) = cmt_metadata("$d~1"); + unless (defined $last_rev) { + die "Unable to extract revision information ", + "from commit $d~1\n"; + } + } + if ($_dry_run) { + print "diff-tree $d~1 $d\n"; + } else { + if (my $r = commit_diff("$d~1", $d, undef, $last_rev)) { + $last_rev = $r; + } # else: no changes, same $last_rev + } + } + return if $_dry_run; + fetch(); + my @diff = safe_qx(qw/git-diff-tree HEAD/, $gs); + my @finish; + if (@diff) { + @finish = qw/rebase/; + push @finish, qw/--merge/ if $_merge; + push @finish, "--strategy=$_strategy" if $_strategy; + print STDERR "W: HEAD and $gs differ, using @finish:\n", @diff; + } else { + print "No changes between current HEAD and $gs\n", + "Hard resetting to the latest $gs\n"; + @finish = qw/reset --hard/; + } + sys('git', @finish, $gs); +} + sub show_ignore { $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); $_use_lib ? show_ignore_lib() : show_ignore_cmd(); @@ -644,12 +708,17 @@ sub multi_init { } $_trunk = $url . $_trunk; } + my $ch_id; if ($GIT_SVN eq 'git-svn') { - print "GIT_SVN_ID set to 'trunk' for $_trunk\n"; + $ch_id = 1; $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk'; } init_vars(); - init($_trunk); + unless (-d $GIT_SVN_DIR) { + print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id; + init($_trunk); + sys('git-repo-config', 'svn.trunk', $_trunk); + } complete_url_ls_init($url, $_branches, '--branches/-b', ''); complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/'); } @@ -709,13 +778,18 @@ sub show_log { # ignore } elsif (/^:\d{6} \d{6} $sha1_short/o) { push @{$c->{raw}}, $_; + } elsif (/^[ACRMDT]\t/) { + # we could add $SVN_PATH here, but that requires + # remote access at the moment (repo_path_split)... + s#^([ACRMDT])\t# $1 #; + push @{$c->{changed}}, $_; } elsif (/^diff /) { $d = 1; push @{$c->{diff}}, $_; } elsif ($d) { push @{$c->{diff}}, $_; } elsif (/^ (git-svn-id:.+)$/) { - (undef, $c->{r}, undef) = extract_metadata($1); + ($c->{url}, $c->{r}, undef) = extract_metadata($1); } elsif (s/^ //) { push @{$c->{l}}, $_; } @@ -751,6 +825,8 @@ sub commit_diff { print STDERR "Needed URL or usable git-svn id command-line\n"; commit_diff_usage(); } + my $r = shift || $_revision; + die "-r|--revision is a required argument\n" unless (defined $r); if (defined $_message && defined $_file) { print STDERR "Both --message/-m and --file/-F specified ", "for the commit message.\n", @@ -767,13 +843,22 @@ sub commit_diff { ($repo, $SVN_PATH) = repo_path_split($SVN_URL); $SVN_LOG ||= libsvn_connect($repo); $SVN ||= libsvn_connect($repo); + if ($r eq 'HEAD') { + $r = $SVN->get_latest_revnum; + } elsif ($r !~ /^\d+$/) { + die "revision argument: $r not understood by git-svn\n"; + } my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); - my $ed = SVN::Git::Editor->new({ r => $SVN->get_latest_revnum, - ra => $SVN, c => $tb, + my $rev_committed; + my $ed = SVN::Git::Editor->new({ r => $r, + ra => $SVN_LOG, c => $tb, svn_path => $SVN_PATH }, $SVN->get_commit_editor($_message, - sub {print "Committed $_[0]\n"},@lock) + sub { + $rev_committed = $_[0]; + print "Committed $_[0]\n"; + }, @lock) ); my $mods = libsvn_checkout_tree($ta, $tb, $ed); if (@$mods == 0) { @@ -782,6 +867,8 @@ sub commit_diff { } else { $ed->close_edit; } + $_message = $_file = undef; + return $rev_committed; } ########################### utility functions ######################### @@ -806,7 +893,8 @@ sub git_svn_log_cmd { my ($r_min, $r_max) = @_; my @cmd = (qw/git-log --abbrev-commit --pretty=raw --default/, "refs/remotes/$GIT_SVN"); - push @cmd, '--summary' if $_verbose; + push @cmd, '-r' unless $_non_recursive; + push @cmd, qw/--raw --name-status/ if $_verbose; return @cmd unless defined $r_max; if ($r_max == $r_min) { push @cmd, '--max-count=1'; @@ -817,7 +905,7 @@ sub git_svn_log_cmd { my ($c_min, $c_max); $c_max = revdb_get($REVDB, $r_max); $c_min = revdb_get($REVDB, $r_min); - if ($c_min && $c_max) { + if (defined $c_min && defined $c_max) { if ($r_max > $r_max) { push @cmd, "$c_min..$c_max"; } else { @@ -898,16 +986,21 @@ sub complete_url_ls_init { print STDERR "W: Unrecognized URL: $u\n"; die "This should never happen\n"; } + # don't try to init already existing refs my $id = $pfx.$1; - print "init $u => $id\n"; $GIT_SVN = $ENV{GIT_SVN_ID} = $id; init_vars(); - init($u); + unless (-d $GIT_SVN_DIR) { + print "init $u => $id\n"; + init($u); + } } exit 0; } waitpid $pid, 0; croak $? if $?; + my ($n) = ($switch =~ /^--(\w+)/); + sys('git-repo-config', "svn.$n", $var); } sub common_prefix { @@ -1208,6 +1301,7 @@ sub assert_svn_wc_clean { } my @status = grep(!/^Performing status on external/,(`svn status`)); @status = grep(!/^\s*$/,@status); + @status = grep(!/^X/,@status) if $_no_ignore_ext; if (scalar @status) { print STDERR "Tree ($SVN_WC) is not clean:\n"; print STDERR $_ foreach @status; @@ -1430,10 +1524,13 @@ sub svn_checkout_tree { apply_mod_line_blob($m); svn_check_prop_executable($m); } elsif ($m->{chg} eq 'T') { - sys(qw(svn rm --force),$m->{file_b}); - apply_mod_line_blob($m); - sys(qw(svn add), $m->{file_b}); svn_check_prop_executable($m); + apply_mod_line_blob($m); + if ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) { + sys(qw(svn propdel svn:special), $m->{file_b}); + } else { + sys(qw(svn propset svn:special *),$m->{file_b}); + } } elsif ($m->{chg} eq 'A') { svn_ensure_parent_path( $m->{file_b} ); apply_mod_line_blob($m); @@ -1696,6 +1793,8 @@ sub next_log_entry { my $rev = $1; my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3); ($lines) = ($lines =~ /(\d+)/); + $date = '1970-01-01 00:00:00 +0000' + if ($_ignore_nodate && $date eq '(no date)'); my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~ /(\d{4})\-(\d\d)\-(\d\d)\s (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x) @@ -2130,7 +2229,7 @@ sub load_authors { open my $authors, '<', $_authors or die "Can't open $_authors $!\n"; while (<$authors>) { chomp; - next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; + next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/; my ($user, $name, $email) = ($1, $2, $3); $users{$user} = [$name, $email]; } @@ -2509,6 +2608,12 @@ sub show_commit { } } +sub show_commit_changed_paths { + my ($c) = @_; + return unless $c->{changed}; + print "Changed paths:\n", @{$c->{changed}}; +} + sub show_commit_normal { my ($c) = @_; print '-' x72, "\nr$c->{r} | "; @@ -2518,7 +2623,8 @@ sub show_commit_normal { my $nr_line = 0; if (my $l = $c->{l}) { - while ($l->[$#$l] eq "\n" && $l->[($#$l - 1)] eq "\n") { + while ($l->[$#$l] eq "\n" && $#$l > 0 + && $l->[($#$l - 1)] eq "\n") { pop @$l; } $nr_line = scalar @$l; @@ -2530,11 +2636,15 @@ sub show_commit_normal { } else { $nr_line .= ' lines'; } - print $nr_line, "\n\n"; + print $nr_line, "\n"; + show_commit_changed_paths($c); + print "\n"; print $_ foreach @$l; } } else { - print "1 line\n\n"; + print "1 line\n"; + show_commit_changed_paths($c); + print "\n"; } foreach my $x (qw/raw diff/) { @@ -2575,11 +2685,12 @@ sub libsvn_connect { } sub libsvn_get_file { - my ($gui, $f, $rev) = @_; + my ($gui, $f, $rev, $chg) = @_; my $p = $f; if (length $SVN_PATH > 0) { return unless ($p =~ s#^\Q$SVN_PATH\E/##); } + print "\t$chg\t$f\n" unless $_q; my ($hash, $pid, $in, $out); my $pool = SVN::Pool->new; @@ -2682,8 +2793,7 @@ sub libsvn_fetch { $pool->clear; } foreach (@amr) { - print "\t$_->[0]\t$_->[1]\n" unless $_q; - libsvn_get_file($gui, $_->[1], $rev) + libsvn_get_file($gui, $_->[1], $rev, $_->[0]); } close $gui or croak $?; return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]); @@ -2761,8 +2871,7 @@ sub libsvn_traverse { if (defined $files) { push @$files, $file; } else { - print "\tA\t$file\n" unless $_q; - libsvn_get_file($gui, $file, $rev); + libsvn_get_file($gui, $file, $rev, 'A'); } } } @@ -3053,7 +3162,7 @@ sub copy_remote_ref { my $ref = "refs/remotes/$GIT_SVN"; if (safe_qx('git-ls-remote', $origin, $ref)) { sys(qw/git fetch/, $origin, "$ref:$ref"); - } else { + } elsif ($_cp_remote && !$_upgrade) { die "Unable to find remote reference: ", "refs/remotes/$GIT_SVN on $origin\n"; } @@ -3270,9 +3379,11 @@ sub chg_file { seek $fh, 0, 0 or croak $!; my $exp = $md5->hexdigest; - my $atd = $self->apply_textdelta($fbat, undef, $self->{pool}); - my $got = SVN::TxDelta::send_stream($fh, @$atd, $self->{pool}); + my $pool = SVN::Pool->new; + my $atd = $self->apply_textdelta($fbat, undef, $pool); + my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool); die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp); + $pool->clear; close $fh or croak $!; }