git-svn info: implement info command
[gitweb.git] / git-svn.perl
index 98c980fa8fc97b3811f8edba2c67ea3a8e174631..fd1036104550c6d500a9ee64498c06558fcca9ff 100755 (executable)
@@ -178,6 +178,10 @@ BEGIN
                          '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;