Merge branch 'ds/close-object-store' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 29 Jul 2019 19:38:22 +0000 (12:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 29 Jul 2019 19:38:22 +0000 (12:38 -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
commit-graph: extract write_commit_graph_file()
commit-graph: extract copy_oids_to_commits()
commit-graph: extract count_distinct_commits()
commit-graph: extract fill_oids_from_all_packs()
commit-graph: extract fill_oids_from_commit_hex()
commit-graph: extract fill_oids_from_packs()
commit-graph: create write_commit_graph_context
commit-graph: remove Future Work section
commit-graph: collapse parameters into flags
commit-graph: return with errors during write
commit-graph: fix the_repository reference

15 files changed:
1  2 
builtin/am.c
builtin/clone.c
builtin/commit.c
builtin/fetch.c
builtin/gc.c
builtin/merge.c
builtin/rebase.c
builtin/receive-pack.c
builtin/repack.c
commit-graph.c
commit.c
packfile.c
packfile.h
t/t5318-commit-graph.sh
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 356bae5ed72d2e510da08d8089fbdda607ba0e9f,82ce682c8065a430ffa953a015efbc4b238b9443..3623f040d4086458b8e36e5e701d892c1d426078
@@@ -66,7 -66,6 +66,7 @@@ 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 recurse_submodules_cb(const struct option *opt,
                                 const char *arg, int unset)
@@@ -99,7 -98,10 +99,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"),
@@@ -657,8 -657,7 +657,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"));
@@@ -1138,9 -1136,6 +1138,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/commit.c
index 1c9e8e2228c7ce58375bc247c4ad5850a1bd7d2d,b001ef565ddcb179a3696dac21ec023751313a7d..192140111747cc0683c1ae19a785000110808ff3
@@@ -235,7 -235,7 +235,7 @@@ static int commit_index_files(void
   * and return the paths that match the given pattern in list.
   */
  static int list_paths(struct string_list *list, const char *with_tree,
 -                    const char *prefix, const struct pathspec *pattern)
 +                    const struct pathspec *pattern)
  {
        int i, ret;
        char *m;
                        item->util = item; /* better a valid pointer than a fake one */
        }
  
 -      ret = report_path_error(m, pattern, prefix);
 +      ret = report_path_error(m, pattern);
        free(m);
        return ret;
  }
@@@ -454,7 -454,7 +454,7 @@@ static const char *prepare_index(int ar
                        die(_("cannot do a partial commit during a cherry-pick."));
        }
  
 -      if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
 +      if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
                exit(1);
  
        discard_cache();
@@@ -609,8 -609,7 +609,8 @@@ static void determine_author_info(struc
                set_ident_var(&date, strbuf_detach(&date_buf, NULL));
        }
  
 -      strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
 +      strbuf_addstr(author_ident, fmt_ident(name, email, WANT_AUTHOR_IDENT, date,
 +                              IDENT_STRICT));
        assert_split_ident(&author, author_ident);
        export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
        export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
@@@ -668,7 -667,6 +668,7 @@@ static int prepare_to_commit(const cha
        const char *hook_arg2 = NULL;
        int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
        int old_display_comment_prefix;
 +      int merge_contains_scissors = 0;
  
        /* This checks and barfs if author is badly specified */
        determine_author_info(author_ident);
                        strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
        } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
 +              size_t merge_msg_start;
 +
                /*
                 * prepend SQUASH_MSG here if it exists and a
                 * "merge --squash" was originally performed
                        hook_arg1 = "squash";
                } else
                        hook_arg1 = "merge";
 +
 +              merge_msg_start = sb.len;
                if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
                        die_errno(_("could not read MERGE_MSG"));
 +
 +              if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
 +                  wt_status_locate_end(sb.buf + merge_msg_start,
 +                                       sb.len - merge_msg_start) <
 +                              sb.len - merge_msg_start)
 +                      merge_contains_scissors = 1;
        } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
                if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
                struct ident_split ci, ai;
  
                if (whence != FROM_COMMIT) {
 -                      if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 +                      if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
 +                              !merge_contains_scissors)
                                wt_status_add_cut_line(s->fp);
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                            whence == FROM_MERGE
                                _("Please enter the commit message for your changes."
                                  " Lines starting\nwith '%c' will be ignored, and an empty"
                                  " message aborts the commit.\n"), comment_line_char);
 -              else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
 -                       whence == FROM_COMMIT)
 -                      wt_status_add_cut_line(s->fp);
 -              else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
 +              else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
 +                      if (whence == FROM_COMMIT && !merge_contains_scissors)
 +                              wt_status_add_cut_line(s->fp);
 +              else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
                        status_printf(s, GIT_COLOR_NORMAL,
                                _("Please enter the commit message for your changes."
                                  " Lines starting\n"
@@@ -1051,10 -1038,6 +1051,10 @@@ static void handle_untracked_files_arg(
                s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
        else if (!strcmp(untracked_files_arg, "all"))
                s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 +      /*
 +       * Please update $__git_untracked_file_modes in
 +       * git-completion.bash when you add new options
 +       */
        else
                die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
  }
@@@ -1184,13 -1167,25 +1184,13 @@@ static int parse_and_validate_options(i
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
        if (argc == 0 && (also || (only && !amend && !allow_empty)))
                die(_("No paths with --include/--only does not make sense."));
 -      if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
 -              cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
 -                                          COMMIT_MSG_CLEANUP_SPACE;
 -      else if (!strcmp(cleanup_arg, "verbatim"))
 -              cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
 -      else if (!strcmp(cleanup_arg, "whitespace"))
 -              cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
 -      else if (!strcmp(cleanup_arg, "strip"))
 -              cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
 -      else if (!strcmp(cleanup_arg, "scissors"))
 -              cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
 -                                          COMMIT_MSG_CLEANUP_SPACE;
 -      else
 -              die(_("Invalid cleanup mode %s"), cleanup_arg);
 +      cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
  
        handle_untracked_files_arg(s);
  
        if (all && argc > 0)
 -              die(_("Paths with -a does not make sense."));
 +              die(_("paths '%s ...' with -a does not make sense"),
 +                  argv[0]);
  
        if (status_format != STATUS_FORMAT_NONE)
                dry_run = 1;
@@@ -1486,7 -1481,7 +1486,7 @@@ int cmd_commit(int argc, const char **a
                OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
 -              OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
 +              OPT_CLEANUP(&cleanup_arg),
                OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
                { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                die(_("could not read commit message: %s"), strerror(saved_errno));
        }
  
 -      if (verbose || /* Truncate the message just before the diff, if any. */
 -          cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 -              strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
 -      if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
 -              strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 +      cleanup_message(&sb, cleanup_mode, verbose);
  
        if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
                rollback_index_files();
                die("%s", err.buf);
        }
  
 -      unlink(git_path_cherry_pick_head(the_repository));
 -      unlink(git_path_revert_head(the_repository));
 +      sequencer_post_commit_cleanup(the_repository);
        unlink(git_path_merge_head(the_repository));
        unlink(git_path_merge_msg(the_repository));
        unlink(git_path_merge_mode(the_repository));
                      "new_index file. Check that disk is not full and quota is\n"
                      "not exceeded, and then \"git reset HEAD\" to recover."));
  
-       if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
-               write_commit_graph_reachable(get_object_directory(), 0, 0);
+       if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+           write_commit_graph_reachable(get_object_directory(), 0))
+               return 1;
  
        repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
diff --combined builtin/fetch.c
index 4ba63d5ac642844832a5c832cea93ddf99507764,3aec95608ffafdc8df65701c3cd3413e56692373..fc6c879bcf9ace22492166379ca7fad8186a8d7d
@@@ -1556,9 -1556,7 +1556,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;
@@@ -1672,7 -1670,7 +1672,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 8943bcc300d4a2ce6786857908e189c374373906,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();
        }
  
-       if (gc_write_commit_graph)
-               write_commit_graph_reachable(get_object_directory(), 0,
-                                            !quiet && !daemonized);
+       if (gc_write_commit_graph &&
+           write_commit_graph_reachable(get_object_directory(),
+                                        !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0))
+               return 1;
  
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
diff --combined builtin/merge.c
index 57c2a24f6d82fc7cedcc86979db6ff1a8fa6bc50,72d7a7c909080cd08c59a6c12c95a516cf08916d..29988e54c5be5ecb3afa2382ba73a965e1d2b511
@@@ -38,7 -38,6 +38,7 @@@
  #include "tag.h"
  #include "alias.h"
  #include "commit-reach.h"
 +#include "wt-status.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -58,7 -57,7 +58,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;
@@@ -99,9 -98,6 +99,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");
  
@@@ -253,7 -246,6 +253,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")),
@@@ -457,7 -449,7 +457,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);
                }
        }
@@@ -617,8 -609,6 +617,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")) {
@@@ -807,13 -797,8 +807,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 *);
@@@ -821,19 -806,11 +821,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);
@@@ -900,6 -877,7 +900,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))
@@@ -920,15 -898,7 +920,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);
@@@ -1328,30 -1298,15 +1328,30 @@@ int cmd_merge(int argc, const char **ar
        }
        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 */
diff --combined builtin/rebase.c
index 3d6219d0223e78218f400bd1f0504e62c2058070,ed30fcd63334c1984f834ecc39a808e4d7745ffa..2748fa6f2e91d2b788d5b54edfb15de29d8aa833
@@@ -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, NULL, 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);
@@@ -738,30 -325,20 +738,30 @@@ static int finish_rebase(struct rebase_
  {
        struct strbuf dir = STRBUF_INIT;
        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
 +      int ret = 0;
  
        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.
         */
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 -      strbuf_addstr(&dir, opts->state_dir);
 -      remove_dir_recursively(&dir, 0);
 -      strbuf_release(&dir);
 +      if (opts->type == REBASE_INTERACTIVE) {
 +              struct replay_opts replay = REPLAY_OPTS_INIT;
  
 -      return 0;
 +              replay.action = REPLAY_INTERACTIVE_REBASE;
 +              ret = sequencer_remove_state(&replay);
 +      } else {
 +              strbuf_addstr(&dir, opts->state_dir);
 +              if (remove_dir_recursively(&dir, 0))
 +                      ret = error(_("could not remove '%s'"),
 +                                  opts->state_dir);
 +              strbuf_release(&dir);
 +      }
 +
 +      return ret;
  }
  
  static struct commit *peel_committish(const char *name)
@@@ -792,7 -369,6 +792,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;
@@@ -879,21 -454,18 +879,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,
@@@ -1053,9 -625,9 +1053,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);
@@@ -1213,7 -843,7 +1213,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);
  }
  
