#include "sha1-array.h"
#include "revision.h"
+static int compare_paths(const struct combine_diff_path *one,
+ const struct diff_filespec *two)
+{
+ if (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))
+ return strcmp(one->path, two->path);
+
+ return base_name_compare(one->path, strlen(one->path), one->mode,
+ two->path, strlen(two->path), two->mode);
+}
+
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;
i = 0;
while ((p = *tail) != NULL) {
cmp = ((i >= q->nr)
- ? -1 : strcmp(p->path, q->queue[i]->two->path));
+ ? -1 : compare_paths(p, q->queue[i]->two));
if (cmp < 0) {
/* p->path not in q->queue[]; drop it */
/* find set of paths that every parent touches */
-static struct combine_diff_path *find_paths(const unsigned char *sha1,
+static struct combine_diff_path *find_paths_generic(const unsigned char *sha1,
const struct sha1_array *parents, struct diff_options *opt)
{
struct combine_diff_path *paths = NULL;
/* tell diff_tree to emit paths in sorted (=tree) order */
opt->orderfile = NULL;
+ /* D(A,P1...Pn) = D(A,P1) ^ ... ^ D(A,Pn) (wrt paths) */
for (i = 0; i < num_parent; i++) {
/*
* show stat against the first parent even when doing
}
+/*
+ * find set of paths that everybody touches, assuming diff is run without
+ * rename/copy detection, etc, comparing all trees simultaneously (= faster).
+ */
+static struct combine_diff_path *find_paths_multitree(
+ const unsigned char *sha1, const struct sha1_array *parents,
+ struct diff_options *opt)
+{
+ int i, nparent = parents->nr;
+ const unsigned char **parents_sha1;
+ struct combine_diff_path paths_head;
+ struct strbuf base;
+
+ parents_sha1 = xmalloc(nparent * sizeof(parents_sha1[0]));
+ for (i = 0; i < nparent; i++)
+ parents_sha1[i] = parents->sha1[i];
+
+ /* fake list head, so worker can assume it is non-NULL */
+ paths_head.next = NULL;
+
+ strbuf_init(&base, PATH_MAX);
+ diff_tree_paths(&paths_head, sha1, parents_sha1, nparent, &base, opt);
+
+ strbuf_release(&base);
+ free(parents_sha1);
+ return paths_head.next;
+}
+
+
void diff_tree_combined(const unsigned char *sha1,
const struct sha1_array *parents,
int dense,
struct diff_options diffopts;
struct combine_diff_path *p, *paths;
int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
+ int need_generic_pathscan;
/* nothing to do, if no parents */
if (!num_parent)
if (show_log_first) {
show_log(rev);
- if (rev->verbose_header && opt->output_format)
+ if (rev->verbose_header && opt->output_format &&
+ opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
+ !commit_format_is_empty(rev->commit_format))
printf("%s%c", diff_line_prefix(opt),
opt->line_termination);
}
/* find set of paths that everybody touches
*
- * NOTE find_paths() also handles --stat, as it computes
- * diff(sha1,parent_i) for all i to do the job, specifically
- * for parent0.
+ * NOTE
+ *
+ * Diffcore transformations are bound to diff_filespec and logic
+ * comparing two entries - i.e. they do not apply directly to combine
+ * diff.
+ *
+ * If some of such transformations is requested - we launch generic
+ * path scanning, which works significantly slower compared to
+ * simultaneous all-trees-in-one-go scan in find_paths_multitree().
+ *
+ * TODO some of the filters could be ported to work on
+ * combine_diff_paths - i.e. all functionality that skips paths, so in
+ * theory, we could end up having only multitree path scanning.
+ *
+ * NOTE please keep this semantically in sync with diffcore_std()
*/
- paths = find_paths(sha1, parents, &diffopts);
+ need_generic_pathscan = opt->skip_stat_unmatch ||
+ DIFF_OPT_TST(opt, FOLLOW_RENAMES) ||
+ opt->break_opt != -1 ||
+ opt->detect_rename ||
+ opt->pickaxe ||
+ opt->filter;
+
+
+ if (need_generic_pathscan) {
+ /*
+ * NOTE generic case also handles --stat, as it computes
+ * diff(sha1,parent_i) for all i to do the job, specifically
+ * for parent0.
+ */
+ paths = find_paths_generic(sha1, parents, &diffopts);
+ }
+ else {
+ int stat_opt;
+ paths = find_paths_multitree(sha1, parents, &diffopts);
+
+ /*
+ * show stat against the first parent even
+ * when doing combined diff.
+ */
+ stat_opt = (opt->output_format &
+ (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+ if (stat_opt) {
+ diffopts.output_format = stat_opt;
+
+ diff_tree_sha1(parents->sha1[0], sha1, "", &diffopts);
+ diffcore_std(&diffopts);
+ if (opt->orderfile)
+ diffcore_order(opt->orderfile);
+ diff_flush(&diffopts);
+ }
+ }
/* find out number of surviving paths */
for (num_paths = 0, p = paths; p; p = p->next)