Merge branch 'jk/save-getenv-result'
authorJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:53 +0000 (12:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:54 +0000 (12:47 -0800)
There were many places the code relied on the string returned from
getenv() to be non-volatile, which is not true, that have been
corrected.

* jk/save-getenv-result:
builtin_diff(): read $GIT_DIFF_OPTS closer to use
merge-recursive: copy $GITHEAD strings
init: make a copy of $GIT_DIR string
config: make a copy of $GIT_CONFIG string
commit: copy saved getenv() result
get_super_prefix(): copy getenv() result

1  2 
builtin/commit.c
builtin/config.c
builtin/init-db.c
diff.c
environment.c
diff --combined builtin/commit.c
index 004b816635bf1628bfe7697f737a22cbcd27622b,04f5b98be34ce126d8b61b64bd29af01ddd4a403..7d2e0b61e5fc73a9b338a4f37b9a51ddc6cbf306
@@@ -33,8 -33,6 +33,8 @@@
  #include "sequencer.h"
  #include "mailmap.h"
  #include "help.h"
 +#include "commit-reach.h"
 +#include "commit-graph.h"
  
  static const char * const builtin_commit_usage[] = {
        N_("git commit [<options>] [--] <pathspec>..."),
@@@ -161,9 -159,6 +161,9 @@@ static int opt_parse_m(const struct opt
  static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset)
  {
        const char **value = opt->value;
 +
 +      BUG_ON_OPT_NEG(unset);
 +
        if (arg != NULL && *arg == '=')
                arg = arg + 1;
  
@@@ -188,7 -183,7 +188,7 @@@ static void determine_whence(struct wt_
  
  static void status_init_config(struct wt_status *s, config_fn_t fn)
  {
 -      wt_status_prepare(s);
 +      wt_status_prepare(the_repository, s);
        init_diff_ui_defaults();
        git_config(fn, s);
        determine_whence(s);
@@@ -351,7 -346,7 +351,7 @@@ static const char *prepare_index(int ar
                if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to create temporary index"));
  
-               old_index_env = getenv(INDEX_ENVIRONMENT);
+               old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
                setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
  
                if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
                        setenv(INDEX_ENVIRONMENT, old_index_env, 1);
                else
                        unsetenv(INDEX_ENVIRONMENT);
+               FREE_AND_NULL(old_index_env);
  
                discard_cache();
                read_cache_from(get_lock_file_path(&index_lock));
@@@ -511,9 -507,8 +512,9 @@@ static int run_status(FILE *fp, const c
  
        wt_status_collect(s);
        wt_status_print(s);
 +      wt_status_collect_free_buffers(s);
  
 -      return s->commitable;
 +      return s->committable;
  }
  
  static int is_a_merge(const struct commit *current_head)
@@@ -659,7 -654,7 +660,7 @@@ static int prepare_to_commit(const cha
  {
        struct stat statbuf;
        struct strbuf committer_ident = STRBUF_INIT;
 -      int commitable;
 +      int committable;
        struct strbuf sb = STRBUF_INIT;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
  
                saved_color_setting = s->use_color;
                s->use_color = 0;
 -              commitable = run_status(s->fp, index_file, prefix, 1, s);
 +              committable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
                string_list_clear(&s->change, 1);
        } else {
                        for (i = 0; i < active_nr; i++)
                                if (ce_intent_to_add(active_cache[i]))
                                        ita_nr++;
 -                      commitable = active_nr - ita_nr > 0;
 +                      committable = active_nr - ita_nr > 0;
                } else {
                        /*
                         * Unless the user did explicitly request a submodule
                        if (ignore_submodule_arg &&
                            !strcmp(ignore_submodule_arg, "all"))
                                flags.ignore_submodules = 1;
 -                      commitable = index_differs_from(parent, &flags, 1);
 +                      committable = index_differs_from(the_repository,
 +                                                       parent, &flags, 1);
                }
        }
        strbuf_release(&committer_ident);
         * explicit --allow-empty. In the cherry-pick case, it may be
         * empty due to conflict resolution, which the user should okay.
         */
 -      if (!commitable && whence != FROM_MERGE && !allow_empty &&
 +      if (!committable && whence != FROM_MERGE && !allow_empty &&
            !(amend && is_a_merge(current_head))) {
                s->display_comment_prefix = old_display_comment_prefix;
                run_status(stdout, index_file, prefix, 0, s);
@@@ -988,7 -982,7 +989,7 @@@ static const char *find_author_by_nickn
        const char *av[20];
        int ac = 0;
  
 -      init_revisions(&revs, NULL);
 +      repo_init_revisions(the_repository, &revs, NULL);
        strbuf_addf(&buf, "--author=%s", name);
        av[++ac] = "--all";
        av[++ac] = "-i";
@@@ -1194,14 -1188,14 +1195,14 @@@ static int parse_and_validate_options(i
  static int dry_run_commit(int argc, const char **argv, const char *prefix,
                          const struct commit *current_head, struct wt_status *s)
  {
 -      int commitable;
 +      int committable;
        const char *index_file;
  
        index_file = prepare_index(argc, argv, prefix, current_head, 1);
 -      commitable = run_status(stdout, index_file, prefix, 0, s);
 +      committable = run_status(stdout, index_file, prefix, 0, s);
        rollback_index_files();
  
 -      return commitable ? 0 : 1;
 +      return committable ? 0 : 1;
  }
  
  define_list_config_array_extra(color_status_slots, {"added"});
@@@ -1303,7 -1297,6 +1304,7 @@@ int cmd_status(int argc, const char **a
        static int no_renames = -1;
        static const char *rename_score_arg = (const char *)-1;
        static struct wt_status s;
 +      unsigned int progress_flag = 0;
        int fd;
        struct object_id oid;
        static struct option builtin_status_options[] = {
                OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")),
                { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg,
                  N_("n"), N_("detect renames, optionally set similarity index"),
 -                PARSE_OPT_OPTARG, opt_parse_rename_score },
 +                PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score },
                OPT_END(),
        };
  
                       PATHSPEC_PREFER_FULL,
                       prefix, argv);
  
 -      read_cache_preload(&s.pathspec);
 -      refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
 +      if (status_format != STATUS_FORMAT_PORCELAIN &&
 +          status_format != STATUS_FORMAT_PORCELAIN_V2)
 +              progress_flag = REFRESH_PROGRESS;
 +      read_index(&the_index);
 +      refresh_index(&the_index,
 +                    REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
 +                    &s.pathspec, NULL, NULL);
  
        if (use_optional_locks())
                fd = hold_locked_index(&index_lock, 0);
                s.prefix = prefix;
  
        wt_status_print(&s);
 +      wt_status_collect_free_buffers(&s);
 +
        return 0;
  }
  
@@@ -1667,10 -1653,7 +1668,10 @@@ int cmd_commit(int argc, const char **a
                      "new_index file. Check that disk is not full and quota is\n"
                      "not exceeded, and then \"git reset HEAD\" to recover."));
  
 -      rerere(0);
 +      if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 +              write_commit_graph_reachable(get_object_directory(), 0, 0);
 +
 +      repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
        if (amend && !no_post_rewrite) {
                        flags |= SUMMARY_INITIAL_COMMIT;
                if (author_date_is_interesting())
                        flags |= SUMMARY_SHOW_AUTHOR_DATE;
 -              print_commit_summary(prefix, &oid, flags);
 +              print_commit_summary(the_repository, prefix,
 +                                   &oid, flags);
        }
  
        UNLEAK(err);
diff --combined builtin/config.c
index 99bc7ef64ec6f7f0ab0e6dc1d0dc3fd149969a16,752d0c65d76ca94498ce413f79efde208399b3e5..98d65bc0ad4fd4bee8bd2755b9d7444d9862eae8
@@@ -5,7 -5,6 +5,7 @@@
  #include "parse-options.h"
  #include "urlmatch.h"
  #include "quote.h"
 +#include "worktree.h"
  
  static const char *const builtin_config_usage[] = {
        N_("git config [<options>]"),
@@@ -25,7 -24,6 +25,7 @@@ static char key_delim = ' '
  static char term = '\n';
  
  static int use_global_config, use_system_config, use_local_config;
 +static int use_worktree_config;
  static struct git_config_source given_config_source;
  static int actions, type;
  static char *default_value;
@@@ -125,7 -123,6 +125,7 @@@ static struct option builtin_config_opt
        OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
        OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
        OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
 +      OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
        OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
        OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
        OPT_GROUP(N_("Action")),
@@@ -164,8 -161,7 +164,8 @@@ static NORETURN void usage_builtin_conf
        usage_with_options(builtin_config_usage, builtin_config_options);
  }
  
 -static void check_argc(int argc, int min, int max) {
 +static void check_argc(int argc, int min, int max)
 +{
        if (argc >= min && argc <= max)
                return;
        if (min == max)
@@@ -599,14 -595,13 +599,14 @@@ int cmd_config(int argc, const char **a
        int nongit = !startup_info->have_repository;
        char *value;
  
-       given_config_source.file = getenv(CONFIG_ENVIRONMENT);
+       given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
  
        argc = parse_options(argc, argv, prefix, builtin_config_options,
                             builtin_config_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
  
        if (use_global_config + use_system_config + use_local_config +
 +          use_worktree_config +
            !!given_config_source.file + !!given_config_source.blob > 1) {
                error(_("only one config file at a time"));
                usage_builtin_config();
                given_config_source.file = git_etc_gitconfig();
        else if (use_local_config)
                given_config_source.file = git_pathdup("config");
 -      else if (given_config_source.file) {
 +      else if (use_worktree_config) {
 +              struct worktree **worktrees = get_worktrees(0);
 +              if (repository_format_worktree_config)
 +                      given_config_source.file = git_pathdup("config.worktree");
 +              else if (worktrees[0] && worktrees[1])
 +                      die(_("--worktree cannot be used with multiple "
 +                            "working trees unless the config\n"
 +                            "extension worktreeConfig is enabled. "
 +                            "Please read \"CONFIGURATION FILE\"\n"
 +                            "section in \"git help worktree\" for details"));
 +              else
 +                      given_config_source.file = git_pathdup("config");
 +              free_worktrees(worktrees);
 +      } else if (given_config_source.file) {
                if (!is_absolute_path(given_config_source.file) && prefix)
                        given_config_source.file =
                                prefix_filename(prefix, given_config_source.file);
diff --combined builtin/init-db.c
index 41faffd28db8850fb97daa52c6d481375b380504,dbb0d10cdd208ece24ef29a5c58043f80531a811..93eff7618cf1b98e3c69a7f1f159063e9e73dbe3
@@@ -451,7 -451,6 +451,7 @@@ static int guess_repository_type(const 
  
  static int shared_callback(const struct option *opt, const char *arg, int unset)
  {
 +      BUG_ON_OPT_NEG(unset);
        *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
        return 0;
  }
@@@ -542,8 -541,8 +542,8 @@@ int cmd_init_db(int argc, const char **
         * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
         * without --bare.  Catch the error early.
         */
-       git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT));
+       work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT));
        if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
                die(_("%s (or --work-tree=<directory>) not allowed without "
                          "specifying %s (or --git-dir=<directory>)"),
        }
  
        UNLEAK(real_git_dir);
+       UNLEAK(git_dir);
+       UNLEAK(work_tree);
  
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, flags);
diff --combined diff.c
index 084bf542931e09c03a7627740f9177e5c9fac5d9,0920b12c0c3827b55a3497924a43ada017ede20f..e8c3e8081f9be0c4e9560a834b0f3df75ca81b72
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -291,7 -291,7 +291,7 @@@ static int parse_color_moved(const cha
                return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'"));
  }
  
 -static int parse_color_moved_ws(const char *arg)
 +static unsigned parse_color_moved_ws(const char *arg)
  {
        int ret = 0;
        struct string_list l = STRING_LIST_INIT_DUP;
                strbuf_addstr(&sb, i->string);
                strbuf_trim(&sb);
  
 -              if (!strcmp(sb.buf, "ignore-space-change"))
 +              if (!strcmp(sb.buf, "no"))
 +                      ret = 0;
 +              else if (!strcmp(sb.buf, "ignore-space-change"))
                        ret |= XDF_IGNORE_WHITESPACE_CHANGE;
                else if (!strcmp(sb.buf, "ignore-space-at-eol"))
                        ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
                        ret |= XDF_IGNORE_WHITESPACE;
                else if (!strcmp(sb.buf, "allow-indentation-change"))
                        ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
 -              else
 -                      error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf);
 +              else {
 +                      ret |= COLOR_MOVED_WS_ERROR;
 +                      error(_("unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', 'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"), sb.buf);
 +              }
  
                strbuf_release(&sb);
        }
  
        if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
 -          (ret & XDF_WHITESPACE_FLAGS))
 -              die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));
 +          (ret & XDF_WHITESPACE_FLAGS)) {
 +              error(_("color-moved-ws: allow-indentation-change cannot be combined with other whitespace modes"));
 +              ret |= COLOR_MOVED_WS_ERROR;
 +      }
  
        string_list_clear(&l, 0);
  
@@@ -347,8 -341,8 +347,8 @@@ int git_diff_ui_config(const char *var
                return 0;
        }
        if (!strcmp(var, "diff.colormovedws")) {
 -              int cm = parse_color_moved_ws(value);
 -              if (cm < 0)
 +              unsigned cm = parse_color_moved_ws(value);
 +              if (cm & COLOR_MOVED_WS_ERROR)
                        return -1;
                diff_color_moved_ws_default = cm;
                return 0;
@@@ -495,7 -489,7 +495,7 @@@ static const char *external_diff(void
  
        if (done_preparing)
                return external_diff_cmd;
 -      external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
 +      external_diff_cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
        if (!external_diff_cmd)
                external_diff_cmd = external_diff_cmd_cfg;
        done_preparing = 1;
@@@ -560,15 -554,14 +560,15 @@@ static int count_lines(const char *data
        return count;
  }
  
 -static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
 +static int fill_mmfile(struct repository *r, mmfile_t *mf,
 +                     struct diff_filespec *one)
  {
        if (!DIFF_FILE_VALID(one)) {
                mf->ptr = (char *)""; /* does not matter */
                mf->size = 0;
                return 0;
        }
 -      else if (diff_populate_filespec(one, 0))
 +      else if (diff_populate_filespec(r, one, 0))
                return -1;
  
        mf->ptr = one->data;
  }
  
  /* like fill_mmfile, but only for size, so we can avoid retrieving blob */
 -static unsigned long diff_filespec_size(struct diff_filespec *one)
 +static unsigned long diff_filespec_size(struct repository *r,
 +                                      struct diff_filespec *one)
  {
        if (!DIFF_FILE_VALID(one))
                return 0;
 -      diff_populate_filespec(one, CHECK_SIZE_ONLY);
 +      diff_populate_filespec(r, one, CHECK_SIZE_ONLY);
        return one->size;
  }
  
@@@ -632,54 -624,42 +632,54 @@@ static void check_blank_at_eof(mmfile_
  }
  
  static void emit_line_0(struct diff_options *o,
 -                      const char *set, unsigned reverse, const char *reset,
 +                      const char *set_sign, const char *set, unsigned reverse, const char *reset,
                        int first, const char *line, int len)
  {
        int has_trailing_newline, has_trailing_carriage_return;
 -      int nofirst;
 +      int needs_reset = 0; /* at the end of the line */
        FILE *file = o->file;
  
 -      if (first)
 -              fputs(diff_line_prefix(o), file);
 -      else if (!len)
 -              return;
 +      fputs(diff_line_prefix(o), file);
  
 -      if (len == 0) {
 -              has_trailing_newline = (first == '\n');
 -              has_trailing_carriage_return = (!has_trailing_newline &&
 -                                              (first == '\r'));
 -              nofirst = has_trailing_newline || has_trailing_carriage_return;
 -      } else {
 -              has_trailing_newline = (len > 0 && line[len-1] == '\n');
 -              if (has_trailing_newline)
 -                      len--;
 -              has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
 -              if (has_trailing_carriage_return)
 -                      len--;
 -              nofirst = 0;
 +      has_trailing_newline = (len > 0 && line[len-1] == '\n');
 +      if (has_trailing_newline)
 +              len--;
 +
 +      has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
 +      if (has_trailing_carriage_return)
 +              len--;
 +
 +      if (!len && !first)
 +              goto end_of_line;
 +
 +      if (reverse && want_color(o->use_color)) {
 +              fputs(GIT_COLOR_REVERSE, file);
 +              needs_reset = 1;
 +      }
 +
 +      if (set_sign) {
 +              fputs(set_sign, file);
 +              needs_reset = 1;
        }
  
 -      if (len || !nofirst) {
 -              if (reverse && want_color(o->use_color))
 -                      fputs(GIT_COLOR_REVERSE, file);
 +      if (first)
 +              fputc(first, file);
 +
 +      if (!len)
 +              goto end_of_line;
 +
 +      if (set) {
 +              if (set_sign && set != set_sign)
 +                      fputs(reset, file);
                fputs(set, file);
 -              if (first && !nofirst)
 -                      fputc(first, file);
 -              fwrite(line, len, 1, file);
 -              fputs(reset, file);
 +              needs_reset = 1;
        }
 +      fwrite(line, len, 1, file);
 +      needs_reset = 1; /* 'line' may contain color codes. */
 +
 +end_of_line:
 +      if (needs_reset)
 +              fputs(reset, file);
        if (has_trailing_carriage_return)
                fputc('\r', file);
        if (has_trailing_newline)
  static void emit_line(struct diff_options *o, const char *set, const char *reset,
                      const char *line, int len)
  {
 -      emit_line_0(o, set, 0, reset, line[0], line+1, len-1);
 +      emit_line_0(o, set, NULL, 0, reset, 0, line, len);
  }
  
  enum diff_symbol {
@@@ -756,8 -736,6 +756,8 @@@ struct emitted_diff_symbol 
        const char *line;
        int len;
        int flags;
 +      int indent_off;   /* Offset to first non-whitespace character */
 +      int indent_width; /* The visual width of the indentation */
        enum diff_symbol s;
  };
  #define EMITTED_DIFF_SYMBOL_INIT {NULL}
@@@ -786,145 -764,90 +786,145 @@@ struct moved_entry 
        struct hashmap_entry ent;
        const struct emitted_diff_symbol *es;
        struct moved_entry *next_line;
 -      struct ws_delta *wsd;
  };
  
 -/**
 - * The struct ws_delta holds white space differences between moved lines, i.e.
 - * between '+' and '-' lines that have been detected to be a move.
 - * The string contains the difference in leading white spaces, before the
 - * rest of the line is compared using the white space config for move
 - * coloring. The current_longer indicates if the first string in the
 - * comparision is longer than the second.
 - */
 -struct ws_delta {
 -      char *string;
 -      unsigned int current_longer : 1;
 +struct moved_block {
 +      struct moved_entry *match;
 +      int wsd; /* The whitespace delta of this block */
  };
 -#define WS_DELTA_INIT { NULL, 0 }
  
 -static int compute_ws_delta(const struct emitted_diff_symbol *a,
 -                           const struct emitted_diff_symbol *b,
 -                           struct ws_delta *out)
 +static void moved_block_clear(struct moved_block *b)
  {
 -      const struct emitted_diff_symbol *longer =  a->len > b->len ? a : b;
 -      const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
 -      int d = longer->len - shorter->len;
 +      memset(b, 0, sizeof(*b));
 +}
  
 -      out->string = xmemdupz(longer->line, d);
 -      out->current_longer = (a == longer);
 +#define INDENT_BLANKLINE INT_MIN
  
 -      return !strncmp(longer->line + d, shorter->line, shorter->len);
 +static void fill_es_indent_data(struct emitted_diff_symbol *es)
 +{
 +      unsigned int off = 0, i;
 +      int width = 0, tab_width = es->flags & WS_TAB_WIDTH_MASK;
 +      const char *s = es->line;
 +      const int len = es->len;
 +
 +      /* skip any \v \f \r at start of indentation */
 +      while (s[off] == '\f' || s[off] == '\v' ||
 +             (s[off] == '\r' && off < len - 1))
 +              off++;
 +
 +      /* calculate the visual width of indentation */
 +      while(1) {
 +              if (s[off] == ' ') {
 +                      width++;
 +                      off++;
 +              } else if (s[off] == '\t') {
 +                      width += tab_width - (width % tab_width);
 +                      while (s[++off] == '\t')
 +                              width += tab_width;
 +              } else {
 +                      break;
 +              }
 +      }
 +
 +      /* check if this line is blank */
 +      for (i = off; i < len; i++)
 +              if (!isspace(s[i]))
 +                  break;
 +
 +      if (i == len) {
 +              es->indent_width = INDENT_BLANKLINE;
 +              es->indent_off = len;
 +      } else {
 +              es->indent_off = off;
 +              es->indent_width = width;
 +      }
 +}
 +
 +static int compute_ws_delta(const struct emitted_diff_symbol *a,
 +                          const struct emitted_diff_symbol *b,
 +                          int *out)
 +{
 +      int a_len = a->len,
 +          b_len = b->len,
 +          a_off = a->indent_off,
 +          a_width = a->indent_width,
 +          b_off = b->indent_off,
 +          b_width = b->indent_width;
 +      int delta;
 +
 +      if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE) {
 +              *out = INDENT_BLANKLINE;
 +              return 1;
 +      }
 +
 +      if (a->s == DIFF_SYMBOL_PLUS)
 +              delta = a_width - b_width;
 +      else
 +              delta = b_width - a_width;
 +
 +      if (a_len - a_off != b_len - b_off ||
 +          memcmp(a->line + a_off, b->line + b_off, a_len - a_off))
 +              return 0;
 +
 +      *out = delta;
 +
 +      return 1;
  }
  
  static int cmp_in_block_with_wsd(const struct diff_options *o,
                                 const struct moved_entry *cur,
                                 const struct moved_entry *match,
 -                               struct moved_entry *pmb,
 +                               struct moved_block *pmb,
                                 int n)
  {
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
 -      int al = cur->es->len, cl = l->len;
 +      int al = cur->es->len, bl = match->es->len, cl = l->len;
        const char *a = cur->es->line,
                   *b = match->es->line,
                   *c = l->line;
 -
 -      int wslen;
 +      int a_off = cur->es->indent_off,
 +          a_width = cur->es->indent_width,
 +          c_off = l->indent_off,
 +          c_width = l->indent_width;
 +      int delta;
  
        /*
 -       * We need to check if 'cur' is equal to 'match'.
 -       * As those are from the same (+/-) side, we do not need to adjust for
 -       * indent changes. However these were found using fuzzy matching
 -       * so we do have to check if they are equal.
 +       * We need to check if 'cur' is equal to 'match'.  As those
 +       * are from the same (+/-) side, we do not need to adjust for
 +       * indent changes. However these were found using fuzzy
 +       * matching so we do have to check if they are equal. Here we
 +       * just check the lengths. We delay calling memcmp() to check
 +       * the contents until later as if the length comparison for a
 +       * and c fails we can avoid the call all together.
         */
 -      if (strcmp(a, b))
 +      if (al != bl)
                return 1;
  
 -      if (!pmb->wsd)
 -              /*
 -               * No white space delta was carried forward? This can happen
 -               * when we exit early in this function and do not carry
 -               * forward ws.
 -               */
 -              return 1;
 +      /* If 'l' and 'cur' are both blank then they match. */
 +      if (a_width == INDENT_BLANKLINE && c_width == INDENT_BLANKLINE)
 +              return 0;
  
        /*
 -       * The indent changes of the block are known and carried forward in
 -       * pmb->wsd; however we need to check if the indent changes of the
 -       * current line are still the same as before.
 -       *
 -       * To do so we need to compare 'l' to 'cur', adjusting the
 -       * one of them for the white spaces, depending which was longer.
 +       * The indent changes of the block are known and stored in pmb->wsd;
 +       * however we need to check if the indent changes of the current line
 +       * match those of the current block and that the text of 'l' and 'cur'
 +       * after the indentation match.
         */
 +      if (cur->es->s == DIFF_SYMBOL_PLUS)
 +              delta = a_width - c_width;
 +      else
 +              delta = c_width - a_width;
  
 -      wslen = strlen(pmb->wsd->string);
 -      if (pmb->wsd->current_longer) {
 -              c += wslen;
 -              cl -= wslen;
 -      } else {
 -              a += wslen;
 -              al -= wslen;
 -      }
 -
 -      if (strcmp(a, c))
 -              return 1;
 +      /*
 +       * If the previous lines of this block were all blank then set its
 +       * whitespace delta.
 +       */
 +      if (pmb->wsd == INDENT_BLANKLINE)
 +              pmb->wsd = delta;
  
 -      return 0;
 +      return !(delta == pmb->wsd && al - a_off == cl - c_off &&
 +               !memcmp(a, b, al) && !
 +               memcmp(a + a_off, c + c_off, al - a_off));
  }
  
  static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
@@@ -963,6 -886,7 +963,6 @@@ static struct moved_entry *prepare_entr
        ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
        ret->es = l;
        ret->next_line = NULL;
 -      ret->wsd = NULL;
  
        return ret;
  }
@@@ -990,9 -914,6 +990,9 @@@ static void add_lines_to_move_detection
                        continue;
                }
  
 +              if (o->color_moved_ws_handling &
 +                  COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
 +                      fill_es_indent_data(&o->emitted_symbols->buf[n]);
                key = prepare_entry(o, n);
                if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
                        prev_line->next_line = key;
  static void pmb_advance_or_null(struct diff_options *o,
                                struct moved_entry *match,
                                struct hashmap *hm,
 -                              struct moved_entry **pmb,
 +                              struct moved_block *pmb,
                                int pmb_nr)
  {
        int i;
        for (i = 0; i < pmb_nr; i++) {
 -              struct moved_entry *prev = pmb[i];
 +              struct moved_entry *prev = pmb[i].match;
                struct moved_entry *cur = (prev && prev->next_line) ?
                                prev->next_line : NULL;
                if (cur && !hm->cmpfn(o, cur, match, NULL)) {
 -                      pmb[i] = cur;
 +                      pmb[i].match = cur;
                } else {
 -                      pmb[i] = NULL;
 +                      pmb[i].match = NULL;
                }
        }
  }
  static void pmb_advance_or_null_multi_match(struct diff_options *o,
                                            struct moved_entry *match,
                                            struct hashmap *hm,
 -                                          struct moved_entry **pmb,
 +                                          struct moved_block *pmb,
                                            int pmb_nr, int n)
  {
        int i;
  
        for (; match; match = hashmap_get_next(hm, match)) {
                for (i = 0; i < pmb_nr; i++) {
 -                      struct moved_entry *prev = pmb[i];
 +                      struct moved_entry *prev = pmb[i].match;
                        struct moved_entry *cur = (prev && prev->next_line) ?
                                        prev->next_line : NULL;
                        if (!cur)
                                continue;
 -                      if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
 +                      if (!cmp_in_block_with_wsd(o, cur, match, &pmb[i], n))
                                got_match[i] |= 1;
                }
        }
  
        for (i = 0; i < pmb_nr; i++) {
                if (got_match[i]) {
 -                      /* Carry the white space delta forward */
 -                      pmb[i]->next_line->wsd = pmb[i]->wsd;
 -                      pmb[i] = pmb[i]->next_line;
 -              } else
 -                      pmb[i] = NULL;
 +                      /* Advance to the next line */
 +                      pmb[i].match = pmb[i].match->next_line;
 +              } else {
 +                      moved_block_clear(&pmb[i]);
 +              }
        }
 +
 +      free(got_match);
  }
  
 -static int shrink_potential_moved_blocks(struct moved_entry **pmb,
 +static int shrink_potential_moved_blocks(struct moved_block *pmb,
                                         int pmb_nr)
  {
        int lp, rp;
  
        /* Shrink the set of potential block to the remaining running */
        for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
 -              while (lp < pmb_nr && pmb[lp])
 +              while (lp < pmb_nr && pmb[lp].match)
                        lp++;
                /* lp points at the first NULL now */
  
 -              while (rp > -1 && !pmb[rp])
 +              while (rp > -1 && !pmb[rp].match)
                        rp--;
                /* rp points at the last non-NULL */
  
                if (lp < pmb_nr && rp > -1 && lp < rp) {
                        pmb[lp] = pmb[rp];
 -                      if (pmb[rp]->wsd) {
 -                              free(pmb[rp]->wsd->string);
 -                              FREE_AND_NULL(pmb[rp]->wsd);
 -                      }
 -                      pmb[rp] = NULL;
 +                      memset(&pmb[rp], 0, sizeof(pmb[rp]));
                        rp--;
                        lp++;
                }
   * The last block consists of the (n - block_length)'th line up to but not
   * including the nth line.
   *
 + * Returns 0 if the last block is empty or is unset by this function, non zero
 + * otherwise.
 + *
   * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
   * Think of a way to unify them.
   */
 -static void adjust_last_block(struct diff_options *o, int n, int block_length)
 +static int adjust_last_block(struct diff_options *o, int n, int block_length)
  {
        int i, alnum_count = 0;
        if (o->color_moved == COLOR_MOVED_PLAIN)
 -              return;
 +              return block_length;
        for (i = 1; i < block_length + 1; i++) {
                const char *c = o->emitted_symbols->buf[n - i].line;
                for (; *c; c++) {
                                continue;
                        alnum_count++;
                        if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
 -                              return;
 +                              return 1;
                }
        }
        for (i = 1; i < block_length + 1; i++)
                o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
 +      return 0;
  }
  
  /* Find blocks of moved code, delegate actual coloring decision to helper */
@@@ -1122,9 -1041,9 +1122,9 @@@ static void mark_color_as_moved(struct 
                                struct hashmap *add_lines,
                                struct hashmap *del_lines)
  {
 -      struct moved_entry **pmb = NULL; /* potentially moved blocks */
 +      struct moved_block *pmb = NULL; /* potentially moved blocks */
        int pmb_nr = 0, pmb_alloc = 0;
 -      int n, flipped_block = 1, block_length = 0;
 +      int n, flipped_block = 0, block_length = 0;
  
  
        for (n = 0; n < o->emitted_symbols->nr; n++) {
                struct moved_entry *key;
                struct moved_entry *match = NULL;
                struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
 +              enum diff_symbol last_symbol = 0;
  
                switch (l->s) {
                case DIFF_SYMBOL_PLUS:
                        free(key);
                        break;
                default:
 -                      flipped_block = 1;
 +                      flipped_block = 0;
                }
  
                if (!match) {
 +                      int i;
 +
                        adjust_last_block(o, n, block_length);
 +                      for(i = 0; i < pmb_nr; i++)
 +                              moved_block_clear(&pmb[i]);
                        pmb_nr = 0;
                        block_length = 0;
 +                      flipped_block = 0;
 +                      last_symbol = l->s;
                        continue;
                }
  
 -              l->flags |= DIFF_SYMBOL_MOVED_LINE;
 -
 -              if (o->color_moved == COLOR_MOVED_PLAIN)
 +              if (o->color_moved == COLOR_MOVED_PLAIN) {
 +                      last_symbol = l->s;
 +                      l->flags |= DIFF_SYMBOL_MOVED_LINE;
                        continue;
 +              }
  
                if (o->color_moved_ws_handling &
                    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
                                ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
                                if (o->color_moved_ws_handling &
                                    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
 -                                      struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
 -                                      if (compute_ws_delta(l, match->es, wsd)) {
 -                                              match->wsd = wsd;
 -                                              pmb[pmb_nr++] = match;
 -                                      } else
 -                                              free(wsd);
 +                                      if (compute_ws_delta(l, match->es,
 +                                                           &pmb[pmb_nr].wsd))
 +                                              pmb[pmb_nr++].match = match;
                                } else {
 -                                      pmb[pmb_nr++] = match;
 +                                      pmb[pmb_nr].wsd = 0;
 +                                      pmb[pmb_nr++].match = match;
                                }
                        }
  
 -                      flipped_block = (flipped_block + 1) % 2;
 +                      if (adjust_last_block(o, n, block_length) &&
 +                          pmb_nr && last_symbol != l->s)
 +                              flipped_block = (flipped_block + 1) % 2;
 +                      else
 +                              flipped_block = 0;
  
 -                      adjust_last_block(o, n, block_length);
                        block_length = 0;
                }
  
 -              block_length++;
 -
 -              if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
 -                      l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
 +              if (pmb_nr) {
 +                      block_length++;
 +                      l->flags |= DIFF_SYMBOL_MOVED_LINE;
 +                      if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
 +                              l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
 +              }
 +              last_symbol = l->s;
        }
        adjust_last_block(o, n, block_length);
  
 +      for(n = 0; n < pmb_nr; n++)
 +              moved_block_clear(&pmb[n]);
        free(pmb);
  }
  
@@@ -1282,13 -1187,12 +1282,13 @@@ static void dim_moved_lines(struct diff
  }
  
  static void emit_line_ws_markup(struct diff_options *o,
 -                              const char *set, const char *reset,
 -                              const char *line, int len,
 -                              const char *set_sign, char sign,
 +                              const char *set_sign, const char *set,
 +                              const char *reset,
 +                              int sign_index, const char *line, int len,
                                unsigned ws_rule, int blank_at_eof)
  {
        const char *ws = NULL;
 +      int sign = o->output_indicators[sign_index];
  
        if (o->ws_error_highlight & ws_rule) {
                ws = diff_get_color_opt(o, DIFF_WHITESPACE);
        }
  
        if (!ws && !set_sign)
 -              emit_line_0(o, set, 0, reset, sign, line, len);
 +              emit_line_0(o, set, NULL, 0, reset, sign, line, len);
        else if (!ws) {
 -              /* Emit just the prefix, then the rest. */
 -              emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
 -                          sign, "", 0);
 -              emit_line_0(o, set, 0, reset, 0, line, len);
 +              emit_line_0(o, set_sign, set, !!set_sign, reset, sign, line, len);
        } else if (blank_at_eof)
                /* Blank line at EOF - paint '+' as well */
 -              emit_line_0(o, ws, 0, reset, sign, line, len);
 +              emit_line_0(o, ws, NULL, 0, reset, sign, line, len);
        else {
                /* Emit just the prefix, then the rest. */
 -              emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
 +              emit_line_0(o, set_sign ? set_sign : set, NULL, !!set_sign, reset,
                            sign, "", 0);
                ws_check_emit(line, len, ws_rule,
                              o->file, set, reset, ws);
@@@ -1329,7 -1236,7 +1329,7 @@@ static void emit_diff_symbol_from_struc
                context = diff_get_color_opt(o, DIFF_CONTEXT);
                reset = diff_get_color_opt(o, DIFF_RESET);
                putc('\n', o->file);
 -              emit_line_0(o, context, 0, reset, '\\',
 +              emit_line_0(o, context, NULL, 0, reset, '\\',
                            nneof, strlen(nneof));
                break;
        case DIFF_SYMBOL_SUBMODULE_HEADER:
                        else if (c == '-')
                                set = diff_get_color_opt(o, DIFF_FILE_OLD);
                }
 -              emit_line_ws_markup(o, set, reset, line, len, set_sign, ' ',
 +              emit_line_ws_markup(o, set_sign, set, reset,
 +                                  OUTPUT_INDICATOR_CONTEXT, line, len,
                                    flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
                break;
        case DIFF_SYMBOL_PLUS:
                                set = diff_get_color_opt(o, DIFF_CONTEXT_BOLD);
                        flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
                }
 -              emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
 +              emit_line_ws_markup(o, set_sign, set, reset,
 +                                  OUTPUT_INDICATOR_NEW, line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK,
                                    flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
                break;
                        else
                                set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
                }
 -              emit_line_ws_markup(o, set, reset, line, len, set_sign, '-',
 +              emit_line_ws_markup(o, set_sign, set, reset,
 +                                  OUTPUT_INDICATOR_OLD, line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
                break;
        case DIFF_SYMBOL_WORDS_PORCELAIN:
  static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
                             const char *line, int len, unsigned flags)
  {
 -      struct emitted_diff_symbol e = {line, len, flags, s};
 +      struct emitted_diff_symbol e = {line, len, flags, 0, 0, s};
  
        if (o->emitted_symbols)
                append_emitted_diff_symbol(o, &e);
@@@ -1704,8 -1608,7 +1704,8 @@@ static void emit_hunk_header(struct emi
        strbuf_release(&msgbuf);
  }
  
 -static struct diff_tempfile *claim_diff_tempfile(void) {
 +static struct diff_tempfile *claim_diff_tempfile(void)
 +{
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
                if (!diff_temp[i].name)
@@@ -1795,12 -1698,12 +1795,12 @@@ static void emit_rewrite_diff(const cha
        quote_two_c_style(&a_name, a_prefix, name_a, 0);
        quote_two_c_style(&b_name, b_prefix, name_b, 0);
  
 -      size_one = fill_textconv(textconv_one, one, &data_one);
 -      size_two = fill_textconv(textconv_two, two, &data_two);
 +      size_one = fill_textconv(o->repo, textconv_one, one, &data_one);
 +      size_two = fill_textconv(o->repo, textconv_two, two, &data_two);
  
        memset(&ecbdata, 0, sizeof(ecbdata));
        ecbdata.color_diff = want_color(o->use_color);
 -      ecbdata.ws_rule = whitespace_rule(name_b);
 +      ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
        ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
                mmfile_t mf1, mf2;
@@@ -1980,17 -1883,19 +1980,17 @@@ static int color_words_output_graph_pre
        }
  }
  
 -static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 +static void fn_out_diff_words_aux(void *priv,
 +                                long minus_first, long minus_len,
 +                                long plus_first, long plus_len,
 +                                const char *func, long funclen)
  {
        struct diff_words_data *diff_words = priv;
        struct diff_words_style *style = diff_words->style;
 -      int minus_first, minus_len, plus_first, plus_len;
        const char *minus_begin, *minus_end, *plus_begin, *plus_end;
        struct diff_options *opt = diff_words->opt;
        const char *line_prefix;
  
 -      if (line[0] != '@' || parse_hunk_header(line, len,
 -                      &minus_first, &minus_len, &plus_first, &plus_len))
 -              return;
 -
        assert(opt);
        line_prefix = diff_line_prefix(opt);
  
@@@ -2140,8 -2045,8 +2140,8 @@@ static void diff_words_show(struct diff
        xpp.flags = 0;
        /* as only the hunk header will be parsed, we need a 0-context */
        xecfg.ctxlen = 0;
 -      if (xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
 -                        &xpp, &xecfg))
 +      if (xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, NULL,
 +                        diff_words, &xpp, &xecfg))
                die("unable to generate word diff");
        free(minus.ptr);
        free(plus.ptr);
@@@ -2186,25 -2091,23 +2186,25 @@@ static void diff_words_flush(struct emi
        }
  }
  
 -static void diff_filespec_load_driver(struct diff_filespec *one)
 +static void diff_filespec_load_driver(struct diff_filespec *one,
 +                                    struct index_state *istate)
  {
        /* Use already-loaded driver */
        if (one->driver)
                return;
  
        if (S_ISREG(one->mode))
 -              one->driver = userdiff_find_by_path(one->path);
 +              one->driver = userdiff_find_by_path(istate, one->path);
  
        /* Fallback to default settings */
        if (!one->driver)
                one->driver = userdiff_find_by_name("default");
  }
  
 -static const char *userdiff_word_regex(struct diff_filespec *one)
 +static const char *userdiff_word_regex(struct diff_filespec *one,
 +                                     struct index_state *istate)
  {
 -      diff_filespec_load_driver(one);
 +      diff_filespec_load_driver(one, istate);
        return one->driver->word_regex;
  }
  
@@@ -2227,9 -2130,9 +2227,9 @@@ static void init_diff_words_data(struc
                        xcalloc(1, sizeof(struct emitted_diff_symbols));
  
        if (!o->word_regex)
 -              o->word_regex = userdiff_word_regex(one);
 +              o->word_regex = userdiff_word_regex(one, o->repo->index);
        if (!o->word_regex)
 -              o->word_regex = userdiff_word_regex(two);
 +              o->word_regex = userdiff_word_regex(two, o->repo->index);
        if (!o->word_regex)
                o->word_regex = diff_word_regex_cfg;
        if (o->word_regex) {
@@@ -3030,11 -2933,16 +3030,11 @@@ static void show_dirstat(struct diff_op
                struct diff_filepair *p = q->queue[i];
                const char *name;
                unsigned long copied, added, damage;
 -              int content_changed;
  
                name = p->two->path ? p->two->path : p->one->path;
  
 -              if (p->one->oid_valid && p->two->oid_valid)
 -                      content_changed = oidcmp(&p->one->oid, &p->two->oid);
 -              else
 -                      content_changed = 1;
 -
 -              if (!content_changed) {
 +              if (p->one->oid_valid && p->two->oid_valid &&
 +                  oideq(&p->one->oid, &p->two->oid)) {
                        /*
                         * The SHA1 has not changed, so pre-/post-content is
                         * identical. We can therefore skip looking at the
                }
  
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
 -                      diff_populate_filespec(p->one, 0);
 -                      diff_populate_filespec(p->two, 0);
 -                      diffcore_count_changes(p->one, p->two, NULL, NULL,
 +                      diff_populate_filespec(options->repo, p->one, 0);
 +                      diff_populate_filespec(options->repo, p->two, 0);
 +                      diffcore_count_changes(options->repo,
 +                                             p->one, p->two, NULL, NULL,
                                               &copied, &added);
                        diff_free_filespec_data(p->one);
                        diff_free_filespec_data(p->two);
                } else if (DIFF_FILE_VALID(p->one)) {
 -                      diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
 +                      diff_populate_filespec(options->repo, p->one, CHECK_SIZE_ONLY);
                        copied = added = 0;
                        diff_free_filespec_data(p->one);
                } else if (DIFF_FILE_VALID(p->two)) {
 -                      diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
 +                      diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY);
                        copied = 0;
                        added = p->two->size;
                        diff_free_filespec_data(p->two);
                 * made to the preimage.
                 * If the resulting damage is zero, we know that
                 * diffcore_count_changes() considers the two entries to
 -               * be identical, but since content_changed is true, we
 +               * be identical, but since the oid changed, we
                 * know that there must have been _some_ kind of change,
                 * so we force all entries to have damage > 0.
                 */
@@@ -3196,15 -3103,6 +3196,15 @@@ static int is_conflict_marker(const cha
        return 1;
  }
  
 +static void checkdiff_consume_hunk(void *priv,
 +                                 long ob, long on, long nb, long nn,
 +                                 const char *func, long funclen)
 +
 +{
 +      struct checkdiff_t *data = priv;
 +      data->lineno = nb - 1;
 +}
 +
  static void checkdiff_consume(void *priv, char *line, unsigned long len)
  {
        struct checkdiff_t *data = priv;
                              data->o->file, set, reset, ws);
        } else if (line[0] == ' ') {
                data->lineno++;
 -      } else if (line[0] == '@') {
 -              char *plus = strchr(line, '+');
 -              if (plus)
 -                      data->lineno = strtol(plus, NULL, 10) - 1;
 -              else
 -                      die("invalid diff");
        }
  }
  
@@@ -3296,7 -3200,7 +3296,7 @@@ static void emit_binary_diff_body(struc
        }
  
        if (delta && delta_size < deflate_size) {
 -              char *s = xstrfmt("%lu", orig_size);
 +              char *s = xstrfmt("%"PRIuMAX , (uintmax_t)orig_size);
                emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
                                 s, strlen(s), 0);
                free(s);
@@@ -3346,16 -3250,15 +3346,16 @@@ static void emit_binary_diff(struct dif
        emit_binary_diff_body(o, two, one);
  }
  
 -int diff_filespec_is_binary(struct diff_filespec *one)
 +int diff_filespec_is_binary(struct repository *r,
 +                          struct diff_filespec *one)
  {
        if (one->is_binary == -1) {
 -              diff_filespec_load_driver(one);
 +              diff_filespec_load_driver(one, r->index);
                if (one->driver->binary != -1)
                        one->is_binary = one->driver->binary;
                else {
                        if (!one->data && DIFF_FILE_VALID(one))
 -                              diff_populate_filespec(one, CHECK_BINARY);
 +                              diff_populate_filespec(r, one, CHECK_BINARY);
                        if (one->is_binary == -1 && one->data)
                                one->is_binary = buffer_is_binary(one->data,
                                                one->size);
        return one->is_binary;
  }
  
 -static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
 +static const struct userdiff_funcname *
 +diff_funcname_pattern(struct diff_options *o, struct diff_filespec *one)
  {
 -      diff_filespec_load_driver(one);
 +      diff_filespec_load_driver(one, o->repo->index);
        return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
  }
  
@@@ -3381,14 -3283,13 +3381,14 @@@ void diff_set_mnemonic_prefix(struct di
                options->b_prefix = b;
  }
  
 -struct userdiff_driver *get_textconv(struct diff_filespec *one)
 +struct userdiff_driver *get_textconv(struct repository *r,
 +                                   struct diff_filespec *one)
  {
        if (!DIFF_FILE_VALID(one))
                return NULL;
  
 -      diff_filespec_load_driver(one);
 -      return userdiff_get_textconv(one->driver);
 +      diff_filespec_load_driver(one, r->index);
 +      return userdiff_get_textconv(r, one->driver);
  }
  
  static void builtin_diff(const char *name_a,
        }
  
        if (o->flags.allow_textconv) {
 -              textconv_one = get_textconv(one);
 -              textconv_two = get_textconv(two);
 +              textconv_one = get_textconv(o->repo, one);
 +              textconv_two = get_textconv(o->repo, two);
        }
  
        /* Never use a non-valid filename anywhere if at all possible */
                if ((one->mode ^ two->mode) & S_IFMT)
                        goto free_ab_and_return;
                if (complete_rewrite &&
 -                  (textconv_one || !diff_filespec_is_binary(one)) &&
 -                  (textconv_two || !diff_filespec_is_binary(two))) {
 +                  (textconv_one || !diff_filespec_is_binary(o->repo, one)) &&
 +                  (textconv_two || !diff_filespec_is_binary(o->repo, two))) {
                        emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
                                         header.buf, header.len, 0);
                        strbuf_reset(&header);
                        emit_rewrite_diff(name_a, name_b, one, two,
 -                                              textconv_one, textconv_two, o);
 +                                        textconv_one, textconv_two, o);
                        o->found_changes = 1;
                        goto free_ab_and_return;
                }
                strbuf_reset(&header);
                goto free_ab_and_return;
        } else if (!o->flags.text &&
 -          ( (!textconv_one && diff_filespec_is_binary(one)) ||
 -            (!textconv_two && diff_filespec_is_binary(two)) )) {
 +                 ( (!textconv_one && diff_filespec_is_binary(o->repo, one)) ||
 +                   (!textconv_two && diff_filespec_is_binary(o->repo, two)) )) {
                struct strbuf sb = STRBUF_INIT;
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
                    !o->flags.binary) {
 -                      if (!oidcmp(&one->oid, &two->oid)) {
 +                      if (oideq(&one->oid, &two->oid)) {
                                if (must_show_header)
                                        emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
                                                         header.buf, header.len,
                        strbuf_release(&sb);
                        goto free_ab_and_return;
                }
 -              if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 +              if (fill_mmfile(o->repo, &mf1, one) < 0 ||
 +                  fill_mmfile(o->repo, &mf2, two) < 0)
                        die("unable to read files to diff");
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                o->found_changes = 1;
        } else {
                /* Crazy xdl interfaces.. */
-               const char *diffopts = getenv("GIT_DIFF_OPTS");
+               const char *diffopts;
                const char *v;
                xpparam_t xpp;
                xdemitconf_t xecfg;
                        strbuf_reset(&header);
                }
  
 -              mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
 -              mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
 +              mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr);
 +              mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr);
  
 -              pe = diff_funcname_pattern(one);
 +              pe = diff_funcname_pattern(o, one);
                if (!pe)
 -                      pe = diff_funcname_pattern(two);
 +                      pe = diff_funcname_pattern(o, two);
  
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                        lbl[0] = NULL;
                ecbdata.label_path = lbl;
                ecbdata.color_diff = want_color(o->use_color);
 -              ecbdata.ws_rule = whitespace_rule(name_b);
 +              ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
                ecbdata.opt = o;
                        xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
                if (pe)
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
+               diffopts = getenv("GIT_DIFF_OPTS");
                if (!diffopts)
                        ;
                else if (skip_prefix(diffopts, "--unified=", &v))
                        xecfg.ctxlen = strtoul(v, NULL, 10);
                else if (skip_prefix(diffopts, "-u", &v))
                        xecfg.ctxlen = strtoul(v, NULL, 10);
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
 -              if (xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
 -                                &xpp, &xecfg))
 +              if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
 +                                &ecbdata, &xpp, &xecfg))
                        die("unable to generate diff for %s", one->path);
                if (o->word_diff)
                        free_diff_words_data(&ecbdata);
@@@ -3669,23 -3572,22 +3672,23 @@@ static void builtin_diffstat(const cha
                return;
        }
  
 -      same_contents = !oidcmp(&one->oid, &two->oid);
 +      same_contents = oideq(&one->oid, &two->oid);
  
 -      if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
 +      if (diff_filespec_is_binary(o->repo, one) ||
 +          diff_filespec_is_binary(o->repo, two)) {
                data->is_binary = 1;
                if (same_contents) {
                        data->added = 0;
                        data->deleted = 0;
                } else {
 -                      data->added = diff_filespec_size(two);
 -                      data->deleted = diff_filespec_size(one);
 +                      data->added = diff_filespec_size(o->repo, two);
 +                      data->deleted = diff_filespec_size(o->repo, one);
                }
        }
  
        else if (complete_rewrite) {
 -              diff_populate_filespec(one, 0);
 -              diff_populate_filespec(two, 0);
 +              diff_populate_filespec(o->repo, one, 0);
 +              diff_populate_filespec(o->repo, two, 0);
                data->deleted = count_lines(one->data, one->size);
                data->added = count_lines(two->data, two->size);
        }
                xpparam_t xpp;
                xdemitconf_t xecfg;
  
 -              if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 +              if (fill_mmfile(o->repo, &mf1, one) < 0 ||
 +                  fill_mmfile(o->repo, &mf2, two) < 0)
                        die("unable to read files to diff");
  
                memset(&xpp, 0, sizeof(xpp));
                xpp.anchors_nr = o->anchors_nr;
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
 -              if (xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
 -                                &xpp, &xecfg))
 +              if (xdi_diff_outf(&mf1, &mf2, discard_hunk_line,
 +                                diffstat_consume, diffstat, &xpp, &xecfg))
                        die("unable to generate diffstat for %s", one->path);
        }
  
@@@ -3731,11 -3632,10 +3734,11 @@@ static void builtin_checkdiff(const cha
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
        data.o = o;
 -      data.ws_rule = whitespace_rule(attr_path);
 -      data.conflict_marker_size = ll_merge_marker_size(attr_path);
 +      data.ws_rule = whitespace_rule(o->repo->index, attr_path);
 +      data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path);
  
 -      if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 +      if (fill_mmfile(o->repo, &mf1, one) < 0 ||
 +          fill_mmfile(o->repo, &mf2, two) < 0)
                die("unable to read files to diff");
  
        /*
         * introduced changes, and as long as the "new" side is text, we
         * can and should check what it introduces.
         */
 -      if (diff_filespec_is_binary(two))
 +      if (diff_filespec_is_binary(o->repo, two))
                goto free_and_return;
        else {
                /* Crazy xdl interfaces.. */
                memset(&xecfg, 0, sizeof(xecfg));
                xecfg.ctxlen = 1; /* at least one context line */
                xpp.flags = 0;
 -              if (xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
 +              if (xdi_diff_outf(&mf1, &mf2, checkdiff_consume_hunk,
 +                                checkdiff_consume, &data,
                                  &xpp, &xecfg))
                        die("unable to generate checkdiff for %s", one->path);
  
@@@ -3818,10 -3717,7 +3821,10 @@@ void fill_filespec(struct diff_filespe
   * the work tree has that object contents, return true, so that
   * prepare_temp_file() does not have to inflate and extract.
   */
 -static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file)
 +static int reuse_worktree_file(struct index_state *istate,
 +                             const char *name,
 +                             const struct object_id *oid,
 +                             int want_file)
  {
        const struct cache_entry *ce;
        struct stat st;
         * by diff-cache --cached, which does read the cache before
         * calling us.
         */
 -      if (!active_cache)
 +      if (!istate->cache)
                return 0;
  
        /* We want to avoid the working directory if our caller
         * Similarly, if we'd have to convert the file contents anyway, that
         * makes the optimization not worthwhile.
         */
 -      if (!want_file && would_convert_to_git(&the_index, name))
 +      if (!want_file && would_convert_to_git(istate, name))
                return 0;
  
        len = strlen(name);
 -      pos = cache_name_pos(name, len);
 +      pos = index_name_pos(istate, name, len);
        if (pos < 0)
                return 0;
 -      ce = active_cache[pos];
 +      ce = istate->cache[pos];
  
        /*
         * This is not the sha1 we are looking for, or
         * unreusable because it is not a regular file.
         */
 -      if (oidcmp(oid, &ce->oid) || !S_ISREG(ce->ce_mode))
 +      if (!oideq(oid, &ce->oid) || !S_ISREG(ce->ce_mode))
                return 0;
  
        /*
         * If ce matches the file in the work tree, we can reuse it.
         */
        if (ce_uptodate(ce) ||
 -          (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
 +          (!lstat(name, &st) && !ie_match_stat(istate, ce, &st, 0)))
                return 1;
  
        return 0;
@@@ -3919,9 -3815,7 +3922,9 @@@ static int diff_populate_gitlink(struc
   * grab the data for the blob (or file) for our own in-core comparison.
   * diff_filespec has data and size fields for this purpose.
   */
 -int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
 +int diff_populate_filespec(struct repository *r,
 +                         struct diff_filespec *s,
 +                         unsigned int flags)
  {
        int size_only = flags & CHECK_SIZE_ONLY;
        int err = 0;
                return diff_populate_gitlink(s, size_only);
  
        if (!s->oid_valid ||
 -          reuse_worktree_file(s->path, &s->oid, 0)) {
 +          reuse_worktree_file(r->index, s->path, &s->oid, 0)) {
                struct strbuf buf = STRBUF_INIT;
                struct stat st;
                int fd;
                 * point if the path requires us to run the content
                 * conversion.
                 */
 -              if (size_only && !would_convert_to_git(&the_index, s->path))
 +              if (size_only && !would_convert_to_git(r->index, s->path))
                        return 0;
  
                /*
                /*
                 * Convert from working tree format to canonical git format
                 */
 -              if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) {
 +              if (convert_to_git(r->index, s->path, s->data, s->size, &buf, conv_flags)) {
                        size_t size = 0;
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
        else {
                enum object_type type;
                if (size_only || (flags & CHECK_BINARY)) {
 -                      type = oid_object_info(the_repository, &s->oid,
 -                                             &s->size);
 +                      type = oid_object_info(r, &s->oid, &s->size);
                        if (type < 0)
                                die("unable to read %s",
                                    oid_to_hex(&s->oid));
@@@ -4058,8 -3953,7 +4061,8 @@@ void diff_free_filespec_data(struct dif
        FREE_AND_NULL(s->cnt_data);
  }
  
 -static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
 +static void prep_temp_blob(struct index_state *istate,
 +                         const char *path, struct diff_tempfile *temp,
                           void *blob,
                           unsigned long size,
                           const struct object_id *oid,
        temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1);
        if (!temp->tempfile)
                die_errno("unable to create temp-file");
 -      if (convert_to_working_tree(&the_index, path,
 +      if (convert_to_working_tree(istate, path,
                        (const char *)blob, (size_t)size, &buf)) {
                blob = buf.buf;
                size = buf.len;
        free(path_dup);
  }
  
 -static struct diff_tempfile *prepare_temp_file(const char *name,
 -              struct diff_filespec *one)
 +static struct diff_tempfile *prepare_temp_file(struct repository *r,
 +                                             const char *name,
 +                                             struct diff_filespec *one)
  {
        struct diff_tempfile *temp = claim_diff_tempfile();
  
  
        if (!S_ISGITLINK(one->mode) &&
            (!one->oid_valid ||
 -           reuse_worktree_file(name, &one->oid, 1))) {
 +           reuse_worktree_file(r->index, name, &one->oid, 1))) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
                        struct strbuf sb = STRBUF_INIT;
                        if (strbuf_readlink(&sb, name, st.st_size) < 0)
                                die_errno("readlink(%s)", name);
 -                      prep_temp_blob(name, temp, sb.buf, sb.len,
 +                      prep_temp_blob(r->index, name, temp, sb.buf, sb.len,
                                       (one->oid_valid ?
                                        &one->oid : &null_oid),
                                       (one->oid_valid ?
                return temp;
        }
        else {
 -              if (diff_populate_filespec(one, 0))
 +              if (diff_populate_filespec(r, one, 0))
                        die("cannot read data blob for %s", one->path);
 -              prep_temp_blob(name, temp, one->data, one->size,
 +              prep_temp_blob(r->index, name, temp,
 +                             one->data, one->size,
                               &one->oid, one->mode);
        }
        return temp;
  }
  
 -static void add_external_diff_name(struct argv_array *argv,
 +static void add_external_diff_name(struct repository *r,
 +                                 struct argv_array *argv,
                                   const char *name,
                                   struct diff_filespec *df)
  {
 -      struct diff_tempfile *temp = prepare_temp_file(name, df);
 +      struct diff_tempfile *temp = prepare_temp_file(r, name, df);
        argv_array_push(argv, temp->name);
        argv_array_push(argv, temp->hex);
        argv_array_push(argv, temp->mode);
@@@ -4191,11 -4082,11 +4194,11 @@@ static void run_external_diff(const cha
        argv_array_push(&argv, name);
  
        if (one && two) {
 -              add_external_diff_name(&argv, name, one);
 +              add_external_diff_name(o->repo, &argv, name, one);
                if (!other)
 -                      add_external_diff_name(&argv, name, two);
 +                      add_external_diff_name(o->repo, &argv, name, two);
                else {
 -                      add_external_diff_name(&argv, other, two);
 +                      add_external_diff_name(o->repo, &argv, other, two);
                        argv_array_push(&argv, other);
                        argv_array_push(&argv, xfrm_msg);
                }
@@@ -4282,16 -4173,14 +4285,16 @@@ static void fill_metainfo(struct strbu
        default:
                *must_show_header = 0;
        }
 -      if (one && two && oidcmp(&one->oid, &two->oid)) {
 +      if (one && two && !oideq(&one->oid, &two->oid)) {
                const unsigned hexsz = the_hash_algo->hexsz;
                int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV;
  
                if (o->flags.binary) {
                        mmfile_t mf;
 -                      if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
 -                          (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
 +                      if ((!fill_mmfile(o->repo, &mf, one) &&
 +                           diff_filespec_is_binary(o->repo, one)) ||
 +                          (!fill_mmfile(o->repo, &mf, two) &&
 +                           diff_filespec_is_binary(o->repo, two)))
                                abbrev = hexsz;
                }
                strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
@@@ -4319,9 -4208,7 +4322,9 @@@ static void run_diff_cmd(const char *pg
  
  
        if (o->flags.allow_external) {
 -              struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
 +              struct userdiff_driver *drv;
 +
 +              drv = userdiff_find_by_path(o->repo->index, attr_path);
                if (drv && drv->external)
                        pgm = drv->external;
        }
                fprintf(o->file, "* Unmerged path %s\n", name);
  }
  
 -static void diff_fill_oid_info(struct diff_filespec *one)
 +static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate)
  {
        if (DIFF_FILE_VALID(one)) {
                if (!one->oid_valid) {
                        }
                        if (lstat(one->path, &st) < 0)
                                die_errno("stat '%s'", one->path);
 -                      if (index_path(&one->oid, one->path, &st, 0))
 +                      if (index_path(istate, &one->oid, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
        }
@@@ -4409,8 -4296,8 +4412,8 @@@ static void run_diff(struct diff_filepa
                return;
        }
  
 -      diff_fill_oid_info(one);
 -      diff_fill_oid_info(two);
 +      diff_fill_oid_info(one, o->repo->index);
 +      diff_fill_oid_info(two, o->repo->index);
  
        if (!pgm &&
            DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
                 */
                struct diff_filespec *null = alloc_filespec(two->path);
                run_diff_cmd(NULL, name, other, attr_path,
 -                           one, null, &msg, o, p);
 +                           one, null, &msg,
 +                           o, p);
                free(null);
                strbuf_release(&msg);
  
@@@ -4446,8 -4332,7 +4449,8 @@@ static void run_diffstat(struct diff_fi
  
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
 -              builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, p);
 +              builtin_diffstat(p->one->path, NULL, NULL, NULL,
 +                               diffstat, o, p);
                return;
        }
  
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
  
 -      diff_fill_oid_info(p->one);
 -      diff_fill_oid_info(p->two);
 +      diff_fill_oid_info(p->one, o->repo->index);
 +      diff_fill_oid_info(p->two, o->repo->index);
  
 -      builtin_diffstat(name, other, p->one, p->two, diffstat, o, p);
 +      builtin_diffstat(name, other, p->one, p->two,
 +                       diffstat, o, p);
  }
  
  static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
  
 -      diff_fill_oid_info(p->one);
 -      diff_fill_oid_info(p->two);
 +      diff_fill_oid_info(p->one, o->repo->index);
 +      diff_fill_oid_info(p->two, o->repo->index);
  
        builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
  }
  
 -void diff_setup(struct diff_options *options)
 +void repo_diff_setup(struct repository *r, struct diff_options *options)
  {
        memcpy(options, &default_diff_options, sizeof(*options));
  
        options->file = stdout;
 +      options->repo = r;
  
 +      options->output_indicators[OUTPUT_INDICATOR_NEW] = '+';
 +      options->output_indicators[OUTPUT_INDICATOR_OLD] = '-';
 +      options->output_indicators[OUTPUT_INDICATOR_CONTEXT] = ' ';
        options->abbrev = DEFAULT_ABBREV;
        options->line_termination = '\n';
        options->break_opt = -1;
@@@ -4887,8 -4767,7 +4890,8 @@@ static int parse_diff_filter_opt(const 
        return 0;
  }
  
 -static void enable_patch_output(int *fmt) {
 +static void enable_patch_output(int *fmt)
 +{
        *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
        *fmt |= DIFF_FORMAT_PATCH;
  }
@@@ -4976,12 -4855,6 +4979,12 @@@ int diff_opt_parse(struct diff_options 
                 options->output_format |= DIFF_FORMAT_DIFFSTAT;
        } else if (!strcmp(arg, "--no-compact-summary"))
                 options->flags.stat_with_summary = 0;
 +      else if (skip_prefix(arg, "--output-indicator-new=", &arg))
 +              options->output_indicators[OUTPUT_INDICATOR_NEW] = arg[0];
 +      else if (skip_prefix(arg, "--output-indicator-old=", &arg))
 +              options->output_indicators[OUTPUT_INDICATOR_OLD] = arg[0];
 +      else if (skip_prefix(arg, "--output-indicator-context=", &arg))
 +              options->output_indicators[OUTPUT_INDICATOR_CONTEXT] = arg[0];
  
        /* renames options */
        else if (starts_with(arg, "-B") ||
        else if (skip_prefix(arg, "--color-moved=", &arg)) {
                int cm = parse_color_moved(arg);
                if (cm < 0)
 -                      die("bad --color-moved argument: %s", arg);
 +                      return error("bad --color-moved argument: %s", arg);
                options->color_moved = cm;
 +      } else if (!strcmp(arg, "--no-color-moved-ws")) {
 +              options->color_moved_ws_handling = 0;
        } else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
 -              options->color_moved_ws_handling = parse_color_moved_ws(arg);
 +              unsigned cm = parse_color_moved_ws(arg);
 +              if (cm & COLOR_MOVED_WS_ERROR)
 +                      return -1;
 +              options->color_moved_ws_handling = cm;
        } else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
@@@ -5458,7 -5326,7 +5461,7 @@@ int diff_unmodified_pair(struct diff_fi
         * dealing with a change.
         */
        if (one->oid_valid && two->oid_valid &&
 -          !oidcmp(&one->oid, &two->oid) &&
 +          oideq(&one->oid, &two->oid) &&
            !one->dirty_submodule && !two->dirty_submodule)
                return 1; /* no change */
        if (!one->oid_valid && !two->oid_valid)
@@@ -5592,7 -5460,7 +5595,7 @@@ static void diff_resolve_rename_copy(vo
                        else
                                p->status = DIFF_STATUS_RENAMED;
                }
 -              else if (oidcmp(&p->one->oid, &p->two->oid) ||
 +              else if (!oideq(&p->one->oid, &p->two->oid) ||
                         p->one->mode != p->two->mode ||
                         p->one->dirty_submodule ||
                         p->two->dirty_submodule ||
@@@ -5742,6 -5610,10 +5745,6 @@@ static void patch_id_consume(void *priv
        struct patch_id_t *data = priv;
        int new_len;
  
 -      /* Ignore line numbers when computing the SHA1 of the patch */
 -      if (starts_with(line, "@@ -"))
 -              return;
 -
        new_len = remove_space(line, len);
  
        git_SHA1_Update(data->ctx, line, new_len);
@@@ -5794,8 -5666,8 +5797,8 @@@ static int diff_get_patch_id(struct dif
                if (DIFF_PAIR_UNMERGED(p))
                        continue;
  
 -              diff_fill_oid_info(p->one);
 -              diff_fill_oid_info(p->two);
 +              diff_fill_oid_info(p->one, options->repo->index);
 +              diff_fill_oid_info(p->two, options->repo->index);
  
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
                if (diff_header_only)
                        continue;
  
 -              if (fill_mmfile(&mf1, p->one) < 0 ||
 -                  fill_mmfile(&mf2, p->two) < 0)
 +              if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
 +                  fill_mmfile(options->repo, &mf2, p->two) < 0)
                        return error("unable to read files to diff");
  
 -              if (diff_filespec_is_binary(p->one) ||
 -                  diff_filespec_is_binary(p->two)) {
 +              if (diff_filespec_is_binary(options->repo, p->one) ||
 +                  diff_filespec_is_binary(options->repo, p->two)) {
                        git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
                                        GIT_SHA1_HEXSZ);
                        git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
                xpp.flags = 0;
                xecfg.ctxlen = 3;
                xecfg.flags = 0;
 -              if (xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
 -                                &xpp, &xecfg))
 +              if (xdi_diff_outf(&mf1, &mf2, discard_hunk_line,
 +                                patch_id_consume, &data, &xpp, &xecfg))
                        return error("unable to generate patch-id diff for %s",
                                     p->one->path);
        }
@@@ -5950,8 -5822,8 +5953,8 @@@ static void diff_flush_patch_all_file_p
                        if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
                                dim_moved_lines(o);
  
 -                      hashmap_free(&add_lines, 0);
 -                      hashmap_free(&del_lines, 0);
 +                      hashmap_free(&add_lines, 1);
 +                      hashmap_free(&del_lines, 1);
                }
  
                for (i = 0; i < esm.nr; i++)
@@@ -6135,21 -6007,19 +6138,21 @@@ static void diffcore_apply_filter(struc
  }
  
  /* Check whether two filespecs with the same mode and size are identical */
 -static int diff_filespec_is_identical(struct diff_filespec *one,
 +static int diff_filespec_is_identical(struct repository *r,
 +                                    struct diff_filespec *one,
                                      struct diff_filespec *two)
  {
        if (S_ISGITLINK(one->mode))
                return 0;
 -      if (diff_populate_filespec(one, 0))
 +      if (diff_populate_filespec(r, one, 0))
                return 0;
 -      if (diff_populate_filespec(two, 0))
 +      if (diff_populate_filespec(r, two, 0))
                return 0;
        return !memcmp(one->data, two->data, one->size);
  }
  
 -static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
 +static int diff_filespec_check_stat_unmatch(struct repository *r,
 +                                          struct diff_filepair *p)
  {
        if (p->done_skip_stat_unmatch)
                return p->skip_stat_unmatch_result;
            !DIFF_FILE_VALID(p->two) ||
            (p->one->oid_valid && p->two->oid_valid) ||
            (p->one->mode != p->two->mode) ||
 -          diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
 -          diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
 +          diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) ||
 +          diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) ||
            (p->one->size != p->two->size) ||
 -          !diff_filespec_is_identical(p->one, p->two)) /* (2) */
 +          !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */
                p->skip_stat_unmatch_result = 1;
        return p->skip_stat_unmatch_result;
  }
@@@ -6191,7 -6061,7 +6194,7 @@@ static void diffcore_skip_stat_unmatch(
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
  
 -              if (diff_filespec_check_stat_unmatch(p))
 +              if (diff_filespec_check_stat_unmatch(diffopt->repo, p))
                        diff_q(&outq, p);
                else {
                        /*
@@@ -6233,8 -6103,7 +6236,8 @@@ void diffcore_std(struct diff_options *
        if (!options->found_follow) {
                /* See try_to_follow_renames() in tree-diff.c */
                if (options->break_opt != -1)
 -                      diffcore_break(options->break_opt);
 +                      diffcore_break(options->repo,
 +                                     options->break_opt);
                if (options->detect_rename)
                        diffcore_rename(options);
                if (options->break_opt != -1)
@@@ -6385,7 -6254,7 +6388,7 @@@ void diff_change(struct diff_options *o
                return;
  
        if (options->flags.quick && options->skip_stat_unmatch &&
 -          !diff_filespec_check_stat_unmatch(p))
 +          !diff_filespec_check_stat_unmatch(options->repo, p))
                return;
  
        options->flags.has_changes = 1;
@@@ -6407,10 -6276,8 +6410,10 @@@ struct diff_filepair *diff_unmerge(stru
        return pair;
  }
  
 -static char *run_textconv(const char *pgm, struct diff_filespec *spec,
 -              size_t *outsize)
 +static char *run_textconv(struct repository *r,
 +                        const char *pgm,
 +                        struct diff_filespec *spec,
 +                        size_t *outsize)
  {
        struct diff_tempfile *temp;
        const char *argv[3];
        struct strbuf buf = STRBUF_INIT;
        int err = 0;
  
 -      temp = prepare_temp_file(spec->path, spec);
 +      temp = prepare_temp_file(r, spec->path, spec);
        *arg++ = pgm;
        *arg++ = temp->name;
        *arg = NULL;
        return strbuf_detach(&buf, outsize);
  }
  
 -size_t fill_textconv(struct userdiff_driver *driver,
 +size_t fill_textconv(struct repository *r,
 +                   struct userdiff_driver *driver,
                     struct diff_filespec *df,
                     char **outbuf)
  {
                        *outbuf = "";
                        return 0;
                }
 -              if (diff_populate_filespec(df, 0))
 +              if (diff_populate_filespec(r, df, 0))
                        die("unable to read files to diff");
                *outbuf = df->data;
                return df->size;
                        return size;
        }
  
 -      *outbuf = run_textconv(driver->textconv, df, &size);
 +      *outbuf = run_textconv(r, driver->textconv, df, &size);
        if (!*outbuf)
                die("unable to read files to diff");
  
        return size;
  }
  
 -int textconv_object(const char *path,
 +int textconv_object(struct repository *r,
 +                  const char *path,
                    unsigned mode,
                    const struct object_id *oid,
                    int oid_valid,
  
        df = alloc_filespec(path);
        fill_filespec(df, oid, oid_valid, mode);
 -      textconv = get_textconv(df);
 +      textconv = get_textconv(r, df);
        if (!textconv) {
                free_filespec(df);
                return 0;
        }
  
 -      *buf_size = fill_textconv(textconv, df, buf);
 +      *buf_size = fill_textconv(r, textconv, df, buf);
        free_filespec(df);
        return 1;
  }
diff --combined environment.c
index 0e37741d838256d196b2ec9f3ca4e54395c66d31,8fa10cc4310373683a087e539d4d494c6eff2279..89af47cb8504903b90460d5faa91be9f61bd2fc3
@@@ -33,7 -33,6 +33,7 @@@ int ref_paranoia = -1
  int repository_format_precious_objects;
  char *repository_format_partial_clone;
  const char *core_partial_clone_filter_default;
 +int repository_format_worktree_config;
  const char *git_commit_encoding;
  const char *git_log_output_encoding;
  const char *apply_default_whitespace;
@@@ -72,6 -71,7 +72,6 @@@ int core_apply_sparse_checkout
  int merge_log_config = -1;
  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
  unsigned long pack_size_limit_cfg;
 -enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
  enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
  
  #ifndef PROTECT_HFS_DEFAULT
@@@ -107,7 -107,7 +107,7 @@@ char *git_work_tree_cfg
  
  static char *git_namespace;
  
- static const char *super_prefix;
+ static char *super_prefix;
  
  /*
   * Repository-local GIT_* environment variables; see cache.h for details.
@@@ -240,7 -240,7 +240,7 @@@ const char *get_super_prefix(void
  {
        static int initialized;
        if (!initialized) {
-               super_prefix = getenv(GIT_SUPER_PREFIX_ENVIRONMENT);
+               super_prefix = xstrdup_or_null(getenv(GIT_SUPER_PREFIX_ENVIRONMENT));
                initialized = 1;
        }
        return super_prefix;
@@@ -274,9 -274,9 +274,9 @@@ const char *get_git_work_tree(void
  
  char *get_object_directory(void)
  {
 -      if (!the_repository->objects->objectdir)
 +      if (!the_repository->objects->odb)
                BUG("git environment hasn't been setup");
 -      return the_repository->objects->objectdir;
 +      return the_repository->objects->odb->path;
  }
  
  int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)