@@@ -1378,7 -1003,14 +1378,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;
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
 +      int reschedule_failed_exec = -1;
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
                        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,
                OPT_BOOL(0, "root", &options.root,
                         N_("rebase all reachable commits up to the root(s)")),
                OPT_BOOL(0, "reschedule-failed-exec",
 -                       &options.reschedule_failed_exec,
 +                       &reschedule_failed_exec,
                         N_("automatically re-schedule any `exec` that fails")),
                OPT_END(),
        };
        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;
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head));
                remove_branch_state(the_repository);
 -              ret = finish_rebase(&options);
 +              ret = !!finish_rebase(&options);
                goto cleanup;
        }
        case ACTION_QUIT: {
 -              strbuf_reset(&buf);
 -              strbuf_addstr(&buf, options.state_dir);
 -              ret = !!remove_dir_recursively(&buf, 0);
 -              if (ret)
 -                      die(_("could not remove '%s'"), options.state_dir);
 +              if (options.type == REBASE_INTERACTIVE) {
 +                      struct replay_opts replay = REPLAY_OPTS_INIT;
 +
 +                      replay.action = REPLAY_INTERACTIVE_REBASE;
 +                      ret = !!sequencer_remove_state(&replay);
 +              } else {
 +                      strbuf_reset(&buf);
 +                      strbuf_addstr(&buf, options.state_dir);
 +                      ret = !!remove_dir_recursively(&buf, 0);
 +                      if (ret)
 +                              error(_("could not remove '%s'"),
 +                                     options.state_dir);
 +              }
                goto cleanup;
        }
        case ACTION_EDIT_TODO:
                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);
                break;
        }
  
 -      if (options.reschedule_failed_exec && !is_interactive(&options))
 -              die(_("%s requires an interactive rebase"), "--reschedule-failed-exec");
 +      if (reschedule_failed_exec > 0 && !is_interactive(&options))
 +              die(_("--reschedule-failed-exec requires "
 +                    "--exec or --interactive"));
 +      if (reschedule_failed_exec >= 0)
 +              options.reschedule_failed_exec = reschedule_failed_exec;
  
        if (options.git_am_opts.argc) {
                /* all am options except -q are compatible only with --am */
                                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(&buf);
        strbuf_release(&revisions);
        free(options.head_name);
        free(options.gpg_sign_opt);
diff --combined builtin/receive-pack.c
index 29f165d8bd3513a6f85667b070e8f157994364bb,92cd1f508c197986c9db5ab4c63368bab0f7b002..c5f5da940b0dee3fd2df55e11514ee9553926e30
@@@ -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;
@@@ -2043,7 -2032,7 +2043,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 7c5e54875fdacdf77235a077c9928f7d3bf0d001,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;
  }
@@@ -525,14 -518,38 +525,38 @@@ struct tree *get_commit_tree_in_graph(s
        return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
  }
  
