Merge branch 'js/plug-leaks'
authorJunio C Hamano <gitster@pobox.com>
Mon, 29 May 2017 03:34:44 +0000 (12:34 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 29 May 2017 03:34:44 +0000 (12:34 +0900)
Fix memory leaks pointed out by Coverity (and people).

* js/plug-leaks: (26 commits)
checkout: fix memory leak
submodule_uses_worktrees(): plug memory leak
show_worktree(): plug memory leak
name-rev: avoid leaking memory in the `deref` case
remote: plug memory leak in match_explicit()
add_reflog_for_walk: avoid memory leak
shallow: avoid memory leak
line-log: avoid memory leak
receive-pack: plug memory leak in update()
fast-export: avoid leaking memory in handle_tag()
mktree: plug memory leaks reported by Coverity
pack-redundant: plug memory leak
setup_discovered_git_dir(): plug memory leak
setup_bare_git_dir(): help static analysis
split_commit_in_progress(): simplify & fix memory leak
checkout: fix memory leak
cat-file: fix memory leak
mailinfo & mailsplit: check for EOF while parsing
status: close file descriptor after reading git-rebase-todo
difftool: address a couple of resource/memory leaks
...

12 files changed:
1  2 
builtin/am.c
builtin/checkout.c
builtin/fast-export.c
builtin/name-rev.c
builtin/receive-pack.c
builtin/worktree.c
config.c
reflog-walk.c
remote.c
shallow.c
worktree.c
wt-status.c
diff --combined builtin/am.c
index 2ec1d7b71eaf376e541764427120f7315d640740,9c5c2c778e28dc5bc2bfc3e5a1cf32c247ed273f..8b218f9978628534ab47db80ab66f880553f4065
@@@ -879,12 -879,12 +879,12 @@@ static int hg_patch_to_mail(FILE *out, 
                if (skip_prefix(sb.buf, "# User ", &str))
                        fprintf(out, "From: %s\n", str);
                else if (skip_prefix(sb.buf, "# Date ", &str)) {
 -                      unsigned long timestamp;
 +                      timestamp_t timestamp;
                        long tz, tz2;
                        char *end;
  
                        errno = 0;
 -                      timestamp = strtoul(str, &end, 10);
 +                      timestamp = parse_timestamp(str, &end, 10);
                        if (errno)
                                return error(_("invalid timestamp"));
  
@@@ -1145,7 -1145,7 +1145,7 @@@ static int index_has_changes(struct str
                DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
                if (!sb)
                        DIFF_OPT_SET(&opt, QUICK);
 -              do_diff_cache(head.hash, &opt);
 +              do_diff_cache(&head, &opt);
                diffcore_std(&opt);
                for (i = 0; sb && i < diff_queued_diff.nr; i++) {
                        if (i)
@@@ -1351,19 -1351,16 +1351,16 @@@ static int get_mail_commit_oid(struct o
        struct strbuf sb = STRBUF_INIT;
        FILE *fp = xfopen(mail, "r");
        const char *x;
+       int ret = 0;
  
-       if (strbuf_getline_lf(&sb, fp))
-               return -1;
-       if (!skip_prefix(sb.buf, "From ", &x))
-               return -1;
-       if (get_oid_hex(x, commit_id) < 0)
-               return -1;
+       if (strbuf_getline_lf(&sb, fp) ||
+           !skip_prefix(sb.buf, "From ", &x) ||
+           get_oid_hex(x, commit_id) < 0)
+               ret = -1;
  
        strbuf_release(&sb);
        fclose(fp);
-       return 0;
+       return ret;
  }
  
  /**
   */
  static void get_commit_info(struct am_state *state, struct commit *commit)
  {
 -      const char *buffer, *ident_line, *author_date, *msg;
 +      const char *buffer, *ident_line, *msg;
        size_t ident_len;
 -      struct ident_split ident_split;
 -      struct strbuf sb = STRBUF_INIT;
 +      struct ident_split id;
  
        buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
  
        ident_line = find_commit_header(buffer, "author", &ident_len);
  
 -      if (split_ident_line(&ident_split, ident_line, ident_len) < 0) {
 -              strbuf_add(&sb, ident_line, ident_len);
 -              die(_("invalid ident line: %s"), sb.buf);
 -      }
 +      if (split_ident_line(&id, ident_line, ident_len) < 0)
 +              die(_("invalid ident line: %.*s"), (int)ident_len, ident_line);
  
        assert(!state->author_name);
 -      if (ident_split.name_begin) {
 -              strbuf_add(&sb, ident_split.name_begin,
 -                      ident_split.name_end - ident_split.name_begin);
 -              state->author_name = strbuf_detach(&sb, NULL);
 -      } else
 +      if (id.name_begin)
 +              state->author_name =
 +                      xmemdupz(id.name_begin, id.name_end - id.name_begin);
 +      else
                state->author_name = xstrdup("");
  
        assert(!state->author_email);
 -      if (ident_split.mail_begin) {
 -              strbuf_add(&sb, ident_split.mail_begin,
 -                      ident_split.mail_end - ident_split.mail_begin);
 -              state->author_email = strbuf_detach(&sb, NULL);
 -      } else
 +      if (id.mail_begin)
 +              state->author_email =
 +                      xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
 +      else
                state->author_email = xstrdup("");
  
 -      author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL));
 -      strbuf_addstr(&sb, author_date);
        assert(!state->author_date);
 -      state->author_date = strbuf_detach(&sb, NULL);
 +      state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL)));
  
        assert(!state->msg);
        msg = strstr(buffer, "\n\n");
                die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
        state->msg = xstrdup(msg + 2);
        state->msg_len = strlen(state->msg);
 +      unuse_commit_buffer(commit, buffer);
  }
  
  /**
@@@ -1447,9 -1450,9 +1444,9 @@@ static void write_index_patch(const str
        FILE *fp;
  
        if (!get_sha1_tree("HEAD", head.hash))
 -              tree = lookup_tree(head.hash);
 +              tree = lookup_tree(&head);
        else
 -              tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
 +              tree = lookup_tree(&empty_tree_oid);
  
        fp = xfopen(am_path(state, "patch"), "w");
        init_revisions(&rev_info, NULL);
@@@ -1482,7 -1485,7 +1479,7 @@@ static int parse_mail_rebase(struct am_
        if (get_mail_commit_oid(&commit_oid, mail) < 0)
                die(_("could not parse %s"), mail);
  
 -      commit = lookup_commit_or_die(commit_oid.hash, mail);
 +      commit = lookup_commit_or_die(&commit_oid, mail);
  
        get_commit_info(state, commit);
  
@@@ -1612,7 -1615,7 +1609,7 @@@ static int fall_back_threeway(const str
                init_revisions(&rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
                diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
 -              add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);
 +              add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
                diff_setup_done(&rev_info.diffopt);
                run_diff_index(&rev_info, 1);
        }
@@@ -1677,7 -1680,7 +1674,7 @@@ static void do_commit(const struct am_s
  
        if (!get_sha1_commit("HEAD", parent.hash)) {
                old_oid = &parent;
 -              commit_list_insert(lookup_commit(parent.hash), &parents);
 +              commit_list_insert(lookup_commit(&parent), &parents);
        } else {
                old_oid = NULL;
                say(state, stderr, _("applying to an empty history"));
@@@ -2039,11 -2042,11 +2036,11 @@@ static int clean_index(const struct obj
        struct tree *head_tree, *remote_tree, *index_tree;
        struct object_id index;
  
 -      head_tree = parse_tree_indirect(head->hash);
 +      head_tree = parse_tree_indirect(head);
        if (!head_tree)
                return error(_("Could not parse object '%s'."), oid_to_hex(head));
  
 -      remote_tree = parse_tree_indirect(remote->hash);
 +      remote_tree = parse_tree_indirect(remote);
        if (!remote_tree)
                return error(_("Could not parse object '%s'."), oid_to_hex(remote));
  
        if (write_cache_as_tree(index.hash, 0, NULL))
                return -1;
  
 -      index_tree = parse_tree_indirect(index.hash);
 +      index_tree = parse_tree_indirect(&index);
        if (!index_tree)
                return error(_("Could not parse object '%s'."), oid_to_hex(&index));
  
@@@ -2150,7 -2153,7 +2147,7 @@@ static void am_abort(struct am_state *s
        am_rerere_clear();
  
        curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
 -      has_curr_head = !is_null_oid(&curr_head);
 +      has_curr_head = curr_branch && !is_null_oid(&curr_head);
        if (!has_curr_head)
                hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
  
diff --combined builtin/checkout.c
index 65877bacb1ed65139bacfb479ced4e322a972eb5,94849cf250ebcf35efe17c120205359d91ae7648..b6249a3a39eea52587c1515a1c54b4263e630a83
@@@ -235,22 -235,24 +235,24 @@@ static int checkout_merged(int pos, con
        /*
         * NEEDSWORK:
         * There is absolutely no reason to write this as a blob object
-        * and create a phony cache entry just to leak.  This hack is
-        * primarily to get to the write_entry() machinery that massages
-        * the contents to work-tree format and writes out which only
-        * allows it for a cache entry.  The code in write_entry() needs
-        * to be refactored to allow us to feed a <buffer, size, mode>
-        * instead of a cache entry.  Such a refactoring would help
-        * merge_recursive as well (it also writes the merge result to the
-        * object database even when it may contain conflicts).
+        * and create a phony cache entry.  This hack is primarily to get
+        * to the write_entry() machinery that massages the contents to
+        * work-tree format and writes out which only allows it for a
+        * cache entry.  The code in write_entry() needs to be refactored
+        * to allow us to feed a <buffer, size, mode> instead of a cache
+        * entry.  Such a refactoring would help merge_recursive as well
+        * (it also writes the merge result to the object database even
+        * when it may contain conflicts).
         */
        if (write_sha1_file(result_buf.ptr, result_buf.size,
                            blob_type, oid.hash))
                die(_("Unable to add merge result for '%s'"), path);
