$GIT_SVN_INDEX $GIT_SVN
$GIT_DIR $REV_DIR/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '0.9.1';
+$VERSION = '0.10.0';
$GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git";
$GIT_SVN = $ENV{GIT_SVN_ID} || 'git-svn';
$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index";
use File::Path qw/mkpath/;
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
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 ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
- $_find_copies_harder, $_l, $_version);
+ $_find_copies_harder, $_l, $_version, $_upgrade);
GetOptions( 'revision|r=s' => \$_revision,
'no-ignore-externals' => \$_no_ignore_ext,
'stdin|' => \$_stdin,
'edit|e' => \$_edit,
'rmdir' => \$_rmdir,
+ 'upgrade' => \$_upgrade,
'help|H|h' => \$_help,
'find-copies-harder' => \$_find_copies_harder,
'l=i' => \$_l,
fetch => [ \&fetch, "Download new revisions from SVN" ],
init => [ \&init, "Initialize and fetch (import)"],
commit => [ \&commit, "Commit git revisions to SVN" ],
+ 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings" ],
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)" ],
help => [ \&usage, "Show help" ],
);
$SVN_URL = shift or undef;
my $repo_uuid;
my $newest_rev = 0;
+ if ($_upgrade) {
+ sys('git-update-ref',"refs/remotes/$GIT_SVN","$GIT_SVN-HEAD");
+ } else {
+ check_upgrade_needed();
+ }
my $pid = open(my $rev_list,'-|');
defined $pid or croak $!;
if ($pid == 0) {
- exec("git-rev-list","$GIT_SVN-HEAD") or croak $!;
+ exec("git-rev-list","refs/remotes/$GIT_SVN") or croak $!;
}
- my $first;
+ my $latest;
while (<$rev_list>) {
chomp;
my $c = $_;
"$c, $id\n";
}
}
+
+ # if we merged or otherwise started elsewhere, this is
+ # how we break out of it
+ next if (defined $repo_uuid && ($uuid ne $repo_uuid));
+ next if (defined $SVN_URL && ($url ne $SVN_URL));
+
print "r$rev = $c\n";
- unless (defined $first) {
+ unless (defined $latest) {
if (!$SVN_URL && !$url) {
croak "SVN repository location required: $url\n";
}
$SVN_URL ||= $url;
- $repo_uuid = setup_git_svn();
- $first = $rev;
- }
- if ($uuid ne $repo_uuid) {
- croak "Repository UUIDs do not match!\ngot: $uuid\n",
- "expected: $repo_uuid\n";
+ $repo_uuid ||= setup_git_svn();
+ $latest = $rev;
}
assert_revision_eq_or_unknown($rev, $c);
sys('git-update-ref',"$GIT_SVN/revs/$rev",$c);
}
close $rev_list or croak $?;
if (!chdir $SVN_WC) {
- my @svn_co = ('svn','co',"-r$first");
+ my @svn_co = ('svn','co',"-r$latest");
push @svn_co, '--ignore-externals' unless $_no_ignore_ext;
sys(@svn_co, $SVN_URL, $SVN_WC);
chdir $SVN_WC or croak $!;
exec('git-write-tree');
}
waitpid $pid, 0;
+
+ if ($_upgrade) {
+ print STDERR <<"";
+Keeping deprecated refs/head/$GIT_SVN-HEAD for now. Please remove it
+when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN
+
+ }
}
sub init {
sub fetch {
my (@parents) = @_;
+ check_upgrade_needed();
$SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url");
my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL);
unless ($_revision) {
sub commit {
my (@commits) = @_;
+ check_upgrade_needed();
if ($_stdin || !@commits) {
print "Reading from stdin...\n";
@commits = ();
}
+sub show_ignore {
+ require File::Find or die $!;
+ my $exclude_file = "$GIT_DIR/info/exclude";
+ open my $fh, '<', $exclude_file or croak $!;
+ chomp(my @excludes = (<$fh>));
+ close $fh or croak $!;
+
+ $SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url");
+ chdir $SVN_WC or croak $!;
+ my %ign;
+ File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){
+ s#^\./##;
+ @{$ign{$_}} = safe_qx(qw(svn propget svn:ignore),$_);
+ }}, no_chdir=>1},'.');
+
+ print "\n# /\n";
+ foreach (@{$ign{'.'}}) { print '/',$_ if /\S/ }
+ delete $ign{'.'};
+ foreach my $i (sort keys %ign) {
+ print "\n# ",$i,"\n";
+ foreach (@{$ign{$i}}) { print '/',$i,'/',$_ if /\S/ }
+ }
+}
+
########################### utility functions #########################
sub setup_git_svn {
sub svn_commit_tree {
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 $!;
chomp(my $type = `git-cat-file -t $commit`);
if (!$in_msg) {
$in_msg = 1 if (/^\s*$/);
} else {
+ $log_msg{msg} .= $_;
print $msg $_ or croak $!;
}
}
join("\n",@ci_output),"\n";
my ($rev_committed) = ($committed =~ /^Committed revision (\d+)\./);
- # resync immediately
- my @svn_up = (qw(svn up), "-r$svn_rev");
+ my @svn_up = qw(svn up);
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
+ if ($rev_committed == ($svn_rev + 1)) {
+ push @svn_up, "-r$rev_committed";
+ sys(@svn_up);
+ my $info = svn_info('.');
+ my $date = $info->{'Last Changed Date'} or die "Missing date\n";
+ if ($info->{'Last Changed Rev'} != $rev_committed) {
+ croak "$info->{'Last Changed Rev'} != $rev_committed\n"
+ }
+ 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)
+ or croak "Failed to parse date: $date\n";
+ $log_msg{date} = "$tz $Y-$m-$d $H:$M:$S";
+ $log_msg{author} = $info->{'Last Changed Author'};
+ $log_msg{revision} = $rev_committed;
+ $log_msg{msg} .= "\n";
+ my $parent = file_to_s("$REV_DIR/$svn_rev");
+ git_commit(\%log_msg, $parent, $commit);
+ return $rev_committed;
+ }
+ # resync immediately
+ push @svn_up, "-r$svn_rev";
sys(@svn_up);
return fetch("$rev_committed=$commit")->{revision};
}
# only single-lines seem to exist in svn info output
while (<$info_fh>) {
chomp $_;
- if (m#^([^:]+)\s*:\s*(\S*)$#) {
+ if (m#^([^:]+)\s*:\s*(\S.*)$#) {
$ret->{$1} = $2;
push @{$ret->{-order}}, $1;
}
if ($commit !~ /^$sha1$/o) {
croak "Failed to commit, invalid sha1: $commit\n";
}
- my @update_ref = ('git-update-ref',"refs/heads/$GIT_SVN-HEAD",$commit);
+ my @update_ref = ('git-update-ref',"refs/remotes/$GIT_SVN",$commit);
if (my $primary_parent = shift @exec_parents) {
push @update_ref, $primary_parent;
}
$_no_ignore_ext = 1;
}
}
+
+sub check_upgrade_needed {
+ my $old = eval {
+ my $pid = open my $child, '-|';
+ defined $pid or croak $!;
+ if ($pid == 0) {
+ close STDERR;
+ exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $?;
+ }
+ my @ret = (<$child>);
+ close $child or croak $?;
+ die $? if $?; # just in case close didn't error out
+ return wantarray ? @ret : join('',@ret);
+ };
+ return unless $old;
+ my $head = eval { safe_qx('git-rev-parse',"refs/remotes/$GIT_SVN") };
+ if ($@ || !$head) {
+ print STDERR "Please run: $0 rebuild --upgrade\n";
+ exit 1;
+ }
+}
+
__END__
Data structures: