Developers of the world, rejoice! :-) Junio, Pranit (and whoever is paying attention to the conversation that was being held about --tips), here's a draft of what I meant when I was talking about the option of "aggregating" blame output. I'm not considering _all_ cases yet, just would like for people to give it a quick test and tell me if they think it's worth "polishing" it for inclusion into mainline git. The output would look like this: $ ./git blame -L 1,19 -t --aggregate builtin/blame.c Blaming lines: 0% (19/2974), done. cee7f245dc builtin-pickaxe.c (Junio C Hamano 1161298804 -0700) 1) /* 31653c1abc builtin-blame.c (Eugene Letuchy 1235170271 -0800) 2) * Blame cee7f245dc builtin-pickaxe.c (Junio C Hamano 1161298804 -0700) 3) * 7e6ac6e439 builtin/blame.c (David Kastrup 1398470209 +0200) 4) * Copyright (c) 2006, 2014 by its authors 5) * See COPYING for licensing conditions cee7f245dc builtin-pickaxe.c (Junio C Hamano 1161298804 -0700) 6) */ 7) 8) #include "cache.h" fb58c8d507 builtin/blame.c (Michael Haggerty 1434981785 +0200) 9) #include "refs.h" cee7f245dc builtin-pickaxe.c (Junio C Hamano 1161298804 -0700) 10) #include "builtin.h" 11) #include "blob.h" 12) #include "commit.h" 13) #include "tag.h" 14) #include "tree-walk.h" 15) #include "diff.h" 16) #include "diffcore.h" 17) #include "revision.h" 717d1462ba builtin-blame.c (Linus Torvalds 1169976846 -0800) 18) #include "quote.h" cee7f245dc builtin-pickaxe.c (Junio C Hamano 1161298804 -0700) 19) #include "xdiff-interface.h" It can be seen that options like -t still work in aggregation. In relation to the previous conversation about "tips", I think a better name could be "hints" and it could be added on top of the aggregation. On Mon, Jan 23, 2017 at 8:10 PM, Edmundo Carmona Antoranz <eantoranz@xxxxxxxxx> wrote: > --- > builtin/blame.c | 78 +++++++++++++++++++++++++++++++++++++-------------------- > 1 file changed, 51 insertions(+), 27 deletions(-) > > diff --git a/builtin/blame.c b/builtin/blame.c > index 126b8c9e5..9e8403303 100644 > --- a/builtin/blame.c > +++ b/builtin/blame.c > @@ -1884,6 +1884,7 @@ static const char *format_time(unsigned long time, const char *tz_str, > #define OUTPUT_NO_AUTHOR 0200 > #define OUTPUT_SHOW_EMAIL 0400 > #define OUTPUT_LINE_PORCELAIN 01000 > +#define OUTPUT_AGGREGATE 02000 > > static void emit_porcelain_details(struct origin *suspect, int repeat) > { > @@ -1931,43 +1932,36 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent, > putchar('\n'); > } > > -static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) > +/** > + * Print information about the revision. > + * This information can be used in either aggregated output > + * or prepending each line of the content of the file being blamed > + */ > +static void print_revision_info(char* revision_hex, int revision_length, struct blame_entry* ent, > + struct commit* commit, struct commit_info ci, int opt, int show_raw_time) > { > - int cnt; > - const char *cp; > - struct origin *suspect = ent->suspect; > - struct commit_info ci; > - char hex[GIT_SHA1_HEXSZ + 1]; > - int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); > - > - get_commit_info(suspect->commit, &ci, 1); > - sha1_to_hex_r(hex, suspect->commit->object.oid.hash); > - > - cp = nth_line(sb, ent->lno); > - for (cnt = 0; cnt < ent->num_lines; cnt++) { > - char ch; > - int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev; > - > - if (suspect->commit->object.flags & UNINTERESTING) { > + if (opt & OUTPUT_AGGREGATE) > + printf("\t"); > + int length = revision_length; > + if (commit->object.flags & UNINTERESTING) { > if (blank_boundary) > - memset(hex, ' ', length); > + memset(revision_hex, ' ', length); > else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) { > length--; > putchar('^'); > } > } > > - printf("%.*s", length, hex); > + printf("%.*s", length, revision_hex); > if (opt & OUTPUT_ANNOTATE_COMPAT) { > const char *name; > if (opt & OUTPUT_SHOW_EMAIL) > name = ci.author_mail.buf; > else > name = ci.author.buf; > - printf("\t(%10s\t%10s\t%d)", name, > + printf("\t(%10s\t%10s\t", name, > format_time(ci.author_time, ci.author_tz.buf, > - show_raw_time), > - ent->lno + 1 + cnt); > + show_raw_time)); > } else { > if (opt & OUTPUT_SHOW_SCORE) > printf(" %*d %02d", > @@ -1975,11 +1969,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) > ent->suspect->refcnt); > if (opt & OUTPUT_SHOW_NAME) > printf(" %-*.*s", longest_file, longest_file, > - suspect->path); > - if (opt & OUTPUT_SHOW_NUMBER) > - printf(" %*d", max_orig_digits, > - ent->s_lno + 1 + cnt); > - > + ent->suspect->path); > if (!(opt & OUTPUT_NO_AUTHOR)) { > const char *name; > int pad; > @@ -1994,9 +1984,42 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) > ci.author_tz.buf, > show_raw_time)); > } > + } > + if (opt & OUTPUT_AGGREGATE) > + printf(")\n"); > +} > + > +static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) > +{ > + int cnt; > + const char *cp; > + struct origin *suspect = ent->suspect; > + struct commit_info ci; > + char hex[GIT_SHA1_HEXSZ + 1]; > + int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); > + int revision_length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev; > + > + get_commit_info(suspect->commit, &ci, 1); > + sha1_to_hex_r(hex, suspect->commit->object.oid.hash); > + > + if (opt & OUTPUT_AGGREGATE) > + print_revision_info(hex, revision_length, ent, suspect->commit, ci, opt, show_raw_time); > + > + cp = nth_line(sb, ent->lno); > + for (cnt = 0; cnt < ent->num_lines; cnt++) { > + if (!(opt & OUTPUT_AGGREGATE)) > + print_revision_info(hex, revision_length, ent, suspect->commit, ci, opt, show_raw_time); > + if (opt & OUTPUT_ANNOTATE_COMPAT) { > + printf("%*d) ", > + max_digits, ent->lno + 1 + cnt); > + } else { > + if (opt & OUTPUT_SHOW_NUMBER) > + printf(" %*d ", max_orig_digits, > + ent->s_lno + 1 + cnt); > printf(" %*d) ", > max_digits, ent->lno + 1 + cnt); > } > + char ch; > do { > ch = *cp++; > putchar(ch); > @@ -2609,6 +2632,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) > { OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback }, > { OPTION_CALLBACK, 'M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback }, > OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")), > + OPT_BIT(0, "aggregate", &output_option, N_("Aggregate output"), OUTPUT_AGGREGATE), > OPT__ABBREV(&abbrev), > OPT_END() > }; > -- > 2.11.0.rc1 >