Merge branch 'bw/diff-opt-impl-to-bitfields'
authorJunio C Hamano <gitster@pobox.com>
Thu, 9 Nov 2017 05:31:27 +0000 (14:31 +0900)
committerJunio C Hamano <gitster@pobox.com>
Thu, 9 Nov 2017 05:31:27 +0000 (14:31 +0900)
A single-word "unsigned flags" in the diff options is being split
into a structure with many bitfields.

* bw/diff-opt-impl-to-bitfields:
diff: make struct diff_flags members lowercase
diff: remove DIFF_OPT_CLR macro
diff: remove DIFF_OPT_SET macro
diff: remove DIFF_OPT_TST macro
diff: remove touched flags
diff: add flag to indicate textconv was set via cmdline
diff: convert flags to be stored in bitfields
add, reset: use DIFF_OPT_SET macro to set a diff flag

20 files changed:
1  2 
blame.c
builtin/am.c
builtin/commit.c
builtin/diff.c
builtin/fast-export.c
builtin/log.c
builtin/merge-ours.c
builtin/reset.c
builtin/rev-list.c
combine-diff.c
diff-lib.c
diff.c
diff.h
log-tree.c
merge-recursive.c
notes-merge.c
revision.c
sequencer.c
submodule.c
wt-status.c
diff --combined blame.c
index c3060de2f87f9cdc598019663186755b52a31953,28e03726ff387c0da957d02043a14d07fb1a9291..2893f3c1030aab91a42ff9e0daf8a54ba8c3ef3c
+++ b/blame.c
@@@ -166,7 -166,7 +166,7 @@@ static struct commit *fake_working_tree
        commit->date = now;
        parent_tail = &commit->parents;
  
 -      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
 +      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                die("no such ref: HEAD");
  
        parent_tail = append_parent(parent_tail, &head_oid);
  
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
-                       if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+                       if (opt->flags.allow_textconv &&
                            textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
                                strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
@@@ -293,7 -293,7 +293,7 @@@ static void fill_origin_blob(struct dif
                unsigned long file_size;
  
                (*num_read_blob)++;
-               if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+               if (opt->flags.allow_textconv &&
                    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
                        ;
                else
@@@ -541,7 -541,7 +541,7 @@@ static struct blame_origin *find_origin
         * same and diff-tree is fairly efficient about this.
         */
        diff_setup(&diff_opts);
-       DIFF_OPT_SET(&diff_opts, RECURSIVE);
+       diff_opts.flags.recursive = 1;
        diff_opts.detect_rename = 0;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        paths[0] = origin->path;
@@@ -615,7 -615,7 +615,7 @@@ static struct blame_origin *find_rename
        int i;
  
        diff_setup(&diff_opts);
-       DIFF_OPT_SET(&diff_opts, RECURSIVE);
+       diff_opts.flags.recursive = 1;
        diff_opts.detect_rename = DIFF_DETECT_RENAME;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_opts.single_follow = origin->path;
@@@ -1238,7 -1238,7 +1238,7 @@@ static void find_copy_in_parent(struct 
                return; /* nothing remains for this target */
  
        diff_setup(&diff_opts);
-       DIFF_OPT_SET(&diff_opts, RECURSIVE);
+       diff_opts.flags.recursive = 1;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
  
        diff_setup_done(&diff_opts);
        if ((opt & PICKAXE_BLAME_COPY_HARDEST)
            || ((opt & PICKAXE_BLAME_COPY_HARDER)
                && (!porigin || strcmp(target->path, porigin->path))))
-               DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
+               diff_opts.flags.find_copies_harder = 1;
  
        if (is_null_oid(&target->commit->object.oid))
                do_diff_cache(&parent->tree->object.oid, &diff_opts);
                              &target->commit->tree->object.oid,
                              "", &diff_opts);
  
-       if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
+       if (!diff_opts.flags.find_copies_harder)
                diffcore_std(&diff_opts);
  
        do {
@@@ -1689,7 -1689,7 +1689,7 @@@ static struct commit *dwim_reverse_init
                return NULL;
  
        /* Do we have HEAD? */
 -      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
 +      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                return NULL;
        head_commit = lookup_commit_reference_gently(&head_oid, 1);
        if (!head_commit)
@@@ -1825,7 -1825,7 +1825,7 @@@ void setup_scoreboard(struct blame_scor
                if (fill_blob_sha1_and_mode(o))
                        die(_("no such path %s in %s"), path, final_commit_name);
  
-               if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&
+               if (sb->revs->diffopt.flags.allow_textconv &&
                    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
                                    &sb->final_buf_size))
                        ;
diff --combined builtin/am.c
index 4b6f1534f88407cf569c7f8e4c4b1df4ba3fc1e0,b281d58f30609ddfd954eedb330cc043d024fca2..92c48535052c2e32e46eeedfc86b009e7ae0726c
@@@ -1068,8 -1068,8 +1068,8 @@@ static void am_setup(struct am_state *s
        if (!get_oid("HEAD", &curr_head)) {
                write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
                if (!state->rebasing)
 -                      update_ref_oid("am", "ORIG_HEAD", &curr_head, NULL, 0,
 -                                      UPDATE_REFS_DIE_ON_ERR);
 +                      update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
 +                                 UPDATE_REFS_DIE_ON_ERR);
        } else {
                write_state_text(state, "abort-safety", "");
                if (!state->rebasing)
@@@ -1134,11 -1134,11 +1134,11 @@@ static const char *msgnum(const struct 
   */
  static void refresh_and_write_cache(void)
  {
 -      struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock_file = LOCK_INIT;
  
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write index file"));
  }
  
@@@ -1157,9 -1157,9 +1157,9 @@@ static int index_has_changes(struct str
                struct diff_options opt;
  
                diff_setup(&opt);
-               DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
+               opt.flags.exit_with_status = 1;
                if (!sb)
-                       DIFF_OPT_SET(&opt, QUICK);
+                       opt.flags.quick = 1;
                do_diff_cache(&head, &opt);
                diffcore_std(&opt);
                for (i = 0; sb && i < diff_queued_diff.nr; i++) {
                        strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
                }
                diff_flush(&opt);
-               return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
+               return opt.flags.has_changes != 0;
        } else {
                for (i = 0; sb && i < active_nr; i++) {
                        if (i)
@@@ -1409,8 -1409,8 +1409,8 @@@ static void write_commit_patch(const st
        rev_info.show_root_diff = 1;
        rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev_info.no_commit_id = 1;
-       DIFF_OPT_SET(&rev_info.diffopt, BINARY);
-       DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
+       rev_info.diffopt.flags.binary = 1;
+       rev_info.diffopt.flags.full_index = 1;
        rev_info.diffopt.use_color = 0;
        rev_info.diffopt.file = fp;
        rev_info.diffopt.close_file = 1;
@@@ -1488,10 -1488,11 +1488,10 @@@ static int run_apply(const struct am_st
        struct argv_array apply_opts = ARGV_ARRAY_INIT;
        struct apply_state apply_state;
        int res, opts_left;
 -      static struct lock_file lock_file;
        int force_apply = 0;
        int options = 0;
  
 -      if (init_apply_state(&apply_state, NULL, &lock_file))
 +      if (init_apply_state(&apply_state, NULL))
                die("BUG: init_apply_state() failed");
  
        argv_array_push(&apply_opts, "apply");
@@@ -1685,8 -1686,8 +1685,8 @@@ static void do_commit(const struct am_s
        strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
                        state->msg);
  
 -      update_ref_oid(sb.buf, "HEAD", &commit, old_oid, 0,
 -                      UPDATE_REFS_DIE_ON_ERR);
 +      update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
 +                 UPDATE_REFS_DIE_ON_ERR);
  
        if (state->rebasing) {
                FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@@ -1945,14 -1946,15 +1945,14 @@@ next
   */
  static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
  {
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[2];
  
        if (parse_tree(head) || parse_tree(remote))
                return -1;
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        refresh_cache(REFRESH_QUIET);
  
        init_tree_desc(&t[1], remote->buffer, remote->size);
  
        if (unpack_trees(2, t, &opts)) {
 -              rollback_lock_file(lock_file);
 +              rollback_lock_file(&lock_file);
                return -1;
        }
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        return 0;
   */
  static int merge_tree(struct tree *tree)
  {
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[1];
  
        if (parse_tree(tree))
                return -1;
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        init_tree_desc(&t[0], tree->buffer, tree->size);
  
        if (unpack_trees(1, t, &opts)) {
 -              rollback_lock_file(lock_file);
 +              rollback_lock_file(&lock_file);
                return -1;
        }
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        return 0;
@@@ -2132,7 -2135,7 +2132,7 @@@ static void am_abort(struct am_state *s
  
        am_rerere_clear();
  
 -      curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
 +      curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
        has_curr_head = curr_branch && !is_null_oid(&curr_head);
        if (!has_curr_head)
                hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
        clean_index(&curr_head, &orig_head);
  
        if (has_orig_head)
 -              update_ref_oid("am --abort", "HEAD", &orig_head,
 -                              has_curr_head ? &curr_head : NULL, 0,
 -                              UPDATE_REFS_DIE_ON_ERR);
 +              update_ref("am --abort", "HEAD", &orig_head,
 +                         has_curr_head ? &curr_head : NULL, 0,
 +                         UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
                delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
  
diff --combined builtin/commit.c
index c38542ee468c11c744cecf1c8b74ecc09e21f58d,0f368ad8145727daf76f14060bd59586e8db7984..605ea8c0e9663726c64950dba07afe21516c9b26
@@@ -355,7 -355,7 +355,7 @@@ static const char *prepare_index(int ar
  
                refresh_cache_or_die(refresh_flags);
  
 -              if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
 +              if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to create temporary index"));
  
                old_index_env = getenv(INDEX_ENVIRONMENT);
                if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
                        if (reopen_lock_file(&index_lock) < 0)
                                die(_("unable to write index file"));
 -                      if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
 +                      if (write_locked_index(&the_index, &index_lock, 0))
                                die(_("unable to update temporary index"));
                } else
                        warning(_("Failed to update main cache tree"));
                add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
 -              if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
 +              if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
                ret = get_lock_file_path(&index_lock);
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
        update_main_cache_tree(WRITE_TREE_SILENT);
 -      if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
 +      if (write_locked_index(&the_index, &index_lock, 0))
                die(_("unable to write new_index file"));
  
        hold_lock_file_for_update(&false_lock,
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
  
 -      if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
 +      if (write_locked_index(&the_index, &false_lock, 0))
                die(_("unable to write temporary index file"));
  
        discard_cache();
@@@ -912,11 -912,12 +912,12 @@@ static int prepare_to_commit(const cha
                         * submodules which were manually staged, which would
                         * be really confusing.
                         */
-                       int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+                       struct diff_flags flags = DIFF_FLAGS_INIT;
+                       flags.override_submodule_config = 1;
                        if (ignore_submodule_arg &&
                            !strcmp(ignore_submodule_arg, "all"))
-                               diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
-                       commitable = index_differs_from(parent, diff_flags, 1);
+                               flags.ignore_submodules = 1;
+                       commitable = index_differs_from(parent, &flags, 1);
                }
        }
        strbuf_release(&committer_ident);
@@@ -1492,8 -1493,6 +1493,8 @@@ static void print_summary(const char *p
        diff_setup_done(&rev.diffopt);
  
        head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
 +      if (!head)
 +              die_errno(_("unable to resolve HEAD after creating commit"));
        if (!strcmp(head, "HEAD"))
                head = _("detached HEAD");
        else
@@@ -1790,9 -1789,9 +1791,9 @@@ int cmd_commit(int argc, const char **a
  
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
 -          ref_transaction_update(transaction, "HEAD", oid.hash,
 +          ref_transaction_update(transaction, "HEAD", &oid,
                                   current_head
 -                                 ? current_head->object.oid.hash : null_sha1,
 +                                 ? &current_head->object.oid : &null_oid,
                                   0, sb.buf, &err) ||
            ref_transaction_commit(transaction, &err)) {
                rollback_index_files();
diff --combined builtin/diff.c
index aa6f746795fb82feb4de1ff28f8f0350ee18092c,3476e95e1ad2294264f13bb7ead90a5f0bca33dd..9808d062a80a2ff07699fb0ea4cbd29f560630d3
@@@ -44,7 -44,7 +44,7 @@@ static void stuff_change(struct diff_op
            !oidcmp(old_oid, new_oid) && (old_mode == new_mode))
                return;
  
-       if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
+       if (opt->flags.reverse_diff) {
                SWAP(old_mode, new_mode);
                SWAP(old_oid, new_oid);
                SWAP(old_path, new_path);
@@@ -203,16 -203,17 +203,16 @@@ static int builtin_diff_combined(struc
  
  static void refresh_index_quietly(void)
  {
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        int fd;
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      fd = hold_locked_index(lock_file, 0);
 +      fd = hold_locked_index(&lock_file, 0);
        if (fd < 0)
                return;
        discard_cache();
        read_cache();
        refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
 -      update_index_if_able(&the_index, lock_file);
 +      update_index_if_able(&the_index, &lock_file);
  }
  
  static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
@@@ -349,8 -350,8 +349,8 @@@ int cmd_diff(int argc, const char **arg
        rev.diffopt.stat_graph_width = -1;
  
        /* Default to let external and textconv be used */
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+       rev.diffopt.flags.allow_external = 1;
+       rev.diffopt.flags.allow_textconv = 1;
  
        if (nongit)
                die(_("Not a git repository"));
                diff_setup_done(&rev.diffopt);
        }
  
-       DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+       rev.diffopt.flags.recursive = 1;
  
        setup_diff_pager(&rev.diffopt);
  
diff --combined builtin/fast-export.c
index d74c73f7776fcf76bc66679b1e34513d7c75d41a,72672665be292bbd25f64177dd086782e15f9302..f8fe04ca5332541213dc059dd02fdf09e0c6cf85
@@@ -823,7 -823,7 +823,7 @@@ static void get_tags_and_duplicates(str
                if (e->flags & UNINTERESTING)
                        continue;
  
 -              if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1)
 +              if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)
                        continue;
  
                if (refspecs) {
@@@ -1066,7 -1066,7 +1066,7 @@@ int cmd_fast_export(int argc, const cha
                die("revision walk setup failed");
        revs.diffopt.format_callback = show_filemodify;
        revs.diffopt.format_callback_data = &paths_of_changed_objects;
-       DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+       revs.diffopt.flags.recursive = 1;
        while ((commit = get_revision(&revs))) {
                if (has_unshown_parent(commit)) {
                        add_object_array(&commit->object, NULL, &commits);
diff --combined builtin/log.c
index ba9d4cd7863b54ec02c62b73220883a8fca3a33f,f3d5a02bea5fb3847c65afb78860bf171a7f3036..6c1fa896ad01a8de0dcd99be80827d436d2cc857
@@@ -121,20 -121,19 +121,19 @@@ static void cmd_log_init_defaults(struc
        if (fmt_pretty)
                get_commit_format(fmt_pretty, rev);
        if (default_follow)
-               DIFF_OPT_SET(&rev->diffopt, DEFAULT_FOLLOW_RENAMES);
+               rev->diffopt.flags.default_follow_renames = 1;
        rev->verbose_header = 1;
-       DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+       rev->diffopt.flags.recursive = 1;
        rev->diffopt.stat_width = -1; /* use full terminal width */
        rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
        rev->show_signature = default_show_signature;
-       DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
+       rev->diffopt.flags.allow_textconv = 1;
  
        if (default_date_mode)
                parse_date_format(default_date_mode, &rev->date_mode);
-       rev->diffopt.touched_flags = 0;
  }
  
  static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                init_display_notes(&rev->notes_opt);
  
        if (rev->diffopt.pickaxe || rev->diffopt.filter ||
-           DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
+           rev->diffopt.flags.follow_renames)
                rev->always_show_header = 0;
  
        if (source)
@@@ -392,7 -391,7 +391,7 @@@ static int cmd_log_walk(struct rev_inf
                fclose(rev->diffopt.file);
  
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
-           DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+           rev->diffopt.flags.check_failed) {
                return 02;
        }
        return diff_result_code(&rev->diffopt, 0);
@@@ -484,8 -483,8 +483,8 @@@ static int show_blob_object(const struc
        unsigned long size;
  
        fflush(rev->diffopt.file);
-       if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
-           !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
+       if (!rev->diffopt.flags.textconv_set_via_cmdline ||
+           !rev->diffopt.flags.allow_textconv)
                return stream_blob_to_fd(1, oid, NULL, 0);
  
        if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
@@@ -667,9 -666,9 +666,9 @@@ int cmd_log_reflog(int argc, const cha
  static void log_setup_revisions_tweak(struct rev_info *rev,
                                      struct setup_revision_opt *opt)
  {
-       if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) &&
+       if (rev->diffopt.flags.default_follow_renames &&
            rev->prune_data.nr == 1)
-               DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES);
+               rev->diffopt.flags.follow_renames = 1;
  
        /* Turn --cc/-c into -p --cc/-c when -p was not given */
        if (!rev->diffopt.output_format && rev->combine_merges)
@@@ -975,7 -974,7 +974,7 @@@ static char *find_branch_name(struct re
                return NULL;
        ref = rev->cmdline.rev[positive].name;
        tip_oid = &rev->cmdline.rev[positive].item->oid;
 -      if (dwim_ref(ref, strlen(ref), branch_oid.hash, &full_ref) &&
 +      if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) &&
            skip_prefix(full_ref, "refs/heads/", &v) &&
            !oidcmp(tip_oid, &branch_oid))
                branch = xstrdup(v);
@@@ -1341,7 -1340,7 +1340,7 @@@ static void prepare_bases(struct base_t
                return;
  
        diff_setup(&diffopt);
-       DIFF_OPT_SET(&diffopt, RECURSIVE);
+       diffopt.flags.recursive = 1;
        diff_setup_done(&diffopt);
  
        oidcpy(&bases->base_commit, &base->object.oid);
@@@ -1512,7 -1511,7 +1511,7 @@@ int cmd_format_patch(int argc, const ch
        rev.verbose_header = 1;
        rev.diff = 1;
        rev.max_parents = 1;
-       DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+       rev.diffopt.flags.recursive = 1;
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.def = "HEAD";
  
        rev.zero_commit = zero_commit;
  
-       if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
-               DIFF_OPT_SET(&rev.diffopt, BINARY);
+       if (!rev.diffopt.flags.text && !no_binary_diff)
+               rev.diffopt.flags.binary = 1;
  
        if (rev.show_notes)
                init_display_notes(&rev.notes_opt);
diff --combined builtin/merge-ours.c
index beb0623d56323a0ea8912e54e94e66fa07268156,684411694f64ca216fb03430df2d2a0ec9ea93ff..c84c6e05e922f09692b9b133cd391b850c7951df
@@@ -9,24 -9,26 +9,24 @@@
   */
  #include "git-compat-util.h"
  #include "builtin.h"
 +#include "diff.h"
  
  static const char builtin_merge_ours_usage[] =
        "git merge-ours <base>... -- HEAD <remote>...";
  
 -static const char *diff_index_args[] = {
 -      "diff-index", "--quiet", "--cached", "HEAD", "--", NULL
 -};
 -#define NARGS (ARRAY_SIZE(diff_index_args) - 1)
 -
  int cmd_merge_ours(int argc, const char **argv, const char *prefix)
  {
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(builtin_merge_ours_usage);
  
        /*
 -       * We need to exit with 2 if the index does not match our HEAD tree,
 -       * because the current index is what we will be committing as the
 -       * merge result.
 +       * The contents of the current index becomes the tree we
 +       * commit.  The index must match HEAD, or this merge cannot go
 +       * through.
         */
 -      if (cmd_diff_index(NARGS, diff_index_args, prefix))
 +      if (read_cache() < 0)
 +              die_errno("read_cache failed");
-       if (index_differs_from("HEAD", 0, 0))
++      if (index_differs_from("HEAD", NULL, 0))
                exit(2);
        exit(0);
  }
diff --combined builtin/reset.c
index d4003f76abcc5cbf469d3d5f029f328717508fa7,4b313a0183c7208860be527502240ae3baa225a0..906e5416582301233cb275fb102a63cb6d1dd139
@@@ -166,7 -166,7 +166,7 @@@ static int read_from_tree(const struct 
        opt.output_format = DIFF_FORMAT_CALLBACK;
        opt.format_callback = update_index_from_diff;
        opt.format_callback_data = &intent_to_add;
-       opt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+       opt.flags.override_submodule_config = 1;
  
        if (do_diff_cache(tree_oid, &opt))
                return 1;
@@@ -266,12 -266,12 +266,12 @@@ static int reset_refs(const char *rev, 
        if (!get_oid("HEAD", &oid_orig)) {
                orig = &oid_orig;
                set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
 -              update_ref_oid(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
 +              update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
                           UPDATE_REFS_MSG_ON_ERR);
        } else if (old_orig)
 -              delete_ref(NULL, "ORIG_HEAD", old_orig->hash, 0);
 +              delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
        set_reflog_message(&msg, "updating HEAD", rev);
 -      update_ref_status = update_ref_oid(msg.buf, "HEAD", oid, orig, 0,
 +      update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
                                       UPDATE_REFS_MSG_ON_ERR);
        strbuf_release(&msg);
        return update_ref_status;
diff --combined builtin/rev-list.c
index 9bf8d5991cb78f56f0c29230483a0b925e8ade58,1d3b6b61bdde791e5e65f8788e468bf8d5c0169c..8034d2eff24eb17b230fdbc28e8c02783e7fb53e
@@@ -258,14 -258,14 +258,14 @@@ static int show_bisect_vars(struct rev_
  }
  
  static int show_object_fast(
 -      const unsigned char *sha1,
 +      const struct object_id *oid,
        enum object_type type,
        int exclude,
        uint32_t name_hash,
        struct packed_git *found_pack,
        off_t found_offset)
  {
 -      fprintf(stdout, "%s\n", sha1_to_hex(sha1));
 +      fprintf(stdout, "%s\n", oid_to_hex(oid));
        return 1;
  }
  
@@@ -294,7 -294,7 +294,7 @@@ int cmd_rev_list(int argc, const char *
        if (revs.bisect)
                bisect_list = 1;
  
-       if (DIFF_OPT_TST(&revs.diffopt, QUICK))
+       if (revs.diffopt.flags.quick)
                info.flags |= REV_LIST_QUIET;
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
diff --combined combine-diff.c
index 82f6070977cf8316fd630cc86fbf9d393d03ba3a,23f3d25e2af1d64ae32f84ffd6bccd700fd7ba1b..2505de119a2be37e9dfb313a2e44db68595ad13f
@@@ -898,7 -898,7 +898,7 @@@ static void show_combined_header(struc
                                 int show_file_header)
  {
        struct diff_options *opt = &rev->diffopt;
-       int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
+       int abbrev = opt->flags.full_index ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
        const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
        const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
        const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
@@@ -987,7 -987,7 +987,7 @@@ static void show_patch_diff(struct comb
        userdiff = userdiff_find_by_path(elem->path);
        if (!userdiff)
                userdiff = userdiff_find_by_name("default");
-       if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV))
+       if (opt->flags.allow_textconv)
                textconv = userdiff_get_textconv(userdiff);
  
        /* Read the result of merge first */
                        elem->mode = canon_mode(st.st_mode);
                } else if (S_ISDIR(st.st_mode)) {
                        struct object_id oid;
 -                      if (resolve_gitlink_ref(elem->path, "HEAD", oid.hash) < 0)
 +                      if (resolve_gitlink_ref(elem->path, "HEAD", &oid) < 0)
                                result = grab_blob(&elem->oid, elem->mode,
                                                   &result_size, NULL, NULL);
                        else
@@@ -1413,8 -1413,8 +1413,8 @@@ void diff_tree_combined(const struct ob
  
        diffopts = *opt;
        copy_pathspec(&diffopts.pathspec, &opt->pathspec);
-       DIFF_OPT_SET(&diffopts, RECURSIVE);
-       DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
+       diffopts.flags.recursive = 1;
+       diffopts.flags.allow_external = 0;
  
        /* find set of paths that everybody touches
         *
         * NOTE please keep this semantically in sync with diffcore_std()
         */
        need_generic_pathscan = opt->skip_stat_unmatch  ||
-                       DIFF_OPT_TST(opt, FOLLOW_RENAMES)       ||
+                       opt->flags.follow_renames       ||
                        opt->break_opt != -1    ||
                        opt->detect_rename      ||
                        opt->pickaxe            ||
diff --combined diff-lib.c
index d2ea02f4d7c9074aff577d9529640315db9c5af7,d373358a5abcd073306d07561590b27a04e25b87..731f0886d6e4146ca526f222381de50406957b30
@@@ -36,7 -36,7 +36,7 @@@ static int check_removed(const struct c
        if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
                return 1;
        if (S_ISDIR(st->st_mode)) {
 -              unsigned char sub[20];
 +              struct object_id sub;
  
                /*
                 * If ce is already a gitlink, we can have a plain
@@@ -50,7 -50,7 +50,7 @@@
                 * a directory --- the blob was removed!
                 */
                if (!S_ISGITLINK(ce->ce_mode) &&
 -                  resolve_gitlink_ref(ce->name, "HEAD", sub))
 +                  resolve_gitlink_ref(ce->name, "HEAD", &sub))
                        return 1;
        }
        return 0;
@@@ -71,14 -71,15 +71,15 @@@ static int match_stat_with_submodule(st
  {
        int changed = ce_match_stat(ce, st, ce_option);
        if (S_ISGITLINK(ce->ce_mode)) {
-               unsigned orig_flags = diffopt->flags;
-               if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+               struct diff_flags orig_flags = diffopt->flags;
+               if (!diffopt->flags.override_submodule_config)
                        set_diffopt_flags_from_submodule_config(diffopt, ce->name);
-               if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+               if (diffopt->flags.ignore_submodules)
                        changed = 0;
-               else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
-                   && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES)))
-                       *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+               else if (!diffopt->flags.ignore_dirty_submodules &&
+                        (!changed || diffopt->flags.dirty_submodules))
+                       *dirty_submodule = is_submodule_modified(ce->name,
+                                                                diffopt->flags.ignore_untracked_in_submodules);
                diffopt->flags = orig_flags;
        }
        return changed;
@@@ -228,7 -229,7 +229,7 @@@ int run_diff_files(struct rev_info *rev
  
                if (!changed && !dirty_submodule) {
                        ce_mark_uptodate(ce);
-                       if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+                       if (!revs->diffopt.flags.find_copies_harder)
                                continue;
                }
                oldmode = ce->ce_mode;
@@@ -362,7 -363,7 +363,7 @@@ static int show_modified(struct rev_inf
  
        oldmode = old->ce_mode;
        if (mode == oldmode && !oidcmp(oid, &old->oid) && !dirty_submodule &&
-           !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+           !revs->diffopt.flags.find_copies_harder)
                return 0;
  
        diff_change(&revs->diffopt, oldmode, mode,
@@@ -493,7 -494,7 +494,7 @@@ static int diff_cache(struct rev_info *
        opts.head_idx = 1;
        opts.index_only = cached;
        opts.diff_index_cached = (cached &&
-                                 !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));
+                                 !revs->diffopt.flags.find_copies_harder);
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = revs;
@@@ -534,7 -535,7 +535,7 @@@ int do_diff_cache(const struct object_i
        return 0;
  }
  
- int index_differs_from(const char *def, int diff_flags,
+ int index_differs_from(const char *def, const struct diff_flags *flags,
                       int ita_invisible_in_index)
  {
        struct rev_info rev;
        memset(&opt, 0, sizeof(opt));
        opt.def = def;
        setup_revisions(0, NULL, &rev, &opt);
-       DIFF_OPT_SET(&rev.diffopt, QUICK);
-       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-       rev.diffopt.flags |= diff_flags;
+       rev.diffopt.flags.quick = 1;
+       rev.diffopt.flags.exit_with_status = 1;
+       if (flags)
+               diff_flags_or(&rev.diffopt.flags, flags);
        rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
        run_diff_index(&rev, 1);
        object_array_clear(&rev.pending);
-       return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
+       return (rev.diffopt.flags.has_changes != 0);
  }
diff --combined diff.c
index 1898dd307d40db131ba2378e22ed3efed1dc2520,5714382d3fa6d16b05a9cff862edf6d24eb4c356..0763e89263efac5fe75a57f0c068049fae610851
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -124,18 -124,18 +124,18 @@@ static int parse_dirstat_params(struct 
        for (i = 0; i < params.nr; i++) {
                const char *p = params.items[i].string;
                if (!strcmp(p, "changes")) {
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+                       options->flags.dirstat_by_line = 0;
+                       options->flags.dirstat_by_file = 0;
                } else if (!strcmp(p, "lines")) {
-                       DIFF_OPT_SET(options, DIRSTAT_BY_LINE);
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+                       options->flags.dirstat_by_line = 1;
+                       options->flags.dirstat_by_file = 0;
                } else if (!strcmp(p, "files")) {
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
-                       DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
+                       options->flags.dirstat_by_line = 0;
+                       options->flags.dirstat_by_file = 1;
                } else if (!strcmp(p, "noncumulative")) {
-                       DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
+                       options->flags.dirstat_cumulative = 0;
                } else if (!strcmp(p, "cumulative")) {
-                       DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+                       options->flags.dirstat_cumulative = 1;
                } else if (isdigit(*p)) {
                        char *end;
                        int permille = strtoul(p, &end, 10) * 10;
@@@ -707,14 -707,83 +707,14 @@@ struct moved_entry 
        struct moved_entry *next_line;
  };
  
 -static int next_byte(const char **cp, const char **endp,
 -                   const struct diff_options *diffopt)
 -{
 -      int retval;
 -
 -      if (*cp > *endp)
 -              return -1;
 -
 -      if (isspace(**cp)) {
 -              if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_CHANGE)) {
 -                      while (*cp < *endp && isspace(**cp))
 -                              (*cp)++;
 -                      /*
 -                       * After skipping a couple of whitespaces,
 -                       * we still have to account for one space.
 -                       */
 -                      return (int)' ';
 -              }
 -
 -              if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE)) {
 -                      while (*cp < *endp && isspace(**cp))
 -                              (*cp)++;
 -                      /* return the first non-ws character via the usual below */
 -              }
 -      }
 -
 -      retval = (unsigned char)(**cp);
 -      (*cp)++;
 -      return retval;
 -}
 -
  static int moved_entry_cmp(const struct diff_options *diffopt,
                           const struct moved_entry *a,
                           const struct moved_entry *b,
                           const void *keydata)
  {
 -      const char *ap = a->es->line, *ae = a->es->line + a->es->len;
 -      const char *bp = b->es->line, *be = b->es->line + b->es->len;
 -
 -      if (!(diffopt->xdl_opts & XDF_WHITESPACE_FLAGS))
 -              return a->es->len != b->es->len  || memcmp(ap, bp, a->es->len);
 -
 -      if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_AT_EOL)) {
 -              while (ae > ap && isspace(*ae))
 -                      ae--;
 -              while (be > bp && isspace(*be))
 -                      be--;
 -      }
 -
 -      while (1) {
 -              int ca, cb;
 -              ca = next_byte(&ap, &ae, diffopt);
 -              cb = next_byte(&bp, &be, diffopt);
 -              if (ca != cb)
 -                      return 1;
 -              if (ca < 0)
 -                      return 0;
 -      }
 -}
 -
 -static unsigned get_string_hash(struct emitted_diff_symbol *es, struct diff_options *o)
 -{
 -      if (o->xdl_opts & XDF_WHITESPACE_FLAGS) {
 -              static struct strbuf sb = STRBUF_INIT;
 -              const char *ap = es->line, *ae = es->line + es->len;
 -              int c;
 -
 -              strbuf_reset(&sb);
 -              while (ae > ap && isspace(*ae))
 -                      ae--;
 -              while ((c = next_byte(&ap, &ae, o)) > 0)
 -                      strbuf_addch(&sb, c);
 -
 -              return memhash(sb.buf, sb.len);
 -      } else {
 -              return memhash(es->line, es->len);
 -      }
 +      return !xdiff_compare_lines(a->es->line, a->es->len,
 +                                  b->es->line, b->es->len,
 +                                  diffopt->xdl_opts);
  }
  
  static struct moved_entry *prepare_entry(struct diff_options *o,
        struct moved_entry *ret = xmalloc(sizeof(*ret));
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
  
 -      ret->ent.hash = get_string_hash(l, o);
 +      ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts);
        ret->es = l;
        ret->next_line = NULL;
  
@@@ -1412,7 -1481,7 +1412,7 @@@ static void emit_rewrite_diff(const cha
        struct emit_callback ecbdata;
        struct strbuf out = STRBUF_INIT;
  
-       if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
+       if (diff_mnemonic_prefix && o->flags.reverse_diff) {
                a_prefix = o->b_prefix;
                b_prefix = o->a_prefix;
        } else {
@@@ -2660,7 -2729,7 +2660,7 @@@ static void show_dirstat(struct diff_op
        dir.alloc = 0;
        dir.nr = 0;
        dir.permille = options->dirstat_permille;
-       dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+       dir.cumulative = options->flags.dirstat_cumulative;
  
        changed = 0;
        for (i = 0; i < q->nr; i++) {
                        goto found_damage;
                }
  
-               if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {
+               if (options->flags.dirstat_by_file) {
                        /*
                         * In --dirstat-by-file mode, we don't really need to
                         * look at the actual file contents at all.
@@@ -2761,7 -2830,7 +2761,7 @@@ static void show_dirstat_by_line(struc
        dir.alloc = 0;
        dir.nr = 0;
        dir.permille = options->dirstat_permille;
-       dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+       dir.cumulative = options->flags.dirstat_cumulative;
  
        changed = 0;
        for (i = 0; i < data->nr; i++) {
@@@ -3048,7 -3117,7 +3048,7 @@@ static void builtin_diff(const char *na
        const char *line_prefix = diff_line_prefix(o);
  
        diff_set_mnemonic_prefix(o, "a/", "b/");
-       if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+       if (o->flags.reverse_diff) {
                a_prefix = o->b_prefix;
                b_prefix = o->a_prefix;
        } else {
                return;
        }
  
-       if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+       if (o->flags.allow_textconv) {
                textconv_one = get_textconv(one);
                textconv_two = get_textconv(two);
        }
                                 header.len, 0);
                strbuf_reset(&header);
                goto free_ab_and_return;
-       } else if (!DIFF_OPT_TST(o, TEXT) &&
+       } else if (!o->flags.text &&
            ( (!textconv_one && diff_filespec_is_binary(one)) ||
              (!textconv_two && diff_filespec_is_binary(two)) )) {
                struct strbuf sb = STRBUF_INIT;
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
-                   !DIFF_OPT_TST(o, BINARY)) {
+                   !o->flags.binary) {
                        if (!oidcmp(&one->oid, &two->oid)) {
                                if (must_show_header)
                                        emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
                }
                emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0);
                strbuf_reset(&header);
-               if (DIFF_OPT_TST(o, BINARY))
+               if (o->flags.binary)
                        emit_binary_diff(o, &mf1, &mf2);
                else {
                        strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
-               if (DIFF_OPT_TST(o, FUNCCONTEXT))
+               if (o->flags.funccontext)
                        xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
                if (pe)
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
@@@ -3378,7 -3447,7 +3378,7 @@@ static void builtin_checkdiff(const cha
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
        if (data.status)
-               DIFF_OPT_SET(o, CHECK_FAILED);
+               o->flags.check_failed = 1;
  }
  
  struct diff_filespec *alloc_filespec(const char *path)
@@@ -3545,12 -3614,14 +3545,12 @@@ int diff_populate_filespec(struct diff_
                int fd;
  
                if (lstat(s->path, &st) < 0) {
 -                      if (errno == ENOENT) {
 -                      err_empty:
 -                              err = -1;
 -                      empty:
 -                              s->data = (char *)"";
 -                              s->size = 0;
 -                              return err;
 -                      }
 +              err_empty:
 +                      err = -1;
 +              empty:
 +                      s->data = (char *)"";
 +                      s->size = 0;
 +                      return err;
                }
                s->size = xsize_t(st.st_size);
                if (!s->size)
@@@ -3870,9 -3941,9 +3870,9 @@@ static void fill_metainfo(struct strbu
                *must_show_header = 0;
        }
        if (one && two && oidcmp(&one->oid, &two->oid)) {
-               int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+               int abbrev = o->flags.full_index ? 40 : DEFAULT_ABBREV;
  
-               if (DIFF_OPT_TST(o, BINARY)) {
+               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)))
@@@ -3902,7 -3973,7 +3902,7 @@@ static void run_diff_cmd(const char *pg
        int must_show_header = 0;
  
  
-       if (DIFF_OPT_TST(o, ALLOW_EXTERNAL)) {
+       if (o->flags.allow_external) {
                struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
                if (drv && drv->external)
                        pgm = drv->external;
@@@ -3982,7 -4053,7 +3982,7 @@@ static void run_diff(struct diff_filepa
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
  
-       if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
+       if (!o->flags.allow_external)
                pgm = NULL;
  
        if (DIFF_PAIR_UNMERGED(p)) {
@@@ -4081,7 -4152,7 +4081,7 @@@ void diff_setup(struct diff_options *op
        options->context = diff_context_default;
        options->interhunkcontext = diff_interhunk_context_default;
        options->ws_error_highlight = ws_error_highlight_default;
-       DIFF_OPT_SET(options, RENAME_EMPTY);
+       options->flags.rename_empty = 1;
  
        /* pathchange left =NULL by default */
        options->change = diff_change;
@@@ -4132,14 -4203,14 +4132,14 @@@ void diff_setup_done(struct diff_option
        if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
            DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
            DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
-               DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
+               options->flags.diff_from_contents = 1;
        else
-               DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);
+               options->flags.diff_from_contents = 0;
  
-       if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+       if (options->flags.find_copies_harder)
                options->detect_rename = DIFF_DETECT_COPY;
  
-       if (!DIFF_OPT_TST(options, RELATIVE_NAME))
+       if (!options->flags.relative_name)
                options->prefix = NULL;
        if (options->prefix)
                options->prefix_length = strlen(options->prefix);
                                      DIFF_FORMAT_DIRSTAT |
                                      DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
-               DIFF_OPT_SET(options, RECURSIVE);
+               options->flags.recursive = 1;
        /*
         * Also pickaxe would not work very well if you do not say recursive
         */
        if (options->pickaxe)
-               DIFF_OPT_SET(options, RECURSIVE);
+               options->flags.recursive = 1;
        /*
         * When patches are generated, submodules diffed against the work tree
         * must be checked for dirtiness too so it can be shown in the output
         */
        if (options->output_format & DIFF_FORMAT_PATCH)
-               DIFF_OPT_SET(options, DIRTY_SUBMODULES);
+               options->flags.dirty_submodules = 1;
  
        if (options->detect_rename && options->rename_limit < 0)
                options->rename_limit = diff_rename_limit_default;
         * to have found.  It does not make sense not to return with
         * exit code in such a case either.
         */
-       if (DIFF_OPT_TST(options, QUICK)) {
+       if (options->flags.quick) {
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
-               DIFF_OPT_SET(options, EXIT_WITH_STATUS);
+               options->flags.exit_with_status = 1;
        }
  
        options->diff_path_counter = 0;
  
-       if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1)
+       if (options->flags.follow_renames && options->pathspec.nr != 1)
                die(_("--follow requires exactly one pathspec"));
  
        if (!options->use_color || external_diff())
@@@ -4559,7 -4630,7 +4559,7 @@@ int diff_opt_parse(struct diff_options 
        else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
                 !strcmp(arg, "--find-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
-                       DIFF_OPT_SET(options, FIND_COPIES_HARDER);
+                       options->flags.find_copies_harder = 1;
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -C: %s", arg+2);
                options->detect_rename = DIFF_DETECT_COPY;
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
        else if (!strcmp(arg, "--rename-empty"))
-               DIFF_OPT_SET(options, RENAME_EMPTY);
+               options->flags.rename_empty = 1;
        else if (!strcmp(arg, "--no-rename-empty"))
-               DIFF_OPT_CLR(options, RENAME_EMPTY);
+               options->flags.rename_empty = 0;
        else if (!strcmp(arg, "--relative"))
-               DIFF_OPT_SET(options, RELATIVE_NAME);
+               options->flags.relative_name = 1;
        else if (skip_prefix(arg, "--relative=", &arg)) {
-               DIFF_OPT_SET(options, RELATIVE_NAME);
+               options->flags.relative_name = 1;
                options->prefix = arg;
        }
  
        /* flags options */
        else if (!strcmp(arg, "--binary")) {
                enable_patch_output(&options->output_format);
-               DIFF_OPT_SET(options, BINARY);
+               options->flags.binary = 1;
        }
        else if (!strcmp(arg, "--full-index"))
-               DIFF_OPT_SET(options, FULL_INDEX);
+               options->flags.full_index = 1;
        else if (!strcmp(arg, "-a") || !strcmp(arg, "--text"))
-               DIFF_OPT_SET(options, TEXT);
+               options->flags.text = 1;
        else if (!strcmp(arg, "-R"))
-               DIFF_OPT_SET(options, REVERSE_DIFF);
+               options->flags.reverse_diff = 1;
        else if (!strcmp(arg, "--find-copies-harder"))
-               DIFF_OPT_SET(options, FIND_COPIES_HARDER);
+               options->flags.find_copies_harder = 1;
        else if (!strcmp(arg, "--follow"))
-               DIFF_OPT_SET(options, FOLLOW_RENAMES);
+               options->flags.follow_renames = 1;
        else if (!strcmp(arg, "--no-follow")) {
-               DIFF_OPT_CLR(options, FOLLOW_RENAMES);
-               DIFF_OPT_CLR(options, DEFAULT_FOLLOW_RENAMES);
+               options->flags.follow_renames = 0;
+               options->flags.default_follow_renames = 0;
        } else if (!strcmp(arg, "--color"))
                options->use_color = 1;
        else if (skip_prefix(arg, "--color=", &arg)) {
                return argcount;
        }
        else if (!strcmp(arg, "--exit-code"))
-               DIFF_OPT_SET(options, EXIT_WITH_STATUS);
+               options->flags.exit_with_status = 1;
        else if (!strcmp(arg, "--quiet"))
-               DIFF_OPT_SET(options, QUICK);
+               options->flags.quick = 1;
        else if (!strcmp(arg, "--ext-diff"))
-               DIFF_OPT_SET(options, ALLOW_EXTERNAL);
+               options->flags.allow_external = 1;
        else if (!strcmp(arg, "--no-ext-diff"))
-               DIFF_OPT_CLR(options, ALLOW_EXTERNAL);
-       else if (!strcmp(arg, "--textconv"))
-               DIFF_OPT_SET(options, ALLOW_TEXTCONV);
-       else if (!strcmp(arg, "--no-textconv"))
-               DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
+               options->flags.allow_external = 0;
+       else if (!strcmp(arg, "--textconv")) {
+               options->flags.allow_textconv = 1;
+               options->flags.textconv_set_via_cmdline = 1;
+       } else if (!strcmp(arg, "--no-textconv"))
+               options->flags.allow_textconv = 0;
        else if (!strcmp(arg, "--ignore-submodules")) {
-               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+               options->flags.override_submodule_config = 1;
                handle_ignore_submodules_arg(options, "all");
        } else if (skip_prefix(arg, "--ignore-submodules=", &arg)) {
-               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+               options->flags.override_submodule_config = 1;
                handle_ignore_submodules_arg(options, arg);
        } else if (!strcmp(arg, "--submodule"))
                options->submodule_format = DIFF_SUBMODULE_LOG;
                         &options->interhunkcontext))
                ;
        else if (!strcmp(arg, "-W"))
-               DIFF_OPT_SET(options, FUNCCONTEXT);
+               options->flags.funccontext = 1;
        else if (!strcmp(arg, "--function-context"))
-               DIFF_OPT_SET(options, FUNCCONTEXT);
+               options->flags.funccontext = 1;
        else if (!strcmp(arg, "--no-function-context"))
-               DIFF_OPT_CLR(options, FUNCCONTEXT);
+               options->flags.funccontext = 0;
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
                char *path = prefix_filename(prefix, optarg);
                options->file = xfopen(path, "w");
@@@ -5528,7 -5600,7 +5529,7 @@@ void diff_flush(struct diff_options *op
                separator++;
        }
  
-       if (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))
+       if (output_format & DIFF_FORMAT_DIRSTAT && options->flags.dirstat_by_line)
                dirstat_by_line = 1;
  
        if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||
        }
  
        if (output_format & DIFF_FORMAT_NO_OUTPUT &&
-           DIFF_OPT_TST(options, EXIT_WITH_STATUS) &&
-           DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+           options->flags.exit_with_status &&
+           options->flags.diff_from_contents) {
                /*
                 * run diff_flush_patch for the exit status. setting
                 * options->file to /dev/null should be safe, because we
@@@ -5612,11 -5684,11 +5613,11 @@@ free_queue
         * diff_addremove/diff_change does not set the bit when
         * DIFF_FROM_CONTENTS is in effect (e.g. with -w).
         */
-       if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+       if (options->flags.diff_from_contents) {
                if (options->found_changes)
-                       DIFF_OPT_SET(options, HAS_CHANGES);
+                       options->flags.has_changes = 1;
                else
-                       DIFF_OPT_CLR(options, HAS_CHANGES);
+                       options->flags.has_changes = 0;
        }
  }
  
@@@ -5736,7 -5808,7 +5737,7 @@@ static void diffcore_skip_stat_unmatch(
                         * to determine how many paths were dirty only
                         * due to stat info mismatch.
                         */
-                       if (!DIFF_OPT_TST(diffopt, NO_INDEX))
+                       if (!diffopt->flags.no_index)
                                diffopt->skip_stat_unmatch++;
                        diff_free_filepair(p);
                }
@@@ -5785,10 -5857,10 +5786,10 @@@ void diffcore_std(struct diff_options *
                diff_resolve_rename_copy();
        diffcore_apply_filter(options);
  
-       if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
-               DIFF_OPT_SET(options, HAS_CHANGES);
+       if (diff_queued_diff.nr && !options->flags.diff_from_contents)
+               options->flags.has_changes = 1;
        else
-               DIFF_OPT_CLR(options, HAS_CHANGES);
+               options->flags.has_changes = 0;
  
        options->found_follow = 0;
  }
@@@ -5800,23 -5872,23 +5801,23 @@@ int diff_result_code(struct diff_option
        diff_warn_rename_limit("diff.renameLimit",
                               opt->needed_rename_limit,
                               opt->degraded_cc_to_c);
-       if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
+       if (!opt->flags.exit_with_status &&
            !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
                return status;
-       if (DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
-           DIFF_OPT_TST(opt, HAS_CHANGES))
+       if (opt->flags.exit_with_status &&
+           opt->flags.has_changes)
                result |= 01;
        if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) &&
-           DIFF_OPT_TST(opt, CHECK_FAILED))
+           opt->flags.check_failed)
                result |= 02;
        return result;
  }
  
  int diff_can_quit_early(struct diff_options *opt)
  {
-       return (DIFF_OPT_TST(opt, QUICK) &&
+       return (opt->flags.quick &&
                !opt->filter &&
-               DIFF_OPT_TST(opt, HAS_CHANGES));
+               opt->flags.has_changes);
  }
  
  /*
  static int is_submodule_ignored(const char *path, struct diff_options *options)
  {
        int ignored = 0;
-       unsigned orig_flags = options->flags;
-       if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+       struct diff_flags orig_flags = options->flags;
+       if (!options->flags.override_submodule_config)
                set_diffopt_flags_from_submodule_config(options, path);
-       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+       if (options->flags.ignore_submodules)
                ignored = 1;
        options->flags = orig_flags;
        return ignored;
@@@ -5860,7 -5932,7 +5861,7 @@@ void diff_addremove(struct diff_option
         * Before the final output happens, they are pruned after
         * merged into rename/copy pairs as appropriate.
         */
-       if (DIFF_OPT_TST(options, REVERSE_DIFF))
+       if (options->flags.reverse_diff)
                addremove = (addremove == '+' ? '-' :
                             addremove == '-' ? '+' : addremove);
  
        }
  
        diff_queue(&diff_queued_diff, one, two);
-       if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
-               DIFF_OPT_SET(options, HAS_CHANGES);
+       if (!options->flags.diff_from_contents)
+               options->flags.has_changes = 1;
  }
  
  void diff_change(struct diff_options *options,
            is_submodule_ignored(concatpath, options))
                return;
  
-       if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
+       if (options->flags.reverse_diff) {
                SWAP(old_mode, new_mode);
                SWAP(old_oid, new_oid);
                SWAP(old_oid_valid, new_oid_valid);
        two->dirty_submodule = new_dirty_submodule;
        p = diff_queue(&diff_queued_diff, one, two);
  
-       if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+       if (options->flags.diff_from_contents)
                return;
  
-       if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch &&
+       if (options->flags.quick && options->skip_stat_unmatch &&
            !diff_filespec_check_stat_unmatch(p))
                return;
  
-       DIFF_OPT_SET(options, HAS_CHANGES);
+       options->flags.has_changes = 1;
  }
  
  struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
@@@ -6062,7 -6134,7 +6063,7 @@@ void setup_diff_pager(struct diff_optio
         * and because it is easy to find people oneline advising "git diff
         * --exit-code" in hooks and other scripts, we do not do so.
         */
-       if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
+       if (!opt->flags.exit_with_status &&
            check_pager_config("diff") != 0)
                setup_pager();
  }
diff --combined diff.h
index 398b87b4cf0fe06010ed20320d55c1803dfc3331,ed2832d7a3a445f4beb95823f5a197a56be6c594..0fb18dd735b5462e7b186c609efd048561381be4
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -60,42 -60,52 +60,52 @@@ typedef struct strbuf *(*diff_prefix_fn
  
  #define DIFF_FORMAT_CALLBACK  0x1000
  
- #define DIFF_OPT_RECURSIVE           (1 <<  0)
- #define DIFF_OPT_TREE_IN_RECURSIVE   (1 <<  1)
- #define DIFF_OPT_BINARY              (1 <<  2)
- #define DIFF_OPT_TEXT                (1 <<  3)
- #define DIFF_OPT_FULL_INDEX          (1 <<  4)
- #define DIFF_OPT_SILENT_ON_REMOVE    (1 <<  5)
- #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)
- #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)
- #define DIFF_OPT_RENAME_EMPTY        (1 <<  8)
- /* (1 <<  9) unused */
- #define DIFF_OPT_HAS_CHANGES         (1 << 10)
- #define DIFF_OPT_QUICK               (1 << 11)
- #define DIFF_OPT_NO_INDEX            (1 << 12)
- #define DIFF_OPT_ALLOW_EXTERNAL      (1 << 13)
- #define DIFF_OPT_EXIT_WITH_STATUS    (1 << 14)
- #define DIFF_OPT_REVERSE_DIFF        (1 << 15)
- #define DIFF_OPT_CHECK_FAILED        (1 << 16)
- #define DIFF_OPT_RELATIVE_NAME       (1 << 17)
- #define DIFF_OPT_IGNORE_SUBMODULES   (1 << 18)
- #define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)
- #define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
- #define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
- #define DIFF_OPT_DIFF_FROM_CONTENTS  (1 << 22)
- #define DIFF_OPT_DIRTY_SUBMODULES    (1 << 24)
- #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
- #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
- #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
- #define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)
- #define DIFF_OPT_FUNCCONTEXT         (1 << 29)
- #define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30)
- #define DIFF_OPT_DEFAULT_FOLLOW_RENAMES (1U << 31)
- #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
- #define DIFF_OPT_TOUCHED(opts, flag)    ((opts)->touched_flags & DIFF_OPT_##flag)
- #define DIFF_OPT_SET(opts, flag)    (((opts)->flags |= DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag))
- #define DIFF_OPT_CLR(opts, flag)    (((opts)->flags &= ~DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag))
+ #define DIFF_FLAGS_INIT { 0 }
+ struct diff_flags {
+       unsigned recursive:1;
+       unsigned tree_in_recursive:1;
+       unsigned binary:1;
+       unsigned text:1;
+       unsigned full_index:1;
+       unsigned silent_on_remove:1;
+       unsigned find_copies_harder:1;
+       unsigned follow_renames:1;
+       unsigned rename_empty:1;
+       unsigned has_changes:1;
+       unsigned quick:1;
+       unsigned no_index:1;
+       unsigned allow_external:1;
+       unsigned exit_with_status:1;
+       unsigned reverse_diff:1;
+       unsigned check_failed:1;
+       unsigned relative_name:1;
+       unsigned ignore_submodules:1;
+       unsigned dirstat_cumulative:1;
+       unsigned dirstat_by_file:1;
+       unsigned allow_textconv:1;
+       unsigned textconv_set_via_cmdline:1;
+       unsigned diff_from_contents:1;
+       unsigned dirty_submodules:1;
+       unsigned ignore_untracked_in_submodules:1;
+       unsigned ignore_dirty_submodules:1;
+       unsigned override_submodule_config:1;
+       unsigned dirstat_by_line:1;
+       unsigned funccontext:1;
+       unsigned pickaxe_ignore_case:1;
+       unsigned default_follow_renames:1;
+ };
+ static inline void diff_flags_or(struct diff_flags *a,
+                                const struct diff_flags *b)
+ {
+       char *tmp_a = (char *)a;
+       const char *tmp_b = (const char *)b;
+       int i;
+       for (i = 0; i < sizeof(struct diff_flags); i++)
+               tmp_a[i] |= tmp_b[i];
+ }
  #define DIFF_XDL_TST(opts, flag)    ((opts)->xdl_opts & XDF_##flag)
  #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
  #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
@@@ -122,8 -132,7 +132,7 @@@ struct diff_options 
        const char *a_prefix, *b_prefix;
        const char *line_prefix;
        size_t line_prefix_length;
-       unsigned flags;
-       unsigned touched_flags;
+       struct diff_flags flags;
  
        /* diff-filter bits */
        unsigned int filter;
        pathchange_fn_t pathchange;
        change_fn_t change;
        add_remove_fn_t add_remove;
 +      void *change_fn_data;
        diff_format_fn_t format_callback;
        void *format_callback_data;
        diff_prefix_fn_t output_prefix;
@@@ -389,7 -397,8 +398,8 @@@ extern int diff_result_code(struct diff
  
  extern void diff_no_index(struct rev_info *, int, const char **);
  
- extern int index_differs_from(const char *def, int diff_flags, int ita_invisible_in_index);
+ extern int index_differs_from(const char *def, const struct diff_flags *flags,
+                             int ita_invisible_in_index);
  
  /*
   * Fill the contents of the filespec "df", respecting any textconv defined by
diff --combined log-tree.c
index 580b3a98a03df8b1920b3f729c783b79ed781164,8dacccc0c28a1699c03726440fe6203db3a6cf3d..3b904f0375e233db974ab49b3b31dd2fc73e842d
@@@ -198,7 -198,7 +198,7 @@@ static const struct name_decoration *cu
  
        /* Now resolve and find the matching current branch */
        branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
 -      if (!(rru_flags & REF_ISSYMREF))
 +      if (!branch_name || !(rru_flags & REF_ISSYMREF))
                return NULL;
  
        if (!starts_with(branch_name, "refs/"))
@@@ -793,7 -793,7 +793,7 @@@ static int log_tree_diff(struct rev_inf
        struct commit_list *parents;
        struct object_id *oid;
  
-       if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
+       if (!opt->diff && !opt->diffopt.flags.exit_with_status)
                return 0;
  
        parse_commit_or_die(commit);
diff --combined merge-recursive.c
index 24c5c26a6abdcc6aaeb1defa194d25b50e7ca6e5,f6c03770b8c38c4d209bafc7fddafad30dd1d8a6..2ca8444c65836578e97f33761f0c7dda696c1f10
@@@ -540,8 -540,8 +540,8 @@@ static struct string_list *get_renames(
                return renames;
  
        diff_setup(&opts);
-       DIFF_OPT_SET(&opts, RECURSIVE);
-       DIFF_OPT_CLR(&opts, RENAME_EMPTY);
+       opts.flags.recursive = 1;
+       opts.flags.rename_empty = 0;
        opts.detect_rename = DIFF_DETECT_RENAME;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
@@@ -2162,7 -2162,7 +2162,7 @@@ int merge_recursive_generic(struct merg
                            struct commit **result)
  {
        int clean;
 -      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock = LOCK_INIT;
        struct commit *head_commit = get_ref(head, o->branch1);
        struct commit *next_commit = get_ref(merge, o->branch2);
        struct commit_list *ca = NULL;
                }
        }
  
 -      hold_locked_index(lock, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (clean < 0)
                return clean;
  
        if (active_cache_changed &&
 -          write_locked_index(&the_index, lock, COMMIT_LOCK))
 +          write_locked_index(&the_index, &lock, COMMIT_LOCK))
                return err(o, _("Unable to write index."));
  
        return clean ? 0 : 1;
diff --combined notes-merge.c
index 30ec83ab043965a743918b4ee39c708a3fb3e717,b3d4d44fce23e0df5f747f765bb93875ad61a17d..4a83b0ebd5ce8b24da0b32820f5666571b099ecf
@@@ -125,7 -125,7 +125,7 @@@ static struct notes_merge_pair *diff_tr
               oid_to_hex(base), oid_to_hex(remote));
  
        diff_setup(&opt);
-       DIFF_OPT_SET(&opt, RECURSIVE);
+       opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
        diff_tree_oid(base, remote, "", &opt);
@@@ -188,7 -188,7 +188,7 @@@ static void diff_tree_local(struct note
               len, oid_to_hex(base), oid_to_hex(local));
  
        diff_setup(&opt);
-       DIFF_OPT_SET(&opt, RECURSIVE);
+       opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
        diff_tree_oid(base, local, "", &opt);
@@@ -547,7 -547,7 +547,7 @@@ int notes_merge(struct notes_merge_opti
               o->local_ref, o->remote_ref);
  
        /* Dereference o->local_ref into local_sha1 */
 -      if (read_ref_full(o->local_ref, 0, local_oid.hash, NULL))
 +      if (read_ref_full(o->local_ref, 0, &local_oid, NULL))
                die("Failed to resolve local notes ref '%s'", o->local_ref);
        else if (!check_refname_format(o->local_ref, 0) &&
                is_null_oid(&local_oid))
diff --combined revision.c
index 99c95c19b0093094878bdae02a22944fd2aeff37,bfde5b0cd5ce46b319dd1a0c0476a2297eb0e4a5..e2e691dd5a48087da0b476cf4d6cded645f23d6e
@@@ -395,16 -395,8 +395,16 @@@ static struct commit *one_relevant_pare
   * if the whole diff is removal of old data, and otherwise
   * REV_TREE_DIFFERENT (of course if the trees are the same we
   * want REV_TREE_SAME).
 - * That means that once we get to REV_TREE_DIFFERENT, we do not
 - * have to look any further.
 + *
 + * The only time we care about the distinction is when
 + * remove_empty_trees is in effect, in which case we care only about
 + * whether the whole change is REV_TREE_NEW, or if there's another type
 + * of change. Which means we can stop the diff early in either of these
 + * cases:
 + *
 + *   1. We're not using remove_empty_trees at all.
 + *
 + *   2. We saw anything except REV_TREE_NEW.
   */
  static int tree_difference = REV_TREE_SAME;
  
@@@ -415,11 -407,10 +415,11 @@@ static void file_add_remove(struct diff
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
 +      struct rev_info *revs = options->change_fn_data;
  
        tree_difference |= diff;
 -      if (tree_difference == REV_TREE_DIFFERENT)
 +      if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW)
-               DIFF_OPT_SET(options, HAS_CHANGES);
+               options->flags.has_changes = 1;
  }
  
  static void file_change(struct diff_options *options,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
        tree_difference = REV_TREE_DIFFERENT;
-       DIFF_OPT_SET(options, HAS_CHANGES);
+       options->flags.has_changes = 1;
  }
  
  static int rev_compare_tree(struct rev_info *revs,
        }
  
        tree_difference = REV_TREE_SAME;
-       DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+       revs->pruning.flags.has_changes = 0;
        if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@@ -480,7 -471,7 +480,7 @@@ static int rev_same_tree_as_empty(struc
                return 0;
  
        tree_difference = REV_TREE_SAME;
-       DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+       revs->pruning.flags.has_changes = 0;
        retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
  
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
@@@ -1412,11 -1403,10 +1412,11 @@@ void init_revisions(struct rev_info *re
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
-       DIFF_OPT_SET(&revs->pruning, RECURSIVE);
-       DIFF_OPT_SET(&revs->pruning, QUICK);
+       revs->pruning.flags.recursive = 1;
+       revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
 +      revs->pruning.change_fn_data = revs;
        revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
        revs->dense = 1;
        revs->prefix = prefix;
@@@ -1927,11 -1917,11 +1927,11 @@@ static int handle_revision_opt(struct r
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
-               DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
+               revs->diffopt.flags.recursive = 1;
        } else if (!strcmp(arg, "-t")) {
                revs->diff = 1;
-               DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
-               DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
+               revs->diffopt.flags.recursive = 1;
+               revs->diffopt.flags.tree_in_recursive = 1;
        } else if (!strcmp(arg, "-m")) {
                revs->ignore_merges = 0;
        } else if (!strcmp(arg, "-c")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.ignore_case = 1;
-               DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
+               revs->diffopt.flags.pickaxe_ignore_case = 1;
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
        } else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) {
@@@ -2409,7 -2399,7 +2409,7 @@@ int setup_revisions(int argc, const cha
        /* Pickaxe, diff-filter and rename following need diffs */
        if (revs->diffopt.pickaxe ||
            revs->diffopt.filter ||
-           DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+           revs->diffopt.flags.follow_renames)
                revs->diff = 1;
  
        if (revs->topo_order)
        if (revs->prune_data.nr) {
                copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
-               if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+               if (!revs->diffopt.flags.follow_renames)
                        revs->prune = 1;
                if (!revs->full_diff)
                        copy_pathspec(&revs->diffopt.pathspec,
diff --combined sequencer.c
index 1eb2c4669d529485a045de66d6711039d19a2442,c0410e49108ae21e99e64c1f06865ad1d75c89f7..6d027b06c8d8dc69b14d05752637a65aa121ab24
@@@ -393,7 -393,7 +393,7 @@@ static int fast_forward_to(const struc
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_update(transaction, "HEAD",
 -                                 to->hash, unborn ? null_sha1 : from->hash,
 +                                 to, unborn ? &null_oid : from,
                                   0, sb.buf, &err) ||
            ref_transaction_commit(transaction, &err)) {
                ref_transaction_free(transaction);
@@@ -489,7 -489,7 +489,7 @@@ static int is_index_unchanged(void
        struct object_id head_oid;
        struct commit *head_commit;
  
 -      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
 +      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                return error(_("could not resolve HEAD commit\n"));
  
        head_commit = lookup_commit(&head_oid);
@@@ -959,7 -959,8 +959,8 @@@ static int do_pick_commit(enum todo_com
                unborn = get_oid("HEAD", &head);
                if (unborn)
                        oidcpy(&head, &empty_tree_oid);
-               if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0))
+               if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD",
+                                      NULL, 0))
                        return error_dirty_index(opts);
        }
        discard_cache();
         * write it at all.
         */
        if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
 -          update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL,
 +          update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
                       REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
        if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
 -          update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL,
 +          update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
                       REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
  
@@@ -1184,6 -1185,7 +1185,6 @@@ static int read_and_refresh_cache(struc
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
        if (the_index.cache_changed && index_fd >= 0) {
                if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
 -                      rollback_lock_file(&index_lock);
                        return error(_("git %s: failed to refresh the index"),
                                _(action_name(opts)));
                }
@@@ -1629,7 -1631,7 +1630,7 @@@ static int rollback_single_pick(void
        if (!file_exists(git_path_cherry_pick_head()) &&
            !file_exists(git_path_revert_head()))
                return error(_("no cherry-pick or revert in progress"));
 -      if (read_ref_full("HEAD", 0, head_oid.hash, NULL))
 +      if (read_ref_full("HEAD", 0, &head_oid, NULL))
                return error(_("cannot resolve HEAD"));
        if (is_null_oid(&head_oid))
                return error(_("cannot abort from a branch yet to be born"));
@@@ -1861,15 -1863,12 +1862,15 @@@ static int error_failed_squash(struct c
  
  static int do_exec(const char *command_line)
  {
 +      struct argv_array child_env = ARGV_ARRAY_INIT;
        const char *child_argv[] = { NULL, NULL };
        int dirty, status;
  
        fprintf(stderr, "Executing: %s\n", command_line);
        child_argv[0] = command_line;
 -      status = run_command_v_opt(child_argv, RUN_USING_SHELL);
 +      argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
 +      status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
 +                                        child_env.argv);
  
        /* force re-reading of the cache */
        if (discard_cache() < 0 || read_cache() < 0)
                status = 1;
        }
  
 +      argv_array_clear(&child_env);
 +
        return status;
  }
  
@@@ -2128,8 -2125,8 +2129,8 @@@ cleanup_head_ref
                        }
                        msg = reflog_message(opts, "finish", "%s onto %s",
                                head_ref.buf, buf.buf);
 -                      if (update_ref(msg, head_ref.buf, head.hash, orig.hash,
 -                                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {
 +                      if (update_ref(msg, head_ref.buf, &head, &orig,
 +                                     REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {
                                res = error(_("could not update %s"),
                                        head_ref.buf);
                                goto cleanup_head_ref;
@@@ -2283,7 -2280,7 +2284,7 @@@ int sequencer_continue(struct replay_op
                        if (res)
                                goto release_todo_list;
                }
-               if (index_differs_from("HEAD", 0, 0)) {
+               if (index_differs_from("HEAD", NULL, 0)) {
                        res = error_dirty_index(opts);
                        goto release_todo_list;
                }
diff --combined submodule.c
index 239d94d5394a318d1a36c3300981e34427928dd7,9b16adc96210ec71264ac75dceb931b70341a097..3ee4a0caa7e2db70ec1ba61a2fe152b989e9a186
@@@ -21,7 -21,7 +21,7 @@@
  #include "parse-options.h"
  
  static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
 -static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP;
 +static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
  static int initialized_fetch_ref_tips;
  static struct oid_array ref_tips_before_fetch;
  static struct oid_array ref_tips_after_fetch;
@@@ -183,7 -183,7 +183,7 @@@ void set_diffopt_flags_from_submodule_c
                if (ignore)
                        handle_ignore_submodules_arg(diffopt, ignore);
                else if (is_gitmodules_unmerged(&the_index))
-                       DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+                       diffopt->flags.ignore_submodules = 1;
        }
  }
  
@@@ -402,16 -402,16 +402,16 @@@ const char *submodule_strategy_to_strin
  void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
  {
-       DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
-       DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+       diffopt->flags.ignore_submodules = 0;
+       diffopt->flags.ignore_untracked_in_submodules = 0;
+       diffopt->flags.ignore_dirty_submodules = 0;
  
        if (!strcmp(arg, "all"))
-               DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+               diffopt->flags.ignore_submodules = 1;
        else if (!strcmp(arg, "untracked"))
-               DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+               diffopt->flags.ignore_untracked_in_submodules = 1;
        else if (!strcmp(arg, "dirty"))
-               DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
+               diffopt->flags.ignore_dirty_submodules = 1;
        else if (strcmp(arg, "none"))
                die("bad --ignore-submodules argument: %s", arg);
  }
@@@ -616,7 -616,7 +616,7 @@@ void show_submodule_inline_diff(struct 
        argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
                         "always" : "never");
  
-       if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+       if (o->flags.reverse_diff) {
                argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
                                 o->b_prefix, path);
                argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
@@@ -674,11 -674,11 +674,11 @@@ const struct submodule *submodule_from_
  }
  
  static struct oid_array *submodule_commits(struct string_list *submodules,
 -                                         const char *path)
 +                                         const char *name)
  {
        struct string_list_item *item;
  
 -      item = string_list_insert(submodules, path);
 +      item = string_list_insert(submodules, name);
        if (item->util)
                return (struct oid_array *) item->util;
  
        return (struct oid_array *) item->util;
  }
  
 +struct collect_changed_submodules_cb_data {
 +      struct string_list *changed;
 +      const struct object_id *commit_oid;
 +};
 +
 +/*
 + * this would normally be two functions: default_name_from_path() and
 + * path_from_default_name(). Since the default name is the same as
 + * the submodule path we can get away with just one function which only
 + * checks whether there is a submodule in the working directory at that
 + * location.
 + */
 +static const char *default_name_or_path(const char *path_or_name)
 +{
 +      int error_code;
 +
 +      if (!is_submodule_populated_gently(path_or_name, &error_code))
 +              return NULL;
 +
 +      return path_or_name;
 +}
 +
  static void collect_changed_submodules_cb(struct diff_queue_struct *q,
                                          struct diff_options *options,
                                          void *data)
  {
 +      struct collect_changed_submodules_cb_data *me = data;
 +      struct string_list *changed = me->changed;
 +      const struct object_id *commit_oid = me->commit_oid;
        int i;
 -      struct string_list *changed = data;
  
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                struct oid_array *commits;
 +              const struct submodule *submodule;
 +              const char *name;
 +
                if (!S_ISGITLINK(p->two->mode))
                        continue;
  
 -              if (S_ISGITLINK(p->one->mode)) {
 -                      /*
 -                       * NEEDSWORK: We should honor the name configured in
 -                       * the .gitmodules file of the commit we are examining
 -                       * here to be able to correctly follow submodules
 -                       * being moved around.
 -                       */
 -                      commits = submodule_commits(changed, p->two->path);
 -                      oid_array_append(commits, &p->two->oid);
 -              } else {
 -                      /* Submodule is new or was moved here */
 -                      /*
 -                       * NEEDSWORK: When the .git directories of submodules
 -                       * live inside the superprojects .git directory some
 -                       * day we should fetch new submodules directly into
 -                       * that location too when config or options request
 -                       * that so they can be checked out from there.
 -                       */
 -                      continue;
 +              submodule = submodule_from_path(commit_oid, p->two->path);
 +              if (submodule)
 +                      name = submodule->name;
 +              else {
 +                      name = default_name_or_path(p->two->path);
 +                      /* make sure name does not collide with existing one */
 +                      submodule = submodule_from_name(commit_oid, name);
 +                      if (submodule) {
 +                              warning("Submodule in commit %s at path: "
 +                                      "'%s' collides with a submodule named "
 +                                      "the same. Skipping it.",
 +                                      oid_to_hex(commit_oid), name);
 +                              name = NULL;
 +                      }
                }
 +
 +              if (!name)
 +                      continue;
 +
 +              commits = submodule_commits(changed, name);
 +              oid_array_append(commits, &p->two->oid);
        }
  }
  
@@@ -770,14 -742,11 +770,14 @@@ static void collect_changed_submodules(
  
        while ((commit = get_revision(&rev))) {
                struct rev_info diff_rev;
 +              struct collect_changed_submodules_cb_data data;
 +              data.changed = changed;
 +              data.commit_oid = &commit->object.oid;
  
                init_revisions(&diff_rev, NULL);
                diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
                diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
 -              diff_rev.diffopt.format_callback_data = changed;
 +              diff_rev.diffopt.format_callback_data = &data;
                diff_tree_combined_merge(commit, 1, &diff_rev);
        }
  
@@@ -925,7 -894,7 +925,7 @@@ int find_unpushed_submodules(struct oid
                const char *remotes_name, struct string_list *needs_pushing)
  {
        struct string_list submodules = STRING_LIST_INIT_DUP;
 -      struct string_list_item *submodule;
 +      struct string_list_item *name;
        struct argv_array argv = ARGV_ARRAY_INIT;
  
        /* argv.argv[0] will be ignored by setup_revisions */
  
        collect_changed_submodules(&submodules, &argv);
  
 -      for_each_string_list_item(submodule, &submodules) {
 -              struct oid_array *commits = submodule->util;
 -              const char *path = submodule->string;
 +      for_each_string_list_item(name, &submodules) {
 +              struct oid_array *commits = name->util;
 +              const struct submodule *submodule;
 +              const char *path = NULL;
 +
 +              submodule = submodule_from_name(&null_oid, name->string);
 +              if (submodule)
 +                      path = submodule->path;
 +              else
 +                      path = default_name_or_path(name->string);
 +
 +              if (!path)
 +                      continue;
  
                if (submodule_needs_pushing(path, commits))
                        string_list_insert(needs_pushing, path);
@@@ -1057,7 -1016,7 +1057,7 @@@ int push_unpushed_submodules(struct oid
                char *head;
                struct object_id head_oid;
  
 -              head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
 +              head = resolve_refdup("HEAD", 0, &head_oid, NULL);
                if (!head)
                        die(_("Failed to resolve HEAD as a valid ref."));
  
@@@ -1106,7 -1065,7 +1106,7 @@@ static void calculate_changed_submodule
  {
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct string_list changed_submodules = STRING_LIST_INIT_DUP;
 -      const struct string_list_item *item;
 +      const struct string_list_item *name;
  
        /* No need to check if there are no submodules configured */
        if (!submodule_from_path(NULL, NULL))
  
        /*
         * Collect all submodules (whether checked out or not) for which new
 -       * commits have been recorded upstream in "changed_submodule_paths".
 +       * commits have been recorded upstream in "changed_submodule_names".
         */
        collect_changed_submodules(&changed_submodules, &argv);
  
 -      for_each_string_list_item(item, &changed_submodules) {
 -              struct oid_array *commits = item->util;
 -              const char *path = item->string;
 +      for_each_string_list_item(name, &changed_submodules) {
 +              struct oid_array *commits = name->util;
 +              const struct submodule *submodule;
 +              const char *path = NULL;
 +
 +              submodule = submodule_from_name(&null_oid, name->string);
 +              if (submodule)
 +                      path = submodule->path;
 +              else
 +                      path = default_name_or_path(name->string);
 +
 +              if (!path)
 +                      continue;
  
                if (!submodule_has_commits(path, commits))
 -                      string_list_append(&changed_submodule_paths, path);
 +                      string_list_append(&changed_submodule_names, name->string);
        }
  
        free_submodules_oids(&changed_submodules);
@@@ -1187,31 -1136,6 +1187,31 @@@ struct submodule_parallel_fetch 
  };
  #define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0}
  
 +static int get_fetch_recurse_config(const struct submodule *submodule,
 +                                  struct submodule_parallel_fetch *spf)
 +{
 +      if (spf->command_line_option != RECURSE_SUBMODULES_DEFAULT)
 +              return spf->command_line_option;
 +
 +      if (submodule) {
 +              char *key;
 +              const char *value;
 +
 +              int fetch_recurse = submodule->fetch_recurse;
 +              key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
 +              if (!repo_config_get_string_const(the_repository, key, &value)) {
 +                      fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
 +              }
 +              free(key);
 +
 +              if (fetch_recurse != RECURSE_SUBMODULES_NONE)
 +                      /* local config overrules everything except commandline */
 +                      return fetch_recurse;
 +      }
 +
 +      return spf->default_option;
 +}
 +
  static int get_next_submodule(struct child_process *cp,
                              struct strbuf *err, void *data, void **task_cb)
  {
                const struct cache_entry *ce = active_cache[spf->count];
                const char *git_dir, *default_argv;
                const struct submodule *submodule;
 +              struct submodule default_submodule = SUBMODULE_INIT;
  
                if (!S_ISGITLINK(ce->ce_mode))
                        continue;
  
                submodule = submodule_from_path(&null_oid, ce->name);
 -
 -              default_argv = "yes";
 -              if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
 -                      int fetch_recurse = RECURSE_SUBMODULES_NONE;
 -
 -                      if (submodule) {
 -                              char *key;
 -                              const char *value;
 -
 -                              fetch_recurse = submodule->fetch_recurse;
 -                              key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
 -                              if (!repo_config_get_string_const(the_repository, key, &value)) {
 -                                      fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
 -                              }
 -                              free(key);
 +              if (!submodule) {
 +                      const char *name = default_name_or_path(ce->name);
 +                      if (name) {
 +                              default_submodule.path = default_submodule.name = name;
 +                              submodule = &default_submodule;
                        }
 +              }
  
 -                      if (fetch_recurse != RECURSE_SUBMODULES_NONE) {
 -                              if (fetch_recurse == RECURSE_SUBMODULES_OFF)
 -                                      continue;
 -                              if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
 -                                      if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
 -                                              continue;
 -                                      default_argv = "on-demand";
 -                              }
 -                      } else {
 -                              if (spf->default_option == RECURSE_SUBMODULES_OFF)
 -                                      continue;
 -                              if (spf->default_option == RECURSE_SUBMODULES_ON_DEMAND) {
 -                                      if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
 -                                              continue;
 -                                      default_argv = "on-demand";
 -                              }
 -                      }
 -              } else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
 -                      if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
 +              switch (get_fetch_recurse_config(submodule, spf))
 +              {
 +              default:
 +              case RECURSE_SUBMODULES_DEFAULT:
 +              case RECURSE_SUBMODULES_ON_DEMAND:
 +                      if (!submodule || !unsorted_string_list_lookup(&changed_submodule_names,
 +                                                       submodule->name))
                                continue;
                        default_argv = "on-demand";
 +                      break;
 +              case RECURSE_SUBMODULES_ON:
 +                      default_argv = "yes";
 +                      break;
 +              case RECURSE_SUBMODULES_OFF:
 +                      continue;
                }
  
                strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name);
@@@ -1344,7 -1282,7 +1344,7 @@@ int fetch_populated_submodules(const st
  
        argv_array_clear(&spf.args);
  out:
 -      string_list_clear(&changed_submodule_paths, 1);
 +      string_list_clear(&changed_submodule_names, 1);
        return spf.result;
  }
  
diff --combined wt-status.c
index bedef256ce1aa3a51b0bb06de47ca7729a9ea3e3,4f76e19d3093b5c7d80f5d3343e0c603bec65aa5..ed3271c3f3eaaa399a19722bce2990d4b7398115
@@@ -559,12 -559,12 +559,12 @@@ static void wt_status_collect_changes_w
        init_revisions(&rev, NULL);
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
-       DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
+       rev.diffopt.flags.dirty_submodules = 1;
        rev.diffopt.ita_invisible_in_index = 1;
        if (!s->show_untracked_files)
-               DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+               rev.diffopt.flags.ignore_untracked_in_submodules = 1;
        if (s->ignore_submodule_arg) {
-               DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+               rev.diffopt.flags.override_submodule_config = 1;
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
        }
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
@@@ -583,7 -583,7 +583,7 @@@ static void wt_status_collect_changes_i
        opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
        setup_revisions(0, NULL, &rev, &opt);
  
-       DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+       rev.diffopt.flags.override_submodule_config = 1;
        rev.diffopt.ita_invisible_in_index = 1;
        if (s->ignore_submodule_arg) {
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
@@@ -949,7 -949,7 +949,7 @@@ static void wt_longstatus_print_verbose
        const char *c = color(WT_STATUS_HEADER, s);
  
        init_revisions(&rev, NULL);
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+       rev.diffopt.flags.allow_textconv = 1;
        rev.diffopt.ita_invisible_in_index = 1;
  
        memset(&opt, 0, sizeof(opt));
@@@ -1449,7 -1449,7 +1449,7 @@@ static void wt_status_get_detached_from
                return;
        }
  
 -      if (dwim_ref(cb.buf.buf, cb.buf.len, oid.hash, &ref) == 1 &&
 +      if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref) == 1 &&
            /* sha1 is a commit? match without further lookup */
            (!oidcmp(&cb.noid, &oid) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
@@@ -2263,8 -2263,8 +2263,8 @@@ int has_unstaged_changes(int ignore_sub
  
        init_revisions(&rev_info, NULL);
        if (ignore_submodules)
-               DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+               rev_info.diffopt.flags.ignore_submodules = 1;
+       rev_info.diffopt.flags.quick = 1;
        diff_setup_done(&rev_info.diffopt);
        result = run_diff_files(&rev_info, 0);
        return diff_result_code(&rev_info.diffopt, result);
@@@ -2283,8 -2283,8 +2283,8 @@@ int has_uncommitted_changes(int ignore_
  
        init_revisions(&rev_info, NULL);
        if (ignore_submodules)
-               DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+               rev_info.diffopt.flags.ignore_submodules = 1;
+       rev_info.diffopt.flags.quick = 1;
        add_head_to_pending(&rev_info);
        diff_setup_done(&rev_info.diffopt);
        result = run_diff_index(&rev_info, 1);
   */
  int require_clean_work_tree(const char *action, const char *hint, int ignore_submodules, int gently)
  {
 -      struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
 +      struct lock_file lock_file = LOCK_INIT;
        int err = 0, fd;
  
 -      fd = hold_locked_index(lock_file, 0);
 +      fd = hold_locked_index(&lock_file, 0);
        refresh_cache(REFRESH_QUIET);
        if (0 <= fd)
 -              update_index_if_able(&the_index, lock_file);
 -      rollback_lock_file(lock_file);
 +              update_index_if_able(&the_index, &lock_file);
 +      rollback_lock_file(&lock_file);
  
        if (has_unstaged_changes(ignore_submodules)) {
                /* TRANSLATORS: the action is e.g. "pull with rebase" */