{"thread":{"id":"3266","subject":"[PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","startedAt":"2006-02-08T14:52:55Z","lastAt":"2006-02-14T10:51:32Z","messageCount":19,"participants":["Ryan Anderson","Peter Eriksen","Johannes Schindelin","Franck Bui-Huu","Randal L. Schwartz","Linus Torvalds","Junio C Hamano","Andreas Ericsson","Ralf Baechle","Fredrik Kuivinen"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"15713","messageId":"11394103753694-git-send-email-ryan@michonline.com","threadId":"3266","inReplyTo":null,"subject":"[PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-02-08T14:52:55Z","receivedAt":"2006-02-08T14:52:55Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"Signed-off-by: Ryan Anderson <ryan@michonline.com>\n\n---\n\nI think this version is mostly ready to go.\n\nJunio, the post you pointed me at was very helpful (once I got around to\nlistening to it), but the code it links to is missing - if that's a\nbetter partial implementation than this, can you ressurrect it\nsomewhere?  I'd be happy to reintegrate it together.\n\n Makefile          |    1 \n git-annotate.perl |  291 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 292 insertions(+), 0 deletions(-)\n create mode 100755 git-annotate.perl\n\n86fa163e7fd1bee2929b7946456407dbc7745193\ndiff --git a/Makefile b/Makefile\nindex 5c32934..8d24660 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -117,6 +117,7 @@ SCRIPT_SH = \\\n SCRIPT_PERL = \\\n \tgit-archimport.perl git-cvsimport.perl git-relink.perl \\\n \tgit-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \\\n+\tgit-annotate.perl \\\n \tgit-svnimport.perl git-mv.perl git-cvsexportcommit.perl\n \n SCRIPT_PYTHON = \\\ndiff --git a/git-annotate.perl b/git-annotate.perl\nnew file mode 100755\nindex 0000000..a3ea201\n--- /dev/null\n+++ b/git-annotate.perl\n@@ -0,0 +1,291 @@\n+#!/usr/bin/perl\n+# Copyright 2006, Ryan Anderson <ryan@michonline.com>\n+#\n+# GPL v2 (See COPYING)\n+#\n+# This file is licensed under the GPL v2, or a later version\n+# at the discretion of Linus Torvalds.\n+\n+use warnings;\n+use strict;\n+\n+use Data::Dumper;\n+\n+my $filename = shift @ARGV;\n+\n+\n+my @stack = (\n+\t{\n+\t\t'rev' => \"HEAD\",\n+\t\t'filename' => $filename,\n+\t},\n+);\n+\n+our (@lineoffsets, @pendinglineoffsets);\n+our @filelines = ();\n+open(F,\"<\",$filename)\n+\tor die \"Failed to open filename: $!\";\n+\n+while(<F>) {\n+\tchomp;\n+\tpush @filelines, $_;\n+}\n+close(F);\n+our $leftover_lines = @filelines;\n+our %revs;\n+our @revqueue;\n+our $head;\n+\n+my $revsprocessed = 0;\n+while (my $bound = pop @stack) {\n+\tmy @revisions = git_rev_list($bound->{'rev'}, $bound->{'filename'});\n+\tforeach my $revinst (@revisions) {\n+\t\tmy ($rev, @parents) = @$revinst;\n+\t\t$head ||= $rev;\n+\n+\t\tif (scalar @parents > 0) {\n+\t\t\t$revs{$rev}{'parents'} = \\@parents;\n+\t\t\t$revs{$rev}{'filename'} = $bound->{'filename'};\n+\t\t\tnext;\n+\t\t}\n+\n+\t\tmy $newbound = find_parent_renames($rev, $bound->{'filename'});\n+\t\tif ( exists $newbound->{'filename'} && $newbound->{'filename'} ne $bound->{'filename'}) {\n+\t\t\tpush @stack, $newbound;\n+\t\t\t$revs{$rev}{'parents'} = [$newbound->{'rev'}];\n+\t\t}\n+\t}\n+}\n+push @revqueue, $head;\n+init_claim($head);\n+$revs{$head}{'lineoffsets'} = {};\n+handle_rev();\n+\n+\n+my $i = 0;\n+foreach my $l (@filelines) {\n+\tmy ($output, $rev, $committer, $date);\n+\tif (ref $l eq 'ARRAY') {\n+\t\t($output, $rev, $committer, $date) = @$l;\n+\t\tif (length($rev) > 8) {\n+\t\t\t$rev = substr($rev,0,8);\n+\t\t}\n+\t} else {\n+\t\t$output = $l;\n+\t\t($rev, $committer, $date) = ('unknown', 'unknown', 'unknown');\n+\t}\n+\n+\tprintf(\"(%8s %10s %10s %d)%s\\n\", $rev, $committer, $date, $i++, $output);\n+}\n+\n+sub init_claim {\n+\tmy ($rev) = @_;\n+\tfor (my $i = 0; $i < @filelines; $i++) {\n+\t\t$filelines[$i] = [ $filelines[$i], $rev, 'unknown', 'unknown', 0];\n+\t\t\t# line,\n+\t\t\t# rev,\n+\t\t\t# author,\n+\t\t\t# date,\n+\t\t\t# confirmed to actually belong to this rev (0 = tentative)\n+\t}\n+}\n+\n+\n+sub handle_rev {\n+\tmy $i = 0;\n+\twhile (my $rev = shift @revqueue) {\n+\n+\t\tmy %revinfo = git_commit_info($rev);\n+\n+\t\tforeach my $p (@{$revs{$rev}{'parents'}}) {\n+\n+\t\t\tmy $nlineoffsets = {%{$revs{$rev}{'lineoffsets'}}};\n+\t\t\tgit_line_assign($p, $rev, $revs{$p}{'filename'}, $nlineoffsets,\n+\t\t\t\t%revinfo);\n+\t\t\tpush @revqueue, $p;\n+\t\t\t$revs{$p}{'lineoffsets'} = $nlineoffsets;\n+\t\t}\n+\n+\t\tfor (my $i = 0; $i < @filelines; $i++) {\n+\t\t\tif ($filelines[$i][1] eq $rev) {\n+\t\t\t\tclaim_line($i, $rev, %revinfo);\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (scalar @{$revs{$rev}{parents}} == 0) {\n+\t\t\t# We must be at the initial rev here, so claim everything that is left.\n+\t\t\tfor (my $i = 0; $i < @filelines; $i++) {\n+\t\t\t\tif (ref $filelines[$i] eq '') {\n+\t\t\t\t\tclaim_line($i, $rev, %revinfo);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\n+\t\treturn 1 if all_lines_claimed();\n+\t}\t\n+}\n+\n+\n+sub git_rev_list {\n+\tmy ($rev, $file) = @_;\n+\t#printf(\"grl = %s, %s\\n\", $rev, $file);\n+\n+# \tprintf(\"Calling: %s\\n\",join(\" \",\"git-rev-list\",\"--parents\",\"--remove-empty\",$rev,\"--\",$file));\n+\topen(P,\"-|\",\"git-rev-list\",\"--parents\",\"--remove-empty\",$rev,\"--\",$file)\n+\t\tor die \"Failed to exec git-rev-list: $!\";\n+\n+\tmy @revs;\n+\twhile(my $line = <P>) {\n+# \t\tprint $line;\n+\t\tchomp $line;\n+\t\tmy ($rev, @parents) = split /\\s+/, $line;\n+\t\tpush @revs, [ $rev, @parents ];\n+\t}\n+\tclose(P);\n+\n+\tprintf(\"0 revs found for rev %s (%s)\\n\", $rev, $file) if (@revs == 0);\n+\treturn @revs;\n+}\n+\n+sub find_parent_renames {\n+\tmy ($rev, $file) = @_;\n+\n+\topen(P,\"-|\",\"git-diff\", \"-r\",\"--name-status\", \"-z\",\"$rev^1..$rev\")\n+\t\tor die \"Failed to exec git-diff: $!\";\n+\n+\tlocal $/ = \"\\0\";\n+\tmy %bound;\n+\twhile (my $change = <P>) {\n+\t\tchomp $change;\n+\t\tmy $filename = <P>;\n+\t\tchomp $filename;\n+\n+\t\tif ($change =~ m/^[AMD]$/ ) {\n+\t\t\tnext;\n+\t\t} elsif ($change =~ m/^R/ ) {\n+\t\t\tmy $oldfilename = $filename;\n+\t\t\t$filename = <P>;\n+\t\t\tchomp $filename;\n+\t\t\tif ( $file eq $filename ) {\n+\t\t\t\tmy $parent = git_find_parent($rev);\n+\t\t\t\t#printf(\"Found rename at boundary: %s-%s, %s\\n\", $rev, $parent, $oldfilename);\n+\t\t\t\t@bound{'rev','filename'} = ($parent, $oldfilename);\n+\n+\t\t\t\tlast;\n+\t\t\t} else {\n+\t\t\t\t#printf(\"Found unknown rename of %s => %s\\n\", $oldfilename, $filename);\n+\t\t\t}\n+\t\t} else {\n+\t\t\t#printf(\"Unknown name-status type of '%s'\\n\", $change);\n+\t\t}\n+\t}\n+\tclose(P);\n+\n+\treturn \\%bound;\n+}\n+\n+\n+sub git_find_parent {\n+\tmy ($rev) = @_;\n+\n+\topen(REVPARENT,\"-|\",\"git-rev-list\",\"--parents\",\"$rev^1..$rev\")\n+\t\tor die \"Failed to open git-rev-list to find a single parent: $!\";\n+\n+\tmy $parentline = <REVPARENT>;\n+\tchomp $parentline;\n+\tmy ($revfound,$parent) = split m/\\s+/, $parentline;\n+\n+\tclose(REVPARENT);\n+\n+\treturn $parent;\n+}\n+\n+\n+# Examine a revision to see if it has unclaimed lines that we have,\n+# if so, give those lines to that revision.\n+sub git_line_assign {\n+\tmy ($parent, $rev, $filename, $lineoffsets, %revinfo) = @_;\n+\n+\tmy @plines = git_cat_file($parent, $filename);\n+\n+\tmy ($i, $j, $jbase) = (0,0,0);\n+\twhile ($i < @filelines && $filelines[$i][1] ne $rev) {\n+\t\t$i++;\n+\t}\n+\n+\tif ($i == @filelines) {\n+\t\tprintf(\"Skipping diff-parse - i = filelines)\\n\");\n+\t}\n+\treturn if $i == @filelines;\n+\n+\twhile($i < @filelines && $j < @plines) {\n+\t\tif ($filelines[$i][0] eq $plines[$j]) {\n+\t\t\t# Our parent has this line, give it away.\n+\t\t\t$filelines[$i][1] = $parent;\n+\t\t\t$jbase = $j;\n+\t\t\t$i++;\n+\t\t\t$j++;\n+\t\t\t\n+\t\t} elsif ($j+1 == @plines) {\n+\t\t\t$i++;\n+\t\t\t$j = $jbase;\n+\t\t} else {\n+\t\t\t$j++;\n+\t\t}\n+\t}\n+}\n+\n+sub git_cat_file {\n+\tmy ($parent, $filename) = @_;\n+\treturn () unless defined $parent && defined $filename;\n+\tmy $blobline = `git-ls-tree $parent $filename`;\n+\tmy ($mode, $type, $blob, $tfilename) = split(/\\s+/, $blobline, 4);\n+\n+\topen(C,\"-|\",\"git-cat-file\", \"blob\", $blob)\n+\t\tor die \"Failed to git-cat-file blob $blob (rev $parent, file $filename): \" . $!;\n+\n+\tmy @lines;\n+\twhile(<C>) {\n+\t\tchomp;\n+\t\tpush @lines, $_;\n+\t}\n+\tclose(C);\n+\n+\treturn @lines;\n+}\n+\n+\n+sub claim_line {\n+\tmy ($floffset, $rev, %revinfo) = @_;\n+\tmy $oline = $filelines[$floffset][0];\n+\t$filelines[$floffset] =\t[ $oline, $rev,\n+\t\t$revinfo{'author'}, $revinfo{'author_date'} ];\n+\t$leftover_lines--;\n+\tprintf(\"Claiming line %d with rev %s: '%s'\\n\",\n+\t\t\t$floffset, $rev, $oline) if 0;\n+}\n+\n+sub git_commit_info {\n+\tmy ($rev) = @_;\n+\topen(COMMIT, \"-|\",\"git-cat-file\", \"commit\", $rev)\n+\t\tor die \"Failed to call git-cat-file: $!\";\n+\n+\tmy %info;\n+\twhile(<COMMIT>) {\n+\t\tchomp;\n+\t\tlast if (length $_ == 0);\n+\n+\t\tif (m/^author (.*) <(.*)> (.*)$/) {\n+\t\t\t$info{'author'} = $1;\n+\t\t\t$info{'author_email'} = $2;\n+\t\t\t$info{'author_date'} = $3;\n+\t\t} elsif (m/^committer (.*) <(.*)> (.*)$/) {\n+\t\t\t$info{'committer'} = $1;\n+\t\t\t$info{'committer_email'} = $2;\n+\t\t\t$info{'committer_date'} = $3;\n+\t\t}\n+\t}\n+\tclose(COMMIT);\n+\n+\treturn %info;\n+}\n-- \n1.1.6.g3b91b\n"},{"id":"15714","messageId":"20060208150950.GA29346@ebar091.ebar.dtu.dk","threadId":"3266","inReplyTo":"11394103753694-git-send-email-ryan@michonline.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Peter Eriksen","fromEmail":"s022018@student.dtu.dk","sentAt":"2006-02-08T15:09:50Z","receivedAt":"2006-02-08T15:09:50Z","isPatch":true,"sender":{"key":"s022018@student.dtu.dk","avatar":null},"body":"On Wed, Feb 08, 2006 at 09:52:55AM -0500, Ryan Anderson wrote:\n> Signed-off-by: Ryan Anderson <ryan@michonline.com>\n> \n> ---\n> \n> I think this version is mostly ready to go.\n> \n> Junio, the post you pointed me at was very helpful (once I got around to\n> listening to it), but the code it links to is missing - if that's a\n> better partial implementation than this, can you ressurrect it\n> somewhere?  I'd be happy to reintegrate it together.\n\nDoes it depends on some ealier patch?  I get this:\n\ngit]$ git-annotate diff-delta.c\nUndefined subroutine &main::all_lines_claimed called at\n/home/peter/bin/git-annotate line 124.\n\nThe patch was applied to: git version 1.1.6.gd19e-dirty.\n\nPeter\n"},{"id":"15716","messageId":"Pine.LNX.4.63.0602081702280.19744@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"3266","inReplyTo":"20060208150950.GA29346@ebar091.ebar.dtu.dk","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-02-08T16:05:15Z","receivedAt":"2006-02-08T16:05:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 8 Feb 2006, Peter Eriksen wrote:\n\n> On Wed, Feb 08, 2006 at 09:52:55AM -0500, Ryan Anderson wrote:\n> > Signed-off-by: Ryan Anderson <ryan@michonline.com>\n> > \n> > ---\n> > \n> > I think this version is mostly ready to go.\n> > \n> > Junio, the post you pointed me at was very helpful (once I got around to\n> > listening to it), but the code it links to is missing - if that's a\n> > better partial implementation than this, can you ressurrect it\n> > somewhere?  I'd be happy to reintegrate it together.\n> \n> Does it depends on some ealier patch?  I get this:\n> \n> git]$ git-annotate diff-delta.c\n> Undefined subroutine &main::all_lines_claimed called at\n> /home/peter/bin/git-annotate line 124.\n\nJust add a function like\n\n-- snip --\nsub all_lines_claimed {\n        return ($leftover_lines == 0);\n}\n-- snap --\n\nand you're done.\n\nHowever, it does not yet do the correct thing: it does not show the root \ncommit. For example, if you do \"git annotate git-am.sh\" it should show \n\"d1c5f2a4\" for the first lines, not \"a1451104\" as it does.\n\nCiao,\nDscho\n"},{"id":"15717","messageId":"cda58cb80602080835s38713193t@mail.gmail.com","threadId":"3266","inReplyTo":"11394103753694-git-send-email-ryan@michonline.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-02-08T16:35:35Z","receivedAt":"2006-02-08T16:35:35Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/2/8, Ryan Anderson <ryan@michonline.com>:\n> Signed-off-by: Ryan Anderson <ryan@michonline.com>\n>\n> ---\n>\n> I think this version is mostly ready to go.\n>\n\nanother perl script :(\n\nAre there any rules on the choice of the script language ?\n\n> Junio, the post you pointed me at was very helpful (once I got around to\n> listening to it), but the code it links to is missing - if that's a\n> better partial implementation than this, can you ressurrect it\n> somewhere?  I'd be happy to reintegrate it together.\n>\n\nThanks\n--\n               Franck\n"},{"id":"15721","messageId":"Pine.LNX.4.63.0602081843220.20568@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"3266","inReplyTo":"cda58cb80602080835s38713193t@mail.gmail.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-02-08T17:45:30Z","receivedAt":"2006-02-08T17:45:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 8 Feb 2006, Franck Bui-Huu wrote:\n\n> another perl script :(\n> \n> Are there any rules on the choice of the script language ?\n\nYes. Do not try to introduce unnecessary dependencies. But if it is \nthe right tool to do the job, you should use it. As of now, we have perl, \npython and Tcl/Tk.\n\nHth,\nDscho\n"},{"id":"15722","messageId":"86ek2dsn5f.fsf@blue.stonehenge.com","threadId":"3266","inReplyTo":"cda58cb80602080835s38713193t@mail.gmail.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Randal L. Schwartz","fromEmail":"merlyn@stonehenge.com","sentAt":"2006-02-08T18:47:40Z","receivedAt":"2006-02-08T18:47:40Z","isPatch":true,"sender":{"key":"merlyn@stonehenge.com","avatar":"https://gravatar.com/avatar/dc528d210743ff0333e6213f9ee7b33b23f1b7bc1f3c5a8c2d819074ecd7ab19?d=mp&s=160"},"body":">>>>> \"Franck\" == Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n\nFranck> another perl script :(\n\nFranck> Are there any rules on the choice of the script language ?\n\nI could argue that they should all be Perl. :)\n\n-- \nRandal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095\n<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>\nPerl/Unix/security consulting, Technical writing, Comedy, etc. etc.\nSee PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!\n"},{"id":"15723","messageId":"Pine.LNX.4.64.0602081058210.2458@g5.osdl.org","threadId":"3266","inReplyTo":"11394103753694-git-send-email-ryan@michonline.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-02-08T19:09:16Z","receivedAt":"2006-02-08T19:09:16Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 8 Feb 2006, Ryan Anderson wrote:\n> \n> I think this version is mostly ready to go.\n\nHmm.. I get\n\n   [torvalds@g5 git]$ ./git-annotate Makefile\n   fatal: 'e83c5163316f89bfbde7d9ab23ca2e25604af290^1..e83c5163316f89bfbde7d9ab23ca2e25604af290': No such file or directory\n   Undefined subroutine &main::all_lines_claimed called at ./git-annotate line 124.\n\nwhere that fatal error is because e83c51.. doesn't _have_ a parent, it's \nthe root (so doing ^1 on it doesn't work).\n\nAfter fixing the \"all_lines_claimed\" problem as outlined by Dscho, I get a \nlot of\n\n\tSkipping diff-parse - i = filelines)\n\nand no actual output.\n\nDoing it on a file that didn't exist in the root commit still have those \n\"Skipping\" messages, but at least it did actually output something. \n\nHowever, what it output was clearly not correct, so there's still some \ntweaking to do.\n\nFor example, doing\n\n\t./git-annotate apply.c\n\nannotates most of that file to Junio's commit 1c15afb9, which is totally \nincorrect, that commit actually only changed a few lines.\n\nSo it looks like there's still some work to be done on this..\n\n\t\t\tLinus\n"},{"id":"15724","messageId":"7v3bitr73q.fsf@assigned-by-dhcp.cox.net","threadId":"3266","inReplyTo":"Pine.LNX.4.63.0602081843220.20568@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-08T19:19:37Z","receivedAt":"2006-02-08T19:19:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> Are there any rules on the choice of the script language ?\n>\n> Yes. Do not try to introduce unnecessary dependencies. But if it is \n> the right tool to do the job, you should use it. As of now, we have perl, \n> python and Tcl/Tk.\n\nVery well said.  That's what currently stands.\n"},{"id":"15725","messageId":"7vd5hxpr2d.fsf@assigned-by-dhcp.cox.net","threadId":"3266","inReplyTo":"11394103753694-git-send-email-ryan@michonline.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-08T19:51:22Z","receivedAt":"2006-02-08T19:51:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ryan Anderson <ryan@michonline.com> writes:\n\n> Signed-off-by: Ryan Anderson <ryan@michonline.com>\n>\n> ---\n>\n> I think this version is mostly ready to go.\n>\n> Junio, the post you pointed me at was very helpful (once I got around to\n> listening to it), but the code it links to is missing - if that's a\n> better partial implementation than this, can you ressurrect it\n> somewhere?  I'd be happy to reintegrate it together.\n\nI still have it, but the reason why I withdrew circulating it\nwas because I found that on some inputs it did not work\ncorrectly as intended.  Not that the algorithm was necessarily\nbroken but the implementation certainly was.\n\nUnlike yours mine reads and interprets diff output to find which\nlines are common and which lines are added, and I think the diff\ninterpretation logic has various corner cases wrong.  I did\ncombine-diff.c diff interpreter without looking at my\n'git-blame', so I do not remember where I got it wrong,\nthough...\n\nIt's been a while since I looked at it the last time so it may\nnot even work with the current git, but here it is..\n\n--\n#!/usr/bin/perl -w\n\nuse strict;\n\npackage main;\n$::debug = 0;\n\nsub read_blob {\n    my $sha1 = shift;\n    my $fh = undef;\n    my $result;\n    local ($/) = undef;\n    open $fh, '-|', 'git-cat-file', 'blob', $sha1\n\tor die \"cannot read blob $sha1\";\n    $result = join('', <$fh>);\n    close $fh\n\tor die \"failure while closing pipe to git-cat-file\";\n    return $result;\n}\n\nsub read_diff_raw {\n    my ($parent, $filename) = @_;\n    my $fh = undef;\n    local ($/) = \"\\0\";\n    my @result = (); \n    my ($meta, $status, $sha1_1, $sha1_2, $file1, $file2);\n\n    print STDERR \"* diff-index --cached $parent $filename\\n\" if $::debug;\n    my $has_changes = 0;\n    open $fh, '-|', 'git-diff-index', '--cached', '-z', $parent, $filename\n\tor die \"cannot read git-diff-index $parent $filename\";\n    while (defined ($meta = <$fh>)) {\n\t$has_changes = 1;\n    }\n    close $fh\n\tor die \"failure while closing pipe to git-diff-index\";\n    if (!$has_changes) {\n\treturn ();\n    }\n\n    $fh = undef;\n    print STDERR \"* diff-index -B -C --find-copies-harder --cached $parent\\n\" if $::debug;\n    open($fh, '-|', 'git-diff-index', '-B', '-C', '--find-copies-harder',\n\t '--cached', '-z', $parent)\n\tor die \"cannot read git-diff-index with $parent\";\n    while (defined ($meta = <$fh>)) {\n\tchomp($meta);\n\t(undef, undef, $sha1_1, $sha1_2, $status) = split(/ /, $meta);\n\t$file1 = <$fh>;\n\tchomp($file1);\n\tif ($status =~ /^[CR]/) {\n\t    $file2 = <$fh>;\n\t    chomp($file2);\n\t} elsif ($status =~ /^D/) {\n\t    next;\n\t} else {\n\t    $file2 = $file1;\n\t}\n\tif ($file2 eq $filename) {\n\t    push @result, [$status, $sha1_1, $sha1_2, $file1, $file2];\n\t}\n    }\n    close $fh\n\tor die \"failure while closing pipe to git-diff-index\";\n    return @result;\n}\n\nsub write_temp_blob {\n    my ($sha1, $temp) = @_;\n    my $fh = undef;\n    my $blob = read_blob($sha1);\n    open $fh, '>', $temp\n\tor die \"cannot open temporary file $temp\";\n    print $fh $blob;\n    close($fh);\n}\n\npackage Git::Patch;\nsub new {\n    my ($class, $sha1_1, $sha1_2) = @_;\n    my $self = bless [], $class;\n    my $fh = undef;\n    ::write_temp_blob($sha1_1, \"/tmp/blame-$$-1\");\n    ::write_temp_blob($sha1_2, \"/tmp/blame-$$-2\");\n    open $fh, '-|', 'diff', '-u0', \"/tmp/blame-$$-1\", \"/tmp/blame-$$-2\"\n\tor die \"cannot read diff\";\n    while (<$fh>) {\n\tif (/^\\@\\@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? \\@\\@/) {\n\t    push @$self, [$1, (defined $2 ? $2 : 1),\n\t\t\t  $3, (defined $4 ? $4 : 1)];\n\t}\n    }\n    close $fh;\n    unlink \"/tmp/blame-$$-1\", \"/tmp/blame-$$-2\";\n    return $self;\n}\n\nsub find_parent_line {\n    my ($self, $commit_lineno) = @_;\n    my $ofs = 0;\n    for (@$self) {\n\tmy ($line_1, $len_1, $line_2, $len_2) = @$_;\n\tif ($commit_lineno < $line_2) {\n\t    return $commit_lineno - $ofs;\n\t}\n\tif ($line_2 <= $commit_lineno && $commit_lineno < $line_2 + $len_2) {\n\t    return -1; # changed by commit.\n\t}\n\t$ofs += ($len_1 - $len_2);\n    }\n    return $commit_lineno + $ofs;\n}\n\npackage Git::Commit;\n\nmy %author_name_canon = \n('Linus Torvalds <torvalds@evo.osdl.org>' =>\n 'Linus Torvalds <torvalds@osdl.org>',\n 'Linus Torvalds <torvalds@ppc970.osdl.org.(none)>' =>\n 'Linus Torvalds <torvalds@osdl.org>',\n 'Linus Torvalds <torvalds@ppc970.osdl.org>' =>\n 'Linus Torvalds <torvalds@osdl.org>',\n 'Linus Torvalds <torvalds@g5.osdl.org>' =>\n 'Linus Torvalds <torvalds@osdl.org>',\n 'Matthias Urlichs <smurf@kiste.(none)>' =>\n 'Matthias Urlichs <smurf@smurf.noris.de>',\n 'Paul Mackerras <paulus@dorrigo.(none)>' =>\n 'Paul Mackerras <paulus@samba.org>',\n 'Paul Mackerras <paulus@pogo.(none)>' =>\n 'Paul Mackerras <paulus@samba.org>',\n 'Petr Baudis <pasky@ucw.cz>' =>\n 'Petr Baudis <pasky@suse.cz>',\n 'tony.luck@intel.com <tony.luck@intel.com>' =>\n 'Tony Luck <tony.luck@intel.com>',\n 'barkalow@iabervon.org <barkalow@iabervon.org>' =>\n 'Daniel Barkalow <barkalow@iabervon.org>',\n 'jon@blackcubes.dyndns.org <jon@blackcubes.dyndns.org>' =>\n 'Jon Seymour <jon.seymour@gmail.com>',\n 'Sven Verdoolaege <skimo@kotnet.org>' =>\n 'Sven Verdoolaege <skimo@liacs.nl>',\n 'Bryan Larsen <bryanlarsen@yahoo.com>' =>\n 'Bryan Larsen <bryan.larsen@gmail.com>',\n 'Junio C Hamano <junio@twinsun.com>' =>\n 'Junio C Hamano <junkio@cox.net>',\n );\n\nsub canon_author_name {\n    my ($name) = @_;\n    if (exists $author_name_canon{$name}) {\n\treturn $author_name_canon{$name};\n    }\n    return $name;\n}\n\nsub new {\n    my $class = shift;\n    my $self = bless {\n\tPARENT => [],\n\tTREE => undef,\n\tAUTHOR => undef,\n\tCOMMITTER => undef,\n    }, $class;\n    my $commit_sha1 = shift;\n    $self->{SHA1} = $commit_sha1;\n    my $fh = undef;\n    open $fh, '-|', 'git-cat-file', 'commit', $commit_sha1\n\tor die \"cannot read commit object $commit_sha1\";\n    while (<$fh>) {\n\tchomp;\n\tif (/^tree ([0-9a-f]{40})$/) { $self->{TREE} = $1; }\n\telsif (/^parent ([0-9a-f]{40})$/) { push @{$self->{PARENT}}, $1; }\n\telsif (/^author ([^>]+>)/) {\n\t    $self->{AUTHOR} = canon_author_name($1);\n\t}\n\telsif (/^committer ([^>]+>)/) {\n\t    $self->{COMMITTER} = canon_author_name($1);\n\t}\n    }\n    close $fh\n\tor die \"failure while closing pipe to git-cat-file\";\n    return $self;\n}\n\nsub find_file {\n    my ($commit, $path) = @_;\n    my $result = undef;\n    my $fh = undef;\n    local ($/) = \"\\0\";\n    open $fh, '-|', 'git-ls-tree', '-z', '-r', '-d', $commit->{TREE}, $path\n\tor die \"cannot read git-ls-tree $commit->{TREE}\";\n    while (<$fh>) {\n\tchomp;\n\tif (/^[0-7]{6} blob ([0-9a-f]{40})\t(.*)$/) {\n\t    if ($2 ne $path) {\n\t\tdie \"$2 ne $path???\";\n\t    }\n\t    $result = $1;\n\t    last;\n\t}\n    }\n    close $fh\n\tor die \"failure while closing pipe to git-ls-tree\";\n    return $result;\n}\n\npackage Git::Blame;\nsub new {\n    my $class = shift;\n    my $self = bless {\n\tLINE => [],\n\tUNKNOWN => undef,\n\tWORK => [],\n    }, $class;\n    my $commit = shift;\n    my $filename = shift;\n    my $sha1 = $commit->find_file($filename);\n    my $blob = ::read_blob($sha1);\n    my @blob = (split(/\\n/, $blob));\n    for (my $i = 0; $i < @blob; $i++) {\n\t$self->{LINE}[$i] = +{\n\t    COMMIT => $commit,\n\t    FOUND => undef,\n\t    FILENAME => $filename,\n\t    LINENO => ($i + 1),\n\t};\n    }\n    $self->{UNKNOWN} = scalar @blob;\n    push @{$self->{WORK}}, [$commit, $filename];\n    return $self;\n}\n\nsub read_blame_cache {\n    my $self = shift;\n    my $filename = shift;\n    my $fh = undef;\n    my $pi = $self->{'PATHINFO'} = {};\n    open $fh, '<', $filename;\n    while (<$fh>) {\n\tchomp;\n\tmy ($commit, $parent, $path) = split(/\\t/, $_);\n\t$pi->{$path}{$commit}{$parent} = 1;\n    }\n    close $fh;\n}\n\nsub print {\n    my $self = shift;\n    my $line_termination = shift;\n    for (my $i = 0; $i < @{$self->{LINE}}; $i++) {\n\tmy $l = $self->{LINE}[$i];\n\tprint ($l->{FOUND} ? ':' : '?');;\n\tprint \"$l->{COMMIT}->{SHA1}\t\";\n\tprint \"$l->{COMMIT}->{AUTHOR}\t\";\n\tprint \"$l->{COMMIT}->{COMMITTER}\t\";\n\tprint \"$l->{LINENO}\t$l->{FILENAME}\";\n\tprint $line_termination;\n    }\n}\n\nsub take_responsibility {\n    my ($self, $commit) = @_;\n    for (my $i = 0; $i < @{$self->{LINE}}; $i++) {\n\tmy $l = $self->{LINE}[$i];\n\tif (! $l->{FOUND} && ($l->{COMMIT}->{SHA1} eq $commit->{SHA1})) {\n\t    $l->{FOUND} = 1;\n\t    $self->{UNKNOWN}--;\n\t}\n    }\n}\n\nsub blame_parent {\n    my ($self, $commit, $parent, $filename) = @_;\n    my @diff = ::read_diff_raw($parent->{SHA1}, $filename);\n    my $filename_in_parent;\n    my $passed_blame_to_parent = undef;\n    if (@diff == 0) {\n\t# We have not touched anything.  Blame parent for everything\n\t# that we are suspected for.\n\tfor (my $i = 0; $i < @{$self->{LINE}}; $i++) {\n\t    my $l = $self->{LINE}[$i];\n\t    if (! $l->{FOUND} && ($l->{COMMIT}->{SHA1} eq $commit->{SHA1})) {\n\t\t$l->{COMMIT} = $parent;\n\t\t$passed_blame_to_parent = 1;\n\t    }\n\t}\n\t$filename_in_parent = $filename;\n    }\n    elsif (@diff != 1) {\n\t# This should not happen.\n\tfor (@diff) {\n\t    print \"** @$_\\n\";\n\t}\n\tdie \"Oops\";\n    }\n    else {\n\tmy ($status, $sha1_1, $sha1_2, $file1, $file2) = @{$diff[0]};\n\tprint STDERR \"** $status $file1 $file2\\n\" if $::debug;\n\tif ($status =~ /A/ || $status =~ /M[0-9][0-9]/) {\n\t    # Either some of other parents created it, or we did.\n\t    # At this point the only thing we know is that this\n\t    # parent is not responsible for it.\n\t    ;\n\t}\n\telse {\n\t    my $patch = Git::Patch->new($sha1_1, $sha1_2);\n\t    $filename_in_parent = $file1;\n\t    for (my $i = 0; $i < @{$self->{LINE}}; $i++) {\n\t\tmy $l = $self->{LINE}[$i];\n\t\tif (! $l->{FOUND} && $l->{COMMIT}->{SHA1} eq $commit->{SHA1}) {\n\t\t    # We are suspected to have introduced this line.\n\t\t    # Does it exist in the parent?\n\t\t    my $lineno = $l->{LINENO};\n\t\t    my $parent_line = $patch->find_parent_line($lineno);\n\t\t    if ($parent_line < 0) {\n\t\t\t# No, we may be the guilty ones, or some other\n\t\t\t# parent might be.  We do not assign blame to\n\t\t\t# ourselves here yet.\n\t\t\t;\n\t\t    }\n\t\t    else {\n\t\t\t# This line is coming from the parent, so pass\n\t\t\t# blame to it.\n\t\t\t$l->{COMMIT} = $parent;\n\t\t\t$l->{FILENAME} = $file1;\n\t\t\t$l->{LINENO} = $parent_line;\n\t\t\t$passed_blame_to_parent = 1;\n\t\t    }\n\t\t}\n\t    }\n\t}\n    }\n    if ($passed_blame_to_parent && $self->{UNKNOWN}) {\n\tunshift @{$self->{WORK}},\n\t[$parent, $filename_in_parent];\n    }\n}\n\nsub assign {\n    my ($self, $commit, $filename) = @_;\n    # We do read-tree of the current commit and diff-index\n    # with each parents, instead of running diff-tree.  This\n    # is because diff-tree does not look for copies hard enough.\n\n    if (exists $self->{'PATHINFO'} && exists $self->{'PATHINFO'}{$filename} &&\n\t!exists $self->{'PATHINFO'}{$filename}{$commit->{SHA1}} &&\n\t@{$commit->{PARENT}} == 1) {\n\t# This commit did not touch the path at all, and\n\t# has only one parent.  It is all that parent's fault.\n\n\tmy $parent = Git::Commit->new($commit->{PARENT}[0]);\n\tmy $passed_blame_to_parent = 0;\n\tfor (my $i = 0; $i < @{$self->{LINE}}; $i++) {\n\t    my $l = $self->{LINE}[$i];\n\t    if (! $l->{FOUND} &&\n\t\t($l->{COMMIT}->{SHA1} eq $commit->{SHA1})) {\n\t\t$l->{COMMIT} = $parent;\n\t\t$passed_blame_to_parent = 1;\n\t    }\n\t}\n\tif ($passed_blame_to_parent && $self->{UNKNOWN}) {\n\t    unshift @{$self->{WORK}},\n\t    [$parent, $filename];\n\t}\n\treturn;\n    }\n\n    print STDERR \"* read-tree  $commit->{SHA1}\\n\" if $::debug;\n    system('git-read-tree', '-m', $commit->{SHA1});\n    for my $parent (@{$commit->{PARENT}}) {\n\t$self->blame_parent($commit, Git::Commit->new($parent), $filename);\n    }\n    $self->take_responsibility($commit);\n}\n\nsub assign_blame {\n    my ($self) = @_;\n    while ($self->{UNKNOWN} && @{$self->{WORK}}) {\n\tmy $wk = shift @{$self->{WORK}};\n\tmy ($commit, $filename) = @$wk;\n\t$self->assign($commit, $filename);\n    }\n}\n\n\n\n################################################################\npackage main;\nmy $usage = \"blame [-z] <commit> filename\";\nmy $line_termination = \"\\n\";\n\n$::ENV{GIT_INDEX_FILE} = \"/tmp/blame-$$-index\";\nunlink($::ENV{GIT_INDEX_FILE});\n\nif ($ARGV[0] eq '-z') {\n    $line_termination = \"\\0\";\n    shift;\n}\n\nif (@ARGV != 2) {\n    die $usage;\n}\n\nmy $head_commit = Git::Commit->new($ARGV[0]);\nmy $filename = $ARGV[1];\nmy $blame = Git::Blame->new($head_commit, $filename);\nif (-f \".blame-cache\") {\n    $blame->read_blame_cache(\".blame-cache\");\n}\n\n$blame->assign_blame();\n$blame->print($line_termination);\n\nunlink($::ENV{GIT_INDEX_FILE});\n\n__END__\n\nHow does this work, and what do we do about merges?\n\nThe algorithm considers that the first parent is our main line of\ndevelopment and treats it somewhat special than other parents.  So we\npass on the blame to the first parent if a line has not changed from\nit.  For lines that have changed from the first parent, we must have\neither inherited that change from some other parent, or it could have\nbeen merge conflict resolution edit we did on our own.\n\nThe following picture illustrates how we pass on and assign blames.\n\nIn the sample, the original O was forked into A and B and then merged\ninto M.  Line 1, 2, and 4 did not change.  Line 3 and 5 are changed in\nA, and Line 5 and 6 are changed in B.  M made its own decision to\nresolve merge conflicts at Line 5 to something different from A and B:\n\n                A: 1 2 T 4 T 6\n               /               \\ \nO: 1 2 3 4 5 6                  M: 1 2 T 4 M S\n               \\               / \n                B: 1 2 3 4 S S\n\nIn the following picture, each line is annotated with a blame letter.\nA lowercase blame (e.g. \"a\" for \"1\") means that commit or its ancestor\nis the guilty party but we do not know which particular ancestor is\nresponsible for the change yet.  An uppercase blame means that we know\nthat commit is the guilty party.\n\nFirst we look at M (the HEAD) and initialize Git::Blame->{LINE} like\nthis:\n\n             M: 1 2 T 4 M S\n                m m m m m m\n\nThat is, we know all lines are results of modification made by some\nancestor of M, so we assign lowercase 'm' to all of them.\n\nThen we examine our first parent A.  Throughout the algorithm, we are\nalways only interested in the lines we are the suspect, but this being\nthe initial round, we are the suspect for all of them.  We notice that\n1 2 T 4 are the same as the parent A, so we pass the blame for these\nfour lines to A.  M and S are different from A, so we leave them as\nthey are (note that we do not immediately take the blame for them):\n\n             M: 1 2 T 4 M S\n                a a a a m m\n\nNext we go on to examine parent B.  Again, we are only interested in\nthe lines we are still the suspect (i.e. M and S).  We notice S is\nsomething we inherited from B, so we pass the blame on to it, like\nthis:\n\n             M: 1 2 T 4 M S\n                a a a a m b\n\nOnce we exhausted the parents, we look at the results and take\nresponsibility for the remaining ones that we are still the suspect:\n\n             M: 1 2 T 4 M S\n                a a a a M b\n\nWe are done with M.  And we know commits A and B need to be examined\nfurther, so we do them recursively.  When we look at A, we again only\nlook at the lines that A is the suspect:\n\n             A: 1 2 T 4 T 6\n                a a a a M b\n\nAmong 1 2 T 4, comparing against its parent O, we notice 1 2 4 are\nthe same so pass the blame for those lines to O:\n\n             A: 1 2 T 4 T 6\n                o o a o M b\n\nA is a non-merge commit; we have already exhausted the parents and\ntake responsibility for the remaining ones that A is the suspect:\n\n             A: 1 2 T 4 T 6\n                o o A o M b\n\nWe go on like this and the final result would become:\n\n             O: 1 2 3 4 5 6\n                O O A O M B\n"},{"id":"15727","messageId":"20060208210756.GA9490@mythryan2.michonline.com","threadId":"3266","inReplyTo":"7vd5hxpr2d.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-02-08T21:07:57Z","receivedAt":"2006-02-08T21:07:57Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"On Wed, Feb 08, 2006 at 11:51:22AM -0800, Junio C Hamano wrote:\n> Ryan Anderson <ryan@michonline.com> writes:\n> \n> > Signed-off-by: Ryan Anderson <ryan@michonline.com>\n> >\n> > ---\n> >\n> > I think this version is mostly ready to go.\n> >\n> > Junio, the post you pointed me at was very helpful (once I got around to\n> > listening to it), but the code it links to is missing - if that's a\n> > better partial implementation than this, can you ressurrect it\n> > somewhere?  I'd be happy to reintegrate it together.\n> \n> I still have it, but the reason why I withdrew circulating it\n> was because I found that on some inputs it did not work\n> correctly as intended.  Not that the algorithm was necessarily\n> broken but the implementation certainly was.\n> \n> Unlike yours mine reads and interprets diff output to find which\n> lines are common and which lines are added, and I think the diff\n> interpretation logic has various corner cases wrong.  I did\n> combine-diff.c diff interpreter without looking at my\n> 'git-blame', so I do not remember where I got it wrong,\n> though...\n\nI tried that approach at first, and it was much much more confusing to\ntry to keep track of.  The problem Linus found (that of a missing\n\"all_lines_claimed()\") was related to that code.  This implementation is\nsimple, though it has to have some problems with guessing at duplicated\nlines incorrectly.\n\n> It's been a while since I looked at it the last time so it may\n> not even work with the current git, but here it is..\n\nI'll take a look through this in greater detail later, hopefully your\napproach can be applied.  Diff-analyzing is apparently tricky.\n\n-- \n\nRyan Anderson\n  sometimes Pug Majere\n"},{"id":"15731","messageId":"7vlkwlo788.fsf@assigned-by-dhcp.cox.net","threadId":"3266","inReplyTo":"20060208210756.GA9490@mythryan2.michonline.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-08T21:45:11Z","receivedAt":"2006-02-08T21:45:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ryan Anderson <ryan@michonline.com> writes:\n\n>> It's been a while since I looked at it the last time so it may\n>> not even work with the current git, but here it is..\n>\n> I'll take a look through this in greater detail later, hopefully your\n> approach can be applied.  Diff-analyzing is apparently tricky.\n\nReading diff is tricky but I was lazy to match up the lines by\nhand, which is also a real work ;-).\n\nThere are a few things I should add to that ancient code:\n\n - It wants old ls-tree behaviour.  The command line used in the\n   \"sub find_file\" needs to be updated to something like this:\n\n    open $fh, '-|', 'git-ls-tree', '-z', '-r', $commit->{TREE}, $path\n\tor die \"cannot read git-ls-tree $commit->{TREE}\";\n\n - It only cares about the line numbers and its output is meant\n   to be postprocessed with the contents from the latest blob.\n\n - It predates the recent rev-list that skips commits that do\n   not change the specified paths, and it literally follows each\n   parent and optimizes not to diff with uninteresting parents\n   by hand.\n\nI suspect if you go with the diff-reading approach, it might be\neasy to convert it to C (or even write the initial version in C)\nusing the machinery similar to what is in combine-diff.c.\n\nThe algorithm combine-diff.c uses keeps the lines discarded from\neach parent in lline structure linked to the sline structure\n(which keeps track of the lines in the final version), but for\nyour annotate purposes what you care about is only what the\nchild adds to the parent (IOW, we do not care about the lines\nthat do not appear in the final version), so the logic and the\ndata structure could be greatly simplified.  You only need to\nkeep \"flag\" element in the sline structure, and maybe bol and\nlen that point at the contents of the resulting line from the\nfinal version.  In addition, you would need to store \"the\ncurrent suspect commit\" (starts from the final revision and\nupdated as you pass the blame along) and another bool that says\nif \"the current suspect\" is known to be the guilty party or if\nthe true culprit is one of its ancestors (capital vs lowercase\ndifference in that explanatory note).\n"},{"id":"15773","messageId":"43EB093A.1060207@op5.se","threadId":"3266","inReplyTo":"86ek2dsn5f.fsf@blue.stonehenge.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-02-09T09:19:54Z","receivedAt":"2006-02-09T09:19:54Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Randal L. Schwartz wrote:\n>>>>>>\"Franck\" == Franck Bui-Huu <vagabon.xyz@gmail.com> writes:\n> \n> \n> Franck> another perl script :(\n> \n> Franck> Are there any rules on the choice of the script language ?\n> \n> I could argue that they should all be Perl. :)\n> \n\nBrave thing to do among such a bunch of hardcore C hackers. ;)\n\nSo long as we never involve ruby, java or DCL, I'm a happy fellow.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"15776","messageId":"7vek2cetxh.fsf@assigned-by-dhcp.cox.net","threadId":"3266","inReplyTo":"43EB093A.1060207@op5.se","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-09T09:57:14Z","receivedAt":"2006-02-09T09:57:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Ericsson <ae@op5.se> writes:\n\n> So long as we never involve ruby, java or DCL, I'm a happy fellow.\n\nWholeheartedly seconded ;-).\n"},{"id":"15786","messageId":"cda58cb80602090404o4e753839g@mail.gmail.com","threadId":"3266","inReplyTo":"7vek2cetxh.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Franck Bui-Huu","fromEmail":"vagabon.xyz@gmail.com","sentAt":"2006-02-09T12:04:54Z","receivedAt":"2006-02-09T12:04:54Z","isPatch":true,"sender":{"key":"vagabon.xyz@gmail.com","avatar":null},"body":"2006/2/9, Junio C Hamano <junkio@cox.net>:\n> Andreas Ericsson <ae@op5.se> writes:\n>\n> > So long as we never involve ruby, java or DCL, I'm a happy fellow.\n>\n> Wholeheartedly seconded ;-).\n>\n\nI agree to but my point was more why not only using python scripts ?\nWhy sometimes some scripts is written in perl whereas python could be\nused and vice-versa ?\n\nThanks\n--\n               Franck\n"},{"id":"15788","messageId":"43EB39C4.3020309@op5.se","threadId":"3266","inReplyTo":"cda58cb80602090404o4e753839g@mail.gmail.com","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-02-09T12:47:00Z","receivedAt":"2006-02-09T12:47:00Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Franck Bui-Huu wrote:\n> 2006/2/9, Junio C Hamano <junkio@cox.net>:\n> \n>>Andreas Ericsson <ae@op5.se> writes:\n>>\n>>\n>>>So long as we never involve ruby, java or DCL, I'm a happy fellow.\n>>\n>>Wholeheartedly seconded ;-).\n>>\n> \n> \n> I agree to but my point was more why not only using python scripts ?\n> Why sometimes some scripts is written in perl whereas python could be\n> used and vice-versa ?\n> \n\nPerl is better suited for some tasks, Python for others. Mostly it's \nbecause the contributor (one out of 137 to date) thought the language \nappropriate for the tool he/she set out to write and felt comfortable \nwith it.\n\nI personally abhor the syntax of Perl and the block indentation of \nPython but I happily embrace both if the alternative is to rewrite all \nthe script tools in C.\n\nThat said, some tools have been rewritten in the past (mostly scripts \nhave been replaced by C code versions), but I don't think Junio will \naccept replacement tools just because they're in one particular \nlanguage. If anything, it would be to replace the two python scripts \nwith Perl versions, since more tools are implemented in Perl than in \nPython (so we could drop one dependency), Perl exists on more platforms \n(so git becomes more portable), and Perl is used inline in four of the \nshell-scripts (which means we can't get rid of the Perl dependency \nwithout major hackery anyway).\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"15849","messageId":"20060210112541.GA3513@linux-mips.org","threadId":"3266","inReplyTo":"7v3bitr73q.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Ralf Baechle","fromEmail":"ralf@linux-mips.org","sentAt":"2006-02-10T11:25:41Z","receivedAt":"2006-02-10T11:25:41Z","isPatch":true,"sender":{"key":"ralf@linux-mips.org","avatar":null},"body":"On Wed, Feb 08, 2006 at 11:19:37AM -0800, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> Are there any rules on the choice of the script language ?\n> >\n> > Yes. Do not try to introduce unnecessary dependencies. But if it is \n> > the right tool to do the job, you should use it. As of now, we have perl, \n> > python and Tcl/Tk.\n> \n> Very well said.  That's what currently stands.\n\nThe dependency on Python 2.4 already is a problem for installation on some\nsystems ...\n\n  Ralf\n"},{"id":"15851","messageId":"43EC7F17.6050007@op5.se","threadId":"3266","inReplyTo":"20060210112541.GA3513@linux-mips.org","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-02-10T11:55:03Z","receivedAt":"2006-02-10T11:55:03Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Ralf Baechle wrote:\n> On Wed, Feb 08, 2006 at 11:19:37AM -0800, Junio C Hamano wrote:\n> \n> \n>>Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>>\n>>\n>>>>Are there any rules on the choice of the script language ?\n>>>\n>>>Yes. Do not try to introduce unnecessary dependencies. But if it is \n>>>the right tool to do the job, you should use it. As of now, we have perl, \n>>>python and Tcl/Tk.\n>>\n>>Very well said.  That's what currently stands.\n> \n> \n> The dependency on Python 2.4 already is a problem for installation on some\n> systems ...\n> \n\nNot many though. Since Python is only required on the workstation where \nthe developer does his/her work it's not a very cumbersome requirement. \nThe same holds for Perl, btw. It's not a requirement on the server \nhosting the public repositories, unless some of the scripts are used \nfrom the hooks (git shortlog is used from the default update-hook, but \nthat can be changed with no trouble at all).\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"15912","messageId":"20060210220445.GD456@mythryan2.michonline.com","threadId":"3266","inReplyTo":"7vlkwlo788.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-02-10T22:04:45Z","receivedAt":"2006-02-10T22:04:45Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"On Wed, Feb 08, 2006 at 01:45:11PM -0800, Junio C Hamano wrote:\n> Ryan Anderson <ryan@michonline.com> writes:\n> \n> >> It's been a while since I looked at it the last time so it may\n> >> not even work with the current git, but here it is..\n> >\n> > I'll take a look through this in greater detail later, hopefully your\n> > approach can be applied.  Diff-analyzing is apparently tricky.\n> \n> Reading diff is tricky but I was lazy to match up the lines by\n> hand, which is also a real work ;-).\n\nReading a diff is tricky, yes, but if you're willing to just throw RAM\nat the problem, it might not be quite as bad as I was trying at first.\n\nMy current thought on how to get it more correct is this:\n\tforeach $rev (@revqueue) {\n\t\tforeach $parent (@{$revs{$rev}{parents}}) {\n\t\t\tmy @templines = @{$revs{$rev}{lines}};\n\n\t\t\t$revs{$parent}{lines} = apply_diff(\\@templlines);\n\t\t}\n\t}\n\nThe @lines arrays that get built will be entirely made up of hash or\narray references, so they just get reused for each successive file.\n\nWhen apply_diff() deletes a line from the new copy, it should mark that\nline as \"claimed\" by the current rev.\n\nI'm thinking that each element of @lines will look like this:\n\t{\n\t\ttext => $text,\n\t\tin_original => [0 | 1],\n\t\tclaimed_by => $rev,\n\t}\nat least to start.\n\nThis method can sanity check itself by calling git cat-file and actually\nreading in each version of the file, and comparing it against the\ngenerated copy, aborting if we get the two out of sync.\n\nI'll see about implementing something along these lines this weekend,\ntime permitting.\n\n-- \n\nRyan Anderson\n  sometimes Pug Majere\n"},{"id":"16109","messageId":"20060214105132.GA7006@c165.ib.student.liu.se","threadId":"3266","inReplyTo":"20060210112541.GA3513@linux-mips.org","subject":"Re: [PATCH] Add git-annotate - a tool for annotating files with the revision and person that created each line in the file.","fromName":"Fredrik Kuivinen","fromEmail":"freku045@student.liu.se","sentAt":"2006-02-14T10:51:32Z","receivedAt":"2006-02-14T10:51:32Z","isPatch":true,"sender":{"key":"frekui@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13770967?v=4"},"body":"On Fri, Feb 10, 2006 at 11:25:41AM +0000, Ralf Baechle wrote:\n> The dependency on Python 2.4 already is a problem for installation on some\n> systems ... \n\nI understand that in the environments where the Python dependency is a\nproblem it is probably not due to the specific version. However, if\nWITH_OWN_SUBPROCESS is defined in the Makefile then Python 2.3 should\nwork fine too (this is actually automatically detected now, so you\nshouldn't have to do anything special to use Python 2.3).\n\n- Fredrik\n"}]}