X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=combine-diff.c;h=243f96775aaa058581491544f53f05a3bd038cc0;hb=d41df15e6913e8f7e1b5d656bb51dc4268232c36;hp=b0846bc09bd2dc2c63f7ec702ac021e192606d93;hpb=e228340961e0f48c6b818251bd4729b2d19647e4;p=git.git diff --git a/combine-diff.c b/combine-diff.c index b0846bc0..243f9677 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -4,14 +4,6 @@ #include "diffcore.h" #include "quote.h" -struct path_list { - struct path_list *next; - int len; - char *path; - unsigned char sha1[20]; - unsigned char parent_sha1[FLEX_ARRAY][20]; -}; - static int uninteresting(struct diff_filepair *p) { if (diff_unmodified_pair(p)) @@ -21,15 +13,14 @@ static int uninteresting(struct diff_filepair *p) return 0; } -static struct path_list *intersect_paths(struct path_list *curr, - int n, int num_parent) +static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent) { struct diff_queue_struct *q = &diff_queued_diff; - struct path_list *p; + struct combine_diff_path *p; int i; if (!n) { - struct path_list *list = NULL, **tail = &list; + struct combine_diff_path *list = NULL, **tail = &list; for (i = 0; i < q->nr; i++) { int len; const char *path; @@ -271,87 +262,219 @@ static int interesting(struct sline *sline, unsigned long all_mask) return ((sline->flag & all_mask) != all_mask || sline->lost_head); } -static unsigned long line_diff_parents(struct sline *sline, unsigned long all_mask) +static unsigned long line_common_diff(struct sline *sline, unsigned long all_mask) { /* - * Look at the line and see from which parents we have difference. - * Lower bits of sline->flag records if the parent had this line, - * so XOR with all_mask gives us on-bits for parents we have - * differences with. + * Look at the line and see from which parents we have the + * same difference. + */ + + /* Lower bits of sline->flag records if the parent had this + * line, so XOR with all_mask gives us on-bits for parents we + * have differences with. */ - unsigned long parents = (sline->flag ^ all_mask); + unsigned long common_adds = (sline->flag ^ all_mask) & all_mask; + unsigned long common_removes = all_mask; + + /* If all the parents have this line, that also counts as + * having the same difference. + */ + if (!common_adds) + common_adds = all_mask; + if (sline->lost_head) { + /* Lost head list records the lines removed from + * the parents, and parent_map records from which + * parent the line was removed. + */ struct lline *ll; - for (ll = sline->lost_head; ll; ll = ll->next) - parents |= ll->parent_map; + for (ll = sline->lost_head; ll; ll = ll->next) { + common_removes &= ll->parent_map; + } } - return parents & all_mask; + return common_adds & common_removes; } -static void make_hunks(struct sline *sline, unsigned long cnt, - int num_parent, int dense) +static unsigned long line_all_diff(struct sline *sline, unsigned long all_mask) +{ + /* + * Look at the line and see from which parents we have some difference. + */ + unsigned long different = (sline->flag ^ all_mask) & all_mask; + if (sline->lost_head) { + /* Lost head list records the lines removed from + * the parents, and parent_map records from which + * parent the line was removed. + */ + struct lline *ll; + for (ll = sline->lost_head; ll; ll = ll->next) { + different |= ll->parent_map; + } + } + return different; +} + +static unsigned long adjust_hunk_tail(struct sline *sline, + unsigned long all_mask, + unsigned long hunk_begin, + unsigned long i) +{ + /* i points at the first uninteresting line. + * If the last line of the hunk was interesting + * only because it has some deletion, then + * it is not all that interesting for the + * purpose of giving trailing context lines. + */ + if ((hunk_begin + 1 <= i) && + ((sline[i-1].flag & all_mask) == all_mask)) + i--; + return i; +} + +static unsigned long next_interesting(struct sline *sline, + unsigned long mark, + unsigned long i, + unsigned long cnt, + int uninteresting) +{ + while (i < cnt) + if (uninteresting ? + !(sline[i].flag & mark) : + (sline[i].flag & mark)) + return i; + else + i++; + return cnt; +} + +static int give_context(struct sline *sline, unsigned long cnt, int num_parent) { unsigned long all_mask = (1UL<sha1, &size); - write_to_temp_file(ourtmp, result, size); + if (memcmp(elem->sha1, null_sha1, 20)) { + result = grab_blob(elem->sha1, &size); + write_to_temp_file(ourtmp, result, size); + } + else { + struct stat st; + int fd; + ourtmp = elem->path; + if (0 <= (fd = open(ourtmp, O_RDONLY)) && + !fstat(fd, &st)) { + int len = st.st_size; + int cnt = 0; + + size = len; + result = xmalloc(len + 1); + while (cnt < len) { + int done = xread(fd, result+cnt, len-cnt); + if (done == 0) + break; + if (done < 0) + die("read error '%s'", ourtmp); + cnt += done; + } + result[len] = 0; + } + else { + /* deleted file */ + size = 0; + result = xmalloc(1); + result[0] = 0; + ourtmp = "/dev/null"; + } + if (0 <= fd) + close(fd); + } for (cnt = 0, cp = result; cp - result < size; cp++) { if (*cp == '\n') @@ -442,10 +599,23 @@ static void show_combined_diff(struct path_list *elem, int num_parent, for (i = 0; i < num_parent; i++) combine_diff(elem->parent_sha1[i], ourtmp, sline, cnt, i); - make_hunks(sline, cnt, num_parent, dense); + show_hunks = make_hunks(sline, cnt, num_parent, dense); - dump_sline(sline, cnt, num_parent); - unlink(ourtmp); + if (header && (show_hunks || show_empty)) { + shown_header++; + puts(header); + } + if (show_hunks) { + printf("diff --%s ", dense ? "cc" : "combined"); + if (quote_c_style(elem->path, NULL, NULL, 0)) + quote_c_style(elem->path, NULL, stdout, 0); + else + printf("%s", elem->path); + putchar('\n'); + dump_sline(sline, cnt, num_parent); + } + if (ourtmp == ourtmp_buf) + unlink(ourtmp); free(result); for (i = 0; i < cnt; i++) { @@ -459,6 +629,7 @@ static void show_combined_diff(struct path_list *elem, int num_parent, } } free(sline); + return shown_header; } int diff_tree_combined_merge(const unsigned char *sha1, @@ -468,7 +639,7 @@ int diff_tree_combined_merge(const unsigned char *sha1, struct commit *commit = lookup_commit(sha1); struct diff_options diffopts; struct commit_list *parents; - struct path_list *p, *paths = NULL; + struct combine_diff_path *p, *paths = NULL; int num_parent, i, num_paths; diff_setup(&diffopts); @@ -498,23 +669,18 @@ int diff_tree_combined_merge(const unsigned char *sha1, num_paths++; } if (num_paths || show_empty_merge) { - puts(header); for (p = paths; p; p = p->next) { if (!p->len) continue; - printf("diff --%s ", dense ? "cc" : "combined"); - if (quote_c_style(p->path, NULL, NULL, 0)) - quote_c_style(p->path, NULL, stdout, 0); - else - printf("%s", p->path); - putchar('\n'); - show_combined_diff(p, num_parent, dense); + if (show_combined_diff(p, num_parent, dense, header, + show_empty_merge)) + header = NULL; } } /* Clean things up */ while (paths) { - struct path_list *tmp = paths; + struct combine_diff_path *tmp = paths; paths = paths->next; free(tmp); }