+ struct packed_commit_list {
+       struct commit **list;
+       int nr;
+       int alloc;
+ };
+ struct packed_oid_list {
+       struct object_id *list;
+       int nr;
+       int alloc;
+ };
+ struct write_commit_graph_context {
+       struct repository *r;
+       const char *obj_dir;
+       char *graph_name;
+       struct packed_oid_list oids;
+       struct packed_commit_list commits;
+       int num_extra_edges;
+       unsigned long approx_nr_objects;
+       struct progress *progress;
+       int progress_done;
+       uint64_t progress_cnt;
+       unsigned append:1,
+                report_progress:1;
+ };
  static void write_graph_chunk_fanout(struct hashfile *f,
-                                    struct commit **commits,
-                                    int nr_commits,
-                                    struct progress *progress,
-                                    uint64_t *progress_cnt)
+                                    struct write_commit_graph_context *ctx)
  {
        int i, count = 0;
-       struct commit **list = commits;
+       struct commit **list = ctx->commits.list;
  
        /*
         * Write the first-level table (the list is sorted,
         * having to do eight extra binary search iterations).
         */
        for (i = 0; i < 256; i++) {
-               while (count < nr_commits) {
+               while (count < ctx->commits.nr) {
                        if ((*list)->object.oid.hash[0] != i)
                                break;
-                       display_progress(progress, ++*progress_cnt);
+                       display_progress(ctx->progress, ++ctx->progress_cnt);
                        count++;
                        list++;
                }
  }
  
  static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits,
-                                  struct progress *progress,
-                                  uint64_t *progress_cnt)
+                                  struct write_commit_graph_context *ctx)
  {
-       struct commit **list = commits;
+       struct commit **list = ctx->commits.list;
        int count;
-       for (count = 0; count < nr_commits; count++, list++) {
-               display_progress(progress, ++*progress_cnt);
+       for (count = 0; count < ctx->commits.nr; count++, list++) {
+               display_progress(ctx->progress, ++ctx->progress_cnt);
                hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
        }
  }
@@@ -572,19 -587,17 +594,17 @@@ static const unsigned char *commit_to_s
  }
  
  static void write_graph_chunk_data(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits,
-                                  struct progress *progress,
-                                  uint64_t *progress_cnt)
+                                  struct write_commit_graph_context *ctx)
  {
-       struct commit **list = commits;
-       struct commit **last = commits + nr_commits;
+       struct commit **list = ctx->commits.list;
+       struct commit **last = ctx->commits.list + ctx->commits.nr;
        uint32_t num_extra_edges = 0;
  
        while (list < last) {
                struct commit_list *parent;
                int edge_value;
                uint32_t packedDate[2];
-               display_progress(progress, ++*progress_cnt);
+               display_progress(ctx->progress, ++ctx->progress_cnt);
  
                parse_commit_no_graph(*list);
                hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
                        edge_value = GRAPH_PARENT_NONE;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
-                                             commits,
-                                             nr_commits,
+                                             ctx->commits.list,
+                                             ctx->commits.nr,
                                              commit_to_sha1);
  
                        if (edge_value < 0)
                        edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
-                                             commits,
-                                             nr_commits,
+                                             ctx->commits.list,
+                                             ctx->commits.nr,
                                              commit_to_sha1);
                        if (edge_value < 0)
                                BUG("missing parent %s for commit %s",
  }
  
  static void write_graph_chunk_extra_edges(struct hashfile *f,
-                                         struct commit **commits,
-                                         int nr_commits,
-                                         struct progress *progress,
-                                         uint64_t *progress_cnt)
+                                         struct write_commit_graph_context *ctx)
  {
-       struct commit **list = commits;
-       struct commit **last = commits + nr_commits;
+       struct commit **list = ctx->commits.list;
+       struct commit **last = ctx->commits.list + ctx->commits.nr;
        struct commit_list *parent;
  
        while (list < last) {
                int num_parents = 0;
  
-               display_progress(progress, ++*progress_cnt);
+               display_progress(ctx->progress, ++ctx->progress_cnt);
  
                for (parent = (*list)->parents; num_parents < 3 && parent;
                     parent = parent->next)
                /* Since num_parents > 2, this initializer is safe. */
                for (parent = (*list)->parents->next; parent; parent = parent->next) {
                        int edge_value = sha1_pos(parent->item->object.oid.hash,
-                                                 commits,
-                                                 nr_commits,
+                                                 ctx->commits.list,
+                                                 ctx->commits.nr,
                                                  commit_to_sha1);
  
                        if (edge_value < 0)
@@@ -700,125 -710,111 +717,111 @@@ static int commit_compare(const void *_
        return oidcmp(a, b);
  }
  
- struct packed_commit_list {
-       struct commit **list;
-       int nr;
-       int alloc;
- };
- struct packed_oid_list {
-       struct object_id *list;
-       int nr;
-       int alloc;
-       struct progress *progress;
-       int progress_done;
- };
  static int add_packed_commits(const struct object_id *oid,
                              struct packed_git *pack,
                              uint32_t pos,
                              void *data)
  {
-       struct packed_oid_list *list = (struct packed_oid_list*)data;
+       struct write_commit_graph_context *ctx = (struct write_commit_graph_context*)data;
        enum object_type type;
        off_t offset = nth_packed_object_offset(pack, pos);
        struct object_info oi = OBJECT_INFO_INIT;
  
-       if (list->progress)
-               display_progress(list->progress, ++list->progress_done);
+       if (ctx->progress)
+               display_progress(ctx->progress, ++ctx->progress_done);
  
        oi.typep = &type;
-       if (packed_object_info(the_repository, pack, offset, &oi) < 0)
+       if (packed_object_info(ctx->r, pack, offset, &oi) < 0)
                die(_("unable to get type of object %s"), oid_to_hex(oid));
  
        if (type != OBJ_COMMIT)
                return 0;
  
-       ALLOC_GROW(list->list, list->nr + 1, list->alloc);
-       oidcpy(&(list->list[list->nr]), oid);
-       list->nr++;
+       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+       oidcpy(&(ctx->oids.list[ctx->oids.nr]), oid);
+       ctx->oids.nr++;
  
        return 0;
  }
  
- static void add_missing_parents(struct packed_oid_list *oids, struct commit *commit)
+ static void add_missing_parents(struct write_commit_graph_context *ctx, struct commit *commit)
  {
        struct commit_list *parent;
        for (parent = commit->parents; parent; parent = parent->next) {
                if (!(parent->item->object.flags & UNINTERESTING)) {
-                       ALLOC_GROW(oids->list, oids->nr + 1, oids->alloc);
-                       oidcpy(&oids->list[oids->nr], &(parent->item->object.oid));
-                       oids->nr++;
+                       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+                       oidcpy(&ctx->oids.list[ctx->oids.nr], &(parent->item->object.oid));
+                       ctx->oids.nr++;
                        parent->item->object.flags |= UNINTERESTING;
                }
        }
  }
  
- static void close_reachable(struct packed_oid_list *oids, int report_progress)
+ static void close_reachable(struct write_commit_graph_context *ctx)
  {
        int i;
        struct commit *commit;
-       struct progress *progress = NULL;
  
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Loading known commits in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Loading known commits in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
                if (commit)
                        commit->object.flags |= UNINTERESTING;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
  
        /*
-        * As this loop runs, oids->nr may grow, but not more
+        * As this loop runs, ctx->oids.nr may grow, but not more
         * than the number of missing commits in the reachable
         * closure.
         */
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Expanding reachable commits in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Expanding reachable commits in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
  
                if (commit && !parse_commit_no_graph(commit))
-                       add_missing_parents(oids, commit);
+                       add_missing_parents(ctx, commit);
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
  
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Clearing commit marks in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Clearing commit marks in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
  
                if (commit)
                        commit->object.flags &= ~UNINTERESTING;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
  }
  
- static void compute_generation_numbers(struct packed_commit_list* commits,
-                                      int report_progress)
+ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
  {
        int i;
        struct commit_list *list = NULL;
-       struct progress *progress = NULL;
  
-       if (report_progress)
-               progress = start_progress(
-                       _("Computing commit graph generation numbers"),
-                       commits->nr);
-       for (i = 0; i < commits->nr; i++) {
-               display_progress(progress, i + 1);
-               if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
-                   commits->list[i]->generation != GENERATION_NUMBER_ZERO)
+       if (ctx->report_progress)
+               ctx->progress = start_progress(
+                                       _("Computing commit graph generation numbers"),
+                                       ctx->commits.nr);
+       for (i = 0; i < ctx->commits.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               if (ctx->commits.list[i]->generation != GENERATION_NUMBER_INFINITY &&
+                   ctx->commits.list[i]->generation != GENERATION_NUMBER_ZERO)
                        continue;
  
-               commit_list_insert(commits->list[i], &list);
+               commit_list_insert(ctx->commits.list[i], &list);
                while (list) {
                        struct commit *current = list->item;
                        struct commit_list *parent;
                        }
                }
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
  }
  
  static int add_ref_to_list(const char *refname,
        return 0;
  }
  
- void write_commit_graph_reachable(const char *obj_dir, int append,
-                                 int report_progress)
+ int write_commit_graph_reachable(const char *obj_dir, unsigned int flags)
  {
        struct string_list list = STRING_LIST_INIT_DUP;
+       int result;
  
        for_each_ref(add_ref_to_list, &list);
-       write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+       result = write_commit_graph(obj_dir, NULL, &list,
+                                   flags);
  
        string_list_clear(&list, 0);
+       return result;
  }
  
- void write_commit_graph(const char *obj_dir,
-                       struct string_list *pack_indexes,
-                       struct string_list *commit_hex,
-                       int append, int report_progress)
+ static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
+                               struct string_list *pack_indexes)
  {
-       struct packed_oid_list oids;
-       struct packed_commit_list commits;
-       struct hashfile *f;
-       uint32_t i, count_distinct = 0;
-       char *graph_name;
-       struct lock_file lk = LOCK_INIT;
-       uint32_t chunk_ids[5];
-       uint64_t chunk_offsets[5];
-       int num_chunks;
-       int num_extra_edges;
-       struct commit_list *parent;
-       struct progress *progress = NULL;
-       const unsigned hashsz = the_hash_algo->rawsz;
-       uint64_t progress_cnt = 0;
+       uint32_t i;
        struct strbuf progress_title = STRBUF_INIT;
-       unsigned long approx_nr_objects;
-       if (!commit_graph_compatible(the_repository))
-               return;
-       oids.nr = 0;
-       approx_nr_objects = approximate_object_count();
-       oids.alloc = approx_nr_objects / 32;
-       oids.progress = NULL;
-       oids.progress_done = 0;
+       struct strbuf packname = STRBUF_INIT;
+       int dirlen;
  
-       if (append) {
-               prepare_commit_graph_one(the_repository, obj_dir);
-               if (the_repository->objects->commit_graph)
-                       oids.alloc += the_repository->objects->commit_graph->num_commits;
+       strbuf_addf(&packname, "%s/pack/", ctx->obj_dir);
+       dirlen = packname.len;
+       if (ctx->report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Finding commits for commit graph in %d pack",
+                              "Finding commits for commit graph in %d packs",
+                              pack_indexes->nr),
+                           pack_indexes->nr);
+               ctx->progress = start_delayed_progress(progress_title.buf, 0);
+               ctx->progress_done = 0;
        }
-       if (oids.alloc < 1024)
-               oids.alloc = 1024;
-       ALLOC_ARRAY(oids.list, oids.alloc);
-       if (append && the_repository->objects->commit_graph) {
-               struct commit_graph *commit_graph =
-                       the_repository->objects->commit_graph;
-               for (i = 0; i < commit_graph->num_commits; i++) {
-                       const unsigned char *hash = commit_graph->chunk_oid_lookup +
-                               commit_graph->hash_len * i;
-                       hashcpy(oids.list[oids.nr++].hash, hash);
+       for (i = 0; i < pack_indexes->nr; i++) {
+               struct packed_git *p;
+               strbuf_setlen(&packname, dirlen);
+               strbuf_addstr(&packname, pack_indexes->items[i].string);
+               p = add_packed_git(packname.buf, packname.len, 1);
+               if (!p) {
+                       error(_("error adding pack %s"), packname.buf);
+                       return -1;
                }
+               if (open_pack_index(p)) {
+                       error(_("error opening index for %s"), packname.buf);
+                       return -1;
+               }
+               for_each_object_in_pack(p, add_packed_commits, ctx,
+                                       FOR_EACH_OBJECT_PACK_ORDER);
+               close_pack(p);
+               free(p);
        }
  
-       if (pack_indexes) {
-               struct strbuf packname = STRBUF_INIT;
-               int dirlen;
-               strbuf_addf(&packname, "%s/pack/", obj_dir);
-               dirlen = packname.len;
-               if (report_progress) {
-                       strbuf_addf(&progress_title,
-                                   Q_("Finding commits for commit graph in %d pack",
-                                      "Finding commits for commit graph in %d packs",
-                                      pack_indexes->nr),
-                                   pack_indexes->nr);
-                       oids.progress = start_delayed_progress(progress_title.buf, 0);
-                       oids.progress_done = 0;
-               }
-               for (i = 0; i < pack_indexes->nr; i++) {
-                       struct packed_git *p;
-                       strbuf_setlen(&packname, dirlen);
-                       strbuf_addstr(&packname, pack_indexes->items[i].string);
-                       p = add_packed_git(packname.buf, packname.len, 1);
-                       if (!p)
-                               die(_("error adding pack %s"), packname.buf);
-                       if (open_pack_index(p))
-                               die(_("error opening index for %s"), packname.buf);
-                       for_each_object_in_pack(p, add_packed_commits, &oids,
-                                               FOR_EACH_OBJECT_PACK_ORDER);
-                       close_pack(p);
-                       free(p);
-               }
-               stop_progress(&oids.progress);
-               strbuf_reset(&progress_title);
-               strbuf_release(&packname);
+       stop_progress(&ctx->progress);
+       strbuf_reset(&progress_title);
+       strbuf_release(&packname);
+       return 0;
+ }
+ static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
+                                     struct string_list *commit_hex)
+ {
+       uint32_t i;
+       struct strbuf progress_title = STRBUF_INIT;
+       if (ctx->report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Finding commits for commit graph from %d ref",
+                              "Finding commits for commit graph from %d refs",
+                              commit_hex->nr),
+                           commit_hex->nr);
+               ctx->progress = start_delayed_progress(
+                                       progress_title.buf,
+                                       commit_hex->nr);
        }
+       for (i = 0; i < commit_hex->nr; i++) {
+               const char *end;
+               struct object_id oid;
+               struct commit *result;
+               display_progress(ctx->progress, i + 1);
+               if (commit_hex->items[i].string &&
+                   parse_oid_hex(commit_hex->items[i].string, &oid, &end))
+                       continue;
  
-       if (commit_hex) {
-               if (report_progress) {
-                       strbuf_addf(&progress_title,
-                                   Q_("Finding commits for commit graph from %d ref",
-                                      "Finding commits for commit graph from %d refs",
-                                      commit_hex->nr),
-                                   commit_hex->nr);
-                       progress = start_delayed_progress(progress_title.buf,
-                                                         commit_hex->nr);
-               }
-               for (i = 0; i < commit_hex->nr; i++) {
-                       const char *end;
-                       struct object_id oid;
-                       struct commit *result;
-                       display_progress(progress, i + 1);
-                       if (commit_hex->items[i].string &&
-                           parse_oid_hex(commit_hex->items[i].string, &oid, &end))
-                               continue;
-                       result = lookup_commit_reference_gently(the_repository, &oid, 1);
-                       if (result) {
-                               ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
-                               oidcpy(&oids.list[oids.nr], &(result->object.oid));
-                               oids.nr++;
-                       }
+               result = lookup_commit_reference_gently(ctx->r, &oid, 1);
+               if (result) {
+                       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+                       oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
+                       ctx->oids.nr++;
                }
-               stop_progress(&progress);
-               strbuf_reset(&progress_title);
        }
+       stop_progress(&ctx->progress);
+       strbuf_release(&progress_title);
+ }
  
-       if (!pack_indexes && !commit_hex) {
-               if (report_progress)
-                       oids.progress = start_delayed_progress(
-                               _("Finding commits for commit graph among packed objects"),
-                               approx_nr_objects);
-               for_each_packed_object(add_packed_commits, &oids,
-                                      FOR_EACH_OBJECT_PACK_ORDER);
-               if (oids.progress_done < approx_nr_objects)
-                       display_progress(oids.progress, approx_nr_objects);
-               stop_progress(&oids.progress);
-       }
+ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
+ {
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                       _("Finding commits for commit graph among packed objects"),
+                       ctx->approx_nr_objects);
+       for_each_packed_object(add_packed_commits, ctx,
+                              FOR_EACH_OBJECT_PACK_ORDER);
+       if (ctx->progress_done < ctx->approx_nr_objects)
+               display_progress(ctx->progress, ctx->approx_nr_objects);
+       stop_progress(&ctx->progress);
+ }
  
-       close_reachable(&oids, report_progress);
+ static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
+ {
+       uint32_t i, count_distinct = 1;
  
-       if (report_progress)
-               progress = start_delayed_progress(
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
                        _("Counting distinct commits in commit graph"),
-                       oids.nr);
-       display_progress(progress, 0); /* TODO: Measure QSORT() progress */
-       QSORT(oids.list, oids.nr, commit_compare);
-       count_distinct = 1;
-       for (i = 1; i < oids.nr; i++) {
-               display_progress(progress, i + 1);
-               if (!oideq(&oids.list[i - 1], &oids.list[i]))
+                       ctx->oids.nr);
+       display_progress(ctx->progress, 0); /* TODO: Measure QSORT() progress */
+       QSORT(ctx->oids.list, ctx->oids.nr, commit_compare);
+       for (i = 1; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               if (!oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
                        count_distinct++;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
  
-       if (count_distinct >= GRAPH_EDGE_LAST_MASK)
-               die(_("the commit graph format cannot write %d commits"), count_distinct);
+       return count_distinct;
+ }
  
-       commits.nr = 0;
-       commits.alloc = count_distinct;
-       ALLOC_ARRAY(commits.list, commits.alloc);
+ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
+ {
+       uint32_t i;
+       struct commit_list *parent;
  
-       num_extra_edges = 0;
-       if (report_progress)
-               progress = start_delayed_progress(
+       ctx->num_extra_edges = 0;
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
                        _("Finding extra edges in commit graph"),
-                       oids.nr);
-       for (i = 0; i < oids.nr; i++) {
+                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
                int num_parents = 0;
-               display_progress(progress, i + 1);
-               if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
+               display_progress(ctx->progress, i + 1);
+               if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
                        continue;
  
-               commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
-               parse_commit_no_graph(commits.list[commits.nr]);
+               ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
+               parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
  
-               for (parent = commits.list[commits.nr]->parents;
+               for (parent = ctx->commits.list[ctx->commits.nr]->parents;
                     parent; parent = parent->next)
                        num_parents++;
  
                if (num_parents > 2)
-                       num_extra_edges += num_parents - 1;
+                       ctx->num_extra_edges += num_parents - 1;
  
-               commits.nr++;
+               ctx->commits.nr++;
        }
-       num_chunks = num_extra_edges ? 4 : 3;
-       stop_progress(&progress);
-       if (commits.nr >= GRAPH_EDGE_LAST_MASK)
-               die(_("too many commits to write graph"));
-       compute_generation_numbers(&commits, report_progress);
+       stop_progress(&ctx->progress);
+ }
  
-       graph_name = get_commit_graph_filename(obj_dir);
-       if (safe_create_leading_directories(graph_name)) {
-               UNLEAK(graph_name);
-               die_errno(_("unable to create leading directories of %s"),
-                         graph_name);
+ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
+ {
+       uint32_t i;
+       struct hashfile *f;
+       struct lock_file lk = LOCK_INIT;
+       uint32_t chunk_ids[5];
+       uint64_t chunk_offsets[5];
+       const unsigned hashsz = the_hash_algo->rawsz;
+       struct strbuf progress_title = STRBUF_INIT;
+       int num_chunks = ctx->num_extra_edges ? 4 : 3;
+       ctx->graph_name = get_commit_graph_filename(ctx->obj_dir);
+       if (safe_create_leading_directories(ctx->graph_name)) {
+               UNLEAK(ctx->graph_name);
+               error(_("unable to create leading directories of %s"),
+                       ctx->graph_name);
+               return -1;
        }
  
-       hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+       hold_lock_file_for_update(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR);
        f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
  
        hashwrite_be32(f, GRAPH_SIGNATURE);
        chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
        chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
        chunk_ids[2] = GRAPH_CHUNKID_DATA;
-       if (num_extra_edges)
+       if (ctx->num_extra_edges)
                chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
        else
                chunk_ids[3] = 0;
  
        chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
        chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
-       chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
-       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
-       chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
+       chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
+       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
+       chunk_offsets[4] = chunk_offsets[3] + 4 * ctx->num_extra_edges;
  
        for (i = 0; i <= num_chunks; i++) {
                uint32_t chunk_write[3];
                hashwrite(f, chunk_write, 12);
        }
  
-       if (report_progress) {
+       if (ctx->report_progress) {
                strbuf_addf(&progress_title,
                            Q_("Writing out commit graph in %d pass",
                               "Writing out commit graph in %d passes",
                               num_chunks),
                            num_chunks);
-               progress = start_delayed_progress(
+               ctx->progress = start_delayed_progress(
                        progress_title.buf,
-                       num_chunks * commits.nr);
+                       num_chunks * ctx->commits.nr);
        }
-       write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
-       write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-       write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-       if (num_extra_edges)
-               write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
-       stop_progress(&progress);
+       write_graph_chunk_fanout(f, ctx);
+       write_graph_chunk_oids(f, hashsz, ctx);
+       write_graph_chunk_data(f, hashsz, ctx);
+       if (ctx->num_extra_edges)
+               write_graph_chunk_extra_edges(f, ctx);
+       stop_progress(&ctx->progress);
        strbuf_release(&progress_title);
  
-       close_commit_graph(the_repository);
+       close_commit_graph(ctx->r->objects);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
  
-       free(graph_name);
-       free(commits.list);
-       free(oids.list);
+       return 0;
+ }
+ int write_commit_graph(const char *obj_dir,
+                      struct string_list *pack_indexes,
+                      struct string_list *commit_hex,
+                      unsigned int flags)
+ {
+       struct write_commit_graph_context *ctx;
+       uint32_t i, count_distinct = 0;
+       int res = 0;
+       if (!commit_graph_compatible(the_repository))
+               return 0;
+       ctx = xcalloc(1, sizeof(struct write_commit_graph_context));
+       ctx->r = the_repository;
+       ctx->obj_dir = obj_dir;
+       ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0;
+       ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0;
+       ctx->approx_nr_objects = approximate_object_count();
+       ctx->oids.alloc = ctx->approx_nr_objects / 32;
+       if (ctx->append) {
+               prepare_commit_graph_one(ctx->r, ctx->obj_dir);
+               if (ctx->r->objects->commit_graph)
+                       ctx->oids.alloc += ctx->r->objects->commit_graph->num_commits;
+       }
+       if (ctx->oids.alloc < 1024)
+               ctx->oids.alloc = 1024;
+       ALLOC_ARRAY(ctx->oids.list, ctx->oids.alloc);
+       if (ctx->append && ctx->r->objects->commit_graph) {
+               struct commit_graph *g = ctx->r->objects->commit_graph;
+               for (i = 0; i < g->num_commits; i++) {
+                       const unsigned char *hash = g->chunk_oid_lookup + g->hash_len * i;
+                       hashcpy(ctx->oids.list[ctx->oids.nr++].hash, hash);
+               }
+       }
+       if (pack_indexes) {
+               if ((res = fill_oids_from_packs(ctx, pack_indexes)))
+                       goto cleanup;
+       }
+       if (commit_hex)
+               fill_oids_from_commit_hex(ctx, commit_hex);
+       if (!pack_indexes && !commit_hex)
+               fill_oids_from_all_packs(ctx);
+       close_reachable(ctx);
+       count_distinct = count_distinct_commits(ctx);
+       if (count_distinct >= GRAPH_EDGE_LAST_MASK) {
+               error(_("the commit graph format cannot write %d commits"), count_distinct);
+               res = -1;
+               goto cleanup;
+       }
+       ctx->commits.alloc = count_distinct;
+       ALLOC_ARRAY(ctx->commits.list, ctx->commits.alloc);
+       copy_oids_to_commits(ctx);
+       if (ctx->commits.nr >= GRAPH_EDGE_LAST_MASK) {
+               error(_("too many commits to write graph"));
+               res = -1;
+               goto cleanup;
+       }
+       compute_generation_numbers(ctx);
+       res = write_commit_graph_file(ctx);
+ cleanup:
+       free(ctx->graph_name);
+       free(ctx->commits.list);
+       free(ctx->oids.list);
+       free(ctx);
+       return res;
  }
  
  #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
diff --combined commit.c
index 8fa1883c61c580578a755cdf2da009203d8d386e,e4d1233226a1f633504056e1e77cc21a3fec2932..26ce0770f688eb94d5be5975d756752dbc0337a8
+++ b/commit.c
@@@ -340,21 -340,15 +340,21 @@@ void free_commit_buffer(struct parsed_o
        }
  }
  
 -struct tree *get_commit_tree(const struct commit *commit)
 +static inline void set_commit_tree(struct commit *c, struct tree *t)
 +{
 +      c->maybe_tree = t;
 +}
 +
 +struct tree *repo_get_commit_tree(struct repository *r,
 +                                const struct commit *commit)
  {
        if (commit->maybe_tree || !commit->object.parsed)
                return commit->maybe_tree;
  
 -      if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
 -              BUG("commit has NULL tree, but was not loaded from commit-graph");
 +      if (commit->graph_pos != COMMIT_NOT_FROM_GRAPH)
 +              return get_commit_tree_in_graph(r, commit);
  
 -      return get_commit_tree_in_graph(the_repository, commit);
 +      return NULL;
  }
  
  struct object_id *get_commit_tree_oid(const struct commit *commit)
  
  void release_commit_memory(struct parsed_object_pool *pool, struct commit *c)
  {
 -      c->maybe_tree = NULL;
 +      set_commit_tree(c, NULL);
        c->index = 0;
        free_commit_buffer(pool, c);
        free_commit_list(c->parents);
@@@ -412,7 -406,7 +412,7 @@@ int parse_commit_buffer(struct reposito
        if (get_oid_hex(bufptr + 5, &parent) < 0)
                return error("bad tree pointer in commit %s",
                             oid_to_hex(&item->object.oid));
 -      item->maybe_tree = lookup_tree(r, &parent);
 +      set_commit_tree(item, lookup_tree(r, &parent));
        bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
  
        item->date = parse_commit_date(bufptr, tail);
  
        if (check_graph)
-               load_commit_graph_info(the_repository, item);
+               load_commit_graph_info(r, item);
  
        return 0;
  }
diff --combined packfile.c
index d55cb7f013f2c28cea7245c09eb0c334ef10511a,017046fcf956fd3070ae848ed45cdc84ccd09b38..c0d83fdfed973de8574224e46fb0afd4be0a98c9
@@@ -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)
@@@ -1269,7 -1274,7 +1272,7 @@@ static enum object_type packed_to_objec
                if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
                        poi_stack_alloc = alloc_nr(poi_stack_nr);
                        ALLOC_ARRAY(poi_stack, poi_stack_alloc);
 -                      memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
 +                      COPY_ARRAY(poi_stack, small_poi_stack, poi_stack_nr);
                } else {
                        ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
                }
@@@ -1679,8 -1684,8 +1682,8 @@@ void *unpack_entry(struct repository *r
                    && delta_stack == small_delta_stack) {
                        delta_stack_alloc = alloc_nr(delta_stack_nr);
                        ALLOC_ARRAY(delta_stack, delta_stack_alloc);
 -                      memcpy(delta_stack, small_delta_stack,
 -                             sizeof(*delta_stack)*delta_stack_nr);
 +                      COPY_ARRAY(delta_stack, small_delta_stack,
 +                                 delta_stack_nr);
                } else {
                        ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
                }
@@@ -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 t/t5318-commit-graph.sh
index 840ad4d8accbfef59e2c9ade82105cc717f6866c,3b6fd0d72848ff182c4a73a4626fae4af06fb023..5267c4be20e709bb1632c5b9786ffdb418bcb5c9
@@@ -23,6 -23,14 +23,14 @@@ test_expect_success 'write graph with n
        test_path_is_file info/commit-graph
  '
  
+ test_expect_success 'close with correct error on bad input' '
+       cd "$TRASH_DIRECTORY/full" &&
+       echo doesnotexist >in &&
+       { git commit-graph write --stdin-packs <in 2>stderr; ret=$?; } &&
+       test "$ret" = 1 &&
+       test_i18ngrep "error adding pack" stderr
+ '
  test_expect_success 'create commits and repack' '
        cd "$TRASH_DIRECTORY/full" &&
        for i in $(test_seq 3)
@@@ -75,7 -83,7 +83,7 @@@ graph_read_expect() 
  
  test_expect_success 'write graph' '
        cd "$TRASH_DIRECTORY/full" &&
 -      graph1=$(git commit-graph write) &&
 +      git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
        graph_read_expect "3"
  '
@@@ -400,7 -408,7 +408,7 @@@ corrupt_graph_and_verify() 
        orig_size=$(wc -c < $objdir/info/commit-graph) &&
        zero_pos=${4:-${orig_size}} &&
        printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
 -      dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" count=0 &&
 +      dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
        generate_zero_bytes $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
        corrupt_graph_verify "$grepstr"
  
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) {