From: Eric Wong <normalperson@yhbt.net>
To: Junio C Hamano <junkio@cox.net>
Cc: Martin Langhoff <martin.langhoff@gmail.com>, git@vger.kernel.org
Subject: Re: [RFC] faking cvs annotate
Date: Thu, 15 Dec 2005 22:09:54 -0800 [thread overview]
Message-ID: <20051216060953.GA22150@mail.yhbt.net> (raw)
In-Reply-To: <7vpsnxlsw7.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> wrote:
> Martin Langhoff <martin.langhoff@gmail.com> writes:
>
> > Suggestions of GIT machinery that would shortcut the trip from
> >
> > git-rev-list HEAD $path
> >
> > to a annotate-ish output. Did I dream it or is qgit showing something
> > annotate-ish in its screenshots?
>
> I haven't actively done anything but one of the good things that
> could happen is to split out the access routines for annotate
> database qgit build when run the first time in the repository,
> and make them available to other Porcelains. There is no need
> to reinvent the wheel.
I started working on something using Algorithm::Annotate on CPAN during
an ADD excursion a few weeks ago, but mostly forgot about it (thanks
again to ADD :). A few versions of git later and it still seems to
work as well as I remembered it.
It doesn't handle renames/copies yet, but it does let you customize the
left-hand side output unlike most/(all?) annotate implementations.
I guess it's also quite useful when the output is piped to vim with a
repository browser like the one I brought up in
<20051124093322.GA3899@mail.yhbt.net>
---------- 8< ---------
#!/usr/bin/env perl
# Copyright (c) 2005, Eric Wong <normalperson@yhbt.net>
#
# This file is licensed under the GPL v2, or a later version
# at the discretion of Linus.
#
# Read git-whatchanged output and let all Algorithm::Annotate do all
# the hard work for us. Thanks to Chia-liang Kao for the awesome
# Algorithm::Annotate module.
#
# Formatting options of annotation output is available.
# Dates can be formatted through strftime strings using the -D switch,
# and annotation expansion can be done using the -F switch
# on following variables:
=cut
%commit% %tree% %author_name% %author_email% %author_date%
%committer_name% %committer_email% %commit_date% %message%
=cut
use warnings;
use strict;
sub usage ($) {
print '* git-annotate ',
'[-w <width>] [-D <date-format>] [-F <annotate-format>] ',
'[--localtime] [--line-prefix <str>] [--line-postfix <str>] ',
'<file>', "\n";
exit $_[0];
}
# everybody with Perl 5 should have these, right?
use POSIX qw(strftime);
use Getopt::Long qw(:config gnu_getopt no_ignore_case no_auto_abbrev);
my %_o; # options
usage(1) unless (GetOptions(\%_o, qw(help|H|h width|w=i
line_prefix|line-prefix:s line_postfix|line-postfix:s
format|F=s date_format|date-format|D=s localtime|l)));
usage(0) if $_o{help};
# ok, see if we can do more than show a help message:
require Algorithm::Annotate or die
"Can't find Algorithm::Annotate, get it from CPAN ($!)\n";
my $file = shift or usage(1);
unless (-f $file) {
print STDERR "File does not exist or is not a file: $file\n";
exit 1;
}
my $date_format = $_o{date_format} || '%Y-%m-%dT%H-%M-%SZ'; # ISO 8601
my $re_sha1 = qr/[a-f0-9]{40}/; # match 40 char hex strings
my @blobs; # we read from newest to oldest, but $ann needs it reversed
sub t ($) {
return localtime($_[0]) if $_o{'localtime'};
return gmtime($_[0]);
}
if (my $pid = open my $child, '-|') {
my $meta;
while (<$child>) {
chomp;
if (/^diff-tree ($re_sha1) /o) {
$meta = { commit => $1, message => [] };
} elsif (/^tree ($re_sha1)$/) {
$meta->{tree} = $1;
} elsif (/^author (.*) <(\S+)> (\d+) [\+\-]?\d+$/) {
$meta->{author_name} = $1;
$meta->{author_email} = $2;
$meta->{author} = "$1 <$2>";
$meta->{author_date} = strftime($date_format, t($3));
} elsif (/^committer (.*) <(\S+)> (\d+) [\+\-]?\d+$/) {
$meta->{committer_name} = $1;
$meta->{committer_email} = $2;
$meta->{committer} = "$1 <$2>";
$meta->{commit_date} = strftime($date_format, t($3));
} elsif (/^\s{4}(.*)$/) {
push @{$meta->{message}}, $1;
} elsif (/^:\d{6} \d{6} $re_sha1 ($re_sha1)/o) {
my $blob = $1;
if ($blob =~ /^0{40}$/) {
# nonexistent (hopefully!) blob
$meta = undef;
next;
}
$meta->{message} = join("\n",@{$meta->{message}});
push @blobs, { meta => $meta, blob => $blob };
$meta = undef;
}
}
} else {
exec('git-whatchanged','-m','--pretty=raw','--',$file) or die
"Unable to execute git-whatchanged $file: $? $!";
}
my $msg_format = $_o{format} || '%commit% %commit_date%';
my $max_len = 0;
my $ann = Algorithm::Annotate->new;
foreach my $x (reverse @blobs) {
my @contents;
# no need to do safe pipe opens since we know $x->{blob} is a hex sum
@contents = `git-cat-file blob $x->{blob}`;
# $msg is the left hand side of our final annotation output,
# format it according to $msg_format
my $msg = $msg_format;
my $meta = $x->{meta};
# our annotation message templating engine:
$msg =~ s/\%$_\%/$meta->{$_}/g foreach keys %$meta;
$msg =~ s/\\t/\t/g;
$msg =~ s/\\n/\n/g;
$ann->add($msg, \@contents);
if ($msg !~ /\n/) {
$max_len = length $msg if length $msg > $max_len;
} else {
foreach (split /\n/, $msg) {
$max_len = length $_ if length $_ > $max_len;
}
}
}
# see if we can annotate local changes, too
my $local_change;
my @diff_opts = qw(-z -C --find-copies-harder --name-only);
{ # see if we've changed $file in the working tree
if (my $pid = open my $child, '-|') {
$local_change = (<$child>);
} else {
exec('git-diff-files',@diff_opts,'--',$file);
}
}
unless ($local_change) {
# see if we've changed $file in the working tree
if (my $pid = open my $child, '-|') {
$local_change = (<$child>);
} else {
exec('git-diff-index',@diff_opts,'--cached','HEAD','--',$file);
}
}
open my $fd,'<',$file or die "Error opening file: $file: $!\n";
my @contents = <$fd>;
if ($local_change) {
my $msg = '(uncommitted): ';
$ann->add($msg, \@contents);
$max_len = length $msg if length $msg > $max_len;
}
# $result here is an array ref, each element corresponding to a line of
# the annotated file contents
my $result = $ann->result or do {
print STDERR "Unable to annotate $file. Is it tracked by git?\n";
exit 1;
};
my $pre = defined $_o{line_prefix} ? $_o{line_prefix} : '';
my $post = defined $_o{line_postfix} ? $_o{line_postfix} : ': ';
my $width = $_o{width} || $max_len;
foreach (@contents) {
my $msg = shift @$result;
if ($msg !~ /\n/) {
print ($pre, pack("A$width", $msg), $post, $_);
} else {
my @msgs = split /\n/, $msg;
print ($pre, pack("A$width", shift @msgs), $post, $_);
foreach my $m (@msgs) {
print ($pre, pack("A$width", $m), $post,"\n");
}
}
}
close $fd;
exit 0;
--
Eric Wong
next prev parent reply other threads:[~2005-12-16 6:09 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-12-16 1:13 [RFC] faking cvs annotate Martin Langhoff
2005-12-16 1:31 ` Johannes Schindelin
2005-12-16 4:37 ` Martin Langhoff
2005-12-16 17:07 ` Linus Torvalds
2005-12-16 21:40 ` Nicolas Pitre
2005-12-16 22:12 ` Linus Torvalds
2005-12-16 22:36 ` Nicolas Pitre
2005-12-16 1:42 ` Junio C Hamano
2005-12-16 6:09 ` Eric Wong [this message]
2005-12-16 15:05 ` Ryan Anderson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20051216060953.GA22150@mail.yhbt.net \
--to=normalperson@yhbt.net \
--cc=git@vger.kernel.org \
--cc=junkio@cox.net \
--cc=martin.langhoff@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).