{"thread":{"id":"44965","subject":"[PATCH v1 1/3] blame: add --aggregate option","startedAt":"2017-01-25T05:27:53Z","lastAt":"2017-01-25T22:22:41Z","messageCount":4,"participants":["Edmundo Carmona Antoranz","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"310138","messageId":"20170125052734.17550-1-eantoranz@gmail.com","threadId":"44965","inReplyTo":null,"subject":"[PATCH v1 1/3] blame: add --aggregate option","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2017-01-25T05:27:32Z","receivedAt":"2017-01-25T05:27:53Z","isPatch":true,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"To avoid taking up so much space on normal output printing duplicate\ninformation on consecutive lines that \"belong\" to the same revision,\nthis option allows to print a single line with the information about\nthe revision before the lines of content themselves.\n\nSigned-off-by: Edmundo Carmona Antoranz <eantoranz@gmail.com>\n---\n Documentation/blame-options.txt |  4 ++\n Documentation/git-blame.txt     |  4 +-\n builtin/blame.c                 | 85 +++++++++++++++++++++++++++--------------\n 3 files changed, 63 insertions(+), 30 deletions(-)\n\ndiff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt\nindex 2669b87c9..be2a327ff 100644\n--- a/Documentation/blame-options.txt\n+++ b/Documentation/blame-options.txt\n@@ -49,6 +49,10 @@ include::line-range-format.txt[]\n \tShow the result incrementally in a format designed for\n \tmachine consumption.\n \n+--aggregate::\n+\tAggregate information about revisions. This option makes no\n+\tdifference on incremental or porcelain format.\n+\n --encoding=<encoding>::\n \tSpecifies the encoding used to output author names\n \tand commit summaries. Setting it to `none` makes blame\ndiff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt\nindex fdc3aea30..385eaf7be 100644\n--- a/Documentation/git-blame.txt\n+++ b/Documentation/git-blame.txt\n@@ -10,8 +10,8 @@ SYNOPSIS\n [verse]\n 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]\n \t    [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]\n-\t    [--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>..<rev>]\n-\t    [--] <file>\n+\t    [--progress] [--abbrev=<n>] [--aggregate] \n+\t    [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>\n \n DESCRIPTION\n -----------\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 126b8c9e5..09b3b0c8a 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1884,6 +1884,7 @@ static const char *format_time(unsigned long time, const char *tz_str,\n #define OUTPUT_NO_AUTHOR       0200\n #define OUTPUT_SHOW_EMAIL\t0400\n #define OUTPUT_LINE_PORCELAIN 01000\n+#define OUTPUT_AGGREGATE      02000\n \n static void emit_porcelain_details(struct origin *suspect, int repeat)\n {\n@@ -1931,43 +1932,41 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,\n \t\tputchar('\\n');\n }\n \n-static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n-{\n-\tint cnt;\n-\tconst char *cp;\n-\tstruct origin *suspect = ent->suspect;\n-\tstruct commit_info ci;\n-\tchar hex[GIT_SHA1_HEXSZ + 1];\n-\tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n-\n-\tget_commit_info(suspect->commit, &ci, 1);\n-\tsha1_to_hex_r(hex, suspect->commit->object.oid.hash);\n-\n-\tcp = nth_line(sb, ent->lno);\n-\tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n-\t\tchar ch;\n-\t\tint length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;\n-\n-\t\tif (suspect->commit->object.flags & UNINTERESTING) {\n+/**\n+ * Print information about the revision.\n+ * This information can be used in either aggregated output\n+ * or prepending each line of the content of the file being blamed\n+ * \n+ * if line_number == 0, it's an aggregate line\n+ */\n+static void print_revision_info(char* revision_hex, int revision_length, struct blame_entry* ent,\n+\t\tstruct commit_info ci, int opt, int show_raw_time, int line_number)\n+{\n+\tif (!line_number)\n+\t\tprintf(\"\\t\");\n+\tint length = revision_length;\n+\tif (!line_number || !(opt & OUTPUT_AGGREGATE)) {\n+\t\tif (ent->suspect->commit->object.flags & UNINTERESTING) {\n \t\t\tif (blank_boundary)\n-\t\t\t\tmemset(hex, ' ', length);\n+\t\t\t\tmemset(revision_hex, ' ', length);\n \t\t\telse if (!(opt & OUTPUT_ANNOTATE_COMPAT)) {\n \t\t\t\tlength--;\n \t\t\t\tputchar('^');\n \t\t\t}\n \t\t}\n \n-\t\tprintf(\"%.*s\", length, hex);\n+\t\tprintf(\"%.*s\", length, revision_hex);\n \t\tif (opt & OUTPUT_ANNOTATE_COMPAT) {\n \t\t\tconst char *name;\n \t\t\tif (opt & OUTPUT_SHOW_EMAIL)\n \t\t\t\tname = ci.author_mail.buf;\n \t\t\telse\n \t\t\t\tname = ci.author.buf;\n-\t\t\tprintf(\"\\t(%10s\\t%10s\\t%d)\", name,\n+\t\t\tprintf(\"\\t(%10s\\t%10s\", name,\n \t\t\t       format_time(ci.author_time, ci.author_tz.buf,\n-\t\t\t\t\t   show_raw_time),\n-\t\t\t       ent->lno + 1 + cnt);\n+\t\t\t\t\t   show_raw_time));\n+\t\t\tif (line_number)\n+\t\t\t\tprintf(\"\\t%d)\", line_number);\n \t\t} else {\n \t\t\tif (opt & OUTPUT_SHOW_SCORE)\n \t\t\t\tprintf(\" %*d %02d\",\n@@ -1975,10 +1974,10 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\t\t\t       ent->suspect->refcnt);\n \t\t\tif (opt & OUTPUT_SHOW_NAME)\n \t\t\t\tprintf(\" %-*.*s\", longest_file, longest_file,\n-\t\t\t\t       suspect->path);\n-\t\t\tif (opt & OUTPUT_SHOW_NUMBER)\n+\t\t\t\t       ent->suspect->path);\n+\t\t\tif ((opt & OUTPUT_SHOW_NUMBER) && line_number)\n \t\t\t\tprintf(\" %*d\", max_orig_digits,\n-\t\t\t\t       ent->s_lno + 1 + cnt);\n+\t\t\t\t       line_number);\n \n \t\t\tif (!(opt & OUTPUT_NO_AUTHOR)) {\n \t\t\t\tconst char *name;\n@@ -1994,9 +1993,38 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \t\t\t\t\t\t   ci.author_tz.buf,\n \t\t\t\t\t\t   show_raw_time));\n \t\t\t}\n-\t\t\tprintf(\" %*d) \",\n-\t\t\t       max_digits, ent->lno + 1 + cnt);\n+\t\t\tif (line_number)\n+\t\t\t\tprintf(\" %*d) \",\n+\t\t\t\t\t   max_digits, line_number);\n \t\t}\n+\t}\n+\tif (line_number && (opt & OUTPUT_AGGREGATE))\n+\t\tprintf(opt & OUTPUT_ANNOTATE_COMPAT ? \"%*d)\" : \" %*d) \",\n+\t\t\t   max_digits, line_number);\n+\tif (!line_number)\n+\t\tprintf(\")\\n\");\n+}\n+\n+static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n+{\n+\tint cnt;\n+\tconst char *cp;\n+\tstruct origin *suspect = ent->suspect;\n+\tstruct commit_info ci;\n+\tchar hex[GIT_SHA1_HEXSZ + 1];\n+\tint show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);\n+\tint revision_length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;\n+\n+\tget_commit_info(suspect->commit, &ci, 1);\n+\tsha1_to_hex_r(hex, suspect->commit->object.oid.hash);\n+\n+\tif (opt & OUTPUT_AGGREGATE)\n+\t\tprint_revision_info(hex, revision_length, ent, ci, opt, show_raw_time, 0);\n+\n+\tcp = nth_line(sb, ent->lno);\n+\tchar ch;\n+\tfor (cnt = 0; cnt < ent->num_lines; cnt++) {\n+\t\tprint_revision_info(hex, revision_length, ent, ci, opt, show_raw_time, ent->lno + 1 + cnt);\n \t\tdo {\n \t\t\tch = *cp++;\n \t\t\tputchar(ch);\n@@ -2609,6 +2637,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 'C', NULL, &opt, N_(\"score\"), N_(\"Find line copies within and across files\"), PARSE_OPT_OPTARG, blame_copy_callback },\n \t\t{ OPTION_CALLBACK, 'M', NULL, &opt, N_(\"score\"), N_(\"Find line movements within and across files\"), PARSE_OPT_OPTARG, blame_move_callback },\n \t\tOPT_STRING_LIST('L', NULL, &range_list, N_(\"n,m\"), N_(\"Process only line range n,m, counting from 1\")),\n+\t\tOPT_BIT(0, \"aggregate\", &output_option, N_(\"Aggregate output\"), OUTPUT_AGGREGATE),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n-- \n2.11.0.rc1\n\n"},{"id":"310139","messageId":"20170125052734.17550-2-eantoranz@gmail.com","threadId":"44965","inReplyTo":"20170125052734.17550-1-eantoranz@gmail.com","subject":"[PATCH v1 2/3] blame: add --hint option","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2017-01-25T05:27:33Z","receivedAt":"2017-01-25T05:28:00Z","isPatch":true,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"When printing aggregate information about revisions, this option\nallows to add the 1-line summary of the revision to provide the\nreader with some additional context about the revision itself.\n\nSigned-off-by: Edmundo Carmona Antoranz <eantoranz@gmail.com>\n---\n Documentation/blame-options.txt |  4 ++++\n Documentation/git-blame.txt     |  2 +-\n builtin/blame.c                 | 13 +++++++++++--\n 3 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt\nindex be2a327ff..64858a631 100644\n--- a/Documentation/blame-options.txt\n+++ b/Documentation/blame-options.txt\n@@ -53,6 +53,10 @@ include::line-range-format.txt[]\n \tAggregate information about revisions. This option makes no\n \tdifference on incremental or porcelain format.\n \n+--hint::\n+\tShow revision hints on aggregated information about the revision.\n+\tImplies --aggregate.\n+\n --encoding=<encoding>::\n \tSpecifies the encoding used to output author names\n \tand commit summaries. Setting it to `none` makes blame\ndiff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt\nindex 385eaf7be..ed8b119fa 100644\n--- a/Documentation/git-blame.txt\n+++ b/Documentation/git-blame.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]\n \t    [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]\n-\t    [--progress] [--abbrev=<n>] [--aggregate] \n+\t    [--progress] [--abbrev=<n>] [--aggregate] [--hint]\n \t    [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>\n \n DESCRIPTION\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 09b3b0c8a..7473b50e9 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1885,6 +1885,7 @@ static const char *format_time(unsigned long time, const char *tz_str,\n #define OUTPUT_SHOW_EMAIL\t0400\n #define OUTPUT_LINE_PORCELAIN 01000\n #define OUTPUT_AGGREGATE      02000\n+#define OUTPUT_HINT           04000\n \n static void emit_porcelain_details(struct origin *suspect, int repeat)\n {\n@@ -2001,8 +2002,12 @@ static void print_revision_info(char* revision_hex, int revision_length, struct\n \tif (line_number && (opt & OUTPUT_AGGREGATE))\n \t\tprintf(opt & OUTPUT_ANNOTATE_COMPAT ? \"%*d)\" : \" %*d) \",\n \t\t\t   max_digits, line_number);\n-\tif (!line_number)\n-\t\tprintf(\")\\n\");\n+\tif (!line_number) {\n+\t\tprintf(\")\");\n+\t\tif (opt & OUTPUT_HINT)\n+\t\t\tprintf(\" %s\", ci.summary.buf);\n+\t\tprintf(\"\\n\");\n+\t}\n }\n \n static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n@@ -2018,6 +2023,9 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n \tget_commit_info(suspect->commit, &ci, 1);\n \tsha1_to_hex_r(hex, suspect->commit->object.oid.hash);\n \n+\tif (~(opt & OUTPUT_AGGREGATE) & (opt & OUTPUT_HINT))\n+\t\topt=opt|OUTPUT_AGGREGATE;\n+\n \tif (opt & OUTPUT_AGGREGATE)\n \t\tprint_revision_info(hex, revision_length, ent, ci, opt, show_raw_time, 0);\n \n@@ -2638,6 +2646,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_CALLBACK, 'M', NULL, &opt, N_(\"score\"), N_(\"Find line movements within and across files\"), PARSE_OPT_OPTARG, blame_move_callback },\n \t\tOPT_STRING_LIST('L', NULL, &range_list, N_(\"n,m\"), N_(\"Process only line range n,m, counting from 1\")),\n \t\tOPT_BIT(0, \"aggregate\", &output_option, N_(\"Aggregate output\"), OUTPUT_AGGREGATE),\n+\t\tOPT_BIT(0, \"hint\", &output_option, N_(\"Show revision hints\"), OUTPUT_HINT),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n-- \n2.11.0.rc1\n\n"},{"id":"310140","messageId":"20170125052734.17550-3-eantoranz@gmail.com","threadId":"44965","inReplyTo":"20170125052734.17550-1-eantoranz@gmail.com","subject":"[PATCH v1 3/3] blame: add --color option","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2017-01-25T05:27:34Z","receivedAt":"2017-01-25T05:28:05Z","isPatch":true,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"Revision information will be highlighted so that it's easier\nto tell from content of the file being \"blamed\".\n\nSigned-off-by: Edmundo Carmona Antoranz <eantoranz@gmail.com>\n---\n Documentation/blame-options.txt |  5 +++++\n Documentation/git-blame.txt     |  2 +-\n builtin/blame.c                 | 13 +++++++++++++\n 3 files changed, 19 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt\nindex 64858a631..4abb4eb7e 100644\n--- a/Documentation/blame-options.txt\n+++ b/Documentation/blame-options.txt\n@@ -57,6 +57,11 @@ include::line-range-format.txt[]\n \tShow revision hints on aggregated information about the revision.\n \tImplies --aggregate.\n \n+--[no-]color::\n+\tRevision information will be highlighted on normal output\n+\twhen running git from a terminal. This option allows\n+\tfor color to be forcibly enabled or disabled.\n+\n --encoding=<encoding>::\n \tSpecifies the encoding used to output author names\n \tand commit summaries. Setting it to `none` makes blame\ndiff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt\nindex ed8b119fa..d25cbc5ef 100644\n--- a/Documentation/git-blame.txt\n+++ b/Documentation/git-blame.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n [verse]\n 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]\n \t    [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]\n-\t    [--progress] [--abbrev=<n>] [--aggregate] [--hint]\n+\t    [--progress] [--abbrev=<n>] [--aggregate] [--hint] [--color]\n \t    [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>\n \n DESCRIPTION\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 7473b50e9..c661dd538 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1886,6 +1886,7 @@ static const char *format_time(unsigned long time, const char *tz_str,\n #define OUTPUT_LINE_PORCELAIN 01000\n #define OUTPUT_AGGREGATE      02000\n #define OUTPUT_HINT           04000\n+#define OUTPUT_COLOR         010000\n \n static void emit_porcelain_details(struct origin *suspect, int repeat)\n {\n@@ -1943,6 +1944,8 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,\n static void print_revision_info(char* revision_hex, int revision_length, struct blame_entry* ent,\n \t\tstruct commit_info ci, int opt, int show_raw_time, int line_number)\n {\n+\tif (opt & OUTPUT_COLOR)\n+\t\tprintf(\"%s\", GIT_COLOR_BOLD);\n \tif (!line_number)\n \t\tprintf(\"\\t\");\n \tint length = revision_length;\n@@ -2008,6 +2011,8 @@ static void print_revision_info(char* revision_hex, int revision_length, struct\n \t\t\tprintf(\" %s\", ci.summary.buf);\n \t\tprintf(\"\\n\");\n \t}\n+\tif (opt & OUTPUT_COLOR)\n+\t\tprintf(\"%s\", GIT_COLOR_RESET);\n }\n \n static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)\n@@ -2608,6 +2613,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tchar *final_commit_name = NULL;\n \tenum object_type type;\n \tstruct commit *final_commit = NULL;\n+\tint show_color;\n \n \tstruct string_list range_list = STRING_LIST_INIT_NODUP;\n \tint output_option = 0, opt = 0;\n@@ -2647,6 +2653,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING_LIST('L', NULL, &range_list, N_(\"n,m\"), N_(\"Process only line range n,m, counting from 1\")),\n \t\tOPT_BIT(0, \"aggregate\", &output_option, N_(\"Aggregate output\"), OUTPUT_AGGREGATE),\n \t\tOPT_BIT(0, \"hint\", &output_option, N_(\"Show revision hints\"), OUTPUT_HINT),\n+\t\tOPT_BOOL(0, \"color\", &show_color, N_(\"Show colors on revision information\")),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n@@ -2666,6 +2673,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tsave_commit_buffer = 0;\n \tdashdash_pos = 0;\n \tshow_progress = -1;\n+\tshow_color = -1;\n \n \tparse_options_start(&ctx, argc, argv, prefix, options,\n \t\t\t    PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);\n@@ -2698,6 +2706,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t} else if (show_progress < 0)\n \t\tshow_progress = isatty(2);\n \n+\tif (show_color < 0)\n+\t\tshow_color = isatty(1);\n+\tif (show_color)\n+\t\toutput_option = output_option | OUTPUT_COLOR;\n+\n \tif (0 < abbrev && abbrev < GIT_SHA1_HEXSZ)\n \t\t/* one more abbrev length is needed for the boundary commit */\n \t\tabbrev++;\n-- \n2.11.0.rc1\n\n"},{"id":"310240","messageId":"20170125222233.rckzhn7lpk63yppq@sigill.intra.peff.net","threadId":"44965","inReplyTo":"20170125052734.17550-1-eantoranz@gmail.com","subject":"Re: [PATCH v1 1/3] blame: add --aggregate option","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-01-25T22:22:34Z","receivedAt":"2017-01-25T22:22:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jan 24, 2017 at 11:27:32PM -0600, Edmundo Carmona Antoranz wrote:\n\n> To avoid taking up so much space on normal output printing duplicate\n> information on consecutive lines that \"belong\" to the same revision,\n> this option allows to print a single line with the information about\n> the revision before the lines of content themselves.\n\nI admit I have not followed the preceding discussion closely, so ignore\nme if my suggestion is way off base.\n\nBut it really seems like the various outputs you are looking for are\nreally all about customizing git-blame's human-readable output.\n\nWould it be more productive to move towards a \"--format\" option that\nshows custom items for each line? It could build on the custom formats\nin the pretty-print code (though I think you would want to add some\ncustom ones, like filename, line number, line content, etc).\n\nI know that only does half of what you want, which is making some output\nnot just per-line, but per-block. But that can come easily on top, if we\nallow separate per-line and per-block formats (which would default to\nthe current output and the empty string, respectively).\n\nThen you could do something like:\n\n  git blame --format-block='%h %an <%ae>%n  %s' \\\n            --format-line='\\t%(filename):%(linenumber) %(linecontent)'\n\nand get something like:\n\n    1234abcd A U Thor <author@example.com>\n      initial commit\n            foo.c:1 #include <foo.h>\n            foo.c:2 #include <bar.h>\n    5678abcd A U Thor <author@example.com>\n      add third include\n            foo.c:3 #include <third.h>\n\nand so on. But people can mix and match what they want to see on each\nline, and what they'd rather push to other lines.\n\nI don't have a huge interest in the feature myself. I switched to \"tig\nblame\" years ago and never looked back. But it just seems like your\noptions touch no this kind of flexibility, but will limit somebody as\nsoon as they want to switch around which information goes where.\n\n-Peff\n"}]}