From: Junio C Hamano Date: Sat, 28 Jan 2006 08:16:09 +0000 (-0800) Subject: Merge lt/revlist,jc/diff,jc/revparse,jc/abbrev X-Git-Tag: v1.2.0~100 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=addafaf92eeb86033da91323d0d3ad7a496dae83;hp=6b1ddbdd6e02719ae2be55dc141a176187e5027e;p=git.git Merge lt/revlist,jc/diff,jc/revparse,jc/abbrev --- diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index f9146f19..1c6146c7 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -14,6 +14,7 @@ SYNOPSIS [ \--min-age=timestamp ] [ \--sparse ] [ \--no-merges ] + [ \--remove-empty ] [ \--all ] [ [ \--merge-order [ \--show-breaks ] ] | [ \--topo-order ] ] [ \--parents ] @@ -80,6 +81,9 @@ OPTIONS (still subject to count and age limitation), but apply merge simplification nevertheless. +--remove-empty:: + Stop when a given path disappears from the tree. + --all:: Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the command line as . diff --git a/Makefile b/Makefile index 6910448e..2aa2385b 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,7 @@ LIB_H = \ DIFF_OBJS = \ diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \ - diffcore-pickaxe.o diffcore-rename.o tree-diff.o + diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o LIB_OBJS = \ blob.o commit.o connect.o count-delta.o csum-file.o \ diff --git a/combine-diff.c b/combine-diff.c new file mode 100644 index 00000000..243f9677 --- /dev/null +++ b/combine-diff.c @@ -0,0 +1,688 @@ +#include "cache.h" +#include "commit.h" +#include "diff.h" +#include "diffcore.h" +#include "quote.h" + +static int uninteresting(struct diff_filepair *p) +{ + if (diff_unmodified_pair(p)) + return 1; + if (!S_ISREG(p->one->mode) || !S_ISREG(p->two->mode)) + return 1; + return 0; +} + +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 combine_diff_path *p; + int i; + + if (!n) { + struct combine_diff_path *list = NULL, **tail = &list; + for (i = 0; i < q->nr; i++) { + int len; + const char *path; + if (uninteresting(q->queue[i])) + continue; + path = q->queue[i]->two->path; + len = strlen(path); + + p = xmalloc(sizeof(*p) + len + 1 + num_parent * 20); + p->path = (char*) &(p->parent_sha1[num_parent][0]); + memcpy(p->path, path, len); + p->path[len] = 0; + p->len = len; + p->next = NULL; + memcpy(p->sha1, q->queue[i]->two->sha1, 20); + memcpy(p->parent_sha1[n], q->queue[i]->one->sha1, 20); + *tail = p; + tail = &p->next; + } + return list; + } + + for (p = curr; p; p = p->next) { + int found = 0; + if (!p->len) + continue; + for (i = 0; i < q->nr; i++) { + const char *path; + int len; + + if (uninteresting(q->queue[i])) + continue; + path = q->queue[i]->two->path; + len = strlen(path); + if (len == p->len && !memcmp(path, p->path, len)) { + found = 1; + memcpy(p->parent_sha1[n], + q->queue[i]->one->sha1, 20); + break; + } + } + if (!found) + p->len = 0; + } + return curr; +} + +struct lline { + struct lline *next; + int len; + unsigned long parent_map; + char line[FLEX_ARRAY]; +}; + +struct sline { + struct lline *lost_head, **lost_tail; + char *bol; + int len; + unsigned long flag; +}; + +static char *grab_blob(const unsigned char *sha1, unsigned long *size) +{ + char *blob; + char type[20]; + if (!memcmp(sha1, null_sha1, 20)) { + /* deleted blob */ + *size = 0; + return xcalloc(1, 1); + } + blob = read_sha1_file(sha1, type, size); + if (strcmp(type, "blob")) + die("object '%s' is not a blob!", sha1_to_hex(sha1)); + 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) +{ + struct lline *lline; + int len = strlen(line); + unsigned long this_mask = (1UL<lost_head) { + struct lline *last_one = NULL; + /* We cannot squash it with earlier one */ + for (lline = sline->lost_head; + lline; + lline = lline->next) + if (lline->parent_map & this_mask) + last_one = lline; + lline = last_one ? last_one->next : sline->lost_head; + while (lline) { + if (lline->len == len && + !memcmp(lline->line, line, len)) { + lline->parent_map |= this_mask; + return; + } + lline = lline->next; + } + } + + lline = xmalloc(sizeof(*lline) + len + 1); + lline->len = len; + lline->next = NULL; + lline->parent_map = this_mask; + memcpy(lline->line, line, len); + lline->line[len] = 0; + *sline->lost_tail = lline; + sline->lost_tail = &lline->next; +} + +static void combine_diff(const unsigned char *parent, const char *ourtmp, + struct sline *sline, int cnt, int n) +{ + FILE *in; + char parent_tmp[TMPPATHLEN]; + char cmd[TMPPATHLEN * 2 + 1024]; + char line[MAXLINELEN]; + unsigned int lno, ob, on, nb, nn; + unsigned long pmask = ~(1UL << n); + struct sline *lost_bucket = NULL; + + 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) + return; + + 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; + } + lost_bucket = &sline[nb-1]; /* sline is 0 based */ + continue; + } + if (!lost_bucket) + continue; + switch (line[0]) { + case '-': + append_lost(lost_bucket, n, line+1); + break; + case '+': + sline[lno-1].flag &= pmask; + lno++; + break; + } + } + fclose(in); + unlink(parent_tmp); +} + +static unsigned long context = 3; +static char combine_marker = '@'; + +static int interesting(struct sline *sline, unsigned long all_mask) +{ + return ((sline->flag & all_mask) != all_mask || sline->lost_head); +} + +static unsigned long line_common_diff(struct sline *sline, unsigned long all_mask) +{ + /* + * 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 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) { + common_removes &= ll->parent_map; + } + } + return common_adds & common_removes; +} + +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<lost_head; + while (ll) { + for (j = 0; j < num_parent; j++) { + if (ll->parent_map & (1UL<line); + ll = ll->next; + } + for (j = 0; j < num_parent; j++) { + if ((1UL<flag) + putchar(' '); + else + putchar('+'); + } + printf("%.*s\n", sl->len, sl->bol); + } + } +} + +int show_combined_diff(struct combine_diff_path *elem, int num_parent, + int dense, const char *header, int show_empty) +{ + unsigned long size, cnt, lno; + char *result, *cp, *ep; + struct sline *sline; /* survived lines */ + int i, show_hunks, shown_header = 0; + char ourtmp_buf[TMPPATHLEN]; + char *ourtmp = ourtmp_buf; + + /* Read the result of merge first */ + 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') + cnt++; + } + if (result[size-1] != '\n') + cnt++; /* incomplete line */ + + sline = xcalloc(cnt, sizeof(*sline)); + ep = result; + sline[0].bol = result; + for (lno = 0, cp = result; cp - result < size; cp++) { + if (*cp == '\n') { + sline[lno].lost_tail = &sline[lno].lost_head; + sline[lno].len = cp - sline[lno].bol; + sline[lno].flag = (1UL<parent_sha1[i], ourtmp, sline, cnt, i); + + show_hunks = make_hunks(sline, cnt, num_parent, dense); + + 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++) { + if (sline[i].lost_head) { + struct lline *ll = sline[i].lost_head; + while (ll) { + struct lline *tmp = ll; + ll = ll->next; + free(tmp); + } + } + } + free(sline); + return shown_header; +} + +int diff_tree_combined_merge(const unsigned char *sha1, + const char *header, + int show_empty_merge, int dense) +{ + struct commit *commit = lookup_commit(sha1); + struct diff_options diffopts; + struct commit_list *parents; + struct combine_diff_path *p, *paths = NULL; + int num_parent, i, num_paths; + + diff_setup(&diffopts); + diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; + 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); + paths = intersect_paths(paths, i, num_parent); + diff_flush(&diffopts); + } + + /* find out surviving paths */ + for (num_paths = 0, p = paths; p; p = p->next) { + if (p->len) + num_paths++; + } + if (num_paths || show_empty_merge) { + for (p = paths; p; p = p->next) { + if (!p->len) + continue; + if (show_combined_diff(p, num_parent, dense, header, + show_empty_merge)) + header = NULL; + } + } + + /* Clean things up */ + while (paths) { + struct combine_diff_path *tmp = paths; + paths = paths->next; + free(tmp); + } + return 0; +} diff --git a/diff-files.c b/diff-files.c index 6c0696c3..4a30c56f 100644 --- a/diff-files.c +++ b/diff-files.c @@ -7,12 +7,14 @@ #include "diff.h" static const char diff_files_usage[] = -"git-diff-files [-q] [-0/-1/2/3] [] [...]" +"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [] [...]" COMMON_DIFF_OPTIONS_HELP; static struct diff_options diff_options; static int silent = 0; static int diff_unmerged_stage = 2; +static int combine_merges = 0; +static int dense_combined_merges = 0; static void show_unmerge(const char *path) { @@ -66,6 +68,10 @@ int main(int argc, const char **argv) ; /* no-op */ else if (!strcmp(argv[1], "-s")) ; /* no-op */ + else if (!strcmp(argv[1], "-c")) + combine_merges = 1; + else if (!strcmp(argv[1], "--cc")) + dense_combined_merges = combine_merges = 1; else { int diff_opt_cnt; diff_opt_cnt = diff_opt_parse(&diff_options, @@ -82,6 +88,9 @@ int main(int argc, const char **argv) } argv++; argc--; } + if (combine_merges) { + diff_options.output_format = DIFF_FORMAT_PATCH; + } /* Find the directory, and set up the pathspec */ pathspec = get_pathspec(prefix, argv + 1); @@ -108,14 +117,35 @@ int main(int argc, const char **argv) continue; if (ce_stage(ce)) { - show_unmerge(ce->name); + struct { + struct combine_diff_path p; + unsigned char fill[4][20]; + } combine; + + combine.p.next = NULL; + combine.p.len = ce_namelen(ce); + combine.p.path = xmalloc(combine.p.len + 1); + memcpy(combine.p.path, ce->name, combine.p.len); + combine.p.path[combine.p.len] = 0; + memset(combine.p.sha1, 0, 100); + while (i < entries) { struct cache_entry *nce = active_cache[i]; + int stage; if (strcmp(ce->name, nce->name)) break; + + /* Stage #2 (ours) is the first parent, + * stage #3 (theirs) is the second. + */ + stage = ce_stage(nce); + if (2 <= stage) + memcpy(combine.p.parent_sha1[stage-2], + nce->sha1, 20); + /* diff against the proper unmerged stage */ - if (ce_stage(nce) == diff_unmerged_stage) + if (stage == diff_unmerged_stage) ce = nce; i++; } @@ -123,10 +153,19 @@ int main(int argc, const char **argv) * Compensate for loop update */ i--; + + if (combine_merges) { + show_combined_diff(&combine.p, 2, + dense_combined_merges, + NULL, 0); + continue; + } + /* * Show the diff for the 'ce' if we found the one * from the desired stage. */ + show_unmerge(ce->name); if (ce_stage(ce) != diff_unmerged_stage) continue; } diff --git a/diff-tree.c b/diff-tree.c index 44bc2381..6593a692 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -6,6 +6,9 @@ static int show_root_diff = 0; static int no_commit_id = 0; static int verbose_header = 0; static int ignore_merges = 1; +static int show_empty_combined = 0; +static int combine_merges = 0; +static int dense_combined_merges = 0; static int read_stdin = 0; static const char *header = NULL; @@ -80,9 +83,13 @@ static const char *generate_header(const unsigned char *commit_sha1, offset = sprintf(this_header, "%s%s ", header_prefix, diff_unique_abbrev(commit_sha1, abbrev)); - offset += sprintf(this_header + offset, "(from %s)\n", - parent_sha1 ? - diff_unique_abbrev(parent_sha1, abbrev) : "root"); + if (commit_sha1 != parent_sha1) + offset += sprintf(this_header + offset, "(from %s)\n", + parent_sha1 + ? diff_unique_abbrev(parent_sha1, abbrev) + : "root"); + else + offset += sprintf(this_header + offset, "(from parents)\n"); offset += pretty_print_commit(commit_format, commit, len, this_header + offset, sizeof(this_header) - offset, abbrev); @@ -109,8 +116,16 @@ static int diff_tree_commit(const unsigned char *commit_sha1) } /* More than one parent? */ - if (ignore_merges && commit->parents && commit->parents->next) - return 0; + if (commit->parents && commit->parents->next) { + if (ignore_merges) + return 0; + else if (combine_merges) { + header = generate_header(sha1, sha1, commit); + return diff_tree_combined_merge(sha1, header, + show_empty_combined, + dense_combined_merges); + } + } for (parents = commit->parents; parents; parents = parents->next) { struct commit *parent = parents->item; @@ -153,7 +168,7 @@ static int diff_tree_stdin(char *line) } static const char diff_tree_usage[] = -"git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] [-r] [--root] " +"git-diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] " "[] [] [...]\n" " -r diff recursively\n" " --root include the initial commit as diff against /dev/null\n" @@ -216,6 +231,14 @@ int main(int argc, const char **argv) ignore_merges = 0; continue; } + if (!strcmp(arg, "-c")) { + combine_merges = 1; + continue; + } + if (!strcmp(arg, "--cc")) { + dense_combined_merges = combine_merges = 1; + continue; + } if (!strcmp(arg, "-v")) { verbose_header = 1; header_prefix = "diff-tree "; @@ -244,6 +267,12 @@ int main(int argc, const char **argv) if (diff_options.output_format == DIFF_FORMAT_PATCH) diff_options.recursive = 1; + if (combine_merges) { + diff_options.output_format = DIFF_FORMAT_PATCH; + show_empty_combined = !ignore_merges; + ignore_merges = 0; + } + diff_tree_setup_paths(get_pathspec(prefix, argv)); diff_setup_done(&diff_options); diff --git a/diff.h b/diff.h index 122c8143..9a0169c0 100644 --- a/diff.h +++ b/diff.h @@ -56,6 +56,19 @@ extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2, extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt); +struct combine_diff_path { + struct combine_diff_path *next; + int len; + char *path; + unsigned char sha1[20]; + unsigned char parent_sha1[FLEX_ARRAY][20]; +}; + +int show_combined_diff(struct combine_diff_path *elem, int num_parent, + int dense, const char *header, int show_empty); + +extern int diff_tree_combined_merge(const unsigned char *sha1, const char *, int, int); + extern void diff_addremove(struct diff_options *, int addremove, unsigned mode, diff --git a/rev-list.c b/rev-list.c index 334713ab..0b142c1a 100644 --- a/rev-list.c +++ b/rev-list.c @@ -21,6 +21,7 @@ static const char rev_list_usage[] = " --min-age=epoch\n" " --sparse\n" " --no-merges\n" +" --remove-empty\n" " --all\n" " ordering output:\n" " --merge-order [ --show-breaks ]\n" @@ -54,6 +55,7 @@ static int stop_traversal = 0; static int topo_order = 0; static int no_merges = 0; static const char **paths = NULL; +static int remove_empty_trees = 0; static void show_commit(struct commit *commit) { @@ -424,14 +426,33 @@ static void mark_edges_uninteresting(struct commit_list *list) } } -static int is_different = 0; +#define TREE_SAME 0 +#define TREE_NEW 1 +#define TREE_DIFFERENT 2 +static int tree_difference = TREE_SAME; static void file_add_remove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, const char *base, const char *path) { - is_different = 1; + int diff = TREE_DIFFERENT; + + /* + * Is it an add of a new file? It means that + * the old tree didn't have it at all, so we + * will turn "TREE_SAME" -> "TREE_NEW", but + * leave any "TREE_DIFFERENT" alone (and if + * it already was "TREE_NEW", we'll keep it + * "TREE_NEW" of course). + */ + if (addremove == '+') { + diff = tree_difference; + if (diff != TREE_SAME) + return; + diff = TREE_NEW; + } + tree_difference = diff; } static void file_change(struct diff_options *options, @@ -440,7 +461,7 @@ static void file_change(struct diff_options *options, const unsigned char *new_sha1, const char *base, const char *path) { - is_different = 1; + tree_difference = TREE_DIFFERENT; } static struct diff_options diff_opt = { @@ -449,12 +470,16 @@ static struct diff_options diff_opt = { .change = file_change, }; -static int same_tree(struct tree *t1, struct tree *t2) +static int compare_tree(struct tree *t1, struct tree *t2) { - is_different = 0; + if (!t1) + return TREE_NEW; + if (!t2) + return TREE_DIFFERENT; + tree_difference = TREE_SAME; if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0) - return 0; - return !is_different; + return TREE_DIFFERENT; + return tree_difference; } static int same_tree_as_empty(struct tree *t1) @@ -474,28 +499,55 @@ static int same_tree_as_empty(struct tree *t1) empty.buf = ""; empty.size = 0; - is_different = 0; + tree_difference = 0; retval = diff_tree(&empty, &real, "", &diff_opt); free(tree); - return retval >= 0 && !is_different; + return retval >= 0 && !tree_difference; } -static struct commit *try_to_simplify_merge(struct commit *commit, struct commit_list *parent) +static void try_to_simplify_commit(struct commit *commit) { + struct commit_list **pp, *parent; + if (!commit->tree) - return NULL; + return; - while (parent) { + if (!commit->parents) { + if (!same_tree_as_empty(commit->tree)) + commit->object.flags |= TREECHANGE; + return; + } + + pp = &commit->parents; + while ((parent = *pp) != NULL) { struct commit *p = parent->item; - parent = parent->next; + + if (p->object.flags & UNINTERESTING) { + pp = &parent->next; + continue; + } + parse_commit(p); - if (!p->tree) + switch (compare_tree(p->tree, commit->tree)) { + case TREE_SAME: + parent->next = NULL; + commit->parents = parent; + return; + + case TREE_NEW: + if (remove_empty_trees && same_tree_as_empty(p->tree)) { + *pp = parent->next; + continue; + } + /* fallthrough */ + case TREE_DIFFERENT: + pp = &parent->next; continue; - if (same_tree(commit->tree, p->tree)) - return p; + } + die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1)); } - return NULL; + commit->object.flags |= TREECHANGE; } static void add_parents_to_list(struct commit *commit, struct commit_list **list) @@ -531,20 +583,14 @@ static void add_parents_to_list(struct commit *commit, struct commit_list **list } /* - * Ok, the commit wasn't uninteresting. If it - * is a merge, try to find the parent that has - * no differences in the path set if one exists. + * Ok, the commit wasn't uninteresting. Try to + * simplify the commit history and find the parent + * that has no differences in the path set if one exists. */ - if (paths && parent && parent->next) { - struct commit *preferred; - - preferred = try_to_simplify_merge(commit, parent); - if (preferred) { - parent->item = preferred; - parent->next = NULL; - } - } + if (paths) + try_to_simplify_commit(commit); + parent = commit->parents; while (parent) { struct commit *p = parent->item; @@ -558,33 +604,6 @@ static void add_parents_to_list(struct commit *commit, struct commit_list **list } } -static void compress_list(struct commit_list *list) -{ - while (list) { - struct commit *commit = list->item; - struct commit_list *parent = commit->parents; - list = list->next; - - if (!parent) { - if (!same_tree_as_empty(commit->tree)) - commit->object.flags |= TREECHANGE; - continue; - } - - /* - * Exactly one parent? Check if it leaves the tree - * unchanged - */ - if (!parent->next) { - struct tree *t1 = commit->tree; - struct tree *t2 = parent->item->tree; - if (!t1 || !t2 || same_tree(t1, t2)) - continue; - } - commit->object.flags |= TREECHANGE; - } -} - static struct commit_list *limit_list(struct commit_list *list) { struct commit_list *newlist = NULL; @@ -614,8 +633,6 @@ static struct commit_list *limit_list(struct commit_list *list) } if (tree_objects) mark_edges_uninteresting(newlist); - if (paths && dense) - compress_list(newlist); if (bisect_list) newlist = find_bisection(newlist); return newlist; @@ -808,6 +825,10 @@ int main(int argc, const char **argv) dense = 0; continue; } + if (!strcmp(arg, "--remove-empty")) { + remove_empty_trees = 1; + continue; + } if (!strcmp(arg, "--")) { i++; break; diff --git a/rev-parse.c b/rev-parse.c index a1aa8639..d2f08643 100644 --- a/rev-parse.c +++ b/rev-parse.c @@ -310,7 +310,9 @@ int main(int argc, char **argv) } if (verify) die("Needed a single revision"); - if (lstat(arg, &st) < 0) + if ((filter & DO_REVS) && + (filter & DO_NONFLAGS) && /* !def && */ + lstat(arg, &st) < 0) die("'%s': %s", arg, strerror(errno)); as_is = 1; show_file(arg);