tree-diff: no need to manually verify that there is no mode change for a path
[gitweb.git] / combine-diff.c
index d7692d7073eb4d8ba79d86796c56a7e22fcc3110..1732dfd110664e06924f465bdd50bc7a1d83ec2c 100644 (file)
 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, *pprev, *ptmp;
+       struct combine_diff_path *p, **tail = &curr;
        int i, cmp;
 
        if (!n) {
-               struct combine_diff_path *list = NULL, **tail = &list;
                for (i = 0; i < q->nr; i++) {
                        int len;
                        const char *path;
@@ -31,7 +30,6 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                        p->path = (char *) &(p->parent[num_parent]);
                        memcpy(p->path, path, len);
                        p->path[len] = 0;
-                       p->len = len;
                        p->next = NULL;
                        memset(p->parent, 0,
                               sizeof(p->parent[0]) * num_parent);
@@ -44,35 +42,27 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                        *tail = p;
                        tail = &p->next;
                }
-               return list;
+               return curr;
        }
 
        /*
-        * NOTE paths are coming sorted here (= in tree order)
+        * paths in curr (linked list) and q->queue[] (array) are
+        * both sorted in the tree order.
         */
-
-       pprev = NULL;
-       p = curr;
        i = 0;
+       while ((p = *tail) != NULL) {
+               cmp = ((i >= q->nr)
+                      ? -1 : strcmp(p->path, q->queue[i]->two->path));
 
-       while (1) {
-               if (!p)
-                       break;
-
-               cmp = (i >= q->nr) ? -1
-                                  : strcmp(p->path, q->queue[i]->two->path);
                if (cmp < 0) {
-                       if (pprev)
-                               pprev->next = p->next;
-                       ptmp = p;
-                       p = p->next;
-                       free(ptmp);
-                       if (curr == ptmp)
-                               curr = p;
+                       /* p->path not in q->queue[]; drop it */
+                       *tail = p->next;
+                       free(p);
                        continue;
                }
 
                if (cmp > 0) {
+                       /* q->queue[i] not in p->path; skip it */
                        i++;
                        continue;
                }
@@ -81,8 +71,7 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                p->parent[n].mode = q->queue[i]->one->mode;
                p->parent[n].status = q->queue[i]->status;
 
-               pprev = p;
-               p = p->next;
+               tail = &p->next;
                i++;
        }
        return curr;
@@ -1234,8 +1223,6 @@ void show_combined_diff(struct combine_diff_path *p,
 {
        struct diff_options *opt = &rev->diffopt;
 
-       if (!p->len)
-               return;
        if (opt->output_format & (DIFF_FORMAT_RAW |
                                  DIFF_FORMAT_NAME |
                                  DIFF_FORMAT_NAME_STATUS))
@@ -1299,11 +1286,8 @@ static void handle_combined_callback(struct diff_options *opt,
        q.queue = xcalloc(num_paths, sizeof(struct diff_filepair *));
        q.alloc = num_paths;
        q.nr = num_paths;
-       for (i = 0, p = paths; p; p = p->next) {
-               if (!p->len)
-                       continue;
+       for (i = 0, p = paths; p; p = p->next)
                q.queue[i++] = combined_pair(p, num_parent);
-       }
        opt->format_callback(&q, opt, opt->format_callback_data);
        for (i = 0; i < num_paths; i++)
                free_combined_pair(q.queue[i]);
@@ -1317,64 +1301,92 @@ static const char *path_path(void *obj)
        return path->path;
 }
 
-void diff_tree_combined(const unsigned char *sha1,
-                       const struct sha1_array *parents,
-                       int dense,
-                       struct rev_info *rev)
+
+/* find set of paths that every parent touches */
+static struct combine_diff_path *find_paths(const unsigned char *sha1,
+       const struct sha1_array *parents, struct diff_options *opt)
 {
-       struct diff_options *opt = &rev->diffopt;
-       struct diff_options diffopts;
-       struct combine_diff_path *p, *paths = NULL;
-       int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
+       struct combine_diff_path *paths = NULL;
+       int i, num_parent = parents->nr;
 
-       diffopts = *opt;
-       copy_pathspec(&diffopts.pathspec, &opt->pathspec);
-       diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       DIFF_OPT_SET(&diffopts, RECURSIVE);
-       DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
+       int output_format = opt->output_format;
+       const char *orderfile = opt->orderfile;
+
+       opt->output_format = DIFF_FORMAT_NO_OUTPUT;
        /* tell diff_tree to emit paths in sorted (=tree) order */
-       diffopts.orderfile = NULL;
+       opt->orderfile = NULL;
 
-       show_log_first = !!rev->loginfo && !rev->no_commit_id;
-       needsep = 0;
-       /* find set of paths that everybody touches */
        for (i = 0; i < num_parent; i++) {
-               /* show stat against the first parent even
-                * when doing combined diff.
+               /*
+                * show stat against the first parent even when doing
+                * combined diff.
                 */
-               int stat_opt = (opt->output_format &
+               int stat_opt = (output_format &
                                (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
                if (i == 0 && stat_opt)
-                       diffopts.output_format = stat_opt;
+                       opt->output_format = stat_opt;
                else
-                       diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
-               diff_tree_sha1(parents->sha1[i], sha1, "", &diffopts);
-               diffcore_std(&diffopts);
+                       opt->output_format = DIFF_FORMAT_NO_OUTPUT;
+               diff_tree_sha1(parents->sha1[i], sha1, "", opt);
+               diffcore_std(opt);
                paths = intersect_paths(paths, i, num_parent);
 
-               if (show_log_first && i == 0) {
-                       show_log(rev);
-
-                       if (rev->verbose_header && opt->output_format)
-                               printf("%s%c", diff_line_prefix(opt),
-                                      opt->line_termination);
-               }
-
                /* if showing diff, show it in requested order */
-               if (diffopts.output_format != DIFF_FORMAT_NO_OUTPUT &&
-                   opt->orderfile) {
-                       diffcore_order(opt->orderfile);
+               if (opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
+                   orderfile) {
+                       diffcore_order(orderfile);
                }
 
-               diff_flush(&diffopts);
+               diff_flush(opt);
        }
 
-       /* find out surviving paths */
-       for (num_paths = 0, p = paths; p; p = p->next) {
-               if (p->len)
-                       num_paths++;
+       opt->output_format = output_format;
+       opt->orderfile = orderfile;
+       return paths;
+}
+
+
+void diff_tree_combined(const unsigned char *sha1,
+                       const struct sha1_array *parents,
+                       int dense,
+                       struct rev_info *rev)
+{
+       struct diff_options *opt = &rev->diffopt;
+       struct diff_options diffopts;
+       struct combine_diff_path *p, *paths;
+       int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
+
+       /* nothing to do, if no parents */
+       if (!num_parent)
+               return;
+
+       show_log_first = !!rev->loginfo && !rev->no_commit_id;
+       needsep = 0;
+       if (show_log_first) {
+               show_log(rev);
+
+               if (rev->verbose_header && opt->output_format)
+                       printf("%s%c", diff_line_prefix(opt),
+                              opt->line_termination);
        }
 
+       diffopts = *opt;
+       copy_pathspec(&diffopts.pathspec, &opt->pathspec);
+       DIFF_OPT_SET(&diffopts, RECURSIVE);
+       DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
+
+       /* 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.
+        */
+       paths = find_paths(sha1, parents, &diffopts);
+
+       /* find out number of surviving paths */
+       for (num_paths = 0, p = paths; p; p = p->next)
+               num_paths++;
+
        /* order paths according to diffcore_order */
        if (opt->orderfile && num_paths) {
                struct obj_order *o;
@@ -1399,10 +1411,8 @@ void diff_tree_combined(const unsigned char *sha1,
                if (opt->output_format & (DIFF_FORMAT_RAW |
                                          DIFF_FORMAT_NAME |
                                          DIFF_FORMAT_NAME_STATUS)) {
-                       for (p = paths; p; p = p->next) {
-                               if (p->len)
-                                       show_raw_diff(p, num_parent, rev);
-                       }
+                       for (p = paths; p; p = p->next)
+                               show_raw_diff(p, num_parent, rev);
                        needsep = 1;
                }
                else if (opt->output_format &
@@ -1415,11 +1425,9 @@ void diff_tree_combined(const unsigned char *sha1,
                        if (needsep)
                                printf("%s%c", diff_line_prefix(opt),
                                       opt->line_termination);
-                       for (p = paths; p; p = p->next) {
-                               if (p->len)
-                                       show_patch_diff(p, num_parent, dense,
-                                                       0, rev);
-                       }
+                       for (p = paths; p; p = p->next)
+                               show_patch_diff(p, num_parent, dense,
+                                               0, rev);
                }
        }