X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=diff.c;h=6a713764831e7521958f4b280f2e1d3911fb3cea;hb=f1efc38bf2856f875b878177c0f0a98d9eb3bb4e;hp=9e9cfc8b75b7b761980b0d32cb0c516ea3505ba3;hpb=31262627109476c529418132e0820687003fb1fe;p=git.git diff --git a/diff.c b/diff.c index 9e9cfc8b7..6a7137648 100644 --- a/diff.c +++ b/diff.c @@ -13,15 +13,197 @@ static int use_size_cache; -int diff_rename_limit_default = -1; +static int diff_detect_rename_default = 0; +static int diff_rename_limit_default = -1; +static int diff_use_color_default = 0; + +/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ +static char diff_colors[][24] = { + "\033[m", /* reset */ + "", /* normal */ + "\033[1m", /* bold */ + "\033[36m", /* cyan */ + "\033[31m", /* red */ + "\033[32m", /* green */ + "\033[33m" /* yellow */ +}; + +static int parse_diff_color_slot(const char *var, int ofs) +{ + if (!strcasecmp(var+ofs, "plain")) + return DIFF_PLAIN; + if (!strcasecmp(var+ofs, "meta")) + return DIFF_METAINFO; + if (!strcasecmp(var+ofs, "frag")) + return DIFF_FRAGINFO; + if (!strcasecmp(var+ofs, "old")) + return DIFF_FILE_OLD; + if (!strcasecmp(var+ofs, "new")) + return DIFF_FILE_NEW; + if (!strcasecmp(var+ofs, "commit")) + return DIFF_COMMIT; + die("bad config variable '%s'", var); +} + +static int parse_color(const char *name, int len) +{ + static const char * const color_names[] = { + "normal", "black", "red", "green", "yellow", + "blue", "magenta", "cyan", "white" + }; + char *end; + int i; + for (i = 0; i < ARRAY_SIZE(color_names); i++) { + const char *str = color_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return i - 1; + } + i = strtol(name, &end, 10); + if (*name && !*end && i >= -1 && i <= 255) + return i; + return -2; +} + +static int parse_attr(const char *name, int len) +{ + static const int attr_values[] = { 1, 2, 4, 5, 7 }; + static const char * const attr_names[] = { + "bold", "dim", "ul", "blink", "reverse" + }; + int i; + for (i = 0; i < ARRAY_SIZE(attr_names); i++) { + const char *str = attr_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return attr_values[i]; + } + return -1; +} + +static void parse_diff_color_value(const char *value, const char *var, char *dst) +{ + const char *ptr = value; + int attr = -1; + int fg = -2; + int bg = -2; + + if (!strcasecmp(value, "reset")) { + strcpy(dst, "\033[m"); + return; + } + + /* [fg [bg]] [attr] */ + while (*ptr) { + const char *word = ptr; + int val, len = 0; + + while (word[len] && !isspace(word[len])) + len++; + + ptr = word + len; + while (*ptr && isspace(*ptr)) + ptr++; + + val = parse_color(word, len); + if (val >= -1) { + if (fg == -2) { + fg = val; + continue; + } + if (bg == -2) { + bg = val; + continue; + } + goto bad; + } + val = parse_attr(word, len); + if (val < 0 || attr != -1) + goto bad; + attr = val; + } + + if (attr >= 0 || fg >= 0 || bg >= 0) { + int sep = 0; + + *dst++ = '\033'; + *dst++ = '['; + if (attr >= 0) { + *dst++ = '0' + attr; + sep++; + } + if (fg >= 0) { + if (sep++) + *dst++ = ';'; + if (fg < 8) { + *dst++ = '3'; + *dst++ = '0' + fg; + } else { + dst += sprintf(dst, "38;5;%d", fg); + } + } + if (bg >= 0) { + if (sep++) + *dst++ = ';'; + if (bg < 8) { + *dst++ = '4'; + *dst++ = '0' + bg; + } else { + dst += sprintf(dst, "48;5;%d", bg); + } + } + *dst++ = 'm'; + } + *dst = 0; + return; +bad: + die("bad config value '%s' for variable '%s'", value, var); +} -int git_diff_config(const char *var, const char *value) +/* + * These are to give UI layer defaults. + * The core-level commands such as git-diff-files should + * never be affected by the setting of diff.renames + * the user happens to have in the configuration file. + */ +int git_diff_ui_config(const char *var, const char *value) { if (!strcmp(var, "diff.renamelimit")) { diff_rename_limit_default = git_config_int(var, value); return 0; } - + if (!strcmp(var, "diff.color")) { + if (!value) + diff_use_color_default = 1; /* bool */ + else if (!strcasecmp(value, "auto")) { + diff_use_color_default = 0; + if (isatty(1) || pager_in_use) { + char *term = getenv("TERM"); + if (term && strcmp(term, "dumb")) + diff_use_color_default = 1; + } + } + else if (!strcasecmp(value, "never")) + diff_use_color_default = 0; + else if (!strcasecmp(value, "always")) + diff_use_color_default = 1; + else + diff_use_color_default = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "diff.renames")) { + if (!value) + diff_detect_rename_default = DIFF_DETECT_RENAME; + else if (!strcasecmp(value, "copies") || + !strcasecmp(value, "copy")) + diff_detect_rename_default = DIFF_DETECT_COPY; + else if (git_config_bool(var,value)) + diff_detect_rename_default = DIFF_DETECT_RENAME; + return 0; + } + if (!strncmp(var, "diff.color.", 11)) { + int slot = parse_diff_color_slot(var, 11); + parse_diff_color_value(value, var, diff_colors[slot]); + return 0; + } return git_default_config(var, value); } @@ -165,7 +347,7 @@ static void emit_rewrite_diff(const char *name_a, static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) { if (!DIFF_FILE_VALID(one)) { - mf->ptr = ""; /* does not matter */ + mf->ptr = (char *)""; /* does not matter */ mf->size = 0; return 0; } @@ -177,23 +359,58 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) } struct emit_callback { + struct xdiff_emit_state xm; + int nparents, color_diff; const char **label_path; }; -static int fn_out(void *priv, mmbuffer_t *mb, int nbuf) +const char *diff_get_color(int diff_use_color, enum color_diff ix) +{ + if (diff_use_color) + return diff_colors[ix]; + return ""; +} + +static void fn_out_consume(void *priv, char *line, unsigned long len) { int i; struct emit_callback *ecbdata = priv; + const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO); + const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET); if (ecbdata->label_path[0]) { - printf("--- %s\n", ecbdata->label_path[0]); - printf("+++ %s\n", ecbdata->label_path[1]); + printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset); + printf("%s+++ %s%s\n", set, ecbdata->label_path[1], reset); ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; } - for (i = 0; i < nbuf; i++) - if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout)) - return -1; - return 0; + + /* This is not really necessary for now because + * this codepath only deals with two-way diffs. + */ + for (i = 0; i < len && line[i] == '@'; i++) + ; + if (2 <= i && i < len && line[i] == ' ') { + ecbdata->nparents = i - 1; + set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO); + } + else if (len < ecbdata->nparents) + set = reset; + else { + int nparents = ecbdata->nparents; + int color = DIFF_PLAIN; + for (i = 0; i < nparents && len; i++) { + if (line[i] == '-') + color = DIFF_FILE_OLD; + else if (line[i] == '+') + color = DIFF_FILE_NEW; + } + set = diff_get_color(ecbdata->color_diff, color); + } + if (len > 0 && line[len-1] == '\n') + len--; + fputs (set, stdout); + fwrite (line, len, 1, stdout); + puts (reset); } static char *pprint_rename(const char *a, const char *b) @@ -324,7 +541,7 @@ static void show_stats(struct diffstat_t* data) } for (i = 0; i < data->nr; i++) { - char *prefix = ""; + const char *prefix = ""; char *name = data->files[i]->name; int added = data->files[i]->added; int deleted = data->files[i]->deleted; @@ -447,7 +664,7 @@ static unsigned char *deflate_it(char *data, z_stream stream; memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, Z_BEST_COMPRESSION); + deflateInit(&stream, zlib_compression_level); bound = deflateBound(&stream, size); deflated = xmalloc(bound); stream.next_out = deflated; @@ -515,7 +732,7 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two) else line[0] = bytes - 26 + 'a' - 1; encode_85(line + 1, cp, bytes); - cp += bytes; + cp = (char *) cp + bytes; puts(line); } printf("\n"); @@ -544,30 +761,32 @@ static void builtin_diff(const char *name_a, mmfile_t mf1, mf2; const char *lbl[2]; char *a_one, *b_two; + const char *set = diff_get_color(o->color_diff, DIFF_METAINFO); + const char *reset = diff_get_color(o->color_diff, DIFF_RESET); a_one = quote_two("a/", name_a); b_two = quote_two("b/", name_b); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; - printf("diff --git %s %s\n", a_one, b_two); + printf("%sdiff --git %s %s%s\n", set, a_one, b_two, reset); if (lbl[0][0] == '/') { /* /dev/null */ - printf("new file mode %06o\n", two->mode); + printf("%snew file mode %06o%s\n", set, two->mode, reset); if (xfrm_msg && xfrm_msg[0]) - puts(xfrm_msg); + printf("%s%s%s\n", set, xfrm_msg, reset); } else if (lbl[1][0] == '/') { - printf("deleted file mode %06o\n", one->mode); + printf("%sdeleted file mode %06o%s\n", set, one->mode, reset); if (xfrm_msg && xfrm_msg[0]) - puts(xfrm_msg); + printf("%s%s%s\n", set, xfrm_msg, reset); } else { if (one->mode != two->mode) { - printf("old mode %06o\n", one->mode); - printf("new mode %06o\n", two->mode); + printf("%sold mode %06o%s\n", set, one->mode, reset); + printf("%snew mode %06o%s\n", set, two->mode, reset); } if (xfrm_msg && xfrm_msg[0]) - puts(xfrm_msg); + printf("%s%s%s\n", set, xfrm_msg, reset); /* * we do not run diff between different kind * of objects. @@ -583,7 +802,7 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) { + if (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) { /* Quite common confusing case */ if (mf1.size == mf2.size && !memcmp(mf1.ptr, mf2.ptr, mf1.size)) @@ -602,8 +821,10 @@ static void builtin_diff(const char *name_a, xdemitcb_t ecb; struct emit_callback ecbdata; + memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.label_path = lbl; - xpp.flags = XDF_NEED_MINIMAL; + ecbdata.color_diff = o->color_diff; + xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; if (!diffopts) @@ -612,8 +833,9 @@ static void builtin_diff(const char *name_a, xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); else if (!strncmp(diffopts, "-u", 2)) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); - ecb.outf = fn_out; + ecb.outf = xdiff_outf; ecb.priv = &ecbdata; + ecbdata.xm.consume = fn_out_consume; xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); } @@ -627,6 +849,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, struct diffstat_t *diffstat, + struct diff_options *o, int complete_rewrite) { mmfile_t mf1, mf2; @@ -656,7 +879,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xdemitconf_t xecfg; xdemitcb_t ecb; - xpp.flags = XDF_NEED_MINIMAL; + xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; xecfg.ctxlen = 0; xecfg.flags = 0; ecb.outf = xdiff_outf; @@ -841,7 +1064,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) err_empty: err = -1; empty: - s->data = ""; + s->data = (char *)""; s->size = 0; return err; } @@ -1241,7 +1464,7 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ - builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, 0); + builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, 0); return; } @@ -1253,7 +1476,7 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, if (p->status == DIFF_STATUS_MODIFIED && p->score) complete_rewrite = 1; - builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite); + builtin_diffstat(name, other, p->one, p->two, diffstat, o, complete_rewrite); } static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) @@ -1278,14 +1501,16 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) void diff_setup(struct diff_options *options) { memset(options, 0, sizeof(*options)); - options->output_format = DIFF_FORMAT_RAW; options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; options->context = 3; + options->msg_sep = ""; options->change = diff_change; options->add_remove = diff_addremove; + options->color_diff = diff_use_color_default; + options->detect_rename = diff_detect_rename_default; } int diff_setup_done(struct diff_options *options) @@ -1295,22 +1520,28 @@ int diff_setup_done(struct diff_options *options) (0 <= options->rename_limit && !options->detect_rename)) return -1; + if (options->output_format & (DIFF_FORMAT_NAME | + DIFF_FORMAT_NAME_STATUS | + DIFF_FORMAT_CHECKDIFF | + DIFF_FORMAT_NO_OUTPUT)) + options->output_format &= ~(DIFF_FORMAT_RAW | + DIFF_FORMAT_DIFFSTAT | + DIFF_FORMAT_SUMMARY | + DIFF_FORMAT_PATCH); + /* * These cases always need recursive; we do not drop caller-supplied * recursive bits for other formats here. */ - if ((options->output_format == DIFF_FORMAT_PATCH) || - (options->output_format == DIFF_FORMAT_DIFFSTAT) || - (options->output_format == DIFF_FORMAT_CHECKDIFF)) + if (options->output_format & (DIFF_FORMAT_PATCH | + DIFF_FORMAT_DIFFSTAT | + DIFF_FORMAT_CHECKDIFF)) options->recursive = 1; - /* - * These combinations do not make sense. + * Also pickaxe would not work very well if you do not say recursive */ - if (options->output_format == DIFF_FORMAT_RAW) - options->with_raw = 0; - if (options->output_format == DIFF_FORMAT_DIFFSTAT) - options->with_stat = 0; + if (options->pickaxe) + options->recursive = 1; if (options->detect_rename && options->rename_limit < 0) options->rename_limit = diff_rename_limit_default; @@ -1332,7 +1563,7 @@ int diff_setup_done(struct diff_options *options) return 0; } -int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) +static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) { char c, *eq; int len; @@ -1383,22 +1614,22 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) { const char *arg = av[0]; if (!strcmp(arg, "-p") || !strcmp(arg, "-u")) - options->output_format = DIFF_FORMAT_PATCH; + options->output_format |= DIFF_FORMAT_PATCH; else if (opt_arg(arg, 'U', "unified", &options->context)) - options->output_format = DIFF_FORMAT_PATCH; + options->output_format |= DIFF_FORMAT_PATCH; + else if (!strcmp(arg, "--raw")) + options->output_format |= DIFF_FORMAT_RAW; else if (!strcmp(arg, "--patch-with-raw")) { - options->output_format = DIFF_FORMAT_PATCH; - options->with_raw = 1; + options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW; } else if (!strcmp(arg, "--stat")) - options->output_format = DIFF_FORMAT_DIFFSTAT; + options->output_format |= DIFF_FORMAT_DIFFSTAT; else if (!strcmp(arg, "--check")) - options->output_format = DIFF_FORMAT_CHECKDIFF; + options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) - options->summary = 1; + options->output_format |= DIFF_FORMAT_SUMMARY; else if (!strcmp(arg, "--patch-with-stat")) { - options->output_format = DIFF_FORMAT_PATCH; - options->with_stat = 1; + options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT; } else if (!strcmp(arg, "-z")) options->line_termination = 0; @@ -1407,19 +1638,23 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--full-index")) options->full_index = 1; else if (!strcmp(arg, "--binary")) { - options->output_format = DIFF_FORMAT_PATCH; + options->output_format |= DIFF_FORMAT_PATCH; options->full_index = options->binary = 1; } + else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) { + options->text = 1; + } else if (!strcmp(arg, "--name-only")) - options->output_format = DIFF_FORMAT_NAME; + options->output_format |= DIFF_FORMAT_NAME; else if (!strcmp(arg, "--name-status")) - options->output_format = DIFF_FORMAT_NAME_STATUS; + options->output_format |= DIFF_FORMAT_NAME_STATUS; else if (!strcmp(arg, "-R")) options->reverse_diff = 1; else if (!strncmp(arg, "-S", 2)) options->pickaxe = arg + 2; - else if (!strcmp(arg, "-s")) - options->output_format = DIFF_FORMAT_NO_OUTPUT; + else if (!strcmp(arg, "-s")) { + options->output_format |= DIFF_FORMAT_NO_OUTPUT; + } else if (!strncmp(arg, "-O", 2)) options->orderfile = arg + 2; else if (!strncmp(arg, "--diff-filter=", 14)) @@ -1456,6 +1691,16 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (40 < options->abbrev) options->abbrev = 40; } + else if (!strcmp(arg, "--color")) + options->color_diff = 1; + else if (!strcmp(arg, "--no-color")) + options->color_diff = 0; + else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) + options->xdl_opts |= XDF_IGNORE_WHITESPACE; + else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) + options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + else if (!strcmp(arg, "--no-renames")) + options->detect_rename = 0; else return 0; return 1; @@ -1588,15 +1833,17 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len) } static void diff_flush_raw(struct diff_filepair *p, - int line_termination, - int inter_name_termination, - struct diff_options *options, - int output_format) + struct diff_options *options) { int two_paths; char status[10]; int abbrev = options->abbrev; const char *path_one, *path_two; + int inter_name_termination = '\t'; + int line_termination = options->line_termination; + + if (!line_termination) + inter_name_termination = 0; path_one = p->one->path; path_two = p->two->path; @@ -1625,7 +1872,7 @@ static void diff_flush_raw(struct diff_filepair *p, two_paths = 0; break; } - if (output_format != DIFF_FORMAT_NAME_STATUS) { + if (!(options->output_format & DIFF_FORMAT_NAME_STATUS)) { printf(":%06o %06o %s ", p->one->mode, p->two->mode, diff_unique_abbrev(p->one->sha1, abbrev)); @@ -1642,16 +1889,12 @@ static void diff_flush_raw(struct diff_filepair *p, free((void*)path_two); } -static void diff_flush_name(struct diff_filepair *p, - int inter_name_termination, - int line_termination) +static void diff_flush_name(struct diff_filepair *p, int line_termination) { char *path = p->two->path; if (line_termination) path = quote_one(p->two->path); - else - path = p->two->path; printf("%s%c", path, line_termination); if (p->two->path != path) free(path); @@ -1838,50 +2081,30 @@ static void diff_resolve_rename_copy(void) diff_debug_queue("resolve-rename-copy done", q); } -static void flush_one_pair(struct diff_filepair *p, - int diff_output_format, - struct diff_options *options, - struct diffstat_t *diffstat) +static int check_pair_status(struct diff_filepair *p) { - int inter_name_termination = '\t'; - int line_termination = options->line_termination; - if (!line_termination) - inter_name_termination = 0; - switch (p->status) { case DIFF_STATUS_UNKNOWN: - break; + return 0; case 0: die("internal error in diff-resolve-rename-copy"); - break; default: - switch (diff_output_format) { - case DIFF_FORMAT_DIFFSTAT: - diff_flush_stat(p, options, diffstat); - break; - case DIFF_FORMAT_CHECKDIFF: - diff_flush_checkdiff(p, options); - break; - case DIFF_FORMAT_PATCH: - diff_flush_patch(p, options); - break; - case DIFF_FORMAT_RAW: - case DIFF_FORMAT_NAME_STATUS: - diff_flush_raw(p, line_termination, - inter_name_termination, - options, diff_output_format); - break; - case DIFF_FORMAT_NAME: - diff_flush_name(p, - inter_name_termination, - line_termination); - break; - case DIFF_FORMAT_NO_OUTPUT: - break; - } + return 1; } } +static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) +{ + int fmt = opt->output_format; + + if (fmt & DIFF_FORMAT_CHECKDIFF) + diff_flush_checkdiff(p, opt); + else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) + diff_flush_raw(p, opt); + else if (fmt & DIFF_FORMAT_NAME) + diff_flush_name(p, opt->line_termination); +} + static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs) { if (fs->mode) @@ -1961,58 +2184,235 @@ static void diff_summary(struct diff_filepair *p) } } -void diff_flush(struct diff_options *options) +struct patch_id_t { + struct xdiff_emit_state xm; + SHA_CTX *ctx; + int patchlen; +}; + +static int remove_space(char *line, int len) +{ + int i; + char *dst = line; + unsigned char c; + + for (i = 0; i < len; i++) + if (!isspace((c = line[i]))) + *dst++ = c; + + return dst - line; +} + +static void patch_id_consume(void *priv, char *line, unsigned long len) +{ + struct patch_id_t *data = priv; + int new_len; + + /* Ignore line numbers when computing the SHA1 of the patch */ + if (!strncmp(line, "@@ -", 4)) + return; + + new_len = remove_space(line, len); + + SHA1_Update(data->ctx, line, new_len); + data->patchlen += new_len; +} + +/* returns 0 upon success, and writes result into sha1 */ +static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1) +{ + struct diff_queue_struct *q = &diff_queued_diff; + int i; + SHA_CTX ctx; + struct patch_id_t data; + char buffer[PATH_MAX * 4 + 20]; + + SHA1_Init(&ctx); + memset(&data, 0, sizeof(struct patch_id_t)); + data.ctx = &ctx; + data.xm.consume = patch_id_consume; + + for (i = 0; i < q->nr; i++) { + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + mmfile_t mf1, mf2; + struct diff_filepair *p = q->queue[i]; + int len1, len2; + + if (p->status == 0) + return error("internal diff status error"); + if (p->status == DIFF_STATUS_UNKNOWN) + continue; + if (diff_unmodified_pair(p)) + continue; + if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || + (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) + continue; + if (DIFF_PAIR_UNMERGED(p)) + continue; + + diff_fill_sha1_info(p->one); + diff_fill_sha1_info(p->two); + if (fill_mmfile(&mf1, p->one) < 0 || + fill_mmfile(&mf2, p->two) < 0) + return error("unable to read files to diff"); + + /* Maybe hash p->two? into the patch id? */ + if (mmfile_is_binary(&mf2)) + continue; + + len1 = remove_space(p->one->path, strlen(p->one->path)); + len2 = remove_space(p->two->path, strlen(p->two->path)); + if (p->one->mode == 0) + len1 = snprintf(buffer, sizeof(buffer), + "diff--gita/%.*sb/%.*s" + "newfilemode%06o" + "---/dev/null" + "+++b/%.*s", + len1, p->one->path, + len2, p->two->path, + p->two->mode, + len2, p->two->path); + else if (p->two->mode == 0) + len1 = snprintf(buffer, sizeof(buffer), + "diff--gita/%.*sb/%.*s" + "deletedfilemode%06o" + "---a/%.*s" + "+++/dev/null", + len1, p->one->path, + len2, p->two->path, + p->one->mode, + len1, p->one->path); + else + len1 = snprintf(buffer, sizeof(buffer), + "diff--gita/%.*sb/%.*s" + "---a/%.*s" + "+++b/%.*s", + len1, p->one->path, + len2, p->two->path, + len1, p->one->path, + len2, p->two->path); + SHA1_Update(&ctx, buffer, len1); + + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 3; + xecfg.flags = XDL_EMIT_FUNCNAMES; + ecb.outf = xdiff_outf; + ecb.priv = &data; + xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + } + + SHA1_Final(sha1, &ctx); + return 0; +} + +int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1) { struct diff_queue_struct *q = &diff_queued_diff; int i; - int diff_output_format = options->output_format; - struct diffstat_t *diffstat = NULL; + int result = diff_get_patch_id(options, sha1); + + for (i = 0; i < q->nr; i++) + diff_free_filepair(q->queue[i]); + + free(q->queue); + q->queue = NULL; + q->nr = q->alloc = 0; + + return result; +} + +static int is_summary_empty(const struct diff_queue_struct *q) +{ + int i; + + for (i = 0; i < q->nr; i++) { + const struct diff_filepair *p = q->queue[i]; - if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) { - diffstat = xcalloc(sizeof (struct diffstat_t), 1); - diffstat->xm.consume = diffstat_consume; + switch (p->status) { + case DIFF_STATUS_DELETED: + case DIFF_STATUS_ADDED: + case DIFF_STATUS_COPIED: + case DIFF_STATUS_RENAMED: + return 0; + default: + if (p->score) + return 0; + if (p->one->mode && p->two->mode && + p->one->mode != p->two->mode) + return 0; + break; + } } + return 1; +} + +void diff_flush(struct diff_options *options) +{ + struct diff_queue_struct *q = &diff_queued_diff; + int i, output_format = options->output_format; + int separator = 0; + + /* + * Order: raw, stat, summary, patch + * or: name/name-status/checkdiff (other bits clear) + */ + if (!q->nr) + goto free_queue; - if (options->with_raw) { + if (output_format & (DIFF_FORMAT_RAW | + DIFF_FORMAT_NAME | + DIFF_FORMAT_NAME_STATUS | + DIFF_FORMAT_CHECKDIFF)) { for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL); + if (check_pair_status(p)) + flush_one_pair(p, options); } - putchar(options->line_termination); + separator++; } - if (options->with_stat) { + + if (output_format & DIFF_FORMAT_DIFFSTAT) { + struct diffstat_t diffstat; + + memset(&diffstat, 0, sizeof(struct diffstat_t)); + diffstat.xm.consume = diffstat_consume; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options, - diffstat); + if (check_pair_status(p)) + diff_flush_stat(p, options, &diffstat); } - show_stats(diffstat); - free(diffstat); - diffstat = NULL; - if (options->summary) - for (i = 0; i < q->nr; i++) - diff_summary(q->queue[i]); - if (options->stat_sep) - fputs(options->stat_sep, stdout); - else - putchar(options->line_termination); - } - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - flush_one_pair(p, diff_output_format, options, diffstat); + show_stats(&diffstat); + separator++; } - if (diffstat) { - show_stats(diffstat); - free(diffstat); + if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) { + for (i = 0; i < q->nr; i++) + diff_summary(q->queue[i]); + separator++; } - for (i = 0; i < q->nr; i++) { - if (diffstat && options->summary) - diff_summary(q->queue[i]); - diff_free_filepair(q->queue[i]); + if (output_format & DIFF_FORMAT_PATCH) { + if (separator) { + if (options->stat_sep) { + /* attach patch instead of inline */ + fputs(options->stat_sep, stdout); + } else { + putchar(options->line_termination); + } + } + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + if (check_pair_status(p)) + diff_flush_patch(p, options); + } } + for (i = 0; i < q->nr; i++) + diff_free_filepair(q->queue[i]); +free_queue: free(q->queue); q->queue = NULL; q->nr = q->alloc = 0;