]> git.ipfire.org Git - thirdparty/git.git/blobdiff - diff.c
The 19th batch
[thirdparty/git.git] / diff.c
diff --git a/diff.c b/diff.c
index 108c1875775df223c4ca4f9dec27194033a7cfaa..3be927b073931127cdf6e08acd2cb1637b89913d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1,6 +1,9 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
+
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "abspath.h"
 #include "base85.h"
 #include "git-compat-util.h"
 #include "abspath.h"
 #include "base85.h"
@@ -9,6 +12,7 @@
 #include "environment.h"
 #include "gettext.h"
 #include "tempfile.h"
 #include "environment.h"
 #include "gettext.h"
 #include "tempfile.h"
+#include "revision.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
@@ -26,6 +30,7 @@
 #include "merge-ll.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "merge-ll.h"
 #include "string-list.h"
 #include "strvec.h"
+#include "tmp-objdir.h"
 #include "graph.h"
 #include "oid-array.h"
 #include "packfile.h"
 #include "graph.h"
 #include "oid-array.h"
 #include "packfile.h"
@@ -56,14 +61,14 @@ static int diff_color_moved_default;
 static int diff_color_moved_ws_default;
 static int diff_context_default = 3;
 static int diff_interhunk_context_default;
 static int diff_color_moved_ws_default;
 static int diff_context_default = 3;
 static int diff_interhunk_context_default;
-static const char *diff_word_regex_cfg;
-static const char *external_diff_cmd_cfg;
-static const char *diff_order_file_cfg;
+static char *diff_word_regex_cfg;
+static struct external_diff external_diff_cfg;
+static char *diff_order_file_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
-static const char *diff_src_prefix = "a/";
-static const char *diff_dst_prefix = "b/";
+static char *diff_src_prefix;
+static char *diff_dst_prefix;
 static int diff_relative;
 static int diff_stat_name_width;
 static int diff_stat_graph_width;
 static int diff_relative;
 static int diff_stat_name_width;
 static int diff_stat_graph_width;
@@ -411,9 +416,11 @@ int git_diff_ui_config(const char *var, const char *value,
                return 0;
        }
        if (!strcmp(var, "diff.srcprefix")) {
                return 0;
        }
        if (!strcmp(var, "diff.srcprefix")) {
+               FREE_AND_NULL(diff_src_prefix);
                return git_config_string(&diff_src_prefix, var, value);
        }
        if (!strcmp(var, "diff.dstprefix")) {
                return git_config_string(&diff_src_prefix, var, value);
        }
        if (!strcmp(var, "diff.dstprefix")) {
+               FREE_AND_NULL(diff_dst_prefix);
                return git_config_string(&diff_dst_prefix, var, value);
        }
        if (!strcmp(var, "diff.relative")) {
                return git_config_string(&diff_dst_prefix, var, value);
        }
        if (!strcmp(var, "diff.relative")) {
@@ -429,7 +436,11 @@ int git_diff_ui_config(const char *var, const char *value,
                return 0;
        }
        if (!strcmp(var, "diff.external"))
                return 0;
        }
        if (!strcmp(var, "diff.external"))
-               return git_config_string(&external_diff_cmd_cfg, var, value);
+               return git_config_string(&external_diff_cfg.cmd, var, value);
+       if (!strcmp(var, "diff.trustexitcode")) {
+               external_diff_cfg.trust_exit_code = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "diff.wordregex"))
                return git_config_string(&diff_word_regex_cfg, var, value);
        if (!strcmp(var, "diff.orderfile"))
        if (!strcmp(var, "diff.wordregex"))
                return git_config_string(&diff_word_regex_cfg, var, value);
        if (!strcmp(var, "diff.orderfile"))
@@ -546,18 +557,22 @@ static char *quote_two(const char *one, const char *two)
        return strbuf_detach(&res, NULL);
 }
 
        return strbuf_detach(&res, NULL);
 }
 
