diff --git a/cache.h b/cache.h index ec2a1610b2..514adb8f8e 100644 --- a/cache.h +++ b/cache.h @@ -165,6 +165,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st); extern int ce_modified(struct cache_entry *ce, struct stat *st); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type); +extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); struct cache_file { diff --git a/diff.c b/diff.c index 7d06b035ae..cbb86320a6 100644 --- a/diff.c +++ b/diff.c @@ -596,15 +596,31 @@ static void run_external_diff(const char *pgm, remove_tempfile(); } +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) { 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; int complete_rewrite = 0; + int len; if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ @@ -616,39 +632,60 @@ static void run_diff(struct diff_filepair *p) name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); 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, other); 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, other); 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]; + memcpy(one_sha1, sha1_to_hex(one->sha1), 41); + + len += snprintf(msg + len, sizeof(msg) - len, + "index %.7s..%.7s", 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)) { diff --git a/sha1_file.c b/sha1_file.c index 895c1fab6f..287f618827 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1545,3 +1545,42 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con munmap(buf, size); return ret; } + +int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object) +{ + int fd; + char *target; + + switch (st->st_mode & S_IFMT) { + case S_IFREG: + fd = open(path, O_RDONLY); + if (fd < 0) + return error("open(\"%s\"): %s", path, + strerror(errno)); + if (index_fd(sha1, fd, st, write_object, NULL) < 0) + return error("%s: failed to insert into database", + path); + break; + case S_IFLNK: + target = xmalloc(st->st_size+1); + if (readlink(path, target, st->st_size+1) != st->st_size) { + char *errstr = strerror(errno); + free(target); + return error("readlink(\"%s\"): %s", path, + errstr); + } + if (!write_object) { + unsigned char hdr[50]; + int hdrlen; + write_sha1_file_prepare(target, st->st_size, "blob", + sha1, hdr, &hdrlen); + } else if (write_sha1_file(target, st->st_size, "blob", sha1)) + return error("%s: failed to insert into database", + path); + free(target); + break; + default: + return error("%s: unsupported file type", path); + } + return 0; +} diff --git a/t/diff-lib.sh b/t/diff-lib.sh index a912f435aa..745a1b0311 100755 --- a/t/diff-lib.sh +++ b/t/diff-lib.sh @@ -29,7 +29,13 @@ compare_diff_raw_z () { compare_diff_patch () { # When heuristics are improved, the score numbers would change. # Ignore them while comparing. - sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$1" >.tmp-1 - sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$2" >.tmp-2 + sed -e ' + /^[dis]*imilarity index [0-9]*%$/d + /^index [0-9a-f]*\.\.[0-9a-f]/d + ' <"$1" >.tmp-1 + sed -e ' + /^[dis]*imilarity index [0-9]*%$/d + /^index [0-9a-f]*\.\.[0-9a-f]/d + ' <"$2" >.tmp-2 diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 } diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index f3b6330a9b..beb6d8f487 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -7,6 +7,7 @@ test_description='Test built-in diff output engine. ' . ./test-lib.sh +. ../diff-lib.sh echo >path0 'Line 1 Line 2 @@ -48,6 +49,6 @@ EOF test_expect_success \ 'validate git-diff-files -p output.' \ - 'cmp -s current expected' + 'compare_diff_patch current expected' test_done diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index be47485682..2e3c20d6b9 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -7,6 +7,7 @@ test_description='Test rename detection in diff engine. ' . ./test-lib.sh +. ../diff-lib.sh echo >path0 'Line 1 Line 2 @@ -61,6 +62,6 @@ EOF test_expect_success \ 'validate the output.' \ - 'diff -I "similarity.*" >/dev/null current expected' + 'compare_diff_patch current expected' test_done diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh index f59614ae25..a23aaa0a94 100755 --- a/t/t4004-diff-rename-symlink.sh +++ b/t/t4004-diff-rename-symlink.sh @@ -10,6 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed by an edit for them. ' . ./test-lib.sh +. ../diff-lib.sh test_expect_success \ 'prepare reference tree' \ @@ -61,6 +62,6 @@ EOF test_expect_success \ 'validate diff output' \ - 'diff -u current expected' + 'compare_diff_patch current expected' test_done diff --git a/update-index.c b/update-index.c index b825a11d2f..9dc518877b 100644 --- a/update-index.c +++ b/update-index.c @@ -37,8 +37,6 @@ static int add_file_to_cache(const char *path) int size, namelen, option, status; struct cache_entry *ce; struct stat st; - int fd; - char *target; status = lstat(path, &st); if (status < 0 || S_ISDIR(st.st_mode)) { @@ -77,34 +75,9 @@ static int add_file_to_cache(const char *path) fill_stat_cache_info(ce, &st); ce->ce_mode = create_ce_mode(st.st_mode); ce->ce_flags = htons(namelen); - switch (st.st_mode & S_IFMT) { - case S_IFREG: - fd = open(path, O_RDONLY); - if (fd < 0) - return error("open(\"%s\"): %s", path, strerror(errno)); - if (index_fd(ce->sha1, fd, &st, !info_only, NULL) < 0) - return error("%s: failed to insert into database", path); - break; - case S_IFLNK: - target = xmalloc(st.st_size+1); - if (readlink(path, target, st.st_size+1) != st.st_size) { - char *errstr = strerror(errno); - free(target); - return error("readlink(\"%s\"): %s", path, - errstr); - } - if (info_only) { - unsigned char hdr[50]; - int hdrlen; - write_sha1_file_prepare(target, st.st_size, "blob", - ce->sha1, hdr, &hdrlen); - } else if (write_sha1_file(target, st.st_size, "blob", ce->sha1)) - return error("%s: failed to insert into database", path); - free(target); - break; - default: - return error("%s: unsupported file type", path); - } + + if (index_path(ce->sha1, path, &st, !info_only)) + return -1; option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; if (add_cache_entry(ce, option))