Imported Upstream version 2.29.2
[platform/upstream/git.git] / combine-diff.c
index ad7752e..9228aeb 100644 (file)
@@ -10,7 +10,7 @@
 #include "log-tree.h"
 #include "refs.h"
 #include "userdiff.h"
-#include "sha1-array.h"
+#include "oid-array.h"
 #include "revision.h"
 
 static int compare_paths(const struct combine_diff_path *one,
@@ -23,11 +23,20 @@ static int compare_paths(const struct combine_diff_path *one,
                                 two->path, strlen(two->path), two->mode);
 }
 
-static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
+static int filename_changed(char status)
+{
+       return status == 'R' || status == 'C';
+}
+
+static struct combine_diff_path *intersect_paths(
+       struct combine_diff_path *curr,
+       int n,
+       int num_parent,
+       int combined_all_paths)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        struct combine_diff_path *p, **tail = &curr;
-       int i, cmp;
+       int i, j, cmp;
 
        if (!n) {
                for (i = 0; i < q->nr; i++) {
@@ -50,6 +59,13 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                        oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
                        p->parent[n].mode = q->queue[i]->one->mode;
                        p->parent[n].status = q->queue[i]->status;
+
+                       if (combined_all_paths &&
+                           filename_changed(p->parent[n].status)) {
+                               strbuf_init(&p->parent[n].path, 0);
+                               strbuf_addstr(&p->parent[n].path,
+                                             q->queue[i]->one->path);
+                       }
                        *tail = p;
                        tail = &p->next;
                }
@@ -68,6 +84,10 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                if (cmp < 0) {
                        /* p->path not in q->queue[]; drop it */
                        *tail = p->next;
+                       for (j = 0; j < num_parent; j++)
+                               if (combined_all_paths &&
+                                   filename_changed(p->parent[j].status))
+                                       strbuf_release(&p->parent[j].path);
                        free(p);
                        continue;
                }
@@ -81,6 +101,10 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
                oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
                p->parent[n].mode = q->queue[i]->one->mode;
                p->parent[n].status = q->queue[i]->status;
+               if (combined_all_paths &&
+                   filename_changed(p->parent[n].status))
+                       strbuf_addstr(&p->parent[n].path,
+                                     q->queue[i]->one->path);
 
                tail = &p->next;
                i++;
@@ -899,14 +923,13 @@ static void dump_quoted_path(const char *head,
 
 static void show_combined_header(struct combine_diff_path *elem,
                                 int num_parent,
-                                int dense,
                                 struct rev_info *rev,
                                 const char *line_prefix,
                                 int mode_differs,
                                 int show_file_header)
 {
        struct diff_options *opt = &rev->diffopt;
-       int abbrev = opt->flags.full_index ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
+       int abbrev = opt->flags.full_index ? the_hash_algo->hexsz : DEFAULT_ABBREV;
        const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
        const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
        const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
@@ -915,6 +938,7 @@ static void show_combined_header(struct combine_diff_path *elem,
        int added = 0;
        int deleted = 0;
        int i;
+       int dense = rev->dense_combined_merges;
 
        if (rev->loginfo && !rev->no_commit_id)
                show_log(rev);
@@ -960,12 +984,25 @@ static void show_combined_header(struct combine_diff_path *elem,
        if (!show_file_header)
                return;
 
-       if (added)
-               dump_quoted_path("--- ", "", "/dev/null",
-                                line_prefix, c_meta, c_reset);
-       else
-               dump_quoted_path("--- ", a_prefix, elem->path,
-                                line_prefix, c_meta, c_reset);
+       if (rev->combined_all_paths) {
+               for (i = 0; i < num_parent; i++) {
+                       char *path = filename_changed(elem->parent[i].status)
+                               ? elem->parent[i].path.buf : elem->path;
+                       if (elem->parent[i].status == DIFF_STATUS_ADDED)
+                               dump_quoted_path("--- ", "", "/dev/null",
+                                                line_prefix, c_meta, c_reset);
+                       else
+                               dump_quoted_path("--- ", a_prefix, path,
+                                                line_prefix, c_meta, c_reset);
+               }
+       } else {
+               if (added)
+                       dump_quoted_path("--- ", "", "/dev/null",
+                                        line_prefix, c_meta, c_reset);
+               else
+                       dump_quoted_path("--- ", a_prefix, elem->path,
+                                        line_prefix, c_meta, c_reset);
+       }
        if (deleted)
                dump_quoted_path("+++ ", "", "/dev/null",
                                 line_prefix, c_meta, c_reset);
@@ -975,7 +1012,7 @@ static void show_combined_header(struct combine_diff_path *elem,
 }
 
 static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
-                           int dense, int working_tree_file,
+                           int working_tree_file,
                            struct rev_info *rev)
 {
        struct diff_options *opt = &rev->diffopt;
@@ -996,7 +1033,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        if (!userdiff)
                userdiff = userdiff_find_by_name("default");
        if (opt->flags.allow_textconv)
-               textconv = userdiff_get_textconv(userdiff);
+               textconv = userdiff_get_textconv(opt->repo, userdiff);
 
        /* Read the result of merge first */
        if (!working_tree_file)
@@ -1108,7 +1145,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                }
        }
        if (is_binary) {
-               show_combined_header(elem, num_parent, dense, rev,
+               show_combined_header(elem, num_parent, rev,
                                     line_prefix, mode_differs, 0);
                printf("Binary files differ\n");
                free(result);
@@ -1163,10 +1200,10 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                                     textconv, elem->path, opt->xdl_opts);
        }
 
-       show_hunks = make_hunks(sline, cnt, num_parent, dense);
+       show_hunks = make_hunks(sline, cnt, num_parent, rev->dense_combined_merges);
 
        if (show_hunks || mode_differs || working_tree_file) {
-               show_combined_header(elem, num_parent, dense, rev,
+               show_combined_header(elem, num_parent, rev,
                                     line_prefix, mode_differs, 1);
                dump_sline(sline, line_prefix, cnt, num_parent,
                           opt->use_color, result_deleted);
@@ -1227,6 +1264,15 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
                putchar(inter_name_termination);
        }
 
+       for (i = 0; i < num_parent; i++)
+               if (rev->combined_all_paths) {
+                       if (filename_changed(p->parent[i].status))
+                               write_name_quoted(p->parent[i].path.buf, stdout,
+                                                 inter_name_termination);
+                       else
+                               write_name_quoted(p->path, stdout,
+                                                 inter_name_termination);
+               }
        write_name_quoted(p->path, stdout, line_termination);
 }
 
@@ -1238,7 +1284,6 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
  */
 void show_combined_diff(struct combine_diff_path *p,
                       int num_parent,
-                      int dense,
                       struct rev_info *rev)
 {
        struct diff_options *opt = &rev->diffopt;
@@ -1248,7 +1293,7 @@ void show_combined_diff(struct combine_diff_path *p,
                                  DIFF_FORMAT_NAME_STATUS))
                show_raw_diff(p, num_parent, rev);
        else if (opt->output_format & DIFF_FORMAT_PATCH)
-               show_patch_diff(p, num_parent, dense, 1, rev);
+               show_patch_diff(p, num_parent, 1, rev);
 }
 
 static void free_combined_pair(struct diff_filepair *pair)
@@ -1321,10 +1366,20 @@ static const char *path_path(void *obj)
        return path->path;
 }
 
+/*
+ * Diff stat formats which we always compute solely against the first parent.
+ */
+#define STAT_FORMAT_MASK (DIFF_FORMAT_NUMSTAT \
+                         | DIFF_FORMAT_SHORTSTAT \
+                         | DIFF_FORMAT_SUMMARY \
+                         | DIFF_FORMAT_DIRSTAT \
+                         | DIFF_FORMAT_DIFFSTAT)
 
 /* find set of paths that every parent touches */
 static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
