"git diff(n)" without --base, --ours, etc. defaults to --cc, which usually is the same as -p unless you are in the middle of a conflicted merge, just like the shell script version. "git diff(n) blobA blobB path" complains and dies. "git diff(n) tree0 tree1 tree2...treeN" does combined diff that shows a merge of tree1..treeN to result in tree0. Giving "-c" option to any command that defaults to "--cc" turns off dense-combined flag. Signed-off-by: Junio C Hamano <junkio@xxxxxxx> --- * Comes on top of the previous "built-in diff" patch. Now I think this is more or less what Linus envisioned when he started converting the rev related option parsing to use setup_revisions(), including the exotic combinations the shell script "git diff" does not support: git diff(n) HEAD~10:Makefile Makefile ; blob vs any file git diff(n) HEAD~10:Makefile HEAD~20:Makefile ; two blobs git diff(n) v1.0.0 v1.0.0^1 v1.0.0^2 ; combine 'em The normal ones are also supported: git diff(n) -- Documentation ; index vs working git diff(n) --cached ; tree vs index git diff(n) HEAD ; tree vs working git diff(n) HEAD~10 HEAD~8 ; two trees git diff(n) HEAD:arch/i386 HEAD:arch/x86_64 ; two trees builtin-diff.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++--------- combine-diff.c | 47 ++++++++++++++++++++++++++++++----------------- diff.h | 2 ++ revision.c | 1 + 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/builtin-diff.c b/builtin-diff.c index 1968212..c543105 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -47,13 +47,17 @@ static int builtin_diff_files(struct rev } /* * Make sure there are NO revision (i.e. pending object) parameter, - * rev.max_count is reasonable (0 <= n <= 3), - * there is no other revision filtering parameters. + * specified rev.max_count is reasonable (0 <= n <= 3), and + * there is no other revision filtering parameter. */ if (revs->pending_objects || revs->min_age != -1 || - revs->max_age != -1) + revs->max_age != -1 || + 3 < revs->max_count) usage(builtin_diff_usage); + if (revs->max_count < 0 && + (revs->diffopt.output_format == DIFF_FORMAT_PATCH)) + revs->combine_merges = revs->dense_combined_merges = 1; /* * Backward compatibility wart - "diff-files -s" used to * defeat the common diff option "-s" which asked for @@ -178,9 +182,6 @@ static int builtin_diff_tree(struct rev_ int argc, const char **argv, struct object_list *ent) { - /* We saw two trees, ent[0] and ent[1]. - * unless ent[0] is unintesting, they are swapped - */ const unsigned char *(sha1[2]); int swap = 1; while (1 < argc) { @@ -191,6 +192,10 @@ static int builtin_diff_tree(struct rev_ usage(builtin_diff_usage); argv++; argc--; } + + /* We saw two trees, ent[0] and ent[1]. + * unless ent[0] is unintesting, they are swapped + */ if (ent[0].item->flags & UNINTERESTING) swap = 0; sha1[swap] = ent[0].item->sha1; @@ -200,6 +205,33 @@ static int builtin_diff_tree(struct rev_ return 0; } +static int builtin_diff_combined(struct rev_info *revs, + int argc, const char **argv, + struct object_list *ent, + int ents) +{ + const unsigned char (*parent)[20]; + int i; + + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--raw")) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + else + usage(builtin_diff_usage); + argv++; argc--; + } + if (!revs->dense_combined_merges && !revs->combine_merges) + revs->dense_combined_merges = revs->combine_merges = 1; + parent = xmalloc(ents * sizeof(*parent)); + /* Again, the revs are all reverse */ + for (i = 0; i < ents; i++) + memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20); + diff_tree_combined(parent[0], parent + 1, ents - 1, + revs->dense_combined_merges, revs); + return 0; +} + static void add_head(struct rev_info *revs) { unsigned char sha1[20]; @@ -215,7 +247,7 @@ static void add_head(struct rev_info *re int cmd_diff(int argc, const char **argv, char **envp) { struct rev_info rev; - struct object_list *list, ent[2]; + struct object_list *list, ent[100]; int ents = 0, blobs = 0, paths = 0; const char *path = NULL; struct blobinfo blob[2]; @@ -273,8 +305,9 @@ int cmd_diff(int argc, const char **argv if (!strcmp(obj->type, commit_type)) obj = &((struct commit *)obj)->tree->object; if (!strcmp(obj->type, tree_type)) { - if (2 <= ents) - die("more than two trees given: '%s'", name); + if (ARRAY_SIZE(ent) <= ents) + die("more than %d trees given: '%s'", + ARRAY_SIZE(ent), name); obj->flags |= flags; ent[ents].item = obj; ent[ents].name = name; @@ -316,6 +349,8 @@ int cmd_diff(int argc, const char **argv return builtin_diff_b_f(&rev, argc, argv, blob, path); break; case 2: + if (paths) + usage(builtin_diff_usage); return builtin_diff_blobs(&rev, argc, argv, blob); break; default: @@ -328,5 +363,7 @@ int cmd_diff(int argc, const char **argv return builtin_diff_index(&rev, argc, argv); else if (ents == 2) return builtin_diff_tree(&rev, argc, argv, ent); + else + return builtin_diff_combined(&rev, argc, argv, ent, ents); usage(builtin_diff_usage); } diff --git a/combine-diff.c b/combine-diff.c index ca36f5d..8a8fe38 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -831,15 +831,16 @@ void show_combined_diff(struct combine_d } } -void diff_tree_combined_merge(const unsigned char *sha1, - int dense, struct rev_info *rev) +void diff_tree_combined(const unsigned char *sha1, + const unsigned char parent[][20], + int num_parent, + int dense, + struct rev_info *rev) { struct diff_options *opt = &rev->diffopt; - 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; + int i, num_paths; int do_diffstat; do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT || @@ -849,17 +850,8 @@ void diff_tree_combined_merge(const unsi 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; + for (i = 0; i < num_parent; i++) { /* show stat against the first parent even * when doing combined diff. */ @@ -867,8 +859,7 @@ void diff_tree_combined_merge(const unsi diffopts.output_format = DIFF_FORMAT_DIFFSTAT; else diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; - diff_tree_sha1(parent->object.sha1, commit->object.sha1, "", - &diffopts); + diff_tree_sha1(parent[i], sha1, "", &diffopts); diffcore_std(&diffopts); paths = intersect_paths(paths, i, num_parent); @@ -907,3 +898,25 @@ void diff_tree_combined_merge(const unsi free(tmp); } } + +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); +} diff --git a/diff.h b/diff.h index 7150b90..b3b2c4d 100644 --- a/diff.h +++ b/diff.h @@ -75,6 +75,8 @@ #define combine_diff_path_size(n, l) \ extern void show_combined_diff(struct combine_diff_path *elem, int num_parent, int dense, struct rev_info *); +extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev); + extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *); extern void diff_addremove(struct diff_options *, diff --git a/revision.c b/revision.c index f2a9f25..4381d45 100644 --- a/revision.c +++ b/revision.c @@ -664,6 +664,7 @@ int setup_revisions(int argc, const char } if (!strcmp(arg, "-c")) { revs->diff = 1; + revs->dense_combined_merges = 0; revs->combine_merges = 1; continue; } -- 1.3.1.ga0c5 - : send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html