X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=diff.c;h=c0548eed98df0fb6c11b04a775b6b64b3bb4d417;hb=be1295d16a2593dcf468fef7d9e811d057d9039f;hp=2e0797bf3ee651a644be50aee10ee125fe96368f;hpb=069b20a198f171512a1d2d2163b40f70c94f5257;p=git.git diff --git a/diff.c b/diff.c index 2e0797bf..c0548eed 100644 --- a/diff.c +++ b/diff.c @@ -178,11 +178,12 @@ static void emit_rewrite_diff(const char *name_a, copy_file('+', temp[1].name); } -static void builtin_diff(const char *name_a, +static const char *builtin_diff(const char *name_a, const char *name_b, struct diff_tempfile *temp, const char *xfrm_msg, - int complete_rewrite) + int complete_rewrite, + const char **args) { int i, next_at, cmd_size; const char *const diff_cmd = "diff -L%s -L%s"; @@ -242,19 +243,24 @@ static void builtin_diff(const char *name_a, } if (xfrm_msg && xfrm_msg[0]) puts(xfrm_msg); + /* + * we do not run diff between different kind + * of objects. + */ if (strncmp(temp[0].mode, temp[1].mode, 3)) - /* we do not run diff between different kind - * of objects. - */ - exit(0); + return NULL; if (complete_rewrite) { - fflush(NULL); emit_rewrite_diff(name_a, name_b, temp); - exit(0); + return NULL; } } - fflush(NULL); - execlp("/bin/sh","sh", "-c", cmd, NULL); + + /* This is disgusting */ + *args++ = "sh"; + *args++ = "-c"; + *args++ = cmd; + *args = NULL; + return "/bin/sh"; } struct diff_filespec *alloc_filespec(const char *path) @@ -311,7 +317,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, @@ -504,9 +510,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); @@ -555,6 +561,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: @@ -571,9 +613,9 @@ 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; @@ -590,59 +632,41 @@ 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); + if (pgm) { + const char **arg = &spawn_arg[0]; + 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; } - else - execlp(pgm, pgm, name, NULL); + } else { + *arg++ = pgm; + *arg++ = name; } - /* - * otherwise we use the built-in one. - */ - if (one && two) - builtin_diff(name, othername, temp, xfrm_msg, - complete_rewrite); - else + *arg = NULL; + } else { + if (one && two) { + pgm = builtin_diff(name, othername, temp, xfrm_msg, complete_rewrite, spawn_arg); + } else printf("* Unmerged path %s\n", name); - exit(0); } - 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(); + + retval = 0; + if (pgm) + 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 diff_fill_sha1_info(struct diff_filespec *one) @@ -650,7 +674,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); @@ -723,11 +747,13 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (memcmp(one->sha1, two->sha1, 20)) { char one_sha1[41]; - const char *index_fmt = o->full_index ? "index %s..%s" : "index %.7s..%.7s"; + 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_fmt, one_sha1, sha1_to_hex(two->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, " %06o", one->mode); @@ -785,12 +811,14 @@ int diff_setup_done(struct diff_options *options) * 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; } @@ -841,6 +869,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; @@ -947,14 +984,41 @@ 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; @@ -985,8 +1049,10 @@ 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, path_one); if (two_paths) @@ -1194,7 +1260,7 @@ void diff_flush(struct diff_options *options) 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,