+       free(result_buf.ptr);
        ce = make_cache_entry(mode, oid.hash, path, 2, 0);
        if (!ce)
                die(_("make_cache_entry failed for path '%s'"), path);
        status = checkout_entry(ce, state, NULL);
+       free(ce);
        return status;
  }
  
@@@ -393,7 -395,7 +395,7 @@@ static int checkout_paths(const struct 
                die(_("unable to write new index file"));
  
        read_ref_full("HEAD", 0, rev.hash, NULL);
 -      head = lookup_commit_reference_gently(rev.hash, 1);
 +      head = lookup_commit_reference_gently(&rev, 1);
  
        errs |= post_checkout_hook(head, head, 0);
        return errs;
@@@ -527,10 -529,10 +529,10 @@@ static int merge_working_tree(const str
                        setup_standard_excludes(topts.dir);
                }
                tree = parse_tree_indirect(old->commit ?
 -                                         old->commit->object.oid.hash :
 -                                         EMPTY_TREE_SHA1_BIN);
 +                                         &old->commit->object.oid :
 +                                         &empty_tree_oid);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
 -              tree = parse_tree_indirect(new->commit->object.oid.hash);
 +              tree = parse_tree_indirect(&new->commit->object.oid);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
  
                ret = unpack_trees(2, trees, &topts);
@@@ -721,7 -723,7 +723,7 @@@ static int add_pending_uninteresting_re
                                         const struct object_id *oid,
                                         int flags, void *cb_data)
  {
 -      add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
 +      add_pending_oid(cb_data, refname, oid, UNINTERESTING);
        return 0;
  }
  
@@@ -807,7 -809,7 +809,7 @@@ static void orphaned_commit_warning(str
        add_pending_object(&revs, object, oid_to_hex(&object->oid));
  
        for_each_ref(add_pending_uninteresting_ref, &revs);
 -      add_pending_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING);
 +      add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
  
        refs = revs.pending;
        revs.leak_pending = 1;
