Merge branch 'mh/tidy-ref-update-flags'
authorJunio C Hamano <gitster@pobox.com>
Wed, 15 Nov 2017 03:14:29 +0000 (12:14 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 15 Nov 2017 03:14:29 +0000 (12:14 +0900)
Code clean-up in refs API implementation.

* mh/tidy-ref-update-flags:
refs: update some more docs to use "oid" rather than "sha1"
write_packed_entry(): take `object_id` arguments
refs: rename constant `REF_ISPRUNING` to `REF_IS_PRUNING`
refs: rename constant `REF_NODEREF` to `REF_NO_DEREF`
refs: tidy up and adjust visibility of the `ref_update` flags
ref_transaction_add_update(): remove a check
ref_transaction_update(): die on disallowed flags
prune_ref(): call `ref_transaction_add_update()` directly
files_transaction_prepare(): don't leak flags to packed transaction

1  2 
bisect.c
builtin/am.c
builtin/checkout.c
builtin/clone.c
builtin/remote.c
refs/files-backend.c
refs/packed-backend.c
sequencer.c
diff --combined bisect.c
index de551c6d5ecef5cfb9d04e836854750ef08741c4,c09f7bbbcb18c389c604daf586bccffa376740fc..0fca17c02bba89d6c65df95e03e88ef6dd02971e
+++ b/bisect.c
@@@ -226,11 -226,10 +226,11 @@@ static struct commit_list *best_bisecti
                add_name_decoration(DECORATION_NONE, buf.buf, obj);
  
                p->item = array[i].commit;
 -              p = p->next;
 +              if (i < cnt - 1)
 +                      p = p->next;
        }
 -      if (p)
 -              p->next = NULL;
 +      free_commit_list(p->next);
 +      p->next = NULL;
        strbuf_release(&buf);
        free(array);
        return list;
