From af3feefa1d057713c2277cc1543d438e60c65306 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 24 Jan 2006 01:22:04 -0800 Subject: diff-tree -c: show a merge commit a bit more sensibly. A new option '-c' to diff-tree changes the way a merge commit is displayed when generating a patch output. It shows a "combined diff" (hence the option letter 'c'), which looks like this: $ git-diff-tree --pretty -c -p fec9ebf1 | head -n 18 diff-tree fec9ebf... (from parents) Merge: 0620db3... 8a263ae... Author: Junio C Hamano Date: Sun Jan 15 22:25:35 2006 -0800 Merge fixes up to GIT 1.1.3 diff --combined describe.c @@@ +98,7 @@@ return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1; } - static void describe(char *arg) - static void describe(struct commit *cmit, int last_one) ++ static void describe(char *arg, int last_one) { + unsigned char sha1[20]; + struct commit *cmit; There are a few things to note about this feature: - The '-c' option implies '-p'. It also implies '-m' halfway in the sense that "interesting" merges are shown, but not all merges. - When a blob matches one of the parents, we do not show a diff for that path at all. For a merge commit, this option shows paths with real file-level merge (aka "interesting things"). - As a concequence of the above, an "uninteresting" merge is not shown at all. You can use '-m' in addition to '-c' to show the commit log for such a merge, but there will be no combined diff output. - Unlike "gitk", the output is monochrome. A '-' character in the nth column means the line is from the nth parent and does not appear in the merge result (i.e. removed from that parent's version). A '+' character in the nth column means the line appears in the merge result, and the nth parent does not have that line (i.e. added by the merge itself or inherited from another parent). The above example output shows that the function signature was changed from either parents (hence two "-" lines and a "++" line), and "unsigned char sha1[20]", prefixed by a " +", was inherited from the first parent. The code as sent to the list was buggy in few corner cases, which I have fixed since then. It does not bother to keep track of and show the line numbers from parent commits, which it probably should. Signed-off-by: Junio C Hamano --- diff.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'diff.h') diff --git a/diff.h b/diff.h index 5696f2aff0..081234cb97 100644 --- a/diff.h +++ b/diff.h @@ -56,6 +56,8 @@ 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); +extern int diff_tree_combined_merge(const unsigned char *sha1, const char *, int); + extern void diff_addremove(struct diff_options *, int addremove, unsigned mode, -- cgit v1.3 From d8f4790e6fe7a9d94468ea83d1370643604d43e5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 24 Jan 2006 01:22:04 -0800 Subject: diff-tree --cc: denser combined diff output for a merge commit. Building on the previous '-c' (combined) option, '--cc' option squelches the output further by omitting hunks that consist of difference with solely one parent. Signed-off-by: Junio C Hamano --- combine-diff.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- diff-tree.c | 10 +++++++-- diff.h | 2 +- 3 files changed, 73 insertions(+), 8 deletions(-) (limited to 'diff.h') diff --git a/combine-diff.c b/combine-diff.c index 669002349a..062ed8a7bf 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -278,7 +278,25 @@ static int interesting(struct sline *sline, unsigned long all_mask) return ((sline->flag & all_mask) != all_mask || sline->lost_head); } -static void make_hunks(struct sline *sline, unsigned long cnt, int num_parent) +static unsigned long line_diff_parents(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. + */ + unsigned long parents = (sline->flag ^ all_mask); + if (sline->lost_head) { + struct lline *ll; + for (ll = sline->lost_head; ll; ll = ll->next) + parents |= ll->parent_map; + } + return parents & all_mask; +} + +static void make_hunks(struct sline *sline, unsigned long cnt, + int num_parent, int dense) { unsigned long all_mask = (1UL<parent_sha1[i], ourtmp, sline, cnt, i); - make_hunks(sline, cnt, num_parent); + make_hunks(sline, cnt, num_parent, dense); dump_sline(sline, cnt, num_parent); unlink(ourtmp); @@ -410,7 +468,8 @@ static void show_combined_diff(struct path_list *elem, int num_parent) } int diff_tree_combined_merge(const unsigned char *sha1, - const char *header, int show_empty_merge) + const char *header, + int show_empty_merge, int dense) { struct commit *commit = lookup_commit(sha1); struct diff_options diffopts; @@ -455,7 +514,7 @@ int diff_tree_combined_merge(const unsigned char *sha1, else printf("%s", p->path); putchar('\n'); - show_combined_diff(p, num_parent); + show_combined_diff(p, num_parent, dense); } } diff --git a/diff-tree.c b/diff-tree.c index 0c689360ee..99c580cf75 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -8,6 +8,7 @@ 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; @@ -121,7 +122,8 @@ static int diff_tree_commit(const unsigned char *commit_sha1) header = generate_header(sha1, sha1, commit->buffer); return diff_tree_combined_merge(sha1, header, - show_empty_combined); + show_empty_combined, + dense_combined_merges); } } @@ -168,7 +170,7 @@ static int diff_tree_stdin(char *line) } static const char diff_tree_usage[] = -"git-diff-tree [--stdin] [-m] [-c] [-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" @@ -235,6 +237,10 @@ int main(int argc, const char **argv) 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 "; diff --git a/diff.h b/diff.h index 081234cb97..ab0d47b982 100644 --- a/diff.h +++ b/diff.h @@ -56,7 +56,7 @@ 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); -extern int diff_tree_combined_merge(const unsigned char *sha1, const char *, int); +extern int diff_tree_combined_merge(const unsigned char *sha1, const char *, int, int); extern void diff_addremove(struct diff_options *, int addremove, -- cgit v1.3 From ea726d02e9677a66586d7ffebe97f112ab6dab33 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 28 Jan 2006 00:03:38 -0800 Subject: diff-files: -c and --cc options. This ports the "combined diff" to diff-files so that differences to the working tree files since stage 2 and stage 3 are shown the same way as combined diff output from diff-tree for the merge commit would be shown if the current working tree files are committed. Signed-off-by: Junio C Hamano --- combine-diff.c | 66 ++++++++++++++++++++++++++++++++++++++++------------------ diff-files.c | 45 ++++++++++++++++++++++++++++++++++++--- diff.h | 11 ++++++++++ 3 files changed, 99 insertions(+), 23 deletions(-) (limited to 'diff.h') diff --git a/combine-diff.c b/combine-diff.c index df52fa20ec..243f96775a 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; @@ -532,18 +523,52 @@ static void dump_sline(struct sline *sline, int cnt, int num_parent) } } -static int show_combined_diff(struct path_list *elem, int num_parent, - int dense, const char *header, int show_empty) +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[TMPPATHLEN]; + char ourtmp_buf[TMPPATHLEN]; + char *ourtmp = ourtmp_buf; /* Read the result of merge first */ - result = grab_blob(elem->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') @@ -589,7 +614,8 @@ static int show_combined_diff(struct path_list *elem, int num_parent, putchar('\n'); dump_sline(sline, cnt, num_parent); } - unlink(ourtmp); + if (ourtmp == ourtmp_buf) + unlink(ourtmp); free(result); for (i = 0; i < cnt; i++) { @@ -613,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); @@ -654,7 +680,7 @@ int diff_tree_combined_merge(const unsigned char *sha1, /* Clean things up */ while (paths) { - struct path_list *tmp = paths; + struct combine_diff_path *tmp = paths; paths = paths->next; free(tmp); } diff --git a/diff-files.c b/diff-files.c index 6c0696c34f..4a30c56f84 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.h b/diff.h index ab0d47b982..539bd2f5ce 100644 --- a/diff.h +++ b/diff.h @@ -56,6 +56,17 @@ 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 *, -- cgit v1.3 From 46a6c2620ba421397eec627b8eb18eb530e694fc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 25 Jan 2006 01:03:18 -0800 Subject: abbrev cleanup: use symbolic constants The minimum length of abbreviated object name was hardcoded in different places to be 4, risking inconsistencies in the future. Also there were three different "default abbreviation precision". Use two C preprocessor symbols to clean up this mess. Signed-off-by: Junio C Hamano --- cache.h | 3 +++ describe.c | 3 +-- diff.c | 4 ++-- diff.h | 3 --- sha1_name.c | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) (limited to 'diff.h') diff --git a/cache.h b/cache.h index 1e8e27f7b3..bdbe2d683e 100644 --- a/cache.h +++ b/cache.h @@ -221,6 +221,9 @@ extern int has_pack_file(const unsigned char *sha1); extern int has_pack_index(const unsigned char *sha1); /* Convert to/from hex/sha1 representation */ +#define MINIMUM_ABBREV 4 +#define DEFAULT_ABBREV 7 + extern int get_sha1(const char *str, unsigned char *sha1); extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ diff --git a/describe.c b/describe.c index 4866510eaf..ff65742615 100644 --- a/describe.c +++ b/describe.c @@ -11,7 +11,6 @@ static const char describe_usage[] = static int all = 0; /* Default to annotated tags only */ static int tags = 0; /* But allow any tags if --tags is specified */ -#define DEFAULT_ABBREV 8 /* maybe too many */ static int abbrev = DEFAULT_ABBREV; static int names = 0, allocs = 0; @@ -155,7 +154,7 @@ int main(int argc, char **argv) tags = 1; else if (!strncmp(arg, "--abbrev=", 9)) { abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev < 4 || 40 <= abbrev) + if (abbrev < MINIMUM_ABBREV || 40 <= abbrev) abbrev = DEFAULT_ABBREV; } else diff --git a/diff.c b/diff.c index 17d68fa699..69767b9de9 100644 --- a/diff.c +++ b/diff.c @@ -723,7 +723,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (memcmp(one->sha1, two->sha1, 20)) { char one_sha1[41]; - int abbrev = o->full_index ? 40 : DIFF_DEFAULT_INDEX_ABBREV; + int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; memcpy(one_sha1, sha1_to_hex(one->sha1), 41); len += snprintf(msg + len, sizeof(msg) - len, @@ -846,7 +846,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--find-copies-harder")) options->find_copies_harder = 1; else if (!strcmp(arg, "--abbrev")) - options->abbrev = DIFF_DEFAULT_ABBREV; + options->abbrev = DEFAULT_ABBREV; else if (!strncmp(arg, "--abbrev=", 9)) options->abbrev = strtoul(arg + 9, NULL, 10); else diff --git a/diff.h b/diff.h index 5696f2aff0..122c8143a3 100644 --- a/diff.h +++ b/diff.h @@ -88,9 +88,6 @@ extern int diff_setup_done(struct diff_options *); #define DIFF_PICKAXE_ALL 1 -#define DIFF_DEFAULT_INDEX_ABBREV 7 /* hex digits */ -#define DIFF_DEFAULT_ABBREV 7 /* hex digits */ - extern void diffcore_std(struct diff_options *); extern void diffcore_std_no_resolve(struct diff_options *); diff --git a/sha1_name.c b/sha1_name.c index e18a96d772..ba0747c84d 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -155,7 +155,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1, char canonical[40]; unsigned char res[20]; - if (len < 4) + if (len < MINIMUM_ABBREV) return -1; memset(res, 0, 20); memset(canonical, 'x', 40); -- cgit v1.3