use File::Spec qw//;
use POSIX qw/strftime/;
my $sha1 = qr/[a-f\d]{40}/;
-my $sha1_short = qr/[a-f\d]{6,40}/;
+my $sha1_short = qr/[a-f\d]{4,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_version, $_upgrade);
+my (@_branch_from, %tree_map);
GetOptions( 'revision|r=s' => \$_revision,
'no-ignore-externals' => \$_no_ignore_ext,
'rmdir' => \$_rmdir,
'upgrade' => \$_upgrade,
'help|H|h' => \$_help,
+ 'branch|b=s' => \@_branch_from,
'find-copies-harder' => \$_find_copies_harder,
'l=i' => \$_l,
'version|V' => \$_version,
Available commands:
foreach (sort keys %cmd) {
- print $fd ' ',pack('A10',$_),$cmd{$_}->[1],"\n";
+ print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n";
}
print $fd <<"";
\nGIT_SVN_ID may be set in the environment to an arbitrary identifier if
you're tracking multiple SVN branches/repositories in one git repository
-and want to keep them separate.
+and want to keep them separate. See git-svn(1) for more information.
exit $exit;
}
print "Reading from stdin...\n";
@commits = ();
while (<STDIN>) {
- if (/\b([a-f\d]{6,40})\b/) {
+ if (/\b($sha1_short)\b/) {
unshift @commits, $1;
}
}
chdir $SVN_WC or croak $!;
my $svn_current_rev = svn_info('.')->{'Last Changed Rev'};
foreach my $c (@revs) {
- print "Committing $c\n";
my $mods = svn_checkout_tree($svn_current_rev, $c);
if (scalar @$mods == 0) {
print "Skipping, no changes detected\n";
my ($svn_rev, $treeish) = @_;
my $from = file_to_s("$REV_DIR/$svn_rev");
assert_svn_wc_clean($svn_rev,$from);
- print "diff-tree '$from' '$treeish'\n";
+ print "diff-tree $from $treeish\n";
my $pid = open my $diff_fh, '-|';
defined $pid or croak $!;
if ($pid == 0) {
}
my $mods = parse_diff_tree($diff_fh);
unless (@$mods) {
- # git can do empty commits, SVN doesn't allow it...
+ # git can do empty commits, but SVN doesn't allow it...
return $mods;
}
my ($rm, $add) = precommit_check($mods);
my ($svn_rev, $commit) = @_;
my $commit_msg = "$GIT_DIR/$GIT_SVN/.svn-commit.tmp.$$";
my %log_msg = ( msg => '' );
- open my $msg, '>', $commit_msg or croak $!;
+ open my $msg, '>', $commit_msg or croak $!;
chomp(my $type = `git-cat-file -t $commit`);
if ($type eq 'commit') {
if (!$in_msg) {
$in_msg = 1 if (/^\s*$/);
} else {
- $log_msg{msg} .= $_;
print $msg $_ or croak $!;
}
}
my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
system($editor, $commit_msg);
}
+
+ # file_to_s removes all trailing newlines, so just use chomp() here:
+ open $msg, '<', $commit_msg or croak $!;
+ { local $/; chomp($log_msg{msg} = <$msg>); }
+ close $msg or croak $!;
+
+ my ($oneline) = ($log_msg{msg} =~ /([^\n\r]+)/);
+ print "Committing $commit: $oneline\n";
+
my @ci_output = safe_qx(qw(svn commit -F),$commit_msg);
my ($committed) = grep(/^Committed revision \d+\./,@ci_output);
unlink $commit_msg;
my $uuid = $info->{'Repository UUID'};
defined $uuid or croak "Unable to get Repository UUID\n";
+ map_tree_joins() if (@_branch_from && !%tree_map);
+
# commit parents can be conditionally bound to a particular
# svn revision via: "svn_revno=commit_sha1", filter them out here:
my @exec_parents;
git_addremove();
chomp(my $tree = `git-write-tree`);
croak if $?;
+ if (exists $tree_map{$tree}) {
+ my %seen_parent = map { $_ => 1 } @exec_parents;
+ foreach (@{$tree_map{$tree}}) {
+ # MAXPARENT is defined to 16 in commit-tree.c:
+ if ($seen_parent{$_} || @exec_parents > 16) {
+ next;
+ }
+ push @exec_parents, $_;
+ $seen_parent{$_} = 1;
+ }
+ }
my $msg_fh = IO::File->new_tmpfile or croak $!;
print $msg_fh $log_msg->{msg}, "\ngit-svn-id: ",
"$SVN_URL\@$log_msg->{revision}",
}
}
+# fills %tree_map with a reverse mapping of trees to commits. Useful
+# for finding parents to commit on.
+sub map_tree_joins {
+ foreach my $br (@_branch_from) {
+ my $pid = open my $pipe, '-|';
+ defined $pid or croak $!;
+ if ($pid == 0) {
+ exec(qw(git-rev-list --pretty=raw), $br) or croak $?;
+ }
+ while (<$pipe>) {
+ if (/^commit ($sha1)$/o) {
+ my $commit = $1;
+ my ($tree) = (<$pipe> =~ /^tree ($sha1)$/o);
+ unless (defined $tree) {
+ die "Failed to parse commit $commit\n";
+ }
+ push @{$tree_map{$tree}}, $commit;
+ }
+ }
+ close $pipe or croak $?;
+ }
+}
+
__END__
Data structures:
mode_a => first column of diff-index output, no leading ':',
mode_b => second column of diff-index output,
sha1_b => sha1sum of the final blob,
- chg => change type [MCRAD],
+ chg => change type [MCRADT],
file_a => original file name of a file (iff chg is 'C' or 'R')
file_b => new/current file name of a file (any chg)
}