{"thread":{"id":"4757","subject":"[PATCH 1/2] annotate: Support annotation of files on other revisions.","startedAt":"2006-07-04T01:30:00Z","lastAt":"2006-07-04T08:46:55Z","messageCount":6,"participants":["Ryan Anderson","Eric Wong","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"23162","messageId":"11519766021208-git-send-email-ryan@michonline.com","threadId":"4757","inReplyTo":null,"subject":"[PATCH 0/2] Fix handling of merges in git-annotate","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-07-04T01:30:00Z","receivedAt":"2006-07-04T01:30:00Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"This 2-patch series is a major overhaul to the way git-annotate calculates the blame for each line.\n\nInstead of parsing each diff in reverse, this uses the output from \"git\ndiff-tree --combined\", so that merges can be handled sanely.\n\nRyan Anderson:\n      annotate: Support annotation of files on other revisions.\n      annotate: Correct most merge following to annotate correctly.\n\n git-annotate.perl   |  197 ++++++++++++++++++++++++++++++++++++---------------\n t/t8001-annotate.sh |    6 ++\n 2 files changed, 145 insertions(+), 58 deletions(-)\n"},{"id":"23160","messageId":"11519766033852-git-send-email-ryan@michonline.com","threadId":"4757","inReplyTo":"11519766021208-git-send-email-ryan@michonline.com","subject":"[PATCH 1/2] annotate: Support annotation of files on other revisions.","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-07-04T01:30:01Z","receivedAt":"2006-07-04T01:30:01Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"This is a bug fix, and cleans up one or two other things spotted during the\ncourse of tracking down the main bug here.\n\nAlso, the test-suite is updated to reflect this case.\n\nSigned-off-by: Ryan Anderson <ryan@michonline.com>\n(cherry picked from 2f7554b4db3ab2c2d3866b160245c91c9236fc9a commit)\n---\n t/t8001-annotate.sh |    6 ++++++\n 1 files changed, 6 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh\nindex 2496397..70e2ad2 100755\n--- a/t/t8001-annotate.sh\n+++ b/t/t8001-annotate.sh\n@@ -6,4 +6,10 @@ test_description='git-annotate'\n PROG='git annotate'\n . ../annotate-tests.sh\n \n+test_expect_success \\\n+    'Annotating an old revision works' \\\n+    '[ $(git annotate file master | awk \"{print \\$3}\" | grep -c \"^A$\") == 2 ] && \\\n+     [ $(git annotate file master | awk \"{print \\$3}\" | grep -c \"^B$\") == 2 ]'\n+\n+\n test_done\n-- \n1.4.1.g8fced\n"},{"id":"23161","messageId":"11519766031273-git-send-email-ryan@michonline.com","threadId":"4757","inReplyTo":"11519766033852-git-send-email-ryan@michonline.com","subject":"[PATCH 2/2] annotate: Correct most merge following to annotate correctly.","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-07-04T01:30:02Z","receivedAt":"2006-07-04T01:30:02Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"There is still a bug involving octopus merges, somewhere, but this gets normal\nmerges correct, so it's still an improvement over the existing version.\n\nSigned-off-by: Ryan Anderson <ryan@michonline.com>\n---\n git-annotate.perl |  197 +++++++++++++++++++++++++++++++++++++----------------\n 1 files changed, 139 insertions(+), 58 deletions(-)\n\ndiff --git a/git-annotate.perl b/git-annotate.perl\nindex a6a7a48..9a7b022 100755\n--- a/git-annotate.perl\n+++ b/git-annotate.perl\n@@ -102,10 +102,10 @@ while (my $bound = pop @stack) {\n push @revqueue, $head;\n init_claim( defined $starting_rev ? $head : 'dirty');\n unless (defined $starting_rev) {\n-\tmy $diff = open_pipe(\"git\",\"diff\",\"-R\", \"HEAD\", \"--\",$filename)\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_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@@ -154,14 +154,13 @@ sub handle_rev {\n \n \t\tmy %revinfo = git_commit_info($rev);\n \n-\t\tforeach my $p (@{$revs{$rev}{'parents'}}) {\n-\n-\t\t\tgit_diff_parse($p, $rev, %revinfo);\n-\t\t\tpush @revqueue, $p;\n-\t\t}\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\tif (scalar @{$revs{$rev}{parents}} == 0) {\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@@ -252,89 +251,171 @@ sub git_find_parent {\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 ($parent, $rev, %revinfo) = @_;\n+\tmy ($parents, $rev, %revinfo) = @_;\n+\n+\tmy @filenames = ( $revs{$rev}{'filename'} );\n+\tforeach my $parent (@$parents) {\n+\t\tpush @filenames, $revs{$parent}{'filename'};\n+\t}\n \n-\tmy $diff = open_pipe(\"git-diff-tree\",\"-M\",\"-p\",$rev,$parent,\"--\",\n-\t\t\t$revs{$rev}{'filename'}, $revs{$parent}{'filename'})\n+\tmy $diff = open_pipe(\"git-diff-tree\",\"-M\",\"-p\",\"-c\",$rev,\"--\",\n+\t\t\t\t@filenames )\n \t\tor die \"Failed to call git-diff for annotation: $!\";\n \n-\t_git_diff_parse($diff, $parent, $rev, %revinfo);\n+\t_git_diff_parse($diff, $parents, $rev, %revinfo);\n \n \tclose($diff);\n }\n \n sub _git_diff_parse {\n-\tmy ($diff, $parent, $rev, %revinfo) = @_;\n+\tmy ($diff, $parents, $rev, %revinfo) = @_;\n+\n+\tmy $ri = 0;\n \n-\tmy ($ri, $pi) = (0,0);\n \tmy $slines = $revs{$rev}{'lines'};\n-\tmy @plines;\n+\tmy (%plines, %pi);\n \n \tmy $gotheader = 0;\n \tmy ($remstart);\n-\tmy ($hunk_start, $hunk_index);\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+\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;\t\t     \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\tif (m/^@@ -(\\d+),(\\d+) \\+(\\d+),(\\d+)/) {\n-\t\t\t$remstart = $1;\n-\t\t\t# Adjust for 0-based arrays\n-\t\t\t$remstart--;\n-\t\t\t# Reinit hunk tracking.\n-\t\t\t$hunk_start = $remstart;\n-\t\t\t$hunk_index = 0;\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\tfor (my $i = $ri; $i < $remstart; $i++) {\n-\t\t\t\t$plines[$pi++] = $slines->[$i];\n-\t\t\t\t$ri++;\n+\t\t\tprintf(\"Copying from %d to %d\\n\", $ri, $remstart);\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\tnext;\n-\t\t} elsif (!$gotheader) {\n-\t\t\tnext;\n-\t\t}\n+\t\t\t$ri = $remstart;\n \n-\t\tif (m/^\\+(.*)$/) {\n-\t\t\tmy $line = $1;\n-\t\t\t$plines[$pi++] = [ $line, '', '', '', 0 ];\n-\t\t\tnext;\n+\t\t\tnext DIFF;\n \n-\t\t} elsif (m/^-(.*)$/) {\n-\t\t\tmy $line = $1;\n-\t\t\tif (get_line($slines, $ri) eq $line) {\n-\t\t\t\t# Found a match, claim\n-\t\t\t\tclaim_line($ri, $rev, $slines, %revinfo);\n-\t\t\t} else {\n-\t\t\t\tdie sprintf(\"Sync error: %d/%d\\n|%s\\n|%s\\n%s => %s\\n\",\n-\t\t\t\t\t\t$ri, $hunk_start + $hunk_index,\n-\t\t\t\t\t\t$line,\n-\t\t\t\t\t\tget_line($slines, $ri),\n-\t\t\t\t\t\t$rev, $parent);\n-\t\t\t}\n-\t\t\t$ri++;\n+\t\t} elsif (!$gotheader) {\n+\t\t\t# Skip over the leadin.\n+\t\t\tnext DIFF;\n+\t\t}\n \n-\t\t} elsif (m/^\\\\/) {\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\tif (substr($_,1) ne get_line($slines,$ri) ) {\n-\t\t\t\tdie sprintf(\"Line %d (%d) does not match:\\n|%s\\n|%s\\n%s => %s\\n\",\n-\t\t\t\t\t\t$hunk_start + $hunk_index, $ri,\n-\t\t\t\t\t\tsubstr($_,1),\n-\t\t\t\t\t\tget_line($slines,$ri),\n-\t\t\t\t\t\t$rev, $parent);\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\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\tsubstr($_,scalar @$parents),\n+\t\t\t\t\t\tget_line($slines,$ri), $rev);\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\t$plines[$pi++] = $slines->[$ri++];\n \t\t}\n-\t\t$hunk_index++;\n \t}\n+\n \tfor (my $i = $ri; $i < @{$slines} ; $i++) {\n-\t\tpush @plines, $slines->[$ri++];\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-\t$revs{$parent}{lines} = \\@plines;\n \treturn;\n }\n \n-- \n1.4.1.g8fced\n"},{"id":"23163","messageId":"44A9C5AB.1020604@michonline.com","threadId":"4757","inReplyTo":"11519766021208-git-send-email-ryan@michonline.com","subject":"Re: [PATCH 0/2] Fix handling of merges in git-annotate","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2006-07-04T01:34:35Z","receivedAt":"2006-07-04T01:34:35Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"Ryan Anderson wrote:\n> This 2-patch series is a major overhaul to the way git-annotate calculates the blame for each line.\n>\n> Instead of parsing each diff in reverse, this uses the output from \"git\n> diff-tree --combined\", so that merges can be handled sanely.\n>   \nI forgot:\n\nPlease pull from http://h4x0r5.com/~ryan/git/ryan.git annotate-upstream\n\n-- \n\nRyan Anderson\n  sometimes Pug Majere\n\n\n"},{"id":"23197","messageId":"20060704080424.GA31612@soma","threadId":"4757","inReplyTo":"11519766033852-git-send-email-ryan@michonline.com","subject":"[PATCH] t8001-annotate: fix a bash-ism in this test","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-07-04T08:04:24Z","receivedAt":"2006-07-04T08:04:24Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Signed-off-by: Eric Wong <normalperson@yhbt.net>\n---\n t/t8001-annotate.sh |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh\nindex 70e2ad2..3a6490e 100755\n--- a/t/t8001-annotate.sh\n+++ b/t/t8001-annotate.sh\n@@ -8,8 +8,8 @@ PROG='git annotate'\n \n test_expect_success \\\n     'Annotating an old revision works' \\\n-    '[ $(git annotate file master | awk \"{print \\$3}\" | grep -c \"^A$\") == 2 ] && \\\n-     [ $(git annotate file master | awk \"{print \\$3}\" | grep -c \"^B$\") == 2 ]'\n+    '[ $(git annotate file master | awk \"{print \\$3}\" | grep -c \"^A$\") -eq 2 ] && \\\n+     [ $(git annotate file master | awk \"{print \\$3}\" | grep -c \"^B$\") -eq 2 ]'\n \n \n test_done\n-- \n1.4.1.g4148\n"},{"id":"23203","messageId":"7vhd1xkb9c.fsf@assigned-by-dhcp.cox.net","threadId":"4757","inReplyTo":"20060704080424.GA31612@soma","subject":"Re: [PATCH] t8001-annotate: fix a bash-ism in this test","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-04T08:46:55Z","receivedAt":"2006-07-04T08:46:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks -- good catch.\n"}]}