X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=diff.c;h=76ba5f4afc162d6cbc31bfaa6cb1875a3975833e;hb=1affea4f629537fd48e3bd66de89a6f45d0fcd82;hp=9c79ee289151e9fc3eb1aaad91dd979bfee63102;hpb=86f8c2368523db5ee56c8856748b26c31ebeb0aa;p=git.git diff --git a/diff.c b/diff.c index 9c79ee289..76ba5f4af 100644 --- a/diff.c +++ b/diff.c @@ -10,6 +10,7 @@ #include "color.h" #include "attr.h" #include "run-command.h" +#include "utf8.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -20,6 +21,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 100; static int diff_use_color_default; +static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; static char diff_colors[][COLOR_MAXLEN] = { @@ -55,17 +57,9 @@ 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; -static void read_config_if_needed(void) -{ - if (!user_diff_tail) { - user_diff_tail = &user_diff; - git_config(git_diff_ui_config); - } -} - /* * Currently there is only "diff..command" variable; * because there are "diff.color." variables, we are parsing @@ -92,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); } /* @@ -163,22 +154,43 @@ int git_diff_ui_config(const char *var, const char *value) diff_auto_refresh_index = git_config_bool(var, 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 (!strcmp(ep, ".funcname")) - return parse_funcname_pattern(var, ep, value); - } + if (ep != var + 4 && !strcmp(ep, ".command")) + return parse_lldiff_command(var, ep, value); } + + return git_diff_basic_config(var, value); +} + +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; } + if (!prefixcmp(var, "diff.")) { + const char *ep = strrchr(var, '.'); + if (ep != var + 4) { + if (!strcmp(ep, ".funcname")) { + if (!value) + return config_error_nonbool(var); + return parse_funcname_pattern(var, ep, value); + } + } + } + return git_default_config(var, value); } @@ -209,6 +221,8 @@ static const char *external_diff(void) if (done_preparing) return external_diff_cmd; external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); + if (!external_diff_cmd) + external_diff_cmd = external_diff_cmd_cfg; done_preparing = 1; return external_diff_cmd; } @@ -258,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--) { @@ -283,36 +297,43 @@ static void emit_rewrite_diff(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, - int color_diff) + struct diff_options *o) { int lc_a, lc_b; + int color_diff = DIFF_OPT_TST(o, COLOR_DIFF); const char *name_a_tab, *name_b_tab; const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO); const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO); const char *old = diff_get_color(color_diff, DIFF_FILE_OLD); const char *new = diff_get_color(color_diff, DIFF_FILE_NEW); const char *reset = diff_get_color(color_diff, DIFF_RESET); + static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT; name_a += (*name_a == '/'); name_b += (*name_b == '/'); name_a_tab = strchr(name_a, ' ') ? "\t" : ""; name_b_tab = strchr(name_b, ' ') ? "\t" : ""; + strbuf_reset(&a_name); + strbuf_reset(&b_name); + quote_two_c_style(&a_name, o->a_prefix, name_a, 0); + quote_two_c_style(&b_name, o->b_prefix, name_b, 0); + diff_populate_filespec(one, 0); diff_populate_filespec(two, 0); lc_a = count_lines(one->data, one->size); lc_b = count_lines(two->data, two->size); - printf("%s--- a/%s%s%s\n%s+++ b/%s%s%s\n%s@@ -", - metainfo, name_a, name_a_tab, reset, - metainfo, name_b, name_b_tab, reset, fraginfo); + printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -", + metainfo, a_name.buf, name_a_tab, reset, + metainfo, b_name.buf, name_b_tab, reset, fraginfo); print_line_count(lc_a); printf(" +"); 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) @@ -439,7 +460,7 @@ static void diff_words_show(struct diff_words_data *diff_words) ecb.outf = xdiff_outf; ecb.priv = diff_words; diff_words->xm.consume = fn_out_diff_words_aux; - xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb); + xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); @@ -451,10 +472,13 @@ static void diff_words_show(struct diff_words_data *diff_words) } } +typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len); + struct emit_callback { struct xdiff_emit_state xm; int nparents, color_diff; unsigned ws_rule; + sane_truncate_fn truncate; const char **label_path; struct diff_words_data *diff_words; int *found_changesp; @@ -486,88 +510,9 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) static void emit_line(const char *set, const char *reset, const char *line, int len) { - if (len > 0 && line[len-1] == '\n') - len--; fputs(set, stdout); fwrite(line, len, 1, stdout); - puts(reset); -} - -static void emit_line_with_ws(int nparents, - const char *set, const char *reset, const char *ws, - const char *line, int len, unsigned ws_rule) -{ - int col0 = nparents; - int last_tab_in_indent = -1; - int last_space_in_indent = -1; - int i; - int tail = len; - int need_highlight_leading_space = 0; - /* - * The line is a newly added line. Does it have funny leading - * whitespaces? In indent, SP should never precede a TAB. In - * addition, under "indent with non tab" rule, there should not - * be more than 8 consecutive spaces. - */ - for (i = col0; i < len; i++) { - if (line[i] == '\t') { - last_tab_in_indent = i; - if ((ws_rule & WS_SPACE_BEFORE_TAB) && - 0 <= last_space_in_indent) - need_highlight_leading_space = 1; - } - else if (line[i] == ' ') - last_space_in_indent = i; - else - break; - } - if ((ws_rule & WS_INDENT_WITH_NON_TAB) && - 0 <= last_space_in_indent && - last_tab_in_indent < 0 && - 8 <= (i - col0)) { - last_tab_in_indent = i; - need_highlight_leading_space = 1; - } - fputs(set, stdout); - fwrite(line, col0, 1, stdout); fputs(reset, stdout); - if (((i == len) || line[i] == '\n') && i != col0) { - /* The whole line was indent */ - emit_line(ws, reset, line + col0, len - col0); - return; - } - i = col0; - if (need_highlight_leading_space) { - while (i < last_tab_in_indent) { - if (line[i] == ' ') { - fputs(ws, stdout); - putchar(' '); - fputs(reset, stdout); - } - else - putchar(line[i]); - i++; - } - } - tail = len - 1; - if (line[tail] == '\n' && i < tail) - tail--; - if (ws_rule & WS_TRAILING_SPACE) { - while (i < tail) { - if (!isspace(line[tail])) - break; - tail--; - } - } - if ((i < tail && line[tail + 1] != '\n')) { - /* This has whitespace between tail+1..len */ - fputs(set, stdout); - fwrite(line + i, tail - i + 1, 1, stdout); - fputs(reset, stdout); - emit_line(ws, reset, line + tail + 1, len - tail - 1); - } - else - emit_line(set, reset, line + i, len - i); } static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len) @@ -577,9 +522,31 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons if (!*ws) emit_line(set, reset, line, len); - else - emit_line_with_ws(ecbdata->nparents, set, reset, ws, - line, len, ecbdata->ws_rule); + else { + /* Emit just the prefix, then the rest. */ + emit_line(set, reset, line, ecbdata->nparents); + (void)check_and_emit_line(line + ecbdata->nparents, + len - ecbdata->nparents, ecbdata->ws_rule, + stdout, set, reset, ws); + } +} + +static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len) +{ + const char *cp; + unsigned long allot; + size_t l = len; + + if (ecb->truncate) + return ecb->truncate(line, len); + cp = line; + allot = l; + while (0 < l) { + (void) utf8_width(&cp, &l); + if (!cp) + break; /* truncated in the middle? */ + } + return allot - l; } static void fn_out_consume(void *priv, char *line, unsigned long len) @@ -587,7 +554,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) int i; int color; struct emit_callback *ecbdata = priv; - const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO); + const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO); + const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN); const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET); *(ecbdata->found_changesp) = 1; @@ -599,9 +567,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : ""; printf("%s--- %s%s%s\n", - set, ecbdata->label_path[0], reset, name_a_tab); + meta, ecbdata->label_path[0], reset, name_a_tab); printf("%s+++ %s%s%s\n", - set, ecbdata->label_path[1], reset, name_b_tab); + meta, ecbdata->label_path[1], reset, name_b_tab); ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } @@ -612,13 +580,15 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) ; if (2 <= i && i < len && line[i] == ' ') { ecbdata->nparents = i - 1; + len = sane_truncate_line(ecbdata, line, len); emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO), reset, line, len); + if (line[len-1] != '\n') + putchar('\n'); return; } if (len < ecbdata->nparents) { - set = reset; emit_line(reset, reset, line, len); return; } @@ -642,7 +612,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) diff_words_show(ecbdata->diff_words); line++; len--; - emit_line(set, reset, line, len); + emit_line(plain, reset, line, len); return; } for (i = 0; i < ecbdata->nparents && len; i++) { @@ -1031,6 +1001,7 @@ struct checkdiff_t { const char *filename; int lineno, color_diff; unsigned ws_rule; + unsigned status; }; static void checkdiff_consume(void *priv, char *line, unsigned long len) @@ -1039,51 +1010,26 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE); const char *reset = diff_get_color(data->color_diff, DIFF_RESET); const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW); + char *err; if (line[0] == '+') { - int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0; - - /* check space before tab */ - for (i = 1; i < len; i++) { - if (line[i] == ' ') - spaces++; - else if (line[i] == '\t') { - if (spaces) { - space_before_tab = 1; - break; - } - } - else - break; - } - - /* check whitespace at line end */ - if (line[len - 1] == '\n') - len--; - if (isspace(line[len - 1])) - white_space_at_end = 1; - - if (space_before_tab || white_space_at_end) { - printf("%s:%d: %s", data->filename, data->lineno, ws); - if (space_before_tab) { - printf("space before tab"); - if (white_space_at_end) - putchar(','); - } - if (white_space_at_end) - printf("whitespace at end"); - printf(":%s ", reset); - emit_line_with_ws(1, set, reset, ws, line, len, - data->ws_rule); - } - data->lineno++; + data->status = check_and_emit_line(line + 1, len - 1, + data->ws_rule, NULL, NULL, NULL, NULL); + if (!data->status) + return; + err = whitespace_error_string(data->status); + printf("%s:%d: %s.\n", data->filename, data->lineno, err); + free(err); + emit_line(set, reset, line, 1); + (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, + stdout, set, reset, ws); } 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"); } @@ -1239,7 +1185,6 @@ static const char *funcname_pattern(const char *ident) { struct funcname_pattern *pp; - read_config_if_needed(); for (pp = funcname_pattern_list; pp; pp = pp->next) if (!strcmp(ident, pp->name)) return pp->pattern; @@ -1281,7 +1226,7 @@ static const char *diff_funcname_pattern(struct diff_filespec *one) /* * And define built-in fallback patterns here. Note that - * these can be overriden by the user's config settings. + * these can be overridden by the user's config settings. */ for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++) if (!strcmp(ident, builtin_funcname_pattern[i].name)) @@ -1304,8 +1249,8 @@ static void builtin_diff(const char *name_a, const char *set = diff_get_color_opt(o, DIFF_METAINFO); const char *reset = diff_get_color_opt(o, DIFF_RESET); - a_one = quote_two("a/", name_a + (*name_a == '/')); - b_two = quote_two("b/", name_b + (*name_b == '/')); + a_one = quote_two(o->a_prefix, name_a + (*name_a == '/')); + b_two = quote_two(o->b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset); @@ -1334,8 +1279,7 @@ static void builtin_diff(const char *name_a, if ((one->mode ^ two->mode) & S_IFMT) goto free_ab_and_return; if (complete_rewrite) { - emit_rewrite_diff(name_a, name_b, one, two, - DIFF_OPT_TST(o, COLOR_DIFF)); + emit_rewrite_diff(name_a, name_b, one, two, o); o->found_changes = 1; goto free_ab_and_return; } @@ -1393,7 +1337,7 @@ static void builtin_diff(const char *name_a, if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1446,7 +1390,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; ecb.outf = xdiff_outf; ecb.priv = diffstat; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } free_and_return: @@ -1486,11 +1430,13 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, xpp.flags = XDF_NEED_MINIMAL; ecb.outf = xdiff_outf; ecb.priv = &data; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } free_and_return: diff_free_filespec_data(one); diff_free_filespec_data(two); + if (data.status) + DIFF_OPT_SET(o, CHECK_FAILED); } struct diff_filespec *alloc_filespec(const char *path) @@ -1895,7 +1841,6 @@ static const char *external_diff_attr(const char *name) !ATTR_UNSET(value)) { struct ll_diff_driver *drv; - read_config_if_needed(); for (drv = user_diff; drv; drv = drv->next) if (!strcmp(drv->name, value)) return drv->cmd; @@ -2110,6 +2055,9 @@ void diff_setup(struct diff_options *options) else DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; + + options->a_prefix = "a/"; + options->b_prefix = "b/"; } int diff_setup_done(struct diff_options *options) @@ -2381,6 +2329,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (40 < options->abbrev) options->abbrev = 40; } + else if (!prefixcmp(arg, "--src-prefix=")) + options->a_prefix = arg + 13; + else if (!prefixcmp(arg, "--dst-prefix=")) + options->b_prefix = arg + 13; + else if (!strcmp(arg, "--no-prefix")) + options->a_prefix = options->b_prefix = ""; else return 0; return 1; @@ -2898,7 +2852,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) xecfg.flags = XDL_EMIT_FUNCNAMES; ecb.outf = xdiff_outf; ecb.priv = &data; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } SHA1_Final(sha1, &ctx); @@ -3171,6 +3125,20 @@ void diffcore_std(struct diff_options *options) DIFF_OPT_CLR(options, HAS_CHANGES); } +int diff_result_code(struct diff_options *opt, int status) +{ + int result = 0; + if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && + !(opt->output_format & DIFF_FORMAT_CHECKDIFF)) + return status; + if (DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && + DIFF_OPT_TST(opt, HAS_CHANGES)) + result |= 01; + if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) && + DIFF_OPT_TST(opt, CHECK_FAILED)) + result |= 02; + return result; +} void diff_addremove(struct diff_options *options, int addremove, unsigned mode,