-static const char *external_diff(void)
+static const struct external_diff *external_diff(void)
 {
 {
-       static const char *external_diff_cmd = NULL;
+       static struct external_diff external_diff_env, *external_diff_ptr;
        static int done_preparing = 0;
 
        if (done_preparing)
        static int done_preparing = 0;
 
        if (done_preparing)
-               return external_diff_cmd;
-       external_diff_cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
-       if (!external_diff_cmd)
-               external_diff_cmd = external_diff_cmd_cfg;
+               return external_diff_ptr;
+       external_diff_env.cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
+       if (git_env_bool("GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE", 0))
+               external_diff_env.trust_exit_code = 1;
+       if (external_diff_env.cmd)
+               external_diff_ptr = &external_diff_env;
+       else if (external_diff_cfg.cmd)
+               external_diff_ptr = &external_diff_cfg;
        done_preparing = 1;
        done_preparing = 1;
-       return external_diff_cmd;
+       return external_diff_ptr;
 }
 
 /*
 }
 
 /*
@@ -3433,8 +3448,8 @@ void diff_set_noprefix(struct diff_options *options)
 
 void diff_set_default_prefix(struct diff_options *options)
 {
 
 void diff_set_default_prefix(struct diff_options *options)
 {
-       options->a_prefix = diff_src_prefix;
-       options->b_prefix = diff_dst_prefix;
+       options->a_prefix = diff_src_prefix ? diff_src_prefix : "a/";
+       options->b_prefix = diff_dst_prefix ? diff_dst_prefix : "b/";
 }
 
 struct userdiff_driver *get_textconv(struct repository *r,
 }
 
 struct userdiff_driver *get_textconv(struct repository *r,
@@ -3552,6 +3567,7 @@ static void builtin_diff(const char *name_a,
                show_submodule_diff_summary(o, one->path ? one->path : two->path,
                                &one->oid, &two->oid,
                                two->dirty_submodule);
                show_submodule_diff_summary(o, one->path ? one->path : two->path,
                                &one->oid, &two->oid,
                                two->dirty_submodule);
+               o->found_changes = 1;
                return;
        } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
                   (!one->mode || S_ISGITLINK(one->mode)) &&
                return;
        } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
                   (!one->mode || S_ISGITLINK(one->mode)) &&
@@ -3560,6 +3576,7 @@ static void builtin_diff(const char *name_a,
                show_submodule_inline_diff(o, one->path ? one->path : two->path,
                                &one->oid, &two->oid,
                                two->dirty_submodule);
                show_submodule_inline_diff(o, one->path ? one->path : two->path,
                                &one->oid, &two->oid,
                                two->dirty_submodule);
+               o->found_changes = 1;
                return;
        }
 
                return;
        }
 
@@ -3762,7 +3779,7 @@ static void builtin_diff(const char *name_a,
        return;
 }
 
        return;
 }
 
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 {
        if (!is_renamed) {
                if (p->status == DIFF_STATUS_ADDED) {
 {
        if (!is_renamed) {
                if (p->status == DIFF_STATUS_ADDED) {
@@ -4074,7 +4091,7 @@ static int reuse_worktree_file(struct index_state *istate,
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
        struct strbuf buf = STRBUF_INIT;
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
        struct strbuf buf = STRBUF_INIT;
-       char *dirty = "";
+       const char *dirty = "";
 
        /* Are we looking at the work tree? */
        if (s->dirty_submodule)
 
        /* Are we looking at the work tree? */
        if (s->dirty_submodule)
@@ -4373,7 +4390,7 @@ static void add_external_diff_name(struct repository *r,
  *               infile2 infile2-sha1 infile2-mode [ rename-to ]
  *
  */
  *               infile2 infile2-sha1 infile2-mode [ rename-to ]
  *
  */
-static void run_external_diff(const char *pgm,
+static void run_external_diff(const struct external_diff *pgm,
                              const char *name,
                              const char *other,
                              struct diff_filespec *one,
                              const char *name,
                              const char *other,
                              struct diff_filespec *one,
@@ -4383,8 +4400,21 @@ static void run_external_diff(const char *pgm,
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
        struct diff_queue_struct *q = &diff_queued_diff;
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
        struct diff_queue_struct *q = &diff_queued_diff;
+       int quiet = !(o->output_format & DIFF_FORMAT_PATCH);
+       int rc;
 
 
-       strvec_push(&cmd.args, pgm);
+       /*
+        * Trivial equality is handled by diff_unmodified_pair() before
+        * we get here.  If we don't need to show the diff and the
+        * external diff program lacks the ability to tell us whether
+        * it's empty then we consider it non-empty without even asking.
+        */
+       if (!pgm->trust_exit_code && quiet) {
+               o->found_changes = 1;
+               return;
+       }
+
+       strvec_push(&cmd.args, pgm->cmd);
        strvec_push(&cmd.args, name);
 
        if (one && two) {
        strvec_push(&cmd.args, name);
 
        if (one && two) {
@@ -4404,7 +4434,15 @@ static void run_external_diff(const char *pgm,
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
        cmd.use_shell = 1;
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
        cmd.use_shell = 1;
-       if (run_command(&cmd))
+       cmd.no_stdout = quiet;
+       rc = run_command(&cmd);
+       if (!pgm->trust_exit_code && rc == 0)
+               o->found_changes = 1;
+       else if (pgm->trust_exit_code && rc == 0)
+               ; /* nothing */
+       else if (pgm->trust_exit_code && rc == 1)
+               o->found_changes = 1;
+       else
                die(_("external diff died, stopping at %s"), name);
 
        remove_tempfile();
                die(_("external diff died, stopping at %s"), name);
 
        remove_tempfile();
@@ -4510,7 +4548,7 @@ static void fill_metainfo(struct strbuf *msg,
        }
 }
 
        }
 }
 
-static void run_diff_cmd(const char *pgm,
+static void run_diff_cmd(const struct external_diff *pgm,
                         const char *name,
                         const char *other,
                         const char *attr_path,
                         const char *name,
                         const char *other,
                         const char *attr_path,
@@ -4528,8 +4566,8 @@ static void run_diff_cmd(const char *pgm,
        if (o->flags.allow_external || !o->ignore_driver_algorithm)
                drv = userdiff_find_by_path(o->repo->index, attr_path);
 
        if (o->flags.allow_external || !o->ignore_driver_algorithm)
                drv = userdiff_find_by_path(o->repo->index, attr_path);
 
-       if (o->flags.allow_external && drv && drv->external)
-               pgm = drv->external;
+       if (o->flags.allow_external && drv && drv->external.cmd)
+               pgm = &drv->external;
 
        if (msg) {
                /*
 
        if (msg) {
                /*
@@ -4553,8 +4591,12 @@ static void run_diff_cmd(const char *pgm,
                builtin_diff(name, other ? other : name,
                             one, two, xfrm_msg, must_show_header,
                             o, complete_rewrite);
                builtin_diff(name, other ? other : name,
                             one, two, xfrm_msg, must_show_header,
                             o, complete_rewrite);
+               if (p->status == DIFF_STATUS_COPIED ||
+                   p->status == DIFF_STATUS_RENAMED)
+                       o->found_changes = 1;
        } else {
                fprintf(o->file, "* Unmerged path %s\n", name);
        } else {
                fprintf(o->file, "* Unmerged path %s\n", name);
+               o->found_changes = 1;
        }
 }
 
        }
 }
 
@@ -4564,7 +4606,7 @@ static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *is
                if (!one->oid_valid) {
                        struct stat st;
                        if (one->is_stdin) {
                if (!one->oid_valid) {
                        struct stat st;
                        if (one->is_stdin) {
-                               oidclr(&one->oid);
+                               oidclr(&one->oid, the_repository->hash_algo);
                                return;
                        }
                        if (lstat(one->path, &st) < 0)
                                return;
                        }
                        if (lstat(one->path, &st) < 0)
@@ -4574,7 +4616,7 @@ static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *is
                }
        }
        else
                }
        }
        else
-               oidclr(&one->oid);
+               oidclr(&one->oid, the_repository->hash_algo);
 }
 
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 }
 
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
@@ -4594,7 +4636,7 @@ static void strip_prefix(int prefix_length, const char **namep, const char **oth
 
 static void run_diff(struct diff_filepair *p, struct diff_options *o)
 {
 
 static void run_diff(struct diff_filepair *p, struct diff_options *o)
 {
-       const char *pgm = external_diff();
+       const struct external_diff *pgm = external_diff();
        struct strbuf msg;
        struct diff_filespec *one = p->one;
        struct diff_filespec *two = p->two;
        struct strbuf msg;
        struct diff_filespec *one = p->one;
        struct diff_filespec *two = p->two;
@@ -4921,12 +4963,20 @@ void diff_setup_done(struct diff_options *options)
                options->flags.exit_with_status = 1;
        }
 
                options->flags.exit_with_status = 1;
        }
 
+       /*
+        * External diffs could declare non-identical contents equal
+        * (think diff --ignore-space-change).
+        */
+       if (options->flags.allow_external && options->flags.exit_with_status)
+               options->flags.diff_from_contents = 1;
+
        options->diff_path_counter = 0;
 
        if (options->flags.follow_renames)
                diff_check_follow_pathspec(&options->pathspec, 1);
 
        options->diff_path_counter = 0;
 
        if (options->flags.follow_renames)
                diff_check_follow_pathspec(&options->pathspec, 1);
 
-       if (!options->use_color || external_diff())
+       if (!options->use_color ||
+           (options->flags.allow_external && external_diff()))
                options->color_moved = 0;
 
        if (options->filter_not) {
                options->color_moved = 0;
 
        if (options->filter_not) {
@@ -5370,8 +5420,8 @@ static int diff_opt_default_prefix(const struct option *opt,
 
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(optarg);
 
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(optarg);
-       diff_src_prefix = "a/";
-       diff_dst_prefix = "b/";
+       FREE_AND_NULL(diff_src_prefix);
+       FREE_AND_NULL(diff_dst_prefix);
        diff_set_default_prefix(options);
        return 0;
 }
        diff_set_default_prefix(options);
        return 0;
 }
@@ -5421,9 +5471,13 @@ static int diff_opt_ignore_regex(const struct option *opt,
        regex_t *regex;
 
        BUG_ON_OPT_NEG(unset);
        regex_t *regex;
 
        BUG_ON_OPT_NEG(unset);
+
        regex = xmalloc(sizeof(*regex));
        regex = xmalloc(sizeof(*regex));
-       if (regcomp(regex, arg, REG_EXTENDED | REG_NEWLINE))
+       if (regcomp(regex, arg, REG_EXTENDED | REG_NEWLINE)) {
+               free(regex);
                return error(_("invalid regex given to -I: '%s'"), arg);
                return error(_("invalid regex given to -I: '%s'"), arg);
+       }
+
        ALLOC_GROW(options->ignore_regex, options->ignore_regex_nr + 1,
                   options->ignore_regex_alloc);
        options->ignore_regex[options->ignore_regex_nr++] = regex;
        ALLOC_GROW(options->ignore_regex, options->ignore_regex_nr + 1,
                   options->ignore_regex_alloc);
        options->ignore_regex[options->ignore_regex_nr++] = regex;
@@ -6401,7 +6455,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
        the_hash_algo->init_fn(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
        data.ctx = &ctx;
        the_hash_algo->init_fn(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
        data.ctx = &ctx;
-       oidclr(oid);
+       oidclr(oid, the_repository->hash_algo);
 
        for (i = 0; i < q->nr; i++) {
                xpparam_t xpp;
 
        for (i = 0; i < q->nr; i++) {
                xpparam_t xpp;
@@ -6646,8 +6700,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
 
 static void diff_free_file(struct diff_options *options)
 {
 
 static void diff_free_file(struct diff_options *options)
 {
-       if (options->close_file)
+       if (options->close_file && options->file) {
                fclose(options->file);
                fclose(options->file);
+               options->file = NULL;
+       }
 }
 
 static void diff_free_ignore_regex(struct diff_options *options)
 }
 
 static void diff_free_ignore_regex(struct diff_options *options)
@@ -6658,7 +6714,9 @@ static void diff_free_ignore_regex(struct diff_options *options)
                regfree(options->ignore_regex[i]);
                free(options->ignore_regex[i]);
        }
                regfree(options->ignore_regex[i]);
                free(options->ignore_regex[i]);
        }
-       free(options->ignore_regex);
+
+       FREE_AND_NULL(options->ignore_regex);
+       options->ignore_regex_nr = 0;
 }
 
 void diff_free(struct diff_options *options)
 }
 
 void diff_free(struct diff_options *options)
@@ -6666,6 +6724,16 @@ void diff_free(struct diff_options *options)
        if (options->no_free)
                return;
 
        if (options->no_free)
                return;
 
+       if (options->objfind) {
+               oidset_clear(options->objfind);
+               FREE_AND_NULL(options->objfind);
+       }
+
+       for (size_t i = 0; i < options->anchors_nr; i++)
+               free(options->anchors[i]);
+       FREE_AND_NULL(options->anchors);
+       options->anchors_nr = options->anchors_alloc = 0;
+
        diff_free_file(options);
        diff_free_ignore_regex(options);
        clear_pathspec(&options->pathspec);
        diff_free_file(options);
        diff_free_ignore_regex(options);
        clear_pathspec(&options->pathspec);
@@ -7022,10 +7090,16 @@ void diffcore_std(struct diff_options *options)
        options->found_follow = 0;
 }
 
        options->found_follow = 0;
 }
 
-int diff_result_code(struct diff_options *opt)
+int diff_result_code(struct rev_info *revs)
 {
 {
+       struct diff_options *opt = &revs->diffopt;
        int result = 0;
 
        int result = 0;
 
+       if (revs->remerge_diff) {
+               tmp_objdir_destroy(revs->remerge_objdir);
+               revs->remerge_objdir = NULL;
+       }
+
        diff_warn_rename_limit("diff.renameLimit",
                               opt->needed_rename_limit,
                               opt->degraded_cc_to_c);
        diff_warn_rename_limit("diff.renameLimit",
                               opt->needed_rename_limit,
                               opt->degraded_cc_to_c);
@@ -7232,7 +7306,7 @@ size_t fill_textconv(struct repository *r,
 
        if (!driver) {
                if (!DIFF_FILE_VALID(df)) {
 
        if (!driver) {
                if (!DIFF_FILE_VALID(df)) {
-                       *outbuf = "";
+                       *outbuf = (char *) "";
                        return 0;
                }
                if (diff_populate_filespec(r, df, NULL))
                        return 0;
                }
                if (diff_populate_filespec(r, df, NULL))