Browse Source

Show original and resulting blob object info in diff output.

This adds more cruft to diff --git header to record the blob SHA1 and
the mode the patch/diff is intended to be applied against, to help the
receiving end fall back on a three-way merge.  The new header looks
like this:

    diff --git a/apply.c b/apply.c
    index 7be5041..8366082 100644
    --- a/apply.c
    +++ b/apply.c
    @@ -14,6 +14,7 @@
     //    files that are being modified, but doesn't apply the patch
     //  --stat does just a diffstat, and doesn't actually apply
    +//  --show-index-info shows the old and new index info for...
    ...

Upon receiving such a patch, if the patch did not apply cleanly to the
target tree, the recipient can try to find the matching old objects in
her object database and create a temporary tree, apply the patch to
that temporary tree, and attempt a 3-way merge between the patched
temporary tree and the target tree using the original temporary tree
as the common ancestor.

The patch lifts the code to compute the hash for an on-filesystem
object from update-index.c and makes it available to the diff output
routine.

Signed-off-by: Junio C Hamano <junkio@cox.net>
maint
Junio C Hamano 19 years ago
parent
commit
ec1fcc16af
  1. 1
      cache.h
  2. 77
      diff.c
  3. 39
      sha1_file.c
  4. 10
      t/diff-lib.sh
  5. 3
      t/t4000-diff-format.sh
  6. 3
      t/t4001-diff-rename.sh
  7. 3
      t/t4004-diff-rename-symlink.sh
  8. 33
      update-index.c

1
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_modified(struct cache_entry *ce, struct stat *st);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); 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_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); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);


struct cache_file { struct cache_file {

77
diff.c

@ -596,15 +596,31 @@ static void run_external_diff(const char *pgm,
remove_tempfile(); 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) static void run_diff(struct diff_filepair *p)
{ {
const char *pgm = external_diff(); 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 *one;
struct diff_filespec *two; struct diff_filespec *two;
const char *name; const char *name;
const char *other; const char *other;
int complete_rewrite = 0; int complete_rewrite = 0;
int len;


if (DIFF_PAIR_UNMERGED(p)) { if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */ /* unmerged */
@ -616,39 +632,60 @@ static void run_diff(struct diff_filepair *p)
name = p->one->path; name = p->one->path;
other = (strcmp(name, p->two->path) ? p->two->path : NULL); other = (strcmp(name, p->two->path) ? p->two->path : NULL);
one = p->one; two = p->two; one = p->one; two = p->two;

diff_fill_sha1_info(one);
diff_fill_sha1_info(two);

len = 0;
switch (p->status) { switch (p->status) {
case DIFF_STATUS_COPIED: case DIFF_STATUS_COPIED:
sprintf(msg_, len += snprintf(msg + len, sizeof(msg) - len,
"similarity index %d%%\n" "similarity index %d%%\n"
"copy from %s\n" "copy from %s\n"
"copy to %s", "copy to %s\n",
(int)(0.5 + p->score * 100.0/MAX_SCORE), (int)(0.5 + p->score * 100.0/MAX_SCORE),
name, other); name, other);
xfrm_msg = msg_;
break; break;
case DIFF_STATUS_RENAMED: case DIFF_STATUS_RENAMED:
sprintf(msg_, len += snprintf(msg + len, sizeof(msg) - len,
"similarity index %d%%\n" "similarity index %d%%\n"
"rename from %s\n" "rename from %s\n"
"rename to %s", "rename to %s\n",
(int)(0.5 + p->score * 100.0/MAX_SCORE), (int)(0.5 + p->score * 100.0/MAX_SCORE),
name, other); name, other);
xfrm_msg = msg_;
break; break;
case DIFF_STATUS_MODIFIED: case DIFF_STATUS_MODIFIED:
if (p->score) { if (p->score) {
sprintf(msg_, len += snprintf(msg + len, sizeof(msg) - len,
"dissimilarity index %d%%", "dissimilarity index %d%%\n",
(int)(0.5 + p->score * 100.0/MAX_SCORE)); (int)(0.5 + p->score *
xfrm_msg = msg_; 100.0/MAX_SCORE));
complete_rewrite = 1; complete_rewrite = 1;
break; break;
} }
/* fallthru */ /* fallthru */
default: 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 && if (!pgm &&
DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
(S_IFMT & one->mode) != (S_IFMT & two->mode)) { (S_IFMT & one->mode) != (S_IFMT & two->mode)) {

39
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); munmap(buf, size);
return ret; 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;
}

10
t/diff-lib.sh

@ -29,7 +29,13 @@ compare_diff_raw_z () {
compare_diff_patch () { compare_diff_patch () {
# When heuristics are improved, the score numbers would change. # When heuristics are improved, the score numbers would change.
# Ignore them while comparing. # Ignore them while comparing.
sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$1" >.tmp-1 sed -e '
sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$2" >.tmp-2 /^[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 -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
} }

3
t/t4000-diff-format.sh

@ -7,6 +7,7 @@ test_description='Test built-in diff output engine.


' '
. ./test-lib.sh . ./test-lib.sh
. ../diff-lib.sh


echo >path0 'Line 1 echo >path0 'Line 1
Line 2 Line 2
@ -48,6 +49,6 @@ EOF


test_expect_success \ test_expect_success \
'validate git-diff-files -p output.' \ 'validate git-diff-files -p output.' \
'cmp -s current expected' 'compare_diff_patch current expected'


test_done test_done

3
t/t4001-diff-rename.sh

@ -7,6 +7,7 @@ test_description='Test rename detection in diff engine.


' '
. ./test-lib.sh . ./test-lib.sh
. ../diff-lib.sh


echo >path0 'Line 1 echo >path0 'Line 1
Line 2 Line 2
@ -61,6 +62,6 @@ EOF


test_expect_success \ test_expect_success \
'validate the output.' \ 'validate the output.' \
'diff -I "similarity.*" >/dev/null current expected' 'compare_diff_patch current expected'


test_done test_done

3
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. by an edit for them.
' '
. ./test-lib.sh . ./test-lib.sh
. ../diff-lib.sh


test_expect_success \ test_expect_success \
'prepare reference tree' \ 'prepare reference tree' \
@ -61,6 +62,6 @@ EOF


test_expect_success \ test_expect_success \
'validate diff output' \ 'validate diff output' \
'diff -u current expected' 'compare_diff_patch current expected'


test_done test_done

33
update-index.c

@ -37,8 +37,6 @@ static int add_file_to_cache(const char *path)
int size, namelen, option, status; int size, namelen, option, status;
struct cache_entry *ce; struct cache_entry *ce;
struct stat st; struct stat st;
int fd;
char *target;


status = lstat(path, &st); status = lstat(path, &st);
if (status < 0 || S_ISDIR(st.st_mode)) { 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); fill_stat_cache_info(ce, &st);
ce->ce_mode = create_ce_mode(st.st_mode); ce->ce_mode = create_ce_mode(st.st_mode);
ce->ce_flags = htons(namelen); ce->ce_flags = htons(namelen);
switch (st.st_mode & S_IFMT) {
case S_IFREG: if (index_path(ce->sha1, path, &st, !info_only))
fd = open(path, O_RDONLY); return -1;
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);
}
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
if (add_cache_entry(ce, option)) if (add_cache_entry(ce, option))

Loading…
Cancel
Save