Add svn-compatible "blame" output format to git-svn
authorSteven Grimm <koreth@midwinter.com>
Sun, 11 May 2008 05:11:18 +0000 (22:11 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 11 May 2008 17:12:19 +0000 (10:12 -0700)
git-svn blame produced output in the format of git blame; in environments
where there are scripts that read the output of svn blame, it's useful
to be able to use them with the output of git-svn. The git-compatible
format is still available using the new "--git-format" option.

This also fixes a bug in the initial git-svn blame implementation; it was
bombing out on uncommitted local changes.

Signed-off-by: Steven Grimm <koreth@midwinter.com>
Acked-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-svn.txt
git-svn.perl

index f4ba1056f0cab8d121a94797349a0bd789704580..c6b56b4ef35879124f9e1bb8468a59761d09cb3b 100644 (file)
@@ -166,11 +166,18 @@ environment). This command has the same behaviour.
 Any other arguments are passed directly to `git log'
 
 'blame'::
-       Show what revision and author last modified each line of a file. This is
-       identical to `git blame', but SVN revision numbers are shown instead of git
-       commit hashes.
+       Show what revision and author last modified each line of a file. The
+       output of this mode is format-compatible with the output of
+       `svn blame' by default. Like the SVN blame command,
+       local uncommitted changes in the working copy are ignored;
+       the version of the file in the HEAD revision is annotated. Unknown
+       arguments are passed directly to git-blame.
 +
-All arguments are passed directly to `git blame'.
+--git-format;;
+       Produce output in the same format as `git blame', but with
+       SVN revision numbers instead of git commit hashes. In this mode,
+       changes that haven't been committed to SVN (including local
+       working-copy edits) are shown as revision 0.
 
 --
 'find-rev'::
index 413e0b1db6a45bbb5d3635f6af64d865580a13ab..2c53f39aefa0131cb15ab679e6b2633622c000a1 100755 (executable)
@@ -65,7 +65,8 @@ my ($_stdin, $_help, $_edit,
        $_template, $_shared,
        $_version, $_fetch_all, $_no_rebase,
        $_merge, $_strategy, $_dry_run, $_local,
-       $_prefix, $_no_checkout, $_url, $_verbose);
+       $_prefix, $_no_checkout, $_url, $_verbose,
+       $_git_format);
 $Git::SVN::_follow_parent = 1;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
@@ -188,7 +189,7 @@ my %cmd = (
                    { 'url' => \$_url, } ],
        'blame' => [ \&Git::SVN::Log::cmd_blame,
                    "Show what revision and author last modified each line of a file",
-                   {} ],
+                   { 'git-format' => \$_git_format } ],
 );
 
 my $cmd;
@@ -225,7 +226,7 @@ unless ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) {
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
 
 read_repo_config(\%opts);
-Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
+Getopt::Long::Configure('pass_through') if ($cmd && ($cmd eq 'log' || $cmd eq 'blame'));
 my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
                     'minimize-connections' => \$Git::SVN::Migration::_minimize,
                     'id|i=s' => \$Git::SVN::default_ref_id,
@@ -4468,19 +4469,51 @@ out:
 }
 
 sub cmd_blame {
-       my $path = shift;
+       my $path = pop;
 
        config_pager();
        run_pager();
 
-       my ($fh, $ctx) = command_output_pipe('blame', @_, $path);
-       while (my $line = <$fh>) {
-               if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
-                       my (undef, $rev, undef) = ::cmt_metadata($1);
-                       $rev = sprintf('%-10s', $rev);
-                       $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+       my ($fh, $ctx, $rev);
+
+       if ($_git_format) {
+               ($fh, $ctx) = command_output_pipe('blame', @_, $path);
+               while (my $line = <$fh>) {
+                       if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
+                               # Uncommitted edits show up as a rev ID of
+                               # all zeros, which we can't look up with
+                               # cmt_metadata
+                               if ($1 !~ /^0+$/) {
+                                       (undef, $rev, undef) =
+                                               ::cmt_metadata($1);
+                                       $rev = '0' if (!$rev);
+                               } else {
+                                       $rev = '0';
+                               }
+                               $rev = sprintf('%-10s', $rev);
+                               $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+                       }
+                       print $line;
+               }
+       } else {
+               ($fh, $ctx) = command_output_pipe('blame', '-p', @_, 'HEAD',
+                                                 '--', $path);
+               my ($sha1);
+               my %authors;
+               while (my $line = <$fh>) {
+                       if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
+                               $sha1 = $1;
+                               (undef, $rev, undef) = ::cmt_metadata($1);
+                               $rev = '0' if (!$rev);
+                       }
+                       elsif ($line =~ /^author (.*)/) {
+                               $authors{$rev} = $1;
+                               $authors{$rev} =~ s/\s/_/g;
+                       }
+                       elsif ($line =~ /^\t(.*)$/) {
+                               printf("%6s %10s %s\n", $rev, $authors{$rev}, $1);
+                       }
                }
-               print $line;
        }
        command_close_pipe($fh, $ctx);
 }