-       const struct oid_array *parents, struct diff_options *opt)
+       const struct oid_array *parents,
+       struct diff_options *opt,
+       int combined_all_paths)
 {
        struct combine_diff_path *paths = NULL;
        int i, num_parent = parents->nr;
@@ -1342,15 +1397,15 @@ static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
                 * show stat against the first parent even when doing
                 * combined diff.
                 */
-               int stat_opt = (output_format &
-                               (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+               int stat_opt = output_format & STAT_FORMAT_MASK;
                if (i == 0 && stat_opt)
                        opt->output_format = stat_opt;
                else
                        opt->output_format = DIFF_FORMAT_NO_OUTPUT;
                diff_tree_oid(&parents->oid[i], oid, "", opt);
                diffcore_std(opt);
-               paths = intersect_paths(paths, i, num_parent);
+               paths = intersect_paths(paths, i, num_parent,
+                                       combined_all_paths);
 
                /* if showing diff, show it in requested order */
                if (opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
@@ -1395,10 +1450,45 @@ static struct combine_diff_path *find_paths_multitree(
        return paths_head.next;
 }
 
+static int match_objfind(struct combine_diff_path *path,
+                        int num_parent,
+                        const struct oidset *set)
+{
+       int i;
+       if (oidset_contains(set, &path->oid))
+               return 1;
+       for (i = 0; i < num_parent; i++) {
+               if (oidset_contains(set, &path->parent[i].oid))
+                       return 1;
+       }
+       return 0;
+}
+
+static struct combine_diff_path *combined_objfind(struct diff_options *opt,
+                                                 struct combine_diff_path *paths,
+                                                 int num_parent)
+{
+       struct combine_diff_path *ret = NULL, **tail = &ret;
+       struct combine_diff_path *p = paths;
+
+       while (p) {
+               struct combine_diff_path *next = p->next;
+
+               if (match_objfind(p, num_parent, opt->objfind)) {
+                       p->next = NULL;
+                       *tail = p;
+                       tail = &p->next;
+               } else {
+                       free(p);
+               }
+               p = next;
+       }
+
+       return ret;
+}
 
 void diff_tree_combined(const struct object_id *oid,
                        const struct oid_array *parents,
-                       int dense,
                        struct rev_info *rev)
 {
        struct diff_options *opt = &rev->diffopt;
@@ -1450,28 +1540,31 @@ void diff_tree_combined(const struct object_id *oid,
                        opt->flags.follow_renames       ||
                        opt->break_opt != -1    ||
                        opt->detect_rename      ||
-                       (opt->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)   ||
+                       (opt->pickaxe_opts &
+                        (DIFF_PICKAXE_KINDS_MASK & ~DIFF_PICKAXE_KIND_OBJFIND)) ||
                        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(oid, parents, &diffopts);
+               paths = find_paths_generic(oid, parents, &diffopts,
+                                          rev->combined_all_paths);
        }
        else {
                int stat_opt;
                paths = find_paths_multitree(oid, parents, &diffopts);
 
+               if (opt->pickaxe_opts & DIFF_PICKAXE_KIND_OBJFIND)
+                       paths = combined_objfind(opt, paths, num_parent);
+
                /*
                 * show stat against the first parent even
                 * when doing combined diff.
                 */
-               stat_opt = (opt->output_format &
-                               (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+               stat_opt = opt->output_format & STAT_FORMAT_MASK;
                if (stat_opt) {
                        diffopts.output_format = stat_opt;
 
@@ -1515,8 +1608,7 @@ void diff_tree_combined(const struct object_id *oid,
                                show_raw_diff(p, num_parent, rev);
                        needsep = 1;
                }
-               else if (opt->output_format &
-                        (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
+               else if (opt->output_format & STAT_FORMAT_MASK)
                        needsep = 1;
                else if (opt->output_format & DIFF_FORMAT_CALLBACK)
                        handle_combined_callback(opt, paths, num_parent, num_paths);
@@ -1526,8 +1618,7 @@ void diff_tree_combined(const struct object_id *oid,
                                printf("%s%c", diff_line_prefix(opt),
                                       opt->line_termination);
                        for (p = paths; p; p = p->next)
-                               show_patch_diff(p, num_parent, dense,
-                                               0, rev);
+                               show_patch_diff(p, num_parent, 0, rev);
                }
        }
 
@@ -1535,13 +1626,17 @@ void diff_tree_combined(const struct object_id *oid,
        while (paths) {
                struct combine_diff_path *tmp = paths;
                paths = paths->next;
+               for (i = 0; i < num_parent; i++)
+                       if (rev->combined_all_paths &&
+                           filename_changed(tmp->parent[i].status))
+                               strbuf_release(&tmp->parent[i].path);
                free(tmp);
        }
 
        clear_pathspec(&diffopts.pathspec);
 }
 
-void diff_tree_combined_merge(const struct commit *commit, int dense,
+void diff_tree_combined_merge(const struct commit *commit,
                              struct rev_info *rev)
 {
        struct commit_list *parent = get_saved_parents(rev, commit);
@@ -1551,6 +1646,6 @@ void diff_tree_combined_merge(const struct commit *commit, int dense,
                oid_array_append(&parents, &parent->item->object.oid);
                parent = parent->next;
        }
-       diff_tree_combined(&commit->object.oid, &parents, dense, rev);
+       diff_tree_combined(&commit->object.oid, &parents, rev);
        oid_array_clear(&parents);
 }