{"thread":{"id":"5812","subject":"[PATCH 1/2] gitweb: blame: Print just a single new line char per table row","startedAt":"2006-10-05T00:59:26Z","lastAt":"2006-10-09T22:33:43Z","messageCount":14,"participants":["Luben Tuikov","Junio C Hamano","Johannes Schindelin","Petr Baudis","Martin Langhoff (CatalystIT)","Ryan Anderson"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"28220","messageId":"20061005005926.57010.qmail@web31808.mail.mud.yahoo.com","threadId":"5812","inReplyTo":null,"subject":"[PATCH 1/2] gitweb: blame: Print just a single new line char per table row","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-10-05T00:59:26Z","receivedAt":"2006-10-05T00:59:26Z","isPatch":true,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"This makes cut-and-paste from blame legible, else after\npasting it is broken into too many lines.\n\nSigned-off-by: Luben Tuikov <ltuikov@yahoo.com>\n---\n gitweb/gitweb.perl |    8 ++++----\n 1 files changed, 4 insertions(+), 4 deletions(-)\n\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex cba0840..451bf5d 100644\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -2509,7 +2509,7 @@ HTML\n \t\t\t$current_color = ++$current_color % $num_colors;\n \t\t\t$print_c8 = 1;\n \t\t}\n-\t\tprint \"<tr class=\\\"$rev_color[$current_color]\\\">\\n\";\n+\t\tprint \"<tr class=\\\"$rev_color[$current_color]\\\">\";\n \t\tprint \"<td class=\\\"sha1\\\"\";\n \t\tif ($print_c8 == 1) {\n \t\t\tprint \" title=\\\"$author, $date\\\"\";\n@@ -2519,10 +2519,10 @@ HTML\n \t\t\tprint $cgi->a({-href => href(action=>\"commit\", hash=>$full_rev, file_name=>$file_name)},\n \t\t\t\t      esc_html($rev));\n \t\t}\n-\t\tprint \"</td>\\n\";\n+\t\tprint \"</td>\";\n \t\tprint \"<td class=\\\"linenr\\\"><a id=\\\"l$lineno\\\" href=\\\"#l$lineno\\\" class=\\\"linenr\\\">\" .\n-\t\t      esc_html($lineno) . \"</a></td>\\n\";\n-\t\tprint \"<td class=\\\"pre\\\">\" . esc_html($data) . \"</td>\\n\";\n+\t\t      esc_html($lineno) . \"</a></td>\";\n+\t\tprint \"<td class=\\\"pre\\\">\" . esc_html($data) . \"</td>\";\n \t\tprint \"</tr>\\n\";\n \t}\n \tprint \"</table>\\n\";\n-- \n1.4.2.3.g7d77\n\n"},{"id":"28227","messageId":"7vejtnij5n.fsf@assigned-by-dhcp.cox.net","threadId":"5812","inReplyTo":"20061005005926.57010.qmail@web31808.mail.mud.yahoo.com","subject":"Re: [PATCH 1/2] gitweb: blame: Print just a single new line char per table row","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-05T02:35:48Z","receivedAt":"2006-10-05T02:35:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Luben Tuikov <ltuikov@yahoo.com> writes:\n\n> This makes cut-and-paste from blame legible, else after\n> pasting it is broken into too many lines.\n>\n> Signed-off-by: Luben Tuikov <ltuikov@yahoo.com>\n\nI have to say the browser is seriously broken if whitespaces\nbetween <tr> and <td>, </td> and next <td>, </td> and </tr>\nmakes a difference to its behaviour.  Aren't there a way to tell\nthe browser to cut \"visually\"?  E.g. \"The area I want to cut is\nfrom this data column, and lines from here to there.  I do not\nwant the leftmost two columns (commit-8 and lineno)\".\n\nI do not mind applying this, but that is only because it does\nnot make its source any less readable.  It does make things\nharder to debug if somebody is debugging its output by reading\nthe generated raw HTML, so it is not like there is no downside.\n\nYou might want to run links (or lynx) in screen ;-).  Screen\nsupports left- (or right-, or both-sides) bounded cut which I\nfind extremely useful.\n"},{"id":"28230","messageId":"20061005064817.21552.qmail@web31804.mail.mud.yahoo.com","threadId":"5812","inReplyTo":"7vejtnij5n.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] gitweb: blame: Print just a single new line char per table row","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-10-05T06:48:17Z","receivedAt":"2006-10-05T06:48:17Z","isPatch":true,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- Junio C Hamano <junkio@cox.net> wrote:\n> I have to say the browser is seriously broken if whitespaces\n> between <tr> and <td>, </td> and next <td>, </td> and </tr>\n> makes a difference to its behaviour.  Aren't there a way to tell\n> the browser to cut \"visually\"?  E.g. \"The area I want to cut is\n\nI'm not sure, I'm using latest Firefox.\n\n> from this data column, and lines from here to there.  I do not\n> want the leftmost two columns (commit-8 and lineno)\".\n> \n> I do not mind applying this, but that is only because it does\n> not make its source any less readable.  It does make things\n> harder to debug if somebody is debugging its output by reading\n> the generated raw HTML, so it is not like there is no downside.\n\nIf you think that this could be a problem or affects negatively,\nplease don't apply.  cut-paste-edit is a small price to pay\nas opposed to messing up the code and output.\n\n> You might want to run links (or lynx) in screen ;-).  Screen\n> supports left- (or right-, or both-sides) bounded cut which I\n> find extremely useful.\n\nOk, I'll try that.\n\n    Luben\n"},{"id":"28231","messageId":"7vu02jfaec.fsf_-_@assigned-by-dhcp.cox.net","threadId":"5812","inReplyTo":"20061005064817.21552.qmail@web31804.mail.mud.yahoo.com","subject":"perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-05T08:13:15Z","receivedAt":"2006-10-05T08:13:15Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"It's been a while since we lost git_blame from %actions list.  I\nam wondering maybe it's time to remove it, after 1.4.3 happens.\n\nWhile I was looking at it, I noticed that it would make\ngit-cvsserver the last in-tree user of git-annotate.  As I\nunderstand it, \"git-blame -c\" should produce compatible output\nto the command, so it might also be a good time to consider\nremoval of git-annotate as well while updating git-cvsserver to\nuse git-blame instead of it.\n\nDo people have reason to favor annotate over blame?  To keep\nexisting people's scripts working I think we should add a small\namount of code to blame.c to default to compatibility mode when\nthe command is called as git-annotate at least for a while, but\nother than that I do not see much issue against scheduling for\nannotate's removal.\n\nI am not going to do anything about this right now (the \"master\"\nbranch is in freeze-and-stabilize phase), but if people have\nissues I overlooked, raise hands now please?\n"},{"id":"28284","messageId":"Pine.LNX.4.63.0610061505360.14200@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5812","inReplyTo":"7vu02jfaec.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-10-06T13:07:04Z","receivedAt":"2006-10-06T13:07:04Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 5 Oct 2006, Junio C Hamano wrote:\n\n> Do people have reason to favor annotate over blame?  To keep\n> existing people's scripts working I think we should add a small\n> amount of code to blame.c to default to compatibility mode when\n> the command is called as git-annotate at least for a while, but\n> other than that I do not see much issue against scheduling for\n> annotate's removal.\n\n+1. Although I would leave git-annotate in git, if only to meet \nexpectations of new git users.\n\nCiao,\nDscho\n"},{"id":"28299","messageId":"20061006161637.GS20017@pasky.or.cz","threadId":"5812","inReplyTo":"7vu02jfaec.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-10-06T16:16:37Z","receivedAt":"2006-10-06T16:16:37Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Thu, Oct 05, 2006 at 10:13:15AM CEST, I got a letter\nwhere Junio C Hamano <junkio@cox.net> said that...\n> It's been a while since we lost git_blame from %actions list.  I\n> am wondering maybe it's time to remove it, after 1.4.3 happens.\n\nI will not mourn git-annotate disappearance (perhaps it could stay an\nalias to git-blame -c; I don't like this UI-wise but we already do this\nkind of thing with git-log / git-whatchanged). I still like gitweb blame\nbetter than blame2 but I'll just patch blame to look like blame2 (or\nbetter) and be happy with that.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\n#!/bin/perl -sp0777i<X+d*lMLa^*lN%0]dsXx++lMlN/dsM0<j]dsj\n$/=unpack('H*',$_);$_=`echo 16dio\\U$k\"SK$/SM$n\\EsN0p[lN*1\nlK[d2%Sa2/d0$^Ixp\"|dc`;s/\\W//g;$_=pack('H*',/((..)*)$/)\n"},{"id":"28306","messageId":"20061006175234.41182.qmail@web31810.mail.mud.yahoo.com","threadId":"5812","inReplyTo":"Pine.LNX.4.63.0610061505360.14200@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-10-06T17:52:34Z","receivedAt":"2006-10-06T17:52:34Z","isPatch":false,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> Hi,\n> \n> On Thu, 5 Oct 2006, Junio C Hamano wrote:\n> \n> > Do people have reason to favor annotate over blame?  To keep\n> > existing people's scripts working I think we should add a small\n> > amount of code to blame.c to default to compatibility mode when\n> > the command is called as git-annotate at least for a while, but\n> > other than that I do not see much issue against scheduling for\n> > annotate's removal.\n> \n> +1. Although I would leave git-annotate in git, if only to meet \n> expectations of new git users.\n\nI agree with Junio's assessment of the situation.\n\n   Luben\n"},{"id":"28318","messageId":"20061006185529.9481.qmail@web31802.mail.mud.yahoo.com","threadId":"5812","inReplyTo":"20061006161637.GS20017@pasky.or.cz","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-10-06T18:55:29Z","receivedAt":"2006-10-06T18:55:29Z","isPatch":false,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- Petr Baudis <pasky@suse.cz> wrote:\n> Dear diary, on Thu, Oct 05, 2006 at 10:13:15AM CEST, I got a letter\n> where Junio C Hamano <junkio@cox.net> said that...\n> > It's been a while since we lost git_blame from %actions list.  I\n> > am wondering maybe it's time to remove it, after 1.4.3 happens.\n> \n> I will not mourn git-annotate disappearance (perhaps it could stay an\n> alias to git-blame -c; I don't like this UI-wise but we already do this\n> kind of thing with git-log / git-whatchanged). I still like gitweb blame\n> better than blame2 but I'll just patch blame to look like blame2 (or\n> better) and be happy with that.\n\nThat's exactly what I don't want to happen.  blame2 is much less\nbloated than blame both in code and in visual appearance and in using\ngit.  And this was the whole point: quick, short, fast and straight\nto the point.\n\nI'd like to keep the blame interface as simple as possible, more\nwindow estate given to the code lines, and as little as possible to\nthe commit id: a clickable commit-8 and now we also have clickable\nline_no to show us the state of the file, is more than enough.\n\nSo far, blame2 has been by far the better \"annotate\" (as it is called\nin the other SCMs) interface I've seen in four other SCMs (some proprietary).\nLet's keep it like this.\n\nWhen data-mining code, what I'm interested in is: where did this line\nof code come from (commit-8), is it a part of a larger chunk (zebra\ncoloring) and how it relates to the surrounding code.  Blame2 is more\nthan efficient at this.\n\n    Luben\n\n\n\n\n> \n> -- \n> \t\t\t\tPetr \"Pasky\" Baudis\n> Stuff: http://pasky.or.cz/\n> #!/bin/perl -sp0777i<X+d*lMLa^*lN%0]dsXx++lMlN/dsM0<j]dsj\n> $/=unpack('H*',$_);$_=`echo 16dio\\U$k\"SK$/SM$n\\EsN0p[lN*1\n> lK[d2%Sa2/d0$^Ixp\"|dc`;s/\\W//g;$_=pack('H*',/((..)*)$/)\n> \n"},{"id":"28320","messageId":"20061006190850.GA4827@pasky.or.cz","threadId":"5812","inReplyTo":"20061006185529.9481.qmail@web31802.mail.mud.yahoo.com","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2006-10-06T19:08:50Z","receivedAt":"2006-10-06T19:08:50Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"(Trimmed cc list to people caring about gitweb.)\n\nDear diary, on Fri, Oct 06, 2006 at 08:55:29PM CEST, I got a letter\nwhere Luben Tuikov <ltuikov@yahoo.com> said that...\n> --- Petr Baudis <pasky@suse.cz> wrote:\n> > I will not mourn git-annotate disappearance (perhaps it could stay an\n> > alias to git-blame -c; I don't like this UI-wise but we already do this\n> > kind of thing with git-log / git-whatchanged). I still like gitweb blame\n> > better than blame2 but I'll just patch blame to look like blame2 (or\n> > better) and be happy with that.\n> \n> That's exactly what I don't want to happen.  blame2 is much less\n> bloated than blame both in code and in visual appearance and in using\n> git.  And this was the whole point: quick, short, fast and straight\n> to the point.\n> \n> I'd like to keep the blame interface as simple as possible, more\n> window estate given to the code lines, and as little as possible to\n> the commit id: a clickable commit-8 and now we also have clickable\n> line_no to show us the state of the file, is more than enough.\n> \n> So far, blame2 has been by far the better \"annotate\" (as it is called\n> in the other SCMs) interface I've seen in four other SCMs (some proprietary).\n> Let's keep it like this.\n> \n> When data-mining code, what I'm interested in is: where did this line\n> of code come from (commit-8), is it a part of a larger chunk (zebra\n> coloring) and how it relates to the surrounding code.  Blame2 is more\n> than efficient at this.\n\nAnd _I_ like to have some general idea about who and when touched given\nline of code, without having to click on a bunch of commit ids or spend\na minute hovering over them patiently. ;-)\n\nIf you really feel strongly about it, we should be able to make the\nindividual columns hideable at view time, e.g. by a tiny bit of\njavascript just changing the display CSS property, which would be really\ncomfortable. My idea about the output would be cg-log -s format, which\nis still reasonably tense. OTOH, there's still some space to burn in the\nLine column.\n\nThere should be no additional load caused by this since we already\nextract this information anyway - we show it as a tooltip.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\n#!/bin/perl -sp0777i<X+d*lMLa^*lN%0]dsXx++lMlN/dsM0<j]dsj\n$/=unpack('H*',$_);$_=`echo 16dio\\U$k\"SK$/SM$n\\EsN0p[lN*1\nlK[d2%Sa2/d0$^Ixp\"|dc`;s/\\W//g;$_=pack('H*',/((..)*)$/)\n"},{"id":"28326","messageId":"20061006202101.41031.qmail@web31804.mail.mud.yahoo.com","threadId":"5812","inReplyTo":"20061006190850.GA4827@pasky.or.cz","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-10-06T20:21:01Z","receivedAt":"2006-10-06T20:21:01Z","isPatch":false,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- Petr Baudis <pasky@suse.cz> wrote:\n> (Trimmed cc list to people caring about gitweb.)\n> \n> Dear diary, on Fri, Oct 06, 2006 at 08:55:29PM CEST, I got a letter\n> where Luben Tuikov <ltuikov@yahoo.com> said that...\n> > --- Petr Baudis <pasky@suse.cz> wrote:\n> > > I will not mourn git-annotate disappearance (perhaps it could stay an\n> > > alias to git-blame -c; I don't like this UI-wise but we already do this\n> > > kind of thing with git-log / git-whatchanged). I still like gitweb blame\n> > > better than blame2 but I'll just patch blame to look like blame2 (or\n> > > better) and be happy with that.\n> > \n> > That's exactly what I don't want to happen.  blame2 is much less\n> > bloated than blame both in code and in visual appearance and in using\n> > git.  And this was the whole point: quick, short, fast and straight\n> > to the point.\n> > \n> > I'd like to keep the blame interface as simple as possible, more\n> > window estate given to the code lines, and as little as possible to\n> > the commit id: a clickable commit-8 and now we also have clickable\n> > line_no to show us the state of the file, is more than enough.\n> > \n> > So far, blame2 has been by far the better \"annotate\" (as it is called\n> > in the other SCMs) interface I've seen in four other SCMs (some proprietary).\n> > Let's keep it like this.\n> > \n> > When data-mining code, what I'm interested in is: where did this line\n> > of code come from (commit-8), is it a part of a larger chunk (zebra\n> > coloring) and how it relates to the surrounding code.  Blame2 is more\n> > than efficient at this.\n> \n> And _I_ like to have some general idea about who and when touched given\n> line of code, without having to click on a bunch of commit ids or spend\n> a minute hovering over them patiently. ;-)\n\nWell understood.\n\nAlthough, when I'm chasing after a problem, I care much about\ntracing back through the commit history as opposed to who did\nwhat and when, not until I've nailed the regressive commit (is when\nI care who/what/when/how).\n\n> If you really feel strongly about it, we should be able to make the\n> individual columns hideable at view time, e.g. by a tiny bit of\n> javascript just changing the display CSS property, which would be really\n> comfortable. My idea about the output would be cg-log -s format, which\n> is still reasonably tense. OTOH, there's still some space to burn in the\n> Line column.\n\nPoint taken.\n\nNow since I don't want to turn blame2 into a circus, and since the whole\npoint of blame2 (over blame) was to stay away from it being a circus,\nand to be fast and to the point, can we just keep git_blame() around\nwhich gives you this extra information right in the main screen?\n\n    Luben\nP.S. The alternative is to create a next git_blame{N+1}() each time\ngit_blameN() becomes git_blame{N-1}().\n"},{"id":"28339","messageId":"45273D16.7050205@catalyst.net.nz","threadId":"5812","inReplyTo":"20061006175234.41182.qmail@web31810.mail.mud.yahoo.com","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Martin Langhoff (CatalystIT)","fromEmail":"martin@catalyst.net.nz","sentAt":"2006-10-07T05:37:26Z","receivedAt":"2006-10-07T05:37:26Z","isPatch":false,"sender":{"key":"martin@laptop.org","avatar":null},"body":"Luben Tuikov wrote:\n>>>Do people have reason to favor annotate over blame?  To keep\n>>>existing people's scripts working I think we should add a small\n>>>amount of code to blame.c to default to compatibility mode when\n>>>the command is called as git-annotate at least for a while, but\n>>>other than that I do not see much issue against scheduling for\n>>>annotate's removal.\n>>\n>>+1. Although I would leave git-annotate in git, if only to meet \n>>expectations of new git users.\n> \n> \n> I agree with Junio's assessment of the situation.\n\n+1 -- I need to test that the switch to git-blame for git-cvsserver \nworks well for Eclipse end users. Will try and fit that next week \nsomehow ;-)\n\n\nmartin\n-- \n-----------------------------------------------------------------------\nMartin @ Catalyst .Net .NZ  Ltd, PO Box 11-053, Manners St,  Wellington\nWEB: http://catalyst.net.nz/           PHYS: Level 2, 150-154 Willis St\nOFFICE: +64(4)916-7224                              MOB: +64(21)364-017\n       Make things as simple as possible, but no simpler - Einstein\n-----------------------------------------------------------------------\n"},{"id":"28451","messageId":"11603899252662-git-send-email-ryan@michonline.com","threadId":"5812","inReplyTo":"7vu02jfaec.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH 1/1] Remove git-annotate.perl and create a builtin-alias for git-blame","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-10-09T10:32:05Z","receivedAt":"2006-10-09T10:32:05Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"Signed-off-by: Ryan Anderson <ryan@michonline.com>\n---\n\nI've clearly been too busy to actually fix this, and blame works, so,\nlet's create an internal alias and delete annotate.\n\n(The tests still pass, for whatever that's worth.)\n---\n Makefile           |    3 +-\n builtin-annotate.c |   25 ++\n builtin.h          |    1 +\n git-annotate.perl  |  708 ----------------------------------------------------\n git.c              |    1 +\n 5 files changed, 29 insertions(+), 709 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 2c7c338..7e62e76 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -173,7 +173,7 @@ SCRIPT_SH = \\\n SCRIPT_PERL = \\\n \tgit-archimport.perl git-cvsimport.perl git-relink.perl \\\n \tgit-shortlog.perl git-rerere.perl \\\n-\tgit-annotate.perl git-cvsserver.perl \\\n+\tgit-cvsserver.perl \\\n \tgit-svnimport.perl git-cvsexportcommit.perl \\\n \tgit-send-email.perl git-svn.perl\n \n@@ -265,6 +265,7 @@ LIB_OBJS = \\\n \n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\n+\tbuiltin-annotate.o \\\n \tbuiltin-apply.o \\\n \tbuiltin-archive.o \\\n \tbuiltin-cat-file.o \\\ndiff --git a/builtin-annotate.c b/builtin-annotate.c\nnew file mode 100644\nindex 0000000..2655e60\n--- /dev/null\n+++ b/builtin-annotate.c\n@@ -0,0 +1,25 @@\n+/*\n+ * \"git annotate\" builtin alias\n+ *\n+ * Copyright (C) 2006 Ryan Anderson\n+ */\n+#include \"git-compat-util.h\"\n+#include \"exec_cmd.h\"\n+\n+int cmd_annotate(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char **nargv;\n+\tint i;\n+\tnargv = xmalloc(sizeof(char *) * (argc + 2));\n+\t\n+\tnargv[0] = \"blame\";\n+\tnargv[1] = \"-c\";\n+\t\n+\tfor (i = 1; i < argc; i++) {\n+\t\tnargv[i+1] = argv[i];\n+\t}\n+\tnargv[argc + 1] = NULL;\n+\n+\treturn execv_git_cmd(nargv);\n+}\n+\t\ndiff --git a/builtin.h b/builtin.h\nindex f9fa9ff..2c5d900 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -13,6 +13,7 @@ extern void stripspace(FILE *in, FILE *o\n extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n+extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\n extern int cmd_archive(int argc, const char **argv, const char *prefix);\n extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\ndiff --git a/git-annotate.perl b/git-annotate.perl\ndeleted file mode 100755\nindex 215ed26..0000000\n--- a/git-annotate.perl\n+++ /dev/null\n@@ -1,708 +0,0 @@\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-use Getopt::Long;\n-use POSIX qw(strftime gmtime);\n-use File::Basename qw(basename dirname);\n-\n-sub usage() {\n-\tprint STDERR \"Usage: ${\\basename $0} [-s] [-S revs-file] file [ revision ]\n-\t-l, --long\n-\t\t\tShow long rev (Defaults off)\n-\t-t, --time\n-\t\t\tShow raw timestamp (Defaults off)\n-\t-r, --rename\n-\t\t\tFollow renames (Defaults on).\n-\t-S, --rev-file revs-file\n-\t\t\tUse revs from revs-file instead of calling git-rev-list\n-\t-h, --help\n-\t\t\tThis message.\n-\";\n-\n-\texit(1);\n-}\n-\n-our ($help, $longrev, $rename, $rawtime, $starting_rev, $rev_file) = (0, 0, 1);\n-\n-my $rc = GetOptions(\t\"long|l\" => \\$longrev,\n-\t\t\t\"time|t\" => \\$rawtime,\n-\t\t\t\"help|h\" => \\$help,\n-\t\t\t\"rename|r\" => \\$rename,\n-\t\t\t\"rev-file|S=s\" => \\$rev_file);\n-if (!$rc or $help or !@ARGV) {\n-\tusage();\n-}\n-\n-my $filename = shift @ARGV;\n-if (@ARGV) {\n-\t$starting_rev = shift @ARGV;\n-}\n-\n-my @stack = (\n-\t{\n-\t\t'rev' => defined $starting_rev ? $starting_rev : \"HEAD\",\n-\t\t'filename' => $filename,\n-\t},\n-);\n-\n-our @filelines = ();\n-\n-if (defined $starting_rev) {\n-\t@filelines = git_cat_file($starting_rev, $filename);\n-} else {\n-\topen(F,\"<\",$filename)\n-\t\tor die \"Failed to open filename: $!\";\n-\n-\twhile(<F>) {\n-\t\tchomp;\n-\t\tpush @filelines, $_;\n-\t}\n-\tclose(F);\n-\n-}\n-\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 (!defined($rev)) {\n-\t\t\t$rev = \"\";\n-\t\t}\n-\t\t$revs{$rev}{'filename'} = $bound->{'filename'};\n-\t\tif (scalar @parents > 0) {\n-\t\t\t$revs{$rev}{'parents'} = \\@parents;\n-\t\t\tnext;\n-\t\t}\n-\n-\t\tif (!$rename) {\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( defined $starting_rev ? $head : 'dirty');\n-unless (defined $starting_rev) {\n-\tmy $diff = open_pipe(\"git\",\"diff\",\"HEAD\", \"--\",$filename)\n-\t\tor die \"Failed to call git diff to check for dirty state: $!\";\n-\n-\t_git_diff_parse($diff, [$head], \"dirty\", (\n-\t\t\t\t'author' => gitvar_name(\"GIT_AUTHOR_IDENT\"),\n-\t\t\t\t'author_date' => sprintf(\"%s +0000\",time()),\n-\t\t\t\t)\n-\t\t\t);\n-\tclose($diff);\n-}\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 (!$longrev && 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(\"%s\\t(%10s\\t%10s\\t%d)%s\\n\", $rev, $committer,\n-\t\tformat_date($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], '', '', '', 1];\n-\t\t\t# line,\n-\t\t\t# rev,\n-\t\t\t# author,\n-\t\t\t# date,\n-\t\t\t# 1 <-- belongs to the original file.\n-\t}\n-\t$revs{$rev}{'lines'} = \\@filelines;\n-}\n-\n-\n-sub handle_rev {\n-\tmy $revseen = 0;\n-\tmy %seen;\n-\twhile (my $rev = shift @revqueue) {\n-\t\tnext if $seen{$rev}++;\n-\n-\t\tmy %revinfo = git_commit_info($rev);\n-\n-\t\tif (exists $revs{$rev}{parents} &&\n-\t\t    scalar @{$revs{$rev}{parents}} != 0) {\n-\n-\t\t\tgit_diff_parse($revs{$rev}{'parents'}, $rev, %revinfo);\n-\t\t\tpush @revqueue, @{$revs{$rev}{'parents'}};\n-\n-\t\t} else {\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 < @{$revs{$rev}{lines}}; $i++) {\n-\t\t\t\tif (ref ${$revs{$rev}{lines}}[$i] eq '' || ${$revs{$rev}{lines}}[$i][1] eq '') {\n-\t\t\t\t\tclaim_line($i, $rev, $revs{$rev}{lines}, %revinfo);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t}\n-}\n-\n-\n-sub git_rev_list {\n-\tmy ($rev, $file) = @_;\n-\n-\tmy $revlist;\n-\tif ($rev_file) {\n-\t\topen($revlist, '<' . $rev_file)\n-\t\t    or die \"Failed to open $rev_file : $!\";\n-\t} else {\n-\t\t$revlist = open_pipe(\"git-rev-list\",\"--parents\",\"--remove-empty\",$rev,\"--\",$file)\n-\t\t\tor die \"Failed to exec git-rev-list: $!\";\n-\t}\n-\n-\tmy @revs;\n-\twhile(my $line = <$revlist>) {\n-\t\tchomp $line;\n-\t\tmy ($rev, @parents) = split /\\s+/, $line;\n-\t\tpush @revs, [ $rev, @parents ];\n-\t}\n-\tclose($revlist);\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-\tmy $patch = open_pipe(\"git-diff-tree\", \"-M50\", \"-r\",\"--name-status\", \"-z\",\"$rev\")\n-\t\tor die \"Failed to exec git-diff: $!\";\n-\n-\tlocal $/ = \"\\0\";\n-\tmy %bound;\n-\tmy $junk = <$patch>;\n-\twhile (my $change = <$patch>) {\n-\t\tchomp $change;\n-\t\tmy $filename = <$patch>;\n-\t\tif (!defined $filename) {\n-\t\t\tnext;\n-\t\t}\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 = <$patch>;\n-\t\t\tchomp $filename;\n-\t\t\tif ( $file eq $filename ) {\n-\t\t\t\tmy $parent = git_find_parent($rev, $oldfilename);\n-\t\t\t\t@bound{'rev','filename'} = ($parent, $oldfilename);\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t}\n-\tclose($patch);\n-\n-\treturn \\%bound;\n-}\n-\n-\n-sub git_find_parent {\n-\tmy ($rev, $filename) = @_;\n-\n-\tmy $revparent = open_pipe(\"git-rev-list\",\"--remove-empty\", \"--parents\",\"--max-count=1\",\"$rev\",\"--\",$filename)\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-sub git_find_all_parents {\n-\tmy ($rev) = @_;\n-\n-\tmy $revparent = open_pipe(\"git-rev-list\",\"--remove-empty\", \"--parents\",\"--max-count=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 ($origrev, @parents) = split m/\\s+/, $parentline;\n-\n-\tclose($revparent);\n-\n-\treturn @parents;\n-}\n-\n-sub git_merge_base {\n-\tmy ($rev1, $rev2) = @_;\n-\n-\tmy $mb = open_pipe(\"git-merge-base\", $rev1, $rev2)\n-\t        or die \"Failed to open git-merge-base: $!\";\n-\n-\tmy $base = <$mb>;\n-\tchomp $base;\n-\n-\tclose($mb);\n-\n-\treturn $base;\n-}\n-\n-# Construct a set of pseudo parents that are in the same order,\n-# and the same quantity as the real parents,\n-# but whose SHA1s are as similar to the logical parents\n-# as possible.\n-sub get_pseudo_parents {\n-\tmy ($all, $fake) = @_;\n-\n-\tmy @all = @$all;\n-\tmy @fake = @$fake;\n-\n-\tmy @pseudo;\n-\n-\tmy %fake = map {$_ => 1} @fake;\n-\tmy %seenfake;\n-\n-\tmy $fakeidx = 0;\n-\tforeach my $p (@all) {\n-\t\tif (exists $fake{$p}) {\n-\t\t\tif ($fake[$fakeidx] ne $p) {\n-\t\t\t\tdie sprintf(\"parent mismatch: %s != %s\\nall:%s\\nfake:%s\\n\",\n-\t\t\t\t\t    $fake[$fakeidx], $p,\n-\t\t\t\t\t    join(\", \", @all),\n-\t\t\t\t\t    join(\", \", @fake),\n-\t\t\t\t\t   );\n-\t\t\t}\n-\n-\t\t\tpush @pseudo, $p;\n-\t\t\t$fakeidx++;\n-\t\t\t$seenfake{$p}++;\n-\n-\t\t} else {\n-\t\t\tmy $base = git_merge_base($fake[$fakeidx], $p);\n-\t\t\tif ($base ne $fake[$fakeidx]) {\n-\t\t\t\tdie sprintf(\"Result of merge-base doesn't match fake: %s,%s != %s\\n\",\n-\t\t\t\t       $fake[$fakeidx], $p, $base);\n-\t\t\t}\n-\n-\t\t\t# The details of how we parse the diffs\n-\t\t\t# mean that we cannot have a duplicate\n-\t\t\t# revision in the list, so if we've already\n-\t\t\t# seen the revision we would normally add, just use\n-\t\t\t# the actual revision.\n-\t\t\tif ($seenfake{$base}) {\n-\t\t\t\tpush @pseudo, $p;\n-\t\t\t} else {\n-\t\t\t\tpush @pseudo, $base;\n-\t\t\t\t$seenfake{$base}++;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\treturn @pseudo;\n-}\n-\n-\n-# Get a diff between the current revision and a parent.\n-# Record the commit information that results.\n-sub git_diff_parse {\n-\tmy ($parents, $rev, %revinfo) = @_;\n-\n-\tmy @pseudo_parents;\n-\tmy @command = (\"git-diff-tree\");\n-\tmy $revision_spec;\n-\n-\tif (scalar @$parents == 1) {\n-\n-\t\t$revision_spec = join(\"..\", $parents->[0], $rev);\n-\t\t@pseudo_parents = @$parents;\n-\t} else {\n-\t\tmy @all_parents = git_find_all_parents($rev);\n-\n-\t\tif (@all_parents !=  @$parents) {\n-\t\t\t@pseudo_parents = get_pseudo_parents(\\@all_parents, $parents);\n-\t\t} else {\n-\t\t\t@pseudo_parents = @$parents;\n-\t\t}\n-\n-\t\t$revision_spec = $rev;\n-\t\tpush @command, \"-c\";\n-\t}\n-\n-\tmy @filenames = ( $revs{$rev}{'filename'} );\n-\n-\tforeach my $parent (@$parents) {\n-\t\tpush @filenames, $revs{$parent}{'filename'};\n-\t}\n-\n-\tpush @command, \"-p\", \"-M\", $revision_spec, \"--\", @filenames;\n-\n-\n-\tmy $diff = open_pipe( @command )\n-\t\tor die \"Failed to call git-diff for annotation: $!\";\n-\n-\t_git_diff_parse($diff, \\@pseudo_parents, $rev, %revinfo);\n-\n-\tclose($diff);\n-}\n-\n-sub _git_diff_parse {\n-\tmy ($diff, $parents, $rev, %revinfo) = @_;\n-\n-\tmy $ri = 0;\n-\n-\tmy $slines = $revs{$rev}{'lines'};\n-\tmy (%plines, %pi);\n-\n-\tmy $gotheader = 0;\n-\tmy ($remstart);\n-\tmy $parent_count = @$parents;\n-\n-\tmy $diff_header_regexp = \"^@\";\n-\t$diff_header_regexp .= \"@\" x @$parents;\n-\t$diff_header_regexp .= ' -\\d+,\\d+' x @$parents;\n-\t$diff_header_regexp .= ' \\+(\\d+),\\d+';\n-\t$diff_header_regexp .= \" \" . (\"@\" x @$parents);\n-\n-\tmy %claim_regexps;\n-\tmy $allparentplus = '^' . '\\\\+' x @$parents . '(.*)$';\n-\n-\t{\n-\t\tmy $i = 0;\n-\t\tforeach my $parent (@$parents) {\n-\n-\t\t\t$pi{$parent} = 0;\n-\t\t\tmy $r = '^' . '.' x @$parents . '(.*)$';\n-\t\t\tmy $p = $r;\n-\t\t\tsubstr($p,$i+1, 1) = '\\\\+';\n-\n-\t\t\tmy $m = $r;\n-\t\t\tsubstr($m,$i+1, 1) = '-';\n-\n-\t\t\t$claim_regexps{$parent}{plus} = $p;\n-\t\t\t$claim_regexps{$parent}{minus} = $m;\n-\n-\t\t\t$plines{$parent} = [];\n-\n-\t\t\t$i++;\n-\t\t}\n-\t}\n-\n-\tDIFF:\n-\twhile(<$diff>) {\n-\t\tchomp;\n-\t\t#printf(\"%d:%s:\\n\", $gotheader, $_);\n-\t\tif (m/$diff_header_regexp/) {\n-\t\t\t$remstart = $1 - 1;\n-\t\t\t# (0-based arrays)\n-\n-\t\t\t$gotheader = 1;\n-\n-\t\t\tforeach my $parent (@$parents) {\n-\t\t\t\tfor (my $i = $ri; $i < $remstart; $i++) {\n-\t\t\t\t\t$plines{$parent}[$pi{$parent}++] = $slines->[$i];\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\t$ri = $remstart;\n-\n-\t\t\tnext DIFF;\n-\n-\t\t} elsif (!$gotheader) {\n-\t\t\t# Skip over the leadin.\n-\t\t\tnext DIFF;\n-\t\t}\n-\n-\t\tif (m/^\\\\/) {\n-\t\t\t;\n-\t\t\t# Skip \\No newline at end of file.\n-\t\t\t# But this can be internationalized, so only look\n-\t\t\t# for an initial \\\n-\n-\t\t} else {\n-\t\t\tmy %claims = ();\n-\t\t\tmy $negclaim = 0;\n-\t\t\tmy $allclaimed = 0;\n-\t\t\tmy $line;\n-\n-\t\t\tif (m/$allparentplus/) {\n-\t\t\t\tclaim_line($ri, $rev, $slines, %revinfo);\n-\t\t\t\t$allclaimed = 1;\n-\n-\t\t\t}\n-\n-\t\t\tPARENT:\n-\t\t\tforeach my $parent (keys %claim_regexps) {\n-\t\t\t\tmy $m = $claim_regexps{$parent}{minus};\n-\t\t\t\tmy $p = $claim_regexps{$parent}{plus};\n-\n-\t\t\t\tif (m/$m/) {\n-\t\t\t\t\t$line = $1;\n-\t\t\t\t\t$plines{$parent}[$pi{$parent}++] = [ $line, '', '', '', 0 ];\n-\t\t\t\t\t$negclaim++;\n-\n-\t\t\t\t} elsif (m/$p/) {\n-\t\t\t\t\t$line = $1;\n-\t\t\t\t\tif (get_line($slines, $ri) eq $line) {\n-\t\t\t\t\t\t# Found a match, claim\n-\t\t\t\t\t\t$claims{$parent}++;\n-\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tdie sprintf(\"Sync error: %d\\n|%s\\n|%s\\n%s => %s\\n\",\n-\t\t\t\t\t\t\t\t$ri, $line,\n-\t\t\t\t\t\t\t\tget_line($slines, $ri),\n-\t\t\t\t\t\t\t\t$rev, $parent);\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\tif (%claims) {\n-\t\t\t\tforeach my $parent (@$parents) {\n-\t\t\t\t\tnext if $claims{$parent} || $allclaimed;\n-\t\t\t\t\t$plines{$parent}[$pi{$parent}++] = $slines->[$ri];\n-\t\t\t\t\t    #[ $line, '', '', '', 0 ];\n-\t\t\t\t}\n-\t\t\t\t$ri++;\n-\n-\t\t\t} elsif ($negclaim) {\n-\t\t\t\tnext DIFF;\n-\n-\t\t\t} else {\n-\t\t\t\tif (substr($_,scalar @$parents) ne get_line($slines,$ri) ) {\n-\t\t\t\t        foreach my $parent (@$parents) {\n-\t\t\t\t\t\tprintf(\"parent %s is on line %d\\n\", $parent, $pi{$parent});\n-\t\t\t\t\t}\n-\n-\t\t\t\t\tmy @context;\n-\t\t\t\t\tfor (my $i = -2; $i < 2; $i++) {\n-\t\t\t\t\t\tpush @context, get_line($slines, $ri + $i);\n-\t\t\t\t\t}\n-\t\t\t\t\tmy $context = join(\"\\n\", @context);\n-\n-\t\t\t\t\tmy $justline = substr($_, scalar @$parents);\n-\t\t\t\t\tdie sprintf(\"Line %d, does not match:\\n|%s|\\n|%s|\\n%s\\n\",\n-\t\t\t\t\t\t    $ri,\n-\t\t\t\t\t\t    $justline,\n-\t\t\t\t\t\t    $context);\n-\t\t\t\t}\n-\t\t\t\tforeach my $parent (@$parents) {\n-\t\t\t\t\t$plines{$parent}[$pi{$parent}++] = $slines->[$ri];\n-\t\t\t\t}\n-\t\t\t\t$ri++;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\tfor (my $i = $ri; $i < @{$slines} ; $i++) {\n-\t\tforeach my $parent (@$parents) {\n-\t\t\tpush @{$plines{$parent}}, $slines->[$ri];\n-\t\t}\n-\t\t$ri++;\n-\t}\n-\n-\tforeach my $parent (@$parents) {\n-\t\t$revs{$parent}{lines} = $plines{$parent};\n-\t}\n-\n-\treturn;\n-}\n-\n-sub get_line {\n-\tmy ($lines, $index) = @_;\n-\n-\treturn ref $lines->[$index] ne '' ? $lines->[$index][0] : $lines->[$index];\n-}\n-\n-sub git_cat_file {\n-\tmy ($rev, $filename) = @_;\n-\treturn () unless defined $rev && defined $filename;\n-\n-\tmy $blob = git_ls_tree($rev, $filename);\n-\tdie \"Failed to find a blob for $filename in rev $rev\\n\" if !defined $blob;\n-\n-\tmy $catfile = open_pipe(\"git\",\"cat-file\", \"blob\", $blob)\n-\t\tor die \"Failed to git-cat-file blob $blob (rev $rev, file $filename): \" . $!;\n-\n-\tmy @lines;\n-\twhile(<$catfile>) {\n-\t\tchomp;\n-\t\tpush @lines, $_;\n-\t}\n-\tclose($catfile);\n-\n-\treturn @lines;\n-}\n-\n-sub git_ls_tree {\n-\tmy ($rev, $filename) = @_;\n-\n-\tmy $lstree = open_pipe(\"git\",\"ls-tree\",$rev,$filename)\n-\t\tor die \"Failed to call git ls-tree: $!\";\n-\n-\tmy ($mode, $type, $blob, $tfilename);\n-\twhile(<$lstree>) {\n-\t\tchomp;\n-\t\t($mode, $type, $blob, $tfilename) = split(/\\s+/, $_, 4);\n-\t\tlast if ($tfilename eq $filename);\n-\t}\n-\tclose($lstree);\n-\n-\treturn $blob if ($tfilename eq $filename);\n-\tdie \"git-ls-tree failed to find blob for $filename\";\n-\n-}\n-\n-\n-\n-sub claim_line {\n-\tmy ($floffset, $rev, $lines, %revinfo) = @_;\n-\tmy $oline = get_line($lines, $floffset);\n-\t@{$lines->[$floffset]} = ( $oline, $rev,\n-\t\t$revinfo{'author'}, $revinfo{'author_date'} );\n-\t#printf(\"Claiming line %d with rev %s: '%s'\\n\",\n-\t#\t\t$floffset, $rev, $oline) if 1;\n-}\n-\n-sub git_commit_info {\n-\tmy ($rev) = @_;\n-\tmy $commit = open_pipe(\"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-\n-sub format_date {\n-\tif ($rawtime) {\n-\t\treturn $_[0];\n-\t}\n-\tmy ($timestamp, $timezone) = split(' ', $_[0]);\n-\tmy $minutes = abs($timezone);\n-\t$minutes = int($minutes / 100) * 60 + ($minutes % 100);\n-\tif ($timezone < 0) {\n-\t    $minutes = -$minutes;\n-\t}\n-\tmy $t = $timestamp + $minutes * 60;\n-\treturn strftime(\"%Y-%m-%d %H:%M:%S \" . $timezone, gmtime($t));\n-}\n-\n-# Copied from git-send-email.perl - We need a Git.pm module..\n-sub gitvar {\n-    my ($var) = @_;\n-    my $fh;\n-    my $pid = open($fh, '-|');\n-    die \"$!\" unless defined $pid;\n-    if (!$pid) {\n-\texec('git-var', $var) or die \"$!\";\n-    }\n-    my ($val) = <$fh>;\n-    close $fh or die \"$!\";\n-    chomp($val);\n-    return $val;\n-}\n-\n-sub gitvar_name {\n-    my ($name) = @_;\n-    my $val = gitvar($name);\n-    my @field = split(/\\s+/, $val);\n-    return join(' ', @field[0...(@field-4)]);\n-}\n-\n-sub open_pipe {\n-\tif ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {\n-\t\treturn open_pipe_activestate(@_);\n-\t} else {\n-\t\treturn open_pipe_normal(@_);\n-\t}\n-}\n-\n-sub open_pipe_activestate {\n-\ttie *fh, \"Git::ActiveStatePipe\", @_;\n-\treturn *fh;\n-}\n-\n-sub open_pipe_normal {\n-\tmy (@execlist) = @_;\n-\n-\tmy $pid = open my $kid, \"-|\";\n-\tdefined $pid or die \"Cannot fork: $!\";\n-\n-\tunless ($pid) {\n-\t\texec @execlist;\n-\t\tdie \"Cannot exec @execlist: $!\";\n-\t}\n-\n-\treturn $kid;\n-}\n-\n-package Git::ActiveStatePipe;\n-use strict;\n-\n-sub TIEHANDLE {\n-\tmy ($class, @params) = @_;\n-\tmy $cmdline = join \" \", @params;\n-\tmy  @data = qx{$cmdline};\n-\tbless { i => 0, data => \\@data }, $class;\n-}\n-\n-sub READLINE {\n-\tmy $self = shift;\n-\tif ($self->{i} >= scalar @{$self->{data}}) {\n-\t\treturn undef;\n-\t}\n-\treturn $self->{'data'}->[ $self->{i}++ ];\n-}\n-\n-sub CLOSE {\n-\tmy $self = shift;\n-\tdelete $self->{data};\n-\tdelete $self->{i};\n-}\n-\n-sub EOF {\n-\tmy $self = shift;\n-\treturn ($self->{i} >= scalar @{$self->{data}});\n-}\ndiff --git a/git.c b/git.c\nindex d7103a4..b32ee0f 100644\n--- a/git.c\n+++ b/git.c\n@@ -219,6 +219,7 @@ static void handle_internal_command(int \n \t\tint option;\n \t} commands[] = {\n \t\t{ \"add\", cmd_add, RUN_SETUP },\n+\t\t{ \"annotate\", cmd_annotate, },\n \t\t{ \"apply\", cmd_apply },\n \t\t{ \"archive\", cmd_archive },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n-- \n1.4.2.3.gbf37d\n"},{"id":"28452","messageId":"20061009103710.GX1558@h4x0r5.com","threadId":"5812","inReplyTo":"7vu02jfaec.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: perhaps time to remove git_blame from gitweb, and git-annotate?","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-10-09T10:37:18Z","receivedAt":"2006-10-09T10:37:18Z","isPatch":false,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"On Thu, Oct 05, 2006 at 01:13:15AM -0700, Junio C Hamano wrote:\n> It's been a while since we lost git_blame from %actions list.  I\n> am wondering maybe it's time to remove it, after 1.4.3 happens.\n\nI certainly have no objection.  In fact, I sent a patch a moment ago.\n(I didn't keep the cc: on it, I figured there was too high a chance of\nmishap when pasting the cc: list.)\n\nI forgot to mentio it in the email, but I have the change pullable from:\nhttp://h4x0r5.com/~ryan/git/ryan.git/ del-annotate\n(and gitwebed from http://h4x0r5.com/~ryan/gitweb.cgi )\n-- \n\nRyan Anderson\n  sometimes Pug Majere\n"},{"id":"28464","messageId":"7v3b9xazlk.fsf_-_@assigned-by-dhcp.cox.net","threadId":"5812","inReplyTo":"20061009103710.GX1558@h4x0r5.com","subject":"Obituary for git-annotate","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-09T22:33:43Z","receivedAt":"2006-10-09T22:33:43Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ryan Anderson <ryan@michonline.com> writes:\n\n> On Thu, Oct 05, 2006 at 01:13:15AM -0700, Junio C Hamano wrote:\n>> It's been a while since we lost git_blame from %actions list.  I\n>> am wondering maybe it's time to remove it, after 1.4.3 happens.\n>\n> I certainly have no objection.  In fact, I sent a patch a moment ago.\n> (I didn't keep the cc: on it, I figured there was too high a chance of\n> mishap when pasting the cc: list.)\n\nSo it's finally settled between annotate and blame.  It is kind\nof sad to see one of them had to go while these stem from\nslightly different algorithm sketches [*1*].  But for 8 months\nof its existence, it served us well as the git-cvsserver\nbackend.  May it rest in peace.\n\nHaving said that, there are a few things in git-blame that\ninterested people may want to further look into.\n\nAnnotation by git-blame is done by \"passing the blame to\nparents\" principle.  You start from the final form of the blob,\nand compare it with its counterpart in the parent version\n(rename detection is used to pick which file in the parent\nversion to compare against).  The lines the commit inherited\nfrom its parent are not responsibility of the child so the\nalgorithm passes blame on them to the parent.  The lines the\ncommit changed from the parent are blamed on the child.  \n\nWhen this is done, the parent \"temporarily\" takes responsibility\nfor those lines that child did not change -- it just becomes\n\"suspect\" for those lines when we compare parent and child.  And\nthen the algorithm goes further down the ancestry to give the\nparent the chance to exonerate itself by passing blames for the\nlines it is suspect for, by passing the blame to its parent.\n\nWhen sifting the lines into \"inherited\" and \"our\nresponsibility\", internally git-blame runs \"diff\", which\nexpresses the changes as \"these lines are deleted and these are\ninserted by the child\".  Lines outside are clearly inherited\nfrom the parent.\n\nThis has an interesting effect on blame output.  \n\nSuppose the original file had two groups of lines; group A\nfollowed by group B.  A commit changes the file so that it has\ngroup B followed by group A.  What git-blame sees as diff\nbetween the two is either:\n\n    -A\n     B\n    +A \n\nor\n\n    +B\n     A\n    -B    \n\nIn either case, it would end up giving blame to the child for\none group (the first diff blames the child for A lines) and pass\nthe blame for the other one to the parent.\n\nIf we used something other than \"diff\" (Delete Insert File vs\nFile ;-)), that expresses changes as \"these are moved from\nthere, these are inserted anew\" (call that \"miff\"), then we\nshould be able to assign blame more accurately.  The above\nexample case would be expressed as \"group A came from the top\npart of the parent, group B came from the bottom part of the\nparent\".  Passing of the blame based on that expression would\nblame the child for neither group of lines.\n\nFurther, if we use \"ciff\" that expresses changes as \"these are\ncopied from there, these are inserted anew\", we can do a lot\nmore interesting thing.  We can track code movement across\nfiles, and that is not limited to renames.\n\nFor example, suppose that the parent had files F1 and F2 and the\nchild moved a function and copy-and-pasted a comment block from\nF1 to F2, and we are annotating lines in F2.\n\nThe current git-blame sees that the function and comment block\nappeared from nowhere into F2 and blames the child for them.\nHowever, when annotating F2, we could:\n\n - use concatenation of all files in the parent that was\n   modified between parent and child (or just \"all files in the\n   parent\" -- the difference is exactly like plain -C vs -C\n   --find-copies-harder) as the source image;\n\n - use lines of F2 in the child as the destination image;\n\n - run \"ciff\" algorithm to see where each line of F2 in the\n   child came from (either copied from existing file somewhere\n   in the parent, or inserted anew by the child).\n\nThis would find that the function and the comment block were\ncopied from F1 in the parent.\n\nAn interesting property of this is that when the parent passes\ndown the blame for the function the child moved in the above\nexample further to its parent, we do not necessarily have to run\n\"ciff\" algorithm on file F1 as the whole.  We only need to give\nthe function (i.e. the lines the parent is still suspect for)\n[*2*].  So this makes destination image fed to \"ciff\" smaller as\nmore lines are blamed on children while digging deeper, which\nmay compensate for the need to feed not just that file but other\nfiles for copy detection on the source image side.\n\n\n[*1*]\n\nI think annotate follows this sketch\nhttp://thread.gmane.org/gmane.comp.version-control.git/14819/focus=14867\n\nwhile blame follows this sketch\nhttp://thread.gmane.org/gmane.comp.version-control.git/5453/focus=5483\n\n\n[*2*]\n\nwe may need to use a handful surrounding context lines for\nbetter identification of copy source by the \"ciff\" algorithm but\nthat is a minor implementation detail.\n"}]}