@@@ -833,8 -835,7 +835,8 @@@ static int switch_branches(const struc
        int flag, writeout_error = 0;
        memset(&old, 0, sizeof(old));
        old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
 -      old.commit = lookup_commit_reference_gently(rev.hash, 1);
 +      if (old.path)
 +              old.commit = lookup_commit_reference_gently(&rev, 1);
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
  
@@@ -1048,10 -1049,10 +1050,10 @@@ static int parse_branchname_arg(int arg
        else
                new->path = NULL; /* not an existing branch */
  
 -      new->commit = lookup_commit_reference_gently(rev->hash, 1);
 +      new->commit = lookup_commit_reference_gently(rev, 1);
        if (!new->commit) {
                /* not a commit */
 -              *source_tree = parse_tree_indirect(rev->hash);
 +              *source_tree = parse_tree_indirect(rev);
        } else {
                parse_commit_or_die(new->commit);
                *source_tree = new->commit->tree;
diff --combined builtin/fast-export.c
index 969550531b9e190ca8169b61cf45c1751ef45fce,64617ad8e362fff47381d34c36a914d9458a7d1a..24e29ad7eab5edc0964a51f32602b062daabb907
@@@ -232,7 -232,7 +232,7 @@@ static void export_blob(const struct ob
  
        if (anonymize) {
                buf = anonymize_blob(&size);
 -              object = (struct object *)lookup_blob(oid->hash);
 +              object = (struct object *)lookup_blob(oid);
                eaten = 0;
        } else {
                buf = read_sha1_file(oid->hash, &type, &size);
                        die ("Could not read blob %s", oid_to_hex(oid));
                if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0)
                        die("sha1 mismatch in blob %s", oid_to_hex(oid));
 -              object = parse_object_buffer(oid->hash, type, size, buf, &eaten);
 +              object = parse_object_buffer(oid, type, size, buf, &eaten);
        }
  
        if (!object)
@@@ -734,6 -734,7 +734,7 @@@ static void handle_tag(const char *name
                             oid_to_hex(&tag->object.oid));
                case DROP:
                        /* Ignore this tag altogether */
+                       free(buf);
                        return;
                case REWRITE:
                        if (tagged->type != OBJ_COMMIT) {
               (int)(tagger_end - tagger), tagger,
               tagger == tagger_end ? "" : "\n",
               (int)message_size, (int)message_size, message ? message : "");
+       free(buf);
  }
  
  static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
  
                /* handle nested tags */
                while (tag && tag->object.type == OBJ_TAG) {
 -                      parse_object(tag->object.oid.hash);
 +                      parse_object(&tag->object.oid);
                        string_list_append(&extra_refs, full_name)->util = tag;
                        tag = (struct tag *)tag->tagged;
                }
@@@ -938,7 -940,7 +940,7 @@@ static void import_marks(char *input_fi
                        /* only commits */
                        continue;
  
 -              commit = lookup_commit(oid.hash);
 +              commit = lookup_commit(&oid);
                if (!commit)
                        die("not a commit? can't happen: %s", oid_to_hex(&oid));
  
diff --combined builtin/name-rev.c
index 1bf4cc4ebb633086ff8ab9a10fa79ba364e19ffd,e7a3fe7ee70759c34b9de12177078a55f27bfb9a..b7afffef73483ffda51dae87a8ae86f2677cef84
@@@ -10,7 -10,7 +10,7 @@@
  
  typedef struct rev_name {
        const char *tip_name;
 -      unsigned long taggerdate;
 +      timestamp_t taggerdate;
        int generation;
        int distance;
  } rev_name;
@@@ -21,13 -21,14 +21,14 @@@ static long cutoff = LONG_MAX
  #define MERGE_TRAVERSAL_WEIGHT 65535
  
  static void name_rev(struct commit *commit,
 -              const char *tip_name, unsigned long taggerdate,
 +              const char *tip_name, timestamp_t taggerdate,
                int generation, int distance,
                int deref)
  {
        struct rev_name *name = (struct rev_name *)commit->util;
        struct commit_list *parents;
        int parent_number = 1;
+       char *to_free = NULL;
  
        parse_commit(commit);
  
@@@ -35,7 -36,7 +36,7 @@@
                return;
  
        if (deref) {
-               tip_name = xstrfmt("%s^0", tip_name);
+               tip_name = to_free = xstrfmt("%s^0", tip_name);
  
                if (generation)
                        die("generation: %d, but deref?", generation);
@@@ -53,8 -54,10 +54,10 @@@ copy_data
                name->taggerdate = taggerdate;
                name->generation = generation;
                name->distance = distance;
-       } else
+       } else {
+               free(to_free);
                return;
+       }
  
        for (parents = commit->parents;
                        parents;
@@@ -114,7 -117,7 +117,7 @@@ struct name_ref_data 
  
  static struct tip_table {
        struct tip_table_entry {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                const char *refname;
        } *table;
        int nr;
        int sorted;
  } tip_table;
  
 -static void add_to_tip_table(const unsigned char *sha1, const char *refname,
 +static void add_to_tip_table(const struct object_id *oid, const char *refname,
                             int shorten_unambiguous)
  {
        refname = name_ref_abbrev(refname, shorten_unambiguous);
  
        ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
 -      hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
 +      oidcpy(&tip_table.table[tip_table.nr].oid, oid);
        tip_table.table[tip_table.nr].refname = xstrdup(refname);
        tip_table.nr++;
        tip_table.sorted = 0;
  static int tipcmp(const void *a_, const void *b_)
  {
        const struct tip_table_entry *a = a_, *b = b_;
 -      return hashcmp(a->sha1, b->sha1);
 +      return oidcmp(&a->oid, &b->oid);
  }
  
  static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
  {
 -      struct object *o = parse_object(oid->hash);
 +      struct object *o = parse_object(oid);
        struct name_ref_data *data = cb_data;
        int can_abbreviate_output = data->tags_only && data->name_only;
        int deref = 0;
 -      unsigned long taggerdate = ULONG_MAX;
 +      timestamp_t taggerdate = TIME_MAX;
  
        if (data->tags_only && !starts_with(path, "refs/tags/"))
                return 0;
                        return 0;
        }
  
 -      add_to_tip_table(oid->hash, path, can_abbreviate_output);
 +      add_to_tip_table(oid, path, can_abbreviate_output);
  
        while (o && o->type == OBJ_TAG) {
                struct tag *t = (struct tag *) o;
                if (!t->tagged)
                        break; /* broken repository */
 -              o = parse_object(t->tagged->oid.hash);
 +              o = parse_object(&t->tagged->oid);
                deref = 1;
                taggerdate = t->date;
        }
  static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
  {
        struct tip_table_entry *table = table_;
 -      return table[ix].sha1;
 +      return table[ix].oid.hash;
  }
  
  static const char *get_exact_ref_match(const struct object *o)
@@@ -301,9 -304,9 +304,9 @@@ static void name_rev_line(char *p, stru
  #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
                if (!ishex(*p))
                        forty = 0;
 -              else if (++forty == 40 &&
 +              else if (++forty == GIT_SHA1_HEXSZ &&
                         !ishex(*(p+1))) {
 -                      unsigned char sha1[40];
 +                      struct object_id oid;
                        const char *name = NULL;
                        char c = *(p+1);
                        int p_len = p - p_start + 1;
                        forty = 0;
  
                        *(p+1) = 0;
 -                      if (!get_sha1(p - 39, sha1)) {
 +                      if (!get_oid(p - (GIT_SHA1_HEXSZ - 1), &oid)) {
                                struct object *o =
 -                                      lookup_object(sha1);
 +                                      lookup_object(oid.hash);
                                if (o)
                                        name = get_rev_name(o, &buf);
                        }
                                continue;
  
                        if (data->name_only)
 -                              printf("%.*s%s", p_len - 40, p_start, name);
 +                              printf("%.*s%s", p_len - GIT_SHA1_HEXSZ, p_start, name);
                        else
                                printf("%.*s (%s)", p_len, p_start, name);
                        p_start = p + 1;
@@@ -374,18 -377,18 +377,18 @@@ int cmd_name_rev(int argc, const char *
                cutoff = 0;
  
        for (; argc; argc--, argv++) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                struct object *object;
                struct commit *commit;
  
 -              if (get_sha1(*argv, sha1)) {
 +              if (get_oid(*argv, &oid)) {
                        fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
                                        *argv);
                        continue;
                }
  
                commit = NULL;
 -              object = parse_object(sha1);
 +              object = parse_object(&oid);
                if (object) {
                        struct object *peeled = deref_tag(object, *argv, 0);
                        if (peeled && peeled->type == OBJ_COMMIT)
diff --combined builtin/receive-pack.c
index eb8b64bfced487d32ffd429e61b9d5848098d99a,48c07ddb65930ed6fd529aed9d90da5618da678b..b1706a5731c0e527a8abc1d42ac4f0e6d346c47c
@@@ -78,7 -78,7 +78,7 @@@ static const char *NONCE_OK = "OK"
  static const char *NONCE_SLOP = "SLOP";
  static const char *nonce_status;
  static long nonce_stamp_slop;
 -static unsigned long nonce_stamp_slop_limit;
 +static timestamp_t nonce_stamp_slop_limit;
  static struct ref_transaction *transaction;
  
  static enum {
@@@ -454,17 -454,17 +454,17 @@@ static void hmac_sha1(unsigned char *ou
        git_SHA1_Final(out, &ctx);
  }
  
 -static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
 +static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
  {
        struct strbuf buf = STRBUF_INIT;
        unsigned char sha1[20];
  
 -      strbuf_addf(&buf, "%s:%lu", path, stamp);
 +      strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
        hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
        strbuf_release(&buf);
  
        /* RFC 2104 5. HMAC-SHA1-80 */
 -      strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
 +      strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
        return strbuf_detach(&buf, NULL);
  }
  
   * after dropping "_commit" from its name and possibly moving it out
   * of commit.c
   */
 -static char *find_header(const char *msg, size_t len, const char *key)
 +static char *find_header(const char *msg, size_t len, const char *key,
 +                       const char **next_line)
  {
        int key_len = strlen(key);
        const char *line = msg;
                if (line + key_len < eol &&
                    !memcmp(line, key, key_len) && line[key_len] == ' ') {
                        int offset = key_len + 1;
 +                      if (next_line)
 +                              *next_line = *eol ? eol + 1 : eol;
                        return xmemdupz(line + offset, (eol - line) - offset);
                }
                line = *eol ? eol + 1 : NULL;
  
  static const char *check_nonce(const char *buf, size_t len)
  {
 -      char *nonce = find_header(buf, len, "nonce");
 -      unsigned long stamp, ostamp;
 +      char *nonce = find_header(buf, len, "nonce", NULL);
 +      timestamp_t stamp, ostamp;
        char *bohmac, *expect = NULL;
        const char *retval = NONCE_BAD;
  
                retval = NONCE_BAD;
                goto leave;
        }
 -      stamp = strtoul(nonce, &bohmac, 10);
 +      stamp = parse_timestamp(nonce, &bohmac, 10);
        if (bohmac == nonce || bohmac[0] != '-') {
                retval = NONCE_BAD;
                goto leave;
         * would mean it was issued by another server with its clock
         * skewed in the future.
         */
 -      ostamp = strtoul(push_cert_nonce, NULL, 10);
 +      ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
        nonce_stamp_slop = (long)ostamp - (long)stamp;
  
        if (nonce_stamp_slop_limit &&
@@@ -578,45 -575,6 +578,45 @@@ leave
        return retval;
  }
  
 +/*
 + * Return 1 if there is no push_cert or if the push options in push_cert are
 + * the same as those in the argument; 0 otherwise.
 + */
 +static int check_cert_push_options(const struct string_list *push_options)
 +{
 +      const char *buf = push_cert.buf;
 +      int len = push_cert.len;
 +
 +      char *option;
 +      const char *next_line;
 +      int options_seen = 0;
 +
 +      int retval = 1;
 +
 +      if (!len)
 +              return 1;
 +
 +      while ((option = find_header(buf, len, "push-option", &next_line))) {
 +              len -= (next_line - buf);
 +              buf = next_line;
 +              options_seen++;
 +              if (options_seen > push_options->nr
 +                  || strcmp(option,
 +                            push_options->items[options_seen - 1].string)) {
 +                      retval = 0;
 +                      goto leave;
 +              }
 +              free(option);
 +      }
 +
 +      if (options_seen != push_options->nr)
 +              retval = 0;
 +
 +leave:
 +      free(option);
 +      return retval;
 +}
 +
  static void prepare_push_cert_sha1(struct child_process *proc)
  {
        static int already_done;
@@@ -900,7 -858,7 +900,7 @@@ static int update_shallow_ref(struct co
         * not lose these new roots..
         */
        for (i = 0; i < extra.nr; i++)
 -              register_shallow(extra.oid[i].hash);
 +              register_shallow(&extra.oid[i]);
  
        si->shallow_ref[cmd->index] = 0;
        oid_array_clear(&extra);
@@@ -1028,7 -986,8 +1028,8 @@@ static const char *update(struct comman
  {
        const char *name = cmd->ref_name;
        struct strbuf namespaced_name_buf = STRBUF_INIT;
-       const char *namespaced_name, *ret;
+       static char *namespaced_name;
+       const char *ret;
        struct object_id *old_oid = &cmd->old_oid;
        struct object_id *new_oid = &cmd->new_oid;
  
        }
  
        strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+       free(namespaced_name);
        namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
  
        if (is_ref_checked_out(namespaced_name)) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
  
 -              old_object = parse_object(old_oid->hash);
 -              new_object = parse_object(new_oid->hash);
 +              old_object = parse_object(old_oid);
 +              new_object = parse_object(new_oid);
  
                if (!old_object || !new_object ||
                    old_object->type != OBJ_COMMIT ||
  
        if (is_null_oid(new_oid)) {
                struct strbuf err = STRBUF_INIT;
 -              if (!parse_object(old_oid->hash)) {
 +              if (!parse_object(old_oid)) {
                        old_oid = NULL;
                        if (ref_exists(name)) {
                                rp_warning("Allowing deletion of corrupt ref.");
@@@ -1971,11 -1931,6 +1973,11 @@@ int cmd_receive_pack(int argc, const ch
  
                if (use_push_options)
                        read_push_options(&push_options);
 +              if (!check_cert_push_options(&push_options)) {
 +                      struct command *cmd;
 +                      for (cmd = commands; cmd; cmd = cmd->next)
 +                              cmd->error_string = "inconsistent push options";
 +              }
  
                prepare_shallow_info(&si, &shallow);
                if (!si.nr_ours && !si.nr_theirs)
diff --combined builtin/worktree.c
index 11f90d6e45f44f96f637cc976163075181c78fac,ff5dfd2b1022fd94314f2cefe3ca91a4820e8abf..793306ea5162f1c3e9663c0c99d693b88baaea3d
@@@ -31,7 -31,7 +31,7 @@@ struct add_opts 
  
  static int show_only;
  static int verbose;
 -static unsigned long expire;
 +static timestamp_t expire;
  
  static int prune_worktree(const char *id, struct strbuf *reason)
  {
@@@ -131,7 -131,7 +131,7 @@@ static int prune(int ac, const char **a
                OPT_END()
        };
  
 -      expire = ULONG_MAX;
 +      expire = TIME_MAX;
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
        if (ac)
                usage_with_options(worktree_usage, options);
@@@ -414,9 -414,11 +414,11 @@@ static void show_worktree(struct worktr
                                find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
                if (wt->is_detached)
                        strbuf_addstr(&sb, "(detached HEAD)");
-               else if (wt->head_ref)
-                       strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0));
-               else
+               else if (wt->head_ref) {
+                       char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
+                       strbuf_addf(&sb, "[%s]", ref);
+                       free(ref);
+               } else
                        strbuf_addstr(&sb, "(error)");
        }
        printf("%s\n", sb.buf);
diff --combined config.c
index bb4d735701928d7f648c33ab371c04e3b7af12ad,a30056ec7e9a0cca87dea494a92a4671a19b7643..deafc17b42d5b6acf8e573f6e463b8ede1cff114
+++ b/config.c
@@@ -1965,7 -1965,7 +1965,7 @@@ int git_config_get_expiry(const char *k
        if (ret)
                return ret;
        if (strcmp(*output, "now")) {
 -              unsigned long now = approxidate("now");
 +              timestamp_t now = approxidate("now");
                if (approxidate(*output) >= now)
                        git_die_config(key, _("Invalid %s: '%s'"), key, *output);
        }
@@@ -2621,7 -2621,7 +2621,7 @@@ int git_config_rename_section_in_file(c
        struct lock_file *lock;
        int out_fd;
        char buf[1024];
-       FILE *config_file;
+       FILE *config_file = NULL;
        struct stat st;
  
        if (new_name && !section_name_is_ok(new_name)) {
                }
        }
        fclose(config_file);
+       config_file = NULL;
  commit_and_out:
        if (commit_lock_file(lock) < 0)
                ret = error_errno("could not write config file %s",
                                  config_filename);
  out:
+       if (config_file)
+               fclose(config_file);
        rollback_lock_file(lock);
  out_no_rollback:
        free(filename_buf);
diff --combined reflog-walk.c
index 70905011eec15c6d8f99e889a2dcc8bbd74ab240,c63eb1a3fd7fa1d6bdca8bacc1781bbf65f41593..ed99437ad2068a75d14e739354e1bc17932fd839
@@@ -12,7 -12,7 +12,7 @@@ struct complete_reflogs 
        struct reflog_info {
                struct object_id ooid, noid;
                char *email;
 -              unsigned long timestamp;
 +              timestamp_t timestamp;
                int tz;
                char *message;
        } *items;
@@@ -20,7 -20,7 +20,7 @@@
  };
  
  static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
 -              const char *email, unsigned long timestamp, int tz,
 +              const char *email, timestamp_t timestamp, int tz,
                const char *message, void *cb_data)
  {
        struct complete_reflogs *array = cb_data;
@@@ -69,7 -69,7 +69,7 @@@ static struct complete_reflogs *read_co
  }
  
  static int get_reflog_recno_by_time(struct complete_reflogs *array,
 -      unsigned long timestamp)
 +      timestamp_t timestamp)
  {
        int i;
        for (i = array->nr - 1; i >= 0; i--)
@@@ -141,7 -141,7 +141,7 @@@ void init_reflog_walk(struct reflog_wal
  int add_reflog_for_walk(struct reflog_walk_info *info,
                struct commit *commit, const char *name)
  {
 -      unsigned long timestamp = 0;
 +      timestamp_t timestamp = 0;
        int recno = -1;
        struct string_list_item *item;
        struct complete_reflogs *reflogs;
                if (!reflogs || reflogs->nr == 0) {
                        struct object_id oid;
                        char *b;
-                       if (dwim_log(branch, strlen(branch), oid.hash, &b) == 1) {
+                       int ret = dwim_log(branch, strlen(branch),
+                                          oid.hash, &b);
+                       if (ret > 1)
+                               free(b);
+                       else if (ret == 1) {
                                if (reflogs) {
                                        free(reflogs->ref);
                                        free(reflogs);
                                reflogs = read_complete_reflog(branch);
                        }
                }
-               if (!reflogs || reflogs->nr == 0)
+               if (!reflogs || reflogs->nr == 0) {
+                       if (reflogs) {
+                               free(reflogs->ref);
+                               free(reflogs);
+                       }
+                       free(branch);
                        return -1;
+               }
                string_list_insert(&info->complete_reflogs, branch)->util
                        = reflogs;
        }
+       free(branch);
  
        commit_reflog = xcalloc(1, sizeof(struct commit_reflog));
        if (recno < 0) {
                commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
                if (commit_reflog->recno < 0) {
-                       free(branch);
+                       if (reflogs) {
+                               free(reflogs->ref);
+                               free(reflogs);
+                       }
                        free(commit_reflog);
                        return -1;
                }
@@@ -238,13 -252,13 +252,13 @@@ void fake_reflog_parent(struct reflog_w
        do {
                reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
                commit_reflog->recno--;
 -              logobj = parse_object(reflog->ooid.hash);
 +              logobj = parse_object(&reflog->ooid);
        } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
  
 -      if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->ooid.hash)) {
 +      if (!logobj && commit_reflog->recno >= 0 && is_null_oid(&reflog->ooid)) {
                /* a root commit, but there are still more entries to show */
                reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
 -              logobj = parse_object(reflog->noid.hash);
 +              logobj = parse_object(&reflog->noid);
        }
  
        if (!logobj || logobj->type != OBJ_COMMIT) {
diff --combined remote.c
index 38ca1353b9db8e25f4b105c3027f541c4c230426,72b4591b98363a83693db82d670a16f05c1e2192..fdc52d802cec6b5322689f1fda79c9cabe9dbb01
+++ b/remote.c
@@@ -1191,9 -1191,10 +1191,10 @@@ static int match_explicit(struct ref *s
                else if (is_null_oid(&matched_src->new_oid))
                        error("unable to delete '%s': remote ref does not exist",
                              dst_value);
-               else if ((dst_guess = guess_ref(dst_value, matched_src)))
+               else if ((dst_guess = guess_ref(dst_value, matched_src))) {
                        matched_dst = make_linked_ref(dst_guess, dst_tail);
-               else
+                       free(dst_guess);
+               } else
                        error("unable to push to unqualified destination: %s\n"
                              "The destination refspec neither matches an "
                              "existing ref on the remote nor\n"
@@@ -1296,7 -1297,7 +1297,7 @@@ static void add_to_tips(struct tips *ti
  
        if (is_null_oid(oid))
                return;
 -      commit = lookup_commit_reference_gently(oid->hash, 1);
 +      commit = lookup_commit_reference_gently(oid, 1);
        if (!commit || (commit->object.flags & TMP_MARK))
                return;
        commit->object.flags |= TMP_MARK;
@@@ -1358,8 -1359,7 +1359,8 @@@ static void add_missing_tags(struct re
  
                        if (is_null_oid(&ref->new_oid))
                                continue;
 -                      commit = lookup_commit_reference_gently(ref->new_oid.hash, 1);
 +                      commit = lookup_commit_reference_gently(&ref->new_oid,
 +                                                              1);
                        if (!commit)
                                /* not pushing a commit, which is not an error */
                                continue;
@@@ -1586,8 -1586,8 +1587,8 @@@ void set_ref_status_for_push(struct re
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
                        else if (!has_object_file(&ref->old_oid))
                                reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
 -                      else if (!lookup_commit_reference_gently(ref->old_oid.hash, 1) ||
 -                               !lookup_commit_reference_gently(ref->new_oid.hash, 1))
 +                      else if (!lookup_commit_reference_gently(&ref->old_oid, 1) ||
 +                               !lookup_commit_reference_gently(&ref->new_oid, 1))
                                reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
                        else if (!ref_newer(&ref->new_oid, &ref->old_oid))
                                reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
@@@ -1954,12 -1954,12 +1955,12 @@@ int ref_newer(const struct object_id *n
         * Both new and old must be commit-ish and new is descendant of
         * old.  Otherwise we require --force.
         */
 -      o = deref_tag(parse_object(old_oid->hash), NULL, 0);
 +      o = deref_tag(parse_object(old_oid), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
        old = (struct commit *) o;
  
 -      o = deref_tag(parse_object(new_oid->hash), NULL, 0);
 +      o = deref_tag(parse_object(new_oid), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
        new = (struct commit *) o;
@@@ -2010,13 -2010,13 +2011,13 @@@ int stat_tracking_info(struct branch *b
        /* Cannot stat if what we used to build on no longer exists */
        if (read_ref(base, oid.hash))
                return -1;
 -      theirs = lookup_commit_reference(oid.hash);
 +      theirs = lookup_commit_reference(&oid);
        if (!theirs)
                return -1;
  
        if (read_ref(branch->refname, oid.hash))
                return -1;
 -      ours = lookup_commit_reference(oid.hash);
 +      ours = lookup_commit_reference(&oid);
        if (!ours)
                return -1;
  
diff --combined shallow.c
index 6950cc24f02bd03480321eeaa0a36a32a460ed99,f9370961f99f5e68c1bfe84d215513536fccbdaf..ef7ca78993df20082c858a7d2b8c7094fcbd1590
+++ b/shallow.c
@@@ -27,13 -27,13 +27,13 @@@ void set_alternate_shallow_file(const c
        alternate_shallow_file = xstrdup_or_null(path);
  }
  
 -int register_shallow(const unsigned char *sha1)
 +int register_shallow(const struct object_id *oid)
  {
        struct commit_graft *graft =
                xmalloc(sizeof(struct commit_graft));
 -      struct commit *commit = lookup_commit(sha1);
 +      struct commit *commit = lookup_commit(oid);
  
 -      hashcpy(graft->oid.hash, sha1);
 +      oidcpy(&graft->oid, oid);
        graft->nr_parent = -1;
        if (commit && commit->object.parsed)
                commit->parents = NULL;
@@@ -65,10 -65,10 +65,10 @@@ int is_repository_shallow(void
        is_shallow = 1;
  
        while (fgets(buf, sizeof(buf), fp)) {
 -              unsigned char sha1[20];
 -              if (get_sha1_hex(buf, sha1))
 +              struct object_id oid;
 +              if (get_oid_hex(buf, &oid))
                        die("bad shallow line: %s", buf);
 -              register_shallow(sha1);
 +              register_shallow(&oid);
        }
        fclose(fp);
        return is_shallow;
@@@ -241,7 -241,7 +241,7 @@@ static int write_one_shallow(const stru
        if (graft->nr_parent != -1)
                return 0;
        if (data->flags & SEEN_ONLY) {
 -              struct commit *c = lookup_commit(graft->oid.hash);
 +              struct commit *c = lookup_commit(&graft->oid);
                if (!c || !(c->object.flags & SEEN)) {
                        if (data->flags & VERBOSE)
                                printf("Removing %s from .git/shallow\n",
@@@ -466,18 -466,22 +466,22 @@@ static uint32_t *paint_alloc(struct pai
   * UNINTERESTING or BOTTOM is hit. Set the id-th bit in ref_bitmap for
   * all walked commits.
   */
 -static void paint_down(struct paint_info *info, const unsigned char *sha1,
 +static void paint_down(struct paint_info *info, const struct object_id *oid,
                       unsigned int id)
  {
        unsigned int i, nr;
        struct commit_list *head = NULL;
        int bitmap_nr = (info->nr_bits + 31) / 32;
        size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr);
-       uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */
-       uint32_t *bitmap = paint_alloc(info);
 -      struct commit *c = lookup_commit_reference_gently(sha1, 1);
 +      struct commit *c = lookup_commit_reference_gently(oid, 1);
+       uint32_t *tmp; /* to be freed before return */
+       uint32_t *bitmap;
        if (!c)
                return;
+       tmp = xmalloc(bitmap_size);
+       bitmap = paint_alloc(info);
        memset(bitmap, 0, bitmap_size);
        bitmap[id / 32] |= (1U << (id % 32));
        commit_list_insert(c, &head);
  static int mark_uninteresting(const char *refname, const struct object_id *oid,
                              int flags, void *cb_data)
  {
 -      struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
 +      struct commit *commit = lookup_commit_reference_gently(oid, 1);
        if (!commit)
                return 0;
        commit->object.flags |= UNINTERESTING;
@@@ -599,18 -603,18 +603,18 @@@ void assign_shallow_commits_to_refs(str
  
        /* Mark potential bottoms so we won't go out of bound */
        for (i = 0; i < nr_shallow; i++) {
 -              struct commit *c = lookup_commit(oid[shallow[i]].hash);
 +              struct commit *c = lookup_commit(&oid[shallow[i]]);
                c->object.flags |= BOTTOM;
        }
  
        for (i = 0; i < ref->nr; i++)
 -              paint_down(&pi, ref->oid[i].hash, i);
 +              paint_down(&pi, ref->oid + i, i);
  
        if (used) {
                int bitmap_size = ((pi.nr_bits + 31) / 32) * sizeof(uint32_t);
                memset(used, 0, sizeof(*used) * info->shallow->nr);
                for (i = 0; i < nr_shallow; i++) {
 -                      const struct commit *c = lookup_commit(oid[shallow[i]].hash);
 +                      const struct commit *c = lookup_commit(&oid[shallow[i]]);
                        uint32_t **map = ref_bitmap_at(&pi.ref_bitmap, c);
                        if (*map)
                                used[shallow[i]] = xmemdupz(*map, bitmap_size);
@@@ -641,7 -645,7 +645,7 @@@ static int add_ref(const char *refname
  {
        struct commit_array *ca = cb_data;
        ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
 -      ca->commits[ca->nr] = lookup_commit_reference_gently(oid->hash, 1);
 +      ca->commits[ca->nr] = lookup_commit_reference_gently(oid, 1);
        if (ca->commits[ca->nr])
                ca->nr++;
        return 0;
@@@ -679,7 -683,7 +683,7 @@@ static void post_assign_shallow(struct 
        for (i = dst = 0; i < info->nr_theirs; i++) {
                if (i != dst)
                        info->theirs[dst] = info->theirs[i];
 -              c = lookup_commit(oid[info->theirs[i]].hash);
 +              c = lookup_commit(&oid[info->theirs[i]]);
                bitmap = ref_bitmap_at(ref_bitmap, c);
                if (!*bitmap)
                        continue;
        for (i = dst = 0; i < info->nr_ours; i++) {
                if (i != dst)
                        info->ours[dst] = info->ours[i];
 -              c = lookup_commit(oid[info->ours[i]].hash);
 +              c = lookup_commit(&oid[info->ours[i]]);
                bitmap = ref_bitmap_at(ref_bitmap, c);
                if (!*bitmap)
                        continue;
  int delayed_reachability_test(struct shallow_info *si, int c)
  {
        if (si->need_reachability_test[c]) {
 -              struct commit *commit = lookup_commit(si->shallow->oid[c].hash);
 +              struct commit *commit = lookup_commit(&si->shallow->oid[c]);
  
                if (!si->commits) {
                        struct commit_array ca;
diff --combined worktree.c
index 726f732e5ecf92824c06b9f7a7dda82dfadf79f5,89a81b13de38473aaa52870d80f59c247578a3a5..2801c6d52bdaa1fb83a0069c8d2217ea129fd6bf
@@@ -19,25 -19,54 +19,25 @@@ void free_worktrees(struct worktree **w
        free (worktrees);
  }
  
 -/*
 - * read 'path_to_ref' into 'ref'.  Also if is_detached is not NULL,
 - * set is_detached to 1 (0) if the ref is detached (is not detached).
 - *
 - * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
 - * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
 - * git_path). Parse the ref ourselves.
 - *
 - * return -1 if the ref is not a proper ref, 0 otherwise (success)
 - */
 -static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
 -{
 -      if (is_detached)
 -              *is_detached = 0;
 -      if (!strbuf_readlink(ref, path_to_ref, 0)) {
 -              /* HEAD is symbolic link */
 -              if (!starts_with(ref->buf, "refs/") ||
 -                              check_refname_format(ref->buf, 0))
 -                      return -1;
 -      } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
 -              /* textual symref or detached */
 -              if (!starts_with(ref->buf, "ref:")) {
 -                      if (is_detached)
 -                              *is_detached = 1;
 -              } else {
 -                      strbuf_remove(ref, 0, strlen("ref:"));
 -                      strbuf_trim(ref);
 -                      if (check_refname_format(ref->buf, 0))
 -                              return -1;
 -              }
 -      } else
 -              return -1;
 -      return 0;
 -}
 -
  /**
 - * Add the head_sha1 and head_ref (if not detached) to the given worktree
 + * Update head_sha1, head_ref and is_detached of the given worktree
   */
 -static void add_head_info(struct strbuf *head_ref, struct worktree *worktree)
 +static void add_head_info(struct worktree *wt)
  {
 -      if (head_ref->len) {
 -              if (worktree->is_detached) {
 -                      get_sha1_hex(head_ref->buf, worktree->head_sha1);
 -              } else {
 -                      resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);
 -                      worktree->head_ref = strbuf_detach(head_ref, NULL);
 -              }
 -      }
 +      int flags;
 +      const char *target;
 +
 +      target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
 +                                       "HEAD",
 +                                       RESOLVE_REF_READING,
 +                                       wt->head_sha1, &flags);
 +      if (!target)
 +              return;
 +
 +      if (flags & REF_ISSYMREF)
 +              wt->head_ref = xstrdup(target);
 +      else
 +              wt->is_detached = 1;
  }
  
  /**
@@@ -48,7 -77,9 +48,7 @@@ static struct worktree *get_main_worktr
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
 -      struct strbuf head_ref = STRBUF_INIT;
        int is_bare = 0;
 -      int is_detached = 0;
  
        strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
        is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
        worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
        worktree->is_bare = is_bare;
 -      worktree->is_detached = is_detached;
 -      if (!parse_ref(path.buf, &head_ref, &is_detached))
 -              add_head_info(&head_ref, worktree);
 +      add_head_info(worktree);
  
        strbuf_release(&path);
        strbuf_release(&worktree_path);
 -      strbuf_release(&head_ref);
        return worktree;
  }
  
@@@ -72,6 -106,8 +72,6 @@@ static struct worktree *get_linked_work
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
 -      struct strbuf head_ref = STRBUF_INIT;
 -      int is_detached = 0;
  
        if (!id)
                die("Missing linked worktree name");
        strbuf_reset(&path);
        strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
  
 -      if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
 -              goto done;
 -
        worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
        worktree->id = xstrdup(id);
 -      worktree->is_detached = is_detached;
 -      add_head_info(&head_ref, worktree);
 +      add_head_info(worktree);
  
  done:
        strbuf_release(&path);
        strbuf_release(&worktree_path);
 -      strbuf_release(&head_ref);
        return worktree;
  }
  
@@@ -296,6 -337,8 +296,6 @@@ const struct worktree *find_shared_symr
                                          const char *target)
  {
        const struct worktree *existing = NULL;
 -      struct strbuf path = STRBUF_INIT;
 -      struct strbuf sb = STRBUF_INIT;
        static struct worktree **worktrees;
        int i = 0;
  
  
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
 +              const char *symref_target;
 +              unsigned char sha1[20];
 +              struct ref_store *refs;
 +              int flags;
 +
                if (wt->is_bare)
                        continue;
  
                        }
                }
  
 -              strbuf_reset(&path);
 -              strbuf_reset(&sb);
 -              strbuf_addf(&path, "%s/%s",
 -                          get_worktree_git_dir(wt),
 -                          symref);
 -
 -              if (parse_ref(path.buf, &sb, NULL)) {
 -                      continue;
 -              }
 -
 -              if (!strcmp(sb.buf, target)) {
 +              refs = get_worktree_ref_store(wt);
 +              symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
 +                                                      sha1, &flags);
 +              if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
                        existing = wt;
                        break;
                }
        }
  
 -      strbuf_release(&path);
 -      strbuf_release(&sb);
 -
        return existing;
  }
  
@@@ -351,6 -399,7 +351,7 @@@ int submodule_uses_worktrees(const cha
  
        /* The env would be set for the superproject. */
        get_common_dir_noenv(&sb, submodule_gitdir);
+       free(submodule_gitdir);
  
        /*
         * The check below is only known to be good for repository format
        /* See if there is any file inside the worktrees directory. */
        dir = opendir(sb.buf);
        strbuf_release(&sb);
-       free(submodule_gitdir);
  
        if (!dir)
                return 0;
diff --combined wt-status.c
index 5db53775abbcafd8ebc1703a5d968b6d604848b1,117ac8cfb019d7d3b2c2bea056a30fe7ee5dcf4e..c43d17355c60677720173e495f0dc1a808ce2c6c
@@@ -665,7 -665,7 +665,7 @@@ static void wt_status_collect_untracked
                dir.untracked = the_index.untracked;
        setup_standard_excludes(&dir);
  
 -      fill_directory(&dir, &s->pathspec);
 +      fill_directory(&dir, &the_index, &s->pathspec);
  
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
@@@ -1002,7 -1002,7 +1002,7 @@@ static void wt_longstatus_print_trackin
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
                                 comment_line_char);
        else
 -              fputs("", s->fp);
 +              fputs("\n", s->fp);
  }
  
  static int has_unmerged(struct wt_status *s)
@@@ -1082,29 -1082,29 +1082,29 @@@ static char *read_line_from_git_path(co
  static int split_commit_in_progress(struct wt_status *s)
  {
        int split_in_progress = 0;
-       char *head = read_line_from_git_path("HEAD");
-       char *orig_head = read_line_from_git_path("ORIG_HEAD");
-       char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
-       char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
+       char *head, *orig_head, *rebase_amend, *rebase_orig_head;
  
-       if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
+       if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
            !s->branch || strcmp(s->branch, "HEAD"))
-               return split_in_progress;
+               return 0;
  
-       if (!strcmp(rebase_amend, rebase_orig_head)) {
-               if (strcmp(head, rebase_amend))
-                       split_in_progress = 1;
-       } else if (strcmp(orig_head, rebase_orig_head)) {
-               split_in_progress = 1;
-       }
+       head = read_line_from_git_path("HEAD");
+       orig_head = read_line_from_git_path("ORIG_HEAD");
+       rebase_amend = read_line_from_git_path("rebase-merge/amend");
+       rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
  
-       if (!s->amend && !s->nowarn && !s->workdir_dirty)
-               split_in_progress = 0;
+       if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
+               ; /* fall through, no split in progress */
+       else if (!strcmp(rebase_amend, rebase_orig_head))
+               split_in_progress = !!strcmp(head, rebase_amend);
+       else if (strcmp(orig_head, rebase_orig_head))
+               split_in_progress = 1;
  
        free(head);
        free(orig_head);
        free(rebase_amend);
        free(rebase_orig_head);
        return split_in_progress;
  }
  
@@@ -1168,6 -1168,7 +1168,7 @@@ static int read_rebase_todolist(const c
                abbrev_sha1_in_line(&line);
                string_list_append(lines, line.buf);
        }
+       fclose(f);
        return 0;
  }
  
@@@ -1387,7 -1388,7 +1388,7 @@@ struct grab_1st_switch_cbdata 
  };
  
  static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
 -                         const char *email, unsigned long timestamp, int tz,
 +                         const char *email, timestamp_t timestamp, int tz,
                           const char *message, void *cb_data)
  {
        struct grab_1st_switch_cbdata *cb = cb_data;
@@@ -1428,7 -1429,7 +1429,7 @@@ static void wt_status_get_detached_from
            /* sha1 is a commit? match without further lookup */
            (!oidcmp(&cb.noid, &oid) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
 -           ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL &&
 +           ((commit = lookup_commit_reference_gently(&oid, 1)) != NULL &&
              !oidcmp(&cb.noid, &commit->object.oid)))) {
                const char *from = ref;
                if (!skip_prefix(from, "refs/tags/", &from))