From: Junio C Hamano Date: Tue, 6 Mar 2018 23:11:46 +0000 (-0800) Subject: Merge branch 'nd/diff-stat-with-summary' into next X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/d543f92f5e61aee239cf988429459101fc14354b?hp=-c Merge branch 'nd/diff-stat-with-summary' into next "git diff" and friends learned "--compact-summary" that shows the information usually given with the "--summary" option on the same line as the diffstat output of the "--stat" option (which saves vertical space and keeps info on a single path at the same place). * nd/diff-stat-with-summary: diff: add --compact-summary diff.c: refactor pprint_rename() to use strbuf --- d543f92f5e61aee239cf988429459101fc14354b diff --combined Documentation/diff-options.txt index c330c01ff0,0c7fd5815e..e3a44f03cd --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@@ -128,6 -128,14 +128,14 @@@ have to use `--diff-algorithm=default` These parameters can also be set individually with `--stat-width=`, `--stat-name-width=` and `--stat-count=`. + --compact-summary:: + Output a condensed summary of extended header information such + as file creations or deletions ("new" or "gone", optionally "+l" + if it's a symlink) and mode changes ("+x" or "-x" for adding + or removing executable bit respectively) in diffstat. The + information is put betwen the filename part and the graph + part. Implies `--stat`. + --numstat:: Similar to `--stat`, but shows number of added and deleted lines in decimal notation and pathname without @@@ -508,15 -516,6 +516,15 @@@ occurrences of that string did not chan See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more information. +--find-object=:: + Look for differences that change the number of occurrences of + the specified object. Similar to `-S`, just the argument is different + in that it doesn't search for a specific string but for a specific + object id. ++ +The object can be a blob or a submodule commit. It implies the `-t` option in +`git-log` to also find trees. + --pickaxe-all:: When `-S` or `-G` finds a change, show all the changes in that changeset, not just the files that contain the change @@@ -525,7 -524,6 +533,7 @@@ --pickaxe-regex:: Treat the given to `-S` as an extended POSIX regular expression to match. + endif::git-format-patch[] -O:: diff --combined diff.c index 915f4d6e9f,95124c0e7c..4c59f5f5d3 --- a/diff.c +++ b/diff.c @@@ -246,7 -246,7 +246,7 @@@ static int parse_ws_error_highlight(con */ void init_diff_ui_defaults(void) { - diff_detect_rename_default = 1; + diff_detect_rename_default = DIFF_DETECT_RENAME; } int git_diff_heuristic_config(const char *var, const char *value, void *cb) @@@ -1504,7 -1504,7 +1504,7 @@@ struct diff_words_style_elem struct diff_words_style { enum diff_words_type type; - struct diff_words_style_elem new, old, ctx; + struct diff_words_style_elem new_word, old_word, ctx; const char *newline; }; @@@ -1655,12 -1655,12 +1655,12 @@@ static void fn_out_diff_words_aux(void } if (minus_begin != minus_end) { fn_out_diff_words_write_helper(diff_words->opt, - &style->old, style->newline, + &style->old_word, style->newline, minus_end - minus_begin, minus_begin); } if (plus_begin != plus_end) { fn_out_diff_words_write_helper(diff_words->opt, - &style->new, style->newline, + &style->new_word, style->newline, plus_end - plus_begin, plus_begin); } @@@ -1758,7 -1758,7 +1758,7 @@@ static void diff_words_show(struct diff emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF, line_prefix, strlen(line_prefix), 0); fn_out_diff_words_write_helper(diff_words->opt, - &style->old, style->newline, + &style->old_word, style->newline, diff_words->minus.text.size, diff_words->minus.text.ptr); diff_words->minus.text.size = 0; @@@ -1883,8 -1883,8 +1883,8 @@@ static void init_diff_words_data(struc } if (want_color(o->use_color)) { struct diff_words_style *st = ecbdata->diff_words->style; - st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD); - st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW); + st->old_word.color = diff_get_color_opt(o, DIFF_FILE_OLD); + st->new_word.color = diff_get_color_opt(o, DIFF_FILE_NEW); st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT); } } @@@ -2045,11 -2045,10 +2045,10 @@@ static void fn_out_consume(void *priv, } } - static char *pprint_rename(const char *a, const char *b) + static void pprint_rename(struct strbuf *name, const char *a, const char *b) { - const char *old = a; - const char *new = b; + const char *old_name = a; + const char *new_name = b; - struct strbuf name = STRBUF_INIT; int pfx_length, sfx_length; int pfx_adjust_for_slash; int len_a = strlen(a); @@@ -2059,24 -2058,24 +2058,24 @@@ int qlen_b = quote_c_style(b, NULL, NULL, 0); if (qlen_a || qlen_b) { - quote_c_style(a, &name, NULL, 0); - strbuf_addstr(&name, " => "); - quote_c_style(b, &name, NULL, 0); - return strbuf_detach(&name, NULL); + quote_c_style(a, name, NULL, 0); + strbuf_addstr(name, " => "); + quote_c_style(b, name, NULL, 0); + return; } /* Find common prefix */ pfx_length = 0; - while (*old && *new && *old == *new) { - if (*old == '/') - pfx_length = old - a + 1; - old++; - new++; + while (*old_name && *new_name && *old_name == *new_name) { + if (*old_name == '/') + pfx_length = old_name - a + 1; + old_name++; + new_name++; } /* Find common suffix */ - old = a + len_a; - new = b + len_b; + old_name = a + len_a; + new_name = b + len_b; sfx_length = 0; /* * If there is a common prefix, it must end in a slash. In @@@ -2087,13 -2086,13 +2086,13 @@@ * underrun the input strings. */ pfx_adjust_for_slash = (pfx_length ? 1 : 0); - while (a + pfx_length - pfx_adjust_for_slash <= old && - b + pfx_length - pfx_adjust_for_slash <= new && - *old == *new) { - if (*old == '/') - sfx_length = len_a - (old - a); - old--; - new--; + while (a + pfx_length - pfx_adjust_for_slash <= old_name && + b + pfx_length - pfx_adjust_for_slash <= new_name && + *old_name == *new_name) { + if (*old_name == '/') + sfx_length = len_a - (old_name - a); + old_name--; + new_name--; } /* @@@ -2109,19 -2108,18 +2108,18 @@@ if (b_midlen < 0) b_midlen = 0; - strbuf_grow(&name, pfx_length + a_midlen + b_midlen + sfx_length + 7); + strbuf_grow(name, pfx_length + a_midlen + b_midlen + sfx_length + 7); if (pfx_length + sfx_length) { - strbuf_add(&name, a, pfx_length); - strbuf_addch(&name, '{'); + strbuf_add(name, a, pfx_length); + strbuf_addch(name, '{'); } - strbuf_add(&name, a + pfx_length, a_midlen); - strbuf_addstr(&name, " => "); - strbuf_add(&name, b + pfx_length, b_midlen); + strbuf_add(name, a + pfx_length, a_midlen); + strbuf_addstr(name, " => "); + strbuf_add(name, b + pfx_length, b_midlen); if (pfx_length + sfx_length) { - strbuf_addch(&name, '}'); - strbuf_add(&name, a + len_a - sfx_length, sfx_length); + strbuf_addch(name, '}'); + strbuf_add(name, a + len_a - sfx_length, sfx_length); } - return strbuf_detach(&name, NULL); } struct diffstat_t { @@@ -2131,6 -2129,7 +2129,7 @@@ char *from_name; char *name; char *print_name; + const char *comments; unsigned is_unmerged:1; unsigned is_binary:1; unsigned is_renamed:1; @@@ -2197,23 -2196,20 +2196,20 @@@ static void show_graph(struct strbuf *o static void fill_print_name(struct diffstat_file *file) { - char *pname; + struct strbuf pname = STRBUF_INIT; if (file->print_name) return; - if (!file->is_renamed) { - struct strbuf buf = STRBUF_INIT; - if (quote_c_style(file->name, &buf, NULL, 0)) { - pname = strbuf_detach(&buf, NULL); - } else { - pname = file->name; - strbuf_release(&buf); - } - } else { - pname = pprint_rename(file->from_name, file->name); - } - file->print_name = pname; + if (file->is_renamed) + pprint_rename(&pname, file->from_name, file->name); + else + quote_c_style(file->name, &pname, NULL, 0); + + if (file->comments) + strbuf_addf(&pname, " (%s)", file->comments); + + file->print_name = strbuf_detach(&pname, NULL); } static void print_stat_summary_inserts_deletes(struct diff_options *options, @@@ -2594,14 -2590,14 +2590,14 @@@ struct dirstat_dir static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen) { - unsigned long this_dir = 0; + unsigned long sum_changes = 0; unsigned int sources = 0; const char *line_prefix = diff_line_prefix(opt); while (dir->nr) { struct dirstat_file *f = dir->files; int namelen = strlen(f->name); - unsigned long this; + unsigned long changes; char *slash; if (namelen < baselen) @@@ -2611,15 -2607,15 +2607,15 @@@ slash = strchr(f->name + baselen, '/'); if (slash) { int newbaselen = slash + 1 - f->name; - this = gather_dirstat(opt, dir, changed, f->name, newbaselen); + changes = gather_dirstat(opt, dir, changed, f->name, newbaselen); sources++; } else { - this = f->changed; + changes = f->changed; dir->files++; dir->nr--; sources += 2; } - this_dir += this; + sum_changes += changes; } /* @@@ -2629,8 -2625,8 +2625,8 @@@ * under this directory (sources == 1). */ if (baselen && sources != 1) { - if (this_dir) { - int permille = this_dir * 1000 / changed; + if (sum_changes) { + int permille = sum_changes * 1000 / changed; if (permille >= dir->permille) { fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix, permille / 10, permille % 10, baselen, base); @@@ -2639,7 -2635,7 +2635,7 @@@ } } } - return this_dir; + return sum_changes; } static int dirstat_compare(const void *_a, const void *_b) @@@ -2797,8 -2793,7 +2793,7 @@@ static void free_diffstat_info(struct d int i; for (i = 0; i < diffstat->nr; i++) { struct diffstat_file *f = diffstat->files[i]; - if (f->name != f->print_name) - free(f->print_name); + free(f->print_name); free(f->name); free(f->from_name); free(f); @@@ -3248,6 -3243,32 +3243,32 @@@ static void builtin_diff(const char *na return; } + static char *get_compact_summary(const struct diff_filepair *p, int is_renamed) + { + if (!is_renamed) { + if (p->status == DIFF_STATUS_ADDED) { + if (S_ISLNK(p->two->mode)) + return "new +l"; + else if ((p->two->mode & 0777) == 0755) + return "new +x"; + else + return "new"; + } else if (p->status == DIFF_STATUS_DELETED) + return "gone"; + } + if (S_ISLNK(p->one->mode) && !S_ISLNK(p->two->mode)) + return "mode -l"; + else if (!S_ISLNK(p->one->mode) && S_ISLNK(p->two->mode)) + return "mode +l"; + else if ((p->one->mode & 0777) == 0644 && + (p->two->mode & 0777) == 0755) + return "mode +x"; + else if ((p->one->mode & 0777) == 0755 && + (p->two->mode & 0777) == 0644) + return "mode -x"; + return NULL; + } + static void builtin_diffstat(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, @@@ -3267,6 -3288,8 +3288,8 @@@ data = diffstat_add(diffstat, name_a, name_b); data->is_interesting = p->status != DIFF_STATUS_UNKNOWN; + if (o->flags.stat_with_summary) + data->comments = get_compact_summary(p, data->is_renamed); if (!one || !two) { data->is_unmerged = 1; @@@ -3520,13 -3543,13 +3543,13 @@@ int diff_populate_filespec(struct diff_ { int size_only = flags & CHECK_SIZE_ONLY; int err = 0; + int conv_flags = global_conv_flags_eol; /* * demote FAIL to WARN to allow inspecting the situation * instead of refusing. */ - enum safe_crlf crlf_warn = (safe_crlf == SAFE_CRLF_FAIL - ? SAFE_CRLF_WARN - : safe_crlf); + if (conv_flags & CONV_EOL_RNDTRP_DIE) + conv_flags = CONV_EOL_RNDTRP_WARN; if (!DIFF_FILE_VALID(s)) die("internal error: asking to populate invalid file."); @@@ -3603,7 -3626,7 +3626,7 @@@ /* * Convert from working tree format to canonical git format */ - if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, crlf_warn)) { + if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) { size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; @@@ -3660,15 -3683,15 +3683,15 @@@ static void prep_temp_blob(const char * int mode) { struct strbuf buf = STRBUF_INIT; - struct strbuf template = STRBUF_INIT; + struct strbuf tempfile = STRBUF_INIT; char *path_dup = xstrdup(path); const char *base = basename(path_dup); /* Generate "XXXXXX_basename.ext" */ - strbuf_addstr(&template, "XXXXXX_"); - strbuf_addstr(&template, base); + strbuf_addstr(&tempfile, "XXXXXX_"); + strbuf_addstr(&tempfile, base); - temp->tempfile = mks_tempfile_ts(template.buf, strlen(base) + 1); + temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1); if (!temp->tempfile) die_errno("unable to create temp-file"); if (convert_to_working_tree(path, @@@ -3683,7 -3706,7 +3706,7 @@@ oid_to_hex_r(temp->hex, oid); xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode); strbuf_release(&buf); - strbuf_release(&template); + strbuf_release(&tempfile); free(path_dup); } @@@ -4086,7 -4109,6 +4109,7 @@@ void diff_setup(struct diff_options *op options->interhunkcontext = diff_interhunk_context_default; options->ws_error_highlight = ws_error_highlight_default; options->flags.rename_empty = 1; + options->objfind = NULL; /* pathchange left =NULL by default */ options->change = diff_change; @@@ -4111,20 -4133,22 +4134,20 @@@ void diff_setup_done(struct diff_options *options) { - int count = 0; + unsigned check_mask = DIFF_FORMAT_NAME | + DIFF_FORMAT_NAME_STATUS | + DIFF_FORMAT_CHECKDIFF | + DIFF_FORMAT_NO_OUTPUT; if (options->set_default) options->set_default(options); - if (options->output_format & DIFF_FORMAT_NAME) - count++; - if (options->output_format & DIFF_FORMAT_NAME_STATUS) - count++; - if (options->output_format & DIFF_FORMAT_CHECKDIFF) - count++; - if (options->output_format & DIFF_FORMAT_NO_OUTPUT) - count++; - if (count > 1) + if (HAS_MULTI_BITS(options->output_format & check_mask)) die(_("--name-only, --name-status, --check and -s are mutually exclusive")); + if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)) + die(_("-G, -S and --find-object are mutually exclusive")); + /* * Most of the time we can say "there are changes" * only by checking if there are changed paths, but @@@ -4174,7 -4198,7 +4197,7 @@@ /* * Also pickaxe would not work very well if you do not say recursive */ - if (options->pickaxe) + if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) options->flags.recursive = 1; /* * When patches are generated, submodules diffed against the work tree @@@ -4488,23 -4512,6 +4511,23 @@@ static int parse_ws_error_highlight_opt return 1; } +static int parse_objfind_opt(struct diff_options *opt, const char *arg) +{ + struct object_id oid; + + if (get_oid(arg, &oid)) + return error("unable to resolve '%s'", arg); + + if (!opt->objfind) + opt->objfind = xcalloc(1, sizeof(*opt->objfind)); + + opt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND; + opt->flags.recursive = 1; + opt->flags.tree_in_recursive = 1; + oidset_insert(opt->objfind, &oid); + return 1; +} + int diff_opt_parse(struct diff_options *options, const char **av, int ac, const char *prefix) { @@@ -4553,6 -4560,11 +4576,11 @@@ else if (starts_with(arg, "--stat")) /* --stat, --stat-width, --stat-name-width, or --stat-count */ return stat_opt(options, av); + else if (!strcmp(arg, "--compact-summary")) { + options->flags.stat_with_summary = 1; + options->output_format |= DIFF_FORMAT_DIFFSTAT; + } else if (!strcmp(arg, "--no-compact-summary")) + options->flags.stat_with_summary = 0; /* renames options */ else if (starts_with(arg, "-B") || @@@ -4752,8 -4764,7 +4780,8 @@@ else if ((argcount = short_opt('O', av, &optarg))) { options->orderfile = prefix_filename(prefix, optarg); return argcount; - } + } else if (skip_prefix(arg, "--find-object=", &arg)) + return parse_objfind_opt(options, arg); else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) { int offending = parse_diff_filter_opt(optarg, options); if (offending) @@@ -5241,10 -5252,12 +5269,12 @@@ static void show_rename_copy(struct dif struct diff_filepair *p) { struct strbuf sb = STRBUF_INIT; - char *names = pprint_rename(p->one->path, p->two->path); + struct strbuf names = STRBUF_INIT; + + pprint_rename(&names, p->one->path, p->two->path); strbuf_addf(&sb, " %s %s (%d%%)\n", - renamecopy, names, similarity_index(p)); - free(names); + renamecopy, names.buf, similarity_index(p)); + strbuf_release(&names); emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY, sb.buf, sb.len, 0); show_mode_change(opt, p, 0); @@@ -5471,7 -5484,6 +5501,7 @@@ N_("you may want to set your %s variabl void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc) { + fflush(stdout); if (degraded_cc) warning(_(degrade_cc_to_c_warning)); else if (needed) @@@ -5801,7 -5813,7 +5831,7 @@@ void diffcore_std(struct diff_options * if (options->break_opt != -1) diffcore_merge_broken(); } - if (options->pickaxe) + if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) diffcore_pickaxe(options); if (options->orderfile) diffcore_order(options->orderfile); diff --combined diff.h index 6bd278aac1,28a8fb4346..d29560f822 --- a/diff.h +++ b/diff.h @@@ -7,7 -7,6 +7,7 @@@ #include "tree-walk.h" #include "pathspec.h" #include "object.h" +#include "oidset.h" struct rev_info; struct diff_options; @@@ -92,7 -91,9 +92,8 @@@ struct diff_flags unsigned override_submodule_config:1; unsigned dirstat_by_line:1; unsigned funccontext:1; - unsigned pickaxe_ignore_case:1; unsigned default_follow_renames:1; + unsigned stat_with_summary:1; }; static inline void diff_flags_or(struct diff_flags *a, @@@ -146,7 -147,7 +147,7 @@@ struct diff_options int skip_stat_unmatch; int line_termination; int output_format; - int pickaxe_opts; + unsigned pickaxe_opts; int rename_score; int rename_limit; int needed_rename_limit; @@@ -178,8 -179,6 +179,8 @@@ enum diff_words_type word_diff; enum diff_submodule_format submodule_format; + struct oidset *objfind; + /* this is set by diffcore for DIFF_FORMAT_PATCH */ int found_changes; @@@ -332,13 -331,6 +333,13 @@@ extern void diff_setup_done(struct diff #define DIFF_PICKAXE_KIND_S 4 /* traditional plumbing counter */ #define DIFF_PICKAXE_KIND_G 8 /* grep in the patch */ +#define DIFF_PICKAXE_KIND_OBJFIND 16 /* specific object IDs */ + +#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | \ + DIFF_PICKAXE_KIND_G | \ + DIFF_PICKAXE_KIND_OBJFIND) + +#define DIFF_PICKAXE_IGNORE_CASE 32 extern void diffcore_std(struct diff_options *); extern void diffcore_fix_diff_index(struct diff_options *);