From: Junio C Hamano Date: Wed, 30 May 2018 05:04:09 +0000 (+0900) Subject: Merge branch 'sb/blame-color' X-Git-Tag: v2.18.0-rc0~18 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/3d241297991c6f6ac6b1e50e52a4ae35bc431f28?hp=-c Merge branch 'sb/blame-color' "git blame" learns to unhighlight uninteresting metadata from the originating commit on lines that are the same as the previous one, and also paint lines in different colors depending on the age of the commit. * sb/blame-color: builtin/blame: add new coloring scheme config builtin/blame: highlight recently changed lines builtin/blame: dim uninteresting metadata lines --- 3d241297991c6f6ac6b1e50e52a4ae35bc431f28 diff --combined Documentation/config.txt index 460c14f047,4eb030ed43..7d8383433c --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -530,12 -530,6 +530,12 @@@ core.autocrlf: This variable can be set to 'input', in which case no output conversion is performed. +core.checkRoundtripEncoding:: + A comma and/or whitespace separated list of encodings that Git + performs UTF-8 round trip checks on if they are used in an + `working-tree-encoding` attribute (see linkgit:gitattributes[5]). + The default value is `SHIFT-JIS`. + core.symlinks:: If false, symbolic links are checked out as small plain files that contain the link text. linkgit:git-update-index[1] and @@@ -904,10 -898,6 +904,10 @@@ core.notesRef: This setting defaults to "refs/notes/commits", and it can be overridden by the `GIT_NOTES_REF` environment variable. See linkgit:git-notes[1]. +core.commitGraph:: + Enable git commit graph feature. Allows reading from the + commit-graph file. + core.sparseCheckout:: Enable "sparse checkout" feature. See section "Sparse checkout" in linkgit:git-read-tree[1] for more information. @@@ -1068,10 -1058,6 +1068,10 @@@ branch..rebase: "git pull" is run. See "pull.rebase" for doing this in a non branch-specific manner. + +When `merges`, pass the `--rebase-merges` option to 'git rebase' +so that the local merge commits are included in the rebase (see +linkgit:git-rebase[1] for details). ++ When preserve, also pass `--preserve-merges` along to 'git rebase' so that locally committed merge commits will not be flattened by running 'git pull'. @@@ -1102,16 -1088,6 +1102,16 @@@ clean.requireForce: A boolean to make git-clean do nothing unless given -f, -i or -n. Defaults to true. +color.advice:: + A boolean to enable/disable color in hints (e.g. when a push + failed, see `advice.*` for a list). May be set to `always`, + `false` (or `never`) or `auto` (or `true`), in which case colors + are used only when the error output goes to a terminal. If + unset, then the value of `color.ui` is used (`auto` by default). + +color.advice.hint:: + Use customized color for hints. + color.branch:: A boolean to enable/disable color in the output of linkgit:git-branch[1]. May be set to `always`, @@@ -1214,15 -1190,6 +1214,15 @@@ color.pager: A boolean to enable/disable colored output when the pager is in use (default is true). +color.push:: + A boolean to enable/disable color in push errors. May be set to + `always`, `false` (or `never`) or `auto` (or `true`), in which + case colors are used only when the error output goes to a terminal. + If unset, then the value of `color.ui` is used (`auto` by default). + +color.push.error:: + Use customized color for push errors. + color.showBranch:: A boolean to enable/disable color in the output of linkgit:git-show-branch[1]. May be set to `always`, @@@ -1251,15 -1218,33 +1251,42 @@@ color.status.: status short-format), or `unmerged` (files which have unmerged changes). + color.blame.repeatedLines:: + Use the customized color for the part of git-blame output that + is repeated meta information per line (such as commit id, + author name, date and timezone). Defaults to cyan. + + color.blame.highlightRecent:: + This can be used to color the metadata of a blame line depending + on age of the line. + + + This setting should be set to a comma-separated list of color and date settings, + starting and ending with a color, the dates should be set from oldest to newest. + The metadata will be colored given the colors if the the line was introduced + before the given timestamp, overwriting older timestamped colors. + + + Instead of an absolute timestamp relative timestamps work as well, e.g. + 2.weeks.ago is valid to address anything older than 2 weeks. + + + It defaults to 'blue,12 month ago,white,1 month ago,red', which colors + everything older than one year blue, recent changes between one month and + one year old are kept white, and lines introduced within the last month are + colored red. + + blame.coloring:: + This determines the coloring scheme to be applied to blame + output. It can be 'repeatedLines', 'highlightRecent', + or 'none' which is the default. + +color.transport:: + A boolean to enable/disable color when pushes are rejected. May be + set to `always`, `false` (or `never`) or `auto` (or `true`), in which + case colors are used only when the error output goes to a terminal. + If unset, then the value of `color.ui` is used (`auto` by default). + +color.transport.rejected:: + Use customized color when a push was rejected. + color.ui:: This variable determines the default value for variables such as `color.diff` and `color.grep` that control the use of color @@@ -1440,16 -1425,7 +1467,16 @@@ fetch.unpackLimit: fetch.prune:: If true, fetch will automatically behave as if the `--prune` - option was given on the command line. See also `remote..prune`. + option was given on the command line. See also `remote..prune` + and the PRUNING section of linkgit:git-fetch[1]. + +fetch.pruneTags:: + If true, fetch will automatically behave as if the + `refs/tags/*:refs/tags/*` refspec was provided when pruning, + if not set already. This allows for setting both this option + and `fetch.prune` to maintain a 1=1 mapping to upstream + refs. See also `remote..pruneTags` and the PRUNING + section of linkgit:git-fetch[1]. fetch.output:: Control how ref update status is printed. Valid values are @@@ -1600,18 -1576,6 +1627,18 @@@ gc.autoDetach: Make `git gc --auto` return immediately and run in background if the system supports it. Default is true. +gc.bigPackThreshold:: + If non-zero, all packs larger than this limit are kept when + `git gc` is run. This is very similar to `--keep-base-pack` + except that all packs that meet the threshold are kept, not + just the base pack. Defaults to zero. Common unit suffixes of + 'k', 'm', or 'g' are supported. ++ +Note that if the number of kept packs is more than gc.autoPackLimit, +this configuration variable is ignored, all packs except the base pack +will be repacked. After this the number of packs should go below +gc.autoPackLimit and gc.bigPackThreshold should be respected again. + gc.logExpiry:: If the file gc.log exists, then `git gc --auto` won't run unless that file is more than 'gc.logExpiry' old. Default is @@@ -2011,7 -1975,6 +2038,7 @@@ http.sslVersion: - tlsv1.0 - tlsv1.1 - tlsv1.2 + - tlsv1.3 + Can be overridden by the `GIT_SSL_VERSION` environment variable. @@@ -2476,7 -2439,6 +2503,7 @@@ pack.window: pack.depth:: The maximum delta depth used by linkgit:git-pack-objects[1] when no maximum depth is given on the command line. Defaults to 50. + Maximum value is 4095. pack.windowMemory:: The maximum size of memory that is consumed by each thread @@@ -2513,8 -2475,7 +2540,8 @@@ pack.deltaCacheLimit: The maximum size of a delta, that is cached in linkgit:git-pack-objects[1]. This cache is used to speed up the writing object phase by not having to recompute the final delta - result once the best match for all objects is found. Defaults to 1000. + result once the best match for all objects is found. + Defaults to 1000. Maximum value is 65535. pack.threads:: Specifies the number of threads to spawn when searching for best @@@ -2673,10 -2634,6 +2700,10 @@@ pull.rebase: pull" is run. See "branch..rebase" for setting this on a per-branch basis. + +When `merges`, pass the `--rebase-merges` option to 'git rebase' +so that the local merge commits are included in the rebase (see +linkgit:git-rebase[1] for details). ++ When preserve, also pass `--preserve-merges` along to 'git rebase' so that locally committed merge commits will not be flattened by running 'git pull'. @@@ -3015,15 -2972,6 +3042,15 @@@ remote..prune: remote (as if the `--prune` option was given on the command line). Overrides `fetch.prune` settings, if any. +remote..pruneTags:: + When set to true, fetching from this remote by default will also + remove any local tags that no longer exist on the remote if pruning + is activated in general via `remote..prune`, `fetch.prune` or + `--prune`. Overrides `fetch.pruneTags` settings, if any. ++ +See also `remote..prune` and the PRUNING section of +linkgit:git-fetch[1]. + remotes.:: The list of remotes which are fetched by "git remote update ". See linkgit:git-remote[1]. @@@ -3179,18 -3127,6 +3206,18 @@@ status.displayCommentPrefix: behavior of linkgit:git-status[1] in Git 1.8.4 and previous. Defaults to false. +status.renameLimit:: + The number of files to consider when performing rename detection + in linkgit:git-status[1] and linkgit:git-commit[1]. Defaults to + the value of diff.renameLimit. + +status.renames:: + Whether and how Git detects renames in linkgit:git-status[1] and + linkgit:git-commit[1] . If set to "false", rename detection is + disabled. If set to "true", basic rename detection is enabled. + If set to "copies" or "copy", Git will detect copies, as well. + Defaults to the value of diff.renames. + status.showStash:: If set to true, linkgit:git-status[1] will display the number of entries currently stashed away. @@@ -3301,8 -3237,7 +3328,8 @@@ submodule.active: submodule.recurse:: Specifies if commands recurse into submodules by default. This - applies to all commands that have a `--recurse-submodules` option. + applies to all commands that have a `--recurse-submodules` option, + except `clone`. Defaults to false. submodule.fetchJobs:: @@@ -3435,10 -3370,6 +3462,10 @@@ uploadpack.packObjectsHook: was run. I.e., `upload-pack` will feed input intended for `pack-objects` to the hook, and expects a completed packfile on stdout. + +uploadpack.allowFilter:: + If this option is set, `upload-pack` will support partial + clone and partial fetch object filtering. + Note that this configuration variable is ignored if it is seen in the repository-level config (this is a safety measure against fetching from diff --combined builtin/blame.c index bfdf7cc132,f95aac7468..4202584f97 --- a/builtin/blame.c +++ b/builtin/blame.c @@@ -7,6 -7,7 +7,7 @@@ #include "cache.h" #include "config.h" + #include "color.h" #include "builtin.h" #include "commit.h" #include "diff.h" @@@ -23,6 -24,7 +24,7 @@@ #include "dir.h" #include "progress.h" #include "blame.h" + #include "string-list.h" static char blame_usage[] = N_("git blame [] [] [] [--] "); @@@ -46,6 -48,8 +48,8 @@@ static int xdl_opts static int abbrev = -1; static int no_whole_file_rename; static int show_progress; + static char repeated_meta_color[COLOR_MAXLEN]; + static int coloring_mode; static struct date_mode blame_date_mode = { DATE_ISO8601 }; static size_t blame_date_width; @@@ -316,10 -320,12 +320,12 @@@ static const char *format_time(timestam #define OUTPUT_PORCELAIN 010 #define OUTPUT_SHOW_NAME 020 #define OUTPUT_SHOW_NUMBER 040 - #define OUTPUT_SHOW_SCORE 0100 - #define OUTPUT_NO_AUTHOR 0200 + #define OUTPUT_SHOW_SCORE 0100 + #define OUTPUT_NO_AUTHOR 0200 #define OUTPUT_SHOW_EMAIL 0400 - #define OUTPUT_LINE_PORCELAIN 01000 + #define OUTPUT_LINE_PORCELAIN 01000 + #define OUTPUT_COLOR_LINE 02000 + #define OUTPUT_SHOW_AGE_WITH_COLOR 04000 static void emit_porcelain_details(struct blame_origin *suspect, int repeat) { @@@ -367,6 -373,63 +373,63 @@@ static void emit_porcelain(struct blame putchar('\n'); } + static struct color_field { + timestamp_t hop; + char col[COLOR_MAXLEN]; + } *colorfield; + static int colorfield_nr, colorfield_alloc; + + static void parse_color_fields(const char *s) + { + struct string_list l = STRING_LIST_INIT_DUP; + struct string_list_item *item; + enum { EXPECT_DATE, EXPECT_COLOR } next = EXPECT_COLOR; + + colorfield_nr = 0; + + /* Ideally this would be stripped and split at the same time? */ + string_list_split(&l, s, ',', -1); + ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); + + for_each_string_list_item(item, &l) { + switch (next) { + case EXPECT_DATE: + colorfield[colorfield_nr].hop = approxidate(item->string); + next = EXPECT_COLOR; + colorfield_nr++; + ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); + break; + case EXPECT_COLOR: + if (color_parse(item->string, colorfield[colorfield_nr].col)) + die(_("expecting a color: %s"), item->string); + next = EXPECT_DATE; + break; + } + } + + if (next == EXPECT_COLOR) + die (_("must end with a color")); + + colorfield[colorfield_nr].hop = TIME_MAX; + } + + static void setup_default_color_by_age(void) + { + parse_color_fields("blue,12 month ago,white,1 month ago,red"); + } + + static void determine_line_heat(struct blame_entry *ent, const char **dest_color) + { + int i = 0; + struct commit_info ci; + get_commit_info(ent->suspect->commit, &ci, 1); + + while (i < colorfield_nr && ci.author_time > colorfield[i].hop) + i++; + + *dest_color = colorfield[i].col; + } + static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) { int cnt; @@@ -375,15 -438,35 +438,35 @@@ struct commit_info ci; char hex[GIT_MAX_HEXSZ + 1]; int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); + const char *default_color = NULL, *color = NULL, *reset = NULL; get_commit_info(suspect->commit, &ci, 1); oid_to_hex_r(hex, &suspect->commit->object.oid); cp = blame_nth_line(sb, ent->lno); + + if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) { + determine_line_heat(ent, &default_color); + color = default_color; + reset = GIT_COLOR_RESET; + } + for (cnt = 0; cnt < ent->num_lines; cnt++) { char ch; int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev; + if (opt & OUTPUT_COLOR_LINE) { + if (cnt > 0) { + color = repeated_meta_color; + reset = GIT_COLOR_RESET; + } else { + color = default_color ? default_color : NULL; + reset = default_color ? GIT_COLOR_RESET : NULL; + } + } + if (color) + fputs(color, stdout); + if (suspect->commit->object.flags & UNINTERESTING) { if (blank_boundary) memset(hex, ' ', length); @@@ -433,6 -516,8 +516,8 @@@ printf(" %*d) ", max_digits, ent->lno + 1 + cnt); } + if (reset) + fputs(reset, stdout); do { ch = *cp++; putchar(ch); @@@ -499,7 -584,7 +584,7 @@@ static int read_ancestry(const char *gr static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect) { - const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash, + const char *uniq = find_unique_abbrev(&suspect->commit->object.oid, auto_abbrev); int len = strlen(uniq); if (auto_abbrev < len) @@@ -607,6 -692,30 +692,30 @@@ static int git_blame_config(const char parse_date_format(value, &blame_date_mode); return 0; } + if (!strcmp(var, "color.blame.repeatedlines")) { + if (color_parse_mem(value, strlen(value), repeated_meta_color)) + warning(_("invalid color '%s' in color.blame.repeatedLines"), + value); + return 0; + } + if (!strcmp(var, "color.blame.highlightrecent")) { + parse_color_fields(value); + return 0; + } + + if (!strcmp(var, "blame.coloring")) { + if (!strcmp(value, "repeatedLines")) { + coloring_mode |= OUTPUT_COLOR_LINE; + } else if (!strcmp(value, "highlightRecent")) { + coloring_mode |= OUTPUT_SHOW_AGE_WITH_COLOR; + } else if (!strcmp(value, "none")) { + coloring_mode &= ~(OUTPUT_COLOR_LINE | + OUTPUT_SHOW_AGE_WITH_COLOR); + } else { + warning(_("invalid value for blame.coloring")); + return 0; + } + } if (git_diff_heuristic_config(var, value, cb) < 0) return -1; @@@ -649,15 -758,6 +758,15 @@@ static int blame_move_callback(const st return 0; } +static int is_a_rev(const char *name) +{ + struct object_id oid; + + if (get_oid(name, &oid)) + return 0; + return OBJ_NONE < oid_object_info(the_repository, &oid, NULL); +} + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@@ -690,6 -790,8 +799,8 @@@ OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR), OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL), OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE), + OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE), + OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR), /* * The following two options are parsed by parse_revision_opt() @@@ -714,6 -816,7 +825,7 @@@ unsigned int range_i; long anchor; + setup_default_color_by_age(); git_config(git_blame_config, &output_option); init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; @@@ -729,7 -832,6 +841,7 @@@ for (;;) { switch (parse_options_step(&ctx, options, blame_opt_usage)) { case PARSE_OPT_HELP: + case PARSE_OPT_ERROR: exit(129); case PARSE_OPT_DONE: if (ctx.argv[0]) @@@ -855,15 -957,16 +967,15 @@@ parse_done } else { if (argc < 2) usage_with_options(blame_opt_usage, options); - path = add_prefix(prefix, argv[argc - 1]); - if (argc == 3 && !file_exists(path)) { /* (2b) */ + if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */ path = add_prefix(prefix, argv[1]); argv[1] = argv[2]; + } else { /* (2a) */ + if (argc == 2 && is_a_rev(argv[1]) && !get_git_work_tree()) + die("missing to blame"); + path = add_prefix(prefix, argv[argc - 1]); } argv[argc - 1] = "--"; - - setup_work_tree(); - if (!file_exists(path)) - die_errno("cannot stat path '%s'", path); } revs.disable_stdin = 1; @@@ -949,8 -1052,17 +1061,17 @@@ blame_coalesce(&sb); - if (!(output_option & OUTPUT_PORCELAIN)) + if (!(output_option & (OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR))) + output_option |= coloring_mode; + + if (!(output_option & OUTPUT_PORCELAIN)) { find_alignment(&sb, &output_option); + if (!*repeated_meta_color && + (output_option & OUTPUT_COLOR_LINE)) + strcpy(repeated_meta_color, GIT_COLOR_CYAN); + } + if (output_option & OUTPUT_ANNOTATE_COMPAT) + output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR); output(&sb, output_option); free((void *)sb.final_buf);