Merge branch 'ds/close-object-store'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:37 +0000 (15:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:37 +0000 (15:25 -0700)
The commit-graph file is now part of the "files that the runtime
may keep open file descriptors on, all of which would need to be
closed when done with the object store", and the file descriptor to
an existing commit-graph file now is closed before "gc" finalizes a
new instance to replace it.

* ds/close-object-store:
packfile: rename close_all_packs to close_object_store
packfile: close commit-graph in close_all_packs
commit-graph: use raw_object_store when closing

12 files changed:
1  2 
builtin/am.c
builtin/clone.c
builtin/fetch.c
builtin/gc.c
builtin/merge.c
builtin/rebase.c
builtin/receive-pack.c
builtin/repack.c
commit-graph.c
packfile.c
packfile.h
upload-pack.c
diff --combined builtin/am.c
index 78389d08b631f0281b7f315de0fb67f61c044d0f,9315d32d2a0bee2d0a22aaaec5f4a174031b5761..252e37ddf08e45eceb98628fe598df3ebcafb1fd
@@@ -453,7 -453,6 +453,7 @@@ static int run_post_rewrite_hook(const 
  
        cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
        cp.stdout_to_stderr = 1;
 +      cp.trace2_hook_name = "post-rewrite";
  
        ret = run_command(&cp);
  
@@@ -486,24 -485,23 +486,24 @@@ static int copy_notes_for_rebase(const 
  
        while (!strbuf_getline_lf(&sb, fp)) {
                struct object_id from_obj, to_obj;
 +              const char *p;
  
 -              if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
 +              if (sb.len != the_hash_algo->hexsz * 2 + 1) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
  
 -              if (get_oid_hex(sb.buf, &from_obj)) {
 +              if (parse_oid_hex(sb.buf, &from_obj, &p)) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
  
 -              if (sb.buf[GIT_SHA1_HEXSZ] != ' ') {
 +              if (*p != ' ') {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
  
 -              if (get_oid_hex(sb.buf + GIT_SHA1_HEXSZ + 1, &to_obj)) {
 +              if (get_oid_hex(p + 1, &to_obj)) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
@@@ -1339,10 -1337,9 +1339,10 @@@ static void write_index_patch(const str
        struct rev_info rev_info;
        FILE *fp;
  
 -      if (!get_oid_tree("HEAD", &head))
 -              tree = lookup_tree(the_repository, &head);
 -      else
 +      if (!get_oid("HEAD", &head)) {
 +              struct commit *commit = lookup_commit_or_die(&head, "HEAD");
 +              tree = get_commit_tree(commit);
 +      } else
                tree = lookup_tree(the_repository,
                                   the_repository->hash_algo->empty_tree);
  
@@@ -1503,11 -1500,11 +1503,11 @@@ static int fall_back_threeway(const str
                 * review them with extra care to spot mismerges.
                 */
                struct rev_info rev_info;
 -              const char *diff_filter_str = "--diff-filter=AM";
  
                repo_init_revisions(the_repository, &rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
 -              diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
 +              rev_info.diffopt.filter |= diff_filter_bit('A');
 +              rev_info.diffopt.filter |= diff_filter_bit('M');
                add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
                diff_setup_done(&rev_info.diffopt);
                run_diff_index(&rev_info, 1);
@@@ -1582,7 -1579,6 +1582,7 @@@ static void do_commit(const struct am_s
        }
  
        author = fmt_ident(state->author_name, state->author_email,
 +              WANT_AUTHOR_IDENT,
                        state->ignore_date ? NULL : state->author_date,
                        IDENT_STRICT);
  
@@@ -1644,8 -1640,11 +1644,8 @@@ static int do_interactive(struct am_sta
  {
        assert(state->msg);
  
 -      if (!isatty(0))
 -              die(_("cannot be interactive without stdin connected to a terminal."));
 -
        for (;;) {
 -              const char *reply;
 +              char reply[64];
  
                puts(_("Commit Body is:"));
                puts("--------------------------");
                 * in your translation. The program will only accept English
                 * input at this point.
                 */
 -              reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);
 +              printf(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "));
 +              if (!fgets(reply, sizeof(reply), stdin))
 +                      die("unable to read from stdin; aborting");
  
 -              if (!reply) {
 -                      continue;
 -              } else if (*reply == 'y' || *reply == 'Y') {
 +              if (*reply == 'y' || *reply == 'Y') {
                        return 0;
                } else if (*reply == 'a' || *reply == 'A') {
                        state->interactive = 0;
@@@ -1801,7 -1800,7 +1801,7 @@@ next
         */
        if (!state->rebasing) {
                am_destroy(state);
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        }
  }
@@@ -2120,10 -2119,6 +2120,10 @@@ static int parse_opt_patchformat(const 
                *opt_value = PATCH_FORMAT_HG;
        else if (!strcmp(arg, "mboxrd"))
                *opt_value = PATCH_FORMAT_MBOXRD;
 +      /*
 +       * Please update $__git_patchformat in git-completion.bash
 +       * when you add new options
 +       */
        else
                return error(_("Invalid value for --patch-format: %s"), arg);
        return 0;
@@@ -2332,9 -2327,6 +2332,9 @@@ int cmd_am(int argc, const char **argv
                                argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
                }
  
 +              if (state.interactive && !paths.argc)
 +                      die(_("interactive mode requires patches on the command line"));
 +
                am_setup(&state, patch_format, paths.argv, keep_cr);
  
                argv_array_clear(&paths);
diff --combined builtin/clone.c
index 5b9ebe994761bd7b45209037b6eab63e28a78efb,82ce682c8065a430ffa953a015efbc4b238b9443..189ea1c2a0d2767534cb244c5780ad4a0d74a814
@@@ -66,8 -66,6 +66,8 @@@ static int option_dissociate
  static int max_jobs = -1;
  static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
  static struct list_objects_filter_options filter_options;
 +static struct string_list server_options = STRING_LIST_INIT_NODUP;
 +static int option_remote_submodules;
  
  static int recurse_submodules_cb(const struct option *opt,
                                 const char *arg, int unset)
@@@ -100,7 -98,10 +100,7 @@@ static struct option builtin_clone_opti
                    N_("don't use local hardlinks, always copy")),
        OPT_BOOL('s', "shared", &option_shared,
                    N_("setup as shared repository")),
 -      { OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
 -        N_("pathspec"), N_("initialize submodules in the clone"),
 -        PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
 -        (intptr_t)"." },
 +      OPT_ALIAS(0, "recursive", "recurse-submodules"),
        { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
          N_("pathspec"), N_("initialize submodules in the clone"),
          PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
                   N_("separate git dir from working tree")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
 +      OPT_STRING_LIST(0, "server-option", &server_options,
 +                      N_("server-specific"), N_("option to transmit")),
        OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
                        TRANSPORT_FAMILY_IPV4),
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
 +      OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
 +                  N_("any cloned submodules will use their remote-tracking branch")),
        OPT_END()
  };
  
@@@ -357,7 -354,8 +357,7 @@@ static void setup_reference(void
                             add_one_reference, &required);
  }
  
 -static void copy_alternates(struct strbuf *src, struct strbuf *dst,
 -                          const char *src_repo)
 +static void copy_alternates(struct strbuf *src, const char *src_repo)
  {
        /*
         * Read from the source objects/info/alternates file
@@@ -438,7 -436,7 +438,7 @@@ static void copy_or_link_directory(stru
  
                /* Files that cannot be copied bit-for-bit... */
                if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
 -                      copy_alternates(src, dest, src_repo);
 +                      copy_alternates(src, src_repo);
                        continue;
                }
  
@@@ -659,8 -657,7 +659,8 @@@ static void update_remote_refs(const st
                               const char *branch_top,
                               const char *msg,
                               struct transport *transport,
 -                             int check_connectivity)
 +                             int check_connectivity,
 +                             int check_refs_only)
  {
        const struct ref *rm = mapped_refs;
  
  
                opt.transport = transport;
                opt.progress = transport->progress;
 +              opt.check_refs_only = !!check_refs_only;
  
                if (check_connected(iterate_ref_map, &rm, &opt))
                        die(_("remote did not send all necessary objects"));
@@@ -793,11 -789,6 +793,11 @@@ static int checkout(int submodule_progr
                if (option_verbosity < 0)
                        argv_array_push(&args, "--quiet");
  
 +              if (option_remote_submodules) {
 +                      argv_array_push(&args, "--remote");
 +                      argv_array_push(&args, "--no-fetch");
 +              }
 +
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
@@@ -1145,9 -1136,6 +1145,9 @@@ int cmd_clone(int argc, const char **ar
                transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                     option_upload_pack);
  
 +      if (server_options.nr)
 +              transport->server_options = &server_options;
 +
        if (filter_options.choice) {
                struct strbuf expanded_filter_spec = STRBUF_INIT;
                expand_list_objects_filter_spec(&filter_options,
                        remote_head_points_at, &branch_top);
  
        if (filter_options.choice)
 -              partial_clone_register("origin", &filter_options);
 +              partial_clone_register(option_origin, &filter_options);
  
        if (is_local)
                clone_local(path, git_dir);
  
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
                           branch_top.buf, reflog_msg.buf, transport,
 -                         !is_local);
 +                         !is_local, filter_options.choice);
  
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
  
        transport_disconnect(transport);
  
        if (option_dissociate) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                dissociate_from_references();
        }
  
diff --combined builtin/fetch.c
index f2be50a4a3cf4291fd2ea40e2bd33db417925f44,3aec95608ffafdc8df65701c3cd3413e56692373..c9b92b1e52448bb1537a229b3ed7d1587db28ecf
@@@ -239,7 -239,6 +239,7 @@@ static int will_fetch(struct ref **head
  struct refname_hash_entry {
        struct hashmap_entry ent; /* must be the first member */
        struct object_id oid;
 +      int ignore;
        char refname[FLEX_ARRAY];
  };
  
@@@ -288,11 -287,6 +288,11 @@@ static int refname_hash_exists(struct h
        return !!hashmap_get_from_hash(map, strhash(refname), refname);
  }
  
 +static void clear_item(struct refname_hash_entry *item)
 +{
 +      item->ignore = 1;
 +}
 +
  static void find_non_local_tags(const struct ref *refs,
                                struct ref **head,
                                struct ref ***tail)
                            !will_fetch(head, ref->old_oid.hash) &&
                            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                            !will_fetch(head, item->oid.hash))
 -                              oidclr(&item->oid);
 +                              clear_item(item);
                        item = NULL;
                        continue;
                }
                if (item &&
                    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                    !will_fetch(head, item->oid.hash))
 -                      oidclr(&item->oid);
 +                      clear_item(item);
  
                item = NULL;
  
        if (item &&
            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
            !will_fetch(head, item->oid.hash))
 -              oidclr(&item->oid);
 +              clear_item(item);
  
        /*
         * For all the tags in the remote_refs_list,
         */
        for_each_string_list_item(remote_ref_item, &remote_refs_list) {
                const char *refname = remote_ref_item->string;
 +              struct ref *rm;
  
                item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
                if (!item)
                        BUG("unseen remote ref?");
  
                /* Unless we have already decided to ignore this item... */
 -              if (!is_null_oid(&item->oid)) {
 -                      struct ref *rm = alloc_ref(item->refname);
 -                      rm->peer_ref = alloc_ref(item->refname);
 -                      oidcpy(&rm->old_oid, &item->oid);
 -                      **tail = rm;
 -                      *tail = &rm->next;
 -              }
 +              if (item->ignore)
 +                      continue;
 +
 +              rm = alloc_ref(item->refname);
 +              rm->peer_ref = alloc_ref(item->refname);
 +              oidcpy(&rm->old_oid, &item->oid);
 +              **tail = rm;
 +              *tail = &rm->next;
        }
        hashmap_free(&remote_refs, 1);
        string_list_clear(&remote_refs_list, 0);
@@@ -1564,9 -1556,7 +1564,9 @@@ static int fetch_one(struct remote *rem
  
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
 +      sigchain_push(SIGPIPE, SIG_IGN);
        exit_code = do_fetch(gtransport, &rs);
 +      sigchain_pop(SIGPIPE);
        refspec_clear(&rs);
        transport_disconnect(gtransport);
        gtransport = NULL;
@@@ -1680,7 -1670,7 +1680,7 @@@ int cmd_fetch(int argc, const char **ar
  
        string_list_clear(&list, 0);
  
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
  
        argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
        if (verbosity < 0)
diff --combined builtin/gc.c
index 8ba9bd247286413c353a1332be0d188b4ae37e47,20c8f1bfe8024d21e4ef138bc08cdc5536c13452..be8e0bfcbe0a428f72c5762ff8a066baaefd2533
@@@ -116,19 -116,6 +116,19 @@@ static void process_log_file_on_signal(
        raise(signo);
  }
  
 +static int gc_config_is_timestamp_never(const char *var)
 +{
 +      const char *value;
 +      timestamp_t expire;
 +
 +      if (!git_config_get_value(var, &value) && value) {
 +              if (parse_expiry_date(value, &expire))
 +                      die(_("failed to parse '%s' value '%s'"), var, value);
 +              return expire == 0;
 +      }
 +      return 0;
 +}
 +
  static void gc_config(void)
  {
        const char *value;
                        pack_refs = git_config_bool("gc.packrefs", value);
        }
  
 +      if (gc_config_is_timestamp_never("gc.reflogexpire") &&
 +          gc_config_is_timestamp_never("gc.reflogexpireunreachable"))
 +              prune_reflogs = 0;
 +
        git_config_get_int("gc.aggressivewindow", &aggressive_window);
        git_config_get_int("gc.aggressivedepth", &aggressive_depth);
        git_config_get_int("gc.auto", &gc_auto_threshold);
@@@ -173,7 -156,9 +173,7 @@@ static int too_many_loose_objects(void
        int auto_threshold;
        int num_loose = 0;
        int needed = 0;
 -
 -      if (gc_auto_threshold <= 0)
 -              return 0;
 +      const unsigned hexsz_loose = the_hash_algo->hexsz - 2;
  
        dir = opendir(git_path("objects/17"));
        if (!dir)
  
        auto_threshold = DIV_ROUND_UP(gc_auto_threshold, 256);
        while ((ent = readdir(dir)) != NULL) {
 -              if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
 -                  ent->d_name[38] != '\0')
 +              if (strspn(ent->d_name, "0123456789abcdef") != hexsz_loose ||
 +                  ent->d_name[hexsz_loose] != '\0')
                        continue;
                if (++num_loose > auto_threshold) {
                        needed = 1;
@@@ -506,20 -491,14 +506,20 @@@ done
  
  static void gc_before_repack(void)
  {
 +      /*
 +       * We may be called twice, as both the pre- and
 +       * post-daemonized phases will call us, but running these
 +       * commands more than once is pointless and wasteful.
 +       */
 +      static int done = 0;
 +      if (done++)
 +              return;
 +
        if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
                die(FAILED_RUN, pack_refs_cmd.argv[0]);
  
        if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
                die(FAILED_RUN, reflog.argv[0]);
 -
 -      pack_refs = 0;
 -      prune_reflogs = 0;
  }
  
  int cmd_gc(int argc, const char **argv, const char *prefix)
        gc_before_repack();
  
        if (!repository_format_precious_objects) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
                        die(FAILED_RUN, repack.argv[0]);
  
        report_garbage = report_pack_garbage;
        reprepare_packed_git(the_repository);
        if (pack_garbage.nr > 0) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                clean_pack_garbage();
        }
  
diff --combined builtin/merge.c
index 6e99aead46390a60580966160c556b39ec890126,72d7a7c909080cd08c59a6c12c95a516cf08916d..aad5a9504c8546db0adfd36c59cb1ad1918a56e9
@@@ -37,9 -37,7 +37,9 @@@
  #include "packfile.h"
  #include "tag.h"
  #include "alias.h"
 +#include "branch.h"
  #include "commit-reach.h"
 +#include "wt-status.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -59,7 -57,7 +59,7 @@@ static const char * const builtin_merge
  };
  
  static int show_diffstat = 1, shortlog_len = -1, squash;
 -static int option_commit = 1;
 +static int option_commit = -1;
  static int option_edit = -1;
  static int allow_trivial = 1, have_message, verify_signatures;
  static int overwrite_ignore = 1;
@@@ -74,7 -72,6 +74,7 @@@ static int option_renormalize
  static int verbosity;
  static int allow_rerere_auto;
  static int abort_current_merge;
 +static int quit_current_merge;
  static int continue_current_merge;
  static int allow_unrelated_histories;
  static int show_progress = -1;
@@@ -101,9 -98,6 +101,9 @@@ enum ff_type 
  
  static enum ff_type fast_forward = FF_ALLOW;
  
 +static const char *cleanup_arg;
 +static enum commit_msg_cleanup_mode cleanup_mode;
 +
  static int option_parse_message(const struct option *opt,
                                const char *arg, int unset)
  {
        return 0;
  }
  
 -static int option_read_message(struct parse_opt_ctx_t *ctx,
 -                             const struct option *opt, int unset)
 +static enum parse_opt_result option_read_message(struct parse_opt_ctx_t *ctx,
 +                                               const struct option *opt,
 +                                               const char *arg_not_used,
 +                                               int unset)
  {
        struct strbuf *buf = opt->value;
        const char *arg;
  
 +      BUG_ON_OPT_ARG(arg_not_used);
        if (unset)
                BUG("-F cannot be negated");
  
@@@ -255,7 -246,6 +255,7 @@@ static struct option builtin_merge_opti
                N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
                N_("edit message before committing")),
 +      OPT_CLEANUP(&cleanup_arg),
        OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
        OPT_SET_INT_F(0, "ff-only", &fast_forward,
                      N_("abort if fast-forward is not possible"),
                option_parse_message),
        { OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
                N_("read message from file"), PARSE_OPT_NONEG,
 -              (parse_opt_cb *) option_read_message },
 +              NULL, 0, option_read_message },
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
 +      OPT_BOOL(0, "quit", &quit_current_merge,
 +              N_("--abort but leave index and working tree alone")),
        OPT_BOOL(0, "continue", &continue_current_merge,
                N_("continue the current in-progress merge")),
        OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
        OPT_END()
  };
  
 -/* Cleans up metadata that is uninteresting after a succeeded merge. */
 -static void drop_save(void)
 -{
 -      unlink(git_path_merge_head(the_repository));
 -      unlink(git_path_merge_msg(the_repository));
 -      unlink(git_path_merge_mode(the_repository));
 -}
 -
  static int save_state(struct object_id *stash)
  {
        int len;
@@@ -384,7 -380,7 +384,7 @@@ static void finish_up_to_date(const cha
  {
        if (verbosity >= 0)
                printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg);
 -      drop_save();
 +      remove_merge_branch_state(the_repository);
  }
  
  static void squash_message(struct commit *commit, struct commit_list *remoteheads)
@@@ -453,7 -449,7 +453,7 @@@ static void finish(struct commit *head_
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
                         */
-                       close_all_packs(the_repository->objects);
+                       close_object_store(the_repository->objects);
                        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
                }
        }
@@@ -613,8 -609,6 +613,8 @@@ static int git_merge_config(const char 
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
                return git_config_string(&pull_octopus, k, v);
 +      else if (!strcmp(k, "commit.cleanup"))
 +              return git_config_string(&cleanup_arg, k, v);
        else if (!strcmp(k, "merge.renormalize"))
                option_renormalize = git_config_bool(k, v);
        else if (!strcmp(k, "merge.ff")) {
@@@ -803,13 -797,8 +803,13 @@@ static void abort_commit(struct commit_
  static const char merge_editor_comment[] =
  N_("Please enter a commit message to explain why this merge is necessary,\n"
     "especially if it merges an updated upstream into a topic branch.\n"
 -   "\n"
 -   "Lines starting with '%c' will be ignored, and an empty message aborts\n"
 +   "\n");
 +
 +static const char scissors_editor_comment[] =
 +N_("An empty message aborts the commit.\n");
 +
 +static const char no_scissors_editor_comment[] =
 +N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
     "the commit.\n");
  
  static void write_merge_heads(struct commit_list *);
@@@ -817,19 -806,11 +817,19 @@@ static void prepare_to_commit(struct co
  {
        struct strbuf msg = STRBUF_INIT;
        strbuf_addbuf(&msg, &merge_msg);
 -      strbuf_addch(&msg, '\n');
        if (squash)
                BUG("the control must not reach here under --squash");
 -      if (0 < option_edit)
 -              strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
 +      if (0 < option_edit) {
 +              strbuf_addch(&msg, '\n');
 +              if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
 +                      wt_status_append_cut_line(&msg);
 +                      strbuf_commented_addf(&msg, "\n");
 +              }
 +              strbuf_commented_addf(&msg, _(merge_editor_comment));
 +              strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?
 +                      scissors_editor_comment :
 +                      no_scissors_editor_comment), comment_line_char);
 +      }
        if (signoff)
                append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
                abort_commit(remoteheads, NULL);
  
        read_merge_msg(&msg);
 -      strbuf_stripspace(&msg, 0 < option_edit);
 +      cleanup_message(&msg, cleanup_mode, 0);
        if (!msg.len)
                abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
@@@ -877,7 -858,7 +877,7 @@@ static int merge_trivial(struct commit 
                        &result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, &result_commit, "In-index merge");
 -      drop_save();
 +      remove_merge_branch_state(the_repository);
        return 0;
  }
  
@@@ -896,6 -877,7 +896,6 @@@ static int finish_automerge(struct comm
        parents = remoteheads;
        if (!head_subsumed || fast_forward == FF_NO)
                commit_list_insert(head, &parents);
 -      strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
        if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
                        &result_commit, NULL, sign_commit))
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, remoteheads, &result_commit, buf.buf);
        strbuf_release(&buf);
 -      drop_save();
 +      remove_merge_branch_state(the_repository);
        return 0;
  }
  
@@@ -916,15 -898,7 +916,15 @@@ static int suggest_conflicts(void
        filename = git_path_merge_msg(the_repository);
        fp = xfopen(filename, "a");
  
 -      append_conflicts_hint(&the_index, &msgbuf);
 +      /*
 +       * We can't use cleanup_mode because if we're not using the editor,
 +       * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even
 +       * though the message is meant to be processed later by git-commit.
 +       * Thus, we will get the cleanup mode which is returned when we _are_
 +       * using an editor.
 +       */
 +      append_conflicts_hint(&the_index, &msgbuf,
 +                            get_cleanup_mode(cleanup_arg, 1));
        fputs(msgbuf.buf, fp);
        strbuf_release(&msgbuf);
        fclose(fp);
@@@ -1285,16 -1259,6 +1285,16 @@@ int cmd_merge(int argc, const char **ar
                goto done;
        }
  
 +      if (quit_current_merge) {
 +              if (orig_argc != 2)
 +                      usage_msg_opt(_("--quit expects no arguments"),
 +                                    builtin_merge_usage,
 +                                    builtin_merge_options);
 +
 +              remove_merge_branch_state(the_repository);
 +              goto done;
 +      }
 +
        if (continue_current_merge) {
                int nargc = 1;
                const char *nargv[] = {"commit", NULL};
        }
        resolve_undo_clear();
  
 +      if (option_edit < 0)
 +              option_edit = default_edit_option();
 +
 +      cleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);
 +
        if (verbosity < 0)
                show_diffstat = 0;
  
        if (squash) {
                if (fast_forward == FF_NO)
                        die(_("You cannot combine --squash with --no-ff."));
 +              if (option_commit > 0)
 +                      die(_("You cannot combine --squash with --commit."));
 +              /*
 +               * squash can now silently disable option_commit - this is not
 +               * a problem as it is only overriding the default, not a user
 +               * supplied option.
 +               */
                option_commit = 0;
        }
  
 +      if (option_commit < 0)
 +              option_commit = 1;
 +
        if (!argc) {
                if (default_to_upstream)
                        argc = setup_with_upstream(&argv);
                        fast_forward = FF_NO;
        }
  
 -      if (option_edit < 0)
 -              option_edit = default_edit_option();
 -
        if (!use_strategies) {
                if (!remoteheads)
                        ; /* already up-to-date */
                }
  
                finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
 -              drop_save();
 +              remove_merge_branch_state(the_repository);
                goto done;
        } else if (!remoteheads->next && common->next)
                ;
diff --combined builtin/rebase.c
index b8116db4876b1a353911a78d97ecd57370df8192,ed30fcd63334c1984f834ecc39a808e4d7745ffa..51438d08f2bedbdde8504afb05f547c6aea07ad8
@@@ -25,8 -25,6 +25,8 @@@
  #include "commit-reach.h"
  #include "rerere.h"
  #include "branch.h"
 +#include "sequencer.h"
 +#include "rebase-interactive.h"
  
  static char const * const builtin_rebase_usage[] = {
        N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@@ -37,8 -35,6 +37,8 @@@
        NULL
  };
  
 +static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 +static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
  static GIT_PATH_FUNC(apply_dir, "rebase-apply")
  static GIT_PATH_FUNC(merge_dir, "rebase-merge")
  
@@@ -50,6 -46,29 +50,6 @@@ enum rebase_type 
        REBASE_PRESERVE_MERGES
  };
  
 -static int use_builtin_rebase(void)
 -{
 -      struct child_process cp = CHILD_PROCESS_INIT;
 -      struct strbuf out = STRBUF_INIT;
 -      int ret, env = git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1);
 -
 -      if (env != -1)
 -              return env;
 -
 -      argv_array_pushl(&cp.args,
 -                       "config", "--bool", "rebase.usebuiltin", NULL);
 -      cp.git_cmd = 1;
 -      if (capture_command(&cp, &out, 6)) {
 -              strbuf_release(&out);
 -              return 1;
 -      }
 -
 -      strbuf_trim(&out);
 -      ret = !strcmp("true", out.buf);
 -      strbuf_release(&out);
 -      return ret;
 -}
 -
  struct rebase_options {
        enum rebase_type type;
        const char *state_dir;
        char *strategy, *strategy_opts;
        struct strbuf git_format_patch_opt;
        int reschedule_failed_exec;
 +      int use_legacy_rebase;
 +};
 +
 +#define REBASE_OPTIONS_INIT {                         \
 +              .type = REBASE_UNSPECIFIED,             \
 +              .flags = REBASE_NO_QUIET,               \
 +              .git_am_opts = ARGV_ARRAY_INIT,         \
 +              .git_format_patch_opt = STRBUF_INIT     \
 +      }
 +
 +static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 +{
 +      struct replay_opts replay = REPLAY_OPTS_INIT;
 +
 +      replay.action = REPLAY_INTERACTIVE_REBASE;
 +      sequencer_init_config(&replay);
 +
 +      replay.signoff = opts->signoff;
 +      replay.allow_ff = !(opts->flags & REBASE_FORCE);
 +      if (opts->allow_rerere_autoupdate)
 +              replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
 +      replay.allow_empty = 1;
 +      replay.allow_empty_message = opts->allow_empty_message;
 +      replay.verbose = opts->flags & REBASE_VERBOSE;
 +      replay.reschedule_failed_exec = opts->reschedule_failed_exec;
 +      replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
 +      replay.strategy = opts->strategy;
 +      if (opts->strategy_opts)
 +              parse_strategy_opts(&replay, opts->strategy_opts);
 +
 +      return replay;
 +}
 +
 +enum action {
 +      ACTION_NONE = 0,
 +      ACTION_CONTINUE,
 +      ACTION_SKIP,
 +      ACTION_ABORT,
 +      ACTION_QUIT,
 +      ACTION_EDIT_TODO,
 +      ACTION_SHOW_CURRENT_PATCH,
 +      ACTION_SHORTEN_OIDS,
 +      ACTION_EXPAND_OIDS,
 +      ACTION_CHECK_TODO_LIST,
 +      ACTION_REARRANGE_SQUASH,
 +      ACTION_ADD_EXEC
  };
  
 +static const char *action_names[] = { "undefined",
 +                                    "continue",
 +                                    "skip",
 +                                    "abort",
 +                                    "quit",
 +                                    "edit_todo",
 +                                    "show_current_patch" };
 +
 +static int add_exec_commands(struct string_list *commands)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      int res;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +
 +      if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                      &todo_list)) {
 +              todo_list_release(&todo_list);
 +              return error(_("unusable todo list: '%s'"), todo_file);
 +      }
 +
 +      todo_list_add_exec_commands(&todo_list, commands);
 +      res = todo_list_write_to_file(the_repository, &todo_list,
 +                                    todo_file, NULL, NULL, -1, 0);
 +      todo_list_release(&todo_list);
 +
 +      if (res)
 +              return error_errno(_("could not write '%s'."), todo_file);
 +      return 0;
 +}
 +
 +static int rearrange_squash_in_todo_file(void)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      int res = 0;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +      if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                      &todo_list)) {
 +              todo_list_release(&todo_list);
 +              return error(_("unusable todo list: '%s'"), todo_file);
 +      }
 +
 +      res = todo_list_rearrange_squash(&todo_list);
 +      if (!res)
 +              res = todo_list_write_to_file(the_repository, &todo_list,
 +                                            todo_file, NULL, NULL, -1, 0);
 +
 +      todo_list_release(&todo_list);
 +
 +      if (res)
 +              return error_errno(_("could not write '%s'."), todo_file);
 +      return 0;
 +}
 +
 +static int transform_todo_file(unsigned flags)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      int res;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +
 +      if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                      &todo_list)) {
 +              todo_list_release(&todo_list);
 +              return error(_("unusable todo list: '%s'"), todo_file);
 +      }
 +
 +      res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
 +                                    NULL, NULL, -1, flags);
 +      todo_list_release(&todo_list);
 +
 +      if (res)
 +              return error_errno(_("could not write '%s'."), todo_file);
 +      return 0;
 +}
 +
 +static int edit_todo_file(unsigned flags)
 +{
 +      const char *todo_file = rebase_path_todo();
 +      struct todo_list todo_list = TODO_LIST_INIT,
 +              new_todo = TODO_LIST_INIT;
 +      int res = 0;
 +
 +      if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 +              return error_errno(_("could not read '%s'."), todo_file);
 +
 +      strbuf_stripspace(&todo_list.buf, 1);
 +      res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
 +      if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
 +                                          NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
 +              res = error_errno(_("could not write '%s'"), todo_file);
 +
 +      todo_list_release(&todo_list);
 +      todo_list_release(&new_todo);
 +
 +      return res;
 +}
 +
 +static int get_revision_ranges(struct commit *upstream, struct commit *onto,
 +                             const char **head_hash,
 +                             char **revisions, char **shortrevisions)
 +{
 +      struct commit *base_rev = upstream ? upstream : onto;
 +      const char *shorthead;
 +      struct object_id orig_head;
 +
 +      if (get_oid("HEAD", &orig_head))
 +              return error(_("no HEAD?"));
 +
 +      *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
 +      *revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
 +                                                 *head_hash);
 +
 +      shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
 +
 +      if (upstream) {
 +              const char *shortrev;
 +
 +              shortrev = find_unique_abbrev(&base_rev->object.oid,
 +                                            DEFAULT_ABBREV);
 +
 +              *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
 +      } else
 +              *shortrevisions = xstrdup(shorthead);
 +
 +      return 0;
 +}
 +
 +static int init_basic_state(struct replay_opts *opts, const char *head_name,
 +                          struct commit *onto, const char *orig_head)
 +{
 +      FILE *interactive;
 +
 +      if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
 +              return error_errno(_("could not create temporary %s"), merge_dir());
 +
 +      delete_reflog("REBASE_HEAD");
 +
 +      interactive = fopen(path_interactive(), "w");
 +      if (!interactive)
 +              return error_errno(_("could not mark as interactive"));
 +      fclose(interactive);
 +
 +      return write_basic_state(opts, head_name, onto, orig_head);
 +}
 +
 +static void split_exec_commands(const char *cmd, struct string_list *commands)
 +{
 +      if (cmd && *cmd) {
 +              string_list_split(commands, cmd, '\n', -1);
 +
 +              /* rebase.c adds a new line to cmd after every command,
 +               * so here the last command is always empty */
 +              string_list_remove_empty_items(commands, 0);
 +      }
 +}
 +
 +static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 +{
 +      int ret;
 +      const char *head_hash = NULL;
 +      char *revisions = NULL, *shortrevisions = NULL;
 +      struct argv_array make_script_args = ARGV_ARRAY_INIT;
 +      struct todo_list todo_list = TODO_LIST_INIT;
 +      struct replay_opts replay = get_replay_opts(opts);
 +      struct string_list commands = STRING_LIST_INIT_DUP;
 +
 +      if (prepare_branch_to_be_rebased(the_repository, &replay,
 +                                       opts->switch_to))
 +              return -1;
 +
 +      if (get_revision_ranges(opts->upstream, opts->onto, &head_hash,
 +                              &revisions, &shortrevisions))
 +              return -1;
 +
 +      if (init_basic_state(&replay,
 +                           opts->head_name ? opts->head_name : "detached HEAD",
 +                           opts->onto, head_hash)) {
 +              free(revisions);
 +              free(shortrevisions);
 +
 +              return -1;
 +      }
 +
 +      if (!opts->upstream && opts->squash_onto)
 +              write_file(path_squash_onto(), "%s\n",
 +                         oid_to_hex(opts->squash_onto));
 +
 +      argv_array_pushl(&make_script_args, "", revisions, NULL);
 +      if (opts->restrict_revision)
 +              argv_array_push(&make_script_args,
 +                              oid_to_hex(&opts->restrict_revision->object.oid));
 +
 +      ret = sequencer_make_script(the_repository, &todo_list.buf,
 +                                  make_script_args.argc, make_script_args.argv,
 +                                  flags);
 +
 +      if (ret)
 +              error(_("could not generate todo list"));
 +      else {
 +              discard_cache();
 +              if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 +                                              &todo_list))
 +                      BUG("unusable todo list");
 +
 +              split_exec_commands(opts->cmd, &commands);
 +              ret = complete_action(the_repository, &replay, flags,
 +                      shortrevisions, opts->onto_name, opts->onto, head_hash,
 +                      &commands, opts->autosquash, &todo_list);
 +      }
 +
 +      string_list_clear(&commands, 0);
 +      free(revisions);
 +      free(shortrevisions);
 +      todo_list_release(&todo_list);
 +      argv_array_clear(&make_script_args);
 +
 +      return ret;
 +}
 +
 +static int run_rebase_interactive(struct rebase_options *opts,
 +                                enum action command)
 +{
 +      unsigned flags = 0;
 +      int abbreviate_commands = 0, ret = 0;
 +
 +      git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
 +
 +      flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
 +      flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
 +      flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
 +      flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
 +      flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
 +
 +      switch (command) {
 +      case ACTION_NONE: {
 +              if (!opts->onto && !opts->upstream)
 +                      die(_("a base commit must be provided with --upstream or --onto"));
 +
 +              ret = do_interactive_rebase(opts, flags);
 +              break;
 +      }
 +      case ACTION_SKIP: {
 +              struct string_list merge_rr = STRING_LIST_INIT_DUP;
 +
 +              rerere_clear(the_repository, &merge_rr);
 +      }
 +              /* fallthrough */
 +      case ACTION_CONTINUE: {
 +              struct replay_opts replay_opts = get_replay_opts(opts);
 +
 +              ret = sequencer_continue(the_repository, &replay_opts);
 +              break;
 +      }
 +      case ACTION_EDIT_TODO:
 +              ret = edit_todo_file(flags);
 +              break;
 +      case ACTION_SHOW_CURRENT_PATCH: {
 +              struct child_process cmd = CHILD_PROCESS_INIT;
 +
 +              cmd.git_cmd = 1;
 +              argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
 +              ret = run_command(&cmd);
 +
 +              break;
 +      }
 +      case ACTION_SHORTEN_OIDS:
 +      case ACTION_EXPAND_OIDS:
 +              ret = transform_todo_file(flags);
 +              break;
 +      case ACTION_CHECK_TODO_LIST:
 +              ret = check_todo_list_from_file(the_repository);
 +              break;
 +      case ACTION_REARRANGE_SQUASH:
 +              ret = rearrange_squash_in_todo_file();
 +              break;
 +      case ACTION_ADD_EXEC: {
 +              struct string_list commands = STRING_LIST_INIT_DUP;
 +
 +              split_exec_commands(opts->cmd, &commands);
 +              ret = add_exec_commands(&commands);
 +              string_list_clear(&commands, 0);
 +              break;
 +      }
 +      default:
 +              BUG("invalid command '%d'", command);
 +      }
 +
 +      return ret;
 +}
 +
 +static const char * const builtin_rebase_interactive_usage[] = {
 +      N_("git rebase--interactive [<options>]"),
 +      NULL
 +};
 +
 +int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 +{
 +      struct rebase_options opts = REBASE_OPTIONS_INIT;
 +      struct object_id squash_onto = null_oid;
 +      enum action command = ACTION_NONE;
 +      struct option options[] = {
 +              OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
 +                         REBASE_FORCE),
 +              OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
 +              OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
 +                       N_("allow commits with empty messages")),
 +              OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
 +              OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
 +                       N_("keep original branch points of cousins")),
 +              OPT_BOOL(0, "autosquash", &opts.autosquash,
 +                       N_("move commits that begin with squash!/fixup!")),
 +              OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
 +              OPT_BIT('v', "verbose", &opts.flags,
 +                      N_("display a diffstat of what changed upstream"),
 +                      REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
 +              OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
 +                          ACTION_CONTINUE),
 +              OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
 +              OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
 +                          ACTION_EDIT_TODO),
 +              OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
 +                          ACTION_SHOW_CURRENT_PATCH),
 +              OPT_CMDMODE(0, "shorten-ids", &command,
 +                      N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
 +              OPT_CMDMODE(0, "expand-ids", &command,
 +                      N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
 +              OPT_CMDMODE(0, "check-todo-list", &command,
 +                      N_("check the todo list"), ACTION_CHECK_TODO_LIST),
 +              OPT_CMDMODE(0, "rearrange-squash", &command,
 +                      N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
 +              OPT_CMDMODE(0, "add-exec-commands", &command,
 +                      N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
 +              { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
 +                PARSE_OPT_NONEG, parse_opt_commit, 0 },
 +              { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
 +                N_("restrict-revision"), N_("restrict revision"),
 +                PARSE_OPT_NONEG, parse_opt_commit, 0 },
 +              { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
 +                N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
 +              { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
 +                N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
 +                0 },
 +              OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
 +              { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
 +                      N_("GPG-sign commits"),
 +                      PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 +              OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
 +                         N_("rebase strategy")),
 +              OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
 +                         N_("strategy options")),
 +              OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
 +                         N_("the branch or commit to checkout")),
 +              OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
 +              OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
 +              OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
 +              OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
 +                       N_("automatically re-schedule any `exec` that fails")),
 +              OPT_END()
 +      };
 +
 +      opts.rebase_cousins = -1;
 +
 +      if (argc == 1)
 +              usage_with_options(builtin_rebase_interactive_usage, options);
 +
 +      argc = parse_options(argc, argv, prefix, options,
 +                      builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
 +
 +      if (!is_null_oid(&squash_onto))
 +              opts.squash_onto = &squash_onto;
 +
 +      if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
 +              warning(_("--[no-]rebase-cousins has no effect without "
 +                        "--rebase-merges"));
 +
 +      return !!run_rebase_interactive(&opts, command);
 +}
 +
  static int is_interactive(struct rebase_options *opts)
  {
        return opts->type == REBASE_INTERACTIVE ||
@@@ -619,13 -206,14 +619,13 @@@ static int read_basic_state(struct reba
                            &buf))
                        return -1;
                if (!strcmp(buf.buf, "--rerere-autoupdate"))
 -                      opts->allow_rerere_autoupdate = 1;
 +                      opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
                else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
 -                      opts->allow_rerere_autoupdate = 0;
 +                      opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE;
                else
                        warning(_("ignoring invalid allow_rerere_autoupdate: "
                                  "'%s'"), buf.buf);
 -      } else
 -              opts->allow_rerere_autoupdate = -1;
 +      }
  
        if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
                strbuf_reset(&buf);
        return 0;
  }
  
 -static int write_basic_state(struct rebase_options *opts)
 +static int rebase_write_basic_state(struct rebase_options *opts)
  {
        write_file(state_dir_path("head-name", opts), "%s",
                   opts->head_name ? opts->head_name : "detached HEAD");
        if (opts->strategy_opts)
                write_file(state_dir_path("strategy_opts", opts), "%s",
                           opts->strategy_opts);
 -      if (opts->allow_rerere_autoupdate >= 0)
 +      if (opts->allow_rerere_autoupdate > 0)
                write_file(state_dir_path("allow_rerere_autoupdate", opts),
                           "-%s-rerere-autoupdate",
 -                         opts->allow_rerere_autoupdate ? "" : "-no");
 +                         opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
 +                              "" : "-no");
        if (opts->gpg_sign_opt)
                write_file(state_dir_path("gpg_sign_opt", opts), "%s",
                           opts->gpg_sign_opt);
