combine-diff: support format_callback

This teaches combine-diff machinery to feed a combined merge to a callback function when DIFF_FORMAT_CALLBACK is specified. So far, format callback functions are not used for anything but 2-way diffs. A callback is given a diff_queue_struct, which is an array of diff_filepair. As its name suggests, a diff_filepair is a _pair_ of diff_filespec that represents a single preimage and a single postimage. Since "diff -c" is to compare N parents with a single merge result and filter out any paths whose result match one (or more) of the parent(s), its output has to be able to represent N preimages and 1 postimage. For this reason, a callback function that inspects a diff_filepair that results from this new infrastructure can and is expected to view the preimage side (i.e. pair->one) as an array of diff_filespec. Each element in the array, except for the last one, is marked with "has_more_entries" bit, so that the same callback function can be used for 2-way diffs and combined diffs. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Aug 19, 2011 at 23:32 UTC 25e5e2bf85c8a2c2c8945935267539940c11020d
2 files changed +70 -1
combine-diff.c
+69
@@ -970,6 +970,72 @@ void show_combined_diff(struct combine_diff_path *p,
970 show_patch_diff(p, num_parent, dense, rev);
971 }
972
973 +static void free_combined_pair(struct diff_filepair *pair)
974 +{
975 + free(pair->two);
976 + free(pair);
977 +}
978 +
979 +/*
980 + * A combine_diff_path expresses N parents on the LHS against 1 merge
981 + * result. Synthesize a diff_filepair that has N entries on the "one"
982 + * side and 1 entry on the "two" side.
983 + *
984 + * In the future, we might want to add more data to combine_diff_path
985 + * so that we can fill fields we are ignoring (most notably, size) here,
986 + * but currently nobody uses it, so this should suffice for now.
987 + */
988 +static struct diff_filepair *combined_pair(struct combine_diff_path *p,
989 + int num_parent)
990 +{
991 + int i;
992 + struct diff_filepair *pair;
993 + struct diff_filespec *pool;
994 +
995 + pair = xmalloc(sizeof(*pair));
996 + pool = xcalloc(num_parent + 1, sizeof(struct diff_filespec));
997 + pair->one = pool + 1;
998 + pair->two = pool;
999 +
1000 + for (i = 0; i < num_parent; i++) {
1001 + pair->one[i].path = p->path;
1002 + pair->one[i].mode = p->parent[i].mode;
1003 + hashcpy(pair->one[i].sha1, p->parent[i].sha1);
1004 + pair->one[i].sha1_valid = !is_null_sha1(p->parent[i].sha1);
1005 + pair->one[i].has_more_entries = 1;
1006 + }
1007 + pair->one[num_parent - 1].has_more_entries = 0;
1008 +
1009 + pair->two->path = p->path;
1010 + pair->two->mode = p->mode;
1011 + hashcpy(pair->two->sha1, p->sha1);
1012 + pair->two->sha1_valid = !is_null_sha1(p->sha1);
1013 + return pair;
1014 +}
1015 +
1016 +static void handle_combined_callback(struct diff_options *opt,
1017 + struct combine_diff_path *paths,
1018 + int num_parent,
1019 + int num_paths)
1020 +{
1021 + struct combine_diff_path *p;
1022 + struct diff_queue_struct q;
1023 + int i;
1024 +
1025 + q.queue = xcalloc(num_paths, sizeof(struct diff_filepair *));
1026 + q.alloc = num_paths;
1027 + q.nr = num_paths;
1028 + for (i = 0, p = paths; p; p = p->next) {
1029 + if (!p->len)
1030 + continue;
1031 + q.queue[i++] = combined_pair(p, num_parent);
1032 + }
1033 + opt->format_callback(&q, opt, opt->format_callback_data);
1034 + for (i = 0; i < num_paths; i++)
1035 + free_combined_pair(q.queue[i]);
1036 + free(q.queue);
1037 +}
1038 +
1039 void diff_tree_combined(const unsigned char *sha1,
1040 const unsigned char parent[][20],
1041 int num_parent,
@@ -1029,6 +1095,9 @@ void diff_tree_combined(const unsigned char *sha1,
1095 else if (opt->output_format &
1096 (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
1097 needsep = 1;
1098 + else if (opt->output_format & DIFF_FORMAT_CALLBACK)
1099 + handle_combined_callback(opt, paths, num_parent, num_paths);
1100 +
1101 if (opt->output_format & DIFF_FORMAT_PATCH) {
1102 if (needsep)
1103 putchar(opt->line_termination);
diffcore.h
+1 -1
@@ -45,7 +45,7 @@ struct diff_filespec {
45 unsigned dirty_submodule : 2; /* For submodules: its work tree is dirty */
46 #define DIRTY_SUBMODULE_UNTRACKED 1
47 #define DIRTY_SUBMODULE_MODIFIED 2
48 -
48 + unsigned has_more_entries : 1; /* only appear in combined diff */
49 struct userdiff_driver *driver;
50 /* data should be considered "binary"; -1 means "don't know yet" */
51 int is_binary;