X-Git-Url: https://git.octo.it/?p=git.git;a=blobdiff_plain;f=combine-diff.c;h=64b20cce2427fcf0cd9565f5e24efce78abb0088;hp=7693884fdb7e43d0aecd77ad1c71531181d7ceee;hb=HEAD;hpb=521a3f676794987bfd03703fe58fbec46ed69d49 diff --git a/combine-diff.c b/combine-diff.c index 7693884f..64b20cce 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -4,6 +4,8 @@ #include "diff.h" #include "diffcore.h" #include "quote.h" +#include "xdiff-interface.h" +#include "log-tree.h" static int uninteresting(struct diff_filepair *p) { @@ -110,78 +112,9 @@ static char *grab_blob(const unsigned char *sha1, unsigned long *size) return blob; } -#define TMPPATHLEN 50 -#define MAXLINELEN 10240 - -static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size) -{ - int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX"); - if (fd < 0) - die("unable to create temp-file"); - if (write(fd, blob, size) != size) - die("unable to write temp-file"); - close(fd); -} - -static void write_temp_blob(char *tmpfile, const unsigned char *sha1) -{ - unsigned long size; - void *blob; - blob = grab_blob(sha1, &size); - write_to_temp_file(tmpfile, blob, size); - free(blob); -} - -static int parse_num(char **cp_p, unsigned int *num_p) -{ - char *cp = *cp_p; - unsigned int num = 0; - int read_some; - - while ('0' <= *cp && *cp <= '9') - num = num * 10 + *cp++ - '0'; - if (!(read_some = cp - *cp_p)) - return -1; - *cp_p = cp; - *num_p = num; - return 0; -} - -static int parse_hunk_header(char *line, int len, - unsigned int *ob, unsigned int *on, - unsigned int *nb, unsigned int *nn) -{ - char *cp; - cp = line + 4; - if (parse_num(&cp, ob)) { - bad_line: - return error("malformed diff output: %s", line); - } - if (*cp == ',') { - cp++; - if (parse_num(&cp, on)) - goto bad_line; - } - else - *on = 1; - if (*cp++ != ' ' || *cp++ != '+') - goto bad_line; - if (parse_num(&cp, nb)) - goto bad_line; - if (*cp == ',') { - cp++; - if (parse_num(&cp, nn)) - goto bad_line; - } - else - *nn = 1; - return -!!memcmp(cp, " @@", 3); -} - -static void append_lost(struct sline *sline, int n, const char *line) +static void append_lost(struct sline *sline, int n, const char *line, int len) { struct lline *lline; - int len = strlen(line); unsigned long this_mask = (1UL<lost_tail = &lline->next; } -static void combine_diff(const unsigned char *parent, const char *ourtmp, +struct combine_diff_state { + struct xdiff_emit_state xm; + + unsigned int lno; + int ob, on, nb, nn; + unsigned long nmask; + int num_parent; + int n; + struct sline *sline; + struct sline *lost_bucket; +}; + +static void consume_line(void *state_, char *line, unsigned long len) +{ + struct combine_diff_state *state = state_; + if (5 < len && !memcmp("@@ -", line, 4)) { + if (parse_hunk_header(line, len, + &state->ob, &state->on, + &state->nb, &state->nn)) + return; + state->lno = state->nb; + if (!state->nb) + /* @@ -1,2 +0,0 @@ to remove the + * first two lines... + */ + state->nb = 1; + if (state->nn == 0) + /* @@ -X,Y +N,0 @@ removed Y lines + * that would have come *after* line N + * in the result. Our lost buckets hang + * to the line after the removed lines, + */ + state->lost_bucket = &state->sline[state->nb]; + else + state->lost_bucket = &state->sline[state->nb-1]; + if (!state->sline[state->nb-1].p_lno) + state->sline[state->nb-1].p_lno = + xcalloc(state->num_parent, + sizeof(unsigned long)); + state->sline[state->nb-1].p_lno[state->n] = state->ob; + return; + } + if (!state->lost_bucket) + return; /* not in any hunk yet */ + switch (line[0]) { + case '-': + append_lost(state->lost_bucket, state->n, line+1, len-1); + break; + case '+': + state->sline[state->lno-1].flag |= state->nmask; + state->lno++; + break; + } +} + +static void combine_diff(const unsigned char *parent, mmfile_t *result_file, struct sline *sline, int cnt, int n, int num_parent) { - FILE *in; - char parent_tmp[TMPPATHLEN]; - char cmd[TMPPATHLEN * 2 + 1024]; - char line[MAXLINELEN]; - unsigned int lno, ob, on, nb, nn, p_lno; + unsigned int p_lno, lno; unsigned long nmask = (1UL << n); - struct sline *lost_bucket = NULL; + xpparam_t xpp; + xdemitconf_t xecfg; + mmfile_t parent_file; + xdemitcb_t ecb; + struct combine_diff_state state; + unsigned long sz; if (!cnt) return; /* result deleted */ - write_temp_blob(parent_tmp, parent); - sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'", - parent_tmp, ourtmp); - in = popen(cmd, "r"); - if (!in) - die("cannot spawn %s", cmd); - - lno = 1; - while (fgets(line, sizeof(line), in) != NULL) { - int len = strlen(line); - if (5 < len && !memcmp("@@ -", line, 4)) { - if (parse_hunk_header(line, len, - &ob, &on, &nb, &nn)) - break; - lno = nb; - if (!nb) - /* @@ -1,2 +0,0 @@ to remove the - * first two lines... - */ - nb = 1; - if (nn == 0) - /* @@ -X,Y +N,0 @@ removed Y lines - * that would have come *after* line N - * in the result. Our lost buckets hang - * to the line after the removed lines, - */ - lost_bucket = &sline[nb]; - else - lost_bucket = &sline[nb-1]; - if (!sline[nb-1].p_lno) - sline[nb-1].p_lno = - xcalloc(num_parent, - sizeof(unsigned long)); - sline[nb-1].p_lno[n] = ob; - continue; - } - if (!lost_bucket) - continue; /* not in any hunk yet */ - switch (line[0]) { - case '-': - append_lost(lost_bucket, n, line+1); - break; - case '+': - sline[lno-1].flag |= nmask; - lno++; - break; - } - } - fclose(in); - unlink(parent_tmp); + parent_file.ptr = grab_blob(parent, &sz); + parent_file.size = sz; + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 0; + xecfg.flags = 0; + ecb.outf = xdiff_outf; + ecb.priv = &state; + memset(&state, 0, sizeof(state)); + state.xm.consume = consume_line; + state.nmask = nmask; + state.sline = sline; + state.lno = 1; + state.num_parent = num_parent; + state.n = n; + + xdl_diff(&parent_file, result_file, &xpp, &xecfg, &ecb); + free(parent_file.ptr); /* Assign line numbers for this parent. * @@ -288,7 +244,7 @@ static void combine_diff(const unsigned char *parent, const char *ourtmp, * started by showing sline[lno] (possibly showing the lost * lines attached to it first). */ - for (lno = 0, p_lno = 1; lno < cnt; lno++) { + for (lno = 0, p_lno = 1; lno <= cnt; lno++) { struct lline *ll; sline[lno].p_lno[n] = p_lno; @@ -299,7 +255,7 @@ static void combine_diff(const unsigned char *parent, const char *ourtmp, p_lno++; /* '-' means parent had it */ ll = ll->next; } - if (!(sline[lno].flag & nmask)) + if (lno < cnt && !(sline[lno].flag & nmask)) p_lno++; /* no '+' means parent had it */ } sline[lno].p_lno[n] = p_lno; /* trailer */ @@ -346,14 +302,14 @@ static unsigned long find_next(struct sline *sline, * lines that are not interesting to interesting() function * that are surrounded by interesting() ones. */ - while (i < cnt) + while (i <= cnt) if (uninteresting ? !(sline[i].flag & mark) : (sline[i].flag & mark)) return i; else i++; - return cnt; + return i; } static int give_context(struct sline *sline, unsigned long cnt, int num_parent) @@ -372,10 +328,10 @@ static int give_context(struct sline *sline, unsigned long cnt, int num_parent) * lines but they are treated as "interesting" in the end. */ i = find_next(sline, mark, 0, cnt, 0); - if (cnt <= i) + if (cnt < i) return 0; - while (i < cnt) { + while (i <= cnt) { unsigned long j = (context < i) ? (i - context) : 0; unsigned long k; @@ -388,7 +344,7 @@ static int give_context(struct sline *sline, unsigned long cnt, int num_parent) * next uninteresting one start? */ j = find_next(sline, mark, i, cnt, 1); - if (cnt <= j) + if (cnt < j) break; /* the rest are all interesting */ /* lookahead context lines */ @@ -410,7 +366,7 @@ static int give_context(struct sline *sline, unsigned long cnt, int num_parent) * the trailing edge a bit. */ i = k; - k = (j + context < cnt) ? j + context : cnt; + k = (j + context < cnt+1) ? j + context : cnt+1; while (j < k) sline[j++].flag |= mark; } @@ -425,7 +381,7 @@ static int make_hunks(struct sline *sline, unsigned long cnt, unsigned long i; int has_interesting = 0; - for (i = 0; i < cnt; i++) { + for (i = 0; i <= cnt; i++) { if (interesting(&sline[i], all_mask)) sline[i].flag |= mark; else @@ -439,15 +395,15 @@ static int make_hunks(struct sline *sline, unsigned long cnt, * parent, mark that uninteresting. */ i = 0; - while (i < cnt) { + while (i <= cnt) { unsigned long j, hunk_begin, hunk_end; unsigned long same_diff; - while (i < cnt && !(sline[i].flag & mark)) + while (i <= cnt && !(sline[i].flag & mark)) i++; - if (cnt <= i) + if (cnt < i) break; /* No more interesting hunks */ hunk_begin = i; - for (j = i + 1; j < cnt; j++) { + for (j = i + 1; j <= cnt; j++) { if (!(sline[j].flag & mark)) { /* Look beyond the end to see if there * is an interesting line after this @@ -457,8 +413,8 @@ static int make_hunks(struct sline *sline, unsigned long cnt, int contin = 0; la = adjust_hunk_tail(sline, all_mask, hunk_begin, j); - la = (la + context < cnt) ? - (la + context) : cnt; + la = (la + context < cnt + 1) ? + (la + context) : cnt + 1; while (j <= --la) { if (sline[la].flag & mark) { contin = 1; @@ -551,18 +507,24 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent) while (1) { struct sline *sl = &sline[lno]; - int hunk_end; - while (lno < cnt && !(sline[lno].flag & mark)) + unsigned long hunk_end; + unsigned long rlines; + while (lno <= cnt && !(sline[lno].flag & mark)) lno++; - if (cnt <= lno) + if (cnt < lno) break; - for (hunk_end = lno + 1; hunk_end < cnt; hunk_end++) - if (!(sline[hunk_end].flag & mark)) - break; + else { + for (hunk_end = lno + 1; hunk_end <= cnt; hunk_end++) + if (!(sline[hunk_end].flag & mark)) + break; + } + rlines = hunk_end - lno; + if (cnt < hunk_end) + rlines--; /* pointing at the last delete hunk */ for (i = 0; i <= num_parent; i++) putchar(combine_marker); for (i = 0; i < num_parent; i++) show_parent_lno(sline, lno, hunk_end, cnt, i); - printf(" +%lu,%lu ", lno+1, hunk_end-lno); + printf(" +%lu,%lu ", lno+1, rlines); for (i = 0; i <= num_parent; i++) putchar(combine_marker); putchar('\n'); while (lno < hunk_end) { @@ -581,6 +543,8 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent) puts(ll->line); ll = ll->next; } + if (cnt < lno) + break; p_mask = 1; for (j = 0; j < num_parent; j++) { if (p_mask & sl->flag) @@ -605,7 +569,7 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt, imask = (1UL<lost_head; sline->p_lno[i] = sline->p_lno[j]; while (ll) { @@ -621,75 +585,80 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt, sline->p_lno[i] = sline->p_lno[j]; } +static void dump_quoted_path(const char *prefix, const char *path) +{ + fputs(prefix, stdout); + if (quote_c_style(path, NULL, NULL, 0)) + quote_c_style(path, NULL, stdout, 0); + else + printf("%s", path); + putchar('\n'); +} + static int show_patch_diff(struct combine_diff_path *elem, int num_parent, - int dense, const char *header, - struct diff_options *opt) + int dense, struct rev_info *rev) { - unsigned long size, cnt, lno; - char *result, *cp, *ep; + struct diff_options *opt = &rev->diffopt; + unsigned long result_size, cnt, lno; + char *result, *cp; struct sline *sline; /* survived lines */ int mode_differs = 0; int i, show_hunks, shown_header = 0; - char ourtmp_buf[TMPPATHLEN]; - char *ourtmp = ourtmp_buf; int working_tree_file = !memcmp(elem->sha1, null_sha1, 20); int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV; + mmfile_t result_file; + context = opt->context; /* Read the result of merge first */ - if (!working_tree_file) { - result = grab_blob(elem->sha1, &size); - write_to_temp_file(ourtmp, result, size); - } + if (!working_tree_file) + result = grab_blob(elem->sha1, &result_size); else { /* Used by diff-tree to read from the working tree */ struct stat st; int fd; - ourtmp = elem->path; - if (0 <= (fd = open(ourtmp, O_RDONLY)) && + if (0 <= (fd = open(elem->path, O_RDONLY)) && !fstat(fd, &st)) { int len = st.st_size; int cnt = 0; elem->mode = canon_mode(st.st_mode); - size = len; + result_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); + die("read error '%s'", elem->path); cnt += done; } result[len] = 0; } else { /* deleted file */ - size = 0; + result_size = 0; elem->mode = 0; result = xmalloc(1); result[0] = 0; - ourtmp = "/dev/null"; } if (0 <= fd) close(fd); } - for (cnt = 0, cp = result; cp - result < size; cp++) { + for (cnt = 0, cp = result; cp - result < result_size; cp++) { if (*cp == '\n') cnt++; } - if (size && result[size-1] != '\n') + if (result_size && result[result_size-1] != '\n') cnt++; /* incomplete line */ - sline = xcalloc(cnt+1, sizeof(*sline)); - ep = result; + sline = xcalloc(cnt+2, sizeof(*sline)); sline[0].bol = result; - for (lno = 0; lno <= cnt; lno++) { + for (lno = 0; lno <= cnt + 1; lno++) { sline[lno].lost_tail = &sline[lno].lost_head; sline[lno].flag = 0; } - for (lno = 0, cp = result; cp - result < size; cp++) { + for (lno = 0, cp = result; cp - result < result_size; cp++) { if (*cp == '\n') { sline[lno].len = cp - sline[lno].bol; lno++; @@ -697,11 +666,17 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, sline[lno].bol = cp + 1; } } - if (size && result[size-1] != '\n') - sline[cnt-1].len = size - (sline[cnt-1].bol - result); + if (result_size && result[result_size-1] != '\n') + sline[cnt-1].len = result_size - (sline[cnt-1].bol - result); + + result_file.ptr = result; + result_file.size = result_size; - sline[0].p_lno = xcalloc((cnt+1) * num_parent, sizeof(unsigned long)); - for (lno = 0; lno < cnt; lno++) + /* Even p_lno[cnt+1] is valid -- that is for the end line number + * for deletion hunk at the end. + */ + sline[0].p_lno = xcalloc((cnt+2) * num_parent, sizeof(unsigned long)); + for (lno = 0; lno <= cnt; lno++) sline[lno+1].p_lno = sline[lno].p_lno + num_parent; for (i = 0; i < num_parent; i++) { @@ -714,7 +689,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, } } if (i <= j) - combine_diff(elem->parent[i].sha1, ourtmp, sline, + combine_diff(elem->parent[i].sha1, &result_file, sline, cnt, i, num_parent); if (elem->parent[i].mode != elem->mode) mode_differs = 1; @@ -725,16 +700,9 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, if (show_hunks || mode_differs || working_tree_file) { const char *abb; - if (header) { - shown_header++; - printf("%s%c", header, opt->line_termination); - } - 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'); + if (rev->loginfo) + show_log(rev, rev->loginfo, "\n"); + dump_quoted_path(dense ? "diff --cc " : "diff --combined ", elem->path); printf("index "); for (i = 0; i < num_parent; i++) { abb = find_unique_abbrev(elem->parent[i].sha1, @@ -765,10 +733,10 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, } putchar('\n'); } + dump_quoted_path("--- a/", elem->path); + dump_quoted_path("+++ b/", elem->path); dump_sline(sline, cnt, num_parent); } - if (ourtmp == ourtmp_buf) - unlink(ourtmp); free(result); for (i = 0; i < cnt; i++) { @@ -788,9 +756,10 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, #define COLONS "::::::::::::::::::::::::::::::::" -static void show_raw_diff(struct combine_diff_path *p, int num_parent, const char *header, struct diff_options *opt) +static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev) { - int i, offset, mod_type = 'A'; + struct diff_options *opt = &rev->diffopt; + int i, offset; const char *prefix; int line_termination, inter_name_termination; @@ -799,15 +768,8 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha if (!line_termination) inter_name_termination = 0; - if (header) - printf("%s%c", header, line_termination); - - for (i = 0; i < num_parent; i++) { - if (p->parent[i].mode) - mod_type = 'M'; - } - if (!p->mode) - mod_type = 'D'; + if (rev->loginfo) + show_log(rev, rev->loginfo, "\n"); if (opt->output_format == DIFF_FORMAT_RAW) { offset = strlen(COLONS) - num_parent; @@ -848,57 +810,66 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, const cha } } -int show_combined_diff(struct combine_diff_path *p, +void show_combined_diff(struct combine_diff_path *p, int num_parent, int dense, - const char *header, - struct diff_options *opt) + struct rev_info *rev) { + struct diff_options *opt = &rev->diffopt; if (!p->len) - return 0; + return; switch (opt->output_format) { case DIFF_FORMAT_RAW: case DIFF_FORMAT_NAME_STATUS: case DIFF_FORMAT_NAME: - show_raw_diff(p, num_parent, header, opt); - return 1; - - default: + show_raw_diff(p, num_parent, rev); + return; case DIFF_FORMAT_PATCH: - return show_patch_diff(p, num_parent, dense, header, opt); + show_patch_diff(p, num_parent, dense, rev); + return; + default: + return; } } -const char *diff_tree_combined_merge(const unsigned char *sha1, - const char *header, int dense, - struct diff_options *opt) +void diff_tree_combined(const unsigned char *sha1, + const unsigned char parent[][20], + int num_parent, + int dense, + struct rev_info *rev) { - struct commit *commit = lookup_commit(sha1); + struct diff_options *opt = &rev->diffopt; struct diff_options diffopts; - struct commit_list *parents; struct combine_diff_path *p, *paths = NULL; - int num_parent, i, num_paths; + int i, num_paths; + int do_diffstat; + do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT || + opt->with_stat); diffopts = *opt; - diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; + diffopts.with_raw = 0; + diffopts.with_stat = 0; diffopts.recursive = 1; - /* count parents */ - for (parents = commit->parents, num_parent = 0; - parents; - parents = parents->next, num_parent++) - ; /* nothing */ - /* find set of paths that everybody touches */ - for (parents = commit->parents, i = 0; - parents; - parents = parents->next, i++) { - struct commit *parent = parents->item; - diff_tree_sha1(parent->object.sha1, commit->object.sha1, "", - &diffopts); + for (i = 0; i < num_parent; i++) { + /* show stat against the first parent even + * when doing combined diff. + */ + if (i == 0 && do_diffstat) + diffopts.output_format = DIFF_FORMAT_DIFFSTAT; + else + diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; + diff_tree_sha1(parent[i], sha1, "", &diffopts); diffcore_std(&diffopts); paths = intersect_paths(paths, i, num_parent); + + if (do_diffstat && rev->loginfo) + show_log(rev, rev->loginfo, + opt->with_stat ? "---\n" : "\n"); diff_flush(&diffopts); + if (opt->with_stat) + putchar('\n'); } /* find out surviving paths */ @@ -907,10 +878,17 @@ const char *diff_tree_combined_merge(const unsigned char *sha1, num_paths++; } if (num_paths) { + if (opt->with_raw) { + int saved_format = opt->output_format; + opt->output_format = DIFF_FORMAT_RAW; + for (p = paths; p; p = p->next) { + show_combined_diff(p, num_parent, dense, rev); + } + opt->output_format = saved_format; + putchar(opt->line_termination); + } for (p = paths; p; p = p->next) { - if (show_combined_diff(p, num_parent, dense, - header, opt)) - header = NULL; + show_combined_diff(p, num_parent, dense, rev); } } @@ -920,5 +898,26 @@ const char *diff_tree_combined_merge(const unsigned char *sha1, paths = paths->next; free(tmp); } - return header; +} + +void diff_tree_combined_merge(const unsigned char *sha1, + int dense, struct rev_info *rev) +{ + int num_parent; + const unsigned char (*parent)[20]; + struct commit *commit = lookup_commit(sha1); + struct commit_list *parents; + + /* count parents */ + for (parents = commit->parents, num_parent = 0; + parents; + parents = parents->next, num_parent++) + ; /* nothing */ + + parent = xmalloc(num_parent * sizeof(*parent)); + for (parents = commit->parents, num_parent = 0; + parents; + parents = parents->next, num_parent++) + memcpy(parent + num_parent, parents->item->object.sha1, 20); + diff_tree_combined(sha1, parent, num_parent, dense, rev); }