@@@ -361,29 -360,28 +361,29 @@@ static struct commit_list *do_find_bise
                return best_bisection_sorted(list, nr);
  }
  
 -struct commit_list *find_bisection(struct commit_list *list,
 -                                        int *reaches, int *all,
 -                                        int find_all)
 +void find_bisection(struct commit_list **commit_list, int *reaches,
 +                  int *all, int find_all)
  {
        int nr, on_list;
 -      struct commit_list *p, *best, *next, *last;
 +      struct commit_list *list, *p, *best, *next, *last;
        int *weights;
  
 -      show_list("bisection 2 entry", 0, 0, list);
 +      show_list("bisection 2 entry", 0, 0, *commit_list);
  
        /*
         * Count the number of total and tree-changing items on the
         * list, while reversing the list.
         */
 -      for (nr = on_list = 0, last = NULL, p = list;
 +      for (nr = on_list = 0, last = NULL, p = *commit_list;
             p;
             p = next) {
                unsigned flags = p->item->object.flags;
  
                next = p->next;
 -              if (flags & UNINTERESTING)
 +              if (flags & UNINTERESTING) {
 +                      free(p);
                        continue;
 +              }
                p->next = last;
                last = p;
                if (!(flags & TREESAME))
        /* Do the real work of finding bisection commit. */
        best = do_find_bisection(list, nr, weights, find_all);
        if (best) {
 -              if (!find_all)
 +              if (!find_all) {
 +                      list->item = best->item;
 +                      free_commit_list(list->next);
 +                      best = list;
                        best->next = NULL;
 +              }
                *reaches = weight(best);
        }
        free(weights);
 -      return best;
 +      *commit_list = best;
  }
  
  static int register_ref(const char *refname, const struct object_id *oid,
@@@ -439,12 -433,7 +439,12 @@@ static int read_bisect_refs(void
  
  static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
  static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 +static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 +static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 +static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 +static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
  static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
 +static GIT_PATH_FUNC(git_path_head_name, "head-name")
  
  static void read_bisect_paths(struct argv_array *array)
  {
@@@ -966,7 -955,8 +966,7 @@@ int bisect_next_all(const char *prefix
  
        bisect_common(&revs);
  
 -      revs.commits = find_bisection(revs.commits, &reaches, &all,
 -                                     !!skipped_revs.nr);
 +      find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr);
        revs.commits = managed_skipped(revs.commits, &tried);
  
        if (!revs.commits) {
@@@ -1055,40 -1045,3 +1055,40 @@@ int estimate_bisect_steps(int all
  
        return (e < 3 * x) ? n : n - 1;
  }
-       result = delete_refs("bisect: remove", &refs_for_removal, REF_NODEREF);
 +
 +static int mark_for_removal(const char *refname, const struct object_id *oid,
 +                          int flag, void *cb_data)
 +{
 +      struct string_list *refs = cb_data;
 +      char *ref = xstrfmt("refs/bisect%s", refname);
 +      string_list_append(refs, ref);
 +      return 0;
 +}
 +
 +int bisect_clean_state(void)
 +{
 +      int result = 0;
 +
 +      /* There may be some refs packed during bisection */
 +      struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
 +      for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
 +      string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
++      result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
 +      refs_for_removal.strdup_strings = 1;
 +      string_list_clear(&refs_for_removal, 0);
 +      unlink_or_warn(git_path_bisect_expected_rev());
 +      unlink_or_warn(git_path_bisect_ancestors_ok());
 +      unlink_or_warn(git_path_bisect_log());
 +      unlink_or_warn(git_path_bisect_names());
 +      unlink_or_warn(git_path_bisect_run());
 +      unlink_or_warn(git_path_bisect_terms());
 +      /* Cleanup head-name if it got left by an old version of git-bisect */
 +      unlink_or_warn(git_path_head_name());
 +      /*
 +       * Cleanup BISECT_START last to support the --no-checkout option
 +       * introduced in the commit 4796e823a.
 +       */
 +      unlink_or_warn(git_path_bisect_start());
 +
 +      return result;
 +}
diff --combined builtin/am.c
index 92c48535052c2e32e46eeedfc86b009e7ae0726c,894290e2d304419a4eb371276a28a91e362efd85..02853b3e05bfef638963dbfe91a7eccf3d40b9e6
@@@ -1134,11 -1134,11 +1134,11 @@@ static const char *msgnum(const struct 
   */
  static void refresh_and_write_cache(void)
  {
 -      struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock_file = LOCK_INIT;
  
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write index file"));
  }
  
@@@ -1157,9 -1157,9 +1157,9 @@@ static int index_has_changes(struct str
                struct diff_options opt;
  
                diff_setup(&opt);
 -              DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
 +              opt.flags.exit_with_status = 1;
                if (!sb)
 -                      DIFF_OPT_SET(&opt, QUICK);
 +                      opt.flags.quick = 1;
                do_diff_cache(&head, &opt);
                diffcore_std(&opt);
                for (i = 0; sb && i < diff_queued_diff.nr; i++) {
                        strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
                }
                diff_flush(&opt);
 -              return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
 +              return opt.flags.has_changes != 0;
        } else {
                for (i = 0; sb && i < active_nr; i++) {
                        if (i)
@@@ -1409,8 -1409,8 +1409,8 @@@ static void write_commit_patch(const st
        rev_info.show_root_diff = 1;
        rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev_info.no_commit_id = 1;
 -      DIFF_OPT_SET(&rev_info.diffopt, BINARY);
 -      DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
 +      rev_info.diffopt.flags.binary = 1;
 +      rev_info.diffopt.flags.full_index = 1;
        rev_info.diffopt.use_color = 0;
        rev_info.diffopt.file = fp;
        rev_info.diffopt.close_file = 1;
@@@ -1488,10 -1488,11 +1488,10 @@@ static int run_apply(const struct am_st
        struct argv_array apply_opts = ARGV_ARRAY_INIT;
        struct apply_state apply_state;
        int res, opts_left;
 -      static struct lock_file lock_file;
        int force_apply = 0;
        int options = 0;
  
 -      if (init_apply_state(&apply_state, NULL, &lock_file))
 +      if (init_apply_state(&apply_state, NULL))
                die("BUG: init_apply_state() failed");
  
        argv_array_push(&apply_opts, "apply");
@@@ -1945,14 -1946,15 +1945,14 @@@ next
   */
  static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
  {
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[2];
  
        if (parse_tree(head) || parse_tree(remote))
                return -1;
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        refresh_cache(REFRESH_QUIET);
  
        init_tree_desc(&t[1], remote->buffer, remote->size);
  
        if (unpack_trees(2, t, &opts)) {
 -              rollback_lock_file(lock_file);
 +              rollback_lock_file(&lock_file);
                return -1;
        }
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        return 0;
   */
  static int merge_tree(struct tree *tree)
  {
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[1];
  
        if (parse_tree(tree))
                return -1;
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        init_tree_desc(&t[0], tree->buffer, tree->size);
  
        if (unpack_trees(1, t, &opts)) {
 -              rollback_lock_file(lock_file);
 +              rollback_lock_file(&lock_file);
                return -1;
        }
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        return 0;
@@@ -2148,7 -2151,7 +2148,7 @@@ static void am_abort(struct am_state *s
                           has_curr_head ? &curr_head : NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
-               delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
+               delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
  
        free(curr_branch);
        am_destroy(state);
diff --combined builtin/checkout.c
index 6c2b4cd419a4a588e73a3f12a9ebdd6c1365ce78,114028ee010752b59d4dfc81a6d2b43affc80aec..7d8bcc3833512bc262bd0a8d86db9169d991b320
@@@ -247,7 -247,7 +247,7 @@@ static int checkout_paths(const struct 
        struct object_id rev;
        struct commit *head;
        int errs = 0;
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
  
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
                die(_("'%s' cannot be used with updating paths"), "--track");
                return run_add_interactive(revision, "--patch=checkout",
                                           &opts->pathspec);
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("index file corrupt"));
  
        }
        errs |= finish_delayed_checkout(&state);
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        read_ref_full("HEAD", 0, &rev, NULL);
@@@ -470,9 -472,9 +470,9 @@@ static int merge_working_tree(const str
                              int *writeout_error)
  {
        int ret;
 -      struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock_file = LOCK_INIT;
  
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(NULL) < 0)
                return error(_("index file corrupt"));
  
        if (!cache_tree_fully_valid(active_cache_tree))
                cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        if (!opts->force && !opts->quiet)
@@@ -663,7 -665,7 +663,7 @@@ static void update_refs_for_switch(cons
                /* Nothing to do. */
        } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
                update_ref(msg.buf, "HEAD", &new->commit->object.oid, NULL,
-                          REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+                          REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
                        if (old->path &&
                            advice_detached_head && !opts->force_detach)
diff --combined builtin/clone.c
index cf6eddc9c56b9fda95ff8d9764a2d5f09b7d20b4,557c6c3c06ff33d181d4759c0dbae33b5dace685..b22845738afe68e61d45883e89a470213de921ef
@@@ -689,7 -689,7 +689,7 @@@ static void update_head(const struct re
        } else if (our) {
                struct commit *c = lookup_commit_reference(&our->old_oid);
                /* --branch specifies a non-branch (i.e. tags), detach HEAD */
-               update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NODEREF,
+               update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
                           UPDATE_REFS_DIE_ON_ERR);
        } else if (remote) {
                /*
                 * HEAD points to a branch but we don't know which one.
                 * Detach HEAD in all these cases.
                 */
-               update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NODEREF,
+               update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
                           UPDATE_REFS_DIE_ON_ERR);
        }
  }
@@@ -706,7 -706,7 +706,7 @@@ static int checkout(int submodule_progr
  {
        struct object_id oid;
        char *head;
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree *tree;
        struct tree_desc t;
        /* We need to be in the new work tree for the checkout */
        setup_work_tree();
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
diff --combined builtin/remote.c
index a04ea50e403931aa5e8cd35828f93de97493604e,3d38c6150cfd15761fe975364af281da7938d72d..d95bf904c3b3fb40438335198a2729e45a9897cb
@@@ -565,7 -565,7 +565,7 @@@ static int read_remote_branches(const c
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
                                            NULL, &flag);
 -              if (flag & REF_ISSYMREF)
 +              if (symref && (flag & REF_ISSYMREF))
                        item->util = xstrdup(symref);
                else
                        item->util = NULL;
@@@ -693,7 -693,7 +693,7 @@@ static int mv(int argc, const char **ar
                read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag);
                if (!(flag & REF_ISSYMREF))
                        continue;
-               if (delete_ref(NULL, item->string, NULL, REF_NODEREF))
+               if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
                        die(_("deleting '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
@@@ -788,7 -788,7 +788,7 @@@ static int rm(int argc, const char **ar
        strbuf_release(&buf);
  
        if (!result)
-               result = delete_refs("remote: remove", &branches, REF_NODEREF);
+               result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
        string_list_clear(&branches, 0);
  
        if (skipped.nr) {
@@@ -1255,7 -1255,7 +1255,7 @@@ static int set_head(int argc, const cha
                        head_name = xstrdup(states.heads.items[0].string);
                free_remote_ref_states(&states);
        } else if (opt_d && !opt_a && argc == 1) {
-               if (delete_ref(NULL, buf.buf, NULL, REF_NODEREF))
+               if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
                        result |= error(_("Could not delete %s"), buf.buf);
        } else
                usage_with_options(builtin_remote_sethead_usage, options);
diff --combined refs/files-backend.c
index b956215bfc8ceb0a5c801163ea84df6a3336d3d5,2298f900dd4dbcef2cbe9c9a4c13f5708655ae40..f75d960e1982a2db5dc8828c6f1b10ffe64fda48
  #include "../object.h"
  #include "../dir.h"
  
+ /*
+  * This backend uses the following flags in `ref_update::flags` for
+  * internal bookkeeping purposes. Their numerical values must not
+  * conflict with REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, REF_HAVE_NEW,
+  * REF_HAVE_OLD, or REF_IS_PRUNING, which are also stored in
+  * `ref_update::flags`.
+  */
+ /*
+  * Used as a flag in ref_update::flags when a loose ref is being
+  * pruned. This flag must only be used when REF_NO_DEREF is set.
+  */
+ #define REF_IS_PRUNING (1 << 4)
+ /*
+  * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken
+  * refs (i.e., because the reference is about to be deleted anyway).
+  */
+ #define REF_DELETING (1 << 5)
+ /*
+  * Used as a flag in ref_update::flags when the lockfile needs to be
+  * committed.
+  */
+ #define REF_NEEDS_COMMIT (1 << 6)
+ /*
+  * Used as a flag in ref_update::flags when we want to log a ref
+  * update but not actually perform it.  This is used when a symbolic
+  * ref update is split up.
+  */
+ #define REF_LOG_ONLY (1 << 7)
+ /*
+  * Used as a flag in ref_update::flags when the ref_update was via an
+  * update to HEAD.
+  */
+ #define REF_UPDATE_VIA_HEAD (1 << 8)
+ /*
+  * Used as a flag in ref_update::flags when the loose reference has
+  * been deleted.
+  */
+ #define REF_DELETED_LOOSE (1 << 9)
  struct ref_lock {
        char *ref_name;
        struct lock_file lk;
@@@ -195,7 -240,7 +240,7 @@@ static void loose_fill_ref_dir(struct r
                        } else if (is_null_oid(&oid)) {
                                /*
                                 * It is so astronomically unlikely
-                                * that NULL_SHA1 is the SHA-1 of an
+                                * that null_oid is the OID of an
                                 * actual object that we consider its
                                 * appearance in a loose reference
                                 * file to be repo corruption
@@@ -428,7 -473,7 +473,7 @@@ static void unlock_ref(struct ref_lock 
   * are passed to refs_verify_refname_available() for this check.
   *
   * If mustexist is not set and the reference is not found or is
-  * broken, lock the reference anyway but clear sha1.
+  * broken, lock the reference anyway but clear old_oid.
   *
   * Return 0 on success. On failure, write an error message to err and
   * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR.
@@@ -989,22 -1034,29 +1034,29 @@@ static void prune_ref(struct files_ref_
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
+       int ret = -1;
  
        if (check_refname_format(r->name, 0))
                return;
  
        transaction = ref_store_transaction_begin(&refs->base, &err);
-       if (!transaction ||
-           ref_transaction_delete(transaction, r->name, &r->oid,
-                                  REF_ISPRUNING | REF_NODEREF, NULL, &err) ||
-           ref_transaction_commit(transaction, &err)) {
-               ref_transaction_free(transaction);
+       if (!transaction)
+               goto cleanup;
+       ref_transaction_add_update(
+                       transaction, r->name,
+                       REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
+                       &null_oid, &r->oid, NULL);
+       if (ref_transaction_commit(transaction, &err))
+               goto cleanup;
+       ret = 0;
+ cleanup:
+       if (ret)
                error("%s", err.buf);
-               strbuf_release(&err);
-               return;
-       }
-       ref_transaction_free(transaction);
        strbuf_release(&err);
+       ref_transaction_free(transaction);
+       return;
  }
  
  /*
@@@ -1081,7 -1133,7 +1133,7 @@@ static int files_pack_refs(struct ref_s
                 */
                if (ref_transaction_update(transaction, iter->refname,
                                           iter->oid, NULL,
-                                          REF_NODEREF, NULL, &err))
+                                          REF_NO_DEREF, NULL, &err))
                        die("failure preparing to create packed reference %s: %s",
                            iter->refname, err.buf);
  
@@@ -1284,7 -1336,7 +1336,7 @@@ static int files_copy_or_rename_ref(str
        }
  
        if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname,
-                           &orig_oid, REF_NODEREF)) {
+                           &orig_oid, REF_NO_DEREF)) {
                error("unable to delete old %s", oldrefname);
                goto rollback;
        }
                                RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
                                &oid, NULL) &&
            refs_delete_ref(&refs->base, NULL, newrefname,
-                           NULL, REF_NODEREF)) {
+                           NULL, REF_NO_DEREF)) {
                if (errno == EISDIR) {
                        struct strbuf path = STRBUF_INIT;
                        int result;
        logmoved = log;
  
        lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL,
-                                 REF_NODEREF, NULL, &err);
+                                 REF_NO_DEREF, NULL, &err);
        if (!lock) {
                if (copy)
                        error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
  
   rollback:
        lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL,
-                                 REF_NODEREF, NULL, &err);
+                                 REF_NO_DEREF, NULL, &err);
        if (!lock) {
                error("unable to lock %s for rollback: %s", oldrefname, err.buf);
                strbuf_release(&err);
@@@ -1596,9 -1648,8 +1648,8 @@@ static int files_log_ref_write(struct f
  }
  
  /*
-  * Write sha1 into the open lockfile, then close the lockfile. On
-  * errors, rollback the lockfile, fill in *err and
-  * return -1.
+  * Write oid into the open lockfile, then close the lockfile. On
+  * errors, rollback the lockfile, fill in *err and return -1.
   */
  static int write_ref_to_lockfile(struct ref_lock *lock,
                                 const struct object_id *oid, struct strbuf *err)
@@@ -1764,7 -1815,7 +1815,7 @@@ static int files_create_symref(struct r
        int ret;
  
        lock = lock_ref_oid_basic(refs, refname, NULL,
-                                 NULL, NULL, REF_NODEREF, NULL,
+                                 NULL, NULL, REF_NO_DEREF, NULL,
                                  &err);
        if (!lock) {
                error("%s", err.buf);
@@@ -2125,7 -2176,7 +2176,7 @@@ static int split_head_update(struct ref
        struct ref_update *new_update;
  
        if ((update->flags & REF_LOG_ONLY) ||
-           (update->flags & REF_ISPRUNING) ||
+           (update->flags & REF_IS_PRUNING) ||
            (update->flags & REF_UPDATE_VIA_HEAD))
                return 0;
  
  
        new_update = ref_transaction_add_update(
                        transaction, "HEAD",
-                       update->flags | REF_LOG_ONLY | REF_NODEREF,
+                       update->flags | REF_LOG_ONLY | REF_NO_DEREF,
                        &update->new_oid, &update->old_oid,
                        update->msg);
  
  
  /*
   * update is for a symref that points at referent and doesn't have
-  * REF_NODEREF set. Split it into two updates:
-  * - The original update, but with REF_LOG_ONLY and REF_NODEREF set
+  * REF_NO_DEREF set. Split it into two updates:
+  * - The original update, but with REF_LOG_ONLY and REF_NO_DEREF set
   * - A new, separate update for the referent reference
   * Note that the new update will itself be subject to splitting when
   * the iteration gets to it.
@@@ -2220,10 -2271,10 +2271,10 @@@ static int split_symref_update(struct f
  
        /*
         * Change the symbolic ref update to log only. Also, it
-        * doesn't need to check its old SHA-1 value, as that will be
+        * doesn't need to check its old OID value, as that will be
         * done when new_update is processed.
         */
-       update->flags |= REF_LOG_ONLY | REF_NODEREF;
+       update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
        update->flags &= ~REF_HAVE_OLD;
  
        /*
@@@ -2289,10 -2340,10 +2340,10 @@@ static int check_old_oid(struct ref_upd
   * Prepare for carrying out update:
   * - Lock the reference referred to by update.
   * - Read the reference under lock.
-  * - Check that its old SHA-1 value (if specified) is correct, and in
+  * - Check that its old OID value (if specified) is correct, and in
   *   any case record it in update->lock->old_oid for later use when
   *   writing the reflog.
-  * - If it is a symref update without REF_NODEREF, split it up into a
+  * - If it is a symref update without REF_NO_DEREF, split it up into a
   *   REF_LOG_ONLY update of the symref and add a separate update for
   *   the referent to transaction.
   * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
@@@ -2340,11 -2391,11 +2391,11 @@@ static int lock_ref_for_update(struct f
        update->backend_data = lock;
  
        if (update->type & REF_ISSYMREF) {
-               if (update->flags & REF_NODEREF) {
+               if (update->flags & REF_NO_DEREF) {
                        /*
                         * We won't be reading the referent as part of
                         * the transaction, so we have to read it here
-                        * to record and possibly check old_sha1:
+                        * to record and possibly check old_oid:
                         */
                        if (refs_read_ref_full(&refs->base,
                                               referent.buf, 0,
                        /*
                         * Create a new update for the reference this
                         * symref is pointing at. Also, we will record
-                        * and verify old_sha1 for this update as part
+                        * and verify old_oid for this update as part
                         * of processing the split-off update, so we
                         * don't have to do it here.
                         */
  
                /*
                 * If this update is happening indirectly because of a
-                * symref update, record the old SHA-1 in the parent
+                * symref update, record the old OID in the parent
                 * update:
                 */
                for (parent_update = update->parent_update;
@@@ -2511,13 -2562,18 +2562,18 @@@ static int files_transaction_prepare(st
         * transaction. (If we end up splitting up any updates using
         * split_symref_update() or split_head_update(), those
         * functions will check that the new updates don't have the
-        * same refname as any existing ones.)
+        * same refname as any existing ones.) Also fail if any of the
+        * updates use REF_IS_PRUNING without REF_NO_DEREF.
         */
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
                struct string_list_item *item =
                        string_list_append(&affected_refnames, update->refname);
  
+               if ((update->flags & REF_IS_PRUNING) &&
+                   !(update->flags & REF_NO_DEREF))
+                       BUG("REF_IS_PRUNING set without REF_NO_DEREF");
                /*
                 * We store a pointer to update in item->util, but at
                 * the moment we never use the value of this field
  
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY) &&
-                   !(update->flags & REF_ISPRUNING)) {
+                   !(update->flags & REF_IS_PRUNING)) {
                        /*
                         * This reference has to be deleted from
                         * packed-refs if it exists there.
  
                        ref_transaction_add_update(
                                        packed_transaction, update->refname,
-                                       update->flags & ~REF_HAVE_OLD,
-                                       &update->new_oid, &update->old_oid,
+                                       REF_HAVE_NEW | REF_NO_DEREF,
+                                       &update->new_oid, NULL,
                                        NULL);
                }
        }
                        goto cleanup;
                }
                backend_data->packed_refs_locked = 1;
 -              ret = ref_transaction_prepare(packed_transaction, err);
 +
 +              if (is_packed_transaction_needed(refs->packed_ref_store,
 +                                               packed_transaction)) {
 +                      ret = ref_transaction_prepare(packed_transaction, err);
 +              } else {
 +                      /*
 +                       * We can skip rewriting the `packed-refs`
 +                       * file. But we do need to leave it locked, so
 +                       * that somebody else doesn't pack a reference
 +                       * that we are trying to delete.
 +                       */
 +                      if (ref_transaction_abort(packed_transaction, err)) {
 +                              ret = TRANSACTION_GENERIC_ERROR;
 +                              goto cleanup;
 +                      }
 +                      backend_data->packed_transaction = NULL;
 +              }
        }
  
  cleanup:
@@@ -2708,7 -2748,7 +2764,7 @@@ static int files_transaction_finish(str
                struct ref_update *update = transaction->updates[i];
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY) &&
-                   !(update->flags & REF_ISPRUNING)) {
+                   !(update->flags & REF_IS_PRUNING)) {
                        strbuf_reset(&sb);
                        files_reflog_path(refs, &sb, update->refname);
                        if (!unlink_or_warn(sb.buf))
@@@ -2954,7 -2994,7 +3010,7 @@@ static int files_reflog_expire(struct r
         * reference if --updateref was specified:
         */
        lock = lock_ref_oid_basic(refs, refname, oid,
-                                 NULL, NULL, REF_NODEREF,
+                                 NULL, NULL, REF_NO_DEREF,
                                  &type, &err);
        if (!lock) {
                error("cannot lock ref '%s': %s", refname, err.buf);
diff --combined refs/packed-backend.c
index 6650aac1e882f6c7bd0af4f76c2df6aceff66c54,72164a1d6485802fad03c6e0e5b532221be38fa9..023243fd5f1833f3c5f0b6fd3cd82b2e0c69644e
@@@ -744,7 -744,7 +744,7 @@@ static int packed_read_raw_ref(struct r
  /*
   * This value is set in `base.flags` if the peeled value of the
   * current reference is known. In that case, `peeled` contains the
-  * correct peeled value for the reference, which might be `null_sha1`
+  * correct peeled value for the reference, which might be `null_oid`
   * if the reference is not a tag or if it is broken.
   */
  #define REF_KNOWS_PEELED 0x40
@@@ -961,11 -961,11 +961,11 @@@ static struct ref_iterator *packed_ref_
   * by the failing call to `fprintf()`.
   */
  static int write_packed_entry(FILE *fh, const char *refname,
-                             const unsigned char *sha1,
-                             const unsigned char *peeled)
+                             const struct object_id *oid,
+                             const struct object_id *peeled)
  {
-       if (fprintf(fh, "%s %s\n", sha1_to_hex(sha1), refname) < 0 ||
-           (peeled && fprintf(fh, "^%s\n", sha1_to_hex(peeled)) < 0))
+       if (fprintf(fh, "%s %s\n", oid_to_hex(oid), refname) < 0 ||
+           (peeled && fprintf(fh, "^%s\n", oid_to_hex(peeled)) < 0))
                return -1;
  
        return 0;
@@@ -1203,8 -1203,8 +1203,8 @@@ static int write_with_updates(struct pa
                        int peel_error = ref_iterator_peel(iter, &peeled);
  
                        if (write_packed_entry(out, iter->refname,
-                                              iter->oid->hash,
-                                              peel_error ? NULL : peeled.hash))
+                                              iter->oid,
+                                              peel_error ? NULL : &peeled))
                                goto write_error;
  
                        if ((ok = ref_iterator_advance(iter)) != ITER_OK)
                                                     &peeled);
  
                        if (write_packed_entry(out, update->refname,
-                                              update->new_oid.hash,
-                                              peel_error ? NULL : peeled.hash))
+                                              &update->new_oid,
+                                              peel_error ? NULL : &peeled))
                                goto write_error;
  
                        i++;
@@@ -1261,100 -1261,6 +1261,100 @@@ error
        return -1;
  }
  
 +int is_packed_transaction_needed(struct ref_store *ref_store,
 +                               struct ref_transaction *transaction)
 +{
 +      struct packed_ref_store *refs = packed_downcast(
 +                      ref_store,
 +                      REF_STORE_READ,
 +                      "is_packed_transaction_needed");
 +      struct strbuf referent = STRBUF_INIT;
 +      size_t i;
 +      int ret;
 +
 +      if (!is_lock_file_locked(&refs->lock))
 +              BUG("is_packed_transaction_needed() called while unlocked");
 +
 +      /*
 +       * We're only going to bother returning false for the common,
 +       * trivial case that references are only being deleted, their
 +       * old values are not being checked, and the old `packed-refs`
 +       * file doesn't contain any of those reference(s). This gives
 +       * false positives for some other cases that could
 +       * theoretically be optimized away:
 +       *
 +       * 1. It could be that the old value is being verified without
 +       *    setting a new value. In this case, we could verify the
 +       *    old value here and skip the update if it agrees. If it
 +       *    disagrees, we could either let the update go through
 +       *    (the actual commit would re-detect and report the
 +       *    problem), or come up with a way of reporting such an
 +       *    error to *our* caller.
 +       *
 +       * 2. It could be that a new value is being set, but that it
 +       *    is identical to the current packed value of the
 +       *    reference.
 +       *
 +       * Neither of these cases will come up in the current code,
 +       * because the only caller of this function passes to it a
 +       * transaction that only includes `delete` updates with no
 +       * `old_id`. Even if that ever changes, false positives only
 +       * cause an optimization to be missed; they do not affect
 +       * correctness.
 +       */
 +
 +      /*
 +       * Start with the cheap checks that don't require old
 +       * reference values to be read:
 +       */
 +      for (i = 0; i < transaction->nr; i++) {
 +              struct ref_update *update = transaction->updates[i];
 +
 +              if (update->flags & REF_HAVE_OLD)
 +                      /* Have to check the old value -> needed. */
 +                      return 1;
 +
 +              if ((update->flags & REF_HAVE_NEW) && !is_null_oid(&update->new_oid))
 +                      /* Have to set a new value -> needed. */
 +                      return 1;
 +      }
 +
 +      /*
 +       * The transaction isn't checking any old values nor is it
 +       * setting any nonzero new values, so it still might be able
 +       * to be skipped. Now do the more expensive check: the update
 +       * is needed if any of the updates is a delete, and the old
 +       * `packed-refs` file contains a value for that reference.
 +       */
 +      ret = 0;
 +      for (i = 0; i < transaction->nr; i++) {
 +              struct ref_update *update = transaction->updates[i];
 +              unsigned int type;
 +              struct object_id oid;
 +
 +              if (!(update->flags & REF_HAVE_NEW))
 +                      /*
 +                       * This reference isn't being deleted -> not
 +                       * needed.
 +                       */
 +                      continue;
 +
 +              if (!refs_read_raw_ref(ref_store, update->refname,
 +                                     &oid, &referent, &type) ||
 +                  errno != ENOENT) {
 +                      /*
 +                       * We have to actually delete that reference
 +                       * -> this transaction is needed.
 +                       */
 +                      ret = 1;
 +                      break;
 +              }
 +      }
 +
 +      strbuf_release(&referent);
 +      return ret;
 +}
 +
  struct packed_transaction_backend_data {
        /* True iff the transaction owns the packed-refs lock. */
        int own_lock;
diff --combined sequencer.c
index cfc1263912dc92409f23ed5f9cc6a4ec50cdee2f,3b88ab2f9cb4d89bfedda3bb35ed5bd14fcb4385..19dd575ed9b3b280a3fdabc9121a2e193d6984db
@@@ -959,8 -959,7 +959,8 @@@ static int do_pick_commit(enum todo_com
                unborn = get_oid("HEAD", &head);
                if (unborn)
                        oidcpy(&head, &empty_tree_oid);
 -              if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0))
 +              if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD",
 +                                     NULL, 0))
                        return error_dirty_index(opts);
        }
        discard_cache();
         */
        if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
            update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
-                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
+                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
        if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
            update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
-                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
+                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
  
        if (res) {
@@@ -1185,6 -1184,7 +1185,6 @@@ static int read_and_refresh_cache(struc
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
        if (the_index.cache_changed && index_fd >= 0) {
                if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
 -                      rollback_lock_file(&index_lock);
                        return error(_("git %s: failed to refresh the index"),
                                _(action_name(opts)));
                }
@@@ -1862,15 -1862,12 +1862,15 @@@ static int error_failed_squash(struct c
  
  static int do_exec(const char *command_line)
  {
 +      struct argv_array child_env = ARGV_ARRAY_INIT;
        const char *child_argv[] = { NULL, NULL };
        int dirty, status;
  
        fprintf(stderr, "Executing: %s\n", command_line);
        child_argv[0] = command_line;
 -      status = run_command_v_opt(child_argv, RUN_USING_SHELL);
 +      argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
 +      status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
 +                                        child_env.argv);
  
        /* force re-reading of the cache */
        if (discard_cache() < 0 || read_cache() < 0)
                status = 1;
        }
  
 +      argv_array_clear(&child_env);
 +
        return status;
  }
  
@@@ -2130,7 -2125,7 +2130,7 @@@ cleanup_head_ref
                        msg = reflog_message(opts, "finish", "%s onto %s",
                                head_ref.buf, buf.buf);
                        if (update_ref(msg, head_ref.buf, &head, &orig,
-                                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {
+                                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
                                res = error(_("could not update %s"),
                                        head_ref.buf);
                                goto cleanup_head_ref;
@@@ -2284,7 -2279,7 +2284,7 @@@ int sequencer_continue(struct replay_op
                        if (res)
                                goto release_todo_list;
                }
 -              if (index_differs_from("HEAD", 0, 0)) {
 +              if (index_differs_from("HEAD", NULL, 0)) {
                        res = error_dirty_index(opts);
                        goto release_todo_list;
                }
@@@ -2670,19 -2665,6 +2670,19 @@@ leave_check
        return res;
  }
  
 +static int rewrite_file(const char *path, const char *buf, size_t len)
 +{
 +      int rc = 0;
 +      int fd = open(path, O_WRONLY | O_TRUNC);
 +      if (fd < 0)
 +              return error_errno(_("could not open '%s' for writing"), path);
 +      if (write_in_full(fd, buf, len) < 0)
 +              rc = error_errno(_("could not write to '%s'"), path);
 +      if (close(fd) && !rc)
 +              rc = error_errno(_("could not close '%s'"), path);
 +      return rc;
 +}
 +
  /* skip picking commits whose parents are unchanged */
  int skip_unnecessary_picks(void)
  {
                }
                close(fd);
  
 -              fd = open(rebase_path_todo(), O_WRONLY, 0666);
 -              if (fd < 0) {
 -                      error_errno(_("could not open '%s' for writing"),
 -                                  rebase_path_todo());
 +              if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
 +                               todo_list.buf.len - offset) < 0) {
                        todo_list_release(&todo_list);
                        return -1;
                }
 -              if (write_in_full(fd, todo_list.buf.buf + offset,
 -                              todo_list.buf.len - offset) < 0) {
 -                      error_errno(_("could not write to '%s'"),
 -                                  rebase_path_todo());
 -                      close(fd);
 -                      todo_list_release(&todo_list);
 -                      return -1;
 -              }
 -              if (ftruncate(fd, todo_list.buf.len - offset) < 0) {
 -                      error_errno(_("could not truncate '%s'"),
 -                                  rebase_path_todo());
 -                      todo_list_release(&todo_list);
 -                      close(fd);
 -                      return -1;
 -              }
 -              close(fd);
  
                todo_list.current = i;
                if (is_fixup(peek_command(&todo_list, 0)))
@@@ -2944,7 -2944,15 +2944,7 @@@ int rearrange_squash(void
                        }
                }
  
 -              fd = open(todo_file, O_WRONLY);
 -              if (fd < 0)
 -                      res = error_errno(_("could not open '%s'"), todo_file);
 -              else if (write(fd, buf.buf, buf.len) < 0)
 -                      res = error_errno(_("could not write to '%s'"), todo_file);
 -              else if (ftruncate(fd, buf.len) < 0)
 -                      res = error_errno(_("could not truncate '%s'"),
 -                                         todo_file);
 -              close(fd);
 +              res = rewrite_file(todo_file, buf.buf, buf.len);
                strbuf_release(&buf);
        }