X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=diff.c;h=e496905bad9853f912e1bbcc081c544524f8cb77;hb=fc9957b0052df6a8248420395bc9febd66194252;hp=cbb86320a6b98ea57712a78c558c7dc84613087b;hpb=f51248eb482a7a0feacb04d02119c94d35845975;p=git.git diff --git a/diff.c b/diff.c index cbb86320..e496905b 100644 --- a/diff.c +++ b/diff.c @@ -8,34 +8,70 @@ #include "quote.h" #include "diff.h" #include "diffcore.h" - -static const char *diff_opts = "-pu"; +#include "xdiff/xdiff.h" static int use_size_cache; +int diff_rename_limit_default = -1; + +int git_diff_config(const char *var, const char *value) +{ + if (!strcmp(var, "diff.renamelimit")) { + diff_rename_limit_default = git_config_int(var, value); + return 0; + } + + return git_default_config(var, value); +} + +static char *quote_one(const char *str) +{ + int needlen; + char *xp; + + if (!str) + return NULL; + needlen = quote_c_style(str, NULL, NULL, 0); + if (!needlen) + return strdup(str); + xp = xmalloc(needlen + 1); + quote_c_style(str, xp, NULL, 0); + return xp; +} + +static char *quote_two(const char *one, const char *two) +{ + int need_one = quote_c_style(one, NULL, NULL, 1); + int need_two = quote_c_style(two, NULL, NULL, 1); + char *xp; + + if (need_one + need_two) { + if (!need_one) need_one = strlen(one); + if (!need_two) need_one = strlen(two); + + xp = xmalloc(need_one + need_two + 3); + xp[0] = '"'; + quote_c_style(one, xp + 1, NULL, 1); + quote_c_style(two, xp + need_one + 1, NULL, 1); + strcpy(xp + need_one + need_two + 1, "\""); + return xp; + } + need_one = strlen(one); + need_two = strlen(two); + xp = xmalloc(need_one + need_two + 1); + strcpy(xp, one); + strcpy(xp + need_one, two); + return xp; +} + static const char *external_diff(void) { static const char *external_diff_cmd = NULL; static int done_preparing = 0; - const char *env_diff_opts; if (done_preparing) return external_diff_cmd; - - /* - * Default values above are meant to match the - * Linux kernel development style. Examples of - * alternative styles you can specify via environment - * variables are: - * - * GIT_DIFF_OPTS="-c"; - */ external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); - - /* In case external diff fails... */ - env_diff_opts = getenv("GIT_DIFF_OPTS"); - if (env_diff_opts) diff_opts = env_diff_opts; - done_preparing = 1; return external_diff_cmd; } @@ -49,13 +85,12 @@ static struct diff_tempfile { char tmp_path[TEMPFILE_PATH_LEN]; } diff_temp[2]; -static int count_lines(const char *filename) +static int count_lines(const char *data, int size) { - FILE *in; int count, ch, completely_empty = 1, nl_just_seen = 0; - in = fopen(filename, "r"); count = 0; - while ((ch = fgetc(in)) != EOF) + while (0 < size--) { + ch = *data++; if (ch == '\n') { count++; nl_just_seen = 1; @@ -65,7 +100,7 @@ static int count_lines(const char *filename) nl_just_seen = 0; completely_empty = 0; } - fclose(in); + } if (completely_empty) return 0; if (!nl_just_seen) @@ -88,12 +123,11 @@ static void print_line_count(int count) } } -static void copy_file(int prefix, const char *filename) +static void copy_file(int prefix, const char *data, int size) { - FILE *in; int ch, nl_just_seen = 1; - in = fopen(filename, "r"); - while ((ch = fgetc(in)) != EOF) { + while (0 < size--) { + ch = *data++; if (nl_just_seen) putchar(prefix); putchar(ch); @@ -102,110 +136,153 @@ static void copy_file(int prefix, const char *filename) else nl_just_seen = 0; } - fclose(in); if (!nl_just_seen) printf("\n\\ No newline at end of file\n"); } static void emit_rewrite_diff(const char *name_a, const char *name_b, - struct diff_tempfile *temp) + struct diff_filespec *one, + struct diff_filespec *two) { /* Use temp[i].name as input, name_a and name_b as labels */ int lc_a, lc_b; - lc_a = count_lines(temp[0].name); - lc_b = count_lines(temp[1].name); + lc_a = count_lines(one->data, one->size); + lc_b = count_lines(two->data, two->size); printf("--- %s\n+++ %s\n@@ -", name_a, name_b); print_line_count(lc_a); printf(" +"); print_line_count(lc_b); printf(" @@\n"); if (lc_a) - copy_file('-', temp[0].name); + copy_file('-', one->data, one->size); if (lc_b) - copy_file('+', temp[1].name); + copy_file('+', two->data, two->size); } -static void builtin_diff(const char *name_a, - const char *name_b, - struct diff_tempfile *temp, - const char *xfrm_msg, - int complete_rewrite) +static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) { - int i, next_at, cmd_size; - const char *const diff_cmd = "diff -L%s%s -L%s%s"; - const char *const diff_arg = "%s %s||:"; /* "||:" is to return 0 */ - const char *input_name_sq[2]; - const char *path0[2]; - const char *path1[2]; - const char *name_sq[2]; - char *cmd; - - name_sq[0] = sq_quote(name_a); - name_sq[1] = sq_quote(name_b); - - /* diff_cmd and diff_arg have 6 %s in total which makes - * the sum of these strings 12 bytes larger than required. - * we use 2 spaces around diff-opts, and we need to count - * terminating NUL, so we subtract 9 here. - */ - cmd_size = (strlen(diff_cmd) + strlen(diff_opts) + - strlen(diff_arg) - 9); - for (i = 0; i < 2; i++) { - input_name_sq[i] = sq_quote(temp[i].name); - if (!strcmp(temp[i].name, "/dev/null")) { - path0[i] = "/dev/null"; - path1[i] = ""; - } else { - path0[i] = i ? "b/" : "a/"; - path1[i] = name_sq[i]; - } - cmd_size += (strlen(path0[i]) + strlen(path1[i]) + - strlen(input_name_sq[i])); + if (!DIFF_FILE_VALID(one)) { + mf->ptr = ""; /* does not matter */ + mf->size = 0; + return 0; } + else if (diff_populate_filespec(one, 0)) + return -1; + mf->ptr = one->data; + mf->size = one->size; + return 0; +} - cmd = xmalloc(cmd_size); +struct emit_callback { + const char **label_path; +}; - next_at = 0; - next_at += snprintf(cmd+next_at, cmd_size-next_at, - diff_cmd, - path0[0], path1[0], path0[1], path1[1]); - next_at += snprintf(cmd+next_at, cmd_size-next_at, - " %s ", diff_opts); - next_at += snprintf(cmd+next_at, cmd_size-next_at, - diff_arg, input_name_sq[0], input_name_sq[1]); +static int fn_out(void *priv, mmbuffer_t *mb, int nbuf) +{ + int i; + struct emit_callback *ecbdata = priv; - printf("diff --git a/%s b/%s\n", name_a, name_b); - if (!path1[0][0]) { - printf("new file mode %s\n", temp[1].mode); + if (ecbdata->label_path[0]) { + printf("--- %s\n", ecbdata->label_path[0]); + printf("+++ %s\n", ecbdata->label_path[1]); + 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; +} + +#define FIRST_FEW_BYTES 8000 +static int mmfile_is_binary(mmfile_t *mf) +{ + long sz = mf->size; + if (FIRST_FEW_BYTES < sz) + sz = FIRST_FEW_BYTES; + if (memchr(mf->ptr, 0, sz)) + return 1; + return 0; +} + +static void builtin_diff(const char *name_a, + const char *name_b, + struct diff_filespec *one, + struct diff_filespec *two, + const char *xfrm_msg, + int complete_rewrite) +{ + mmfile_t mf1, mf2; + const char *lbl[2]; + char *a_one, *b_two; + + 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); + if (lbl[0][0] == '/') { + /* /dev/null */ + printf("new file mode %06o\n", two->mode); if (xfrm_msg && xfrm_msg[0]) puts(xfrm_msg); } - else if (!path1[1][0]) { - printf("deleted file mode %s\n", temp[0].mode); + else if (lbl[1][0] == '/') { + printf("deleted file mode %06o\n", one->mode); if (xfrm_msg && xfrm_msg[0]) puts(xfrm_msg); } else { - if (strcmp(temp[0].mode, temp[1].mode)) { - printf("old mode %s\n", temp[0].mode); - printf("new mode %s\n", temp[1].mode); + if (one->mode != two->mode) { + printf("old mode %06o\n", one->mode); + printf("new mode %06o\n", two->mode); } if (xfrm_msg && xfrm_msg[0]) puts(xfrm_msg); - if (strncmp(temp[0].mode, temp[1].mode, 3)) - /* we do not run diff between different kind - * of objects. - */ - exit(0); + /* + * we do not run diff between different kind + * of objects. + */ + if ((one->mode ^ two->mode) & S_IFMT) + goto free_ab_and_return; if (complete_rewrite) { - fflush(NULL); - emit_rewrite_diff(name_a, name_b, temp); - exit(0); + emit_rewrite_diff(name_a, name_b, one, two); + goto free_ab_and_return; } } - fflush(NULL); - execlp("/bin/sh","sh", "-c", cmd, NULL); + + 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)) + printf("Binary files %s and %s differ\n", lbl[0], lbl[1]); + else { + /* Crazy xdl interfaces.. */ + const char *diffopts = getenv("GIT_DIFF_OPTS"); + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + struct emit_callback ecbdata; + + ecbdata.label_path = lbl; + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 3; + xecfg.flags = XDL_EMIT_FUNCNAMES; + if (!diffopts) + ; + else if (!strncmp(diffopts, "--unified=", 10)) + 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.priv = &ecbdata; + xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + } + + free_ab_and_return: + free(a_one); + free(b_two); + return; } struct diff_filespec *alloc_filespec(const char *path) @@ -223,7 +300,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, unsigned short mode) { if (mode) { - spec->mode = DIFF_FILE_CANON_MODE(mode); + spec->mode = canon_mode(mode); memcpy(spec->sha1, sha1, 20); spec->sha1_valid = !!memcmp(sha1, null_sha1, 20); } @@ -262,7 +339,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1) ce = active_cache[pos]; if ((lstat(name, &st) < 0) || !S_ISREG(st.st_mode) || /* careful! */ - ce_match_stat(ce, &st) || + ce_match_stat(ce, &st, 0) || memcmp(sha1, ce->sha1, 20)) return 0; /* we return 1 only when we can stat, it is a regular file, @@ -408,6 +485,8 @@ void diff_free_filespec_data(struct diff_filespec *s) munmap(s->data, s->size); s->should_free = s->should_munmap = 0; s->data = NULL; + free(s->cnt_data); + s->cnt_data = NULL; } static void prep_temp_blob(struct diff_tempfile *temp, @@ -455,9 +534,9 @@ static void prepare_temp_file(const char *name, } if (S_ISLNK(st.st_mode)) { int ret; - char *buf, buf_[1024]; - buf = ((sizeof(buf_) < st.st_size) ? - xmalloc(st.st_size) : buf_); + char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */ + if (sizeof(buf) <= st.st_size) + die("symlink too long: %s", name); ret = readlink(name, buf, st.st_size); if (ret < 0) die("readlink(%s)", name); @@ -506,6 +585,42 @@ static void remove_tempfile(void) static void remove_tempfile_on_signal(int signo) { remove_tempfile(); + signal(SIGINT, SIG_DFL); + raise(signo); +} + +static int spawn_prog(const char *pgm, const char **arg) +{ + pid_t pid; + int status; + + fflush(NULL); + pid = fork(); + if (pid < 0) + die("unable to fork"); + if (!pid) { + execvp(pgm, (char *const*) arg); + exit(255); + } + + while (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) + continue; + return -1; + } + + /* Earlier we did not check the exit status because + * diff exits non-zero if files are different, and + * we are not interested in knowing that. It was a + * mistake which made it harder to quit a diff-* + * session that uses the git-apply-patch-script as + * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF + * should also exit non-zero only when it wants to + * abort the entire diff-* session. + */ + if (WIFEXITED(status) && !WEXITSTATUS(status)) + return 0; + return -1; } /* An external diff command takes: @@ -522,11 +637,12 @@ static void run_external_diff(const char *pgm, const char *xfrm_msg, int complete_rewrite) { + const char *spawn_arg[10]; struct diff_tempfile *temp = diff_temp; - pid_t pid; - int status; + int retval; static int atexit_asked = 0; const char *othername; + const char **arg = &spawn_arg[0]; othername = (other? other : name); if (one && two) { @@ -541,59 +657,50 @@ static void run_external_diff(const char *pgm, signal(SIGINT, remove_tempfile_on_signal); } - fflush(NULL); - pid = fork(); - if (pid < 0) - die("unable to fork"); - if (!pid) { - if (pgm) { - if (one && two) { - const char *exec_arg[10]; - const char **arg = &exec_arg[0]; - *arg++ = pgm; - *arg++ = name; - *arg++ = temp[0].name; - *arg++ = temp[0].hex; - *arg++ = temp[0].mode; - *arg++ = temp[1].name; - *arg++ = temp[1].hex; - *arg++ = temp[1].mode; - if (other) { - *arg++ = other; - *arg++ = xfrm_msg; - } - *arg = NULL; - execvp(pgm, (char *const*) exec_arg); - } - else - execlp(pgm, pgm, name, NULL); + if (one && two) { + *arg++ = pgm; + *arg++ = name; + *arg++ = temp[0].name; + *arg++ = temp[0].hex; + *arg++ = temp[0].mode; + *arg++ = temp[1].name; + *arg++ = temp[1].hex; + *arg++ = temp[1].mode; + if (other) { + *arg++ = other; + *arg++ = xfrm_msg; } - /* - * otherwise we use the built-in one. - */ - if (one && two) - builtin_diff(name, othername, temp, xfrm_msg, - complete_rewrite); - else - printf("* Unmerged path %s\n", name); - exit(0); + } else { + *arg++ = pgm; + *arg++ = name; } - if (waitpid(pid, &status, 0) < 0 || - !WIFEXITED(status) || WEXITSTATUS(status)) { - /* Earlier we did not check the exit status because - * diff exits non-zero if files are different, and - * we are not interested in knowing that. It was a - * mistake which made it harder to quit a diff-* - * session that uses the git-apply-patch-script as - * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF - * should also exit non-zero only when it wants to - * abort the entire diff-* session. - */ - remove_tempfile(); + *arg = NULL; + retval = spawn_prog(pgm, spawn_arg); + remove_tempfile(); + if (retval) { fprintf(stderr, "external diff died, stopping at %s.\n", name); exit(1); } - remove_tempfile(); +} + +static void run_diff_cmd(const char *pgm, + const char *name, + const char *other, + struct diff_filespec *one, + struct diff_filespec *two, + const char *xfrm_msg, + int complete_rewrite) +{ + if (pgm) { + run_external_diff(pgm, name, other, one, two, xfrm_msg, + complete_rewrite); + return; + } + if (one && two) + builtin_diff(name, other ? other : name, + one, two, xfrm_msg, complete_rewrite); + else + printf("* Unmerged path %s\n", name); } static void diff_fill_sha1_info(struct diff_filespec *one) @@ -601,7 +708,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one) if (DIFF_FILE_VALID(one)) { if (!one->sha1_valid) { struct stat st; - if (stat(one->path, &st) < 0) + if (lstat(one->path, &st) < 0) die("stat %s", one->path); if (index_path(one->sha1, one->path, &st, 0)) die("cannot hash %s\n", one->path); @@ -611,7 +718,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one) memset(one->sha1, 0, 20); } -static void run_diff(struct diff_filepair *p) +static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); char msg[PATH_MAX*2+300], *xfrm_msg; @@ -619,18 +726,20 @@ static void run_diff(struct diff_filepair *p) struct diff_filespec *two; const char *name; const char *other; + char *name_munged, *other_munged; int complete_rewrite = 0; int len; if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ - run_external_diff(pgm, p->one->path, NULL, NULL, NULL, NULL, - 0); + run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0); return; } name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); + name_munged = quote_one(name); + other_munged = quote_one(other); one = p->one; two = p->two; diff_fill_sha1_info(one); @@ -644,7 +753,7 @@ static void run_diff(struct diff_filepair *p) "copy from %s\n" "copy to %s\n", (int)(0.5 + p->score * 100.0/MAX_SCORE), - name, other); + name_munged, other_munged); break; case DIFF_STATUS_RENAMED: len += snprintf(msg + len, sizeof(msg) - len, @@ -652,7 +761,7 @@ static void run_diff(struct diff_filepair *p) "rename from %s\n" "rename to %s\n", (int)(0.5 + p->score * 100.0/MAX_SCORE), - name, other); + name_munged, other_munged); break; case DIFF_STATUS_MODIFIED: if (p->score) { @@ -671,10 +780,12 @@ static void run_diff(struct diff_filepair *p) if (memcmp(one->sha1, two->sha1, 20)) { char one_sha1[41]; + int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; memcpy(one_sha1, sha1_to_hex(one->sha1), 41); len += snprintf(msg + len, sizeof(msg) - len, - "index %.7s..%.7s", one_sha1, + "index %.*s..%.*s", + abbrev, one_sha1, abbrev, sha1_to_hex(two->sha1)); if (one->mode == two->mode) len += snprintf(msg + len, sizeof(msg) - len, @@ -693,15 +804,18 @@ static void run_diff(struct diff_filepair *p) * needs to be split into deletion and creation. */ struct diff_filespec *null = alloc_filespec(two->path); - run_external_diff(NULL, name, other, one, null, xfrm_msg, 0); + run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0); free(null); null = alloc_filespec(one->path); - run_external_diff(NULL, name, other, null, two, xfrm_msg, 0); + run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0); free(null); } else - run_external_diff(pgm, name, other, one, two, xfrm_msg, - complete_rewrite); + run_diff_cmd(pgm, name, other, one, two, xfrm_msg, + complete_rewrite); + + free(name_munged); + free(other_munged); } void diff_setup(struct diff_options *options) @@ -711,25 +825,33 @@ void diff_setup(struct diff_options *options) options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; + + options->change = diff_change; + options->add_remove = diff_addremove; } int diff_setup_done(struct diff_options *options) { - if ((options->find_copies_harder || 0 <= options->rename_limit) && - options->detect_rename != DIFF_DETECT_COPY) + if ((options->find_copies_harder && + options->detect_rename != DIFF_DETECT_COPY) || + (0 <= options->rename_limit && !options->detect_rename)) return -1; + if (options->detect_rename && options->rename_limit < 0) + options->rename_limit = diff_rename_limit_default; if (options->setup & DIFF_SETUP_USE_CACHE) { if (!active_cache) /* read-cache does not die even when it fails * so it is safe for us to do this here. Also * it does not smudge active_cache or active_nr * when it fails, so we do not have to worry about - * cleaning it up oufselves either. + * cleaning it up ourselves either. */ read_cache(); } if (options->setup & DIFF_SETUP_USE_SIZE_CACHE) use_size_cache = 1; + if (options->abbrev <= 0 || 40 < options->abbrev) + options->abbrev = 40; /* full */ return 0; } @@ -743,6 +865,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->line_termination = 0; else if (!strncmp(arg, "-l", 2)) options->rename_limit = strtoul(arg+2, NULL, 10); + else if (!strcmp(arg, "--full-index")) + options->full_index = 1; else if (!strcmp(arg, "--name-only")) options->output_format = DIFF_FORMAT_NAME; else if (!strcmp(arg, "--name-status")) @@ -778,6 +902,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } else if (!strcmp(arg, "--find-copies-harder")) options->find_copies_harder = 1; + else if (!strcmp(arg, "--abbrev")) + options->abbrev = DEFAULT_ABBREV; + else if (!strncmp(arg, "--abbrev=", 9)) { + options->abbrev = strtoul(arg + 9, NULL, 10); + if (options->abbrev < MINIMUM_ABBREV) + options->abbrev = MINIMUM_ABBREV; + else if (40 < options->abbrev) + options->abbrev = 40; + } else return 0; return 1; @@ -785,16 +918,29 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) static int parse_num(const char **cp_p) { - int num, scale, ch, cnt; + unsigned long num, scale; + int ch, dot; const char *cp = *cp_p; - cnt = num = 0; + num = 0; scale = 1; - while ('0' <= (ch = *cp) && ch <= '9') { - if (cnt++ < 5) { - /* We simply ignore more than 5 digits precision. */ - scale *= 10; - num = num * 10 + ch - '0'; + dot = 0; + for(;;) { + ch = *cp; + if ( !dot && ch == '.' ) { + scale = 1; + dot = 1; + } else if ( ch == '%' ) { + scale = dot ? scale*100 : 100; + cp++; /* % is always at the end */ + break; + } else if ( ch >= '0' && ch <= '9' ) { + if ( scale < 100000 ) { + scale *= 10; + num = (num*10) + (ch-'0'); + } + } else { + break; } cp++; } @@ -803,7 +949,7 @@ static int parse_num(const char **cp_p) /* user says num divided by scale and we say internally that * is MAX_SCORE * num / scale. */ - return (MAX_SCORE * num / scale); + return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale); } int diff_scoreopt_parse(const char *opt) @@ -871,23 +1017,47 @@ void diff_free_filepair(struct diff_filepair *p) free(p); } +/* This is different from find_unique_abbrev() in that + * it stuffs the result with dots for alignment. + */ +const char *diff_unique_abbrev(const unsigned char *sha1, int len) +{ + int abblen; + const char *abbrev; + if (len == 40) + return sha1_to_hex(sha1); + + abbrev = find_unique_abbrev(sha1, len); + if (!abbrev) + return sha1_to_hex(sha1); + abblen = strlen(abbrev); + if (abblen < 37) { + static char hex[41]; + if (len < abblen && abblen <= len + 2) + sprintf(hex, "%s%.*s", abbrev, len+3-abblen, ".."); + else + sprintf(hex, "%s...", abbrev); + return hex; + } + return sha1_to_hex(sha1); +} + static void diff_flush_raw(struct diff_filepair *p, int line_termination, int inter_name_termination, - int output_format) + struct diff_options *options) { int two_paths; char status[10]; + int abbrev = options->abbrev; + const char *path_one, *path_two; + int output_format = options->output_format; + path_one = p->one->path; + path_two = p->two->path; if (line_termination) { - const char *const err = - "path %s cannot be expressed without -z"; - if (strchr(p->one->path, line_termination) || - strchr(p->one->path, inter_name_termination)) - die(err, p->one->path); - if (strchr(p->two->path, line_termination) || - strchr(p->two->path, inter_name_termination)) - die(err, p->two->path); + path_one = quote_one(path_one); + path_two = quote_one(path_two); } if (p->score) @@ -912,19 +1082,34 @@ static void diff_flush_raw(struct diff_filepair *p, } if (output_format != DIFF_FORMAT_NAME_STATUS) { printf(":%06o %06o %s ", - p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1)); - printf("%s ", sha1_to_hex(p->two->sha1)); + p->one->mode, p->two->mode, + diff_unique_abbrev(p->one->sha1, abbrev)); + printf("%s ", + diff_unique_abbrev(p->two->sha1, abbrev)); } - printf("%s%c%s",status, inter_name_termination, p->one->path); + printf("%s%c%s", status, inter_name_termination, path_one); if (two_paths) - printf("%c%s", inter_name_termination, p->two->path); + printf("%c%s", inter_name_termination, path_two); putchar(line_termination); + if (path_one != p->one->path) + free((void*)path_one); + if (path_two != p->two->path) + free((void*)path_two); } static void diff_flush_name(struct diff_filepair *p, + int inter_name_termination, int line_termination) { - printf("%s%c", p->two->path, 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); } int diff_unmodified_pair(struct diff_filepair *p) @@ -961,7 +1146,7 @@ int diff_unmodified_pair(struct diff_filepair *p) return 0; } -static void diff_flush_patch(struct diff_filepair *p) +static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o) { if (diff_unmodified_pair(p)) return; @@ -970,7 +1155,7 @@ static void diff_flush_patch(struct diff_filepair *p) (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) return; /* no tree diffs in patch format */ - run_diff(p); + run_diff(p, o); } int diff_queue_is_empty(void) @@ -1102,16 +1287,18 @@ void diff_flush(struct diff_options *options) die("internal error in diff-resolve-rename-copy"); switch (diff_output_format) { case DIFF_FORMAT_PATCH: - diff_flush_patch(p); + diff_flush_patch(p, options); break; case DIFF_FORMAT_RAW: case DIFF_FORMAT_NAME_STATUS: diff_flush_raw(p, line_termination, inter_name_termination, - diff_output_format); + options); break; case DIFF_FORMAT_NAME: - diff_flush_name(p, line_termination); + diff_flush_name(p, + inter_name_termination, + line_termination); break; } diff_free_filepair(q->queue[i]);