From: Junio C Hamano Date: Mon, 9 Sep 2013 21:35:11 +0000 (-0700) Subject: Merge branch 'es/blame-L-twice' X-Git-Tag: v1.8.5-rc0~166 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/de9a25354aa22aa6796787f3ef3af276fba82339?ds=inline;hp=-c Merge branch 'es/blame-L-twice' Teaches "git blame" to take more than one -L ranges. * es/blame-L-twice: line-range: reject -L line numbers less than 1 t8001/t8002: blame: add tests of -L line numbers less than 1 line-range: teach -L^:RE to search from start of file line-range: teach -L:RE to search from end of previous -L range line-range: teach -L^/RE/ to search from start of file line-range-format.txt: document -L/RE/ relative search log: teach -L/RE/ to search from end of previous -L range blame: teach -L/RE/ to search from end of previous -L range line-range: teach -L/RE/ to search relative to anchor point blame: document multiple -L support t8001/t8002: blame: add tests of multiple -L options blame: accept multiple -L ranges blame: inline one-line function into its lone caller range-set: publish API for re-use by git-blame -L line-range-format.txt: clarify -L:regex usage form git-log.txt: place each -L option variation on its own line --- de9a25354aa22aa6796787f3ef3af276fba82339 diff --combined builtin/blame.c index aa1abb6d5e,1bf8056f6b..00927e0347 --- a/builtin/blame.c +++ b/builtin/blame.c @@@ -22,6 -22,7 +22,7 @@@ #include "utf8.h" #include "userdiff.h" #include "line-range.h" + #include "line-log.h" static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file"); @@@ -1937,18 -1938,6 +1938,6 @@@ static const char *add_prefix(const cha return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); } - /* - * Parsing of -L option - */ - static void prepare_blame_range(struct scoreboard *sb, - const char *bottomtop, - long lno, - long *bottom, long *top) - { - if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top, sb->path)) - usage(blame_usage); - } - static int git_blame_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "blame.showroot")) { @@@ -2245,38 -2234,27 +2234,27 @@@ static int blame_move_callback(const st return 0; } - static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset) - { - const char **bottomtop = option->value; - if (!arg) - return -1; - if (*bottomtop) - die("More than one '-L n,m' option given"); - *bottomtop = arg; - return 0; - } - int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; const char *path; struct scoreboard sb; struct origin *o; - struct blame_entry *ent; - long dashdash_pos, bottom, top, lno; + struct blame_entry *ent = NULL; + long dashdash_pos, lno; const char *final_commit_name = NULL; enum object_type type; - static const char *bottomtop = NULL; + static struct string_list range_list; static int output_option = 0, opt = 0; static int show_stats = 0; static const char *revs_file = NULL; static const char *contents_from = NULL; static const struct option options[] = { - OPT_BOOLEAN(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")), - OPT_BOOLEAN('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")), - OPT_BOOLEAN(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), - OPT_BOOLEAN(0, "show-stats", &show_stats, N_("Show work cost statistics")), + OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")), + OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")), + OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), + OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")), OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE), OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME), OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER), @@@ -2293,13 -2271,16 +2271,16 @@@ OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use 's contents as the final image")), { 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_CALLBACK('L', NULL, &bottomtop, N_("n,m"), N_("Process only line range n,m, counting from 1"), blame_bottomtop_callback), + OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")), OPT__ABBREV(&abbrev), OPT_END() }; struct parse_opt_ctx_t ctx; int cmd_is_annotate = !strcmp(argv[0], "annotate"); + struct range_set ranges; + unsigned int range_i; + long anchor; git_config(git_blame_config, NULL); init_revisions(&revs, NULL); @@@ -2492,22 -2473,48 +2473,48 @@@ parse_done num_read_blob++; lno = prepare_lines(&sb); - bottom = top = 0; - if (bottomtop) - prepare_blame_range(&sb, bottomtop, lno, &bottom, &top); - if (lno < top || ((lno || bottom) && lno < bottom)) - die("file %s has only %lu lines", path, lno); - if (bottom < 1) - bottom = 1; - if (top < 1) - top = lno; - bottom--; - - ent = xcalloc(1, sizeof(*ent)); - ent->lno = bottom; - ent->num_lines = top - bottom; - ent->suspect = o; - ent->s_lno = bottom; + if (lno && !range_list.nr) + string_list_append(&range_list, xstrdup("1")); + + anchor = 1; + range_set_init(&ranges, range_list.nr); + for (range_i = 0; range_i < range_list.nr; ++range_i) { + long bottom, top; + if (parse_range_arg(range_list.items[range_i].string, + nth_line_cb, &sb, lno, anchor, + &bottom, &top, sb.path)) + usage(blame_usage); + if (lno < top || ((lno || bottom) && lno < bottom)) + die("file %s has only %lu lines", path, lno); + if (bottom < 1) + bottom = 1; + if (top < 1) + top = lno; + bottom--; + range_set_append_unsafe(&ranges, bottom, top); + anchor = top + 1; + } + sort_and_merge_range_set(&ranges); + + for (range_i = ranges.nr; range_i > 0; --range_i) { + const struct range *r = &ranges.ranges[range_i - 1]; + long bottom = r->start; + long top = r->end; + struct blame_entry *next = ent; + ent = xcalloc(1, sizeof(*ent)); + ent->lno = bottom; + ent->num_lines = top - bottom; + ent->suspect = o; + ent->s_lno = bottom; + ent->next = next; + if (next) + next->prev = ent; + origin_incref(o); + } + origin_decref(o); + + range_set_release(&ranges); + string_list_clear(&range_list, 0); sb.ent = ent; sb.path = path;