Merge branch 'bc/object-id'
authorJunio C Hamano <gitster@pobox.com>
Mon, 9 Apr 2018 23:25:45 +0000 (08:25 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 9 Apr 2018 23:25:45 +0000 (08:25 +0900)
Conversion from uchar[20] to struct object_id continues.

* bc/object-id: (36 commits)
convert: convert to struct object_id
sha1_file: introduce a constant for max header length
Convert lookup_replace_object to struct object_id
sha1_file: convert read_sha1_file to struct object_id
sha1_file: convert read_object_with_reference to object_id
tree-walk: convert tree entry functions to object_id
streaming: convert istream internals to struct object_id
tree-walk: convert get_tree_entry_follow_symlinks internals to object_id
builtin/notes: convert static functions to object_id
builtin/fmt-merge-msg: convert remaining code to object_id
sha1_file: convert sha1_object_info* to object_id
Convert remaining callers of sha1_object_info_extended to object_id
packfile: convert unpack_entry to struct object_id
sha1_file: convert retry_bad_packed_offset to struct object_id
sha1_file: convert assert_sha1_type to object_id
builtin/mktree: convert to struct object_id
streaming: convert open_istream to use struct object_id
sha1_file: convert check_sha1_signature to struct object_id
sha1_file: convert read_loose_object to use struct object_id
builtin/index-pack: convert struct ref_delta_entry to object_id
...

26 files changed:
1  2 
apply.c
builtin/branch.c
builtin/checkout.c
builtin/fetch.c
builtin/grep.c
builtin/index-pack.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/reflog.c
builtin/replace.c
builtin/rev-list.c
builtin/rm.c
builtin/tag.c
builtin/unpack-objects.c
builtin/worktree.c
cache.h
diff.c
fast-import.c
merge-recursive.c
read-cache.c
rerere.c
sequencer.c
sha1_file.c
strbuf.c
strbuf.h
diff --combined apply.c
index 134dc7ba78cddd99406b78a97898bd8a32393b4c,b1e1bd58bfa33f0568c5b9e3414441eb88ce9b1b..7e5792c996f430952b1b768f8267de851156ce83
+++ b/apply.c
@@@ -3180,7 -3180,7 +3180,7 @@@ static int apply_binary(struct apply_st
                unsigned long size;
                char *result;
  
-               result = read_sha1_file(oid.hash, &type, &size);
+               result = read_object_file(&oid, &type, &size);
                if (!result)
                        return error(_("the necessary postimage %s for "
                                       "'%s' cannot be read"),
@@@ -3242,7 -3242,7 +3242,7 @@@ static int read_blob_object(struct strb
                unsigned long sz;
                char *result;
  
-               result = read_sha1_file(oid->hash, &type, &sz);
+               result = read_object_file(oid, &type, &sz);
                if (!result)
                        return -1;
                /* XXX read_sha1_file NUL-terminates */
@@@ -4943,9 -4943,8 +4943,9 @@@ int apply_parse_options(int argc, cons
                        N_("make sure the patch is applicable to the current index")),
                OPT_BOOL(0, "cached", &state->cached,
                        N_("apply a patch without touching the working tree")),
 -              OPT_BOOL(0, "unsafe-paths", &state->unsafe_paths,
 -                      N_("accept a patch that touches outside the working area")),
 +              OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,
 +                         N_("accept a patch that touches outside the working area"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_BOOL(0, "apply", force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_BOOL('3', "3way", &state->threeway,
diff --combined builtin/branch.c
index 6d0cea9d4bcc4eb866280d6424a6dec32b5f9c87,659deb36f372ef3608eb4fd426e2b954103b822e..5bd2a0dd4891ce0d42ad897c7b5fe0f66ca73be4
@@@ -273,7 -273,7 +273,7 @@@ static int delete_branches(int argc, co
                               bname.buf,
                               (flags & REF_ISBROKEN) ? "broken"
                               : (flags & REF_ISSYMREF) ? target
-                              : find_unique_abbrev(oid.hash, DEFAULT_ABBREV));
+                              : find_unique_abbrev(&oid, DEFAULT_ABBREV));
                }
                delete_branch_config(bname.buf);
  
@@@ -615,7 -615,7 +615,7 @@@ int cmd_branch(int argc, const char **a
                OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
                OPT_BOOL(0, "edit-description", &edit_description,
                         N_("edit the description for the branch")),
 -              OPT__FORCE(&force, N_("force creation, move/rename, deletion")),
 +              OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
                OPT_MERGED(&filter, N_("print only branches that are merged")),
                OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
                OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
diff --combined builtin/checkout.c
index d76e13c8524003fcc5c55d706c1177f66520b9d4,45968c2d85f2e204f450a616a62dd8f26097c6cc..b49b5820718335ba6a70b70ef339ece7157281cc
@@@ -66,7 -66,7 +66,7 @@@ static int post_checkout_hook(struct co
  
  }
  
- static int update_some(const unsigned char *sha1, struct strbuf *base,
+ static int update_some(const struct object_id *oid, struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
  {
        int len;
@@@ -78,7 -78,7 +78,7 @@@
  
        len = base->len + strlen(pathname);
        ce = xcalloc(1, cache_entry_size(len));
-       hashcpy(ce->oid.hash, sha1);
+       oidcpy(&ce->oid, oid);
        memcpy(ce->name, base->buf, base->len);
        memcpy(ce->name + base->len, pathname, len - base->len);
        ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
@@@ -405,10 -405,10 +405,10 @@@ static void describe_detached_head(cons
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        if (print_sha1_ellipsis()) {
                fprintf(stderr, "%s %s... %s\n", msg,
-                       find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
+                       find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
        } else {
                fprintf(stderr, "%s %s %s\n", msg,
-                       find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
+                       find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
        }
        strbuf_release(&sb);
  }
@@@ -720,7 -720,7 +720,7 @@@ static int add_pending_uninteresting_re
  static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
  {
        strbuf_addstr(sb, "  ");
-       strbuf_add_unique_abbrev(sb, commit->object.oid.hash, DEFAULT_ABBREV);
+       strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
        strbuf_addch(sb, ' ');
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
@@@ -778,7 -778,7 +778,7 @@@ static void suggest_reattach(struct com
                        " git branch <new-branch-name> %s\n\n",
                        /* Give ngettext() the count */
                        lost),
-                       find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
+                       find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
  }
  
  /*
@@@ -1117,12 -1117,9 +1117,12 @@@ int cmd_checkout(int argc, const char *
                            2),
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
                            3),
 -              OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")),
 +              OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
 -              OPT_BOOL(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")),
 +              OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
 +                         N_("update ignored files (default)"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_STRING(0, "conflict", &conflict_style, N_("style"),
                           N_("conflict style (merge or diff3)")),
                OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
diff --combined builtin/fetch.c
index 6d73656a486fed1afd031e6cf5e26e1c2039e0cb,90bb659390e6df7fb51e153de662761ebe33a414..8295f92b3e63d2675b8238d6bc17e9b1fc81319a
@@@ -126,7 -126,7 +126,7 @@@ static struct option builtin_fetch_opti
                 N_("append to .git/FETCH_HEAD instead of overwriting")),
        OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
                   N_("path to upload pack on remote end")),
 -      OPT__FORCE(&force, N_("force overwrite of local branch")),
 +      OPT__FORCE(&force, N_("force overwrite of local branch"), 0),
        OPT_BOOL('m', "multiple", &multiple,
                 N_("fetch from multiple remotes")),
        OPT_SET_INT('t', "tags", &tags,
@@@ -637,7 -637,7 +637,7 @@@ static int update_local_ref(struct ref 
        struct branch *current_branch = branch_get(NULL);
        const char *pretty_ref = prettify_refname(ref->name);
  
-       type = sha1_object_info(ref->new_oid.hash, NULL);
+       type = oid_object_info(&ref->new_oid, NULL);
        if (type < 0)
                die(_("object %s not found"), oid_to_hex(&ref->new_oid));
  
        if (in_merge_bases(current, updated)) {
                struct strbuf quickref = STRBUF_INIT;
                int r;
-               strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "..");
-               strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(&ref->new_oid);
        } else if (force || ref->force) {
                struct strbuf quickref = STRBUF_INIT;
                int r;
-               strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "...");
-               strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(&ref->new_oid);
diff --combined builtin/grep.c
index 789a89133aca7b8eeb93a936fd2301bd3f37d0c7,bf50bab6f8f1bcf20110af6036c1eb924b70f78b..668cb8050ae68a729cf522011f36d727fff04cfb
@@@ -306,7 -306,7 +306,7 @@@ static void *lock_and_read_oid_file(con
        void *data;
  
        grep_read_lock();
-       data = read_sha1_file(oid->hash, type, size);
+       data = read_object_file(oid, type, size);
        grep_read_unlock();
        return data;
  }
@@@ -452,7 -452,7 +452,7 @@@ static int grep_submodule(struct grep_o
                object = parse_object_or_die(oid, oid_to_hex(oid));
  
                grep_read_lock();
-               data = read_object_with_reference(object->oid.hash, tree_type,
+               data = read_object_with_reference(&object->oid, tree_type,
                                                  &size, NULL);
                grep_read_unlock();
  
@@@ -614,7 -614,7 +614,7 @@@ static int grep_object(struct grep_opt 
                int hit, len;
  
                grep_read_lock();
-               data = read_object_with_reference(obj->oid.hash, tree_type,
+               data = read_object_with_reference(&obj->oid, tree_type,
                                                  &size, NULL);
                grep_read_unlock();
  
@@@ -839,9 -839,8 +839,9 @@@ int cmd_grep(int argc, const char **arg
                OPT_BOOL('L', "files-without-match",
                        &opt.unmatch_name_only,
                        N_("show only the names of files without match")),
 -              OPT_BOOL('z', "null", &opt.null_following_name,
 -                      N_("print NUL after filenames")),
 +              OPT_BOOL_F('z', "null", &opt.null_following_name,
 +                         N_("print NUL after filenames"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('c', "count", &opt.count,
                        N_("show the number of matches instead of matching lines")),
                OPT__COLOR(&opt.color, N_("highlight matches")),
                OPT_GROUP(""),
                { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
                        N_("pager"), N_("show matching files in the pager"),
 -                      PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
 -              OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored,
 -                       N_("allow calling of grep(1) (ignored by this build)")),
 +                      PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE,
 +                      NULL, (intptr_t)default_pager },
 +              OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
 +                         N_("allow calling of grep(1) (ignored by this build)"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
  
diff --combined builtin/index-pack.c
index bda84a92effe41adb1e50a06ff6accac0563d04a,7873e7243ff1cd7bb705384d5579ae2c7334663a..657a5dda06556baf909134f98c96029c615ae719
@@@ -49,7 -49,6 +49,7 @@@ struct thread_local 
        int pack_fd;
  };
  
 +/* Remember to update object flag allocation in object.h */
  #define FLAG_LINK (1u<<20)
  #define FLAG_CHECKED (1u<<21)
  
@@@ -59,7 -58,7 +59,7 @@@ struct ofs_delta_entry 
  };
  
  struct ref_delta_entry {
-       unsigned char sha1[20];
+       struct object_id oid;
        int obj_no;
  };
  
@@@ -222,7 -221,7 +222,7 @@@ static unsigned check_object(struct obj
  
        if (!(obj->flags & FLAG_CHECKED)) {
                unsigned long size;
-               int type = sha1_object_info(obj->oid.hash, &size);
+               int type = oid_object_info(&obj->oid, &size);
                if (type <= 0)
                        die(_("did not receive expected object %s"),
                              oid_to_hex(&obj->oid));
@@@ -672,18 -671,18 +672,18 @@@ static void find_ofs_delta_children(off
        *last_index = last;
  }
  
- static int compare_ref_delta_bases(const unsigned char *sha1,
-                                  const unsigned char *sha2,
+ static int compare_ref_delta_bases(const struct object_id *oid1,
+                                  const struct object_id *oid2,
                                   enum object_type type1,
                                   enum object_type type2)
  {
        int cmp = type1 - type2;
        if (cmp)
                return cmp;
-       return hashcmp(sha1, sha2);
+       return oidcmp(oid1, oid2);
  }
  
- static int find_ref_delta(const unsigned char *sha1, enum object_type type)
+ static int find_ref_delta(const struct object_id *oid, enum object_type type)
  {
        int first = 0, last = nr_ref_deltas;
  
                struct ref_delta_entry *delta = &ref_deltas[next];
                int cmp;
  
-               cmp = compare_ref_delta_bases(sha1, delta->sha1,
+               cmp = compare_ref_delta_bases(oid, &delta->oid,
                                              type, objects[delta->obj_no].type);
                if (!cmp)
                        return next;
        return -first-1;
  }
  
- static void find_ref_delta_children(const unsigned char *sha1,
+ static void find_ref_delta_children(const struct object_id *oid,
                                    int *first_index, int *last_index,
                                    enum object_type type)
  {
-       int first = find_ref_delta(sha1, type);
+       int first = find_ref_delta(oid, type);
        int last = first;
        int end = nr_ref_deltas - 1;
  
                *last_index = -1;
                return;
        }
-       while (first > 0 && !hashcmp(ref_deltas[first - 1].sha1, sha1))
+       while (first > 0 && !oidcmp(&ref_deltas[first - 1].oid, oid))
                --first;
-       while (last < end && !hashcmp(ref_deltas[last + 1].sha1, sha1))
+       while (last < end && !oidcmp(&ref_deltas[last + 1].oid, oid))
                ++last;
        *first_index = first;
        *last_index = last;
@@@ -772,7 -771,7 +772,7 @@@ static int check_collison(struct object
  
        memset(&data, 0, sizeof(data));
        data.entry = entry;
-       data.st = open_istream(entry->idx.oid.hash, &type, &size, NULL);
+       data.st = open_istream(&entry->idx.oid, &type, &size, NULL);
        if (!data.st)
                return -1;
        if (size != entry->size || type != entry->type)
@@@ -811,12 -810,12 +811,12 @@@ static void sha1_object(const void *dat
                enum object_type has_type;
                unsigned long has_size;
                read_lock();
-               has_type = sha1_object_info(oid->hash, &has_size);
+               has_type = oid_object_info(oid, &has_size);
                if (has_type < 0)
                        die(_("cannot read existing object info %s"), oid_to_hex(oid));
                if (has_type != type || has_size != size)
                        die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
-               has_data = read_sha1_file(oid->hash, &has_type, &has_size);
+               has_data = read_object_file(oid, &has_type, &has_size);
                read_unlock();
                if (!data)
                        data = new_data = get_data_from_pack(obj_entry);
                free(has_data);
        }
  
 -      if (strict) {
 +      if (strict || do_fsck_object) {
                read_lock();
                if (type == OBJ_BLOB) {
                        struct blob *blob = lookup_blob(oid);
                        if (do_fsck_object &&
                            fsck_object(obj, buf, size, &fsck_options))
                                die(_("Error in object"));
 -                      if (fsck_walk(obj, NULL, &fsck_options))
 +                      if (strict && fsck_walk(obj, NULL, &fsck_options))
                                die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
  
                        if (obj->type == OBJ_TREE) {
@@@ -992,7 -991,7 +992,7 @@@ static struct base_data *find_unresolve
                                                  struct base_data *prev_base)
  {
        if (base->ref_last == -1 && base->ofs_last == -1) {
-               find_ref_delta_children(base->obj->idx.oid.hash,
+               find_ref_delta_children(&base->obj->idx.oid,
                                        &base->ref_first, &base->ref_last,
                                        OBJ_REF_DELTA);
  
@@@ -1076,7 -1075,7 +1076,7 @@@ static int compare_ref_delta_entry(cons
        const struct ref_delta_entry *delta_a = a;
        const struct ref_delta_entry *delta_b = b;
  
-       return hashcmp(delta_a->sha1, delta_b->sha1);
+       return oidcmp(&delta_a->oid, &delta_b->oid);
  }
  
  static void resolve_base(struct object_entry *obj)
@@@ -1142,7 -1141,7 +1142,7 @@@ static void parse_pack_objects(unsigne
                        ofs_delta++;
                } else if (obj->type == OBJ_REF_DELTA) {
                        ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc);
-                       hashcpy(ref_deltas[nr_ref_deltas].sha1, ref_delta_oid.hash);
+                       oidcpy(&ref_deltas[nr_ref_deltas].oid, &ref_delta_oid);
                        ref_deltas[nr_ref_deltas].obj_no = i;
                        nr_ref_deltas++;
                } else if (!data) {
@@@ -1374,14 -1373,15 +1374,15 @@@ static void fix_unresolved_deltas(struc
  
                if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
                        continue;
-               base_obj->data = read_sha1_file(d->sha1, &type, &base_obj->size);
+               base_obj->data = read_object_file(&d->oid, &type,
+                                                 &base_obj->size);
                if (!base_obj->data)
                        continue;
  
-               if (check_sha1_signature(d->sha1, base_obj->data,
+               if (check_object_signature(&d->oid, base_obj->data,
                                base_obj->size, type_name(type)))
-                       die(_("local object %s is corrupt"), sha1_to_hex(d->sha1));
-               base_obj->obj = append_obj_to_pack(f, d->sha1,
+                       die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
+               base_obj->obj = append_obj_to_pack(f, d->oid.hash,
                                        base_obj->data, base_obj->size, type);
                find_unresolved_deltas(base_obj);
                display_progress(progress, nr_resolved_deltas);
@@@ -1689,8 -1689,6 +1690,8 @@@ int cmd_index_pack(int argc, const cha
                        } else if (!strcmp(arg, "--check-self-contained-and-connected")) {
                                strict = 1;
                                check_self_contained_and_connected = 1;
 +                      } else if (!strcmp(arg, "--fsck-objects")) {
 +                              do_fsck_object = 1;
                        } else if (!strcmp(arg, "--verify")) {
                                verify = 1;
                        } else if (!strcmp(arg, "--verify-stat")) {
diff --combined builtin/merge.c
index ee050a47f34d7394d048f955baabb37a9e716ef8,b4f32105b5ddaa6ecb346f5926b34105ada3f066..8746c5e3e867d79c85728974825ebca4b07a2e9a
@@@ -639,7 -639,7 +639,7 @@@ static int read_tree_trivial(struct obj
  
  static void write_tree_trivial(struct object_id *oid)
  {
-       if (write_cache_as_tree(oid->hash, 0, NULL))
+       if (write_cache_as_tree(oid, 0, NULL))
                die(_("git write-tree failed to write a tree"));
  }
  
@@@ -652,9 -652,10 +652,9 @@@ static int try_merge_strategy(const cha
  
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
 -      if (active_cache_changed &&
 -          write_locked_index(&the_index, &lock, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
                return error(_("Unable to write index."));
 -      rollback_lock_file(&lock);
  
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
                int clean, x;
                                remoteheads->item, reversed, &result);
                if (clean < 0)
                        exit(128);
 -              if (active_cache_changed &&
 -                  write_locked_index(&the_index, &lock, COMMIT_LOCK))
 +              if (write_locked_index(&the_index, &lock,
 +                                     COMMIT_LOCK | SKIP_IF_UNCHANGED))
                        die (_("unable to write %s"), get_index_file());
 -              rollback_lock_file(&lock);
                return clean ? 0 : 1;
        } else {
                return try_merge_command(strategy, xopts_nr, xopts,
@@@ -809,9 -811,10 +809,9 @@@ static int merge_trivial(struct commit 
  
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
 -      if (active_cache_changed &&
 -          write_locked_index(&the_index, &lock, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
                return error(_("Unable to write index."));
 -      rollback_lock_file(&lock);
  
        write_tree_trivial(&result_tree);
        printf(_("Wonderful.\n"));
@@@ -1324,7 -1327,7 +1324,7 @@@ int cmd_merge(int argc, const char **ar
  
                        check_commit_signature(commit, &signature_check);
  
-                       find_unique_abbrev_r(hex, commit->object.oid.hash, DEFAULT_ABBREV);
+                       find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
                        switch (signature_check.result) {
                        case 'G':
                                break;
  
                if (verbosity >= 0) {
                        printf(_("Updating %s..%s\n"),
-                              find_unique_abbrev(head_commit->object.oid.hash,
+                              find_unique_abbrev(&head_commit->object.oid,
                                                  DEFAULT_ABBREV),
-                              find_unique_abbrev(remoteheads->item->object.oid.hash,
+                              find_unique_abbrev(&remoteheads->item->object.oid,
                                                  DEFAULT_ABBREV));
                }
                strbuf_addstr(&msg, "Fast-forward");
diff --combined builtin/notes.c
index 6d2fda4a7d7ba89633f5cfee73f39cacb9f27898,5ddfa44cde469afb196eb9be340f54d8d43e75f1..921e08d5bf545ac4124933459d257d95e2144409
@@@ -118,11 -118,11 +118,11 @@@ static int list_each_note(const struct 
        return 0;
  }
  
- static void copy_obj_to_fd(int fd, const unsigned char *sha1)
+ static void copy_obj_to_fd(int fd, const struct object_id *oid)
  {
        unsigned long size;
        enum object_type type;
-       char *buf = read_sha1_file(sha1, &type, &size);
+       char *buf = read_object_file(oid, &type, &size);
        if (buf) {
                if (size)
                        write_or_die(fd, buf, size);
@@@ -162,7 -162,7 +162,7 @@@ static void write_commented_object(int 
  }
  
  static void prepare_note_data(const struct object_id *object, struct note_data *d,
-               const unsigned char *old_note)
+               const struct object_id *old_note)
  {
        if (d->use_editor || !d->given) {
                int fd;
@@@ -253,7 -253,7 +253,7 @@@ static int parse_reuse_arg(const struc
  
        if (get_oid(arg, &object))
                die(_("failed to resolve '%s' as a valid ref."), arg);
-       if (!(buf = read_sha1_file(object.hash, &type, &len))) {
+       if (!(buf = read_object_file(&object, &type, &len))) {
                free(buf);
                die(_("failed to read object '%s'."), arg);
        }
@@@ -413,7 -413,7 +413,7 @@@ static int add(int argc, const char **a
                        parse_reuse_arg},
                OPT_BOOL(0, "allow-empty", &allow_empty,
                        N_("allow storing empty note")),
 -              OPT__FORCE(&force, N_("replace existing notes")),
 +              OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
  
                        oid_to_hex(&object));
        }
  
-       prepare_note_data(&object, &d, note ? note->hash : NULL);
+       prepare_note_data(&object, &d, note);
        if (d.buf.len || allow_empty) {
                write_note_data(&d, &new_note);
                if (add_note(t, &object, &new_note, combine_notes_overwrite))
@@@ -484,7 -484,7 +484,7 @@@ static int copy(int argc, const char **
        struct notes_tree *t;
        const char *rewrite_cmd = NULL;
        struct option options[] = {
 -              OPT__FORCE(&force, N_("replace existing notes")),
 +              OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
                OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")),
                OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"),
                           N_("load rewriting config for <command> (implies "
@@@ -602,13 -602,13 +602,13 @@@ static int append_edit(int argc, const 
        t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
        note = get_note(t, &object);
  
-       prepare_note_data(&object, &d, edit && note ? note->hash : NULL);
+       prepare_note_data(&object, &d, edit && note ? note : NULL);
  
        if (note && !edit) {
                /* Append buf to previous note contents */
                unsigned long size;
                enum object_type type;
-               char *prev_buf = read_sha1_file(note->hash, &type, &size);
+               char *prev_buf = read_object_file(note, &type, &size);
  
                strbuf_grow(&d.buf, size + 1);
                if (d.buf.len && prev_buf && size)
diff --combined builtin/pack-objects.c
index e9d3cfb9e33a6b874751ac6acc5aac2361d4a58d,a1d674608a3477da14f4caf426b17aefe82d0dd9..e7e673266e8e2e6fcbe7723c851c6d216eecb84c
@@@ -122,11 -122,10 +122,10 @@@ static void *get_delta(struct object_en
        void *buf, *base_buf, *delta_buf;
        enum object_type type;
  
-       buf = read_sha1_file(entry->idx.oid.hash, &type, &size);
+       buf = read_object_file(&entry->idx.oid, &type, &size);
        if (!buf)
                die("unable to read %s", oid_to_hex(&entry->idx.oid));
-       base_buf = read_sha1_file(entry->delta->idx.oid.hash, &type,
-                                 &base_size);
+       base_buf = read_object_file(&entry->delta->idx.oid, &type, &base_size);
        if (!base_buf)
                die("unable to read %s",
                    oid_to_hex(&entry->delta->idx.oid));
@@@ -267,11 -266,10 +266,10 @@@ static unsigned long write_no_reuse_obj
        if (!usable_delta) {
                if (entry->type == OBJ_BLOB &&
                    entry->size > big_file_threshold &&
-                   (st = open_istream(entry->idx.oid.hash, &type, &size, NULL)) != NULL)
+                   (st = open_istream(&entry->idx.oid, &type, &size, NULL)) != NULL)
                        buf = NULL;
                else {
-                       buf = read_sha1_file(entry->idx.oid.hash, &type,
-                                            &size);
+                       buf = read_object_file(&entry->idx.oid, &type, &size);
                        if (!buf)
                                die(_("unable to read %s"),
                                    oid_to_hex(&entry->idx.oid));
@@@ -1190,7 -1188,7 +1188,7 @@@ static struct pbase_tree_cache *pbase_t
        /* Did not find one.  Either we got a bogus request or
         * we need to read and perhaps cache.
         */
-       data = read_sha1_file(oid->hash, &type, &size);
+       data = read_object_file(oid, &type, &size);
        if (!data)
                return NULL;
        if (type != OBJ_TREE) {
@@@ -1351,7 -1349,7 +1349,7 @@@ static void add_preferred_base(struct o
        if (window <= num_preferred_base++)
                return;
  
-       data = read_object_with_reference(oid->hash, tree_type, &size, tree_oid.hash);
+       data = read_object_with_reference(oid, tree_type, &size, &tree_oid);
        if (!data)
                return;
  
@@@ -1516,7 -1514,7 +1514,7 @@@ static void check_object(struct object_
                unuse_pack(&w_curs);
        }
  
-       entry->type = sha1_object_info(entry->idx.oid.hash, &entry->size);
+       entry->type = oid_object_info(&entry->idx.oid, &entry->size);
        /*
         * The error condition is checked in prepare_pack().  This is
         * to permit a missing preferred base object to be ignored
@@@ -1578,8 -1576,7 +1576,7 @@@ static void drop_reused_delta(struct ob
                 * And if that fails, the error will be recorded in entry->type
                 * and dealt with in prepare_pack().
                 */
-               entry->type = sha1_object_info(entry->idx.oid.hash,
-                                              &entry->size);
+               entry->type = oid_object_info(&entry->idx.oid, &entry->size);
        }
  }
  
@@@ -1871,8 -1868,7 +1868,7 @@@ static int try_delta(struct unpacked *t
        /* Load data if not already done */
        if (!trg->data) {
                read_lock();
-               trg->data = read_sha1_file(trg_entry->idx.oid.hash, &type,
-                                          &sz);
+               trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
                read_unlock();
                if (!trg->data)
                        die("object %s cannot be read",
        }
        if (!src->data) {
                read_lock();
-               src->data = read_sha1_file(src_entry->idx.oid.hash, &type,
-                                          &sz);
+               src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
                read_unlock();
                if (!src->data) {
                        if (src_entry->preferred_base) {
@@@ -2549,7 -2544,6 +2544,7 @@@ static void read_object_list_from_stdin
        }
  }
  
 +/* Remember to update object flag allocation in object.h */
  #define OBJECT_ADDED (1u<<20)
  
  static void show_commit(struct commit *commit, void *data)
@@@ -2709,7 -2703,7 +2704,7 @@@ static void add_objects_in_unpacked_pac
  static int add_loose_object(const struct object_id *oid, const char *path,
                            void *data)
  {
-       enum object_type type = sha1_object_info(oid->hash, NULL);
+       enum object_type type = oid_object_info(oid, NULL);
  
        if (type < 0) {
                warning("loose object at %s could not be examined", path);
diff --combined builtin/reflog.c
index 4719a5354cf182eb91b257a99da97b9b81da5257,25f7c5ef0b8887d5a1b5003e7a6d02da081aa7c9..a89bd1dd25252ddfe5ad6b32ee89950d3a4b258f
@@@ -52,7 -52,6 +52,7 @@@ struct collect_reflog_cb 
        int nr;
  };
  
 +/* Remember to update object flag allocation in object.h */
  #define INCOMPLETE    (1u<<10)
  #define STUDYING      (1u<<11)
  #define REACHABLE     (1u<<12)
@@@ -75,7 -74,7 +75,7 @@@ static int tree_is_complete(const struc
        if (!tree->buffer) {
                enum object_type type;
                unsigned long size;
-               void *data = read_sha1_file(oid->hash, &type, &size);
+               void *data = read_object_file(oid, &type, &size);
                if (!data) {
                        tree->object.flags |= INCOMPLETE;
                        return 0;
diff --combined builtin/replace.c
index 482f12018fa912eeea860721af2acbf5f8a2e87d,19006e52bc644eca598aec76c5ccda6cbdc8c334..935647be6bdf2ffc2b460038a10e63c2eb387a5c
@@@ -53,8 -53,8 +53,8 @@@ static int show_reference(const char *r
                        if (get_oid(refname, &object))
                                return error("Failed to resolve '%s' as a valid ref.", refname);
  
-                       obj_type = sha1_object_info(object.hash, NULL);
-                       repl_type = sha1_object_info(oid->hash, NULL);
+                       obj_type = oid_object_info(&object, NULL);
+                       repl_type = oid_object_info(oid, NULL);
  
                        printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
                               oid_to_hex(oid), type_name(repl_type));
@@@ -162,8 -162,8 +162,8 @@@ static int replace_object_oid(const cha
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
  
-       obj_type = sha1_object_info(object->hash, NULL);
-       repl_type = sha1_object_info(repl->hash, NULL);
+       obj_type = oid_object_info(object, NULL);
+       repl_type = oid_object_info(repl, NULL);
        if (!force && obj_type != repl_type)
                die("Objects must be of the same type.\n"
                    "'%s' points to a replaced object of type '%s'\n"
@@@ -290,7 -290,7 +290,7 @@@ static int edit_and_replace(const char 
        if (get_oid(object_ref, &old_oid) < 0)
                die("Not a valid object name: '%s'", object_ref);
  
-       type = sha1_object_info(old_oid.hash, NULL);
+       type = oid_object_info(&old_oid, NULL);
        if (type < 0)
                die("unable to get object type for %s", oid_to_hex(&old_oid));
  
@@@ -439,8 -439,7 +439,8 @@@ int cmd_replace(int argc, const char **
                OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
                OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
                OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
 -              OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
 +              OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
                OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
diff --combined builtin/rev-list.c
index 6f5b9b0847321ca214b4d32719eaeacefffd5ce4,2fd72c0be5a643c98d97ff779e449541e48434cd..fadd3ec14cbf0469c332a85278e5d1b4932ef788
@@@ -108,7 -108,7 +108,7 @@@ static void show_commit(struct commit *
        if (!revs->graph)
                fputs(get_revision_mark(revs, commit), stdout);
        if (revs->abbrev_commit && revs->abbrev)
-               fputs(find_unique_abbrev(commit->object.oid.hash, revs->abbrev),
+               fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
                      stdout);
        else
                fputs(oid_to_hex(&commit->object.oid), stdout);
@@@ -536,7 -536,7 +536,7 @@@ int cmd_rev_list(int argc, const char *
                mark_edges_uninteresting(&revs, show_edge);
  
        if (bisect_list) {
 -              int reaches = reaches, all = all;
 +              int reaches, all;
  
                find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
  
diff --combined builtin/rm.c
index 4447bb4d0faf8c34f3fc96361a651eaad396d6d4,974a7ef5bf5f9b427e4083caed09f04dcfddd2b3..5b6fc7ee818be4a4f060dc06f12fb45a25a2ea9b
@@@ -178,7 -178,7 +178,7 @@@ static int check_local_mod(struct objec
                 * way as changed from the HEAD.
                 */
                if (no_head
-                    || get_tree_entry(head->hash, name, oid.hash, &mode)
+                    || get_tree_entry(head, name, &oid, &mode)
                     || ce->ce_mode != create_ce_mode(mode)
                     || oidcmp(&ce->oid, &oid))
                        staged_changes = 1;
@@@ -242,7 -242,7 +242,7 @@@ static struct option builtin_rm_options
        OPT__DRY_RUN(&show_only, N_("dry run")),
        OPT__QUIET(&quiet, N_("do not list removed files")),
        OPT_BOOL( 0 , "cached",         &index_only, N_("only remove from the index")),
 -      OPT__FORCE(&force, N_("override the up-to-date check")),
 +      OPT__FORCE(&force, N_("override the up-to-date check"), PARSE_OPT_NOCOMPLETE),
        OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
        OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
                                N_("exit with a zero status even if nothing matched")),
@@@ -385,9 -385,10 +385,9 @@@ int cmd_rm(int argc, const char **argv
                        stage_updated_gitmodules(&the_index);
        }
  
 -      if (active_cache_changed) {
 -              if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 -                      die(_("Unable to write new index file"));
 -      }
 +      if (write_locked_index(&the_index, &lock_file,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
 +              die(_("Unable to write new index file"));
  
        return 0;
  }
diff --combined builtin/tag.c
index da186691ed8853bc5848c4106c752b015756eda3,86450bae0506b7ec75bd168566067010df0cb0a0..8cff6d0b727a572a34adcfe72246953b93c79edd
@@@ -99,7 -99,8 +99,8 @@@ static int delete_tag(const char *name
  {
        if (delete_ref(NULL, ref, oid, 0))
                return 1;
-       printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
+       printf(_("Deleted tag '%s' (was %s)\n"), name,
+              find_unique_abbrev(oid, DEFAULT_ABBREV));
        return 0;
  }
  
@@@ -167,7 -168,7 +168,7 @@@ static void write_tag_body(int fd, cons
        enum object_type type;
        char *buf, *sp;
  
-       buf = read_sha1_file(oid->hash, &type, &size);
+       buf = read_object_file(oid, &type, &size);
        if (!buf)
                return;
        /* skip header */
@@@ -211,7 -212,7 +212,7 @@@ static void create_tag(const struct obj
        struct strbuf header = STRBUF_INIT;
        char *path = NULL;
  
-       type = sha1_object_info(object->hash, NULL);
+       type = oid_object_info(object, NULL);
        if (type <= OBJ_NONE)
            die(_("bad object type."));
  
@@@ -293,17 -294,17 +294,17 @@@ static void create_reflog_msg(const str
                strbuf_addstr(sb, rla);
        } else {
                strbuf_addstr(sb, "tag: tagging ");
-               strbuf_add_unique_abbrev(sb, oid->hash, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(sb, oid, DEFAULT_ABBREV);
        }
  
        strbuf_addstr(sb, " (");
-       type = sha1_object_info(oid->hash, NULL);
+       type = oid_object_info(oid, NULL);
        switch (type) {
        default:
                strbuf_addstr(sb, "object of unknown type");
                break;
        case OBJ_COMMIT:
-               if ((buf = read_sha1_file(oid->hash, &type, &size)) != NULL) {
+               if ((buf = read_object_file(oid, &type, &size)) != NULL) {
                        subject_len = find_commit_subject(buf, &subject_start);
                        strbuf_insert(sb, sb->len, subject_start, subject_len);
                } else {
@@@ -397,7 -398,7 +398,7 @@@ int cmd_tag(int argc, const char **argv
                        N_("how to strip spaces and #comments from message")),
                OPT_STRING('u', "local-user", &keyid, N_("key-id"),
                                        N_("use another key to sign the tag")),
 -              OPT__FORCE(&force, N_("replace the tag if exists")),
 +              OPT__FORCE(&force, N_("replace the tag if exists"), 0),
                OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")),
  
                OPT_GROUP(N_("Tag listing options")),
                die("%s", err.buf);
        ref_transaction_free(transaction);
        if (force && !is_null_oid(&prev) && oidcmp(&prev, &object))
-               printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev.hash, DEFAULT_ABBREV));
+               printf(_("Updated tag '%s' (was %s)\n"), tag,
+                      find_unique_abbrev(&prev, DEFAULT_ABBREV));
  
        UNLEAK(buf);
        UNLEAK(ref);
diff --combined builtin/unpack-objects.c
index 6620feec68b15573340f4c28fe6be952e3c00e3a,af02e1bc6b3062a1863e798cd9812bbe9dac50bc..b7755c6cc5a05a536666d0ae2fd9d0c3171a6cfd
@@@ -158,7 -158,6 +158,7 @@@ struct obj_info 
        struct object *obj;
  };
  
 +/* Remember to update object flag allocation in object.h */
  #define FLAG_OPEN (1u<<20)
  #define FLAG_WRITTEN (1u<<21)
  
@@@ -199,7 -198,7 +199,7 @@@ static int check_object(struct object *
  
        if (!(obj->flags & FLAG_OPEN)) {
                unsigned long size;
-               int type = sha1_object_info(obj->oid.hash, &size);
+               int type = oid_object_info(&obj->oid, &size);
                if (type != obj->type || type <= 0)
                        die("object of unexpected type");
                obj->flags |= FLAG_WRITTEN;
@@@ -423,7 -422,7 +423,7 @@@ static void unpack_delta_entry(enum obj
        if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
                return;
  
-       base = read_sha1_file(base_oid.hash, &type, &base_size);
+       base = read_object_file(&base_oid, &type, &base_size);
        if (!base) {
                error("failed to read delta-pack base object %s",
                      oid_to_hex(&base_oid));
diff --combined builtin/worktree.c
index 670555deddaca8ff8050c03ea68c83c7b4e459e6,3442f257b7d1029ac3ff09ff2ef2107b3ef233e3..ba2cb877c3a2506ddb75fa69200e93facfc115c3
@@@ -17,9 -17,7 +17,9 @@@ static const char * const worktree_usag
        N_("git worktree add [<options>] <path> [<commit-ish>]"),
        N_("git worktree list [<options>]"),
        N_("git worktree lock [<options>] <path>"),
 +      N_("git worktree move <worktree> <new-path>"),
        N_("git worktree prune [<options>]"),
 +      N_("git worktree remove [<options>] <worktree>"),
        N_("git worktree unlock <path>"),
        NULL
  };
@@@ -381,9 -379,7 +381,9 @@@ static int add(int ac, const char **av
        const char *branch;
        const char *opt_track = NULL;
        struct option options[] = {
 -              OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 +              OPT__FORCE(&opts.force,
 +                         N_("checkout <branch> even if already checked out in other worktree"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
                           N_("create a new branch")),
                OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
@@@ -502,7 -498,7 +502,7 @@@ static void show_worktree(struct worktr
                strbuf_addstr(&sb, "(bare)");
        else {
                strbuf_addf(&sb, "%-*s ", abbrev_len,
-                               find_unique_abbrev(wt->head_oid.hash, DEFAULT_ABBREV));
+                               find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
                if (wt->is_detached)
                        strbuf_addstr(&sb, "(detached HEAD)");
                else if (wt->head_ref) {
@@@ -527,7 -523,7 +527,7 @@@ static void measure_widths(struct workt
  
                if (path_len > *maxlen)
                        *maxlen = path_len;
-               sha1_len = strlen(find_unique_abbrev(wt[i]->head_oid.hash, *abbrev));
+               sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
                if (sha1_len > *abbrev)
                        *abbrev = sha1_len;
        }
@@@ -623,220 -619,6 +623,220 @@@ static int unlock_worktree(int ac, cons
        return ret;
  }
  
 +static void validate_no_submodules(const struct worktree *wt)
 +{
 +      struct index_state istate = { NULL };
 +      int i, found_submodules = 0;
 +
 +      if (read_index_from(&istate, worktree_git_path(wt, "index"),
 +                          get_worktree_git_dir(wt)) > 0) {
 +              for (i = 0; i < istate.cache_nr; i++) {
 +                      struct cache_entry *ce = istate.cache[i];
 +
 +                      if (S_ISGITLINK(ce->ce_mode)) {
 +                              found_submodules = 1;
 +                              break;
 +                      }
 +              }
 +      }
 +      discard_index(&istate);
 +
 +      if (found_submodules)
 +              die(_("working trees containing submodules cannot be moved or removed"));
 +}
 +
 +static int move_worktree(int ac, const char **av, const char *prefix)
 +{
 +      struct option options[] = {
 +              OPT_END()
 +      };
 +      struct worktree **worktrees, *wt;
 +      struct strbuf dst = STRBUF_INIT;
 +      struct strbuf errmsg = STRBUF_INIT;
 +      const char *reason;
 +      char *path;
 +
 +      ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 +      if (ac != 2)
 +              usage_with_options(worktree_usage, options);
 +
 +      path = prefix_filename(prefix, av[1]);
 +      strbuf_addstr(&dst, path);
 +      free(path);
 +
 +      worktrees = get_worktrees(0);
 +      wt = find_worktree(worktrees, prefix, av[0]);
 +      if (!wt)
 +              die(_("'%s' is not a working tree"), av[0]);
 +      if (is_main_worktree(wt))
 +              die(_("'%s' is a main working tree"), av[0]);
 +      if (is_directory(dst.buf)) {
 +              const char *sep = find_last_dir_sep(wt->path);
 +
 +              if (!sep)
 +                      die(_("could not figure out destination name from '%s'"),
 +                          wt->path);
 +              strbuf_trim_trailing_dir_sep(&dst);
 +              strbuf_addstr(&dst, sep);
 +      }
 +      if (file_exists(dst.buf))
 +              die(_("target '%s' already exists"), dst.buf);
 +
 +      validate_no_submodules(wt);
 +
 +      reason = is_worktree_locked(wt);
 +      if (reason) {
 +              if (*reason)
 +                      die(_("cannot move a locked working tree, lock reason: %s"),
 +                          reason);
 +              die(_("cannot move a locked working tree"));
 +      }
 +      if (validate_worktree(wt, &errmsg, 0))
 +              die(_("validation failed, cannot move working tree: %s"),
 +                  errmsg.buf);
 +      strbuf_release(&errmsg);
 +
 +      if (rename(wt->path, dst.buf) == -1)
 +              die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf);
 +
 +      update_worktree_location(wt, dst.buf);
 +
 +      strbuf_release(&dst);
 +      free_worktrees(worktrees);
 +      return 0;
 +}
 +
 +/*
 + * Note, "git status --porcelain" is used to determine if it's safe to
 + * delete a whole worktree. "git status" does not ignore user
 + * configuration, so if a normal "git status" shows "clean" for the
 + * user, then it's ok to remove it.
 + *
 + * This assumption may be a bad one. We may want to ignore
 + * (potentially bad) user settings and only delete a worktree when
 + * it's absolutely safe to do so from _our_ point of view because we
 + * know better.
 + */
 +static void check_clean_worktree(struct worktree *wt,
 +                               const char *original_path)
 +{
 +      struct argv_array child_env = ARGV_ARRAY_INIT;
 +      struct child_process cp;
 +      char buf[1];
 +      int ret;
 +
 +      /*
 +       * Until we sort this out, all submodules are "dirty" and
 +       * will abort this function.
 +       */
 +      validate_no_submodules(wt);
 +
 +      argv_array_pushf(&child_env, "%s=%s/.git",
 +                       GIT_DIR_ENVIRONMENT, wt->path);
 +      argv_array_pushf(&child_env, "%s=%s",
 +                       GIT_WORK_TREE_ENVIRONMENT, wt->path);
 +      memset(&cp, 0, sizeof(cp));
 +      argv_array_pushl(&cp.args, "status",
 +                       "--porcelain", "--ignore-submodules=none",
 +                       NULL);
 +      cp.env = child_env.argv;
 +      cp.git_cmd = 1;
 +      cp.dir = wt->path;
 +      cp.out = -1;
 +      ret = start_command(&cp);
 +      if (ret)
 +              die_errno(_("failed to run 'git status' on '%s'"),
 +                        original_path);
 +      ret = xread(cp.out, buf, sizeof(buf));
 +      if (ret)
 +              die(_("'%s' is dirty, use --force to delete it"),
 +                  original_path);
 +      close(cp.out);
 +      ret = finish_command(&cp);
 +      if (ret)
 +              die_errno(_("failed to run 'git status' on '%s', code %d"),
 +                        original_path, ret);
 +}
 +
 +static int delete_git_work_tree(struct worktree *wt)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret = 0;
 +
 +      strbuf_addstr(&sb, wt->path);
 +      if (remove_dir_recursively(&sb, 0)) {
 +              error_errno(_("failed to delete '%s'"), sb.buf);
 +              ret = -1;
 +      }
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +static int delete_git_dir(struct worktree *wt)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret = 0;
 +
 +      strbuf_addstr(&sb, git_common_path("worktrees/%s", wt->id));
 +      if (remove_dir_recursively(&sb, 0)) {
 +              error_errno(_("failed to delete '%s'"), sb.buf);
 +              ret = -1;
 +      }
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +static int remove_worktree(int ac, const char **av, const char *prefix)
 +{
 +      int force = 0;
 +      struct option options[] = {
 +              OPT_BOOL(0, "force", &force,
 +                       N_("force removing even if the worktree is dirty")),
 +              OPT_END()
 +      };
 +      struct worktree **worktrees, *wt;
 +      struct strbuf errmsg = STRBUF_INIT;
 +      const char *reason;
 +      int ret = 0;
 +
 +      ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 +      if (ac != 1)
 +              usage_with_options(worktree_usage, options);
 +
 +      worktrees = get_worktrees(0);
 +      wt = find_worktree(worktrees, prefix, av[0]);
 +      if (!wt)
 +              die(_("'%s' is not a working tree"), av[0]);
 +      if (is_main_worktree(wt))
 +              die(_("'%s' is a main working tree"), av[0]);
 +      reason = is_worktree_locked(wt);
 +      if (reason) {
 +              if (*reason)
 +                      die(_("cannot remove a locked working tree, lock reason: %s"),
 +                          reason);
 +              die(_("cannot remove a locked working tree"));
 +      }
 +      if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
 +              die(_("validation failed, cannot remove working tree: %s"),
 +                  errmsg.buf);
 +      strbuf_release(&errmsg);
 +
 +      if (file_exists(wt->path)) {
 +              if (!force)
 +                      check_clean_worktree(wt, av[0]);
 +
 +              ret |= delete_git_work_tree(wt);
 +      }
 +      /*
 +       * continue on even if ret is non-zero, there's no going back
 +       * from here.
 +       */
 +      ret |= delete_git_dir(wt);
 +
 +      free_worktrees(worktrees);
 +      return ret;
 +}
 +
  int cmd_worktree(int ac, const char **av, const char *prefix)
  {
        struct option options[] = {
                return lock_worktree(ac - 1, av + 1, prefix);
        if (!strcmp(av[1], "unlock"))
                return unlock_worktree(ac - 1, av + 1, prefix);
 +      if (!strcmp(av[1], "move"))
 +              return move_worktree(ac - 1, av + 1, prefix);
 +      if (!strcmp(av[1], "remove"))
 +              return remove_worktree(ac - 1, av + 1, prefix);
        usage_with_options(worktree_usage, options);
  }
diff --combined cache.h
index 09f78084dad1377d81a1ba0a028a96f6770bf051,0491f0cbc43b16c20147272626a2f4fd7d17d0f8..83ba2d2b03d7fbe6b0a30822177ac2fad11e96b4
+++ b/cache.h
@@@ -599,7 -599,6 +599,7 @@@ extern int read_index_unmerged(struct i
  
  /* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 +#define SKIP_IF_UNCHANGED     (1 << 1)
  
  /*
   * Write the index while holding an already-taken lock. Close the lock,
   * With `COMMIT_LOCK`, the lock is always committed or rolled back.
   * Without it, the lock is closed, but neither committed nor rolled
   * back.
 + *
 + * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
 + * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
   */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
  
@@@ -955,14 -951,14 +955,14 @@@ extern void sha1_file_name(struct strbu
   * more calls to find_unique_abbrev are made.
   *
   * The `_r` variant writes to a buffer supplied by the caller, which must be at
-  * least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes
+  * least `GIT_MAX_HEXSZ + 1` bytes. The return value is the number of bytes
   * written (excluding the NUL terminator).
   *
   * Note that while this version avoids the static buffer, it is not fully
   * reentrant, as it calls into other non-reentrant git code.
   */
- 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 char *find_unique_abbrev(const struct object_id *oid, int len);
+ extern int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len);
  
  extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
  extern const struct object_id null_oid;
@@@ -1189,19 -1185,19 +1189,19 @@@ extern char *xdg_config_home(const cha
   */
  extern char *xdg_cache_home(const char *filename);
  
- extern void *read_sha1_file_extended(const unsigned char *sha1,
-                                    enum object_type *type,
-                                    unsigned long *size, int lookup_replace);
- static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+ extern void *read_object_file_extended(const struct object_id *oid,
+                                      enum object_type *type,
+                                      unsigned long *size, int lookup_replace);
+ static inline void *read_object_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
  {
-       return read_sha1_file_extended(sha1, type, size, 1);
+       return read_object_file_extended(oid, type, size, 1);
  }
  
  /*
   * This internal function is only declared here for the benefit of
   * lookup_replace_object().  Please do not call it directly.
   */
- extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
+ extern const struct object_id *do_lookup_replace_object(const struct object_id *oid);
  
  /*
   * If object sha1 should be replaced, return the replacement object's
   * either sha1 or a pointer to a permanently-allocated value.  When
   * object replacement is suppressed, always return sha1.
   */
- static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
+ static inline const struct object_id *lookup_replace_object(const struct object_id *oid)
  {
        if (!check_replace_refs)
-               return sha1;
-       return do_lookup_replace_object(sha1);
+               return oid;
+       return do_lookup_replace_object(oid);
  }
  
- /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
- extern int sha1_object_info(const unsigned char *, unsigned long *);
+ /* Read and unpack an object file into memory, write memory to an object file */
+ extern int oid_object_info(const struct object_id *, unsigned long *);
  
  extern int hash_object_file(const void *buf, unsigned long len,
                            const char *type, struct object_id *oid);
@@@ -1240,19 -1236,19 +1240,19 @@@ extern void *map_sha1_file(const unsign
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
  
- extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
+ extern int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
  
  extern int finalize_object_file(const char *tmpfile, const char *filename);
  
  /*
-  * Open the loose object at path, check its sha1, and return the contents,
+  * Open the loose object at path, check its hash, and return the contents,
   * type, and size. If the object is a blob, then "contents" may return NULL,
   * to allow streaming of large blobs.
   *
   * Returns 0 on success, negative on error (details may be written to stderr).
   */
  int read_loose_object(const char *path,
-                     const unsigned char *expected_sha1,
+                     const struct object_id *expected_oid,
                      enum object_type *type,
                      unsigned long *size,
                      void **contents);
@@@ -1279,7 -1275,7 +1279,7 @@@ extern int has_object_file_with_flags(c
   */
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
- extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
+ extern void assert_oid_type(const struct object_id *oid, enum object_type expect);
  
  /* Helper to check and "touch" a file */
  extern int check_and_freshen_file(const char *fn, int freshen);
@@@ -1435,10 -1431,10 +1435,10 @@@ extern int df_name_compare(const char *
  extern int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
  extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
  
- extern void *read_object_with_reference(const unsigned char *sha1,
+ extern void *read_object_with_reference(const struct object_id *oid,
                                        const char *required_type,
                                        unsigned long *size,
-                                       unsigned char *sha1_ret);
+                                       struct object_id *oid_ret);
  
  extern struct object *peel_to_type(const char *name, int namelen,
                                   struct object *o, enum object_type);
@@@ -1777,9 -1773,7 +1777,9 @@@ struct object_info 
  #define OBJECT_INFO_SKIP_CACHED 4
  /* Do not retry packed storage after checking packed and loose storage */
  #define OBJECT_INFO_QUICK 8
- extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 +/* Do not check loose object */
 +#define OBJECT_INFO_IGNORE_LOOSE 16
+ extern int oid_object_info_extended(const struct object_id *, struct object_info *, unsigned flags);
  
  /*
   * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
diff --combined diff.c
index 4c59f5f5d3d32286daefbeeb25ed5291070fe762,5c8f3e16e8f2d75f583af426c01bbcd08dd9a14c..1289df4b1f9f395010e475073c2c5e5ce43976a7
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -2045,10 -2045,11 +2045,10 @@@ static void fn_out_consume(void *priv, 
        }
  }
  
 -static char *pprint_rename(const char *a, const char *b)
 +static void pprint_rename(struct strbuf *name, const char *a, const char *b)
  {
        const char *old_name = a;
        const char *new_name = b;
 -      struct strbuf name = STRBUF_INIT;
        int pfx_length, sfx_length;
        int pfx_adjust_for_slash;
        int len_a = strlen(a);
        int qlen_b = quote_c_style(b, NULL, NULL, 0);
  
        if (qlen_a || qlen_b) {
 -              quote_c_style(a, &name, NULL, 0);
 -              strbuf_addstr(&name, " => ");
 -              quote_c_style(b, &name, NULL, 0);
 -              return strbuf_detach(&name, NULL);
 +              quote_c_style(a, name, NULL, 0);
 +              strbuf_addstr(name, " => ");
 +              quote_c_style(b, name, NULL, 0);
 +              return;
        }
  
        /* Find common prefix */
        if (b_midlen < 0)
                b_midlen = 0;
  
 -      strbuf_grow(&name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
 +      strbuf_grow(name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
        if (pfx_length + sfx_length) {
 -              strbuf_add(&name, a, pfx_length);
 -              strbuf_addch(&name, '{');
 +              strbuf_add(name, a, pfx_length);
 +              strbuf_addch(name, '{');
        }
 -      strbuf_add(&name, a + pfx_length, a_midlen);
 -      strbuf_addstr(&name, " => ");
 -      strbuf_add(&name, b + pfx_length, b_midlen);
 +      strbuf_add(name, a + pfx_length, a_midlen);
 +      strbuf_addstr(name, " => ");
 +      strbuf_add(name, b + pfx_length, b_midlen);
        if (pfx_length + sfx_length) {
 -              strbuf_addch(&name, '}');
 -              strbuf_add(&name, a + len_a - sfx_length, sfx_length);
 +              strbuf_addch(name, '}');
 +              strbuf_add(name, a + len_a - sfx_length, sfx_length);
        }
 -      return strbuf_detach(&name, NULL);
  }
  
  struct diffstat_t {
                char *from_name;
                char *name;
                char *print_name;
 +              const char *comments;
                unsigned is_unmerged:1;
                unsigned is_binary:1;
                unsigned is_renamed:1;
@@@ -2196,20 -2197,23 +2196,20 @@@ static void show_graph(struct strbuf *o
  
  static void fill_print_name(struct diffstat_file *file)
  {
 -      char *pname;
 +      struct strbuf pname = STRBUF_INIT;
  
        if (file->print_name)
                return;
  
 -      if (!file->is_renamed) {
 -              struct strbuf buf = STRBUF_INIT;
 -              if (quote_c_style(file->name, &buf, NULL, 0)) {
 -                      pname = strbuf_detach(&buf, NULL);
 -              } else {
 -                      pname = file->name;
 -                      strbuf_release(&buf);
 -              }
 -      } else {
 -              pname = pprint_rename(file->from_name, file->name);
 -      }
 -      file->print_name = pname;
 +      if (file->is_renamed)
 +              pprint_rename(&pname, file->from_name, file->name);
 +      else
 +              quote_c_style(file->name, &pname, NULL, 0);
 +
 +      if (file->comments)
 +              strbuf_addf(&pname, " (%s)", file->comments);
 +
 +      file->print_name = strbuf_detach(&pname, NULL);
  }
  
  static void print_stat_summary_inserts_deletes(struct diff_options *options,
@@@ -2793,7 -2797,8 +2793,7 @@@ static void free_diffstat_info(struct d
        int i;
        for (i = 0; i < diffstat->nr; i++) {
                struct diffstat_file *f = diffstat->files[i];
 -              if (f->name != f->print_name)
 -                      free(f->print_name);
 +              free(f->print_name);
                free(f->name);
                free(f->from_name);
                free(f);
@@@ -3243,32 -3248,6 +3243,32 @@@ static void builtin_diff(const char *na
        return;
  }
  
 +static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 +{
 +      if (!is_renamed) {
 +              if (p->status == DIFF_STATUS_ADDED) {
 +                      if (S_ISLNK(p->two->mode))
 +                              return "new +l";
 +                      else if ((p->two->mode & 0777) == 0755)
 +                              return "new +x";
 +                      else
 +                              return "new";
 +              } else if (p->status == DIFF_STATUS_DELETED)
 +                      return "gone";
 +      }
 +      if (S_ISLNK(p->one->mode) && !S_ISLNK(p->two->mode))
 +              return "mode -l";
 +      else if (!S_ISLNK(p->one->mode) && S_ISLNK(p->two->mode))
 +              return "mode +l";
 +      else if ((p->one->mode & 0777) == 0644 &&
 +               (p->two->mode & 0777) == 0755)
 +              return "mode +x";
 +      else if ((p->one->mode & 0777) == 0755 &&
 +               (p->two->mode & 0777) == 0644)
 +              return "mode -x";
 +      return NULL;
 +}
 +
  static void builtin_diffstat(const char *name_a, const char *name_b,
                             struct diff_filespec *one,
                             struct diff_filespec *two,
  
        data = diffstat_add(diffstat, name_a, name_b);
        data->is_interesting = p->status != DIFF_STATUS_UNKNOWN;
 +      if (o->flags.stat_with_summary)
 +              data->comments = get_compact_summary(p, data->is_renamed);
  
        if (!one || !two) {
                data->is_unmerged = 1;
@@@ -3638,7 -3615,7 +3638,7 @@@ int diff_populate_filespec(struct diff_
        else {
                enum object_type type;
                if (size_only || (flags & CHECK_BINARY)) {
-                       type = sha1_object_info(s->oid.hash, &s->size);
+                       type = oid_object_info(&s->oid, &s->size);
                        if (type < 0)
                                die("unable to read %s",
                                    oid_to_hex(&s->oid));
                                return 0;
                        }
                }
-               s->data = read_sha1_file(s->oid.hash, &type, &s->size);
+               s->data = read_object_file(&s->oid, &type, &s->size);
                if (!s->data)
                        die("unable to read %s", oid_to_hex(&s->oid));
                s->should_free = 1;
@@@ -3834,7 -3811,7 +3834,7 @@@ static int similarity_index(struct diff
  static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
  {
        if (startup_info->have_repository)
-               return find_unique_abbrev(oid->hash, abbrev);
+               return find_unique_abbrev(oid, abbrev);
        else {
                char *hex = oid_to_hex(oid);
                if (abbrev < 0)
@@@ -4576,11 -4553,6 +4576,11 @@@ int diff_opt_parse(struct diff_options 
        else if (starts_with(arg, "--stat"))
                /* --stat, --stat-width, --stat-name-width, or --stat-count */
                return stat_opt(options, av);
 +      else if (!strcmp(arg, "--compact-summary")) {
 +               options->flags.stat_with_summary = 1;
 +               options->output_format |= DIFF_FORMAT_DIFFSTAT;
 +      } else if (!strcmp(arg, "--no-compact-summary"))
 +               options->flags.stat_with_summary = 0;
  
        /* renames options */
        else if (starts_with(arg, "-B") ||
@@@ -5269,12 -5241,10 +5269,12 @@@ static void show_rename_copy(struct dif
                struct diff_filepair *p)
  {
        struct strbuf sb = STRBUF_INIT;
 -      char *names = pprint_rename(p->one->path, p->two->path);
 +      struct strbuf names = STRBUF_INIT;
 +
 +      pprint_rename(&names, p->one->path, p->two->path);
        strbuf_addf(&sb, " %s %s (%d%%)\n",
 -                      renamecopy, names, similarity_index(p));
 -      free(names);
 +                  renamecopy, names.buf, similarity_index(p));
 +      strbuf_release(&names);
        emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
                                 sb.buf, sb.len, 0);
        show_mode_change(opt, p, 0);
diff --combined fast-import.c
index b5db5d20b1b20c642cf97f990eb538ee6fbaa299,b300e4416de38cf7f2bed96da988e7de861f0a09..a2e8b1d763a30e1bbec97a92da46c82f0f33353d
@@@ -1412,7 -1412,7 +1412,7 @@@ static void load_tree(struct tree_entr
                        die("Can't load tree %s", oid_to_hex(oid));
        } else {
                enum object_type type;
-               buf = read_sha1_file(oid->hash, &type, &size);
+               buf = read_object_file(oid, &type, &size);
                if (!buf || type != OBJ_TREE)
                        die("Can't load tree %s", oid_to_hex(oid));
        }
@@@ -1913,7 -1913,7 +1913,7 @@@ static void read_marks(void
                        die("corrupt mark line: %s", line);
                e = find_object(&oid);
                if (!e) {
-                       enum object_type type = sha1_object_info(oid.hash, NULL);
+                       enum object_type type = oid_object_info(&oid, NULL);
                        if (type < 0)
                                die("object not found: %s", oid_to_hex(&oid));
                        e = insert_object(&oid);
@@@ -2443,7 -2443,7 +2443,7 @@@ static void file_change_m(const char *p
                enum object_type expected = S_ISDIR(mode) ?
                                                OBJ_TREE: OBJ_BLOB;
                enum object_type type = oe ? oe->type :
-                                       sha1_object_info(oid.hash, NULL);
+                                       oid_object_info(&oid, NULL);
                if (type < 0)
                        die("%s not found: %s",
                                        S_ISDIR(mode) ?  "Tree" : "Blob",
@@@ -2583,8 -2583,9 +2583,9 @@@ static void note_change_n(const char *p
                oidcpy(&commit_oid, &commit_oe->idx.oid);
        } else if (!get_oid(p, &commit_oid)) {
                unsigned long size;
-               char *buf = read_object_with_reference(commit_oid.hash,
-                       commit_type, &size, commit_oid.hash);
+               char *buf = read_object_with_reference(&commit_oid,
+                                                      commit_type, &size,
+                                                      &commit_oid);
                if (!buf || size < 46)
                        die("Not a valid commit: %s", p);
                free(buf);
                        die("Not a blob (actually a %s): %s",
                                type_name(oe->type), command_buf.buf);
        } else if (!is_null_oid(&oid)) {
-               enum object_type type = sha1_object_info(oid.hash, NULL);
+               enum object_type type = oid_object_info(&oid, NULL);
                if (type < 0)
                        die("Blob not found: %s", command_buf.buf);
                if (type != OBJ_BLOB)
@@@ -2653,9 -2654,8 +2654,8 @@@ static void parse_from_existing(struct 
                unsigned long size;
                char *buf;
  
-               buf = read_object_with_reference(b->oid.hash,
-                                                commit_type, &size,
-                                                b->oid.hash);
+               buf = read_object_with_reference(&b->oid, commit_type, &size,
+                                                &b->oid);
                parse_from_commit(b, buf, size);
                free(buf);
        }
@@@ -2732,8 -2732,9 +2732,9 @@@ static struct hash_list *parse_merge(un
                        oidcpy(&n->oid, &oe->idx.oid);
                } else if (!get_oid(from, &n->oid)) {
                        unsigned long size;
-                       char *buf = read_object_with_reference(n->oid.hash,
-                               commit_type, &size, n->oid.hash);
+                       char *buf = read_object_with_reference(&n->oid,
+                                                              commit_type,
+                                                              &size, &n->oid);
                        if (!buf || size < 46)
                                die("Not a valid commit: %s", from);
                        free(buf);
@@@ -2890,7 -2891,7 +2891,7 @@@ static void parse_new_tag(const char *a
        } else if (!get_oid(from, &oid)) {
                struct object_entry *oe = find_object(&oid);
                if (!oe) {
-                       type = sha1_object_info(oid.hash, NULL);
+                       type = oid_object_info(&oid, NULL);
                        if (type < 0)
                                die("Not a valid object: %s", from);
                } else
@@@ -2966,7 -2967,7 +2967,7 @@@ static void cat_blob(struct object_entr
        char *buf;
  
        if (!oe || oe->pack_id == MAX_PACK_ID) {
-               buf = read_sha1_file(oid->hash, &type, &size);
+               buf = read_object_file(oid, &type, &size);
        } else {
                type = oe->type;
                buf = gfi_unpack_entry(oe, &size);
  
  static void parse_get_mark(const char *p)
  {
 -      struct object_entry *oe = oe;
 +      struct object_entry *oe;
        char output[GIT_MAX_HEXSZ + 2];
  
        /* get-mark SP <object> LF */
  
  static void parse_cat_blob(const char *p)
  {
 -      struct object_entry *oe = oe;
 +      struct object_entry *oe;
        struct object_id oid;
  
        /* cat-blob SP <object> LF */
@@@ -3048,7 -3049,7 +3049,7 @@@ static struct object_entry *dereference
        unsigned long size;
        char *buf = NULL;
        if (!oe) {
-               enum object_type type = sha1_object_info(oid->hash, NULL);
+               enum object_type type = oid_object_info(oid, NULL);
                if (type < 0)
                        die("object not found: %s", oid_to_hex(oid));
                /* cache it! */
                buf = gfi_unpack_entry(oe, &size);
        } else {
                enum object_type unused;
-               buf = read_sha1_file(oid->hash, &unused, &size);
+               buf = read_object_file(oid, &unused, &size);
        }
        if (!buf)
                die("Can't load object %s", oid_to_hex(oid));
diff --combined merge-recursive.c
index 55d1e779a0e8faa2a31e699ad001e5f799fee55e,c55401698dbab0a3fffd1b38371b4e8bc02795fe..9c05eb7f700eed0dc3e20a4c22f3a60e0aa21488
@@@ -49,67 -49,6 +49,67 @@@ static unsigned int path_hash(const cha
        return ignore_case ? strihash(path) : strhash(path);
  }
  
 +static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,
 +                                                    char *dir)
 +{
 +      struct dir_rename_entry key;
 +
 +      if (dir == NULL)
 +              return NULL;
 +      hashmap_entry_init(&key, strhash(dir));
 +      key.dir = dir;
 +      return hashmap_get(hashmap, &key, NULL);
 +}
 +
 +static int dir_rename_cmp(const void *unused_cmp_data,
 +                        const void *entry,
 +                        const void *entry_or_key,
 +                        const void *unused_keydata)
 +{
 +      const struct dir_rename_entry *e1 = entry;
 +      const struct dir_rename_entry *e2 = entry_or_key;
 +
 +      return strcmp(e1->dir, e2->dir);
 +}
 +
 +static void dir_rename_init(struct hashmap *map)
 +{
 +      hashmap_init(map, dir_rename_cmp, NULL, 0);
 +}
 +
 +static void dir_rename_entry_init(struct dir_rename_entry *entry,
 +                                char *directory)
 +{
 +      hashmap_entry_init(entry, strhash(directory));
 +      entry->dir = directory;
 +      entry->non_unique_new_dir = 0;
 +      strbuf_init(&entry->new_dir, 0);
 +      string_list_init(&entry->possible_new_dirs, 0);
 +}
 +
 +static struct collision_entry *collision_find_entry(struct hashmap *hashmap,
 +                                                  char *target_file)
 +{
 +      struct collision_entry key;
 +
 +      hashmap_entry_init(&key, strhash(target_file));
 +      key.target_file = target_file;
 +      return hashmap_get(hashmap, &key, NULL);
 +}
 +
 +static int collision_cmp(void *unused_cmp_data,
 +                       const struct collision_entry *e1,
 +                       const struct collision_entry *e2,
 +                       const void *unused_keydata)
 +{
 +      return strcmp(e1->target_file, e2->target_file);
 +}
 +
 +static void collision_init(struct hashmap *map)
 +{
 +      hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);
 +}
 +
  static void flush_output(struct merge_options *o)
  {
        if (o->buffer_output < 2 && o->obuf.len) {
@@@ -180,7 -119,6 +180,7 @@@ static int oid_eq(const struct object_i
  
  enum rename_type {
        RENAME_NORMAL = 0,
 +      RENAME_DIR,
        RENAME_DELETE,
        RENAME_ONE_FILE_TO_ONE,
        RENAME_ONE_FILE_TO_TWO,
@@@ -290,7 -228,7 +290,7 @@@ static void output_commit_title(struct 
                strbuf_addf(&o->obuf, "virtual %s\n",
                        merge_remote_util(commit)->name);
        else {
-               strbuf_add_unique_abbrev(&o->obuf, commit->object.oid.hash,
+               strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,
                                         DEFAULT_ABBREV);
                strbuf_addch(&o->obuf, ' ');
                if (parse_commit(commit) != 0)
@@@ -337,37 -275,32 +337,37 @@@ static void init_tree_desc_from_tree(st
        init_tree_desc(desc, tree->buffer, tree->size);
  }
  
 -static int git_merge_trees(int index_only,
 +static int git_merge_trees(struct merge_options *o,
                           struct tree *common,
                           struct tree *head,
                           struct tree *merge)
  {
        int rc;
        struct tree_desc t[3];
 -      struct unpack_trees_options opts;
  
 -      memset(&opts, 0, sizeof(opts));
 -      if (index_only)
 -              opts.index_only = 1;
 +      memset(&o->unpack_opts, 0, sizeof(o->unpack_opts));
 +      if (o->call_depth)
 +              o->unpack_opts.index_only = 1;
        else
 -              opts.update = 1;
 -      opts.merge = 1;
 -      opts.head_idx = 2;
 -      opts.fn = threeway_merge;
 -      opts.src_index = &the_index;
 -      opts.dst_index = &the_index;
 -      setup_unpack_trees_porcelain(&opts, "merge");
 +              o->unpack_opts.update = 1;
 +      o->unpack_opts.merge = 1;
 +      o->unpack_opts.head_idx = 2;
 +      o->unpack_opts.fn = threeway_merge;
 +      o->unpack_opts.src_index = &the_index;
 +      o->unpack_opts.dst_index = &the_index;
 +      setup_unpack_trees_porcelain(&o->unpack_opts, "merge");
  
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
        init_tree_desc_from_tree(t+2, merge);
  
 -      rc = unpack_trees(3, t, &opts);
 +      rc = unpack_trees(3, t, &o->unpack_opts);
 +      /*
 +       * unpack_trees NULLifies src_index, but it's used in verify_uptodate,
 +       * so set to the new index which will usually have modification
 +       * timestamp info copied over.
 +       */
 +      o->unpack_opts.src_index = &the_index;
        cache_tree_free(&active_cache_tree);
        return rc;
  }
@@@ -402,7 -335,7 +402,7 @@@ struct tree *write_tree_from_memory(str
        return result;
  }
  
- static int save_files_dirs(const unsigned char *sha1,
+ static int save_files_dirs(const struct object_id *oid,
                struct strbuf *base, const char *path,
                unsigned int mode, int stage, void *context)
  {
@@@ -427,21 -360,6 +427,21 @@@ static void get_files_dirs(struct merge
        read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o);
  }
  
- static int get_tree_entry_if_blob(const unsigned char *tree,
++static int get_tree_entry_if_blob(struct tree *tree,
 +                                const char *path,
-                                 unsigned char *hashy,
++                                struct object_id *hashy,
 +                                unsigned int *mode_o)
 +{
 +      int ret;
 +
-       ret = get_tree_entry(tree, path, hashy, mode_o);
++      ret = get_tree_entry(&tree->object.oid, path, hashy, mode_o);
 +      if (S_ISDIR(*mode_o)) {
-               hashcpy(hashy, null_sha1);
++              oidcpy(hashy, &null_oid);
 +              *mode_o = 0;
 +      }
 +      return ret;
 +}
 +
  /*
   * Returns an index_entry instance which doesn't have to correspond to
   * a real cache entry in Git's index.
@@@ -452,12 -370,12 +452,12 @@@ static struct stage_data *insert_stage_
  {
        struct string_list_item *item;
        struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
-       get_tree_entry_if_blob(o->object.oid.hash, path,
-                              e->stages[1].oid.hash, &e->stages[1].mode);
-       get_tree_entry_if_blob(a->object.oid.hash, path,
-                              e->stages[2].oid.hash, &e->stages[2].mode);
-       get_tree_entry_if_blob(b->object.oid.hash, path,
-                              e->stages[3].oid.hash, &e->stages[3].mode);
 -      get_tree_entry(&o->object.oid, path,
 -                      &e->stages[1].oid, &e->stages[1].mode);
 -      get_tree_entry(&a->object.oid, path,
 -                      &e->stages[2].oid, &e->stages[2].mode);
 -      get_tree_entry(&b->object.oid, path,
 -                      &e->stages[3].oid, &e->stages[3].mode);
++      get_tree_entry_if_blob(o, path,
++                             &e->stages[1].oid, &e->stages[1].mode);
++      get_tree_entry_if_blob(a, path,
++                             &e->stages[2].oid, &e->stages[2].mode);
++      get_tree_entry_if_blob(b, path,
++                             &e->stages[3].oid, &e->stages[3].mode);
        item = string_list_insert(entries, path);
        item->util = e;
        return e;
@@@ -616,10 -534,78 +616,10 @@@ struct rename 
         */
        struct stage_data *src_entry;
        struct stage_data *dst_entry;
 +      unsigned add_turned_into_rename:1;
        unsigned processed:1;
  };
  
 -/*
 - * Get information of all renames which occurred between 'o_tree' and
 - * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and
 - * 'b_tree') to be able to associate the correct cache entries with
 - * the rename information. 'tree' is always equal to either a_tree or b_tree.
 - */
 -static struct string_list *get_renames(struct merge_options *o,
 -                                     struct tree *tree,
 -                                     struct tree *o_tree,
 -                                     struct tree *a_tree,
 -                                     struct tree *b_tree,
 -                                     struct string_list *entries)
 -{
 -      int i;
 -      struct string_list *renames;
 -      struct diff_options opts;
 -
 -      renames = xcalloc(1, sizeof(struct string_list));
 -      if (!o->detect_rename)
 -              return renames;
 -
 -      diff_setup(&opts);
 -      opts.flags.recursive = 1;
 -      opts.flags.rename_empty = 0;
 -      opts.detect_rename = DIFF_DETECT_RENAME;
 -      opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
 -                          o->diff_rename_limit >= 0 ? o->diff_rename_limit :
 -                          1000;
 -      opts.rename_score = o->rename_score;
 -      opts.show_rename_progress = o->show_rename_progress;
 -      opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 -      diff_setup_done(&opts);
 -      diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
 -      diffcore_std(&opts);
 -      if (opts.needed_rename_limit > o->needed_rename_limit)
 -              o->needed_rename_limit = opts.needed_rename_limit;
 -      for (i = 0; i < diff_queued_diff.nr; ++i) {
 -              struct string_list_item *item;
 -              struct rename *re;
 -              struct diff_filepair *pair = diff_queued_diff.queue[i];
 -              if (pair->status != 'R') {
 -                      diff_free_filepair(pair);
 -                      continue;
 -              }
 -              re = xmalloc(sizeof(*re));
 -              re->processed = 0;
 -              re->pair = pair;
 -              item = string_list_lookup(entries, re->pair->one->path);
 -              if (!item)
 -                      re->src_entry = insert_stage_data(re->pair->one->path,
 -                                      o_tree, a_tree, b_tree, entries);
 -              else
 -                      re->src_entry = item->util;
 -
 -              item = string_list_lookup(entries, re->pair->two->path);
 -              if (!item)
 -                      re->dst_entry = insert_stage_data(re->pair->two->path,
 -                                      o_tree, a_tree, b_tree, entries);
 -              else
 -                      re->dst_entry = item->util;
 -              item = string_list_insert(renames, pair->one->path);
 -              item->util = re;
 -      }
 -      opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 -      diff_queued_diff.nr = 0;
 -      diff_flush(&opts);
 -      return renames;
 -}
 -
  static int update_stages(struct merge_options *opt, const char *path,
                         const struct diff_filespec *o,
                         const struct diff_filespec *a,
        return 0;
  }
  
 +static int update_stages_for_stage_data(struct merge_options *opt,
 +                                      const char *path,
 +                                      const struct stage_data *stage_data)
 +{
 +      struct diff_filespec o, a, b;
 +
 +      o.mode = stage_data->stages[1].mode;
 +      oidcpy(&o.oid, &stage_data->stages[1].oid);
 +
 +      a.mode = stage_data->stages[2].mode;
 +      oidcpy(&a.oid, &stage_data->stages[2].oid);
 +
 +      b.mode = stage_data->stages[3].mode;
 +      oidcpy(&b.oid, &stage_data->stages[3].oid);
 +
 +      return update_stages(opt, path,
 +                           is_null_oid(&o.oid) ? NULL : &o,
 +                           is_null_oid(&a.oid) ? NULL : &a,
 +                           is_null_oid(&b.oid) ? NULL : &b);
 +}
 +
  static void update_entry(struct stage_data *entry,
                         struct diff_filespec *o,
                         struct diff_filespec *a,
@@@ -800,20 -765,6 +800,20 @@@ static int would_lose_untracked(const c
        return !was_tracked(path) && file_exists(path);
  }
  
 +static int was_dirty(struct merge_options *o, const char *path)
 +{
 +      struct cache_entry *ce;
 +      int dirty = 1;
 +
 +      if (o->call_depth || !was_tracked(path))
 +              return !dirty;
 +
 +      ce = cache_file_exists(path, strlen(path), ignore_case);
 +      dirty = (ce->ce_stat_data.sd_mtime.sec > 0 &&
 +               verify_uptodate(ce, &o->unpack_opts) != 0);
 +      return dirty;
 +}
 +
  static int make_room_for_path(struct merge_options *o, const char *path)
  {
        int status, i;
@@@ -891,7 -842,7 +891,7 @@@ static int update_file_flags(struct mer
                        goto update_index;
                }
  
-               buf = read_sha1_file(oid->hash, &type, &size);
+               buf = read_object_file(oid, &type, &size);
                if (!buf)
                        return err(o, _("cannot read object %s '%s'"), oid_to_hex(oid), path);
                if (type != OBJ_BLOB) {
@@@ -1163,38 -1114,6 +1163,38 @@@ static int merge_file_one(struct merge_
        return merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);
  }
  
 +static int conflict_rename_dir(struct merge_options *o,
 +                             struct diff_filepair *pair,
 +                             const char *rename_branch,
 +                             const char *other_branch)
 +{
 +      const struct diff_filespec *dest = pair->two;
 +
 +      if (!o->call_depth && would_lose_untracked(dest->path)) {
 +              char *alt_path = unique_path(o, dest->path, rename_branch);
 +
 +              output(o, 1, _("Error: Refusing to lose untracked file at %s; "
 +                             "writing to %s instead."),
 +                     dest->path, alt_path);
 +              /*
 +               * Write the file in worktree at alt_path, but not in the
 +               * index.  Instead, write to dest->path for the index but
 +               * only at the higher appropriate stage.
 +               */
 +              if (update_file(o, 0, &dest->oid, dest->mode, alt_path))
 +                      return -1;
 +              free(alt_path);
 +              return update_stages(o, dest->path, NULL,
 +                                   rename_branch == o->branch1 ? dest : NULL,
 +                                   rename_branch == o->branch1 ? NULL : dest);
 +      }
 +
 +      /* Update dest->path both in index and in worktree */
 +      if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
 +              return -1;
 +      return 0;
 +}
 +
  static int handle_change_delete(struct merge_options *o,
                                 const char *path, const char *old_path,
                                 const struct object_id *o_oid, int o_mode,
        const char *update_path = path;
        int ret = 0;
  
 -      if (dir_in_way(path, !o->call_depth, 0)) {
 +      if (dir_in_way(path, !o->call_depth, 0) ||
 +          (!o->call_depth && would_lose_untracked(path))) {
                update_path = alt_path = unique_path(o, path, change_branch);
        }
  
@@@ -1324,34 -1242,17 +1324,34 @@@ static int handle_file(struct merge_opt
  
        add = filespec_from_entry(&other, dst_entry, stage ^ 1);
        if (add) {
 +              int ren_src_was_dirty = was_dirty(o, rename->path);
                char *add_name = unique_path(o, rename->path, other_branch);
                if (update_file(o, 0, &add->oid, add->mode, add_name))
                        return -1;
  
 -              remove_file(o, 0, rename->path, 0);
 +              if (ren_src_was_dirty) {
 +                      output(o, 1, _("Refusing to lose dirty file at %s"),
 +                             rename->path);
 +              }
 +              /*
 +               * Because the double negatives somehow keep confusing me...
 +               *    1) update_wd iff !ren_src_was_dirty.
 +               *    2) no_wd iff !update_wd
 +               *    3) so, no_wd == !!ren_src_was_dirty == ren_src_was_dirty
 +               */
 +              remove_file(o, 0, rename->path, ren_src_was_dirty);
                dst_name = unique_path(o, rename->path, cur_branch);
        } else {
                if (dir_in_way(rename->path, !o->call_depth, 0)) {
                        dst_name = unique_path(o, rename->path, cur_branch);
                        output(o, 1, _("%s is a directory in %s adding as %s instead"),
                               rename->path, other_branch, dst_name);
 +              } else if (!o->call_depth &&
 +                         would_lose_untracked(rename->path)) {
 +                      dst_name = unique_path(o, rename->path, cur_branch);
 +                      output(o, 1, _("Refusing to lose untracked file at %s; "
 +                                     "adding as %s instead"),
 +                             rename->path, dst_name);
                }
        }
        if ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))
@@@ -1477,43 -1378,11 +1477,43 @@@ static int conflict_rename_rename_2to1(
                char *new_path2 = unique_path(o, path, ci->branch2);
                output(o, 1, _("Renaming %s to %s and %s to %s instead"),
                       a->path, new_path1, b->path, new_path2);
 -              remove_file(o, 0, path, 0);
 +              if (was_dirty(o, path))
 +                      output(o, 1, _("Refusing to lose dirty file at %s"),
 +                             path);
 +              else if (would_lose_untracked(path))
 +                      /*
 +                       * Only way we get here is if both renames were from
 +                       * a directory rename AND user had an untracked file
 +                       * at the location where both files end up after the
 +                       * two directory renames.  See testcase 10d of t6043.
 +                       */
 +                      output(o, 1, _("Refusing to lose untracked file at "
 +                                     "%s, even though it's in the way."),
 +                             path);
 +              else
 +                      remove_file(o, 0, path, 0);
                ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);
                if (!ret)
                        ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
                                          new_path2);
 +              /*
 +               * unpack_trees() actually populates the index for us for
 +               * "normal" rename/rename(2to1) situtations so that the
 +               * correct entries are at the higher stages, which would
 +               * make the call below to update_stages_for_stage_data
 +               * unnecessary.  However, if either of the renames came
 +               * from a directory rename, then unpack_trees() will not
 +               * have gotten the right data loaded into the index, so we
 +               * need to do so now.  (While it'd be tempting to move this
 +               * call to update_stages_for_stage_data() to
 +               * apply_directory_rename_modifications(), that would break
 +               * our intermediate calls to would_lose_untracked() since
 +               * those rely on the current in-memory index.  See also the
 +               * big "NOTE" in update_stages()).
 +               */
 +              if (update_stages_for_stage_data(o, path, ci->dst_entry1))
 +                      ret = -1;
 +
                free(new_path2);
                free(new_path1);
        }
        return ret;
  }
  
-       unsigned char hashy[GIT_MAX_RAWSZ];
 +/*
 + * Get the diff_filepairs changed between o_tree and tree.
 + */
 +static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
 +                                             struct tree *o_tree,
 +                                             struct tree *tree)
 +{
 +      struct diff_queue_struct *ret;
 +      struct diff_options opts;
 +
 +      diff_setup(&opts);
 +      opts.flags.recursive = 1;
 +      opts.flags.rename_empty = 0;
 +      opts.detect_rename = DIFF_DETECT_RENAME;
 +      opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
 +                          o->diff_rename_limit >= 0 ? o->diff_rename_limit :
 +                          1000;
 +      opts.rename_score = o->rename_score;
 +      opts.show_rename_progress = o->show_rename_progress;
 +      opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 +      diff_setup_done(&opts);
 +      diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
 +      diffcore_std(&opts);
 +      if (opts.needed_rename_limit > o->needed_rename_limit)
 +              o->needed_rename_limit = opts.needed_rename_limit;
 +
 +      ret = xmalloc(sizeof(*ret));
 +      *ret = diff_queued_diff;
 +
 +      opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 +      diff_queued_diff.nr = 0;
 +      diff_queued_diff.queue = NULL;
 +      diff_flush(&opts);
 +      return ret;
 +}
 +
 +static int tree_has_path(struct tree *tree, const char *path)
 +{
-       return !get_tree_entry(tree->object.oid.hash, path,
-                              hashy, &mode_o);
++      struct object_id hashy;
 +      unsigned int mode_o;
 +
-       get_tree_entry(tree->object.oid.hash,
++      return !get_tree_entry(&tree->object.oid, path,
++                             &hashy, &mode_o);
 +}
 +
 +/*
 + * Return a new string that replaces the beginning portion (which matches
 + * entry->dir), with entry->new_dir.  In perl-speak:
 + *   new_path_name = (old_path =~ s/entry->dir/entry->new_dir/);
 + * NOTE:
 + *   Caller must ensure that old_path starts with entry->dir + '/'.
 + */
 +static char *apply_dir_rename(struct dir_rename_entry *entry,
 +                            const char *old_path)
 +{
 +      struct strbuf new_path = STRBUF_INIT;
 +      int oldlen, newlen;
 +
 +      if (entry->non_unique_new_dir)
 +              return NULL;
 +
 +      oldlen = strlen(entry->dir);
 +      newlen = entry->new_dir.len + (strlen(old_path) - oldlen) + 1;
 +      strbuf_grow(&new_path, newlen);
 +      strbuf_addbuf(&new_path, &entry->new_dir);
 +      strbuf_addstr(&new_path, &old_path[oldlen]);
 +
 +      return strbuf_detach(&new_path, NULL);
 +}
 +
 +static void get_renamed_dir_portion(const char *old_path, const char *new_path,
 +                                  char **old_dir, char **new_dir)
 +{
 +      char *end_of_old, *end_of_new;
 +      int old_len, new_len;
 +
 +      *old_dir = NULL;
 +      *new_dir = NULL;
 +
 +      /*
 +       * For
 +       *    "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
 +       * the "e/foo.c" part is the same, we just want to know that
 +       *    "a/b/c/d" was renamed to "a/b/some/thing/else"
 +       * so, for this example, this function returns "a/b/c/d" in
 +       * *old_dir and "a/b/some/thing/else" in *new_dir.
 +       *
 +       * Also, if the basename of the file changed, we don't care.  We
 +       * want to know which portion of the directory, if any, changed.
 +       */
 +      end_of_old = strrchr(old_path, '/');
 +      end_of_new = strrchr(new_path, '/');
 +
 +      if (end_of_old == NULL || end_of_new == NULL)
 +              return;
 +      while (*--end_of_new == *--end_of_old &&
 +             end_of_old != old_path &&
 +             end_of_new != new_path)
 +              ; /* Do nothing; all in the while loop */
 +      /*
 +       * We've found the first non-matching character in the directory
 +       * paths.  That means the current directory we were comparing
 +       * represents the rename.  Move end_of_old and end_of_new back
 +       * to the full directory name.
 +       */
 +      if (*end_of_old == '/')
 +              end_of_old++;
 +      if (*end_of_old != '/')
 +              end_of_new++;
 +      end_of_old = strchr(end_of_old, '/');
 +      end_of_new = strchr(end_of_new, '/');
 +
 +      /*
 +       * It may have been the case that old_path and new_path were the same
 +       * directory all along.  Don't claim a rename if they're the same.
 +       */
 +      old_len = end_of_old - old_path;
 +      new_len = end_of_new - new_path;
 +
 +      if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
 +              *old_dir = xstrndup(old_path, old_len);
 +              *new_dir = xstrndup(new_path, new_len);
 +      }
 +}
 +
 +static void remove_hashmap_entries(struct hashmap *dir_renames,
 +                                 struct string_list *items_to_remove)
 +{
 +      int i;
 +      struct dir_rename_entry *entry;
 +
 +      for (i = 0; i < items_to_remove->nr; i++) {
 +              entry = items_to_remove->items[i].util;
 +              hashmap_remove(dir_renames, entry, NULL);
 +      }
 +      string_list_clear(items_to_remove, 0);
 +}
 +
 +/*
 + * See if there is a directory rename for path, and if there are any file
 + * level conflicts for the renamed location.  If there is a rename and
 + * there are no conflicts, return the new name.  Otherwise, return NULL.
 + */
 +static char *handle_path_level_conflicts(struct merge_options *o,
 +                                       const char *path,
 +                                       struct dir_rename_entry *entry,
 +                                       struct hashmap *collisions,
 +                                       struct tree *tree)
 +{
 +      char *new_path = NULL;
 +      struct collision_entry *collision_ent;
 +      int clean = 1;
 +      struct strbuf collision_paths = STRBUF_INIT;
 +
 +      /*
 +       * entry has the mapping of old directory name to new directory name
 +       * that we want to apply to path.
 +       */
 +      new_path = apply_dir_rename(entry, path);
 +
 +      if (!new_path) {
 +              /* This should only happen when entry->non_unique_new_dir set */
 +              if (!entry->non_unique_new_dir)
 +                      BUG("entry->non_unqiue_dir not set and !new_path");
 +              output(o, 1, _("CONFLICT (directory rename split): "
 +                             "Unclear where to place %s because directory "
 +                             "%s was renamed to multiple other directories, "
 +                             "with no destination getting a majority of the "
 +                             "files."),
 +                     path, entry->dir);
 +              clean = 0;
 +              return NULL;
 +      }
 +
 +      /*
 +       * The caller needs to have ensured that it has pre-populated
 +       * collisions with all paths that map to new_path.  Do a quick check
 +       * to ensure that's the case.
 +       */
 +      collision_ent = collision_find_entry(collisions, new_path);
 +      if (collision_ent == NULL)
 +              BUG("collision_ent is NULL");
 +
 +      /*
 +       * Check for one-sided add/add/.../add conflicts, i.e.
 +       * where implicit renames from the other side doing
 +       * directory rename(s) can affect this side of history
 +       * to put multiple paths into the same location.  Warn
 +       * and bail on directory renames for such paths.
 +       */
 +      if (collision_ent->reported_already) {
 +              clean = 0;
 +      } else if (tree_has_path(tree, new_path)) {
 +              collision_ent->reported_already = 1;
 +              strbuf_add_separated_string_list(&collision_paths, ", ",
 +                                               &collision_ent->source_files);
 +              output(o, 1, _("CONFLICT (implicit dir rename): Existing "
 +                             "file/dir at %s in the way of implicit "
 +                             "directory rename(s) putting the following "
 +                             "path(s) there: %s."),
 +                     new_path, collision_paths.buf);
 +              clean = 0;
 +      } else if (collision_ent->source_files.nr > 1) {
 +              collision_ent->reported_already = 1;
 +              strbuf_add_separated_string_list(&collision_paths, ", ",
 +                                               &collision_ent->source_files);
 +              output(o, 1, _("CONFLICT (implicit dir rename): Cannot map "
 +                             "more than one path to %s; implicit directory "
 +                             "renames tried to put these paths there: %s"),
 +                     new_path, collision_paths.buf);
 +              clean = 0;
 +      }
 +
 +      /* Free memory we no longer need */
 +      strbuf_release(&collision_paths);
 +      if (!clean && new_path) {
 +              free(new_path);
 +              return NULL;
 +      }
 +
 +      return new_path;
 +}
 +
 +/*
 + * There are a couple things we want to do at the directory level:
 + *   1. Check for both sides renaming to the same thing, in order to avoid
 + *      implicit renaming of files that should be left in place.  (See
 + *      testcase 6b in t6043 for details.)
 + *   2. Prune directory renames if there are still files left in the
 + *      the original directory.  These represent a partial directory rename,
 + *      i.e. a rename where only some of the files within the directory
 + *      were renamed elsewhere.  (Technically, this could be done earlier
 + *      in get_directory_renames(), except that would prevent us from
 + *      doing the previous check and thus failing testcase 6b.)
 + *   3. Check for rename/rename(1to2) conflicts (at the directory level).
 + *      In the future, we could potentially record this info as well and
 + *      omit reporting rename/rename(1to2) conflicts for each path within
 + *      the affected directories, thus cleaning up the merge output.
 + *   NOTE: We do NOT check for rename/rename(2to1) conflicts at the
 + *         directory level, because merging directories is fine.  If it
 + *         causes conflicts for files within those merged directories, then
 + *         that should be detected at the individual path level.
 + */
 +static void handle_directory_level_conflicts(struct merge_options *o,
 +                                           struct hashmap *dir_re_head,
 +                                           struct tree *head,
 +                                           struct hashmap *dir_re_merge,
 +                                           struct tree *merge)
 +{
 +      struct hashmap_iter iter;
 +      struct dir_rename_entry *head_ent;
 +      struct dir_rename_entry *merge_ent;
 +
 +      struct string_list remove_from_head = STRING_LIST_INIT_NODUP;
 +      struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;
 +
 +      hashmap_iter_init(dir_re_head, &iter);
 +      while ((head_ent = hashmap_iter_next(&iter))) {
 +              merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);
 +              if (merge_ent &&
 +                  !head_ent->non_unique_new_dir &&
 +                  !merge_ent->non_unique_new_dir &&
 +                  !strbuf_cmp(&head_ent->new_dir, &merge_ent->new_dir)) {
 +                      /* 1. Renamed identically; remove it from both sides */
 +                      string_list_append(&remove_from_head,
 +                                         head_ent->dir)->util = head_ent;
 +                      strbuf_release(&head_ent->new_dir);
 +                      string_list_append(&remove_from_merge,
 +                                         merge_ent->dir)->util = merge_ent;
 +                      strbuf_release(&merge_ent->new_dir);
 +              } else if (tree_has_path(head, head_ent->dir)) {
 +                      /* 2. This wasn't a directory rename after all */
 +                      string_list_append(&remove_from_head,
 +                                         head_ent->dir)->util = head_ent;
 +                      strbuf_release(&head_ent->new_dir);
 +              }
 +      }
 +
 +      remove_hashmap_entries(dir_re_head, &remove_from_head);
 +      remove_hashmap_entries(dir_re_merge, &remove_from_merge);
 +
 +      hashmap_iter_init(dir_re_merge, &iter);
 +      while ((merge_ent = hashmap_iter_next(&iter))) {
 +              head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);
 +              if (tree_has_path(merge, merge_ent->dir)) {
 +                      /* 2. This wasn't a directory rename after all */
 +                      string_list_append(&remove_from_merge,
 +                                         merge_ent->dir)->util = merge_ent;
 +              } else if (head_ent &&
 +                         !head_ent->non_unique_new_dir &&
 +                         !merge_ent->non_unique_new_dir) {
 +                      /* 3. rename/rename(1to2) */
 +                      /*
 +                       * We can assume it's not rename/rename(1to1) because
 +                       * that was case (1), already checked above.  So we
 +                       * know that head_ent->new_dir and merge_ent->new_dir
 +                       * are different strings.
 +                       */
 +                      output(o, 1, _("CONFLICT (rename/rename): "
 +                                     "Rename directory %s->%s in %s. "
 +                                     "Rename directory %s->%s in %s"),
 +                             head_ent->dir, head_ent->new_dir.buf, o->branch1,
 +                             head_ent->dir, merge_ent->new_dir.buf, o->branch2);
 +                      string_list_append(&remove_from_head,
 +                                         head_ent->dir)->util = head_ent;
 +                      strbuf_release(&head_ent->new_dir);
 +                      string_list_append(&remove_from_merge,
 +                                         merge_ent->dir)->util = merge_ent;
 +                      strbuf_release(&merge_ent->new_dir);
 +              }
 +      }
 +
 +      remove_hashmap_entries(dir_re_head, &remove_from_head);
 +      remove_hashmap_entries(dir_re_merge, &remove_from_merge);
 +}
 +
 +static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
 +                                           struct tree *tree)
 +{
 +      struct hashmap *dir_renames;
 +      struct hashmap_iter iter;
 +      struct dir_rename_entry *entry;
 +      int i;
 +
 +      /*
 +       * Typically, we think of a directory rename as all files from a
 +       * certain directory being moved to a target directory.  However,
 +       * what if someone first moved two files from the original
 +       * directory in one commit, and then renamed the directory
 +       * somewhere else in a later commit?  At merge time, we just know
 +       * that files from the original directory went to two different
 +       * places, and that the bulk of them ended up in the same place.
 +       * We want each directory rename to represent where the bulk of the
 +       * files from that directory end up; this function exists to find
 +       * where the bulk of the files went.
 +       *
 +       * The first loop below simply iterates through the list of file
 +       * renames, finding out how often each directory rename pair
 +       * possibility occurs.
 +       */
 +      dir_renames = xmalloc(sizeof(struct hashmap));
 +      dir_rename_init(dir_renames);
 +      for (i = 0; i < pairs->nr; ++i) {
 +              struct string_list_item *item;
 +              int *count;
 +              struct diff_filepair *pair = pairs->queue[i];
 +              char *old_dir, *new_dir;
 +
 +              /* File not part of directory rename if it wasn't renamed */
 +              if (pair->status != 'R')
 +                      continue;
 +
 +              get_renamed_dir_portion(pair->one->path, pair->two->path,
 +                                      &old_dir,        &new_dir);
 +              if (!old_dir)
 +                      /* Directory didn't change at all; ignore this one. */
 +                      continue;
 +
 +              entry = dir_rename_find_entry(dir_renames, old_dir);
 +              if (!entry) {
 +                      entry = xmalloc(sizeof(struct dir_rename_entry));
 +                      dir_rename_entry_init(entry, old_dir);
 +                      hashmap_put(dir_renames, entry);
 +              } else {
 +                      free(old_dir);
 +              }
 +              item = string_list_lookup(&entry->possible_new_dirs, new_dir);
 +              if (!item) {
 +                      item = string_list_insert(&entry->possible_new_dirs,
 +                                                new_dir);
 +                      item->util = xcalloc(1, sizeof(int));
 +              } else {
 +                      free(new_dir);
 +              }
 +              count = item->util;
 +              *count += 1;
 +      }
 +
 +      /*
 +       * For each directory with files moved out of it, we find out which
 +       * target directory received the most files so we can declare it to
 +       * be the "winning" target location for the directory rename.  This
 +       * winner gets recorded in new_dir.  If there is no winner
 +       * (multiple target directories received the same number of files),
 +       * we set non_unique_new_dir.  Once we've determined the winner (or
 +       * that there is no winner), we no longer need possible_new_dirs.
 +       */
 +      hashmap_iter_init(dir_renames, &iter);
 +      while ((entry = hashmap_iter_next(&iter))) {
 +              int max = 0;
 +              int bad_max = 0;
 +              char *best = NULL;
 +
 +              for (i = 0; i < entry->possible_new_dirs.nr; i++) {
 +                      int *count = entry->possible_new_dirs.items[i].util;
 +
 +                      if (*count == max)
 +                              bad_max = max;
 +                      else if (*count > max) {
 +                              max = *count;
 +                              best = entry->possible_new_dirs.items[i].string;
 +                      }
 +              }
 +              if (bad_max == max)
 +                      entry->non_unique_new_dir = 1;
 +              else {
 +                      assert(entry->new_dir.len == 0);
 +                      strbuf_addstr(&entry->new_dir, best);
 +              }
 +              /*
 +               * The relevant directory sub-portion of the original full
 +               * filepaths were xstrndup'ed before inserting into
 +               * possible_new_dirs, and instead of manually iterating the
 +               * list and free'ing each, just lie and tell
 +               * possible_new_dirs that it did the strdup'ing so that it
 +               * will free them for us.
 +               */
 +              entry->possible_new_dirs.strdup_strings = 1;
 +              string_list_clear(&entry->possible_new_dirs, 1);
 +      }
 +
 +      return dir_renames;
 +}
 +
 +static struct dir_rename_entry *check_dir_renamed(const char *path,
 +                                                struct hashmap *dir_renames)
 +{
 +      char temp[PATH_MAX];
 +      char *end;
 +      struct dir_rename_entry *entry;
 +
 +      strcpy(temp, path);
 +      while ((end = strrchr(temp, '/'))) {
 +              *end = '\0';
 +              entry = dir_rename_find_entry(dir_renames, temp);
 +              if (entry)
 +                      return entry;
 +      }
 +      return NULL;
 +}
 +
 +static void compute_collisions(struct hashmap *collisions,
 +                             struct hashmap *dir_renames,
 +                             struct diff_queue_struct *pairs)
 +{
 +      int i;
 +
 +      /*
 +       * Multiple files can be mapped to the same path due to directory
 +       * renames done by the other side of history.  Since that other
 +       * side of history could have merged multiple directories into one,
 +       * if our side of history added the same file basename to each of
 +       * those directories, then all N of them would get implicitly
 +       * renamed by the directory rename detection into the same path,
 +       * and we'd get an add/add/.../add conflict, and all those adds
 +       * from *this* side of history.  This is not representable in the
 +       * index, and users aren't going to easily be able to make sense of
 +       * it.  So we need to provide a good warning about what's
 +       * happening, and fall back to no-directory-rename detection
 +       * behavior for those paths.
 +       *
 +       * See testcases 9e and all of section 5 from t6043 for examples.
 +       */
 +      collision_init(collisions);
 +
 +      for (i = 0; i < pairs->nr; ++i) {
 +              struct dir_rename_entry *dir_rename_ent;
 +              struct collision_entry *collision_ent;
 +              char *new_path;
 +              struct diff_filepair *pair = pairs->queue[i];
 +
 +              if (pair->status != 'A' && pair->status != 'R')
 +                      continue;
 +              dir_rename_ent = check_dir_renamed(pair->two->path,
 +                                                 dir_renames);
 +              if (!dir_rename_ent)
 +                      continue;
 +
 +              new_path = apply_dir_rename(dir_rename_ent, pair->two->path);
 +              if (!new_path)
 +                      /*
 +                       * dir_rename_ent->non_unique_new_path is true, which
 +                       * means there is no directory rename for us to use,
 +                       * which means it won't cause us any additional
 +                       * collisions.
 +                       */
 +                      continue;
 +              collision_ent = collision_find_entry(collisions, new_path);
 +              if (!collision_ent) {
 +                      collision_ent = xcalloc(1,
 +                                              sizeof(struct collision_entry));
 +                      hashmap_entry_init(collision_ent, strhash(new_path));
 +                      hashmap_put(collisions, collision_ent);
 +                      collision_ent->target_file = new_path;
 +              } else {
 +                      free(new_path);
 +              }
 +              string_list_insert(&collision_ent->source_files,
 +                                 pair->two->path);
 +      }
 +}
 +
 +static char *check_for_directory_rename(struct merge_options *o,
 +                                      const char *path,
 +                                      struct tree *tree,
 +                                      struct hashmap *dir_renames,
 +                                      struct hashmap *dir_rename_exclusions,
 +                                      struct hashmap *collisions,
 +                                      int *clean_merge)
 +{
 +      char *new_path = NULL;
 +      struct dir_rename_entry *entry = check_dir_renamed(path, dir_renames);
 +      struct dir_rename_entry *oentry = NULL;
 +
 +      if (!entry)
 +              return new_path;
 +
 +      /*
 +       * This next part is a little weird.  We do not want to do an
 +       * implicit rename into a directory we renamed on our side, because
 +       * that will result in a spurious rename/rename(1to2) conflict.  An
 +       * example:
 +       *   Base commit: dumbdir/afile, otherdir/bfile
 +       *   Side 1:      smrtdir/afile, otherdir/bfile
 +       *   Side 2:      dumbdir/afile, dumbdir/bfile
 +       * Here, while working on Side 1, we could notice that otherdir was
 +       * renamed/merged to dumbdir, and change the diff_filepair for
 +       * otherdir/bfile into a rename into dumbdir/bfile.  However, Side
 +       * 2 will notice the rename from dumbdir to smrtdir, and do the
 +       * transitive rename to move it from dumbdir/bfile to
 +       * smrtdir/bfile.  That gives us bfile in dumbdir vs being in
 +       * smrtdir, a rename/rename(1to2) conflict.  We really just want
 +       * the file to end up in smrtdir.  And the way to achieve that is
 +       * to not let Side1 do the rename to dumbdir, since we know that is
 +       * the source of one of our directory renames.
 +       *
 +       * That's why oentry and dir_rename_exclusions is here.
 +       *
 +       * As it turns out, this also prevents N-way transient rename
 +       * confusion; See testcases 9c and 9d of t6043.
 +       */
 +      oentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf);
 +      if (oentry) {
 +              output(o, 1, _("WARNING: Avoiding applying %s -> %s rename "
 +                             "to %s, because %s itself was renamed."),
 +                     entry->dir, entry->new_dir.buf, path, entry->new_dir.buf);
 +      } else {
 +              new_path = handle_path_level_conflicts(o, path, entry,
 +                                                     collisions, tree);
 +              *clean_merge &= (new_path != NULL);
 +      }
 +
 +      return new_path;
 +}
 +
 +static void apply_directory_rename_modifications(struct merge_options *o,
 +                                               struct diff_filepair *pair,
 +                                               char *new_path,
 +                                               struct rename *re,
 +                                               struct tree *tree,
 +                                               struct tree *o_tree,
 +                                               struct tree *a_tree,
 +                                               struct tree *b_tree,
 +                                               struct string_list *entries,
 +                                               int *clean)
 +{
 +      struct string_list_item *item;
 +      int stage = (tree == a_tree ? 2 : 3);
 +      int update_wd;
 +
 +      /*
 +       * In all cases where we can do directory rename detection,
 +       * unpack_trees() will have read pair->two->path into the
 +       * index and the working copy.  We need to remove it so that
 +       * we can instead place it at new_path.  It is guaranteed to
 +       * not be untracked (unpack_trees() would have errored out
 +       * saying the file would have been overwritten), but it might
 +       * be dirty, though.
 +       */
 +      update_wd = !was_dirty(o, pair->two->path);
 +      if (!update_wd)
 +              output(o, 1, _("Refusing to lose dirty file at %s"),
 +                     pair->two->path);
 +      remove_file(o, 1, pair->two->path, !update_wd);
 +
 +      /* Find or create a new re->dst_entry */
 +      item = string_list_lookup(entries, new_path);
 +      if (item) {
 +              /*
 +               * Since we're renaming on this side of history, and it's
 +               * due to a directory rename on the other side of history
 +               * (which we only allow when the directory in question no
 +               * longer exists on the other side of history), the
 +               * original entry for re->dst_entry is no longer
 +               * necessary...
 +               */
 +              re->dst_entry->processed = 1;
 +
 +              /*
 +               * ...because we'll be using this new one.
 +               */
 +              re->dst_entry = item->util;
 +      } else {
 +              /*
 +               * re->dst_entry is for the before-dir-rename path, and we
 +               * need it to hold information for the after-dir-rename
 +               * path.  Before creating a new entry, we need to mark the
 +               * old one as unnecessary (...unless it is shared by
 +               * src_entry, i.e. this didn't use to be a rename, in which
 +               * case we can just allow the normal processing to happen
 +               * for it).
 +               */
 +              if (pair->status == 'R')
 +                      re->dst_entry->processed = 1;
 +
 +              re->dst_entry = insert_stage_data(new_path,
 +                                                o_tree, a_tree, b_tree,
 +                                                entries);
 +              item = string_list_insert(entries, new_path);
 +              item->util = re->dst_entry;
 +      }
 +
 +      /*
 +       * Update the stage_data with the information about the path we are
 +       * moving into place.  That slot will be empty and available for us
 +       * to write to because of the collision checks in
 +       * handle_path_level_conflicts().  In other words,
 +       * re->dst_entry->stages[stage].oid will be the null_oid, so it's
 +       * open for us to write to.
 +       *
 +       * It may be tempting to actually update the index at this point as
 +       * well, using update_stages_for_stage_data(), but as per the big
 +       * "NOTE" in update_stages(), doing so will modify the current
 +       * in-memory index which will break calls to would_lose_untracked()
 +       * that we need to make.  Instead, we need to just make sure that
 +       * the various conflict_rename_*() functions update the index
 +       * explicitly rather than relying on unpack_trees() to have done it.
 +       */
-                      re->dst_entry->stages[stage].oid.hash,
++      get_tree_entry(&tree->object.oid,
 +                     pair->two->path,
++                     &re->dst_entry->stages[stage].oid,
 +                     &re->dst_entry->stages[stage].mode);
 +
 +      /* Update pair status */
 +      if (pair->status == 'A') {
 +              /*
 +               * Recording rename information for this add makes it look
 +               * like a rename/delete conflict.  Make sure we can
 +               * correctly handle this as an add that was moved to a new
 +               * directory instead of reporting a rename/delete conflict.
 +               */
 +              re->add_turned_into_rename = 1;
 +      }
 +      /*
 +       * We don't actually look at pair->status again, but it seems
 +       * pedagogically correct to adjust it.
 +       */
 +      pair->status = 'R';
 +
 +      /*
 +       * Finally, record the new location.
 +       */
 +      pair->two->path = new_path;
 +}
 +
 +/*
 + * Get information of all renames which occurred in 'pairs', making use of
 + * any implicit directory renames inferred from the other side of history.
 + * We need the three trees in the merge ('o_tree', 'a_tree' and 'b_tree')
 + * to be able to associate the correct cache entries with the rename
 + * information; tree is always equal to either a_tree or b_tree.
 + */
 +static struct string_list *get_renames(struct merge_options *o,
 +                                     struct diff_queue_struct *pairs,
 +                                     struct hashmap *dir_renames,
 +                                     struct hashmap *dir_rename_exclusions,
 +                                     struct tree *tree,
 +                                     struct tree *o_tree,
 +                                     struct tree *a_tree,
 +                                     struct tree *b_tree,
 +                                     struct string_list *entries,
 +                                     int *clean_merge)
 +{
 +      int i;
 +      struct hashmap collisions;
 +      struct hashmap_iter iter;
 +      struct collision_entry *e;
 +      struct string_list *renames;
 +
 +      compute_collisions(&collisions, dir_renames, pairs);
 +      renames = xcalloc(1, sizeof(struct string_list));
 +
 +      for (i = 0; i < pairs->nr; ++i) {
 +              struct string_list_item *item;
 +              struct rename *re;
 +              struct diff_filepair *pair = pairs->queue[i];
 +              char *new_path; /* non-NULL only with directory renames */
 +
 +              if (pair->status != 'A' && pair->status != 'R') {
 +                      diff_free_filepair(pair);
 +                      continue;
 +              }
 +              new_path = check_for_directory_rename(o, pair->two->path, tree,
 +                                                    dir_renames,
 +                                                    dir_rename_exclusions,
 +                                                    &collisions,
 +                                                    clean_merge);
 +              if (pair->status != 'R' && !new_path) {
 +                      diff_free_filepair(pair);
 +                      continue;
 +              }
 +
 +              re = xmalloc(sizeof(*re));
 +              re->processed = 0;
 +              re->add_turned_into_rename = 0;
 +              re->pair = pair;
 +              item = string_list_lookup(entries, re->pair->one->path);
 +              if (!item)
 +                      re->src_entry = insert_stage_data(re->pair->one->path,
 +                                      o_tree, a_tree, b_tree, entries);
 +              else
 +                      re->src_entry = item->util;
 +
 +              item = string_list_lookup(entries, re->pair->two->path);
 +              if (!item)
 +                      re->dst_entry = insert_stage_data(re->pair->two->path,
 +                                      o_tree, a_tree, b_tree, entries);
 +              else
 +                      re->dst_entry = item->util;
 +              item = string_list_insert(renames, pair->one->path);
 +              item->util = re;
 +              if (new_path)
 +                      apply_directory_rename_modifications(o, pair, new_path,
 +                                                           re, tree, o_tree,
 +                                                           a_tree, b_tree,
 +                                                           entries,
 +                                                           clean_merge);
 +      }
 +
 +      hashmap_iter_init(&collisions, &iter);
 +      while ((e = hashmap_iter_next(&iter))) {
 +              free(e->target_file);
 +              string_list_clear(&e->source_files, 0);
 +      }
 +      hashmap_free(&collisions, 1);
 +      return renames;
 +}
 +
  static int process_renames(struct merge_options *o,
                           struct string_list *a_renames,
                           struct string_list *b_renames)
                        dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
                        try_merge = 0;
  
 -                      if (oid_eq(&src_other.oid, &null_oid)) {
 +                      if (oid_eq(&src_other.oid, &null_oid) &&
 +                          ren1->add_turned_into_rename) {
 +                              setup_rename_conflict_info(RENAME_DIR,
 +                                                         ren1->pair,
 +                                                         NULL,
 +                                                         branch1,
 +                                                         branch2,
 +                                                         ren1->dst_entry,
 +                                                         NULL,
 +                                                         o,
 +                                                         NULL,
 +                                                         NULL);
 +                      } else if (oid_eq(&src_other.oid, &null_oid)) {
                                setup_rename_conflict_info(RENAME_DELETE,
                                                           ren1->pair,
                                                           NULL,
@@@ -2536,105 -1645,6 +2536,105 @@@ cleanup_and_return
        return clean_merge;
  }
  
 +struct rename_info {
 +      struct string_list *head_renames;
 +      struct string_list *merge_renames;
 +};
 +
 +static void initial_cleanup_rename(struct diff_queue_struct *pairs,
 +                                 struct hashmap *dir_renames)
 +{
 +      struct hashmap_iter iter;
 +      struct dir_rename_entry *e;
 +
 +      hashmap_iter_init(dir_renames, &iter);
 +      while ((e = hashmap_iter_next(&iter))) {
 +              free(e->dir);
 +              strbuf_release(&e->new_dir);
 +              /* possible_new_dirs already cleared in get_directory_renames */
 +      }
 +      hashmap_free(dir_renames, 1);
 +      free(dir_renames);
 +
 +      free(pairs->queue);
 +      free(pairs);
 +}
 +
 +static int handle_renames(struct merge_options *o,
 +                        struct tree *common,
 +                        struct tree *head,
 +                        struct tree *merge,
 +                        struct string_list *entries,
 +                        struct rename_info *ri)
 +{
 +      struct diff_queue_struct *head_pairs, *merge_pairs;
 +      struct hashmap *dir_re_head, *dir_re_merge;
 +      int clean = 1;
 +
 +      ri->head_renames = NULL;
 +      ri->merge_renames = NULL;
 +
 +      if (!o->detect_rename)
 +              return 1;
 +
 +      head_pairs = get_diffpairs(o, common, head);
 +      merge_pairs = get_diffpairs(o, common, merge);
 +
 +      dir_re_head = get_directory_renames(head_pairs, head);
 +      dir_re_merge = get_directory_renames(merge_pairs, merge);
 +
 +      handle_directory_level_conflicts(o,
 +                                       dir_re_head, head,
 +                                       dir_re_merge, merge);
 +
 +      ri->head_renames  = get_renames(o, head_pairs,
 +                                      dir_re_merge, dir_re_head, head,
 +                                      common, head, merge, entries,
 +                                      &clean);
 +      if (clean < 0)
 +              goto cleanup;
 +      ri->merge_renames = get_renames(o, merge_pairs,
 +                                      dir_re_head, dir_re_merge, merge,
 +                                      common, head, merge, entries,
 +                                      &clean);
 +      if (clean < 0)
 +              goto cleanup;
 +      clean &= process_renames(o, ri->head_renames, ri->merge_renames);
 +
 +cleanup:
 +      /*
 +       * Some cleanup is deferred until cleanup_renames() because the
 +       * data structures are still needed and referenced in
 +       * process_entry().  But there are a few things we can free now.
 +       */
 +      initial_cleanup_rename(head_pairs, dir_re_head);
 +      initial_cleanup_rename(merge_pairs, dir_re_merge);
 +
 +      return clean;
 +}
 +
 +static void final_cleanup_rename(struct string_list *rename)
 +{
 +      const struct rename *re;
 +      int i;
 +
 +      if (rename == NULL)
 +              return;
 +
 +      for (i = 0; i < rename->nr; i++) {
 +              re = rename->items[i].util;
 +              diff_free_filepair(re->pair);
 +      }
 +      string_list_clear(rename, 1);
 +      free(rename);
 +}
 +
 +static void final_cleanup_renames(struct rename_info *re_info)
 +{
 +      final_cleanup_rename(re_info->head_renames);
 +      final_cleanup_rename(re_info->merge_renames);
 +}
 +
  static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
  {
        return (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;
@@@ -2646,7 -1656,7 +2646,7 @@@ static int read_oid_strbuf(struct merge
        void *buf;
        enum object_type type;
        unsigned long size;
-       buf = read_sha1_file(oid->hash, &type, &size);
+       buf = read_object_file(oid, &type, &size);
        if (!buf)
                return err(o, _("cannot read object %s"), oid_to_hex(oid));
        if (type != OBJ_BLOB) {
@@@ -2725,7 -1735,6 +2725,7 @@@ static int handle_modify_delete(struct 
  
  static int merge_content(struct merge_options *o,
                         const char *path,
 +                       int file_in_way,
                         struct object_id *o_oid, int o_mode,
                         struct object_id *a_oid, int a_mode,
                         struct object_id *b_oid, int b_mode,
  
        if (mfi.clean && !df_conflict_remains &&
            oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {
 -              int path_renamed_outside_HEAD;
                output(o, 3, _("Skipped %s (merged same as existing)"), path);
                /*
                 * The content merge resulted in the same file contents we
                 * are recorded at the correct path (which may not be true
                 * if the merge involves a rename).
                 */
 -              path_renamed_outside_HEAD = !path2 || !strcmp(path, path2);
 -              if (!path_renamed_outside_HEAD) {
 +              if (was_tracked(path)) {
                        add_cacheinfo(o, mfi.mode, &mfi.oid, path,
                                      0, (!o->call_depth), 0);
                        return mfi.clean;
                                return -1;
        }
  
 -      if (df_conflict_remains) {
 +      if (df_conflict_remains || file_in_way) {
                char *new_path;
                if (o->call_depth) {
                        remove_file_from_cache(path);
        return mfi.clean;
  }
  
 +static int conflict_rename_normal(struct merge_options *o,
 +                                const char *path,
 +                                struct object_id *o_oid, unsigned int o_mode,
 +                                struct object_id *a_oid, unsigned int a_mode,
 +                                struct object_id *b_oid, unsigned int b_mode,
 +                                struct rename_conflict_info *ci)
 +{
 +      int clean_merge;
 +      int file_in_the_way = 0;
 +
 +      if (was_dirty(o, path)) {
 +              file_in_the_way = 1;
 +              output(o, 1, _("Refusing to lose dirty file at %s"), path);
 +      }
 +
 +      /* Merge the content and write it out */
 +      clean_merge = merge_content(o, path, file_in_the_way,
 +                                  o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
 +                                  ci);
 +      if (clean_merge > 0 && file_in_the_way)
 +              clean_merge = 0;
 +      return clean_merge;
 +}
 +
  /* Per entry merge function */
  static int process_entry(struct merge_options *o,
                         const char *path, struct stage_data *entry)
                switch (conflict_info->rename_type) {
                case RENAME_NORMAL:
                case RENAME_ONE_FILE_TO_ONE:
 -                      clean_merge = merge_content(o, path,
 -                                                  o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
 -                                                  conflict_info);
 +                      clean_merge = conflict_rename_normal(o,
 +                                                           path,
 +                                                           o_oid, o_mode,
 +                                                           a_oid, a_mode,
 +                                                           b_oid, b_mode,
 +                                                           conflict_info);
 +                      break;
 +              case RENAME_DIR:
 +                      clean_merge = 1;
 +                      if (conflict_rename_dir(o,
 +                                              conflict_info->pair1,
 +                                              conflict_info->branch1,
 +                                              conflict_info->branch2))
 +                              clean_merge = -1;
                        break;
                case RENAME_DELETE:
                        clean_merge = 0;
        } else if (a_oid && b_oid) {
                /* Case C: Added in both (check for same permissions) and */
                /* case D: Modified in both, but differently. */
 -              clean_merge = merge_content(o, path,
 +              clean_merge = merge_content(o, path, 0 /* file_in_way */,
                                            o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
                                            NULL);
        } else if (!o_oid && !a_oid && !b_oid) {
@@@ -3017,7 -1993,7 +3017,7 @@@ int merge_trees(struct merge_options *o
                return 1;
        }
  
 -      code = git_merge_trees(o->call_depth, common, head, merge);
 +      code = git_merge_trees(o, common, head, merge);
  
        if (code != 0) {
                if (show(o, 4) || o->call_depth)
        }
  
        if (unmerged_cache()) {
 -              struct string_list *entries, *re_head, *re_merge;
 +              struct string_list *entries;
 +              struct rename_info re_info;
                int i;
                /*
                 * Only need the hashmap while processing entries, so
                get_files_dirs(o, merge);
  
                entries = get_unmerged();
 -              re_head  = get_renames(o, head, common, head, merge, entries);
 -              re_merge = get_renames(o, merge, common, head, merge, entries);
 -              clean = process_renames(o, re_head, re_merge);
 +              clean = handle_renames(o, common, head, merge, entries,
 +                                     &re_info);
                record_df_conflict_files(o, entries);
                if (clean < 0)
                        goto cleanup;
                }
  
  cleanup:
 -              string_list_clear(re_merge, 0);
 -              string_list_clear(re_head, 0);
 +              final_cleanup_renames(&re_info);
 +
                string_list_clear(entries, 1);
 +              free(entries);
  
                hashmap_free(&o->current_file_dir_set, 1);
  
 -              free(re_merge);
 -              free(re_head);
 -              free(entries);
 -
                if (clean < 0)
                        return clean;
        }
@@@ -3111,7 -2090,7 +3111,7 @@@ int merge_recursive(struct merge_option
  {
        struct commit_list *iter;
        struct commit *merged_common_ancestors;
 -      struct tree *mrtree = mrtree;
 +      struct tree *mrtree;
        int clean;
  
        if (show(o, 4)) {
@@@ -3239,13 -2218,11 +3239,13 @@@ int merge_recursive_generic(struct merg
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
 -      if (clean < 0)
 +      if (clean < 0) {
 +              rollback_lock_file(&lock);
                return clean;
 +      }
  
 -      if (active_cache_changed &&
 -          write_locked_index(&the_index, &lock, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
                return err(o, _("Unable to write index."));
  
        return clean ? 0 : 1;
diff --combined read-cache.c
index 59a73f4a81d76a19b8a2280e9643f7c1e715a5d4,3f0246fe8822fb7584826a5de449b6a6055d47cc..10f1c6bb8a316e85448445afc3478c832d61709c
@@@ -62,7 -62,6 +62,7 @@@ static void replace_index_entry(struct 
        replace_index_entry_in_base(istate, old, ce);
        remove_name_hash(istate, old);
        free(old);
 +      ce->ce_flags &= ~CE_HASHED;
        set_index_entry(istate, nr, ce);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
        mark_fsmonitor_invalid(istate, ce);
@@@ -185,7 -184,7 +185,7 @@@ static int ce_compare_link(const struc
        if (strbuf_readlink(&sb, ce->name, expected_size))
                return -1;
  
-       buffer = read_sha1_file(ce->oid.hash, &type, &size);
+       buffer = read_object_file(&ce->oid, &type, &size);
        if (buffer) {
                if (size == sb.len)
                        match = memcmp(buffer, sb.buf, size);
@@@ -1325,8 -1324,7 +1325,8 @@@ static struct cache_entry *refresh_cach
  
        size = ce_size(ce);
        updated = xmalloc(size);
 -      memcpy(updated, ce, size);
 +      copy_cache_entry(updated, ce);
 +      memcpy(updated->name, ce->name, ce->ce_namelen + 1);
        fill_stat_cache_info(updated, &st);
        /*
         * If ignore_valid is not set, we should leave CE_VALID bit
@@@ -2110,15 -2108,13 +2110,15 @@@ static int ce_write_entry(git_hash_ctx 
                          struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
  {
        int size;
 -      int saved_namelen = saved_namelen; /* compiler workaround */
        int result;
 +      unsigned int saved_namelen;
 +      int stripped_name = 0;
        static unsigned char padding[8] = { 0x00 };
  
        if (ce->ce_flags & CE_STRIP_NAME) {
                saved_namelen = ce_namelen(ce);
                ce->ce_namelen = 0;
 +              stripped_name = 1;
        }
  
        if (ce->ce_flags & CE_EXTENDED)
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
        }
 -      if (ce->ce_flags & CE_STRIP_NAME) {
 +      if (stripped_name) {
                ce->ce_namelen = saved_namelen;
                ce->ce_flags &= ~CE_STRIP_NAME;
        }
@@@ -2542,12 -2538,6 +2542,12 @@@ int write_locked_index(struct index_sta
        int new_shared_index, ret;
        struct split_index *si = istate->split_index;
  
 +      if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
 +              if (flags & COMMIT_LOCK)
 +                      rollback_lock_file(lock);
 +              return 0;
 +      }
 +
        if (istate->fsmonitor_last_update)
                fill_fsmonitor_bitmap(istate);
  
@@@ -2693,7 -2683,7 +2693,7 @@@ void *read_blob_data_from_index(const s
        }
        if (pos < 0)
                return NULL;
-       data = read_sha1_file(istate->cache[pos]->oid.hash, &type, &sz);
+       data = read_object_file(&istate->cache[pos]->oid, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return NULL;
diff --combined rerere.c
index ea24d4c2f47ab6495d97f38245dd420a1ad38391,8af6b660e0dc71b0c82db1ff21c58babffaf8800..18cae2d11c9a86aae0ed352a8f7606b142c5c183
+++ b/rerere.c
@@@ -719,9 -719,11 +719,9 @@@ static void update_paths(struct string_
                        item->string);
        }
  
 -      if (active_cache_changed) {
 -              if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
 -                      die("Unable to write new index file");
 -      } else
 -              rollback_lock_file(&index_lock);
 +      if (write_locked_index(&the_index, &index_lock,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
 +              die("Unable to write new index file");
  }
  
  static void remove_variant(struct rerere_id *id)
@@@ -979,8 -981,8 +979,8 @@@ static int handle_cache(const char *pat
                        break;
                i = ce_stage(ce) - 1;
                if (!mmfile[i].ptr) {
-                       mmfile[i].ptr = read_sha1_file(ce->oid.hash, &type,
-                                                      &size);
+                       mmfile[i].ptr = read_object_file(&ce->oid, &type,
+                                                        &size);
                        mmfile[i].size = size;
                }
        }
diff --combined sequencer.c
index f9d1001dee9ad10e243aaeafc46fbdd13597fce7,47b050edfbba053cc5fac3266994c3f68fb5735d..667f35ebdffbc1ef730e310cbea9b3dbce786e21
@@@ -282,7 -282,7 +282,7 @@@ struct commit_message 
  
  static const char *short_commit_name(struct commit *commit)
  {
-       return find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
+       return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV);
  }
  
  static int get_message(struct commit *commit, struct commit_message *out)
@@@ -339,7 -339,7 +339,7 @@@ static void print_advice(int show_hint
  static int write_message(const void *buf, size_t len, const char *filename,
                         int append_eol)
  {
 -      static struct lock_file msg_file;
 +      struct lock_file msg_file = LOCK_INIT;
  
        int msg_fd = hold_lock_file_for_update(&msg_file, filename, 0);
        if (msg_fd < 0)
                rollback_lock_file(&msg_file);
                return error_errno(_("could not write eol to '%s'"), filename);
        }
 -      if (commit_lock_file(&msg_file) < 0) {
 -              rollback_lock_file(&msg_file);
 -              return error(_("failed to finalize '%s'."), filename);
 -      }
 +      if (commit_lock_file(&msg_file) < 0)
 +              return error(_("failed to finalize '%s'"), filename);
  
        return 0;
  }
@@@ -483,7 -485,7 +483,7 @@@ static int do_recursive_merge(struct co
        struct tree *result, *next_tree, *base_tree, *head_tree;
        int clean;
        char **xopt;
 -      static struct lock_file index_lock;
 +      struct lock_file index_lock = LOCK_INIT;
  
        if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
                fputs(o.obuf.buf, stdout);
        strbuf_release(&o.obuf);
        diff_warn_rename_limit("merge.renamelimit", o.needed_rename_limit, 0);
 -      if (clean < 0)
 +      if (clean < 0) {
 +              rollback_lock_file(&index_lock);
                return clean;
 +      }
  
 -      if (active_cache_changed &&
 -          write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &index_lock,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
                /*
                 * TRANSLATORS: %s will be "revert", "cherry-pick" or
                 * "rebase -i".
                 */
                return error(_("%s: Unable to write new index file"),
                        _(action_name(opts)));
 -      rollback_lock_file(&index_lock);
  
        if (!clean)
                append_conflicts_hint(msgbuf);
@@@ -1112,7 -1113,7 +1112,7 @@@ static int try_to_commit(struct strbuf 
                commit_list_insert(current_head, &parents);
        }
  
-       if (write_cache_as_tree(tree.hash, 0, NULL)) {
+       if (write_cache_as_tree(&tree, 0, NULL)) {
                res = error(_("git write-tree failed to write a tree"));
                goto out;
        }
@@@ -1474,7 -1475,7 +1474,7 @@@ static int do_pick_commit(enum todo_com
                 * that represents the "current" state for merge-recursive
                 * to work on.
                 */
-               if (write_cache_as_tree(head.hash, 0, NULL))
+               if (write_cache_as_tree(&head, 0, NULL))
                        return error(_("your index file is unmerged."));
        } else {
                unborn = get_oid("HEAD", &head);
@@@ -1704,7 -1705,7 +1704,7 @@@ static int prepare_revs(struct replay_o
  
  static int read_and_refresh_cache(struct replay_opts *opts)
  {
 -      static struct lock_file index_lock;
 +      struct lock_file index_lock = LOCK_INIT;
        int index_fd = hold_locked_index(&index_lock, 0);
        if (read_index_preload(&the_index, NULL) < 0) {
                rollback_lock_file(&index_lock);
                        _(action_name(opts)));
        }
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
 -      if (the_index.cache_changed && index_fd >= 0) {
 -              if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
 +      if (index_fd >= 0) {
 +              if (write_locked_index(&the_index, &index_lock,
 +                                     COMMIT_LOCK | SKIP_IF_UNCHANGED)) {
                        return error(_("git %s: failed to refresh the index"),
                                _(action_name(opts)));
                }
        }
 -      rollback_lock_file(&index_lock);
        return 0;
  }
  
@@@ -2107,14 -2108,16 +2107,14 @@@ static int create_seq_dir(void
  
  static int save_head(const char *head)
  {
 -      static struct lock_file head_lock;
 +      struct lock_file head_lock = LOCK_INIT;
        struct strbuf buf = STRBUF_INIT;
        int fd;
        ssize_t written;
  
        fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
 -      if (fd < 0) {
 -              rollback_lock_file(&head_lock);
 +      if (fd < 0)
                return error_errno(_("could not lock HEAD"));
 -      }
        strbuf_addf(&buf, "%s\n", head);
        written = write_in_full(fd, buf.buf, buf.len);
        strbuf_release(&buf);
                return error_errno(_("could not write to '%s'"),
                                   git_path_head_file());
        }
 -      if (commit_lock_file(&head_lock) < 0) {
 -              rollback_lock_file(&head_lock);
 -              return error(_("failed to finalize '%s'."), git_path_head_file());
 -      }
 +      if (commit_lock_file(&head_lock) < 0)
 +              return error(_("failed to finalize '%s'"), git_path_head_file());
        return 0;
  }
  
@@@ -2228,7 -2233,7 +2228,7 @@@ fail
  
  static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
  {
 -      static struct lock_file todo_lock;
 +      struct lock_file todo_lock = LOCK_INIT;
        const char *todo_path = get_todo_path(opts);
        int next = todo_list->current, offset, fd;
  
                        todo_list->buf.len - offset) < 0)
                return error_errno(_("could not write to '%s'"), todo_path);
        if (commit_lock_file(&todo_lock) < 0)
 -              return error(_("failed to finalize '%s'."), todo_path);
 +              return error(_("failed to finalize '%s'"), todo_path);
  
        if (is_rebase_i(opts)) {
                const char *done_path = rebase_path_done();
@@@ -2876,7 -2881,8 +2876,8 @@@ int sequencer_pick_revisions(struct rep
  
                if (!get_oid(name, &oid)) {
                        if (!lookup_commit_reference_gently(&oid, 1)) {
-                               enum object_type type = sha1_object_info(oid.hash, NULL);
+                               enum object_type type = oid_object_info(&oid,
+                                                                       NULL);
                                return error(_("%s: can't cherry-pick a %s"),
                                        name, type_name(type));
                        }
diff --combined sha1_file.c
index ad775495659ee09cf287965f2f7b3044bb1b04e2,581a9dc52223ceb6d2b21a1b674688c6788f8339..1c61d9d9273f14f669b703271cc3f34dd5ca57c7
@@@ -30,6 -30,9 +30,9 @@@
  #include "packfile.h"
  #include "fetch-object.h"
  
+ /* The maximum size for an object header. */
+ #define MAX_HEADER_LEN 32
  const unsigned char null_sha1[GIT_MAX_RAWSZ];
  const struct object_id null_oid;
  const struct object_id empty_tree_oid = {
@@@ -784,22 -787,22 +787,22 @@@ void *xmmap(void *start, size_t length
   * With "map" == NULL, try reading the object named with "sha1" using
   * the streaming interface and rehash it to do the same.
   */
- int check_sha1_signature(const unsigned char *sha1, void *map,
-                        unsigned long size, const char *type)
+ int check_object_signature(const struct object_id *oid, void *map,
+                          unsigned long size, const char *type)
  {
        struct object_id real_oid;
        enum object_type obj_type;
        struct git_istream *st;
        git_hash_ctx c;
-       char hdr[32];
+       char hdr[MAX_HEADER_LEN];
        int hdrlen;
  
        if (map) {
                hash_object_file(map, size, type, &real_oid);
-               return hashcmp(sha1, real_oid.hash) ? -1 : 0;
+               return oidcmp(oid, &real_oid) ? -1 : 0;
        }
  
-       st = open_istream(sha1, &obj_type, &size, NULL);
+       st = open_istream(oid, &obj_type, &size, NULL);
        if (!st)
                return -1;
  
        }
        the_hash_algo->final_fn(real_oid.hash, &c);
        close_istream(st);
-       return hashcmp(sha1, real_oid.hash) ? -1 : 0;
+       return oidcmp(oid, &real_oid) ? -1 : 0;
  }
  
  int git_open_cloexec(const char *name, int flags)
@@@ -1150,7 -1153,7 +1153,7 @@@ static int sha1_loose_object_info(cons
        unsigned long mapsize;
        void *map;
        git_zstream stream;
-       char hdr[32];
+       char hdr[MAX_HEADER_LEN];
        struct strbuf hdrbuf = STRBUF_INIT;
        unsigned long size_scratch;
  
  
  int fetch_if_missing = 1;
  
- int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
+ int oid_object_info_extended(const struct object_id *oid, struct object_info *oi, unsigned flags)
  {
        static struct object_info blank_oi = OBJECT_INFO_INIT;
        struct pack_entry e;
        int rtype;
-       const unsigned char *real = (flags & OBJECT_INFO_LOOKUP_REPLACE) ?
-                                   lookup_replace_object(sha1) :
-                                   sha1;
+       const struct object_id *real = oid;
        int already_retried = 0;
  
-       if (is_null_sha1(real))
+       if (flags & OBJECT_INFO_LOOKUP_REPLACE)
+               real = lookup_replace_object(oid);
+       if (is_null_oid(real))
                return -1;
  
        if (!oi)
                oi = &blank_oi;
  
        if (!(flags & OBJECT_INFO_SKIP_CACHED)) {
-               struct cached_object *co = find_cached_object(real);
+               struct cached_object *co = find_cached_object(real->hash);
                if (co) {
                        if (oi->typep)
                                *(oi->typep) = co->type;
        }
  
        while (1) {
-               if (find_pack_entry(real, &e))
+               if (find_pack_entry(real->hash, &e))
                        break;
  
 +              if (flags & OBJECT_INFO_IGNORE_LOOSE)
 +                      return -1;
 +
                /* Most likely it's a loose object. */
-               if (!sha1_loose_object_info(real, oi, flags))
+               if (!sha1_loose_object_info(real->hash, oi, flags))
                        return 0;
  
                /* Not a loose object; someone else may have just packed it. */
 -              reprepare_packed_git();
 -              if (find_pack_entry(real->hash, &e))
 -                      break;
 +              if (!(flags & OBJECT_INFO_QUICK)) {
 +                      reprepare_packed_git();
-                       if (find_pack_entry(real, &e))
++                      if (find_pack_entry(real->hash, &e))
 +                              break;
 +              }
  
                /* Check if it is a missing object */
                if (fetch_if_missing && repository_format_partial_clone &&
                         * TODO Investigate haveing fetch_object() return
                         * TODO error/success and stopping the music here.
                         */
-                       fetch_object(repository_format_partial_clone, real);
+                       fetch_object(repository_format_partial_clone, real->hash);
                        already_retried = 1;
                        continue;
                }
                return 0;
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
-               mark_bad_packed_object(e.p, real);
-               return sha1_object_info_extended(real, oi, 0);
+               mark_bad_packed_object(e.p, real->hash);
+               return oid_object_info_extended(real, oi, 0);
        } else if (oi->whence == OI_PACKED) {
                oi->u.packed.offset = e.offset;
                oi->u.packed.pack = e.p;
  }
  
  /* returns enum object_type or negative */
- int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
+ int oid_object_info(const struct object_id *oid, unsigned long *sizep)
  {
        enum object_type type;
        struct object_info oi = OBJECT_INFO_INIT;
  
        oi.typep = &type;
        oi.sizep = sizep;
-       if (sha1_object_info_extended(sha1, &oi,
-                                     OBJECT_INFO_LOOKUP_REPLACE) < 0)
+       if (oid_object_info_extended(oid, &oi,
+                                    OBJECT_INFO_LOOKUP_REPLACE) < 0)
                return -1;
        return type;
  }
  static void *read_object(const unsigned char *sha1, enum object_type *type,
                         unsigned long *size)
  {
+       struct object_id oid;
        struct object_info oi = OBJECT_INFO_INIT;
        void *content;
        oi.typep = type;
        oi.sizep = size;
        oi.contentp = &content;
  
-       if (sha1_object_info_extended(sha1, &oi, 0) < 0)
+       hashcpy(oid.hash, sha1);
+       if (oid_object_info_extended(&oid, &oi, 0) < 0)
                return NULL;
        return content;
  }
@@@ -1362,65 -1364,65 +1369,65 @@@ int pretend_object_file(void *buf, unsi
   * deal with them should arrange to call read_object() and give error
   * messages themselves.
   */
- void *read_sha1_file_extended(const unsigned char *sha1,
-                             enum object_type *type,
-                             unsigned long *size,
-                             int lookup_replace)
+ void *read_object_file_extended(const struct object_id *oid,
+                               enum object_type *type,
+                               unsigned long *size,
+                               int lookup_replace)
  {
        void *data;
        const struct packed_git *p;
        const char *path;
        struct stat st;
-       const unsigned char *repl = lookup_replace ? lookup_replace_object(sha1)
-                                                  : sha1;
+       const struct object_id *repl = lookup_replace ? lookup_replace_object(oid)
+                                                     : oid;
  
        errno = 0;
-       data = read_object(repl, type, size);
+       data = read_object(repl->hash, type, size);
        if (data)
                return data;
  
        if (errno && errno != ENOENT)
-               die_errno("failed to read object %s", sha1_to_hex(sha1));
+               die_errno("failed to read object %s", oid_to_hex(oid));
  
        /* die if we replaced an object with one that does not exist */
-       if (repl != sha1)
+       if (repl != oid)
                die("replacement %s not found for %s",
-                   sha1_to_hex(repl), sha1_to_hex(sha1));
+                   oid_to_hex(repl), oid_to_hex(oid));
  
-       if (!stat_sha1_file(repl, &st, &path))
+       if (!stat_sha1_file(repl->hash, &st, &path))
                die("loose object %s (stored in %s) is corrupt",
-                   sha1_to_hex(repl), path);
+                   oid_to_hex(repl), path);
  
-       if ((p = has_packed_and_bad(repl)) != NULL)
+       if ((p = has_packed_and_bad(repl->hash)) != NULL)
                die("packed object %s (stored in %s) is corrupt",
-                   sha1_to_hex(repl), p->pack_name);
+                   oid_to_hex(repl), p->pack_name);
  
        return NULL;
  }
  
- void *read_object_with_reference(const unsigned char *sha1,
+ void *read_object_with_reference(const struct object_id *oid,
                                 const char *required_type_name,
                                 unsigned long *size,
-                                unsigned char *actual_sha1_return)
+                                struct object_id *actual_oid_return)
  {
        enum object_type type, required_type;
        void *buffer;
        unsigned long isize;
-       unsigned char actual_sha1[20];
+       struct object_id actual_oid;
  
        required_type = type_from_string(required_type_name);
-       hashcpy(actual_sha1, sha1);
+       oidcpy(&actual_oid, oid);
        while (1) {
                int ref_length = -1;
                const char *ref_type = NULL;
  
-               buffer = read_sha1_file(actual_sha1, &type, &isize);
+               buffer = read_object_file(&actual_oid, &type, &isize);
                if (!buffer)
                        return NULL;
                if (type == required_type) {
                        *size = isize;
-                       if (actual_sha1_return)
-                               hashcpy(actual_sha1_return, actual_sha1);
+                       if (actual_oid_return)
+                               oidcpy(actual_oid_return, &actual_oid);
                        return buffer;
                }
                /* Handle references */
                }
                ref_length = strlen(ref_type);
  
-               if (ref_length + 40 > isize ||
+               if (ref_length + GIT_SHA1_HEXSZ > isize ||
                    memcmp(buffer, ref_type, ref_length) ||
-                   get_sha1_hex((char *) buffer + ref_length, actual_sha1)) {
+                   get_oid_hex((char *) buffer + ref_length, &actual_oid)) {
                        free(buffer);
                        return NULL;
                }
                free(buffer);
                /* Now we have the ID of the referred-to object in
-                * actual_sha1.  Check again. */
+                * actual_oid.  Check again. */
        }
  }
  
@@@ -1515,7 -1517,7 +1522,7 @@@ static int write_buffer(int fd, const v
  int hash_object_file(const void *buf, unsigned long len, const char *type,
                     struct object_id *oid)
  {
-       char hdr[32];
+       char hdr[MAX_HEADER_LEN];
        int hdrlen = sizeof(hdr);
        write_object_file_prepare(buf, len, type, oid, hdr, &hdrlen);
        return 0;
@@@ -1670,7 -1672,7 +1677,7 @@@ static int freshen_packed_object(const 
  int write_object_file(const void *buf, unsigned long len, const char *type,
                      struct object_id *oid)
  {
-       char hdr[32];
+       char hdr[MAX_HEADER_LEN];
        int hdrlen = sizeof(hdr);
  
        /* Normally if we have it in the pack then we do not bother writing
@@@ -1690,7 -1692,7 +1697,7 @@@ int hash_object_file_literally(const vo
        int hdrlen, status = 0;
  
        /* type string, SP, %lu of the length plus NUL must fit this */
-       hdrlen = strlen(type) + 32;
+       hdrlen = strlen(type) + MAX_HEADER_LEN;
        header = xmalloc(hdrlen);
        write_object_file_prepare(buf, len, type, oid, header, &hdrlen);
  
@@@ -1710,7 -1712,7 +1717,7 @@@ int force_object_loose(const struct obj
        void *buf;
        unsigned long len;
        enum object_type type;
-       char hdr[32];
+       char hdr[MAX_HEADER_LEN];
        int hdrlen;
        int ret;
  
  
  int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
  {
+       struct object_id oid;
        if (!startup_info->have_repository)
                return 0;
-       return sha1_object_info_extended(sha1, NULL,
-                                        flags | OBJECT_INFO_SKIP_CACHED) >= 0;
+       hashcpy(oid.hash, sha1);
+       return oid_object_info_extended(&oid, NULL,
+                                       flags | OBJECT_INFO_SKIP_CACHED) >= 0;
  }
  
  int has_object_file(const struct object_id *oid)
@@@ -1897,7 -1901,7 +1906,7 @@@ static int index_stream(struct object_i
                        enum object_type type, const char *path,
                        unsigned flags)
  {
-       return index_bulk_checkin(oid->hash, fd, size, type, path, flags);
+       return index_bulk_checkin(oid, fd, size, type, path, flags);
  }
  
  int index_fd(struct object_id *oid, int fd, struct stat *st,
@@@ -1971,13 -1975,13 +1980,13 @@@ int read_pack_header(int fd, struct pac
        return 0;
  }
  
- void assert_sha1_type(const unsigned char *sha1, enum object_type expect)
+ void assert_oid_type(const struct object_id *oid, enum object_type expect)
  {
-       enum object_type type = sha1_object_info(sha1, NULL);
+       enum object_type type = oid_object_info(oid, NULL);
        if (type < 0)
-               die("%s is not a valid object", sha1_to_hex(sha1));
+               die("%s is not a valid object", oid_to_hex(oid));
        if (type != expect)
-               die("%s is not a valid '%s' object", sha1_to_hex(sha1),
+               die("%s is not a valid '%s' object", oid_to_hex(oid),
                    type_name(expect));
  }
  
@@@ -2181,7 -2185,7 +2190,7 @@@ static int check_stream_sha1(git_zstrea
  }
  
  int read_loose_object(const char *path,
-                     const unsigned char *expected_sha1,
+                     const struct object_id *expected_oid,
                      enum object_type *type,
                      unsigned long *size,
                      void **contents)
        void *map = NULL;
        unsigned long mapsize;
        git_zstream stream;
-       char hdr[32];
+       char hdr[MAX_HEADER_LEN];
  
        *contents = NULL;
  
        }
  
        if (*type == OBJ_BLOB) {
-               if (check_stream_sha1(&stream, hdr, *size, path, expected_sha1) < 0)
+               if (check_stream_sha1(&stream, hdr, *size, path, expected_oid->hash) < 0)
                        goto out;
        } else {
-               *contents = unpack_sha1_rest(&stream, hdr, *size, expected_sha1);
+               *contents = unpack_sha1_rest(&stream, hdr, *size, expected_oid->hash);
                if (!*contents) {
                        error("unable to unpack contents of %s", path);
                        git_inflate_end(&stream);
                        goto out;
                }
-               if (check_sha1_signature(expected_sha1, *contents,
+               if (check_object_signature(expected_oid, *contents,
                                         *size, type_name(*type))) {
                        error("sha1 mismatch for %s (expected %s)", path,
-                             sha1_to_hex(expected_sha1));
+                             oid_to_hex(expected_oid));
                        free(*contents);
                        goto out;
                }
diff --combined strbuf.c
index 013fc673fa421d6c5abdc40d3ce3c48528464724,aa97ad09197b4847329a58a72fd6022cc1742043..83d05024e6718f4b9481e1782ed84117d5e7598a
+++ b/strbuf.c
@@@ -1,6 -1,5 +1,6 @@@
  #include "cache.h"
  #include "refs.h"
 +#include "string-list.h"
  #include "utf8.h"
  
  int starts_with(const char *str, const char *prefix)
@@@ -96,7 -95,6 +96,7 @@@ void strbuf_trim(struct strbuf *sb
        strbuf_rtrim(sb);
        strbuf_ltrim(sb);
  }
 +
  void strbuf_rtrim(struct strbuf *sb)
  {
        while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
        sb->buf[sb->len] = '\0';
  }
  
 +void strbuf_trim_trailing_dir_sep(struct strbuf *sb)
 +{
 +      while (sb->len > 0 && is_dir_sep((unsigned char)sb->buf[sb->len - 1]))
 +              sb->len--;
 +      sb->buf[sb->len] = '\0';
 +}
 +
  void strbuf_ltrim(struct strbuf *sb)
  {
        char *b = sb->buf;
@@@ -172,21 -163,6 +172,21 @@@ struct strbuf **strbuf_split_buf(const 
        return ret;
  }
  
 +void strbuf_add_separated_string_list(struct strbuf *str,
 +                                    const char *sep,
 +                                    struct string_list *slist)
 +{
 +      struct string_list_item *item;
 +      int sep_needed = 0;
 +
 +      for_each_string_list_item(item, slist) {
 +              if (sep_needed)
 +                      strbuf_addstr(str, sep);
 +              strbuf_addstr(str, item->string);
 +              sep_needed = 1;
 +      }
 +}
 +
  void strbuf_list_free(struct strbuf **sbs)
  {
        struct strbuf **s = sbs;
@@@ -897,12 -873,12 +897,12 @@@ void strbuf_addftime(struct strbuf *sb
        strbuf_setlen(sb, sb->len + len);
  }
  
- void strbuf_add_unique_abbrev(struct strbuf *sb, const unsigned char *sha1,
+ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
                              int abbrev_len)
  {
        int r;
        strbuf_grow(sb, GIT_SHA1_HEXSZ + 1);
-       r = find_unique_abbrev_r(sb->buf + sb->len, sha1, abbrev_len);
+       r = find_unique_abbrev_r(sb->buf + sb->len, oid, abbrev_len);
        strbuf_setlen(sb, sb->len + r);
  }
  
diff --combined strbuf.h
index 652f17392c15993272db5891227ec25b3c78d8bd,cd7ad898eb293199b3f4c0fe1640aefec625fb4a..c4de5e4588bd4326d363b9387599cd94e29d2f8f
+++ b/strbuf.h
@@@ -1,8 -1,6 +1,8 @@@
  #ifndef STRBUF_H
  #define STRBUF_H
  
 +struct string_list;
 +
  /**
   * strbuf's are meant to be used with all the usual C string and memory
   * APIs. Given that the length of the buffer is known, it's often better to
@@@ -72,6 -70,12 +72,12 @@@ struct strbuf 
  extern char strbuf_slopbuf[];
  #define STRBUF_INIT  { .alloc = 0, .len = 0, .buf = strbuf_slopbuf }
  
+ /*
+  * Predeclare this here, since cache.h includes this file before it defines the
+  * struct.
+  */
+ struct object_id;
  /**
   * Life Cycle Functions
   * --------------------
@@@ -181,9 -185,6 +187,9 @@@ extern void strbuf_trim(struct strbuf *
  extern void strbuf_rtrim(struct strbuf *);
  extern void strbuf_ltrim(struct strbuf *);
  
 +/* Strip trailing directory separators */
 +extern void strbuf_trim_trailing_dir_sep(struct strbuf *);
 +
  /**
   * Replace the contents of the strbuf with a reencoded form.  Returns -1
   * on error, 0 on success.
@@@ -533,20 -534,6 +539,20 @@@ static inline struct strbuf **strbuf_sp
        return strbuf_split_max(sb, terminator, 0);
  }
  
 +/*
 + * Adds all strings of a string list to the strbuf, separated by the given
 + * separator.  For example, if sep is
 + *   ', '
 + * and slist contains
 + *   ['element1', 'element2', ..., 'elementN'],
 + * then write:
 + *   'element1, element2, ..., elementN'
 + * to str.  If only one element, just write "element1" to str.
 + */
 +extern void strbuf_add_separated_string_list(struct strbuf *str,
 +                                           const char *sep,
 +                                           struct string_list *slist);
 +
  /**
   * Free a NULL-terminated list of strbufs (for example, the return
   * values of the strbuf_split*() functions).
@@@ -558,7 -545,7 +564,7 @@@ extern void strbuf_list_free(struct str
   * the strbuf `sb`.
   */
  extern void strbuf_add_unique_abbrev(struct strbuf *sb,
-                                    const unsigned char *sha1,
+                                    const struct object_id *oid,
                                     int abbrev_len);
  
  /**