Add svn-compatible "blame" output format to git-svn

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>
This commit is contained in:
Steven Grimm 2008-05-10 22:11:18 -07:00 committed by Junio C Hamano
parent f4e9f786f7
commit 4be4038153
2 changed files with 55 additions and 15 deletions

View File

@ -166,11 +166,18 @@ environment). This command has the same behaviour.
Any other arguments are passed directly to `git log' Any other arguments are passed directly to `git log'
'blame':: 'blame'::
Show what revision and author last modified each line of a file. This is Show what revision and author last modified each line of a file. The
identical to `git blame', but SVN revision numbers are shown instead of git output of this mode is format-compatible with the output of
commit hashes. `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':: 'find-rev'::

View File

@ -65,7 +65,8 @@ my ($_stdin, $_help, $_edit,
$_template, $_shared, $_template, $_shared,
$_version, $_fetch_all, $_no_rebase, $_version, $_fetch_all, $_no_rebase,
$_merge, $_strategy, $_dry_run, $_local, $_merge, $_strategy, $_dry_run, $_local,
$_prefix, $_no_checkout, $_url, $_verbose); $_prefix, $_no_checkout, $_url, $_verbose,
$_git_format);
$Git::SVN::_follow_parent = 1; $Git::SVN::_follow_parent = 1;
my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
'config-dir=s' => \$Git::SVN::Ra::config_dir, 'config-dir=s' => \$Git::SVN::Ra::config_dir,
@ -188,7 +189,7 @@ my %cmd = (
{ 'url' => \$_url, } ], { 'url' => \$_url, } ],
'blame' => [ \&Git::SVN::Log::cmd_blame, 'blame' => [ \&Git::SVN::Log::cmd_blame,
"Show what revision and author last modified each line of a file", "Show what revision and author last modified each line of a file",
{} ], { 'git-format' => \$_git_format } ],
); );
my $cmd; my $cmd;
@ -225,7 +226,7 @@ unless ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) {
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
read_repo_config(\%opts); 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, my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
'minimize-connections' => \$Git::SVN::Migration::_minimize, 'minimize-connections' => \$Git::SVN::Migration::_minimize,
'id|i=s' => \$Git::SVN::default_ref_id, 'id|i=s' => \$Git::SVN::default_ref_id,
@ -4468,19 +4469,51 @@ out:
} }
sub cmd_blame { sub cmd_blame {
my $path = shift; my $path = pop;
config_pager(); config_pager();
run_pager(); run_pager();
my ($fh, $ctx) = command_output_pipe('blame', @_, $path); my ($fh, $ctx, $rev);
while (my $line = <$fh>) {
if ($line =~ /^\^?([[:xdigit:]]+)\s/) { if ($_git_format) {
my (undef, $rev, undef) = ::cmt_metadata($1); ($fh, $ctx) = command_output_pipe('blame', @_, $path);
$rev = sprintf('%-10s', $rev); while (my $line = <$fh>) {
$line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/; 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); command_close_pipe($fh, $ctx);
} }