Merge branch 'jk/diff-follow-must-take-one-pathspec'
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 635dee244d767bffc689550fd3e32d0ac1a37fea..de25819e4edede0a24ee30b202dee478165b4fed 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -16,6 +16,7 @@
 #include "submodule.h"
 #include "ll-merge.h"
 #include "string-list.h"
+#include "argv-array.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -1361,11 +1362,7 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
 {
        struct diffstat_file *x;
        x = xcalloc(sizeof (*x), 1);
-       if (diffstat->nr == diffstat->alloc) {
-               diffstat->alloc = alloc_nr(diffstat->alloc);
-               diffstat->files = xrealloc(diffstat->files,
-                               diffstat->alloc * sizeof(x));
-       }
+       ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc);
        diffstat->files[diffstat->nr++] = x;
        if (name_b) {
                x->from_name = xstrdup(name_a);
@@ -1465,20 +1462,12 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions)
         * but nothing about added/removed lines? Is this a bug in Git?").
         */
        if (insertions || deletions == 0) {
-               /*
-                * TRANSLATORS: "+" in (+) is a line addition marker;
-                * do not translate it.
-                */
                strbuf_addf(&sb,
                            (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)",
                            insertions);
        }
 
        if (deletions || insertions == 0) {
-               /*
-                * TRANSLATORS: "-" in (-) is a line removal marker;
-                * do not translate it.
-                */
                strbuf_addf(&sb,
                            (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)",
                            deletions);
@@ -2891,6 +2880,16 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
        return temp;
 }
 
+static void add_external_diff_name(struct argv_array *argv,
+                                  const char *name,
+                                  struct diff_filespec *df)
+{
+       struct diff_tempfile *temp = prepare_temp_file(name, df);
+       argv_array_push(argv, temp->name);
+       argv_array_push(argv, temp->hex);
+       argv_array_push(argv, temp->mode);
+}
+
 /* An external diff command takes:
  *
  * diff-cmd name infile1 infile1-sha1 infile1-mode \
@@ -2906,50 +2905,33 @@ static void run_external_diff(const char *pgm,
                              int complete_rewrite,
                              struct diff_options *o)
 {
-       const char *spawn_arg[10];
-       int retval;
-       const char **arg = &spawn_arg[0];
+       struct argv_array argv = ARGV_ARRAY_INIT;
+       struct argv_array env = ARGV_ARRAY_INIT;
        struct diff_queue_struct *q = &diff_queued_diff;
-       const char *env[3] = { NULL };
-       char env_counter[50];
-       char env_total[50];
+
+       argv_array_push(&argv, pgm);
+       argv_array_push(&argv, name);
 
        if (one && two) {
-               struct diff_tempfile *temp_one, *temp_two;
-               const char *othername = (other ? other : name);
-               temp_one = prepare_temp_file(name, one);
-               temp_two = prepare_temp_file(othername, two);
-               *arg++ = pgm;
-               *arg++ = name;
-               *arg++ = temp_one->name;
-               *arg++ = temp_one->hex;
-               *arg++ = temp_one->mode;
-               *arg++ = temp_two->name;
-               *arg++ = temp_two->hex;
-               *arg++ = temp_two->mode;
-               if (other) {
-                       *arg++ = other;
-                       *arg++ = xfrm_msg;
+               add_external_diff_name(&argv, name, one);
+               if (!other)
+                       add_external_diff_name(&argv, name, two);
+               else {
+                       add_external_diff_name(&argv, other, two);
+                       argv_array_push(&argv, other);
+                       argv_array_push(&argv, xfrm_msg);
                }
-       } else {
-               *arg++ = pgm;
-               *arg++ = name;
        }
-       *arg = NULL;
-       fflush(NULL);
 
-       env[0] = env_counter;
-       snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d",
-                ++o->diff_path_counter);
-       env[1] = env_total;
-       snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+       argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
+       argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+
+       if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
+               die(_("external diff died, stopping at %s"), name);
 
-       retval = run_command_v_opt_cd_env(spawn_arg, RUN_USING_SHELL, NULL, env);
        remove_tempfile();
-       if (retval) {
-               fprintf(stderr, "external diff died, stopping at %s.\n", name);
-               exit(1);
-       }
+       argv_array_clear(&argv);
+       argv_array_clear(&env);
 }
 
 static int similarity_index(struct diff_filepair *p)
@@ -3217,6 +3199,7 @@ void diff_setup(struct diff_options *options)
        options->context = diff_context_default;
        DIFF_OPT_SET(options, RENAME_EMPTY);
 
+       /* pathchange left =NULL by default */
        options->change = diff_change;
        options->add_remove = diff_addremove;
        options->use_color = diff_use_color_default;
@@ -3337,6 +3320,9 @@ void diff_setup_done(struct diff_options *options)
        }
 
        options->diff_path_counter = 0;
+
+       if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1)
+               die(_("--follow requires exactly one pathspec"));
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@ -3366,14 +3352,11 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va
        if (c != '-')
                return 0;
        arg++;
-       eq = strchr(arg, '=');
-       if (eq)
-               len = eq - arg;
-       else
-               len = strlen(arg);
+       eq = strchrnul(arg, '=');
+       len = eq - arg;
        if (!len || strncmp(arg, arg_long, len))
                return 0;
-       if (eq) {
+       if (*eq) {
                int n;
                char *end;
                if (!isdigit(*++eq))
@@ -3600,14 +3583,6 @@ static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)
        return 0;
 }
 
-/* Used only by "diff-files" and "diff --no-index" */
-void handle_deprecated_show_diff_q(struct diff_options *opt)
-{
-       warning("'diff -q' and 'diff-files -q' are deprecated.");
-       warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs.");
-       parse_diff_filter_opt("d", opt);
-}
-
 static void enable_patch_output(int *fmt) {
        *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
        *fmt |= DIFF_FORMAT_PATCH;
@@ -3966,11 +3941,7 @@ struct diff_queue_struct diff_queued_diff;
 
 void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
 {
-       if (queue->alloc <= queue->nr) {
-               queue->alloc = alloc_nr(queue->alloc);
-               queue->queue = xrealloc(queue->queue,
-                                       sizeof(dp) * queue->alloc);
-       }
+       ALLOC_GROW(queue->queue, queue->nr + 1, queue->alloc);
        queue->queue[queue->nr++] = dp;
 }
 
@@ -4776,6 +4747,7 @@ void diffcore_fix_diff_index(struct diff_options *options)
 
 void diffcore_std(struct diff_options *options)
 {
+       /* NOTE please keep the following in sync with diff_tree_combined() */
        if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
        if (!options->found_follow) {