X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=diff.c;h=0391e8c42336cda9c83eeb1b9ac8f1378e4a43f9;hb=1b1e59c5084ac8b0af0aa443bb1713179d400211;hp=9bded28729d6608f0449c483707f35279c24b1d5;hpb=946f5f7c245a0f9398417be5946795cfebfd1c62;p=git.git diff --git a/diff.c b/diff.c index 9bded287..0391e8c4 100644 --- a/diff.c +++ b/diff.c @@ -10,10 +10,51 @@ #include "diffcore.h" static const char *diff_opts = "-pu"; -static unsigned char null_sha1[20] = { 0, }; static int use_size_cache; +int diff_rename_limit_default = -1; + +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; @@ -134,55 +175,52 @@ static void builtin_diff(const char *name_a, int complete_rewrite) { 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 *const diff_cmd = "diff -L%s -L%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]; + const char *label_path[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. + /* diff_cmd and diff_arg have 4 %s in total which makes + * the sum of these strings 8 bytes larger than required. * we use 2 spaces around diff-opts, and we need to count - * terminating NUL, so we subtract 9 here. + * terminating NUL; we used to subtract 5 here, but we do not + * care about small leaks in this subprocess that is about + * to exec "diff" anymore. */ - cmd_size = (strlen(diff_cmd) + strlen(diff_opts) + - strlen(diff_arg) - 9); + cmd_size = (strlen(diff_cmd) + strlen(diff_opts) + strlen(diff_arg) + + 128); + 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 (!strcmp(temp[i].name, "/dev/null")) + label_path[i] = "/dev/null"; + else if (!i) + label_path[i] = sq_quote(quote_two("a/", name_a)); + else + label_path[i] = sq_quote(quote_two("b/", name_b)); + cmd_size += (strlen(label_path[i]) + strlen(input_name_sq[i])); } cmd = xmalloc(cmd_size); next_at = 0; next_at += snprintf(cmd+next_at, cmd_size-next_at, - diff_cmd, - path0[0], path1[0], path0[1], path1[1]); + diff_cmd, label_path[0], label_path[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]); - printf("diff --git a/%s b/%s\n", name_a, name_b); - if (!path1[0][0]) { + printf("diff --git %s %s\n", + quote_two("a/", name_a), quote_two("b/", name_b)); + if (label_path[0][0] == '/') { + /* dev/null */ printf("new file mode %s\n", temp[1].mode); if (xfrm_msg && xfrm_msg[0]) puts(xfrm_msg); } - else if (!path1[1][0]) { + else if (label_path[1][0] == '/') { printf("deleted file mode %s\n", temp[0].mode); if (xfrm_msg && xfrm_msg[0]) puts(xfrm_msg); @@ -414,7 +452,7 @@ void diff_free_filespec_data(struct diff_filespec *s) static void prep_temp_blob(struct diff_tempfile *temp, void *blob, unsigned long size, - unsigned char *sha1, + const unsigned char *sha1, int mode) { int fd; @@ -597,15 +635,32 @@ static void run_external_diff(const char *pgm, remove_tempfile(); } -static void run_diff(struct diff_filepair *p) +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) + die("stat %s", one->path); + if (index_path(one->sha1, one->path, &st, 0)) + die("cannot hash %s\n", one->path); + } + } + else + memset(one->sha1, 0, 20); +} + +static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); - char msg_[PATH_MAX*2+200], *xfrm_msg; + char msg[PATH_MAX*2+300], *xfrm_msg; struct diff_filespec *one; 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 */ @@ -616,40 +671,63 @@ static void run_diff(struct diff_filepair *p) 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); + diff_fill_sha1_info(two); + + len = 0; switch (p->status) { case DIFF_STATUS_COPIED: - sprintf(msg_, - "similarity index %d%%\n" - "copy from %s\n" - "copy to %s", - (int)(0.5 + p->score * 100.0/MAX_SCORE), - name, other); - xfrm_msg = msg_; + len += snprintf(msg + len, sizeof(msg) - len, + "similarity index %d%%\n" + "copy from %s\n" + "copy to %s\n", + (int)(0.5 + p->score * 100.0/MAX_SCORE), + name_munged, other_munged); break; case DIFF_STATUS_RENAMED: - sprintf(msg_, - "similarity index %d%%\n" - "rename from %s\n" - "rename to %s", - (int)(0.5 + p->score * 100.0/MAX_SCORE), - name, other); - xfrm_msg = msg_; + len += snprintf(msg + len, sizeof(msg) - len, + "similarity index %d%%\n" + "rename from %s\n" + "rename to %s\n", + (int)(0.5 + p->score * 100.0/MAX_SCORE), + name_munged, other_munged); break; case DIFF_STATUS_MODIFIED: if (p->score) { - sprintf(msg_, - "dissimilarity index %d%%", - (int)(0.5 + p->score * 100.0/MAX_SCORE)); - xfrm_msg = msg_; + len += snprintf(msg + len, sizeof(msg) - len, + "dissimilarity index %d%%\n", + (int)(0.5 + p->score * + 100.0/MAX_SCORE)); complete_rewrite = 1; break; } /* fallthru */ default: - xfrm_msg = NULL; + /* nothing */ + ; } + if (memcmp(one->sha1, two->sha1, 20)) { + char one_sha1[41]; + const char *index_fmt = o->full_index ? "index %s..%s" : "index %.7s..%.7s"; + memcpy(one_sha1, sha1_to_hex(one->sha1), 41); + + len += snprintf(msg + len, sizeof(msg) - len, + index_fmt, one_sha1, sha1_to_hex(two->sha1)); + if (one->mode == two->mode) + len += snprintf(msg + len, sizeof(msg) - len, + " %06o", one->mode); + len += snprintf(msg + len, sizeof(msg) - len, "\n"); + } + + if (len) + msg[--len] = 0; + xfrm_msg = len ? msg : NULL; + if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && (S_IFMT & one->mode) != (S_IFMT & two->mode)) { @@ -666,6 +744,9 @@ static void run_diff(struct diff_filepair *p) else run_external_diff(pgm, name, other, one, two, xfrm_msg, complete_rewrite); + + free(name_munged); + free(other_munged); } void diff_setup(struct diff_options *options) @@ -675,13 +756,19 @@ 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 @@ -707,6 +794,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")) @@ -842,16 +931,13 @@ static void diff_flush_raw(struct diff_filepair *p, { int two_paths; char status[10]; + const char *path_one, *path_two; + 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) @@ -879,16 +965,29 @@ static void diff_flush_raw(struct diff_filepair *p, p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1)); printf("%s ", sha1_to_hex(p->two->sha1)); } - 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) @@ -925,7 +1024,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; @@ -934,7 +1033,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) @@ -1066,7 +1165,7 @@ 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: @@ -1075,7 +1174,9 @@ void diff_flush(struct diff_options *options) diff_output_format); 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]);