diff options
Diffstat (limited to 'diff.c')
-rw-r--r-- | diff.c | 162 |
1 files changed, 131 insertions, 31 deletions
@@ -20,7 +20,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 100; -static int diff_use_color_default; +int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@ -57,7 +57,7 @@ static int parse_diff_color_slot(const char *var, int ofs) static struct ll_diff_driver { const char *name; struct ll_diff_driver *next; - char *cmd; + const char *cmd; } *user_diff, **user_diff_tail; /* @@ -86,10 +86,7 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val user_diff_tail = &(drv->next); } - if (!value) - return error("%s: lacks value", var); - drv->cmd = strdup(value); - return 0; + return git_config_string(&(drv->cmd), var, value); } /* @@ -158,16 +155,16 @@ int git_diff_ui_config(const char *var, const char *value) return 0; } if (!strcmp(var, "diff.external")) { + if (!value) + return config_error_nonbool(var); external_diff_cmd_cfg = xstrdup(value); return 0; } if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); - if (ep != var + 4) { - if (!strcmp(ep, ".command")) - return parse_lldiff_command(var, ep, value); - } + if (ep != var + 4 && !strcmp(ep, ".command")) + return parse_lldiff_command(var, ep, value); } return git_diff_basic_config(var, value); @@ -177,6 +174,8 @@ int git_diff_basic_config(const char *var, const char *value) { if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); + if (!value) + return config_error_nonbool(var); color_parse(value, var, diff_colors[slot]); return 0; } @@ -184,12 +183,15 @@ int git_diff_basic_config(const char *var, const char *value) if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); if (ep != var + 4) { - if (!strcmp(ep, ".funcname")) + if (!strcmp(ep, ".funcname")) { + if (!value) + return config_error_nonbool(var); return parse_funcname_pattern(var, ep, value); + } } } - return git_default_config(var, value); + return git_color_default_config(var, value); } static char *quote_two(const char *one, const char *two) @@ -270,8 +272,8 @@ static void print_line_count(int count) } } -static void copy_file(int prefix, const char *data, int size, - const char *set, const char *reset) +static void copy_file_with_prefix(int prefix, const char *data, int size, + const char *set, const char *reset) { int ch, nl_just_seen = 1; while (0 < size--) { @@ -329,9 +331,9 @@ static void emit_rewrite_diff(const char *name_a, print_line_count(lc_b); printf(" @@%s\n", reset); if (lc_a) - copy_file('-', one->data, one->size, old, reset); + copy_file_with_prefix('-', one->data, one->size, old, reset); if (lc_b) - copy_file('+', two->data, two->size, new, reset); + copy_file_with_prefix('+', two->data, two->size, new, reset); } static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) @@ -980,6 +982,90 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) } } +struct diffstat_dir { + struct diffstat_file **files; + int nr, percent, cumulative; +}; + +static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) +{ + unsigned long this_dir = 0; + unsigned int sources = 0; + + while (dir->nr) { + struct diffstat_file *f = *dir->files; + int namelen = strlen(f->name); + unsigned long this; + char *slash; + + if (namelen < baselen) + break; + if (memcmp(f->name, base, baselen)) + break; + slash = strchr(f->name + baselen, '/'); + if (slash) { + int newbaselen = slash + 1 - f->name; + this = gather_dirstat(dir, changed, f->name, newbaselen); + sources++; + } else { + if (f->is_unmerged || f->is_binary) + this = 0; + else + this = f->added + f->deleted; + dir->files++; + dir->nr--; + sources += 2; + } + this_dir += this; + } + + /* + * We don't report dirstat's for + * - the top level + * - or cases where everything came from a single directory + * under this directory (sources == 1). + */ + if (baselen && sources != 1) { + int permille = this_dir * 1000 / changed; + if (permille) { + int percent = permille / 10; + if (percent >= dir->percent) { + printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base); + if (!dir->cumulative) + return 0; + } + } + } + return this_dir; +} + +static void show_dirstat(struct diffstat_t *data, struct diff_options *options) +{ + int i; + unsigned long changed; + struct diffstat_dir dir; + + /* Calculate total changes */ + changed = 0; + for (i = 0; i < data->nr; i++) { + if (data->files[i]->is_binary || data->files[i]->is_unmerged) + continue; + changed += data->files[i]->added; + changed += data->files[i]->deleted; + } + + /* This can happen even with many files, if everything was renames */ + if (!changed) + return; + + /* Show all directories with more than x% of the changes */ + dir.files = data->files; + dir.nr = data->nr; + dir.percent = options->dirstat_percent; + dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; + gather_dirstat(&dir, changed, "", 0); +} + static void free_diffstat_info(struct diffstat_t *diffstat) { int i; @@ -1011,6 +1097,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) char *err; if (line[0] == '+') { + data->lineno++; data->status = check_and_emit_line(line + 1, len - 1, data->ws_rule, NULL, NULL, NULL, NULL); if (!data->status) @@ -1021,13 +1108,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) emit_line(set, reset, line, 1); (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, stdout, set, reset, ws); - data->lineno++; } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { char *plus = strchr(line, '+'); if (plus) - data->lineno = strtol(plus, NULL, 10); + data->lineno = strtol(plus, NULL, 10) - 1; else die("invalid diff"); } @@ -1197,7 +1283,7 @@ static struct builtin_funcname_pattern { "new\\|return\\|switch\\|throw\\|while\\)\n" "^[ ]*\\(\\([ ]*" "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}" - "[ ]*([^;]*$\\)" }, + "[ ]*([^;]*\\)$" }, { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" }, }; @@ -1511,17 +1597,22 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int if (pos < 0) return 0; ce = active_cache[pos]; - if ((lstat(name, &st) < 0) || - !S_ISREG(st.st_mode) || /* careful! */ - ce_match_stat(ce, &st, 0) || - hashcmp(sha1, ce->sha1)) + + /* + * This is not the sha1 we are looking for, or + * unreusable because it is not a regular file. + */ + if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode)) return 0; - /* we return 1 only when we can stat, it is a regular file, - * stat information matches, and sha1 recorded in the cache - * matches. I.e. we know the file in the work tree really is - * the same as the <name, sha1> pair. + + /* + * If ce matches the file in the work tree, we can reuse it. */ - return 1; + if (ce_uptodate(ce) || + (!lstat(name, &st) && !ce_match_stat(ce, &st, 0))) + return 1; + + return 0; } static int populate_from_stdin(struct diff_filespec *s) @@ -1625,7 +1716,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) * Convert from working tree format to canonical git format */ strbuf_init(&buf, 0); - if (convert_to_git(s->path, s->data, s->size, &buf)) { + if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) { size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; @@ -2072,12 +2163,13 @@ void diff_setup(struct diff_options *options) options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; + options->dirstat_percent = 3; options->context = 3; options->msg_sep = ""; options->change = diff_change; options->add_remove = diff_addremove; - if (diff_use_color_default) + if (diff_use_color_default > 0) DIFF_OPT_SET(options, COLOR_DIFF); else DIFF_OPT_CLR(options, COLOR_DIFF); @@ -2120,6 +2212,7 @@ int diff_setup_done(struct diff_options *options) DIFF_FORMAT_NUMSTAT | DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SHORTSTAT | + DIFF_FORMAT_DIRSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH); @@ -2131,6 +2224,7 @@ int diff_setup_done(struct diff_options *options) DIFF_FORMAT_NUMSTAT | DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SHORTSTAT | + DIFF_FORMAT_DIRSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_CHECKDIFF)) DIFF_OPT_SET(options, RECURSIVE); @@ -2241,6 +2335,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_NUMSTAT; else if (!strcmp(arg, "--shortstat")) options->output_format |= DIFF_FORMAT_SHORTSTAT; + else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent)) + options->output_format |= DIFF_FORMAT_DIRSTAT; + else if (!strcmp(arg, "--cumulative")) + options->output_format |= DIFF_FORMAT_CUMULATIVE; else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) @@ -2978,7 +3076,7 @@ void diff_flush(struct diff_options *options) separator++; } - if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) { + if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) { struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); @@ -2988,6 +3086,8 @@ void diff_flush(struct diff_options *options) if (check_pair_status(p)) diff_flush_stat(p, options, &diffstat); } + if (output_format & DIFF_FORMAT_DIRSTAT) + show_dirstat(&diffstat, options); if (output_format & DIFF_FORMAT_NUMSTAT) show_numstat(&diffstat, options); if (output_format & DIFF_FORMAT_DIFFSTAT) |