@@@ -741,7 -328,7 +741,7 @@@ static int finish_rebase(struct rebase_
  
        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
        apply_autostash(opts);
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
        /*
         * We ignore errors in 'gc --auto', since the
         * user should see them.
@@@ -782,7 -369,6 +782,7 @@@ static void add_var(struct strbuf *buf
  #define RESET_HEAD_HARD (1<<1)
  #define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
  #define RESET_HEAD_REFS_ONLY (1<<3)
 +#define RESET_ORIG_HEAD (1<<4)
  
  static int reset_head(struct object_id *oid, const char *action,
                      const char *switch_to_branch, unsigned flags,
        unsigned reset_hard = flags & RESET_HEAD_HARD;
        unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
 +      unsigned update_orig_head = flags & RESET_ORIG_HEAD;
        struct object_id head_oid;
        struct tree_desc desc[2] = { { NULL }, { NULL } };
        struct lock_file lock = LOCK_INIT;
@@@ -869,21 -454,18 +869,21 @@@ reset_head_refs
        strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
        prefix_len = msg.len;
  
 -      if (!get_oid("ORIG_HEAD", &oid_old_orig))
 -              old_orig = &oid_old_orig;
 -      if (!get_oid("HEAD", &oid_orig)) {
 -              orig = &oid_orig;
 -              if (!reflog_orig_head) {
 -                      strbuf_addstr(&msg, "updating ORIG_HEAD");
 -                      reflog_orig_head = msg.buf;
 -              }
 -              update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
 -                         UPDATE_REFS_MSG_ON_ERR);
 -      } else if (old_orig)
 -              delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
 +      if (update_orig_head) {
 +              if (!get_oid("ORIG_HEAD", &oid_old_orig))
 +                      old_orig = &oid_old_orig;
 +              if (!get_oid("HEAD", &oid_orig)) {
 +                      orig = &oid_orig;
 +                      if (!reflog_orig_head) {
 +                              strbuf_addstr(&msg, "updating ORIG_HEAD");
 +                              reflog_orig_head = msg.buf;
 +                      }
 +                      update_ref(reflog_orig_head, "ORIG_HEAD", orig,
 +                                 old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
 +              } else if (old_orig)
 +                      delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
 +      }
 +
        if (!reflog_head) {
                strbuf_setlen(&msg, prefix_len);
                strbuf_addstr(&msg, "updating HEAD");
                                 detach_head ? REF_NO_DEREF : 0,
                                 UPDATE_REFS_MSG_ON_ERR);
        else {
 -              ret = update_ref(reflog_orig_head, switch_to_branch, oid,
 +              ret = update_ref(reflog_head, switch_to_branch, oid,
                                 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
                if (!ret)
                        ret = create_symref("HEAD", switch_to_branch,
@@@ -1043,9 -625,9 +1043,9 @@@ static int run_am(struct rebase_option
        argv_array_push(&am.args, "--rebasing");
        argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
        argv_array_push(&am.args, "--patch-format=mboxrd");
 -      if (opts->allow_rerere_autoupdate > 0)
 +      if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
                argv_array_push(&am.args, "--rerere-autoupdate");
 -      else if (opts->allow_rerere_autoupdate == 0)
 +      else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
                argv_array_push(&am.args, "--no-rerere-autoupdate");
        if (opts->gpg_sign_opt)
                argv_array_push(&am.args, opts->gpg_sign_opt);
        }
  
        if (is_directory(opts->state_dir))
 -              write_basic_state(opts);
 +              rebase_write_basic_state(opts);
  
        return status;
  }
  
 -static int run_specific_rebase(struct rebase_options *opts)
 +static int run_specific_rebase(struct rebase_options *opts, enum action action)
  {
        const char *argv[] = { NULL, NULL };
        struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
  
        if (opts->type == REBASE_INTERACTIVE) {
                /* Run builtin interactive rebase */
 -              struct child_process child = CHILD_PROCESS_INIT;
 -
 -              argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s",
 -                               resolvemsg);
 +              setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
 -                      argv_array_push(&child.env_array,
 -                                      "GIT_SEQUENCE_EDITOR=:");
 +                      setenv("GIT_SEQUENCE_EDITOR", ":", 1);
                        opts->autosquash = 0;
                }
 +              if (opts->gpg_sign_opt) {
 +                      /* remove the leading "-S" */
 +                      char *tmp = xstrdup(opts->gpg_sign_opt + 2);
 +                      free(opts->gpg_sign_opt);
 +                      opts->gpg_sign_opt = tmp;
 +              }
  
 -              child.git_cmd = 1;
 -              argv_array_push(&child.args, "rebase--interactive");
 -
 -              if (opts->action)
 -                      argv_array_pushf(&child.args, "--%s", opts->action);
 -              if (opts->keep_empty)
 -                      argv_array_push(&child.args, "--keep-empty");
 -              if (opts->rebase_merges)
 -                      argv_array_push(&child.args, "--rebase-merges");
 -              if (opts->rebase_cousins)
 -                      argv_array_push(&child.args, "--rebase-cousins");
 -              if (opts->autosquash)
 -                      argv_array_push(&child.args, "--autosquash");
 -              if (opts->flags & REBASE_VERBOSE)
 -                      argv_array_push(&child.args, "--verbose");
 -              if (opts->flags & REBASE_FORCE)
 -                      argv_array_push(&child.args, "--no-ff");
 -              if (opts->restrict_revision)
 -                      argv_array_pushf(&child.args,
 -                                       "--restrict-revision=^%s",
 -                                       oid_to_hex(&opts->restrict_revision->object.oid));
 -              if (opts->upstream)
 -                      argv_array_pushf(&child.args, "--upstream=%s",
 -                                       oid_to_hex(&opts->upstream->object.oid));
 -              if (opts->onto)
 -                      argv_array_pushf(&child.args, "--onto=%s",
 -                                       oid_to_hex(&opts->onto->object.oid));
 -              if (opts->squash_onto)
 -                      argv_array_pushf(&child.args, "--squash-onto=%s",
 -                                       oid_to_hex(opts->squash_onto));
 -              if (opts->onto_name)
 -                      argv_array_pushf(&child.args, "--onto-name=%s",
 -                                       opts->onto_name);
 -              argv_array_pushf(&child.args, "--head-name=%s",
 -                               opts->head_name ?
 -                               opts->head_name : "detached HEAD");
 -              if (opts->strategy)
 -                      argv_array_pushf(&child.args, "--strategy=%s",
 -                                       opts->strategy);
 -              if (opts->strategy_opts)
 -                      argv_array_pushf(&child.args, "--strategy-opts=%s",
 -                                       opts->strategy_opts);
 -              if (opts->switch_to)
 -                      argv_array_pushf(&child.args, "--switch-to=%s",
 -                                       opts->switch_to);
 -              if (opts->cmd)
 -                      argv_array_pushf(&child.args, "--cmd=%s", opts->cmd);
 -              if (opts->allow_empty_message)
 -                      argv_array_push(&child.args, "--allow-empty-message");
 -              if (opts->allow_rerere_autoupdate > 0)
 -                      argv_array_push(&child.args, "--rerere-autoupdate");
 -              else if (opts->allow_rerere_autoupdate == 0)
 -                      argv_array_push(&child.args, "--no-rerere-autoupdate");
 -              if (opts->gpg_sign_opt)
 -                      argv_array_push(&child.args, opts->gpg_sign_opt);
 -              if (opts->signoff)
 -                      argv_array_push(&child.args, "--signoff");
 -              if (opts->reschedule_failed_exec)
 -                      argv_array_push(&child.args, "--reschedule-failed-exec");
 -
 -              status = run_command(&child);
 +              status = run_rebase_interactive(opts, action);
                goto finished_rebase;
        }
  
        add_var(&script_snippet, "action", opts->action ? opts->action : "");
        add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
        add_var(&script_snippet, "allow_rerere_autoupdate",
 -              opts->allow_rerere_autoupdate < 0 ? "" :
                opts->allow_rerere_autoupdate ?
 -              "--rerere-autoupdate" : "--no-rerere-autoupdate");
 +                      opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
 +                      "--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
        add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
        add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
        add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
        }
  
        switch (opts->type) {
 -      case REBASE_AM:
 -              backend = "git-rebase--am";
 -              backend_func = "git_rebase__am";
 -              break;
        case REBASE_PRESERVE_MERGES:
                backend = "git-rebase--preserve-merges";
                backend_func = "git_rebase__preserve_merges";
        }
  
        strbuf_addf(&script_snippet,
 -                  ". git-sh-setup && . git-rebase--common &&"
 -                  " . %s && %s", backend, backend_func);
 +                  ". git-sh-setup && . %s && %s", backend, backend_func);
        argv[0] = script_snippet.buf;
  
        status = run_command_v_opt(argv, RUN_USING_SHELL);
