Merge branch 'bc/object-id'
authorJunio C Hamano <gitster@pobox.com>
Thu, 20 Apr 2017 04:37:13 +0000 (21:37 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 20 Apr 2017 04:37:13 +0000 (21:37 -0700)
Conversion from unsigned char [40] to struct object_id continues.

* bc/object-id:
Documentation: update and rename api-sha1-array.txt
Rename sha1_array to oid_array
Convert sha1_array_for_each_unique and for_each_abbrev to object_id
Convert sha1_array_lookup to take struct object_id
Convert remaining callers of sha1_array_lookup to object_id
Make sha1_array_append take a struct object_id *
sha1-array: convert internal storage for struct sha1_array to object_id
builtin/pull: convert to struct object_id
submodule: convert check_for_new_submodule_commits to object_id
sha1_name: convert disambiguate_hint_fn to take object_id
sha1_name: convert struct disambiguate_state to object_id
test-sha1-array: convert most code to struct object_id
parse-options-cb: convert sha1_array_append caller to struct object_id
fsck: convert init_skiplist to struct object_id
builtin/receive-pack: convert portions to struct object_id
builtin/pull: convert portions to struct object_id
builtin/diff: convert to struct object_id
Convert GIT_SHA1_RAWSZ used for allocation to GIT_MAX_RAWSZ
Convert GIT_SHA1_HEXSZ used for allocation to GIT_MAX_HEXSZ
Define new hash-size constants for allocating memory

19 files changed:
1  2 
bisect.c
builtin/fetch.c
builtin/merge.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/rev-parse.c
builtin/send-pack.c
cache.h
combine-diff.c
diff.c
ref-filter.c
ref-filter.h
remote-curl.c
remote.h
send-pack.c
sha1_file.c
sha1_name.c
submodule.c
submodule.h
diff --combined bisect.c
index d12583eaac26edb865f22f98f54dc6495b7f994e,54d69e77b95d211995690d1edaac11507cb23daa..03af06c66cebcbc0699b8a074f9ef120da09248a
+++ b/bisect.c
@@@ -12,8 -12,8 +12,8 @@@
  #include "sha1-array.h"
  #include "argv-array.h"
  
- static struct sha1_array good_revs;
- static struct sha1_array skipped_revs;
+ static struct oid_array good_revs;
+ static struct oid_array skipped_revs;
  
  static struct object_id *current_bad_oid;
  
@@@ -200,7 -200,6 +200,7 @@@ static struct commit_list *best_bisecti
  {
        struct commit_list *p;
        struct commit_dist *array = xcalloc(nr, sizeof(*array));
 +      struct strbuf buf = STRBUF_INIT;
        int cnt, i;
  
        for (p = list, cnt = 0; p; p = p->next) {
        }
        QSORT(array, cnt, compare_commit_dist);
        for (p = list, i = 0; i < cnt; i++) {
 -              char buf[100]; /* enough for dist=%d */
                struct object *obj = &(array[i].commit->object);
  
 -              snprintf(buf, sizeof(buf), "dist=%d", array[i].distance);
 -              add_name_decoration(DECORATION_NONE, buf, obj);
 +              strbuf_reset(&buf);
 +              strbuf_addf(&buf, "dist=%d", array[i].distance);
 +              add_name_decoration(DECORATION_NONE, buf.buf, obj);
  
                p->item = array[i].commit;
                p = p->next;
        }
        if (p)
                p->next = NULL;
 +      strbuf_release(&buf);
        free(array);
        return list;
  }
@@@ -415,9 -413,9 +415,9 @@@ static int register_ref(const char *ref
                current_bad_oid = xmalloc(sizeof(*current_bad_oid));
                oidcpy(current_bad_oid, oid);
        } else if (starts_with(refname, good_prefix.buf)) {
-               sha1_array_append(&good_revs, oid->hash);
+               oid_array_append(&good_revs, oid);
        } else if (starts_with(refname, "skip-")) {
-               sha1_array_append(&skipped_revs, oid->hash);
+               oid_array_append(&skipped_revs, oid);
        }
  
        strbuf_release(&good_prefix);
@@@ -453,13 -451,13 +453,13 @@@ static void read_bisect_paths(struct ar
        fclose(fp);
  }
  
- static char *join_sha1_array_hex(struct sha1_array *array, char delim)
+ static char *join_sha1_array_hex(struct oid_array *array, char delim)
  {
        struct strbuf joined_hexs = STRBUF_INIT;
        int i;
  
        for (i = 0; i < array->nr; i++) {
-               strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
+               strbuf_addstr(&joined_hexs, oid_to_hex(array->oid + i));
                if (i + 1 < array->nr)
                        strbuf_addch(&joined_hexs, delim);
        }
@@@ -501,8 -499,7 +501,7 @@@ struct commit_list *filter_skipped(stru
        while (list) {
                struct commit_list *next = list->next;
                list->next = NULL;
-               if (0 <= sha1_array_lookup(&skipped_revs,
-                                          list->item->object.oid.hash)) {
+               if (0 <= oid_array_lookup(&skipped_revs, &list->item->object.oid)) {
                        if (skipped_first && !*skipped_first)
                                *skipped_first = 1;
                        /* Move current to tried list */
@@@ -623,7 -620,7 +622,7 @@@ static void bisect_rev_setup(struct rev
        argv_array_pushf(&rev_argv, bad_format, oid_to_hex(current_bad_oid));
        for (i = 0; i < good_revs.nr; i++)
                argv_array_pushf(&rev_argv, good_format,
-                                sha1_to_hex(good_revs.sha1[i]));
+                                oid_to_hex(good_revs.oid + i));
        argv_array_push(&rev_argv, "--");
        if (read_paths)
                read_bisect_paths(&rev_argv);
@@@ -684,7 -681,7 +683,7 @@@ static int is_expected_rev(const struc
  
  static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
  {
-       char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
+       char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
  
        memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
        update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
        return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
  }
  
- static struct commit *get_commit_reference(const unsigned char *sha1)
+ static struct commit *get_commit_reference(const struct object_id *oid)
  {
-       struct commit *r = lookup_commit_reference(sha1);
+       struct commit *r = lookup_commit_reference(oid->hash);
        if (!r)
-               die(_("Not a valid commit name %s"), sha1_to_hex(sha1));
+               die(_("Not a valid commit name %s"), oid_to_hex(oid));
        return r;
  }
  
@@@ -717,9 -714,9 +716,9 @@@ static struct commit **get_bad_and_good
        int i, n = 0;
  
        ALLOC_ARRAY(rev, 1 + good_revs.nr);
-       rev[n++] = get_commit_reference(current_bad_oid->hash);
+       rev[n++] = get_commit_reference(current_bad_oid);
        for (i = 0; i < good_revs.nr; i++)
-               rev[n++] = get_commit_reference(good_revs.sha1[i]);
+               rev[n++] = get_commit_reference(good_revs.oid + i);
        *rev_nr = n;
  
        return rev;
@@@ -756,9 -753,9 +755,9 @@@ static void handle_bad_merge_base(void
        exit(1);
  }
  
- static void handle_skipped_merge_base(const unsigned char *mb)
+ static void handle_skipped_merge_base(const struct object_id *mb)
  {
-       char *mb_hex = sha1_to_hex(mb);
+       char *mb_hex = oid_to_hex(mb);
        char *bad_hex = oid_to_hex(current_bad_oid);
        char *good_hex = join_sha1_array_hex(&good_revs, ' ');
  
@@@ -789,16 -786,16 +788,16 @@@ static void check_merge_bases(int no_ch
        result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
  
        for (; result; result = result->next) {
-               const unsigned char *mb = result->item->object.oid.hash;
-               if (!hashcmp(mb, current_bad_oid->hash)) {
+               const struct object_id *mb = &result->item->object.oid;
+               if (!oidcmp(mb, current_bad_oid)) {
                        handle_bad_merge_base();
-               } else if (0 <= sha1_array_lookup(&good_revs, mb)) {
+               } else if (0 <= oid_array_lookup(&good_revs, mb)) {
                        continue;
-               } else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
+               } else if (0 <= oid_array_lookup(&skipped_revs, mb)) {
                        handle_skipped_merge_base(mb);
                } else {
                        printf(_("Bisecting: a merge base must be tested\n"));
-                       exit(bisect_checkout(mb, no_checkout));
+                       exit(bisect_checkout(mb->hash, no_checkout));
                }
        }
  
diff --combined builtin/fetch.c
index 4ef7a08afc79a02dfb49a54065544666d1f6b6da,a41b892dcc4822acdcb0aead6513d2ce554f5cb1..5f2c2ab23e4cde17747147f23dcedaebe66c74a3
@@@ -421,7 -421,7 +421,7 @@@ static int s_update_ref(const char *act
                        struct ref *ref,
                        int check_old)
  {
 -      char msg[1024];
 +      char *msg;
        char *rla = getenv("GIT_REFLOG_ACTION");
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
                return 0;
        if (!rla)
                rla = default_rla.buf;
 -      snprintf(msg, sizeof(msg), "%s: %s", rla, action);
 +      msg = xstrfmt("%s: %s", rla, action);
  
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
  
        ref_transaction_free(transaction);
        strbuf_release(&err);
 +      free(msg);
        return 0;
  fail:
        ref_transaction_free(transaction);
        error("%s", err.buf);
        strbuf_release(&err);
 +      free(msg);
        return df_conflict ? STORE_REF_ERROR_DF_CONFLICT
                           : STORE_REF_ERROR_OTHER;
  }
@@@ -661,7 -659,7 +661,7 @@@ static int update_local_ref(struct ref 
  
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(ref->new_oid.hash);
+                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref(msg, ref, 0);
                format_display(display, r ? '!' : '*', what,
                               r ? _("unable to update local ref") : NULL,
                strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(ref->new_oid.hash);
+                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("fast-forward", ref, 1);
                format_display(display, r ? '!' : ' ', quickref.buf,
                               r ? _("unable to update local ref") : NULL,
                strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(ref->new_oid.hash);
+                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("forced-update", ref, 1);
                format_display(display, r ? '!' : '+', quickref.buf,
                               r ? _("unable to update local ref") : _("forced update"),
diff --combined builtin/merge.c
index 95572b18108d6c51391c6527359657cd37329bd6,a2cceea3fb0f45372cc113eeb335756b44c9eed0..703827f00668ca0df807f35e300f0927d00075cd
@@@ -44,6 -44,7 +44,6 @@@ struct strategy 
  
  static const char * const builtin_merge_usage[] = {
        N_("git merge [<options>] [<commit>...]"),
 -      N_("git merge [<options>] <msg> HEAD <commit>"),
        N_("git merge --abort"),
        N_("git merge --continue"),
        NULL
@@@ -633,10 -634,9 +633,10 @@@ static void write_tree_trivial(struct o
  
  static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              struct commit_list *remoteheads,
 -                            struct commit *head, const char *head_arg)
 +                            struct commit *head)
  {
        static struct lock_file lock;
 +      const char *head_arg = "HEAD";
  
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
@@@ -853,6 -853,24 +853,6 @@@ static int suggest_conflicts(void
        return 1;
  }
  
 -static struct commit *is_old_style_invocation(int argc, const char **argv,
 -                                            const struct object_id *head)
 -{
 -      struct commit *second_token = NULL;
 -      if (argc > 2) {
 -              struct object_id second_oid;
 -
 -              if (get_oid(argv[1], &second_oid))
 -                      return NULL;
 -              second_token = lookup_commit_reference_gently(second_oid.hash, 0);
 -              if (!second_token)
 -                      die(_("'%s' is not a commit"), argv[1]);
 -              if (oidcmp(&second_token->object.oid, head))
 -                      return NULL;
 -      }
 -      return second_token;
 -}
 -
  static int evaluate_result(void)
  {
        int cnt = 0;
@@@ -1102,6 -1120,7 +1102,6 @@@ int cmd_merge(int argc, const char **ar
        struct object_id result_tree, stash, head_oid;
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
 -      const char *head_arg;
        int i, ret = 0, head_subsumed;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
        }
  
        /*
 -       * This could be traditional "merge <msg> HEAD <commit>..."  and
 -       * the way we can tell it is to see if the second token is HEAD,
 -       * but some people might have misused the interface and used a
 -       * commit-ish that is the same as HEAD there instead.
 -       * Traditional format never would have "-m" so it is an
 -       * additional safety measure to check for it.
 +       * All the rest are the commits being merged; prepare
 +       * the standard merge summary message to be appended
 +       * to the given message.
         */
 -      if (!have_message &&
 -          is_old_style_invocation(argc, argv, &head_commit->object.oid)) {
 -              warning("old-style 'git merge <msg> HEAD <commit>' is deprecated.");
 -              strbuf_addstr(&merge_msg, argv[0]);
 -              head_arg = argv[1];
 -              argv += 2;
 -              argc -= 2;
 -              remoteheads = collect_parents(head_commit, &head_subsumed,
 -                                            argc, argv, NULL);
 -      } else {
 -              /* We are invoked directly as the first-class UI. */
 -              head_arg = "HEAD";
 -
 -              /*
 -               * All the rest are the commits being merged; prepare
 -               * the standard merge summary message to be appended
 -               * to the given message.
 -               */
 -              remoteheads = collect_parents(head_commit, &head_subsumed,
 -                                            argc, argv, &merge_msg);
 -      }
 +      remoteheads = collect_parents(head_commit, &head_subsumed,
 +                                    argc, argv, &merge_msg);
  
        if (!head_commit || !argc)
                usage_with_options(builtin_merge_usage,
        if (verify_signatures) {
                for (p = remoteheads; p; p = p->next) {
                        struct commit *commit = p->item;
-                       char hex[GIT_SHA1_HEXSZ + 1];
+                       char hex[GIT_MAX_HEXSZ + 1];
                        struct signature_check signature_check;
                        memset(&signature_check, 0, sizeof(signature_check));
  
  
                ret = try_merge_strategy(use_strategies[i]->name,
                                         common, remoteheads,
 -                                       head_commit, head_arg);
 +                                       head_commit);
                if (!option_commit && !ret) {
                        merge_was_ok = 1;
                        /*
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
                try_merge_strategy(best_strategy, common, remoteheads,
 -                                 head_commit, head_arg);
 +                                 head_commit);
        }
  
        if (squash)
diff --combined builtin/pack-objects.c
index 84af7c2324bad29dd6522321328921d3afde7d57,4fc032e3ec2e4f6c8914e6a1f6c1599a3de7ee7d..0fe35d1b5aebd74116d66d5414bdabf19baf7d97
@@@ -239,8 -239,7 +239,8 @@@ static unsigned long write_no_reuse_obj
                                           unsigned long limit, int usable_delta)
  {
        unsigned long size, datalen;
 -      unsigned char header[10], dheader[10];
 +      unsigned char header[MAX_PACK_OBJECT_HEADER],
 +                    dheader[MAX_PACK_OBJECT_HEADER];
        unsigned hdrlen;
        enum object_type type;
        void *buf;
         * The object header is a byte of 'type' followed by zero or
         * more bytes of length.
         */
 -      hdrlen = encode_in_pack_object_header(type, size, header);
 +      hdrlen = encode_in_pack_object_header(header, sizeof(header),
 +                                            type, size);
  
        if (type == OBJ_OFS_DELTA) {
                /*
@@@ -354,15 -352,13 +354,15 @@@ static off_t write_reuse_object(struct 
        off_t offset;
        enum object_type type = entry->type;
        off_t datalen;
 -      unsigned char header[10], dheader[10];
 +      unsigned char header[MAX_PACK_OBJECT_HEADER],
 +                    dheader[MAX_PACK_OBJECT_HEADER];
        unsigned hdrlen;
  
        if (entry->delta)
                type = (allow_ofs_delta && entry->delta->idx.offset) ?
                        OBJ_OFS_DELTA : OBJ_REF_DELTA;
 -      hdrlen = encode_in_pack_object_header(type, entry->size, header);
 +      hdrlen = encode_in_pack_object_header(header, sizeof(header),
 +                                            type, entry->size);
  
        offset = entry->in_pack_offset;
        revidx = find_pack_revindex(p, offset);
@@@ -2672,16 -2668,16 +2672,16 @@@ static int has_sha1_pack_kept_or_nonloc
   *
   * This is filled by get_object_list.
   */
- static struct sha1_array recent_objects;
+ static struct oid_array recent_objects;
  
- static int loosened_object_can_be_discarded(const unsigned char *sha1,
+ static int loosened_object_can_be_discarded(const struct object_id *oid,
                                            unsigned long mtime)
  {
        if (!unpack_unreachable_expiration)
                return 0;
        if (mtime > unpack_unreachable_expiration)
                return 0;
-       if (sha1_array_lookup(&recent_objects, sha1) >= 0)
+       if (oid_array_lookup(&recent_objects, oid) >= 0)
                return 0;
        return 1;
  }
@@@ -2690,7 -2686,7 +2690,7 @@@ static void loosen_unused_packed_object
  {
        struct packed_git *p;
        uint32_t i;
-       const unsigned char *sha1;
+       struct object_id oid;
  
        for (p = packed_git; p; p = p->next) {
                if (!p->pack_local || p->pack_keep)
                        die("cannot open pack index");
  
                for (i = 0; i < p->num_objects; i++) {
-                       sha1 = nth_packed_object_sha1(p, i);
-                       if (!packlist_find(&to_pack, sha1, NULL) &&
-                           !has_sha1_pack_kept_or_nonlocal(sha1) &&
-                           !loosened_object_can_be_discarded(sha1, p->mtime))
-                               if (force_object_loose(sha1, p->mtime))
+                       nth_packed_object_oid(&oid, p, i);
+                       if (!packlist_find(&to_pack, oid.hash, NULL) &&
+                           !has_sha1_pack_kept_or_nonlocal(oid.hash) &&
+                           !loosened_object_can_be_discarded(&oid, p->mtime))
+                               if (force_object_loose(oid.hash, p->mtime))
                                        die("unable to force loose object");
                }
        }
@@@ -2743,12 -2739,12 +2743,12 @@@ static void record_recent_object(struc
                                 const char *name,
                                 void *data)
  {
-       sha1_array_append(&recent_objects, obj->oid.hash);
+       oid_array_append(&recent_objects, &obj->oid);
  }
  
  static void record_recent_commit(struct commit *commit, void *data)
  {
-       sha1_array_append(&recent_objects, commit->object.oid.hash);
+       oid_array_append(&recent_objects, &commit->object.oid);
  }
  
  static void get_object_list(int ac, const char **av)
        if (unpack_unreachable)
                loosen_unused_packed_objects(&revs);
  
-       sha1_array_clear(&recent_objects);
+       oid_array_clear(&recent_objects);
  }
  
  static int option_parse_index_version(const struct option *opt,
diff --combined builtin/receive-pack.c
index 70202780819130ad56ea7391c319ed810d80b2a4,bec46a5a92fa390222e60df451b632fb401d583b..3cba3fd278fb05f86ec2e23483261d22bc4c04cd
@@@ -225,10 -225,10 +225,10 @@@ static int receive_pack_config(const ch
        return git_default_config(var, value, cb);
  }
  
- static void show_ref(const char *path, const unsigned char *sha1)
+ static void show_ref(const char *path, const struct object_id *oid)
  {
        if (sent_capabilities) {
-               packet_write_fmt(1, "%s %s\n", sha1_to_hex(sha1), path);
+               packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), path);
        } else {
                struct strbuf cap = STRBUF_INIT;
  
                        strbuf_addstr(&cap, " push-options");
                strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
                packet_write_fmt(1, "%s %s%c%s\n",
-                            sha1_to_hex(sha1), path, 0, cap.buf);
+                            oid_to_hex(oid), path, 0, cap.buf);
                strbuf_release(&cap);
                sent_capabilities = 1;
        }
@@@ -271,7 -271,7 +271,7 @@@ static int show_ref_cb(const char *path
        } else {
                oidset_insert(seen, oid);
        }
-       show_ref(path, oid->hash);
+       show_ref(path, oid);
        return 0;
  }
  
@@@ -284,7 -284,7 +284,7 @@@ static void show_one_alternate_ref(cons
        if (oidset_insert(seen, oid))
                return;
  
-       show_ref(".have", oid->hash);
+       show_ref(".have", oid);
  }
  
  static void write_head_info(void)
        for_each_alternate_ref(show_one_alternate_ref, &seen);
        oidset_clear(&seen);
        if (!sent_capabilities)
-               show_ref("capabilities^{}", null_sha1);
+               show_ref("capabilities^{}", &null_oid);
  
        advertise_shallow_grafts(1);
  
@@@ -309,8 -309,8 +309,8 @@@ struct command 
        unsigned int skip_update:1,
                     did_not_exist:1;
        int index;
-       unsigned char old_sha1[20];
-       unsigned char new_sha1[20];
+       struct object_id old_oid;
+       struct object_id new_oid;
        char ref_name[FLEX_ARRAY]; /* more */
  };
  
@@@ -723,7 -723,7 +723,7 @@@ static int feed_receive_hook(void *stat
                return -1; /* EOF */
        strbuf_reset(&state->buf);
        strbuf_addf(&state->buf, "%s %s %s\n",
-                   sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
+                   oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
                    cmd->ref_name);
        state->cmd = cmd->next;
        if (bufp) {
@@@ -764,8 -764,8 +764,8 @@@ static int run_update_hook(struct comma
                return 0;
  
        argv[1] = cmd->ref_name;
-       argv[2] = sha1_to_hex(cmd->old_sha1);
-       argv[3] = sha1_to_hex(cmd->new_sha1);
+       argv[2] = oid_to_hex(&cmd->old_oid);
+       argv[3] = oid_to_hex(&cmd->new_oid);
        argv[4] = NULL;
  
        proc.no_stdin = 1;
@@@ -831,7 -831,7 +831,7 @@@ static int command_singleton_iterator(v
  static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
  {
        static struct lock_file shallow_lock;
-       struct sha1_array extra = SHA1_ARRAY_INIT;
+       struct oid_array extra = OID_ARRAY_INIT;
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        uint32_t mask = 1 << (cmd->index % 32);
        int i;
                if (si->used_shallow[i] &&
                    (si->used_shallow[i][cmd->index / 32] & mask) &&
                    !delayed_reachability_test(si, i))
-                       sha1_array_append(&extra, si->shallow->sha1[i]);
+                       oid_array_append(&extra, &si->shallow->oid[i]);
  
        opt.env = tmp_objdir_env(tmp_objdir);
        setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
        if (check_connected(command_singleton_iterator, cmd, &opt)) {
                rollback_lock_file(&shallow_lock);
-               sha1_array_clear(&extra);
+               oid_array_clear(&extra);
                return -1;
        }
  
         * not lose these new roots..
         */
        for (i = 0; i < extra.nr; i++)
-               register_shallow(extra.sha1[i]);
+               register_shallow(extra.oid[i].hash);
  
        si->shallow_ref[cmd->index] = 0;
-       sha1_array_clear(&extra);
+       oid_array_clear(&extra);
        return 0;
  }
  
@@@ -988,8 -988,8 +988,8 @@@ static const char *update(struct comman
        const char *name = cmd->ref_name;
        struct strbuf namespaced_name_buf = STRBUF_INIT;
        const char *namespaced_name, *ret;
-       unsigned char *old_sha1 = cmd->old_sha1;
-       unsigned char *new_sha1 = cmd->new_sha1;
+       struct object_id *old_oid = &cmd->old_oid;
+       struct object_id *new_oid = &cmd->new_oid;
  
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
                case DENY_UPDATE_INSTEAD:
-                       ret = update_worktree(new_sha1);
+                       ret = update_worktree(new_oid->hash);
                        if (ret)
                                return ret;
                        break;
                }
        }
  
-       if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
+       if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
                error("unpack should have generated %s, "
-                     "but I can't find it!", sha1_to_hex(new_sha1));
+                     "but I can't find it!", oid_to_hex(new_oid));
                return "bad pack";
        }
  
-       if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
+       if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
                if (deny_deletes && starts_with(name, "refs/heads/")) {
                        rp_error("denying ref deletion for %s", name);
                        return "deletion prohibited";
                }
        }
  
-       if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
-           !is_null_sha1(old_sha1) &&
+       if (deny_non_fast_forwards && !is_null_oid(new_oid) &&
+           !is_null_oid(old_oid) &&
            starts_with(name, "refs/heads/")) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
  
-               old_object = parse_object(old_sha1);
-               new_object = parse_object(new_sha1);
+               old_object = parse_object(old_oid->hash);
+               new_object = parse_object(new_oid->hash);
  
                if (!old_object || !new_object ||
                    old_object->type != OBJ_COMMIT ||
                return "hook declined";
        }
  
-       if (is_null_sha1(new_sha1)) {
+       if (is_null_oid(new_oid)) {
                struct strbuf err = STRBUF_INIT;
-               if (!parse_object(old_sha1)) {
-                       old_sha1 = NULL;
+               if (!parse_object(old_oid->hash)) {
+                       old_oid = NULL;
                        if (ref_exists(name)) {
                                rp_warning("Allowing deletion of corrupt ref.");
                        } else {
                }
                if (ref_transaction_delete(transaction,
                                           namespaced_name,
-                                          old_sha1,
+                                          old_oid->hash,
                                           0, "push", &err)) {
                        rp_error("%s", err.buf);
                        strbuf_release(&err);
  
                if (ref_transaction_update(transaction,
                                           namespaced_name,
-                                          new_sha1, old_sha1,
+                                          new_oid->hash, old_oid->hash,
                                           0, "push",
                                           &err)) {
                        rp_error("%s", err.buf);
@@@ -1162,7 -1162,7 +1162,7 @@@ static void check_aliased_update(struc
        const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
-       unsigned char sha1[GIT_SHA1_RAWSZ];
+       unsigned char sha1[GIT_MAX_RAWSZ];
        int flag;
  
        strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
  
        dst_cmd = (struct command *) item->util;
  
-       if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
-           !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
+       if (!oidcmp(&cmd->old_oid, &dst_cmd->old_oid) &&
+           !oidcmp(&cmd->new_oid, &dst_cmd->new_oid))
                return;
  
        dst_cmd->skip_update = 1;
        rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
                 " its target '%s' (%s..%s)",
                 cmd->ref_name,
-                find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV),
-                find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV),
+                find_unique_abbrev(cmd->old_oid.hash, DEFAULT_ABBREV),
+                find_unique_abbrev(cmd->new_oid.hash, DEFAULT_ABBREV),
                 dst_cmd->ref_name,
-                find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV),
-                find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+                find_unique_abbrev(dst_cmd->old_oid.hash, DEFAULT_ABBREV),
+                find_unique_abbrev(dst_cmd->new_oid.hash, DEFAULT_ABBREV));
  
        cmd->error_string = dst_cmd->error_string =
                "inconsistent aliased update";
@@@ -1231,10 -1231,10 +1231,10 @@@ static int command_singleton_iterator(v
        struct command **cmd_list = cb_data;
        struct command *cmd = *cmd_list;
  
-       if (!cmd || is_null_sha1(cmd->new_sha1))
+       if (!cmd || is_null_oid(&cmd->new_oid))
                return -1; /* end of list */
        *cmd_list = NULL; /* this returns only one */
-       hashcpy(sha1, cmd->new_sha1);
+       hashcpy(sha1, cmd->new_oid.hash);
        return 0;
  }
  
@@@ -1275,8 -1275,8 +1275,8 @@@ static int iterate_receive_command_list
                if (shallow_update && data->si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
-               if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
-                       hashcpy(sha1, cmd->new_sha1);
+               if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
+                       hashcpy(sha1, cmd->new_oid.hash);
                        *cmd_list = cmd->next;
                        return 0;
                }
@@@ -1303,7 -1303,7 +1303,7 @@@ static void reject_updates_to_hidden(st
  
                if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
                        continue;
-               if (is_null_sha1(cmd->new_sha1))
+               if (is_null_oid(&cmd->new_oid))
                        cmd->error_string = "deny deleting a hidden ref";
                else
                        cmd->error_string = "deny updating a hidden ref";
@@@ -1486,23 -1486,23 +1486,23 @@@ static struct command **queue_command(s
                                      const char *line,
                                      int linelen)
  {
-       unsigned char old_sha1[20], new_sha1[20];
+       struct object_id old_oid, new_oid;
        struct command *cmd;
        const char *refname;
        int reflen;
+       const char *p;
  
-       if (linelen < 83 ||
-           line[40] != ' ' ||
-           line[81] != ' ' ||
-           get_sha1_hex(line, old_sha1) ||
-           get_sha1_hex(line + 41, new_sha1))
+       if (parse_oid_hex(line, &old_oid, &p) ||
+           *p++ != ' ' ||
+           parse_oid_hex(p, &new_oid, &p) ||
+           *p++ != ' ')
                die("protocol error: expected old/new/ref, got '%s'", line);
  
-       refname = line + 82;
-       reflen = linelen - 82;
+       refname = p;
+       reflen = linelen - (p - line);
        FLEX_ALLOC_MEM(cmd, ref_name, refname, reflen);
-       hashcpy(cmd->old_sha1, old_sha1);
-       hashcpy(cmd->new_sha1, new_sha1);
+       oidcpy(&cmd->old_oid, &old_oid);
+       oidcpy(&cmd->new_oid, &new_oid);
        *tail = cmd;
        return &cmd->next;
  }
@@@ -1524,12 -1524,12 +1524,12 @@@ static void queue_commands_from_cert(st
  
        while (boc < eoc) {
                const char *eol = memchr(boc, '\n', eoc - boc);
 -              tail = queue_command(tail, boc, eol ? eol - boc : eoc - eol);
 +              tail = queue_command(tail, boc, eol ? eol - boc : eoc - boc);
                boc = eol ? eol + 1 : eoc;
        }
  }
  
- static struct command *read_head_info(struct sha1_array *shallow)
+ static struct command *read_head_info(struct oid_array *shallow)
  {
        struct command *commands = NULL;
        struct command **p = &commands;
                if (!line)
                        break;
  
-               if (len == 48 && starts_with(line, "shallow ")) {
-                       unsigned char sha1[20];
-                       if (get_sha1_hex(line + 8, sha1))
+               if (len 8 && starts_with(line, "shallow ")) {
+                       struct object_id oid;
+                       if (get_oid_hex(line + 8, &oid))
                                die("protocol error: expected shallow sha, got '%s'",
                                    line + 8);
-                       sha1_array_append(shallow, sha1);
+                       oid_array_append(shallow, &oid);
                        continue;
                }
  
@@@ -1634,17 -1634,12 +1634,17 @@@ static const char *parse_pack_header(st
  
  static const char *pack_lockfile;
  
 +static void push_header_arg(struct argv_array *args, struct pack_header *hdr)
 +{
 +      argv_array_pushf(args, "--pack_header=%"PRIu32",%"PRIu32,
 +                      ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
 +}
 +
  static const char *unpack(int err_fd, struct shallow_info *si)
  {
        struct pack_header hdr;
        const char *hdr_err;
        int status;
 -      char hdr_arg[38];
        struct child_process child = CHILD_PROCESS_INIT;
        int fsck_objects = (receive_fsck_objects >= 0
                            ? receive_fsck_objects
                        close(err_fd);
                return hdr_err;
        }
 -      snprintf(hdr_arg, sizeof(hdr_arg),
 -                      "--pack_header=%"PRIu32",%"PRIu32,
 -                      ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
  
        if (si->nr_ours || si->nr_theirs) {
                alt_shallow_file = setup_temporary_shallow(si->shallow);
        tmp_objdir_add_as_alternate(tmp_objdir);
  
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
 -              argv_array_pushl(&child.args, "unpack-objects", hdr_arg, NULL);
 +              argv_array_push(&child.args, "unpack-objects");
 +              push_header_arg(&child.args, &hdr);
                if (quiet)
                        argv_array_push(&child.args, "-q");
                if (fsck_objects)
        } else {
                char hostname[256];
  
 -              argv_array_pushl(&child.args, "index-pack",
 -                               "--stdin", hdr_arg, NULL);
 +              argv_array_pushl(&child.args, "index-pack", "--stdin", NULL);
 +              push_header_arg(&child.args, &hdr);
  
                if (gethostname(hostname, sizeof(hostname)))
                        xsnprintf(hostname, sizeof(hostname), "localhost");
@@@ -1807,7 -1804,7 +1807,7 @@@ static void prepare_shallow_update(stru
  
  static void update_shallow_info(struct command *commands,
                                struct shallow_info *si,
-                               struct sha1_array *ref)
+                               struct oid_array *ref)
  {
        struct command *cmd;
        int *ref_status;
        }
  
        for (cmd = commands; cmd; cmd = cmd->next) {
-               if (is_null_sha1(cmd->new_sha1))
+               if (is_null_oid(&cmd->new_oid))
                        continue;
-               sha1_array_append(ref, cmd->new_sha1);
+               oid_array_append(ref, &cmd->new_oid);
                cmd->index = ref->nr - 1;
        }
        si->ref = ref;
        ALLOC_ARRAY(ref_status, ref->nr);
        assign_shallow_commits_to_refs(si, NULL, ref_status);
        for (cmd = commands; cmd; cmd = cmd->next) {
-               if (is_null_sha1(cmd->new_sha1))
+               if (is_null_oid(&cmd->new_oid))
                        continue;
                if (ref_status[cmd->index]) {
                        cmd->error_string = "shallow update not allowed";
@@@ -1871,7 -1868,7 +1871,7 @@@ static int delete_only(struct command *
  {
        struct command *cmd;
        for (cmd = commands; cmd; cmd = cmd->next) {
-               if (!is_null_sha1(cmd->new_sha1))
+               if (!is_null_oid(&cmd->new_oid))
                        return 0;
        }
        return 1;
@@@ -1881,8 -1878,8 +1881,8 @@@ int cmd_receive_pack(int argc, const ch
  {
        int advertise_refs = 0;
        struct command *commands;
-       struct sha1_array shallow = SHA1_ARRAY_INIT;
-       struct sha1_array ref = SHA1_ARRAY_INIT;
+       struct oid_array shallow = OID_ARRAY_INIT;
+       struct oid_array ref = OID_ARRAY_INIT;
        struct shallow_info si;
  
        struct option options[] = {
        }
        if (use_sideband)
                packet_flush(1);
-       sha1_array_clear(&shallow);
-       sha1_array_clear(&ref);
+       oid_array_clear(&shallow);
+       oid_array_clear(&ref);
        free((void *)push_cert_nonce);
        return 0;
  }
diff --combined builtin/rev-parse.c
index f54d7b5028a455eae9b133e72ac24333c5035f2e,8d635add8f1f8643645f16648ac05d3c5c38e4ea..051333091062ef8e2e717ec4aef8a9bdf3b4450f
@@@ -205,22 -205,21 +205,22 @@@ static int anti_reference(const char *r
        return 0;
  }
  
- static int show_abbrev(const unsigned char *sha1, void *cb_data)
+ static int show_abbrev(const struct object_id *oid, void *cb_data)
  {
-       show_rev(NORMAL, sha1, NULL);
+       show_rev(NORMAL, oid->hash, NULL);
        return 0;
  }
  
  static void show_datestring(const char *flag, const char *datestr)
  {
 -      static char buffer[100];
 +      char *buffer;
  
        /* date handling requires both flags and revs */
        if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
                return;
 -      snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
 +      buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
        show(buffer);
 +      free(buffer);
  }
  
  static int show_file(const char *arg, int output_prefix)
        if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
                if (output_prefix) {
                        const char *prefix = startup_info->prefix;
 -                      show(prefix_filename(prefix,
 -                                           prefix ? strlen(prefix) : 0,
 -                                           arg));
 +                      char *fname = prefix_filename(prefix, arg);
 +                      show(fname);
 +                      free(fname);
                } else
                        show(arg);
                return 1;
diff --combined builtin/send-pack.c
index 832fd7ed0a2b4d8bff6d6edfadd285e499aa54d5,b64619442946cb0808a7950ee940e4895e7250bc..b8e2e74fe0c5cdefa6ed4d6c309a8bba203cc6e8
@@@ -131,8 -131,8 +131,8 @@@ int cmd_send_pack(int argc, const char 
        const char *dest = NULL;
        int fd[2];
        struct child_process *conn;
-       struct sha1_array extra_have = SHA1_ARRAY_INIT;
-       struct sha1_array shallow = SHA1_ARRAY_INIT;
+       struct oid_array extra_have = OID_ARRAY_INIT;
+       struct oid_array shallow = OID_ARRAY_INIT;
        struct ref *remote_refs, *local_refs;
        int ret;
        int helper_status = 0;
        unsigned force_update = 0;
        unsigned quiet = 0;
        int push_cert = 0;
 +      struct string_list push_options = STRING_LIST_INIT_NODUP;
        unsigned use_thin_pack = 0;
        unsigned atomic = 0;
        unsigned stateless_rpc = 0;
                { OPTION_CALLBACK,
                  0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
                  PARSE_OPT_OPTARG, option_parse_push_signed },
 +              OPT_STRING_LIST(0, "push-option", &push_options,
 +                              N_("server-specific"),
 +                              N_("option to transmit")),
                OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
                OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")),
                OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
        args.use_thin_pack = use_thin_pack;
        args.atomic = atomic;
        args.stateless_rpc = stateless_rpc;
 +      args.push_options = push_options.nr ? &push_options : NULL;
  
        if (from_stdin) {
                struct argv_array all_refspecs = ARGV_ARRAY_INIT;
diff --combined cache.h
index 556468c25b8c3a06eb776fbf322bed805ec97c33,93e7f0d00018405f30cb6e79bf43721cb2f66966..ba27595d54d864f600cb537e531e8855b8b0bdb6
+++ b/cache.h
@@@ -66,8 -66,12 +66,12 @@@ unsigned long git_deflate_bound(git_zst
  #define GIT_SHA1_RAWSZ 20
  #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
  
+ /* The length in byte and in hex digits of the largest possible hash value. */
+ #define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
+ #define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
  struct object_id {
-       unsigned char hash[GIT_SHA1_RAWSZ];
+       unsigned char hash[GIT_MAX_RAWSZ];
  };
  
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
@@@ -343,7 -347,6 +347,7 @@@ struct index_state 
  extern struct index_state the_index;
  
  /* Name hashing */
 +extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
  extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
  extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
  extern void free_name_hash(struct index_state *istate);
@@@ -411,7 -414,6 +415,7 @@@ static inline enum object_type object_t
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
  #define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
 +#define GIT_TOPLEVEL_PREFIX_ENVIRONMENT "GIT_INTERNAL_TOPLEVEL_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@@ -531,19 -533,7 +535,19 @@@ extern const char *setup_git_directory_
  extern const char *setup_git_directory(void);
  extern char *prefix_path(const char *prefix, int len, const char *path);
  extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
 -extern const char *prefix_filename(const char *prefix, int len, const char *path);
 +
 +/*
 + * Concatenate "prefix" (if len is non-zero) and "path", with no
 + * connecting characters (so "prefix" should end with a "/").
 + * Unlike prefix_path, this should be used if the named file does
 + * not have to interact with index entry; i.e. name of a random file
 + * on the filesystem.
 + *
 + * The return value is always a newly allocated string (even if the
 + * prefix was empty).
 + */
 +extern char *prefix_filename(const char *prefix, const char *path);
 +
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix,
                            const char *name,
@@@ -978,7 -968,7 +982,7 @@@ extern char *sha1_pack_index_name(cons
  extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
  extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
  
- extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
+ extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
  extern const struct object_id null_oid;
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
@@@ -1360,7 -1350,7 +1364,7 @@@ extern int get_sha1_with_context(const 
  
  extern int get_oid(const char *str, struct object_id *oid);
  
- typedef int each_abbrev_fn(const unsigned char *sha1, void *);
+ typedef int each_abbrev_fn(const struct object_id *oid, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
  extern int set_disambiguate_hint_config(const char *var, const char *value);
@@@ -1675,12 -1665,9 +1679,12 @@@ extern struct packed_git *find_sha1_pac
  extern void pack_report(void);
  
  /*
 - * Create a temporary file rooted in the object database directory.
 + * Create a temporary file rooted in the object database directory, or
 + * die on failure. The filename is taken from "pattern", which should have the
 + * usual "XXXXXX" trailer, and the resulting filename is written into the
 + * "template" buffer. Returns the open descriptor.
   */
 -extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
 +extern int odb_mkstemp(struct strbuf *template, const char *pattern);
  
  /*
   * Generate the filename to be used for a pack file with checksum "sha1" and
diff --combined combine-diff.c
index d3560573acee6ed216dca9779aa078738e9246b1,c5586c972d17ffaf398eefae9ae814314e5fcbd3..2848034fe9c3f3cc1d930347e892cee1cbed4f52
@@@ -292,10 -292,9 +292,10 @@@ static char *grab_blob(const struct obj
        enum object_type type;
  
        if (S_ISGITLINK(mode)) {
 -              blob = xmalloc(100);
 -              *size = snprintf(blob, 100,
 -                               "Subproject commit %s\n", oid_to_hex(oid));
 +              struct strbuf buf = STRBUF_INIT;
 +              strbuf_addf(&buf, "Subproject commit %s\n", oid_to_hex(oid));
 +              *size = buf.len;
 +              blob = strbuf_detach(&buf, NULL);
        } else if (is_null_oid(oid)) {
                /* deleted blob */
                *size = 0;
@@@ -1312,7 -1311,7 +1312,7 @@@ static const char *path_path(void *obj
  
  /* find set of paths that every parent touches */
  static struct combine_diff_path *find_paths_generic(const unsigned char *sha1,
-       const struct sha1_array *parents, struct diff_options *opt)
+       const struct oid_array *parents, struct diff_options *opt)
  {
        struct combine_diff_path *paths = NULL;
        int i, num_parent = parents->nr;
                        opt->output_format = stat_opt;
                else
                        opt->output_format = DIFF_FORMAT_NO_OUTPUT;
-               diff_tree_sha1(parents->sha1[i], sha1, "", opt);
+               diff_tree_sha1(parents->oid[i].hash, sha1, "", opt);
                diffcore_std(opt);
                paths = intersect_paths(paths, i, num_parent);
  
   * rename/copy detection, etc, comparing all trees simultaneously (= faster).
   */
  static struct combine_diff_path *find_paths_multitree(
-       const unsigned char *sha1, const struct sha1_array *parents,
+       const unsigned char *sha1, const struct oid_array *parents,
        struct diff_options *opt)
  {
        int i, nparent = parents->nr;
  
        ALLOC_ARRAY(parents_sha1, nparent);
        for (i = 0; i < nparent; i++)
-               parents_sha1[i] = parents->sha1[i];
+               parents_sha1[i] = parents->oid[i].hash;
  
        /* fake list head, so worker can assume it is non-NULL */
        paths_head.next = NULL;
  
  
  void diff_tree_combined(const unsigned char *sha1,
-                       const struct sha1_array *parents,
+                       const struct oid_array *parents,
                        int dense,
                        struct rev_info *rev)
  {
                if (stat_opt) {
                        diffopts.output_format = stat_opt;
  
-                       diff_tree_sha1(parents->sha1[0], sha1, "", &diffopts);
+                       diff_tree_sha1(parents->oid[0].hash, sha1, "", &diffopts);
                        diffcore_std(&diffopts);
                        if (opt->orderfile)
                                diffcore_order(opt->orderfile);
@@@ -1533,12 -1532,12 +1533,12 @@@ void diff_tree_combined_merge(const str
                              struct rev_info *rev)
  {
        struct commit_list *parent = get_saved_parents(rev, commit);
-       struct sha1_array parents = SHA1_ARRAY_INIT;
+       struct oid_array parents = OID_ARRAY_INIT;
  
        while (parent) {
-               sha1_array_append(&parents, parent->item->object.oid.hash);
+               oid_array_append(&parents, &parent->item->object.oid);
                parent = parent->next;
        }
        diff_tree_combined(commit->object.oid.hash, &parents, dense, rev);
-       sha1_array_clear(&parents);
+       oid_array_clear(&parents);
  }
diff --combined diff.c
index 92d78e4596f23af9b0b31b1a4d36fdfcbd72be55,330b640c684cf8e718f831c5bf4ef9e95c902bdb..11eef1c85d6d766320bfa24b10483fbc0869efe1
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -398,7 -398,7 +398,7 @@@ static struct diff_tempfile 
         */
        const char *name;
  
-       char hex[GIT_SHA1_HEXSZ + 1];
+       char hex[GIT_MAX_HEXSZ + 1];
        char mode[10];
  
        /*
@@@ -4023,7 -4023,8 +4023,7 @@@ int diff_opt_parse(struct diff_options 
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
        else if ((argcount = short_opt('O', av, &optarg))) {
 -              const char *path = prefix_filename(prefix, strlen(prefix), optarg);
 -              options->orderfile = xstrdup(path);
 +              options->orderfile = prefix_filename(prefix, optarg);
                return argcount;
        }
        else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
        else if (!strcmp(arg, "--no-function-context"))
                DIFF_OPT_CLR(options, FUNCCONTEXT);
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
 -              const char *path = prefix_filename(prefix, strlen(prefix), optarg);
 +              char *path = prefix_filename(prefix, optarg);
                options->file = fopen(path, "w");
                if (!options->file)
                        die_errno("Could not open '%s'", path);
                options->close_file = 1;
                if (options->use_color != GIT_COLOR_ALWAYS)
                        options->use_color = GIT_COLOR_NEVER;
 +              free(path);
                return argcount;
        } else
                return 0;
@@@ -4219,7 -4219,7 +4219,7 @@@ const char *diff_aligned_abbrev(const s
         * uniqueness across all objects (statistically speaking).
         */
        if (abblen < GIT_SHA1_HEXSZ - 3) {
-               static char hex[GIT_SHA1_HEXSZ + 1];
+               static char hex[GIT_MAX_HEXSZ + 1];
                if (len < abblen && abblen <= len + 2)
                        xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
                else
@@@ -4570,19 -4570,6 +4570,19 @@@ static void patch_id_consume(void *priv
        data->patchlen += new_len;
  }
  
 +static void patch_id_add_string(git_SHA_CTX *ctx, const char *str)
 +{
 +      git_SHA1_Update(ctx, str, strlen(str));
 +}
 +
 +static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode)
 +{
 +      /* large enough for 2^32 in octal */
 +      char buf[12];
 +      int len = xsnprintf(buf, sizeof(buf), "%06o", mode);
 +      git_SHA1_Update(ctx, buf, len);
 +}
 +
  /* returns 0 upon success, and writes result into sha1 */
  static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
  {
        int i;
        git_SHA_CTX ctx;
        struct patch_id_t data;
 -      char buffer[PATH_MAX * 4 + 20];
  
        git_SHA1_Init(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
  
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
 -              if (p->one->mode == 0)
 -                      len1 = snprintf(buffer, sizeof(buffer),
 -                                      "diff--gita/%.*sb/%.*s"
 -                                      "newfilemode%06o"
 -                                      "---/dev/null"
 -                                      "+++b/%.*s",
 -                                      len1, p->one->path,
 -                                      len2, p->two->path,
 -                                      p->two->mode,
 -                                      len2, p->two->path);
 -              else if (p->two->mode == 0)
 -                      len1 = snprintf(buffer, sizeof(buffer),
 -                                      "diff--gita/%.*sb/%.*s"
 -                                      "deletedfilemode%06o"
 -                                      "---a/%.*s"
 -                                      "+++/dev/null",
 -                                      len1, p->one->path,
 -                                      len2, p->two->path,
 -                                      p->one->mode,
 -                                      len1, p->one->path);
 -              else
 -                      len1 = snprintf(buffer, sizeof(buffer),
 -                                      "diff--gita/%.*sb/%.*s"
 -                                      "---a/%.*s"
 -                                      "+++b/%.*s",
 -                                      len1, p->one->path,
 -                                      len2, p->two->path,
 -                                      len1, p->one->path,
 -                                      len2, p->two->path);
 -              git_SHA1_Update(&ctx, buffer, len1);
 +              patch_id_add_string(&ctx, "diff--git");
 +              patch_id_add_string(&ctx, "a/");
 +              git_SHA1_Update(&ctx, p->one->path, len1);
 +              patch_id_add_string(&ctx, "b/");
 +              git_SHA1_Update(&ctx, p->two->path, len2);
 +
 +              if (p->one->mode == 0) {
 +                      patch_id_add_string(&ctx, "newfilemode");
 +                      patch_id_add_mode(&ctx, p->two->mode);
 +                      patch_id_add_string(&ctx, "---/dev/null");
 +                      patch_id_add_string(&ctx, "+++b/");
 +                      git_SHA1_Update(&ctx, p->two->path, len2);
 +              } else if (p->two->mode == 0) {
 +                      patch_id_add_string(&ctx, "deletedfilemode");
 +                      patch_id_add_mode(&ctx, p->one->mode);
 +                      patch_id_add_string(&ctx, "---a/");
 +                      git_SHA1_Update(&ctx, p->one->path, len1);
 +                      patch_id_add_string(&ctx, "+++/dev/null");
 +              } else {
 +                      patch_id_add_string(&ctx, "---a/");
 +                      git_SHA1_Update(&ctx, p->one->path, len1);
 +                      patch_id_add_string(&ctx, "+++b/");
 +                      git_SHA1_Update(&ctx, p->two->path, len2);
 +              }
  
                if (diff_header_only)
                        continue;
diff --combined ref-filter.c
index 1e392730057ce97dab8be4b05dfa7c610e10fdce,fe1abf65ffb4659fa557a8d5c667f5e47d73a6a3..3a640448fd83e6cf0bda8ddda081757d5941361e
@@@ -1487,7 -1487,6 +1487,7 @@@ struct ref_filter_cbdata 
        struct ref_array *array;
        struct ref_filter *filter;
        struct contains_cache contains_cache;
 +      struct contains_cache no_contains_cache;
  };
  
  /*
@@@ -1587,11 -1586,11 +1587,11 @@@ static enum contains_result contains_ta
  }
  
  static int commit_contains(struct ref_filter *filter, struct commit *commit,
 -                         struct contains_cache *cache)
 +                         struct commit_list *list, struct contains_cache *cache)
  {
        if (filter->with_commit_tag_algo)
 -              return contains_tag_algo(commit, filter->with_commit, cache) == CONTAINS_YES;
 -      return is_descendant_of(commit, filter->with_commit);
 +              return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
 +      return is_descendant_of(commit, list);
  }
  
  /*
@@@ -1678,22 -1677,22 +1678,22 @@@ static int filter_pattern_match(struct 
   * the need to parse the object via parse_object(). peel_ref() might be a
   * more efficient alternative to obtain the pointee.
   */
- static const unsigned char *match_points_at(struct sha1_array *points_at,
-                                           const unsigned char *sha1,
-                                           const char *refname)
+ static const struct object_id *match_points_at(struct oid_array *points_at,
+                                              const struct object_id *oid,
+                                              const char *refname)
  {
-       const unsigned char *tagged_sha1 = NULL;
+       const struct object_id *tagged_oid = NULL;
        struct object *obj;
  
-       if (sha1_array_lookup(points_at, sha1) >= 0)
-               return sha1;
-       obj = parse_object(sha1);
+       if (oid_array_lookup(points_at, oid) >= 0)
+               return oid;
+       obj = parse_object(oid->hash);
        if (!obj)
                die(_("malformed object at '%s'"), refname);
        if (obj->type == OBJ_TAG)
-               tagged_sha1 = ((struct tag *)obj)->tagged->oid.hash;
-       if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
-               return tagged_sha1;
+               tagged_oid = &((struct tag *)obj)->tagged->oid;
+       if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0)
+               return tagged_oid;
        return NULL;
  }
  
@@@ -1773,7 -1772,7 +1773,7 @@@ static int ref_filter_handler(const cha
        if (!filter_pattern_match(filter, refname))
                return 0;
  
-       if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))
+       if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
                return 0;
  
        /*
         * obtain the commit using the 'oid' available and discard all
         * non-commits early. The actual filtering is done later.
         */
 -      if (filter->merge_commit || filter->with_commit || filter->verbose) {
 +      if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) {
                commit = lookup_commit_reference_gently(oid->hash, 1);
                if (!commit)
                        return 0;
 -              /* We perform the filtering for the '--contains' option */
 +              /* We perform the filtering for the '--contains' option... */
                if (filter->with_commit &&
 -                  !commit_contains(filter, commit, &ref_cbdata->contains_cache))
 +                  !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache))
 +                      return 0;
 +              /* ...or for the `--no-contains' option */
 +              if (filter->no_commit &&
 +                  commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache))
                        return 0;
        }
  
@@@ -1892,7 -1887,6 +1892,7 @@@ int filter_refs(struct ref_array *array
        filter->kind = type & FILTER_REFS_KIND_MASK;
  
        init_contains_cache(&ref_cbdata.contains_cache);
 +      init_contains_cache(&ref_cbdata.no_contains_cache);
  
        /*  Simple per-ref filtering */
        if (!filter->kind)
        }
  
        clear_contains_cache(&ref_cbdata.contains_cache);
 +      clear_contains_cache(&ref_cbdata.no_contains_cache);
  
        /*  Filters that need revision walking */
        if (filter->merge_commit)
@@@ -2091,17 -2084,8 +2091,17 @@@ int parse_opt_merge_filter(const struc
  {
        struct ref_filter *rf = opt->value;
        unsigned char sha1[20];
 +      int no_merged = starts_with(opt->long_name, "no");
 +
 +      if (rf->merge) {
 +              if (no_merged) {
 +                      return opterror(opt, "is incompatible with --merged", 0);
 +              } else {
 +                      return opterror(opt, "is incompatible with --no-merged", 0);
 +              }
 +      }
  
 -      rf->merge = starts_with(opt->long_name, "no")
 +      rf->merge = no_merged
                ? REF_FILTER_MERGED_OMIT
                : REF_FILTER_MERGED_INCLUDE;
  
diff --combined ref-filter.h
index dde40f68497c5456913a764e5e02433ff5eaf509,e5e177d5f18f78ae7cb2985222266b5fd2396250..c20167aa3c785f0060147204d963537f58617570
@@@ -51,9 -51,8 +51,9 @@@ struct ref_array 
  
  struct ref_filter {
        const char **name_patterns;
-       struct sha1_array points_at;
+       struct oid_array points_at;
        struct commit_list *with_commit;
 +      struct commit_list *no_commit;
  
        enum {
                REF_FILTER_MERGED_NONE = 0,
diff --combined remote-curl.c
index e953d06f66a5993b9f1a0039920c71a90afa5bc9,82a019d7db79a76f499604ae41dac44693aaab03..d41fe16ff320e889820a6cb04ad941770415c6d2
@@@ -22,7 -22,6 +22,7 @@@ struct options 
        unsigned long depth;
        char *deepen_since;
        struct string_list deepen_not;
 +      struct string_list push_options;
        unsigned progress : 1,
                check_self_contained_and_connected : 1,
                cloning : 1,
@@@ -140,9 -139,6 +140,9 @@@ static int set_option(const char *name
                else
                        return -1;
                return 0;
 +      } else if (!strcmp(name, "push-option")) {
 +              string_list_append(&options.push_options, value);
 +              return 0;
  
  #if LIBCURL_VERSION_NUM >= 0x070a08
        } else if (!strcmp(name, "family")) {
@@@ -167,7 -163,7 +167,7 @@@ struct discovery 
        char *buf;
        size_t len;
        struct ref *refs;
-       struct sha1_array shallow;
+       struct oid_array shallow;
        unsigned proto_git : 1;
  };
  static struct discovery *last_discovery;
@@@ -234,7 -230,7 +234,7 @@@ static void free_discovery(struct disco
        if (d) {
                if (d == last_discovery)
                        last_discovery = NULL;
-               free(d->shallow.sha1);
+               free(d->shallow.oid);
                free(d->buf_alloc);
                free_refs(d->refs);
                free(d);
@@@ -947,9 -943,6 +947,9 @@@ static int push_git(struct discovery *h
                argv_array_push(&args, "--quiet");
        else if (options.verbosity > 1)
                argv_array_push(&args, "--verbose");
 +      for (i = 0; i < options.push_options.nr; i++)
 +              argv_array_pushf(&args, "--push-option=%s",
 +                               options.push_options.items[i].string);
        argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
        for_each_string_list_item(cas_option, &cas_options)
                argv_array_push(&args, cas_option->string);
@@@ -1035,7 -1028,6 +1035,7 @@@ int cmd_main(int argc, const char **arg
        options.progress = !!isatty(2);
        options.thin = 1;
        string_list_init(&options.deepen_not, 1);
 +      string_list_init(&options.push_options, 1);
  
        remote = remote_get(argv[1]);
  
diff --combined remote.h
index d7992c4ef192fb7fbdf644f0b836290411505c82,eb61cc884d40eeea602b54efb07b5c0cdd112b71..949badc4eea89f32f6839233d658e457721b43af
+++ b/remote.h
@@@ -149,11 -149,11 +149,11 @@@ int check_ref_type(const struct ref *re
   */
  void free_refs(struct ref *ref);
  
- struct sha1_array;
+ struct oid_array;
  extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                                     struct ref **list, unsigned int flags,
-                                    struct sha1_array *extra_have,
-                                    struct sha1_array *shallow);
+                                    struct oid_array *extra_have,
+                                    struct oid_array *shallow);
  
  int resolve_remote_symref(struct ref *ref, struct ref *list);
  int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);
@@@ -290,6 -290,7 +290,6 @@@ struct push_cas_option 
  };
  
  extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
 -extern int parse_push_cas_option(struct push_cas_option *, const char *arg, int unset);
  
  extern int is_empty_cas(const struct push_cas_option *);
  void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
diff --combined send-pack.c
index 66e652f7ef79e43e5427364960da186b99b11686,312cd2c2828a0e41d12087a5fc206ba4854709cb..78bb34ebec297102c852a5b88ec7b4f10ffbc1d8
@@@ -50,7 -50,7 +50,7 @@@ static void feed_object(const unsigned 
  /*
   * Make a pack stream and spit it out into file descriptor fd
   */
- static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, struct send_pack_args *args)
+ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struct send_pack_args *args)
  {
        /*
         * The child becomes pack-objects --revs; we feed
@@@ -98,7 -98,7 +98,7 @@@
         */
        po_in = xfdopen(po.in, "w");
        for (i = 0; i < extra->nr; i++)
-               feed_object(extra->sha1[i], po_in, 1);
+               feed_object(extra->oid[i].hash, po_in, 1);
  
        while (refs) {
                if (!is_null_oid(&refs->old_oid))
@@@ -376,7 -376,7 +376,7 @@@ static void reject_invalid_nonce(const 
  int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
-             struct sha1_array *extra_have)
+             struct oid_array *extra_have)
  {
        int in = fd[0];
        int out = fd[1];
                }
        }
  
 +      if (use_push_options) {
 +              struct string_list_item *item;
 +
 +              packet_buf_flush(&req_buf);
 +              for_each_string_list_item(item, args->push_options)
 +                      packet_buf_write(&req_buf, "%s", item->string);
 +      }
 +
        if (args->stateless_rpc) {
                if (!args->dry_run && (cmds_sent || is_repository_shallow())) {
                        packet_buf_flush(&req_buf);
        strbuf_release(&req_buf);
        strbuf_release(&cap_buf);
  
 -      if (use_push_options) {
 -              struct string_list_item *item;
 -              struct strbuf sb = STRBUF_INIT;
 -
 -              for_each_string_list_item(item, args->push_options)
 -                      packet_buf_write(&sb, "%s", item->string);
 -
 -              write_or_die(out, sb.buf, sb.len);
 -              packet_flush(out);
 -              strbuf_release(&sb);
 -      }
 -
        if (use_sideband && cmds_sent) {
                memset(&demux, 0, sizeof(demux));
                demux.proc = sideband_demux;
diff --combined sha1_file.c
index 7369f7495a06e40eb885deb2101736c9105d950e,b4666ee5c244cacefb08f1f41968c3f3524cf94b..2270f36a2b12d9f4b2f4c0cb2d8c5b5386aac142
@@@ -1606,7 -1606,7 +1606,7 @@@ static void mark_bad_packed_object(stru
                if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
                        return;
        p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
-                                     st_mult(GIT_SHA1_RAWSZ,
+                                     st_mult(GIT_MAX_RAWSZ,
                                              st_add(p->num_bad_objects, 1)));
        hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
        p->num_bad_objects++;
@@@ -2952,7 -2952,7 +2952,7 @@@ static int sha1_loose_object_info(cons
        if (status && oi->typep)
                *oi->typep = status;
        strbuf_release(&hdrbuf);
 -      return 0;
 +      return (status < 0) ? status : 0;
  }
  
  int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
@@@ -3759,11 -3759,11 +3759,11 @@@ static int for_each_file_in_obj_subdir(
                strbuf_addf(path, "/%s", de->d_name);
  
                if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
-                       char hex[GIT_SHA1_HEXSZ+1];
+                       char hex[GIT_MAX_HEXSZ+1];
                        struct object_id oid;
  
 -                      snprintf(hex, sizeof(hex), "%02x%s",
 -                               subdir_nr, de->d_name);
 +                      xsnprintf(hex, sizeof(hex), "%02x%s",
 +                                subdir_nr, de->d_name);
                        if (!get_oid_hex(hex, &oid)) {
                                if (obj_cb) {
                                        r = obj_cb(&oid, path->buf, data);
@@@ -3913,7 -3913,7 +3913,7 @@@ static int check_stream_sha1(git_zstrea
                             const unsigned char *expected_sha1)
  {
        git_SHA_CTX c;
-       unsigned char real_sha1[GIT_SHA1_RAWSZ];
+       unsigned char real_sha1[GIT_MAX_RAWSZ];
        unsigned char buf[4096];
        unsigned long total_read;
        int status = Z_OK;
diff --combined sha1_name.c
index d9d1b2fce8dddfe51c3e23b4b524e6eed4028b86,323dd34c63fe03326738bca0f42d00b6639009eb..8eec9f7c1bb59b8124e098367b7bff552bcafcf4
  
  static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
  
- typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
+ typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
  
  struct disambiguate_state {
        int len; /* length of prefix in hex chars */
-       char hex_pfx[GIT_SHA1_HEXSZ + 1];
-       unsigned char bin_pfx[GIT_SHA1_RAWSZ];
+       char hex_pfx[GIT_MAX_HEXSZ + 1];
+       struct object_id bin_pfx;
  
        disambiguate_hint_fn fn;
        void *cb_data;
-       unsigned char candidate[GIT_SHA1_RAWSZ];
+       struct object_id candidate;
        unsigned candidate_exists:1;
        unsigned candidate_checked:1;
        unsigned candidate_ok:1;
@@@ -29,7 -29,7 +29,7 @@@
        unsigned always_call_fn:1;
  };
  
- static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
+ static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
  {
        if (ds->always_call_fn) {
                ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
        }
        if (!ds->candidate_exists) {
                /* this is the first candidate */
-               hashcpy(ds->candidate, current);
+               oidcpy(&ds->candidate, current);
                ds->candidate_exists = 1;
                return;
-       } else if (!hashcmp(ds->candidate, current)) {
+       } else if (!oidcmp(&ds->candidate, current)) {
                /* the same as what we already have seen */
                return;
        }
        }
  
        if (!ds->candidate_checked) {
-               ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
+               ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data);
                ds->disambiguate_fn_used = 1;
                ds->candidate_checked = 1;
        }
  
        if (!ds->candidate_ok) {
                /* discard the candidate; we know it does not satisfy fn */
-               hashcpy(ds->candidate, current);
+               oidcpy(&ds->candidate, current);
                ds->candidate_checked = 0;
                return;
        }
@@@ -80,7 -80,7 +80,7 @@@
  static void find_short_object_filename(struct disambiguate_state *ds)
  {
        struct alternate_object_database *alt;
-       char hex[GIT_SHA1_HEXSZ];
+       char hex[GIT_MAX_HEXSZ];
        static struct alternate_object_database *fakeent;
  
        if (!fakeent) {
                        continue;
  
                while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
-                       unsigned char sha1[20];
+                       struct object_id oid;
  
-                       if (strlen(de->d_name) != 38)
+                       if (strlen(de->d_name) != GIT_SHA1_HEXSZ - 2)
                                continue;
                        if (memcmp(de->d_name, ds->hex_pfx + 2, ds->len - 2))
                                continue;
-                       memcpy(hex + 2, de->d_name, 38);
-                       if (!get_sha1_hex(hex, sha1))
-                               update_candidates(ds, sha1);
+                       memcpy(hex + 2, de->d_name, GIT_SHA1_HEXSZ - 2);
+                       if (!get_oid_hex(hex, &oid))
+                               update_candidates(ds, &oid);
                }
                closedir(dir);
        }
@@@ -140,7 -140,7 +140,7 @@@ static void unique_in_pack(struct packe
                           struct disambiguate_state *ds)
  {
        uint32_t num, last, i, first = 0;
-       const unsigned char *current = NULL;
+       const struct object_id *current = NULL;
  
        open_pack_index(p);
        num = p->num_objects;
                int cmp;
  
                current = nth_packed_object_sha1(p, mid);
-               cmp = hashcmp(ds->bin_pfx, current);
+               cmp = hashcmp(ds->bin_pfx.hash, current);
                if (!cmp) {
                        first = mid;
                        break;
         * 0, 1 or more objects that actually match(es).
         */
        for (i = first; i < num && !ds->ambiguous; i++) {
-               current = nth_packed_object_sha1(p, i);
-               if (!match_sha(ds->len, ds->bin_pfx, current))
+               struct object_id oid;
+               current = nth_packed_object_oid(&oid, p, i);
+               if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash))
                        break;
                update_candidates(ds, current);
        }
@@@ -213,66 -214,66 +214,66 @@@ static int finish_object_disambiguation
                 * same repository!
                 */
                ds->candidate_ok = (!ds->disambiguate_fn_used ||
-                                   ds->fn(ds->candidate, ds->cb_data));
+                                   ds->fn(&ds->candidate, ds->cb_data));
  
        if (!ds->candidate_ok)
                return SHORT_NAME_AMBIGUOUS;
  
-       hashcpy(sha1, ds->candidate);
+       hashcpy(sha1, ds->candidate.hash);
        return 0;
  }
  
- static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
+ static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
  {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_COMMIT;
  }
  
- static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
+ static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused)
  {
        struct object *obj;
        int kind;
  
-       kind = sha1_object_info(sha1, NULL);
+       kind = sha1_object_info(oid->hash, NULL);
        if (kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
  
        /* We need to do this the hard way... */
-       obj = deref_tag(parse_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(oid->hash), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
  }
  
- static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
+ static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
  {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_TREE;
  }
  
- static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
+ static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused)
  {
        struct object *obj;
        int kind;
  
-       kind = sha1_object_info(sha1, NULL);
+       kind = sha1_object_info(oid->hash, NULL);
        if (kind == OBJ_TREE || kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
  
        /* We need to do this the hard way... */
-       obj = deref_tag(parse_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(oid->hash), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
  }
  
- static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
+ static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
  {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_BLOB;
  }
  
@@@ -332,7 -333,7 +333,7 @@@ static int init_object_disambiguation(c
                ds->hex_pfx[i] = c;
                if (!(i & 1))
                        val <<= 4;
-               ds->bin_pfx[i >> 1] |= val;
+               ds->bin_pfx.hash[i >> 1] |= val;
        }
  
        ds->len = len;
        return 0;
  }
  
- static int show_ambiguous_object(const unsigned char *sha1, void *data)
+ static int show_ambiguous_object(const struct object_id *oid, void *data)
  {
        const struct disambiguate_state *ds = data;
        struct strbuf desc = STRBUF_INIT;
        int type;
  
-       if (ds->fn && !ds->fn(sha1, ds->cb_data))
+       if (ds->fn && !ds->fn(oid, ds->cb_data))
                return 0;
  
-       type = sha1_object_info(sha1, NULL);
+       type = sha1_object_info(oid->hash, NULL);
        if (type == OBJ_COMMIT) {
-               struct commit *commit = lookup_commit(sha1);
+               struct commit *commit = lookup_commit(oid->hash);
                if (commit) {
                        struct pretty_print_context pp = {0};
                        pp.date_mode.type = DATE_SHORT;
                        format_commit_message(commit, " %ad - %s", &desc, &pp);
                }
        } else if (type == OBJ_TAG) {
-               struct tag *tag = lookup_tag(sha1);
+               struct tag *tag = lookup_tag(oid->hash);
                if (!parse_tag(tag) && tag->tag)
                        strbuf_addf(&desc, " %s", tag->tag);
        }
  
        advise("  %s %s%s",
-              find_unique_abbrev(sha1, DEFAULT_ABBREV),
+              find_unique_abbrev(oid->hash, DEFAULT_ABBREV),
               typename(type) ? typename(type) : "unknown type",
               desc.buf);
  
@@@ -422,15 -424,15 +424,15 @@@ static int get_short_sha1(const char *n
        return status;
  }
  
- static int collect_ambiguous(const unsigned char *sha1, void *data)
+ static int collect_ambiguous(const struct object_id *oid, void *data)
  {
-       sha1_array_append(data, sha1);
+       oid_array_append(data, oid);
        return 0;
  }
  
  int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
  {
-       struct sha1_array collect = SHA1_ARRAY_INIT;
+       struct oid_array collect = OID_ARRAY_INIT;
        struct disambiguate_state ds;
        int ret;
  
        find_short_object_filename(&ds);
        find_short_packed_object(&ds);
  
-       ret = sha1_array_for_each_unique(&collect, fn, cb_data);
-       sha1_array_clear(&collect);
+       ret = oid_array_for_each_unique(&collect, fn, cb_data);
+       oid_array_clear(&collect);
        return ret;
  }
  
@@@ -509,7 -511,7 +511,7 @@@ int find_unique_abbrev_r(char *hex, con
  const char *find_unique_abbrev(const unsigned char *sha1, int len)
  {
        static int bufno;
-       static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
+       static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
        char *hex = hexbuffer[bufno];
        bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
        find_unique_abbrev_r(hex, sha1, len);
@@@ -549,7 -551,7 +551,7 @@@ static inline int at_mark(const char *s
        for (i = 0; i < nr; i++) {
                int suffix_len = strlen(suffix[i]);
                if (suffix_len <= len
 -                  && !memcmp(string, suffix[i], suffix_len))
 +                  && !strncasecmp(string, suffix[i], suffix_len))
                        return suffix_len;
        }
        return 0;
diff --combined submodule.c
index 2ee970572917f8dfdb6be58dfd8252ff58d07c12,b6bfb0cad92211839d55cba27e5f46d56cd157ff..6368d5fa6afe001d38a63908a5a62cfe498ceaea
  #include "worktree.h"
  
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
 +static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  static int parallel_jobs = 1;
  static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP;
  static int initialized_fetch_ref_tips;
- static struct sha1_array ref_tips_before_fetch;
- static struct sha1_array ref_tips_after_fetch;
+ static struct oid_array ref_tips_before_fetch;
+ static struct oid_array ref_tips_after_fetch;
  
  /*
   * The following flag is set if the .gitmodules file is unmerged. We then
@@@ -213,68 -212,37 +213,68 @@@ void gitmodules_config_sha1(const unsig
  }
  
  /*
 + * NEEDSWORK: With the addition of different configuration options to determine
 + * if a submodule is of interests, the validity of this function's name comes
 + * into question.  Once the dust has settled and more concrete terminology is
 + * decided upon, come up with a more proper name for this function.  One
 + * potential candidate could be 'is_submodule_active()'.
 + *
   * Determine if a submodule has been initialized at a given 'path'
   */
  int is_submodule_initialized(const char *path)
  {
        int ret = 0;
 -      const struct submodule *module = NULL;
 +      char *key = NULL;
 +      char *value = NULL;
 +      const struct string_list *sl;
 +      const struct submodule *module = submodule_from_path(null_sha1, path);
  
 -      module = submodule_from_path(null_sha1, path);
 +      /* early return if there isn't a path->module mapping */
 +      if (!module)
 +              return 0;
  
 -      if (module) {
 -              char *key = xstrfmt("submodule.%s.url", module->name);
 -              char *value = NULL;
 +      /* submodule.<name>.active is set */
 +      key = xstrfmt("submodule.%s.active", module->name);
 +      if (!git_config_get_bool(key, &ret)) {
 +              free(key);
 +              return ret;
 +      }
 +      free(key);
  
 -              ret = !git_config_get_string(key, &value);
 +      /* submodule.active is set */
 +      sl = git_config_get_value_multi("submodule.active");
 +      if (sl) {
 +              struct pathspec ps;
 +              struct argv_array args = ARGV_ARRAY_INIT;
 +              const struct string_list_item *item;
  
 -              free(value);
 -              free(key);
 +              for_each_string_list_item(item, sl) {
 +                      argv_array_push(&args, item->string);
 +              }
 +
 +              parse_pathspec(&ps, 0, 0, NULL, args.argv);
 +              ret = match_pathspec(&ps, path, strlen(path), 0, NULL, 1);
 +
 +              argv_array_clear(&args);
 +              clear_pathspec(&ps);
 +              return ret;
        }
  
 +      /* fallback to checking if the URL is set */
 +      key = xstrfmt("submodule.%s.url", module->name);
 +      ret = !git_config_get_string(key, &value);
 +
 +      free(value);
 +      free(key);
        return ret;
  }
  
 -/*
 - * Determine if a submodule has been populated at a given 'path'
 - */
 -int is_submodule_populated(const char *path)
 +int is_submodule_populated_gently(const char *path, int *return_error_code)
  {
        int ret = 0;
        char *gitdir = xstrfmt("%s/.git", path);
  
 -      if (resolve_gitdir(gitdir))
 +      if (resolve_gitdir_gently(gitdir, return_error_code))
                ret = 1;
  
        free(gitdir);
@@@ -390,23 -358,6 +390,23 @@@ static void print_submodule_summary(str
        strbuf_release(&sb);
  }
  
 +static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out)
 +{
 +      const char * const *var;
 +
 +      for (var = local_repo_env; *var; var++) {
 +              if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
 +                      argv_array_push(out, *var);
 +      }
 +}
 +
 +void prepare_submodule_repo_env(struct argv_array *out)
 +{
 +      prepare_submodule_repo_env_no_git_dir(out);
 +      argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
 +                       DEFAULT_GIT_DIR_ENVIRONMENT);
 +}
 +
  /* Helper function to display the submodule header line prior to the full
   * summary output. If it can locate the submodule objects directory it will
   * attempt to lookup both the left and right commits and put them into the
@@@ -576,7 -527,6 +576,7 @@@ void show_submodule_inline_diff(FILE *f
        if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
                argv_array_push(&cp.args, oid_to_hex(new));
  
 +      prepare_submodule_repo_env(&cp.env_array);
        if (run_command(&cp))
                fprintf(f, "(diff failed)\n");
  
@@@ -595,62 -545,41 +595,62 @@@ void set_config_fetch_recurse_submodule
        config_fetch_recurse_submodules = value;
  }
  
 +void set_config_update_recurse_submodules(int value)
 +{
 +      config_update_recurse_submodules = value;
 +}
 +
 +int should_update_submodules(void)
 +{
 +      return config_update_recurse_submodules == RECURSE_SUBMODULES_ON;
 +}
 +
 +const struct submodule *submodule_from_ce(const struct cache_entry *ce)
 +{
 +      if (!S_ISGITLINK(ce->ce_mode))
 +              return NULL;
 +
 +      if (!should_update_submodules())
 +              return NULL;
 +
 +      return submodule_from_path(null_sha1, ce->name);
 +}
 +
  static int has_remote(const char *refname, const struct object_id *oid,
                      int flags, void *cb_data)
  {
        return 1;
  }
  
- static int append_sha1_to_argv(const unsigned char sha1[20], void *data)
+ static int append_oid_to_argv(const struct object_id *oid, void *data)
  {
        struct argv_array *argv = data;
-       argv_array_push(argv, sha1_to_hex(sha1));
+       argv_array_push(argv, oid_to_hex(oid));
        return 0;
  }
  
- static int check_has_commit(const unsigned char sha1[20], void *data)
+ static int check_has_commit(const struct object_id *oid, void *data)
  {
        int *has_commit = data;
  
-       if (!lookup_commit_reference(sha1))
+       if (!lookup_commit_reference(oid->hash))
                *has_commit = 0;
  
        return 0;
  }
  
- static int submodule_has_commits(const char *path, struct sha1_array *commits)
+ static int submodule_has_commits(const char *path, struct oid_array *commits)
  {
        int has_commit = 1;
  
        if (add_submodule_odb(path))
                return 0;
  
-       sha1_array_for_each_unique(commits, check_has_commit, &has_commit);
+       oid_array_for_each_unique(commits, check_has_commit, &has_commit);
        return has_commit;
  }
  
- static int submodule_needs_pushing(const char *path, struct sha1_array *commits)
+ static int submodule_needs_pushing(const char *path, struct oid_array *commits)
  {
        if (!submodule_has_commits(path, commits))
                /*
                int needs_pushing = 0;
  
                argv_array_push(&cp.args, "rev-list");
-               sha1_array_for_each_unique(commits, append_sha1_to_argv, &cp.args);
+               oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
                argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
  
                prepare_submodule_repo_env(&cp.env_array);
        return 0;
  }
  
- static struct sha1_array *submodule_commits(struct string_list *submodules,
+ static struct oid_array *submodule_commits(struct string_list *submodules,
                                            const char *path)
  {
        struct string_list_item *item;
  
        item = string_list_insert(submodules, path);
        if (item->util)
-               return (struct sha1_array *) item->util;
+               return (struct oid_array *) item->util;
  
-       /* NEEDSWORK: should we have sha1_array_init()? */
-       item->util = xcalloc(1, sizeof(struct sha1_array));
-       return (struct sha1_array *) item->util;
+       /* NEEDSWORK: should we have oid_array_init()? */
+       item->util = xcalloc(1, sizeof(struct oid_array));
+       return (struct oid_array *) item->util;
  }
  
  static void collect_submodules_from_diff(struct diff_queue_struct *q,
  
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
-               struct sha1_array *commits;
+               struct oid_array *commits;
                if (!S_ISGITLINK(p->two->mode))
                        continue;
                commits = submodule_commits(submodules, p->two->path);
-               sha1_array_append(commits, p->two->oid.hash);
+               oid_array_append(commits, &p->two->oid);
        }
  }
  
@@@ -741,11 -670,11 +741,11 @@@ static void free_submodules_sha1s(struc
  {
        struct string_list_item *item;
        for_each_string_list_item(item, submodules)
-               sha1_array_clear((struct sha1_array *) item->util);
+               oid_array_clear((struct oid_array *) item->util);
        string_list_clear(submodules, 1);
  }
  
- int find_unpushed_submodules(struct sha1_array *commits,
+ int find_unpushed_submodules(struct oid_array *commits,
                const char *remotes_name, struct string_list *needs_pushing)
  {
        struct rev_info rev;
  
        /* argv.argv[0] will be ignored by setup_revisions */
        argv_array_push(&argv, "find_unpushed_submodules");
-       sha1_array_for_each_unique(commits, append_sha1_to_argv, &argv);
+       oid_array_for_each_unique(commits, append_oid_to_argv, &argv);
        argv_array_push(&argv, "--not");
        argv_array_pushf(&argv, "--remotes=%s", remotes_name);
  
        argv_array_clear(&argv);
  
        for_each_string_list_item(submodule, &submodules) {
-               struct sha1_array *commits = (struct sha1_array *) submodule->util;
+               struct oid_array *commits = (struct oid_array *) submodule->util;
  
                if (submodule_needs_pushing(submodule->string, commits))
                        string_list_insert(needs_pushing, submodule->string);
@@@ -806,7 -735,7 +806,7 @@@ static int push_submodule(const char *p
        return 1;
  }
  
- int push_unpushed_submodules(struct sha1_array *commits,
+ int push_unpushed_submodules(struct oid_array *commits,
                             const char *remotes_name,
                             int dry_run)
  {
@@@ -888,23 -817,23 +888,23 @@@ static void submodule_collect_changed_c
  static int add_sha1_to_array(const char *ref, const struct object_id *oid,
                             int flags, void *data)
  {
-       sha1_array_append(data, oid->hash);
+       oid_array_append(data, oid);
        return 0;
  }
  
- void check_for_new_submodule_commits(unsigned char new_sha1[20])
+ void check_for_new_submodule_commits(struct object_id *oid)
  {
        if (!initialized_fetch_ref_tips) {
                for_each_ref(add_sha1_to_array, &ref_tips_before_fetch);
                initialized_fetch_ref_tips = 1;
        }
  
-       sha1_array_append(&ref_tips_after_fetch, new_sha1);
+       oid_array_append(&ref_tips_after_fetch, oid);
  }
  
- static int add_sha1_to_argv(const unsigned char sha1[20], void *data)
+ static int add_oid_to_argv(const struct object_id *oid, void *data)
  {
-       argv_array_push(data, sha1_to_hex(sha1));
+       argv_array_push(data, oid_to_hex(oid));
        return 0;
  }
  
@@@ -920,11 -849,11 +920,11 @@@ static void calculate_changed_submodule
  
        init_revisions(&rev, NULL);
        argv_array_push(&argv, "--"); /* argv[0] program name */
-       sha1_array_for_each_unique(&ref_tips_after_fetch,
-                                  add_sha1_to_argv, &argv);
+       oid_array_for_each_unique(&ref_tips_after_fetch,
+                                  add_oid_to_argv, &argv);
        argv_array_push(&argv, "--not");
-       sha1_array_for_each_unique(&ref_tips_before_fetch,
-                                  add_sha1_to_argv, &argv);
+       oid_array_for_each_unique(&ref_tips_before_fetch,
+                                  add_oid_to_argv, &argv);
        setup_revisions(argv.argc, argv.argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
        }
  
        argv_array_clear(&argv);
-       sha1_array_clear(&ref_tips_before_fetch);
-       sha1_array_clear(&ref_tips_after_fetch);
+       oid_array_clear(&ref_tips_before_fetch);
+       oid_array_clear(&ref_tips_after_fetch);
        initialized_fetch_ref_tips = 0;
  }
  
  
  unsigned is_submodule_modified(const char *path, int ignore_untracked)
  {
 -      ssize_t len;
        struct child_process cp = CHILD_PROCESS_INIT;
 -      const char *argv[] = {
 -              "status",
 -              "--porcelain",
 -              NULL,
 -              NULL,
 -      };
        struct strbuf buf = STRBUF_INIT;
 +      FILE *fp;
        unsigned dirty_submodule = 0;
 -      const char *line, *next_line;
        const char *git_dir;
 +      int ignore_cp_exit_code = 0;
  
        strbuf_addf(&buf, "%s/.git", path);
        git_dir = read_gitfile(buf.buf);
        if (!git_dir)
                git_dir = buf.buf;
 -      if (!is_directory(git_dir)) {
 +      if (!is_git_directory(git_dir)) {
 +              if (is_directory(git_dir))
 +                      die(_("'%s' not recognized as a git repository"), git_dir);
                strbuf_release(&buf);
                /* The submodule is not checked out, so it is not modified */
                return 0;
 -
        }
        strbuf_reset(&buf);
  
 +      argv_array_pushl(&cp.args, "status", "--porcelain=2", NULL);
        if (ignore_untracked)
 -              argv[2] = "-uno";
 +              argv_array_push(&cp.args, "-uno");
  
 -      cp.argv = argv;
        prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
        cp.dir = path;
        if (start_command(&cp))
 -              die("Could not run 'git status --porcelain' in submodule %s", path);
 +              die("Could not run 'git status --porcelain=2' in submodule %s", path);
  
 -      len = strbuf_read(&buf, cp.out, 1024);
 -      line = buf.buf;
 -      while (len > 2) {
 -              if ((line[0] == '?') && (line[1] == '?')) {
 +      fp = xfdopen(cp.out, "r");
 +      while (strbuf_getwholeline(&buf, fp, '\n') != EOF) {
 +              /* regular untracked files */
 +              if (buf.buf[0] == '?')
                        dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
 -                      if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
 -                              break;
 -              } else {
 -                      dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
 -                      if (ignore_untracked ||
 -                          (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
 -                              break;
 +
 +              if (buf.buf[0] == 'u' ||
 +                  buf.buf[0] == '1' ||
 +                  buf.buf[0] == '2') {
 +                      /* T = line type, XY = status, SSSS = submodule state */
 +                      if (buf.len < strlen("T XY SSSS"))
 +                              die("BUG: invalid status --porcelain=2 line %s",
 +                                  buf.buf);
 +
 +                      if (buf.buf[5] == 'S' && buf.buf[8] == 'U')
 +                              /* nested untracked file */
 +                              dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
 +
 +                      if (buf.buf[0] == 'u' ||
 +                          buf.buf[0] == '2' ||
 +                          memcmp(buf.buf + 5, "S..U", 4))
 +                              /* other change */
 +                              dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
                }
 -              next_line = strchr(line, '\n');
 -              if (!next_line)
 +
 +              if ((dirty_submodule & DIRTY_SUBMODULE_MODIFIED) &&
 +                  ((dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ||
 +                   ignore_untracked)) {
 +                      /*
 +                       * We're not interested in any further information from
 +                       * the child any more, neither output nor its exit code.
 +                       */
 +                      ignore_cp_exit_code = 1;
                        break;
 -              next_line++;
 -              len -= (next_line - line);
 -              line = next_line;
 +              }
        }
 -      close(cp.out);
 +      fclose(fp);
  
 -      if (finish_command(&cp))
 -              die("'git status --porcelain' failed in submodule %s", path);
 +      if (finish_command(&cp) && !ignore_cp_exit_code)
 +              die("'git status --porcelain=2' failed in submodule %s", path);
  
        strbuf_release(&buf);
        return dirty_submodule;
        return ret;
  }
  
 +static const char *get_super_prefix_or_empty(void)
 +{
 +      const char *s = get_super_prefix();
 +      if (!s)
 +              s = "";
 +      return s;
 +}
 +
 +static int submodule_has_dirty_index(const struct submodule *sub)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +
 +      prepare_submodule_repo_env_no_git_dir(&cp.env_array);
 +
 +      cp.git_cmd = 1;
 +      argv_array_pushl(&cp.args, "diff-index", "--quiet",
 +                                 "--cached", "HEAD", NULL);
 +      cp.no_stdin = 1;
 +      cp.no_stdout = 1;
 +      cp.dir = sub->path;
 +      if (start_command(&cp))
 +              die("could not recurse into submodule '%s'", sub->path);
 +
 +      return finish_command(&cp);
 +}
 +
 +static void submodule_reset_index(const char *path)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      prepare_submodule_repo_env_no_git_dir(&cp.env_array);
 +
 +      cp.git_cmd = 1;
 +      cp.no_stdin = 1;
 +      cp.dir = path;
 +
 +      argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
 +                                 get_super_prefix_or_empty(), path);
 +      argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
 +
 +      argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
 +
 +      if (run_command(&cp))
 +              die("could not reset submodule index");
 +}
 +
 +/**
 + * Moves a submodule at a given path from a given head to another new head.
 + * For edge cases (a submodule coming into existence or removing a submodule)
 + * pass NULL for old or new respectively.
 + */
 +int submodule_move_head(const char *path,
 +                       const char *old,
 +                       const char *new,
 +                       unsigned flags)
 +{
 +      int ret = 0;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      const struct submodule *sub;
 +
 +      sub = submodule_from_path(null_sha1, path);
 +
 +      if (!sub)
 +              die("BUG: could not get submodule information for '%s'", path);
 +
 +      if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) {
 +              /* Check if the submodule has a dirty index. */
 +              if (submodule_has_dirty_index(sub))
 +                      return error(_("submodule '%s' has dirty index"), path);
 +      }
 +
 +      if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
 +              if (old) {
 +                      if (!submodule_uses_gitfile(path))
 +                              absorb_git_dir_into_superproject("", path,
 +                                      ABSORB_GITDIR_RECURSE_SUBMODULES);
 +              } else {
 +                      struct strbuf sb = STRBUF_INIT;
 +                      strbuf_addf(&sb, "%s/modules/%s",
 +                                  get_git_common_dir(), sub->name);
 +                      connect_work_tree_and_git_dir(path, sb.buf);
 +                      strbuf_release(&sb);
 +
 +                      /* make sure the index is clean as well */
 +                      submodule_reset_index(path);
 +              }
 +      }
 +
 +      prepare_submodule_repo_env_no_git_dir(&cp.env_array);
 +
 +      cp.git_cmd = 1;
 +      cp.no_stdin = 1;
 +      cp.dir = path;
 +
 +      argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
 +                      get_super_prefix_or_empty(), path);
 +      argv_array_pushl(&cp.args, "read-tree", NULL);
 +
 +      if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
 +              argv_array_push(&cp.args, "-n");
 +      else
 +              argv_array_push(&cp.args, "-u");
 +
 +      if (flags & SUBMODULE_MOVE_HEAD_FORCE)
 +              argv_array_push(&cp.args, "--reset");
 +      else
 +              argv_array_push(&cp.args, "-m");
 +
 +      argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX);
 +      argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX);
 +
 +      if (run_command(&cp)) {
 +              ret = -1;
 +              goto out;
 +      }
 +
 +      if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
 +              if (new) {
 +                      struct child_process cp1 = CHILD_PROCESS_INIT;
 +                      /* also set the HEAD accordingly */
 +                      cp1.git_cmd = 1;
 +                      cp1.no_stdin = 1;
 +                      cp1.dir = path;
 +
 +                      argv_array_pushl(&cp1.args, "update-ref", "HEAD",
 +                                       new ? new : EMPTY_TREE_SHA1_HEX, NULL);
 +
 +                      if (run_command(&cp1)) {
 +                              ret = -1;
 +                              goto out;
 +                      }
 +              } else {
 +                      struct strbuf sb = STRBUF_INIT;
 +
 +                      strbuf_addf(&sb, "%s/.git", path);
 +                      unlink_or_warn(sb.buf);
 +                      strbuf_release(&sb);
 +
 +                      if (is_empty_dir(path))
 +                              rmdir_or_warn(path);
 +              }
 +      }
 +out:
 +      return ret;
 +}
 +
  static int find_first_merges(struct object_array *result, const char *path,
                struct commit *a, struct commit *b)
  {
        memset(&rev_opts, 0, sizeof(rev_opts));
  
        /* get all revisions that merge commit a */
 -      snprintf(merged_revision, sizeof(merged_revision), "^%s",
 +      xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
                        oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
@@@ -1598,6 -1371,18 +1598,6 @@@ int parallel_submodules(void
        return parallel_jobs;
  }
  
 -void prepare_submodule_repo_env(struct argv_array *out)
 -{
 -      const char * const *var;
 -
 -      for (var = local_repo_env; *var; var++) {
 -              if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
 -                      argv_array_push(out, *var);
 -      }
 -      argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
 -                       DEFAULT_GIT_DIR_ENVIRONMENT);
 -}
 -
  /*
   * Embeds a single submodules git directory into the superprojects git dir,
   * non recursively.
@@@ -1629,8 -1414,11 +1629,8 @@@ static void relocate_single_git_dir_int
                die(_("could not create directory '%s'"), new_git_dir);
        real_new_git_dir = real_pathdup(new_git_dir, 1);
  
 -      if (!prefix)
 -              prefix = get_super_prefix();
 -
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
 -              prefix ? prefix : "", path,
 +              get_super_prefix_or_empty(), path,
                real_old_git_dir, real_new_git_dir);
  
        relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@@@ -1657,6 -1445,8 +1657,6 @@@ void absorb_git_dir_into_superproject(c
  
        /* Not populated? */
        if (!sub_git_dir) {
 -              char *real_new_git_dir;
 -              const char *new_git_dir;
                const struct submodule *sub;
  
                if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
                sub = submodule_from_path(null_sha1, path);
                if (!sub)
                        die(_("could not lookup name for submodule '%s'"), path);
 -              new_git_dir = git_path("modules/%s", sub->name);
 -              if (safe_create_leading_directories_const(new_git_dir) < 0)
 -                      die(_("could not create directory '%s'"), new_git_dir);
 -              real_new_git_dir = real_pathdup(new_git_dir, 1);
 -              connect_work_tree_and_git_dir(path, real_new_git_dir);
 -
 -              free(real_new_git_dir);
 +              connect_work_tree_and_git_dir(path,
 +                      git_path("modules/%s", sub->name));
        } else {
                /* Is it already absorbed into the superprojects git dir? */
                char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
                if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES)
                        die("BUG: we don't know how to pass the flags down?");
  
 -              if (get_super_prefix())
 -                      strbuf_addstr(&sb, get_super_prefix());
 +              strbuf_addstr(&sb, get_super_prefix_or_empty());
                strbuf_addstr(&sb, path);
                strbuf_addch(&sb, '/');
  
diff --combined submodule.h
index 8a8bc49dc9626b9ba4112f3b0bf25b212d864d1d,decca25e370767dab6dd1acba1f19bc9d19f3706..a86f757a06720e1bc85d7e9ecad51ffbc6610dbe
@@@ -3,7 -3,7 +3,7 @@@
  
  struct diff_options;
  struct argv_array;
- struct sha1_array;
+ struct oid_array;
  
  enum {
        RECURSE_SUBMODULES_ONLY = -5,
@@@ -41,13 -41,7 +41,13 @@@ extern int submodule_config(const char 
  extern void gitmodules_config(void);
  extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
  extern int is_submodule_initialized(const char *path);
 -extern int is_submodule_populated(const char *path);
 +/*
 + * Determine if a submodule has been populated at a given 'path' by checking if
 + * the <path>/.git resolves to a valid git repository.
 + * If return_error_code is NULL, die on error.
 + * Otherwise the return error code is the same as of resolve_gitdir_gently.
 + */
 +extern int is_submodule_populated_gently(const char *path, int *return_error_code);
  extern int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst);
  extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
@@@ -64,15 -58,7 +64,15 @@@ extern void show_submodule_inline_diff(
                const char *del, const char *add, const char *reset,
                const struct diff_options *opt);
  extern void set_config_fetch_recurse_submodules(int value);
- extern void check_for_new_submodule_commits(unsigned char new_sha1[20]);
 +extern void set_config_update_recurse_submodules(int value);
 +/* Check if we want to update any submodule.*/
 +extern int should_update_submodules(void);
 +/*
 + * Returns the submodule struct if the given ce entry is a submodule
 + * and it should be updated. Returns NULL otherwise.
 + */
 +extern const struct submodule *submodule_from_ce(const struct cache_entry *ce);
+ extern void check_for_new_submodule_commits(struct object_id *oid);
  extern int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
                               int quiet, int max_parallel_jobs);
@@@ -87,22 -73,15 +87,22 @@@ extern int merge_submodule(unsigned cha
                           const unsigned char base[20],
                           const unsigned char a[20],
                           const unsigned char b[20], int search);
- extern int find_unpushed_submodules(struct sha1_array *commits,
+ extern int find_unpushed_submodules(struct oid_array *commits,
                                    const char *remotes_name,
                                    struct string_list *needs_pushing);
- extern int push_unpushed_submodules(struct sha1_array *commits,
+ extern int push_unpushed_submodules(struct oid_array *commits,
                                    const char *remotes_name,
                                    int dry_run);
  extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
  extern int parallel_submodules(void);
  
 +#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
 +#define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
 +extern int submodule_move_head(const char *path,
 +                             const char *old,
 +                             const char *new,
 +                             unsigned flags);
 +
  /*
   * Prepare the "env_array" parameter of a "struct child_process" for executing
   * a submodule by clearing any repo-specific envirionment variables, but