X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=diff.c;h=74004e5a3f9fa491b30ab3d5f231826593e4eae4;hb=b11645be0139f907488b17ca4267e593ee58dd30;hp=65110333f2a71b0f4ff13094b431380089b79ffe;hpb=c983370e5e208cca4deda82184478ba445a156e6;p=git.git diff --git a/diff.c b/diff.c index 65110333..74004e5a 100644 --- a/diff.c +++ b/diff.c @@ -4,14 +4,15 @@ #include #include #include +#include #include "cache.h" #include "diff.h" -static char *diff_opts = "-pu"; +static const char *diff_opts = "-pu"; static const char *external_diff(void) { - static char *external_diff_cmd = NULL; + static const char *external_diff_cmd = NULL; static int done_preparing = 0; if (done_preparing) @@ -25,11 +26,11 @@ static const char *external_diff(void) * * GIT_DIFF_OPTS="-c"; */ - if (getenv("GIT_EXTERNAL_DIFF")) - external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); + if (gitenv("GIT_EXTERNAL_DIFF")) + external_diff_cmd = gitenv("GIT_EXTERNAL_DIFF"); /* In case external diff fails... */ - diff_opts = getenv("GIT_DIFF_OPTS") ? : diff_opts; + diff_opts = gitenv("GIT_DIFF_OPTS") ? : diff_opts; done_preparing = 1; return external_diff_cmd; @@ -78,18 +79,22 @@ static struct diff_tempfile { char tmp_path[50]; } diff_temp[2]; -static void builtin_diff(const char *name, +static void builtin_diff(const char *name_a, + const char *name_b, struct diff_tempfile *temp) { int i, next_at; const char *diff_cmd = "diff -L'%s%s' -L'%s%s'"; - const char *diff_arg = "'%s' '%s'"; + const char *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 = sq_expand(name); + const char *name_sq[2]; char *cmd; - + + name_sq[0] = sq_expand(name_a); + name_sq[1] = sq_expand(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 @@ -103,8 +108,8 @@ static void builtin_diff(const char *name, path0[i] = "/dev/null"; path1[i] = ""; } else { - path0[i] = i ? "l/" : "k/"; - path1[i] = name_sq; + path0[i] = i ? "b/" : "a/"; + path1[i] = name_sq[i]; } cmd_size += (strlen(path0[i]) + strlen(path1[i]) + strlen(input_name_sq[i])); @@ -121,22 +126,96 @@ static void builtin_diff(const char *name, 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("Created: %s (mode:%s)\n", name, temp[1].mode); + printf("new file mode %s\n", temp[1].mode); else if (!path1[1][0]) - printf("Deleted: %s\n", name); - else if (strcmp(temp[0].mode, temp[1].mode)) - printf("Mode changed: %s (%s->%s)\n", name, - temp[0].mode, temp[1].mode); + printf("deleted file mode %s\n", temp[0].mode); + 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 (strcmp(name_a, name_b)) { + printf("rename old %s\n", name_a); + printf("rename new %s\n", name_b); + } + if (strncmp(temp[0].mode, temp[1].mode, 3)) + /* we do not run diff between different kind + * of objects. + */ + exit(0); + } fflush(NULL); execlp("/bin/sh","sh", "-c", cmd, NULL); } +/* + * Given a name and sha1 pair, if the dircache tells us the file in + * the work tree has that object contents, return true, so that + * prepare_temp_file() does not have to inflate and extract. + */ +static int work_tree_matches(const char *name, const unsigned char *sha1) +{ + struct cache_entry *ce; + struct stat st; + int pos, len; + + /* We do not read the cache ourselves here, because the + * benchmark with my previous version that always reads cache + * shows that it makes things worse for diff-tree comparing + * two linux-2.6 kernel trees in an already checked out work + * tree. This is because most diff-tree comparisons deal with + * only a small number of files, while reading the cache is + * expensive for a large project, and its cost outweighs the + * savings we get by not inflating the object to a temporary + * file. Practically, this code only helps when we are used + * by diff-cache --cached, which does read the cache before + * calling us. + */ + if (!active_cache) + return 0; + + len = strlen(name); + pos = cache_name_pos(name, len); + if (pos < 0) + return 0; + ce = active_cache[pos]; + if ((lstat(name, &st) < 0) || + !S_ISREG(st.st_mode) || + ce_match_stat(ce, &st) || + memcmp(sha1, ce->sha1, 20)) + return 0; + return 1; +} + +static void prep_temp_blob(struct diff_tempfile *temp, + void *blob, + unsigned long size, + unsigned char *sha1, + int mode) +{ + int fd; + + strcpy(temp->tmp_path, ".diff_XXXXXX"); + fd = mkstemp(temp->tmp_path); + if (fd < 0) + die("unable to create temp-file"); + if (write(fd, blob, size) != size) + die("unable to write temp-file"); + close(fd); + temp->name = temp->tmp_path; + strcpy(temp->hex, sha1_to_hex(sha1)); + temp->hex[40] = 0; + sprintf(temp->mode, "%06o", mode); +} + static void prepare_temp_file(const char *name, struct diff_tempfile *temp, struct diff_spec *one) { static unsigned char null_sha1[20] = { 0, }; + int use_work_tree = 0; if (!one->file_valid) { not_a_valid_file: @@ -150,46 +229,53 @@ static void prepare_temp_file(const char *name, } if (one->sha1_valid && - !memcmp(one->u.sha1, null_sha1, sizeof(null_sha1))) { - one->sha1_valid = 0; - one->u.name = name; - } + (!memcmp(one->blob_sha1, null_sha1, sizeof(null_sha1)) || + work_tree_matches(name, one->blob_sha1))) + use_work_tree = 1; - if (!one->sha1_valid) { + if (!one->sha1_valid || use_work_tree) { struct stat st; - temp->name = one->u.name; - if (stat(temp->name, &st) < 0) { + temp->name = name; + if (lstat(temp->name, &st) < 0) { if (errno == ENOENT) goto not_a_valid_file; die("stat(%s): %s", temp->name, strerror(errno)); } - strcpy(temp->hex, sha1_to_hex(null_sha1)); - sprintf(temp->mode, "%06o", - S_IFREG |ce_permissions(st.st_mode)); + if (S_ISLNK(st.st_mode)) { + int ret; + char *buf, buf_[1024]; + buf = ((sizeof(buf_) < st.st_size) ? + xmalloc(st.st_size) : buf_); + ret = readlink(name, buf, st.st_size); + if (ret < 0) + die("readlink(%s)", name); + prep_temp_blob(temp, buf, st.st_size, + (one->sha1_valid ? + one->blob_sha1 : null_sha1), + (one->sha1_valid ? + one->mode : S_IFLNK)); + } + else { + if (!one->sha1_valid) + strcpy(temp->hex, sha1_to_hex(null_sha1)); + else + strcpy(temp->hex, sha1_to_hex(one->blob_sha1)); + sprintf(temp->mode, "%06o", + S_IFREG |ce_permissions(st.st_mode)); + } + return; } else { - int fd; void *blob; char type[20]; unsigned long size; - blob = read_sha1_file(one->u.sha1, type, &size); + blob = read_sha1_file(one->blob_sha1, type, &size); if (!blob || strcmp(type, "blob")) die("unable to read blob object for %s (%s)", - name, sha1_to_hex(one->u.sha1)); - - strcpy(temp->tmp_path, ".diff_XXXXXX"); - fd = mkstemp(temp->tmp_path); - if (fd < 0) - die("unable to create temp-file"); - if (write(fd, blob, size) != size) - die("unable to write temp-file"); - close(fd); + name, sha1_to_hex(one->blob_sha1)); + prep_temp_blob(temp, blob, size, one->blob_sha1, one->mode); free(blob); - temp->name = temp->tmp_path; - strcpy(temp->hex, sha1_to_hex(one->u.sha1)); - temp->hex[40] = 0; - sprintf(temp->mode, "%06o", one->mode); } } @@ -216,6 +302,7 @@ static void remove_tempfile_on_signal(int signo) * */ void run_external_diff(const char *name, + const char *other, struct diff_spec *one, struct diff_spec *two) { @@ -226,7 +313,7 @@ void run_external_diff(const char *name, if (one && two) { prepare_temp_file(name, &temp[0], one); - prepare_temp_file(name, &temp[1], two); + prepare_temp_file(other ? : name, &temp[1], two); if (! atexit_asked && (temp[0].name == temp[0].tmp_path || temp[1].name == temp[1].tmp_path)) { @@ -242,7 +329,8 @@ void run_external_diff(const char *name, die("unable to fork"); if (!pid) { const char *pgm = external_diff(); - if (pgm) { + /* not passing rename patch to external ones */ + if (!other && pgm) { if (one && two) execlp(pgm, pgm, name, @@ -256,21 +344,25 @@ void run_external_diff(const char *name, * otherwise we use the built-in one. */ if (one && two) - builtin_diff(name, temp); + builtin_diff(name, other ? : name, temp); else printf("* Unmerged path %s\n", name); exit(0); } - if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status)) { - /* We do not check the exit status because typically + 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. We *knew* - * they are different and that's why we ran diff - * in the first place! However if it dies by a signal, - * we stop processing immediately. + * 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(); - die("external diff died unexpectedly.\n"); + fprintf(stderr, "external diff died, stopping at %s.\n", name); + exit(1); } remove_tempfile(); } @@ -282,7 +374,7 @@ void diff_addremove(int addremove, unsigned mode, char concatpath[PATH_MAX]; struct diff_spec spec[2], *one, *two; - memcpy(spec[0].u.sha1, sha1, 20); + memcpy(spec[0].blob_sha1, sha1, 20); spec[0].mode = mode; spec[0].sha1_valid = spec[0].file_valid = 1; spec[1].file_valid = 0; @@ -297,7 +389,7 @@ void diff_addremove(int addremove, unsigned mode, strcpy(concatpath, base); strcat(concatpath, path); } - run_external_diff(path ? concatpath : base, one, two); + run_external_diff(path ? concatpath : base, NULL, one, two); } void diff_change(unsigned old_mode, unsigned new_mode, @@ -307,9 +399,9 @@ void diff_change(unsigned old_mode, unsigned new_mode, char concatpath[PATH_MAX]; struct diff_spec spec[2]; - memcpy(spec[0].u.sha1, old_sha1, 20); + memcpy(spec[0].blob_sha1, old_sha1, 20); spec[0].mode = old_mode; - memcpy(spec[1].u.sha1, new_sha1, 20); + memcpy(spec[1].blob_sha1, new_sha1, 20); spec[1].mode = new_mode; spec[0].sha1_valid = spec[0].file_valid = 1; spec[1].sha1_valid = spec[1].file_valid = 1; @@ -318,10 +410,10 @@ void diff_change(unsigned old_mode, unsigned new_mode, strcpy(concatpath, base); strcat(concatpath, path); } - run_external_diff(path ? concatpath : base, &spec[0], &spec[1]); + run_external_diff(path ? concatpath : base, NULL, &spec[0], &spec[1]); } void diff_unmerge(const char *path) { - run_external_diff(path, NULL, NULL); + run_external_diff(path, NULL, NULL, NULL); }