@@@ -1198,7 -843,7 +1198,7 @@@ static int rebase_config(const char *va
                if (git_config_bool(var, value))
                        opts->flags |= REBASE_DIFFSTAT;
                else
 -                      opts->flags &= !REBASE_DIFFSTAT;
 +                      opts->flags &= ~REBASE_DIFFSTAT;
                return 0;
        }
  
                return 0;
        }
  
 +      if (!strcmp(var, "rebase.usebuiltin")) {
 +              opts->use_legacy_rebase = !git_config_bool(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, data);
  }
  
@@@ -1363,7 -1003,14 +1363,7 @@@ static int check_exec_cmd(const char *c
  
  int cmd_rebase(int argc, const char **argv, const char *prefix)
  {
 -      struct rebase_options options = {
 -              .type = REBASE_UNSPECIFIED,
 -              .flags = REBASE_NO_QUIET,
 -              .git_am_opts = ARGV_ARRAY_INIT,
 -              .allow_rerere_autoupdate  = -1,
 -              .allow_empty_message = 1,
 -              .git_format_patch_opt = STRBUF_INIT,
 -      };
 +      struct rebase_options options = REBASE_OPTIONS_INIT;
        const char *branch_name;
        int ret, flags, total_argc, in_progress = 0;
        int ok_to_skip_pre_rebase = 0;
        struct strbuf revisions = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct object_id merge_base;
 -      enum {
 -              NO_ACTION,
 -              ACTION_CONTINUE,
 -              ACTION_SKIP,
 -              ACTION_ABORT,
 -              ACTION_QUIT,
 -              ACTION_EDIT_TODO,
 -              ACTION_SHOW_CURRENT_PATCH,
 -      } action = NO_ACTION;
 +      enum action action = ACTION_NONE;
        const char *gpg_sign = NULL;
        struct string_list exec = STRING_LIST_INIT_NODUP;
        const char *rebase_merges = NULL;
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        parse_opt_interactive },
                OPT_SET_INT('p', "preserve-merges", &options.type,
 -                          N_("try to recreate merges instead of ignoring "
 -                             "them"), REBASE_PRESERVE_MERGES),
 -              OPT_BOOL(0, "rerere-autoupdate",
 -                       &options.allow_rerere_autoupdate,
 -                       N_("allow rerere to update index with resolved "
 -                          "conflict")),
 +                          N_("(DEPRECATED) try to recreate merges instead of "
 +                             "ignoring them"), REBASE_PRESERVE_MERGES),
 +              OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
                OPT_BOOL('k', "keep-empty", &options.keep_empty,
                         N_("preserve empty commits during rebase")),
                OPT_BOOL(0, "autosquash", &options.autosquash,
        };
        int i;
  
 -      /*
 -       * NEEDSWORK: Once the builtin rebase has been tested enough
 -       * and git-legacy-rebase.sh is retired to contrib/, this preamble
 -       * can be removed.
 -       */
 -
 -      if (!use_builtin_rebase()) {
 -              const char *path = mkpath("%s/git-legacy-rebase",
 -                                        git_exec_path());
 -
 -              if (sane_execvp(path, (char **)argv) < 0)
 -                      die_errno(_("could not exec %s"), path);
 -              else
 -                      BUG("sane_execvp() returned???");
 -      }
 -
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
        trace_repo_setup(prefix);
        setup_work_tree();
  
 +      options.allow_empty_message = 1;
        git_config(rebase_config, &options);
  
 +      if (options.use_legacy_rebase ||
 +          !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1))
 +              warning(_("the rebase.useBuiltin support has been removed!\n"
 +                        "See its entry in 'git help config' for details."));
 +
        strbuf_reset(&buf);
        strbuf_addf(&buf, "%s/applying", apply_dir());
        if(file_exists(buf.buf))
                             builtin_rebase_options,
                             builtin_rebase_usage, 0);
  
 -      if (action != NO_ACTION && total_argc != 2) {
 +      if (action != ACTION_NONE && total_argc != 2) {
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
        }
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
  
 -      if (action != NO_ACTION && !in_progress)
 +      if (options.type == REBASE_PRESERVE_MERGES)
 +              warning(_("git rebase --preserve-merges is deprecated. "
 +                        "Use --rebase-merges instead."));
 +
 +      if (action != ACTION_NONE && !in_progress)
                die(_("No rebase in progress?"));
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
  
                die(_("The --edit-todo action can only be used during "
                      "interactive rebase."));
  
 +      if (trace2_is_enabled()) {
 +              if (is_interactive(&options))
 +                      trace2_cmd_mode("interactive");
 +              else if (exec.nr)
 +                      trace2_cmd_mode("interactive-exec");
 +              else
 +                      trace2_cmd_mode(action_names[action]);
 +      }
 +
        switch (action) {
        case ACTION_CONTINUE: {
                struct object_id head;
                options.action = "show-current-patch";
                options.dont_finish_rebase = 1;
                goto run_rebase;
 -      case NO_ACTION:
 +      case ACTION_NONE:
                break;
        default:
                BUG("action: %d", action);
                                branch_name = options.head_name;
  
                } else {
 -                      free(options.head_name);
 -                      options.head_name = NULL;
 +                      FREE_AND_NULL(options.head_name);
                        branch_name = "HEAD";
                }
                if (get_oid("HEAD", &options.orig_head))
        strbuf_addf(&msg, "%s: checkout %s",
                    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
        if (reset_head(&options.onto->object.oid, "checkout", NULL,
 -                     RESET_HEAD_DETACH | RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
 +                     RESET_HEAD_DETACH | RESET_ORIG_HEAD | 
 +                     RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
                       NULL, msg.buf))
                die(_("Could not detach HEAD"));
        strbuf_release(&msg);
         * we just fast-forwarded.
         */
        strbuf_reset(&msg);
 -      if (!oidcmp(&merge_base, &options.orig_head)) {
 +      if (oideq(&merge_base, &options.orig_head)) {
                printf(_("Fast-forwarded %s to %s.\n"),
                        branch_name, options.onto_name);
                strbuf_addf(&msg, "rebase finished: %s onto %s",
                        options.head_name ? options.head_name : "detached HEAD",
                        oid_to_hex(&options.onto->object.oid));
 -              reset_head(NULL, "Fast-forwarded", options.head_name, 0,
 -                         "HEAD", msg.buf);
 +              reset_head(NULL, "Fast-forwarded", options.head_name,
 +                         RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
                strbuf_release(&msg);
                ret = !!finish_rebase(&options);
                goto cleanup;
        options.revisions = revisions.buf;
  
  run_rebase:
 -      ret = !!run_specific_rebase(&options);
 +      ret = !!run_specific_rebase(&options, action);
  
  cleanup:
        strbuf_release(&revisions);
diff --combined builtin/receive-pack.c
index 77b7122456dbc893809038217bdd6f0972fc6745,92cd1f508c197986c9db5ab4c63368bab0f7b002..610eadf5f092a651fe3d04b07528f40f6adddafe
@@@ -694,8 -694,6 +694,8 @@@ static int run_and_feed_hook(const cha
        proc.argv = argv;
        proc.in = -1;
        proc.stdout_to_stderr = 1;
 +      proc.trace2_hook_name = hook_name;
 +
        if (feed_state->push_options) {
                int i;
                for (i = 0; i < feed_state->push_options->nr; i++)
@@@ -809,7 -807,6 +809,7 @@@ static int run_update_hook(struct comma
        proc.stdout_to_stderr = 1;
        proc.err = use_sideband ? -1 : 0;
        proc.argv = argv;
 +      proc.trace2_hook_name = "update";
  
        code = start_command(&proc);
        if (code)
@@@ -1193,7 -1190,6 +1193,7 @@@ static void run_update_post_hook(struc
        proc.no_stdin = 1;
        proc.stdout_to_stderr = 1;
        proc.err = use_sideband ? -1 : 0;
 +      proc.trace2_hook_name = "post-update";
  
        if (!start_command(&proc)) {
                if (use_sideband)
        }
  }
  
 -static void check_aliased_update(struct command *cmd, struct string_list *list)
 +static void check_aliased_update_internal(struct command *cmd,
 +                                        struct string_list *list,
 +                                        const char *dst_name, int flag)
  {
 -      struct strbuf buf = STRBUF_INIT;
 -      const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
 -      int flag;
 -
 -      strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
 -      dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
 -      strbuf_release(&buf);
  
        if (!(flag & REF_ISSYMREF))
                return;
                "inconsistent aliased update";
  }
  
 +static void check_aliased_update(struct command *cmd, struct string_list *list)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      const char *dst_name;
 +      int flag;
 +
 +      strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
 +      dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
 +      check_aliased_update_internal(cmd, list, dst_name, flag);
 +      strbuf_release(&buf);
 +}
 +
  static void check_aliased_updates(struct command *commands)
  {
        struct command *cmd;
@@@ -1809,7 -1798,8 +1809,7 @@@ static const char *unpack_with_sideband
        return ret;
  }
  
 -static void prepare_shallow_update(struct command *commands,
 -                                 struct shallow_info *si)
 +static void prepare_shallow_update(struct shallow_info *si)
  {
        int i, j, k, bitmap_size = DIV_ROUND_UP(si->ref->nr, 32);
  
@@@ -1875,7 -1865,7 +1875,7 @@@ static void update_shallow_info(struct 
        si->ref = ref;
  
        if (shallow_update) {
 -              prepare_shallow_update(commands, si);
 +              prepare_shallow_update(si);
                return;
        }
  
@@@ -2042,7 -2032,7 +2042,7 @@@ int cmd_receive_pack(int argc, const ch
                        proc.git_cmd = 1;
                        proc.argv = argv_gc_auto;
  
-                       close_all_packs(the_repository->objects);
+                       close_object_store(the_repository->objects);
                        if (!start_command(&proc)) {
                                if (use_sideband)
                                        copy_to_sideband(proc.err, -1, NULL);
diff --combined builtin/repack.c
index caca11392713eb92816d1e503f286bfe0d9be78a,4de8b6600c4523f275121244522bc8654173dcbd..f834b5551b1ffe003b943c18cd35397e9196e730
@@@ -14,7 -14,7 +14,7 @@@
  
  static int delta_base_offset = 1;
  static int pack_kept_objects = -1;
 -static int write_bitmaps;
 +static int write_bitmaps = -1;
  static int use_delta_islands;
  static char *packdir, *packtmp;
  
@@@ -343,9 -343,6 +343,9 @@@ int cmd_repack(int argc, const char **a
            (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
                die(_("--keep-unreachable and -A are incompatible"));
  
 +      if (write_bitmaps < 0)
 +              write_bitmaps = (pack_everything & ALL_INTO_ONE) &&
 +                               is_bare_repository();
        if (pack_kept_objects < 0)
                pack_kept_objects = write_bitmaps;
  
        if (!names.nr && !po_args.quiet)
                printf_ln(_("Nothing new to pack."));
  
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
  
        /*
         * Ok we have prepared all new packfiles.
diff --combined commit-graph.c
index 89c4b982dd905adb20ab71b44e7c6c25e89b5b23,76d189de45416165fea039e7be67375b30e7a230..1752341098c328da1e9f6bce230e6b04730ffa11
@@@ -267,10 -267,8 +267,10 @@@ struct commit_graph *parse_commit_graph
                last_chunk_offset = chunk_offset;
        }
  
 -      if (verify_commit_graph_lite(graph))
 +      if (verify_commit_graph_lite(graph)) {
 +              free(graph);
                return NULL;
 +      }
  
        return graph;
  }
@@@ -361,10 -359,10 +361,10 @@@ int generation_numbers_enabled(struct r
        return !!first_generation;
  }
  
- void close_commit_graph(struct repository *r)
+ void close_commit_graph(struct raw_object_store *o)
  {
-       free_commit_graph(r->objects->commit_graph);
-       r->objects->commit_graph = NULL;
+       free_commit_graph(o->commit_graph);
+       o->commit_graph = NULL;
  }
  
  static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@@ -399,11 -397,6 +399,11 @@@ static void fill_commit_graph_info(stru
        item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
  }
  
 +static inline void set_commit_tree(struct commit *c, struct tree *t)
 +{
 +      c->maybe_tree = t;
 +}
 +
  static int fill_commit_in_graph(struct repository *r,
                                struct commit *item,
                                struct commit_graph *g, uint32_t pos)
        item->object.parsed = 1;
        item->graph_pos = pos;
  
 -      item->maybe_tree = NULL;
 +      set_commit_tree(item, NULL);
  
        date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
        date_low = get_be32(commit_data + g->hash_len + 12);
@@@ -503,7 -496,7 +503,7 @@@ static struct tree *load_tree_for_commi
                                           GRAPH_DATA_WIDTH * (c->graph_pos);
  
        hashcpy(oid.hash, commit_data);
 -      c->maybe_tree = lookup_tree(r, &oid);
 +      set_commit_tree(c, lookup_tree(r, &oid));
  
        return c->maybe_tree;
  }
@@@ -1093,7 -1086,7 +1093,7 @@@ static int write_commit_graph_file(stru
        stop_progress(&ctx->progress);
        strbuf_release(&progress_title);
  
-       close_commit_graph(ctx->r);
+       close_commit_graph(ctx->r->objects);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
  
diff --combined packfile.c
index d786ec731202e5d623a7558bec2168194db11514,017046fcf956fd3070ae848ed45cdc84ccd09b38..ed611875045ebf5d682d33d27a124cb04958673a
@@@ -16,6 -16,7 +16,7 @@@
  #include "tree.h"
  #include "object-store.h"
  #include "midx.h"
+ #include "commit-graph.h"
  
  char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@@ -235,7 -236,7 +236,7 @@@ struct packed_git *parse_pack_index(uns
        struct packed_git *p = alloc_packed_git(alloc);
  
        memcpy(p->pack_name, path, alloc); /* includes NUL */
 -      hashcpy(p->sha1, sha1);
 +      hashcpy(p->hash, sha1);
        if (check_packed_git_idx(idx_path, p)) {
                free(p);
                return NULL;
@@@ -309,7 -310,7 +310,7 @@@ void close_pack_windows(struct packed_g
        }
  }
  
 -static int close_pack_fd(struct packed_git *p)
 +int close_pack_fd(struct packed_git *p)
  {
        if (p->pack_fd < 0)
                return 0;
@@@ -336,7 -337,7 +337,7 @@@ void close_pack(struct packed_git *p
        close_pack_index(p);
  }
  
- void close_all_packs(struct raw_object_store *o)
+ void close_object_store(struct raw_object_store *o)
  {
        struct packed_git *p;
  
                close_midx(o->multi_pack_index);
                o->multi_pack_index = NULL;
        }
+       close_commit_graph(o);
  }
  
  /*
@@@ -466,16 -469,6 +469,16 @@@ static unsigned int get_max_fd_limit(vo
  #endif
  }
  
 +const char *pack_basename(struct packed_git *p)
 +{
 +      const char *ret = strrchr(p->pack_name, '/');
 +      if (ret)
 +              ret = ret + 1; /* skip past slash */
 +      else
 +              ret = p->pack_name; /* we only have a base */
 +      return ret;
 +}
 +
  /*
   * Do not call this directly as this leaks p->pack_fd on error return;
   * call open_packed_git() instead.
@@@ -492,7 -485,7 +495,7 @@@ static int open_packed_git_1(struct pac
  
        if (!p->index_data) {
                struct multi_pack_index *m;
 -              const char *pack_name = strrchr(p->pack_name, '/');
 +              const char *pack_name = pack_basename(p);
  
                for (m = the_repository->objects->multi_pack_index;
                     m; m = m->next) {
@@@ -640,7 -633,7 +643,7 @@@ unsigned char *use_pack(struct packed_g
                        while (packed_git_limit < pack_mapped
                                && unuse_one_window(p))
                                ; /* nothing */
 -                      win->base = xmmap(NULL, win->len,
 +                      win->base = xmmap_gently(NULL, win->len,
                                PROT_READ, MAP_PRIVATE,
                                p->pack_fd, win->offset);
                        if (win->base == MAP_FAILED)
@@@ -732,8 -725,8 +735,8 @@@ struct packed_git *add_packed_git(cons
        p->pack_local = local;
        p->mtime = st.st_mtime;
        if (path_len < the_hash_algo->hexsz ||
 -          get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->sha1))
 -              hashclr(p->sha1);
 +          get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash))
 +              hashclr(p->hash);
        return p;
  }
  
@@@ -903,25 -896,25 +906,25 @@@ static void prepare_packed_git(struct r
   * all unreachable objects about to be pruned, in which case they're not really
   * interesting as a measure of repo size in the first place.
   */
 -unsigned long approximate_object_count(void)
 +unsigned long repo_approximate_object_count(struct repository *r)
  {
 -      if (!the_repository->objects->approximate_object_count_valid) {
 +      if (!r->objects->approximate_object_count_valid) {
                unsigned long count;
                struct multi_pack_index *m;
                struct packed_git *p;
  
 -              prepare_packed_git(the_repository);
 +              prepare_packed_git(r);
                count = 0;
 -              for (m = get_multi_pack_index(the_repository); m; m = m->next)
 +              for (m = get_multi_pack_index(r); m; m = m->next)
                        count += m->num_objects;
 -              for (p = the_repository->objects->packed_git; p; p = p->next) {
 +              for (p = r->objects->packed_git; p; p = p->next) {
                        if (open_pack_index(p))
                                continue;
                        count += p->num_objects;
                }
 -              the_repository->objects->approximate_object_count = count;
 +              r->objects->approximate_object_count = count;
        }
 -      return the_repository->objects->approximate_object_count;
 +      return r->objects->approximate_object_count;
  }
  
  static void *get_next_packed_git(const void *p)
@@@ -994,6 -987,8 +997,6 @@@ static void prepare_packed_git(struct r
        }
        rearrange_packed_git(r);
  
 -      r->objects->all_packs = NULL;
 -
        prepare_packed_git_mru(r);
        r->objects->packed_git_initialized = 1;
  }
@@@ -1024,16 -1019,26 +1027,16 @@@ struct multi_pack_index *get_multi_pack
  
  struct packed_git *get_all_packs(struct repository *r)
  {
 -      prepare_packed_git(r);
 -
 -      if (!r->objects->all_packs) {
 -              struct packed_git *p = r->objects->packed_git;
 -              struct multi_pack_index *m;
 -
 -              for (m = r->objects->multi_pack_index; m; m = m->next) {
 -                      uint32_t i;
 -                      for (i = 0; i < m->num_packs; i++) {
 -                              if (!prepare_midx_pack(m, i)) {
 -                                      m->packs[i]->next = p;
 -                                      p = m->packs[i];
 -                              }
 -                      }
 -              }
 +      struct multi_pack_index *m;
  
 -              r->objects->all_packs = p;
 +      prepare_packed_git(r);
 +      for (m = r->objects->multi_pack_index; m; m = m->next) {
 +              uint32_t i;
 +              for (i = 0; i < m->num_packs; i++)
 +                      prepare_midx_pack(r, m, i);
        }
  
 -      return r->objects->all_packs;
 +      return r->objects->packed_git;
  }
  
  struct list_head *get_packed_git_mru(struct repository *r)
@@@ -1986,13 -1991,13 +1989,13 @@@ int find_pack_entry(struct repository *
                return 0;
  
        for (m = r->objects->multi_pack_index; m; m = m->next) {
 -              if (fill_midx_entry(oid, e, m))
 +              if (fill_midx_entry(r, oid, e, m))
                        return 1;
        }
  
        list_for_each(pos, &r->objects->packed_git_mru) {
                struct packed_git *p = list_entry(pos, struct packed_git, mru);
 -              if (fill_pack_entry(oid, e, p)) {
 +              if (!p->multi_pack_index && fill_pack_entry(oid, e, p)) {
                        list_move(&p->mru, &r->objects->packed_git_mru);
                        return 1;
                }
@@@ -2021,10 -2026,8 +2024,10 @@@ int for_each_object_in_pack(struct pack
        uint32_t i;
        int r = 0;
  
 -      if (flags & FOR_EACH_OBJECT_PACK_ORDER)
 -              load_pack_revindex(p);
 +      if (flags & FOR_EACH_OBJECT_PACK_ORDER) {
 +              if (load_pack_revindex(p))
 +                      return -1;
 +      }
  
        for (i = 0; i < p->num_objects; i++) {
                uint32_t pos;
diff --combined packfile.h
index b678d35c0b6df11623f2d29f7b7ea7dfe0e1bea1,e95e389eb804e55cf1357cdefddebf9f3f653965..81e868d55a9b1f1aeaafa4925a72ae5c53af86e9
@@@ -15,29 -15,23 +15,29 @@@ struct object_info
   *
   * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
   */
 -extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
 +char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
  
  /*
   * Return the name of the (local) packfile with the specified sha1 in
   * its name.  The return value is a pointer to memory that is
   * overwritten each time this function is called.
   */
 -extern char *sha1_pack_name(const unsigned char *sha1);
 +char *sha1_pack_name(const unsigned char *sha1);
  
  /*
   * Return the name of the (local) pack index file with the specified
   * sha1 in its name.  The return value is a pointer to memory that is
   * overwritten each time this function is called.
   */
 -extern char *sha1_pack_index_name(const unsigned char *sha1);
 +char *sha1_pack_index_name(const unsigned char *sha1);
  
 -extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 +/*
 + * Return the basename of the packfile, omitting any containing directory
 + * (e.g., "pack-1234abcd[...].pack").
 + */
 +const char *pack_basename(struct packed_git *p);
 +
 +struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
  typedef void each_file_in_pack_dir_fn(const char *full_path, size_t full_path_len,
                                      const char *file_pach, void *data);
@@@ -51,8 -45,8 +51,8 @@@ void for_each_file_in_pack_dir(const ch
  #define PACKDIR_FILE_GARBAGE 4
  extern void (*report_garbage)(unsigned seen_bits, const char *path);
  
 -extern void reprepare_packed_git(struct repository *r);
 -extern void install_packed_git(struct repository *r, struct packed_git *pack);
 +void reprepare_packed_git(struct repository *r);
 +void install_packed_git(struct repository *r, struct packed_git *pack);
  
  struct packed_git *get_packed_git(struct repository *r);
  struct list_head *get_packed_git_mru(struct repository *r);
@@@ -63,37 -57,34 +63,37 @@@ struct packed_git *get_all_packs(struc
   * Give a rough count of objects in the repository. This sacrifices accuracy
   * for speed.
   */
 -unsigned long approximate_object_count(void);
 +unsigned long repo_approximate_object_count(struct repository *r);
 +#define approximate_object_count() repo_approximate_object_count(the_repository)
  
 -extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 -                                       struct packed_git *packs);
 +struct packed_git *find_sha1_pack(const unsigned char *sha1,
 +                                struct packed_git *packs);
  
 -extern void pack_report(void);
 +void pack_report(void);
  
  /*
   * mmap the index file for the specified packfile (if it is not
   * already mmapped).  Return 0 on success.
   */
 -extern int open_pack_index(struct packed_git *);
 +int open_pack_index(struct packed_git *);
  
  /*
   * munmap the index file for the specified packfile (if it is
   * currently mmapped).
   */
 -extern void close_pack_index(struct packed_git *);
 +void close_pack_index(struct packed_git *);
 +
 +int close_pack_fd(struct packed_git *p);
  
 -extern uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
 +uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
  
 -extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 -extern void close_pack_windows(struct packed_git *);
 -extern void close_pack(struct packed_git *);
 -extern void close_object_store(struct raw_object_store *o);
 -extern void unuse_pack(struct pack_window **);
 -extern void clear_delta_base_cache(void);
 -extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
 +unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 +void close_pack_windows(struct packed_git *);
 +void close_pack(struct packed_git *);
- void close_all_packs(struct raw_object_store *o);
++void close_object_store(struct raw_object_store *o);
 +void unuse_pack(struct pack_window **);
 +void clear_delta_base_cache(void);
 +struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
  
  /*
   * Make sure that a pointer access into an mmap'd index file is within bounds,
   * (like the 64-bit extended offset table), as we compare the size to the
   * fixed-length parts when we open the file.
   */
 -extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
 +void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
  
  /*
   * Perform binary search on a pack-index for a given oid. Packfile is expected to
@@@ -119,59 -110,59 +119,59 @@@ int bsearch_pack(const struct object_i
   * at the SHA-1 within the mmapped index.  Return NULL if there is an
   * error.
   */
 -extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 +const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
  /*
   * Like nth_packed_object_sha1, but write the data into the object specified by
   * the the first argument.  Returns the first argument on success, and NULL on
   * error.
   */
 -extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
 +const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
  
  /*
   * Return the offset of the nth object within the specified packfile.
   * The index must already be opened.
   */
 -extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
 +off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
  
  /*
   * If the object named sha1 is present in the specified packfile,
   * return its offset within the packfile; otherwise, return 0.
   */
 -extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
 +off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
  
 -extern int is_pack_valid(struct packed_git *);
 -extern void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, unsigned long *);
 -extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 -extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 -extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
 +int is_pack_valid(struct packed_git *);
 +void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, unsigned long *);
 +unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 +unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 +int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
  
 -extern void release_pack_memory(size_t);
 +void release_pack_memory(size_t);
  
  /* global flag to enable extra checks when accessing packed objects */
  extern int do_check_packed_object_crc;
  
 -extern int packed_object_info(struct repository *r,
 -                            struct packed_git *pack,
 -                            off_t offset, struct object_info *);
 +int packed_object_info(struct repository *r,
 +                     struct packed_git *pack,
 +                     off_t offset, struct object_info *);
  
 -extern void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
 -extern const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1);
 +void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
 +const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1);
  
  /*
   * Iff a pack file in the given repository contains the object named by sha1,
   * return true and store its location to e.
   */
 -extern int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e);
 +int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e);
  
 -extern int has_object_pack(const struct object_id *oid);
 +int has_object_pack(const struct object_id *oid);
  
 -extern int has_pack_index(const unsigned char *sha1);
 +int has_pack_index(const unsigned char *sha1);
  
  /*
   * Return 1 if an object in a promisor packfile is or refers to the given
   * object, 0 otherwise.
   */
 -extern int is_promisor_object(const struct object_id *oid);
 +int is_promisor_object(const struct object_id *oid);
  
  /*
   * Expose a function for fuzz testing.
   * have a convenient entry-point for fuzz testing. For real uses, you should
   * probably use open_pack_index() or parse_pack_index() instead.
   */
 -extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
 -                  size_t idx_size, struct packed_git *p);
 +int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
 +           size_t idx_size, struct packed_git *p);
  
  #endif
diff --combined upload-pack.c
index 4d2129e7fc134cdbc67e08ce9fb4f805023059b5,b51bed21e4059b63d304fdd34f9777bb6810adf7..b2a9f368ecd6ab159cae99deac0b9a9b5ee08f6a
@@@ -592,8 -592,7 +592,8 @@@ error
        return 1;
  }
  
 -static void check_non_tip(struct object_array *want_obj)
 +static void check_non_tip(struct object_array *want_obj,
 +                        struct packet_writer *writer)
  {
        int i;
  
@@@ -612,13 -611,9 +612,13 @@@ error
        /* Pick one of them (we know there at least is one) */
        for (i = 0; i < want_obj->nr; i++) {
                struct object *o = want_obj->objects[i].item;
 -              if (!is_our_ref(o))
 +              if (!is_our_ref(o)) {
 +                      packet_writer_error(writer,
 +                                          "upload-pack: not our ref %s",
 +                                          oid_to_hex(&o->oid));
                        die("git upload-pack: not our ref %s",
                            oid_to_hex(&o->oid));
 +              }
        }
  }
  
@@@ -722,7 -717,7 +722,7 @@@ static void deepen_by_rev_list(struct p
  {
        struct commit_list *result;
  
-       close_commit_graph(the_repository);
+       close_commit_graph(the_repository->objects);
        result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
        send_shallow(writer, result);
        free_commit_list(result);
@@@ -839,7 -834,7 +839,7 @@@ static int process_deepen_not(const cha
        if (skip_prefix(line, "deepen-not ", &arg)) {
                char *ref = NULL;
                struct object_id oid;
 -              if (expand_ref(arg, strlen(arg), &oid, &ref) != 1)
 +              if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1)
                        die("git upload-pack: ambiguous deepen-not: %s", line);
                string_list_append(deepen_not, ref);
                free(ref);
@@@ -941,7 -936,7 +941,7 @@@ static void receive_needs(struct packet
         * by another process that handled the initial request.
         */
        if (has_non_tip)
 -              check_non_tip(want_obj);
 +              check_non_tip(want_obj, &writer);
  
        if (!use_sideband && daemon_mode)
                no_progress = 1;
@@@ -1037,8 -1032,8 +1037,8 @@@ static int find_symref(const char *refn
        symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
        if (!symref_target || (flag & REF_ISSYMREF) == 0)
                die("'%s' is a symref but it is not?", refname);
 -      item = string_list_append(cb_data, refname);
 -      item->util = xstrdup(symref_target);
 +      item = string_list_append(cb_data, strip_namespace(refname));
 +      item->util = xstrdup(strip_namespace(symref_target));
        return 0;
  }
  
@@@ -1069,8 -1064,6 +1069,8 @@@ static int upload_pack_config(const cha
                allow_ref_in_want = git_config_bool(var, value);
        } else if (!strcmp("uploadpack.allowsidebandall", var)) {
                allow_sideband_all = git_config_bool(var, value);
 +      } else if (!strcmp("core.precomposeunicode", var)) {
 +              precomposed_unicode = git_config_bool(var, value);
        }
  
        if (current_config_scope() != CONFIG_SCOPE_REPO) {