X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=diff.c;h=680b521a8bcab5355306ba6367d4c7f2cbd75f5c;hb=1a0756ffe4a4faf2dd70b36c36519d8530d98e7c;hp=3d86b7a317d0699bec3a2f5c0e09fb1df1421083;hpb=6f93a631acd2017c5f7c5bc29294b753588b0bea;p=git.git diff --git a/diff.c b/diff.c index 3d86b7a3..680b521a 100644 --- a/diff.c +++ b/diff.c @@ -171,8 +171,8 @@ struct diff_filespec *alloc_filespec(const char *path) void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, unsigned short mode) { - if (mode) { /* just playing defensive */ - spec->mode = mode; + if (mode) { + spec->mode = DIFF_FILE_CANON_MODE(mode); memcpy(spec->sha1, sha1, 20); spec->sha1_valid = !!memcmp(sha1, null_sha1, 20); } @@ -390,7 +390,8 @@ static void remove_tempfile_on_signal(int signo) * infile2 infile2-sha1 infile2-mode [ rename-to ] * */ -static void run_external_diff(const char *name, +static void run_external_diff(const char *pgm, + const char *name, const char *other, struct diff_filespec *one, struct diff_filespec *two, @@ -418,7 +419,6 @@ static void run_external_diff(const char *name, if (pid < 0) die("unable to fork"); if (!pid) { - const char *pgm = external_diff(); if (pgm) { if (one && two) { const char *exec_arg[10]; @@ -468,6 +468,30 @@ static void run_external_diff(const char *name, remove_tempfile(); } +static void run_diff(const char *name, + const char *other, + struct diff_filespec *one, + struct diff_filespec *two, + const char *xfrm_msg) +{ + const char *pgm = external_diff(); + if (!pgm && + DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && + (S_IFMT & one->mode) != (S_IFMT & two->mode)) { + /* a filepair that changes between file and symlink + * needs to be split into deletion and creation. + */ + struct diff_filespec *null = alloc_filespec(two->path); + run_external_diff(NULL, name, other, one, null, xfrm_msg); + free(null); + null = alloc_filespec(one->path); + run_external_diff(NULL, name, other, null, two, xfrm_msg); + free(null); + } + else + run_external_diff(pgm, name, other, one, two, xfrm_msg); +} + void diff_setup(int reverse_diff_) { reverse_diff = reverse_diff_; @@ -497,6 +521,13 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue, return dp; } +void diff_free_filepair(struct diff_filepair *p) +{ + diff_free_filespec_data(p->one); + diff_free_filespec_data(p->two); + free(p); +} + static void diff_flush_raw(struct diff_filepair *p, int line_termination, int inter_name_termination) @@ -517,7 +548,8 @@ static void diff_flush_raw(struct diff_filepair *p, switch (p->status) { case 'C': case 'R': two_paths = 1; - sprintf(status, "%c%1d", p->status, p->score); + sprintf(status, "%c%03d", p->status, + (int)(0.5 + p->score * 100.0/MAX_SCORE)); break; default: two_paths = 0; @@ -552,9 +584,11 @@ int diff_unmodified_pair(struct diff_filepair *p) one = p->one; two = p->two; - /* deletion, addition, mode change and renames are all interesting. */ + /* deletion, addition, mode or type change + * and rename are all interesting. + */ if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) || - (one->mode != two->mode) || + DIFF_PAIR_MODE_CHANGED(p) || strcmp(one->path, two->path)) return 0; @@ -607,9 +641,9 @@ static void diff_flush_patch(struct diff_filepair *p) } if (DIFF_PAIR_UNMERGED(p)) - run_external_diff(name, NULL, NULL, NULL, NULL); + run_diff(name, NULL, NULL, NULL, NULL); else - run_external_diff(name, other, p->one, p->two, msg); + run_diff(name, other, p->one, p->two, msg); } int diff_needs_to_stay(struct diff_queue_struct *q, int i, @@ -691,27 +725,34 @@ static void diff_resolve_rename_copy(void) for (i = 0; i < q->nr; i++) { p = q->queue[i]; - p->status = 0; + p->status = 0; /* undecided */ if (DIFF_PAIR_UNMERGED(p)) p->status = 'U'; else if (!DIFF_FILE_VALID((p)->one)) p->status = 'N'; else if (!DIFF_FILE_VALID((p)->two)) { /* Deletion record should be omitted if there - * is another entry that is a rename or a copy - * and it uses this one as the source. Then we - * can say the other one is a rename. + * are rename/copy entries using this one as + * the source. Then we can say one of them + * is a rename and the rest are copies. */ + p->status = 'D'; for (j = 0; j < q->nr; j++) { pp = q->queue[j]; if (!strcmp(pp->one->path, p->one->path) && - strcmp(pp->one->path, pp->two->path)) + strcmp(pp->one->path, pp->two->path)) { + p->status = 'X'; break; + } } - if (j < q->nr) - continue; /* has rename/copy */ - p->status = 'D'; } + else if (DIFF_PAIR_TYPE_CHANGED(p)) + p->status = 'T'; + + /* from this point on, we are dealing with a pair + * whose both sides are valid and of the same type, i.e. + * either in-place edit or rename/copy edit. + */ else if (strcmp(p->one->path, p->two->path)) { /* See if there is somebody else anywhere that * will keep the path (either modified or @@ -719,7 +760,7 @@ static void diff_resolve_rename_copy(void) * not a rename. In addition, if there is * some other rename or copy that comes later * than us that uses the same source, we - * cannot be a rename either. + * have to be a copy, not a rename. */ for (j = 0; j < q->nr; j++) { pp = q->queue[j]; @@ -743,12 +784,12 @@ static void diff_resolve_rename_copy(void) if (!p->status) p->status = 'R'; } - else if (memcmp(p->one->sha1, p->two->sha1, 20)) + else if (memcmp(p->one->sha1, p->two->sha1, 20) || + p->one->mode != p->two->mode) p->status = 'M'; - else { - /* we do not need this one */ - p->status = 0; - } + else + /* this is a "no-change" entry */ + p->status = 'X'; } diff_debug_queue("resolve-rename-copy done", q); } @@ -767,8 +808,11 @@ void diff_flush(int diff_output_style, int resolve_rename_copy) for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; + if ((diff_output_style == DIFF_FORMAT_NO_OUTPUT) || + (p->status == 'X')) + continue; if (p->status == 0) - p->status = '?'; + die("internal error in diff-resolve-rename-copy"); switch (diff_output_style) { case DIFF_FORMAT_PATCH: diff_flush_patch(p); @@ -780,12 +824,8 @@ void diff_flush(int diff_output_style, int resolve_rename_copy) break; } } - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - diff_free_filespec_data(p->one); - diff_free_filespec_data(p->two); - free(p); - } + for (i = 0; i < q->nr; i++) + diff_free_filepair(q->queue[i]); free(q->queue); q->queue = NULL; q->nr = q->alloc = 0;