From 8d92f24852420c425f3ee499d3c3be01be4dc15c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 21 Nov 2007 00:57:33 -0800 Subject: [PATCH 1/5] t9106: fix a race condition that caused svn to miss modifications carbonated beverage noticed this test was occasionally failing. Signed-off-by: Eric Wong --- t/t9106-git-svn-dcommit-clobber-series.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh index d59acc8d1a..745254665d 100755 --- a/t/t9106-git-svn-dcommit-clobber-series.sh +++ b/t/t9106-git-svn-dcommit-clobber-series.sh @@ -22,6 +22,7 @@ test_expect_success '(supposedly) non-conflicting change from SVN' " cd tmp && perl -i -p -e 's/^58\$/5588/' file && perl -i -p -e 's/^61\$/6611/' file && + poke file && test x\"\`sed -n -e 58p < file\`\" = x5588 && test x\"\`sed -n -e 61p < file\`\" = x6611 && svn commit -m '58 => 5588, 61 => 6611' && From b2b3ada7fc46f6e43f065718cd64940811cd50b3 Mon Sep 17 00:00:00 2001 From: "David D. Kilzer" Date: Tue, 20 Nov 2007 22:43:17 -0800 Subject: [PATCH 2/5] git-svn: extract reusable code into utility functions Extacted canonicalize_path() in the main package. Created new Git::SVN::Util package with an md5sum() function. A new package was created so that Digest::MD5 did not have to be loaded in the main package. Replaced code in the SVN::Git::Editor and SVN::Git::Fetcher packages with calls to md5sum(). Extracted the format_svn_date(), parse_git_date() and set_local_timezone() functions within the Git::SVN::Log package. Signed-off-by: David D. Kilzer Acked-by: Eric Wong --- git-svn.perl | 96 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 32 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 5b1deeab94..98c980fa8f 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -48,7 +48,8 @@ BEGIN { foreach (qw/command command_oneline command_noisy command_output_pipe command_input_pipe command_close_pipe/) { for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher - Git::SVN::Migration Git::SVN::Log Git::SVN), + Git::SVN::Migration Git::SVN::Log Git::SVN + Git::SVN::Util), __PACKAGE__) { *{"${package}::$_"} = \&{"Git::$_"}; } @@ -583,6 +584,17 @@ sub cmd_create_ignore { }); } +sub canonicalize_path { + my ($path) = @_; + # File::Spec->canonpath doesn't collapse x/../y into y (for a + # good reason), so let's do this manually. + $path =~ s#/+#/#g; + $path =~ s#/\.(?:/|$)#/#g; + $path =~ s#/[^/]+/\.\.##g; + $path =~ s#/$##g; + return $path; +} + # get_svnprops(PATH) # ------------------ # Helper for cmd_propget and cmd_proplist below. @@ -600,12 +612,7 @@ sub get_svnprops { # canonicalize the path (otherwise libsvn will abort or fail to # find the file) - # File::Spec->canonpath doesn't collapse x/../y into y (for a - # good reason), so let's do this manually. - $path =~ s#/+#/#g; - $path =~ s#/\.(?:/|$)#/#g; - $path =~ s#/[^/]+/\.\.##g; - $path =~ s#/$##g; + $path = canonicalize_path($path); my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); my $props; @@ -1043,6 +1050,27 @@ sub linearize_history { (\@linear_refs, \%parents); } +package Git::SVN::Util; +use strict; +use warnings; +use Digest::MD5; + +sub md5sum { + my $arg = shift; + my $ref = ref $arg; + my $md5 = Digest::MD5->new(); + if ($ref eq 'GLOB' || $ref eq 'IO::File') { + $md5->addfile($arg) or croak $!; + } elsif ($ref eq 'SCALAR') { + $md5->add($$arg) or croak $!; + } elsif (!$ref) { + $md5->add($arg) or croak $!; + } else { + ::fatal "Can't provide MD5 hash for unknown ref type: '", $ref, "'"; + } + return $md5->hexdigest(); +} + package Git::SVN; use strict; use warnings; @@ -2610,7 +2638,6 @@ use strict; use warnings; use Carp qw/croak/; use IO::File qw//; -use Digest::MD5; # file baton members: path, mode_a, mode_b, pool, fh, blob, base sub new { @@ -2762,9 +2789,7 @@ sub apply_textdelta { if (defined $exp) { seek $base, 0, 0 or croak $!; - my $md5 = Digest::MD5->new; - $md5->addfile($base); - my $got = $md5->hexdigest; + my $got = Git::SVN::Util::md5sum($base); die "Checksum mismatch: $fb->{path} $fb->{blob}\n", "expected: $exp\n", " got: $got\n" if ($got ne $exp); @@ -2783,9 +2808,7 @@ sub close_file { if (my $fh = $fb->{fh}) { if (defined $exp) { seek($fh, 0, 0) or croak $!; - my $md5 = Digest::MD5->new; - $md5->addfile($fh); - my $got = $md5->hexdigest; + my $got = Git::SVN::Util::md5sum($fh); if ($got ne $exp) { die "Checksum mismatch: $path\n", "expected: $exp\n got: $got\n"; @@ -2837,7 +2860,6 @@ use strict; use warnings; use Carp qw/croak/; use IO::File; -use Digest::MD5; sub new { my ($class, $opts) = @_; @@ -3141,11 +3163,9 @@ sub chg_file { $fh->flush == 0 or croak $!; seek $fh, 0, 0 or croak $!; - my $md5 = Digest::MD5->new; - $md5->addfile($fh) or croak $!; + my $exp = Git::SVN::Util::md5sum($fh); seek $fh, 0, 0 or croak $!; - my $exp = $md5->hexdigest; my $pool = SVN::Pool->new; my $atd = $self->apply_textdelta($fbat, undef, $pool); my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool); @@ -3859,6 +3879,29 @@ sub run_pager { exec $pager or ::fatal "Can't run pager: $! ($pager)"; } +sub format_svn_date { + return strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", localtime(shift)); +} + +sub parse_git_date { + my ($t, $tz) = @_; + # Date::Parse isn't in the standard Perl distro :( + if ($tz =~ s/^\+//) { + $t += tz_to_s_offset($tz); + } elsif ($tz =~ s/^\-//) { + $t -= tz_to_s_offset($tz); + } + return $t; +} + +sub set_local_timezone { + if (defined $TZ) { + $ENV{TZ} = $TZ; + } else { + delete $ENV{TZ}; + } +} + sub tz_to_s_offset { my ($tz) = @_; $tz =~ s/(\d\d)$//; @@ -3879,13 +3922,7 @@ sub get_author_info { $dest->{t} = $t; $dest->{tz} = $tz; $dest->{a} = $au; - # Date::Parse isn't in the standard Perl distro :( - if ($tz =~ s/^\+//) { - $t += tz_to_s_offset($tz); - } elsif ($tz =~ s/^\-//) { - $t -= tz_to_s_offset($tz); - } - $dest->{t_utc} = $t; + $dest->{t_utc} = parse_git_date($t, $tz); } sub process_commit { @@ -3939,8 +3976,7 @@ sub show_commit_normal { my ($c) = @_; print commit_log_separator, "r$c->{r} | "; print "$c->{c} | " if $show_commit; - print "$c->{a} | ", strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", - localtime($c->{t_utc})), ' | '; + print "$c->{a} | ", format_svn_date($c->{t_utc}), ' | '; my $nr_line = 0; if (my $l = $c->{l}) { @@ -3980,11 +4016,7 @@ sub cmd_show_log { my (@args) = @_; my ($r_min, $r_max); my $r_last = -1; # prevent dupes - if (defined $TZ) { - $ENV{TZ} = $TZ; - } else { - delete $ENV{TZ}; - } + set_local_timezone(); if (defined $::_revision) { if ($::_revision =~ /^(\d+):(\d+)$/) { ($r_min, $r_max) = ($1, $2); From e6fefa926de3fcc1f0424e1b901347379ed51935 Mon Sep 17 00:00:00 2001 From: "David D. Kilzer" Date: Wed, 21 Nov 2007 11:57:18 -0800 Subject: [PATCH 3/5] git-svn info: implement info command Implement "git-svn info" for files and directories based on the "svn info" command. Note that the -r/--revision argument is not supported yet. Added 18 tests in t/t9119-git-svn-info.sh. [ew: small fix to work without arguments on all working directories] Signed-off-by: David D. Kilzer Acked-by: Eric Wong --- Documentation/git-svn.txt | 5 + git-svn.perl | 133 ++++++++++++++++++ t/t9119-git-svn-info.sh | 275 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 413 insertions(+) create mode 100644 t/t9119-git-svn-info.sh diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 488e4b1caf..c3fc878d55 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -193,6 +193,11 @@ Any other arguments are passed directly to `git log' repository (that has been init-ed with git-svn). The -r option is required for this. +'info':: + Shows information about a file or directory similar to what + `svn info' provides. Does not currently support a -r/--revision + argument. + -- OPTIONS diff --git a/git-svn.perl b/git-svn.perl index 98c980fa8f..fd10361045 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -178,6 +178,10 @@ my %cmd = ( 'file|F=s' => \$_file, 'revision|r=s' => \$_revision, %cmt_opts } ], + 'info' => [ \&cmd_info, + "Show info about the latest SVN revision + on the current branch", + { } ], ); my $cmd; @@ -586,12 +590,18 @@ sub cmd_create_ignore { sub canonicalize_path { my ($path) = @_; + my $dot_slash_added = 0; + if (substr($path, 0, 1) ne "/") { + $path = "./" . $path; + $dot_slash_added = 1; + } # File::Spec->canonpath doesn't collapse x/../y into y (for a # good reason), so let's do this manually. $path =~ s#/+#/#g; $path =~ s#/\.(?:/|$)#/#g; $path =~ s#/[^/]+/\.\.##g; $path =~ s#/$##g; + $path =~ s#^\./## if $dot_slash_added; return $path; } @@ -743,6 +753,104 @@ sub cmd_commit_diff { } } +sub cmd_info { + my $path = canonicalize_path(shift or "."); + unless (scalar(@_) == 0) { + die "Too many arguments specified\n"; + } + + my ($file_type, $diff_status) = find_file_type_and_diff_status($path); + + if (!$file_type && !$diff_status) { + print STDERR "$path: (Not a versioned resource)\n\n"; + return; + } + + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); + unless ($gs) { + die "Unable to determine upstream SVN information from ", + "working tree history\n"; + } + my $full_url = $url . ($path eq "." ? "" : "/$path"); + + my $result = "Path: $path\n"; + $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir"; + $result .= "URL: " . $full_url . "\n"; + + my $repos_root = $gs->ra->{repos_root}; + Git::SVN::remove_username($repos_root); + $result .= "Repository Root: $repos_root\n"; + $result .= "Repository UUID: $uuid\n" unless $diff_status eq "A"; + $result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n"; + + $result .= "Node Kind: " . + ($file_type eq "dir" ? "directory" : "file") . "\n"; + + my $schedule = $diff_status eq "A" + ? "add" + : ($diff_status eq "D" ? "delete" : "normal"); + $result .= "Schedule: $schedule\n"; + + if ($diff_status eq "A") { + print $result, "\n"; + return; + } + + my ($lc_author, $lc_rev, $lc_date_utc); + my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path); + my $log = command_output_pipe(@args); + my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/; + while (<$log>) { + if (/^${esc_color}author (.+) <[^>]+> (\d+) ([\-\+]?\d+)$/o) { + $lc_author = $1; + $lc_date_utc = Git::SVN::Log::parse_git_date($2, $3); + } elsif (/^${esc_color} (git-svn-id:.+)$/o) { + (undef, $lc_rev, undef) = ::extract_metadata($1); + } + } + close $log; + + Git::SVN::Log::set_local_timezone(); + + $result .= "Last Changed Author: $lc_author\n"; + $result .= "Last Changed Rev: $lc_rev\n"; + $result .= "Last Changed Date: " . + Git::SVN::Log::format_svn_date($lc_date_utc) . "\n"; + + if ($file_type ne "dir") { + my $text_last_updated_date = + ($diff_status eq "D" ? $lc_date_utc : (stat $path)[9]); + $result .= + "Text Last Updated: " . + Git::SVN::Log::format_svn_date($text_last_updated_date) . + "\n"; + my $checksum; + if ($diff_status eq "D") { + my ($fh, $ctx) = + command_output_pipe(qw(cat-file blob), "HEAD:$path"); + if ($file_type eq "link") { + my $file_name = <$fh>; + $checksum = Git::SVN::Util::md5sum("link $file_name"); + } else { + $checksum = Git::SVN::Util::md5sum($fh); + } + command_close_pipe($fh, $ctx); + } elsif ($file_type eq "link") { + my $file_name = + command(qw(cat-file blob), "HEAD:$path"); + $checksum = + Git::SVN::Util::md5sum("link " . $file_name); + } else { + open FILE, "<", $path or die $!; + $checksum = Git::SVN::Util::md5sum(\*FILE); + close FILE or die $!; + } + $result .= "Checksum: " . $checksum . "\n"; + } + + print $result, "\n"; +} + ########################### utility functions ######################### sub rebase_cmd { @@ -1050,6 +1158,31 @@ sub linearize_history { (\@linear_refs, \%parents); } +sub find_file_type_and_diff_status { + my ($path) = @_; + return ('dir', '') if $path eq '.'; + + my $diff_output = + command_oneline(qw(diff --cached --name-status --), $path) || ""; + my $diff_status = (split(' ', $diff_output))[0] || ""; + + my $ls_tree = command_oneline(qw(ls-tree HEAD), $path) || ""; + + return (undef, undef) if !$diff_status && !$ls_tree; + + if ($diff_status eq "A") { + return ("link", $diff_status) if -l $path; + return ("dir", $diff_status) if -d $path; + return ("file", $diff_status); + } + + my $mode = (split(' ', $ls_tree))[0] || ""; + + return ("link", $diff_status) if $mode eq "120000"; + return ("dir", $diff_status) if $mode eq "040000"; + return ("file", $diff_status); +} + package Git::SVN::Util; use strict; use warnings; diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh new file mode 100644 index 0000000000..fbde8c81b5 --- /dev/null +++ b/t/t9119-git-svn-info.sh @@ -0,0 +1,275 @@ +#!/bin/sh +# +# Copyright (c) 2007 David D. Kilzer + +test_description='git-svn info' + +. ./lib-git-svn.sh + +ptouch() { + perl -w -e ' + use strict; + die "ptouch requires exactly 2 arguments" if @ARGV != 2; + die "$ARGV[0] does not exist" if ! -e $ARGV[0]; + my @s = stat $ARGV[0]; + utime $s[8], $s[9], $ARGV[1]; + ' "$1" "$2" +} + +test_expect_success 'setup repository and import' " + mkdir info && + cd info && + echo FIRST > A && + echo one > file && + ln -s file symlink-file && + mkdir directory && + touch directory/.placeholder && + ln -s directory symlink-directory && + svn import -m 'initial' . $svnrepo && + cd .. && + mkdir gitwc && + cd gitwc && + git-svn init $svnrepo && + git-svn fetch && + cd .. && + svn co $svnrepo svnwc && + ptouch svnwc/file gitwc/file && + ptouch svnwc/directory gitwc/directory && + ptouch svnwc/symlink-file gitwc/symlink-file && + ptouch svnwc/symlink-directory gitwc/symlink-directory + " + +test_expect_success 'info' " + (cd svnwc; svn info) > expected.info && + (cd gitwc; git-svn info) > actual.info && + git-diff expected.info actual.info + " + +test_expect_success 'info .' " + (cd svnwc; svn info .) > expected.info-dot && + (cd gitwc; git-svn info .) > actual.info-dot && + git-diff expected.info-dot actual.info-dot + " + +test_expect_success 'info file' " + (cd svnwc; svn info file) > expected.info-file && + (cd gitwc; git-svn info file) > actual.info-file && + git-diff expected.info-file actual.info-file + " + +test_expect_success 'info directory' " + (cd svnwc; svn info directory) > expected.info-directory && + (cd gitwc; git-svn info directory) > actual.info-directory && + git-diff expected.info-directory actual.info-directory + " + +test_expect_success 'info symlink-file' " + (cd svnwc; svn info symlink-file) > expected.info-symlink-file && + (cd gitwc; git-svn info symlink-file) > actual.info-symlink-file && + git-diff expected.info-symlink-file actual.info-symlink-file + " + +test_expect_success 'info symlink-directory' " + (cd svnwc; svn info symlink-directory) \ + > expected.info-symlink-directory && + (cd gitwc; git-svn info symlink-directory) \ + > actual.info-symlink-directory && + git-diff expected.info-symlink-directory actual.info-symlink-directory + " + +test_expect_success 'info added-file' " + echo two > gitwc/added-file && + cd gitwc && + git add added-file && + cd .. && + cp gitwc/added-file svnwc/added-file && + ptouch gitwc/added-file svnwc/added-file && + cd svnwc && + svn add added-file > /dev/null && + cd .. && + (cd svnwc; svn info added-file) > expected.info-added-file && + (cd gitwc; git-svn info added-file) > actual.info-added-file && + git-diff expected.info-added-file actual.info-added-file + " + +test_expect_success 'info added-directory' " + mkdir gitwc/added-directory svnwc/added-directory && + ptouch gitwc/added-directory svnwc/added-directory && + touch gitwc/added-directory/.placeholder && + cd svnwc && + svn add added-directory > /dev/null && + cd .. && + cd gitwc && + git add added-directory && + cd .. && + (cd svnwc; svn info added-directory) \ + > expected.info-added-directory && + (cd gitwc; git-svn info added-directory) \ + > actual.info-added-directory && + git-diff expected.info-added-directory actual.info-added-directory + " + +test_expect_success 'info added-symlink-file' " + cd gitwc && + ln -s added-file added-symlink-file && + git add added-symlink-file && + cd .. && + cd svnwc && + ln -s added-file added-symlink-file && + svn add added-symlink-file > /dev/null && + cd .. && + ptouch gitwc/added-symlink-file svnwc/added-symlink-file && + (cd svnwc; svn info added-symlink-file) \ + > expected.info-added-symlink-file && + (cd gitwc; git-svn info added-symlink-file) \ + > actual.info-added-symlink-file && + git-diff expected.info-added-symlink-file \ + actual.info-added-symlink-file + " + +test_expect_success 'info added-symlink-directory' " + cd gitwc && + ln -s added-directory added-symlink-directory && + git add added-symlink-directory && + cd .. && + cd svnwc && + ln -s added-directory added-symlink-directory && + svn add added-symlink-directory > /dev/null && + cd .. && + ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory && + (cd svnwc; svn info added-symlink-directory) \ + > expected.info-added-symlink-directory && + (cd gitwc; git-svn info added-symlink-directory) \ + > actual.info-added-symlink-directory && + git-diff expected.info-added-symlink-directory \ + actual.info-added-symlink-directory + " + +# The next few tests replace the "Text Last Updated" value with a +# placeholder since git doesn't have a way to know the date that a +# now-deleted file was last checked out locally. Internally it +# simply reuses the Last Changed Date. + +test_expect_success 'info deleted-file' " + cd gitwc && + git rm -f file > /dev/null && + cd .. && + cd svnwc && + svn rm --force file > /dev/null && + cd .. && + (cd svnwc; svn info file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-file && + (cd gitwc; git-svn info file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-file && + git-diff expected.info-deleted-file actual.info-deleted-file + " + +test_expect_success 'info deleted-directory' " + cd gitwc && + git rm -r -f directory > /dev/null && + cd .. && + cd svnwc && + svn rm --force directory > /dev/null && + cd .. && + (cd svnwc; svn info directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-directory && + (cd gitwc; git-svn info directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-directory && + git-diff expected.info-deleted-directory actual.info-deleted-directory + " + +test_expect_success 'info deleted-symlink-file' " + cd gitwc && + git rm -f symlink-file > /dev/null && + cd .. && + cd svnwc && + svn rm --force symlink-file > /dev/null && + cd .. && + (cd svnwc; svn info symlink-file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-symlink-file && + (cd gitwc; git-svn info symlink-file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-symlink-file && + git-diff expected.info-deleted-symlink-file \ + actual.info-deleted-symlink-file + " + +test_expect_success 'info deleted-symlink-directory' " + cd gitwc && + git rm -f symlink-directory > /dev/null && + cd .. && + cd svnwc && + svn rm --force symlink-directory > /dev/null && + cd .. && + (cd svnwc; svn info symlink-directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-symlink-directory && + (cd gitwc; git-svn info symlink-directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-symlink-directory && + git-diff expected.info-deleted-symlink-directory \ + actual.info-deleted-symlink-directory + " + +# NOTE: git does not have the concept of replaced objects, +# so we can't test for files in that state. + +test_expect_success 'info unknown-file' " + echo two > gitwc/unknown-file && + cp gitwc/unknown-file svnwc/unknown-file && + ptouch gitwc/unknown-file svnwc/unknown-file && + (cd svnwc; svn info unknown-file) 2> expected.info-unknown-file && + (cd gitwc; git-svn info unknown-file) 2> actual.info-unknown-file && + git-diff expected.info-unknown-file actual.info-unknown-file + " + +test_expect_success 'info unknown-directory' " + mkdir gitwc/unknown-directory svnwc/unknown-directory && + ptouch gitwc/unknown-directory svnwc/unknown-directory && + touch gitwc/unknown-directory/.placeholder && + (cd svnwc; svn info unknown-directory) \ + 2> expected.info-unknown-directory && + (cd gitwc; git-svn info unknown-directory) \ + 2> actual.info-unknown-directory && + git-diff expected.info-unknown-directory actual.info-unknown-directory + " + +test_expect_success 'info unknown-symlink-file' " + cd gitwc && + ln -s unknown-file unknown-symlink-file && + cd .. && + cd svnwc && + ln -s unknown-file unknown-symlink-file && + cd .. && + ptouch gitwc/unknown-symlink-file svnwc/unknown-symlink-file && + (cd svnwc; svn info unknown-symlink-file) \ + 2> expected.info-unknown-symlink-file && + (cd gitwc; git-svn info unknown-symlink-file) \ + 2> actual.info-unknown-symlink-file && + git-diff expected.info-unknown-symlink-file \ + actual.info-unknown-symlink-file + " + +test_expect_success 'info unknown-symlink-directory' " + cd gitwc && + ln -s unknown-directory unknown-symlink-directory && + cd .. && + cd svnwc && + ln -s unknown-directory unknown-symlink-directory && + cd .. && + ptouch gitwc/unknown-symlink-directory \ + svnwc/unknown-symlink-directory && + (cd svnwc; svn info unknown-symlink-directory) \ + 2> expected.info-unknown-symlink-directory && + (cd gitwc; git-svn info unknown-symlink-directory) \ + 2> actual.info-unknown-symlink-directory && + git-diff expected.info-unknown-symlink-directory \ + actual.info-unknown-symlink-directory + " + +test_done From 8b014d7157d29ce76b0f631e19c6e2ce9aeb2366 Mon Sep 17 00:00:00 2001 From: "David D. Kilzer" Date: Wed, 21 Nov 2007 11:57:19 -0800 Subject: [PATCH 4/5] git-svn: info --url [path] Return the svn URL for the given path, or return the svn repository URL if no path is given. Added 18 tests to t/t9119-git-svn-info.sh. Signed-off-by: David D. Kilzer Acked-by: Eric Wong --- Documentation/git-svn.txt | 3 +- git-svn.perl | 9 +++- t/t9119-git-svn-info.sh | 93 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index c3fc878d55..918a9928b1 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -196,7 +196,8 @@ Any other arguments are passed directly to `git log' 'info':: Shows information about a file or directory similar to what `svn info' provides. Does not currently support a -r/--revision - argument. + argument. Use the --url option to output only the value of the + 'URL:' field. -- diff --git a/git-svn.perl b/git-svn.perl index fd10361045..7d86870ee4 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -65,7 +65,7 @@ my ($_stdin, $_help, $_edit, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_merge, $_strategy, $_dry_run, $_local, - $_prefix, $_no_checkout, $_verbose); + $_prefix, $_no_checkout, $_url, $_verbose); $Git::SVN::_follow_parent = 1; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, @@ -181,7 +181,7 @@ my %cmd = ( 'info' => [ \&cmd_info, "Show info about the latest SVN revision on the current branch", - { } ], + { 'url' => \$_url, } ], ); my $cmd; @@ -773,6 +773,11 @@ sub cmd_info { } my $full_url = $url . ($path eq "." ? "" : "/$path"); + if ($_url) { + print $full_url, "\n"; + return; + } + my $result = "Path: $path\n"; $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir"; $result .= "URL: " . $full_url . "\n"; diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh index fbde8c81b5..439bd93c88 100644 --- a/t/t9119-git-svn-info.sh +++ b/t/t9119-git-svn-info.sh @@ -45,30 +45,51 @@ test_expect_success 'info' " git-diff expected.info actual.info " +test_expect_success 'info --url' ' + test $(cd gitwc; git-svn info --url) = $svnrepo + ' + test_expect_success 'info .' " (cd svnwc; svn info .) > expected.info-dot && (cd gitwc; git-svn info .) > actual.info-dot && git-diff expected.info-dot actual.info-dot " +test_expect_success 'info --url .' ' + test $(cd gitwc; git-svn info --url .) = $svnrepo + ' + test_expect_success 'info file' " (cd svnwc; svn info file) > expected.info-file && (cd gitwc; git-svn info file) > actual.info-file && git-diff expected.info-file actual.info-file " +test_expect_success 'info --url file' ' + test $(cd gitwc; git-svn info --url file) = "$svnrepo/file" + ' + test_expect_success 'info directory' " (cd svnwc; svn info directory) > expected.info-directory && (cd gitwc; git-svn info directory) > actual.info-directory && git-diff expected.info-directory actual.info-directory " +test_expect_success 'info --url directory' ' + test $(cd gitwc; git-svn info --url directory) = "$svnrepo/directory" + ' + test_expect_success 'info symlink-file' " (cd svnwc; svn info symlink-file) > expected.info-symlink-file && (cd gitwc; git-svn info symlink-file) > actual.info-symlink-file && git-diff expected.info-symlink-file actual.info-symlink-file " +test_expect_success 'info --url symlink-file' ' + test $(cd gitwc; git-svn info --url symlink-file) \ + = "$svnrepo/symlink-file" + ' + test_expect_success 'info symlink-directory' " (cd svnwc; svn info symlink-directory) \ > expected.info-symlink-directory && @@ -77,6 +98,11 @@ test_expect_success 'info symlink-directory' " git-diff expected.info-symlink-directory actual.info-symlink-directory " +test_expect_success 'info --url symlink-directory' ' + test $(cd gitwc; git-svn info --url symlink-directory) \ + = "$svnrepo/symlink-directory" + ' + test_expect_success 'info added-file' " echo two > gitwc/added-file && cd gitwc && @@ -92,6 +118,11 @@ test_expect_success 'info added-file' " git-diff expected.info-added-file actual.info-added-file " +test_expect_success 'info --url added-file' ' + test $(cd gitwc; git-svn info --url added-file) \ + = "$svnrepo/added-file" + ' + test_expect_success 'info added-directory' " mkdir gitwc/added-directory svnwc/added-directory && ptouch gitwc/added-directory svnwc/added-directory && @@ -109,6 +140,11 @@ test_expect_success 'info added-directory' " git-diff expected.info-added-directory actual.info-added-directory " +test_expect_success 'info --url added-directory' ' + test $(cd gitwc; git-svn info --url added-directory) \ + = "$svnrepo/added-directory" + ' + test_expect_success 'info added-symlink-file' " cd gitwc && ln -s added-file added-symlink-file && @@ -127,6 +163,11 @@ test_expect_success 'info added-symlink-file' " actual.info-added-symlink-file " +test_expect_success 'info --url added-symlink-file' ' + test $(cd gitwc; git-svn info --url added-symlink-file) \ + = "$svnrepo/added-symlink-file" + ' + test_expect_success 'info added-symlink-directory' " cd gitwc && ln -s added-directory added-symlink-directory && @@ -145,6 +186,11 @@ test_expect_success 'info added-symlink-directory' " actual.info-added-symlink-directory " +test_expect_success 'info --url added-symlink-directory' ' + test $(cd gitwc; git-svn info --url added-symlink-directory) \ + = "$svnrepo/added-symlink-directory" + ' + # The next few tests replace the "Text Last Updated" value with a # placeholder since git doesn't have a way to know the date that a # now-deleted file was last checked out locally. Internally it @@ -166,6 +212,11 @@ test_expect_success 'info deleted-file' " git-diff expected.info-deleted-file actual.info-deleted-file " +test_expect_success 'info --url file (deleted)' ' + test $(cd gitwc; git-svn info --url file) \ + = "$svnrepo/file" + ' + test_expect_success 'info deleted-directory' " cd gitwc && git rm -r -f directory > /dev/null && @@ -182,6 +233,11 @@ test_expect_success 'info deleted-directory' " git-diff expected.info-deleted-directory actual.info-deleted-directory " +test_expect_success 'info --url directory (deleted)' ' + test $(cd gitwc; git-svn info --url directory) \ + = "$svnrepo/directory" + ' + test_expect_success 'info deleted-symlink-file' " cd gitwc && git rm -f symlink-file > /dev/null && @@ -199,6 +255,11 @@ test_expect_success 'info deleted-symlink-file' " actual.info-deleted-symlink-file " +test_expect_success 'info --url symlink-file (deleted)' ' + test $(cd gitwc; git-svn info --url symlink-file) \ + = "$svnrepo/symlink-file" + ' + test_expect_success 'info deleted-symlink-directory' " cd gitwc && git rm -f symlink-directory > /dev/null && @@ -216,6 +277,11 @@ test_expect_success 'info deleted-symlink-directory' " actual.info-deleted-symlink-directory " +test_expect_success 'info --url symlink-directory (deleted)' ' + test $(cd gitwc; git-svn info --url symlink-directory) \ + = "$svnrepo/symlink-directory" + ' + # NOTE: git does not have the concept of replaced objects, # so we can't test for files in that state. @@ -228,6 +294,12 @@ test_expect_success 'info unknown-file' " git-diff expected.info-unknown-file actual.info-unknown-file " +test_expect_success 'info --url unknown-file' ' + test -z $(cd gitwc; git-svn info --url unknown-file \ + 2> ../actual.info--url-unknown-file) && + git-diff expected.info-unknown-file actual.info--url-unknown-file + ' + test_expect_success 'info unknown-directory' " mkdir gitwc/unknown-directory svnwc/unknown-directory && ptouch gitwc/unknown-directory svnwc/unknown-directory && @@ -239,6 +311,13 @@ test_expect_success 'info unknown-directory' " git-diff expected.info-unknown-directory actual.info-unknown-directory " +test_expect_success 'info --url unknown-directory' ' + test -z $(cd gitwc; git-svn info --url unknown-directory \ + 2> ../actual.info--url-unknown-directory) && + git-diff expected.info-unknown-directory \ + actual.info--url-unknown-directory + ' + test_expect_success 'info unknown-symlink-file' " cd gitwc && ln -s unknown-file unknown-symlink-file && @@ -255,6 +334,13 @@ test_expect_success 'info unknown-symlink-file' " actual.info-unknown-symlink-file " +test_expect_success 'info --url unknown-symlink-file' ' + test -z $(cd gitwc; git-svn info --url unknown-symlink-file \ + 2> ../actual.info--url-unknown-symlink-file) && + git-diff expected.info-unknown-symlink-file \ + actual.info--url-unknown-symlink-file + ' + test_expect_success 'info unknown-symlink-directory' " cd gitwc && ln -s unknown-directory unknown-symlink-directory && @@ -272,4 +358,11 @@ test_expect_success 'info unknown-symlink-directory' " actual.info-unknown-symlink-directory " +test_expect_success 'info --url unknown-symlink-directory' ' + test -z $(cd gitwc; git-svn info --url unknown-symlink-directory \ + 2> ../actual.info--url-unknown-symlink-directory) && + git-diff expected.info-unknown-symlink-directory \ + actual.info--url-unknown-symlink-directory + ' + test_done From a5460eb7bb3ccef599adce87a620a3b89c7eb5df Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 21 Nov 2007 18:20:57 -0800 Subject: [PATCH 5/5] git-svn: allow `info' command to work offline Cache the repository root whenever we connect to the repository. This will allow us to notice URL changes if the user changes the URL in .git/config, too. If the repository is no longer accessible, or if `git svn info' is the first and only command run; then '(offline)' will be displayed for "Repository Root:" in the output. Signed-off-by: Eric Wong --- git-svn.perl | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 7d86870ee4..43e1591cef 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -782,9 +782,14 @@ sub cmd_info { $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir"; $result .= "URL: " . $full_url . "\n"; - my $repos_root = $gs->ra->{repos_root}; - Git::SVN::remove_username($repos_root); - $result .= "Repository Root: $repos_root\n"; + eval { + my $repos_root = $gs->repos_root; + Git::SVN::remove_username($repos_root); + $result .= "Repository Root: $repos_root\n"; + }; + if ($@) { + $result .= "Repository Root: (offline)\n"; + } $result .= "Repository UUID: $uuid\n" unless $diff_status eq "A"; $result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n"; @@ -1773,9 +1778,24 @@ sub ra_uuid { $self->{ra_uuid}; } +sub _set_repos_root { + my ($self, $repos_root) = @_; + my $k = "svn-remote.$self->{repo_id}.reposRoot"; + $repos_root ||= $self->ra->{repos_root}; + tmp_config($k, $repos_root); + $repos_root; +} + +sub repos_root { + my ($self) = @_; + my $k = "svn-remote.$self->{repo_id}.reposRoot"; + eval { tmp_config('--get', $k) } || $self->_set_repos_root; +} + sub ra { my ($self) = shift; my $ra = Git::SVN::Ra->new($self->{url}); + $self->_set_repos_root($ra->{repos_root}); if ($self->use_svm_props && !$self->{svm}) { if ($self->no_metadata) { die "Can't have both 'noMetadata' and ",