Merge branch 'ma/clear-repository-format'
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Mar 2019 06:16:07 +0000 (15:16 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Mar 2019 06:16:07 +0000 (15:16 +0900)
The setup code has been cleaned up to avoid leaks around the
repository_format structure.

* ma/clear-repository-format:
setup: fix memory leaks with `struct repository_format`
setup: free old value before setting `work_tree`

1  2 
builtin/init-db.c
cache.h
repository.c
setup.c
diff --combined builtin/init-db.c
index 93eff7618cf1b98e3c69a7f1f159063e9e73dbe3,04c60eaad559a67c739121eb312bcbae8ac082aa..6090217025a27281b0723537e570357bf9c86629
@@@ -96,7 -96,7 +96,7 @@@ static void copy_templates(const char *
        struct strbuf path = STRBUF_INIT;
        struct strbuf template_path = STRBUF_INIT;
        size_t template_len;
-       struct repository_format template_format;
+       struct repository_format template_format = REPOSITORY_FORMAT_INIT;
        struct strbuf err = STRBUF_INIT;
        DIR *dir;
        char *to_free = NULL;
@@@ -148,6 -148,7 +148,7 @@@ free_return
        free(to_free);
        strbuf_release(&path);
        strbuf_release(&template_path);
+       clear_repository_format(&template_format);
  }
  
  static int git_init_db_config(const char *k, const char *v, void *cb)
@@@ -542,8 -543,8 +543,8 @@@ int cmd_init_db(int argc, const char **
         * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
         * without --bare.  Catch the error early.
         */
 -      git_dir = getenv(GIT_DIR_ENVIRONMENT);
 -      work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
 +      git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT));
 +      work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT));
        if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
                die(_("%s (or --work-tree=<directory>) not allowed without "
                          "specifying %s (or --git-dir=<directory>)"),
        }
  
        UNLEAK(real_git_dir);
 +      UNLEAK(git_dir);
 +      UNLEAK(work_tree);
  
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, flags);
diff --combined cache.h
index abd518a9a23394fd47b572c6957c712abac139a2,8c32c904c3208323f7a8839195b2d03693de4454..ac92421f3a8bc814da3f01731f2d8d8a52dc8364
+++ b/cache.h
@@@ -9,7 -9,6 +9,7 @@@
  #include "gettext.h"
  #include "convert.h"
  #include "trace.h"
 +#include "trace2.h"
  #include "string-list.h"
  #include "pack-revindex.h"
  #include "hash.h"
@@@ -46,20 -45,10 +46,20 @@@ unsigned long git_deflate_bound(git_zst
  /* The length in bytes and in hex digits of an object name (SHA-1 value). */
  #define GIT_SHA1_RAWSZ 20
  #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 +/* The block size of SHA-1. */
 +#define GIT_SHA1_BLKSZ 64
 +
 +/* The length in bytes and in hex digits of an object name (SHA-256 value). */
 +#define GIT_SHA256_RAWSZ 32
 +#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
 +/* The block size of SHA-256. */
 +#define GIT_SHA256_BLKSZ 64
  
  /* The length in byte and in hex digits of the largest possible hash value. */
 -#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
 -#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
 +#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
 +#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
 +/* The largest possible block size for any supported hash. */
 +#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
  
  struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@@ -349,6 -338,8 +349,6 @@@ struct index_state 
        struct mem_pool *ce_mem_pool;
  };
  
 -extern struct index_state the_index;
 -
  /* Name hashing */
  extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
  extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
@@@ -410,20 -401,18 +410,20 @@@ struct cache_entry *dup_cache_entry(con
   */
  void validate_cache_entries(const struct index_state *istate);
  
 -#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
 +#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
 +extern struct index_state the_index;
 +
  #define active_cache (the_index.cache)
  #define active_nr (the_index.cache_nr)
  #define active_alloc (the_index.cache_alloc)
  #define active_cache_changed (the_index.cache_changed)
  #define active_cache_tree (the_index.cache_tree)
  
 -#define read_cache() read_index(&the_index)
 +#define read_cache() repo_read_index(the_repository)
  #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
 -#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0)
 +#define read_cache_preload(pathspec) repo_read_index_preload(the_repository, (pathspec), 0)
  #define is_cache_unborn() is_index_unborn(&the_index)
 -#define read_cache_unmerged() read_index_unmerged(&the_index)
 +#define read_cache_unmerged() repo_read_index_unmerged(the_repository)
  #define discard_cache() discard_index(&the_index)
  #define unmerged_cache() unmerged_index(&the_index)
  #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
  #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
  #define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
  #define read_blob_data_from_cache(path, sz) read_blob_data_from_index(&the_index, (path), (sz))
 +#define hold_locked_index(lock_file, flags) repo_hold_locked_index(the_repository, (lock_file), (flags))
  #endif
  
  #define TYPE_BITS 3
@@@ -672,14 -660,19 +672,14 @@@ extern int daemonize(void)
  
  /* Initialize and use the cache information */
  struct lock_file;
 -extern int read_index(struct index_state *);
  extern void preload_index(struct index_state *index,
                          const struct pathspec *pathspec,
                          unsigned int refresh_flags);
 -extern int read_index_preload(struct index_state *,
 -                            const struct pathspec *pathspec,
 -                            unsigned int refresh_flags);
  extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
  extern int read_index_from(struct index_state *, const char *path,
                           const char *gitdir);
  extern int is_index_unborn(struct index_state *);
 -extern int read_index_unmerged(struct index_state *);
  
  /* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
@@@ -717,9 -710,9 +717,9 @@@ extern int unmerged_index(const struct 
   * provided, the space-separated list of files that differ will be appended
   * to it.
   */
 -extern int index_has_changes(struct index_state *istate,
 -                           struct tree *tree,
 -                           struct strbuf *sb);
 +extern int repo_index_has_changes(struct repository *repo,
 +                                struct tree *tree,
 +                                struct strbuf *sb);
  
  extern int verify_path(const char *path, unsigned mode);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
@@@ -752,14 -745,13 +752,14 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  #define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
 +#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  
  /* Remove entry, return true if there are more entries to go. */
  extern int remove_index_entry_at(struct index_state *, int pos);
  
 -extern void remove_marked_cache_entries(struct index_state *istate);
 +extern void remove_marked_cache_entries(struct index_state *istate, int invalidate);
  extern int remove_file_from_index(struct index_state *, const char *path);
  #define ADD_CACHE_VERBOSE 1
  #define ADD_CACHE_PRETEND 2
@@@ -835,6 -827,13 +835,6 @@@ extern void fill_stat_cache_info(struc
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
  
 -/*
 - * Opportunistically update the index but do not complain if we can't.
 - * The lockfile is always committed or rolled back.
 - */
 -extern void update_index_if_able(struct index_state *, struct lock_file *);
 -
 -extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
@@@ -962,6 -961,10 +962,10 @@@ extern char *repository_format_partial_
  extern const char *core_partial_clone_filter_default;
  extern int repository_format_worktree_config;
  
+ /*
+  * You _have_ to initialize a `struct repository_format` using
+  * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
+  */
  struct repository_format {
        int version;
        int precious_objects;
        struct string_list unknown_extensions;
  };
  
+ /*
+  * Always use this to initialize a `struct repository_format`
+  * to a well-defined, default state before calling
+  * `read_repository()`.
+  */
+ #define REPOSITORY_FORMAT_INIT \
+ { \
+       .version = -1, \
+       .is_bare = -1, \
+       .hash_algo = GIT_HASH_SHA1, \
+       .unknown_extensions = STRING_LIST_INIT_DUP, \
+ }
  /*
   * Read the repository format characteristics from the config file "path" into
-  * "format" struct. Returns the numeric version. On error, -1 is returned,
-  * format->version is set to -1, and all other fields in the struct are
-  * undefined.
+  * "format" struct. Returns the numeric version. On error, or if no version is
+  * found in the configuration, -1 is returned, format->version is set to -1,
+  * and all other fields in the struct are set to the default configuration
+  * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
+  * REPOSITORY_FORMAT_INIT before calling this function.
   */
  int read_repository_format(struct repository_format *format, const char *path);
  
+ /*
+  * Free the memory held onto by `format`, but not the struct itself.
+  * (No need to use this after `read_repository_format()` fails.)
+  */
+ void clear_repository_format(struct repository_format *format);
  /*
   * Verify that the repository described by repository_format is something we
   * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
@@@ -1029,12 -1053,16 +1054,12 @@@ extern const struct object_id null_oid
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
        /*
 -       * This is a temporary optimization hack. By asserting the size here,
 -       * we let the compiler know that it's always going to be 20, which lets
 -       * it turn this fixed-size memcmp into a few inline instructions.
 -       *
 -       * This will need to be extended or ripped out when we learn about
 -       * hashes of different sizes.
 +       * Teach the compiler that there are only two possibilities of hash size
 +       * here, so that it can optimize for this case as much as possible.
         */
 -      if (the_hash_algo->rawsz != 20)
 -              BUG("hash size not yet supported by hashcmp");
 -      return memcmp(sha1, sha2, the_hash_algo->rawsz);
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
  
  static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      return !hashcmp(sha1, sha2);
 +      /*
 +       * We write this here instead of deferring to hashcmp so that the
 +       * compiler can properly inline it and avoid calling memcmp.
 +       */
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
@@@ -1075,7 -1097,7 +1100,7 @@@ static inline void hashcpy(unsigned cha
  
  static inline void oidcpy(struct object_id *dst, const struct object_id *src)
  {
 -      hashcpy(dst->hash, src->hash);
 +      memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
  }
  
  static inline struct object_id *oiddup(const struct object_id *src)
@@@ -1272,8 -1294,8 +1297,8 @@@ extern char *xdg_cache_home(const char 
  
  extern int git_open_cloexec(const char *name, int flags);
  #define git_open(name) git_open_cloexec(name, O_RDONLY)
 -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 unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 +extern int parse_loose_header(const char *hdr, unsigned long *sizep);
  
  extern int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
  
@@@ -1335,24 -1357,6 +1360,24 @@@ struct object_context 
        GET_OID_TREE | GET_OID_TREEISH | \
        GET_OID_BLOB)
  
 +enum get_oid_result {
 +      FOUND = 0,
 +      MISSING_OBJECT = -1, /* The requested object is missing */
 +      SHORT_NAME_AMBIGUOUS = -2,
 +      /* The following only apply when symlinks are followed */
 +      DANGLING_SYMLINK = -4, /*
 +                              * The initial symlink is there, but
 +                              * (transitively) points to a missing
 +                              * in-tree file
 +                              */
 +      SYMLINK_LOOP = -5,
 +      NOT_DIR = -6, /*
 +                     * Somewhere along the symlink chain, a path is
 +                     * requested which contains a file as a
 +                     * non-final element.
 +                     */
 +};
 +
  extern int get_oid(const char *str, struct object_id *oid);
  extern int get_oid_commit(const char *str, struct object_id *oid);
  extern int get_oid_committish(const char *str, struct object_id *oid);
@@@ -1360,9 -1364,8 +1385,9 @@@ extern int get_oid_tree(const char *str
  extern int get_oid_treeish(const char *str, struct object_id *oid);
  extern int get_oid_blob(const char *str, struct object_id *oid);
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 -extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
 -
 +extern enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
 +                              unsigned flags, struct object_id *oid,
 +                              struct object_context *oc);
  
  typedef int each_abbrev_fn(const struct object_id *oid, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
@@@ -1387,9 -1390,9 +1412,9 @@@ extern int get_oid_hex(const char *hex
  extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  
  /*
 - * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
 + * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
 - * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
 + * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
   * convenience.
   *
   * The non-`_r` variant returns a static buffer, but uses a ring of 4
   *
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
 -extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 -extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 -extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
 -extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
 +char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
 +char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +char *oid_to_hex_r(char *out, const struct object_id *oid);
 +char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);     /* static buffer result! */
 +char *sha1_to_hex(const unsigned char *sha1);                                         /* same static buffer */
 +char *hash_to_hex(const unsigned char *hash);                                         /* same static buffer */
 +char *oid_to_hex(const struct object_id *oid);                                                /* same static buffer */
  
  /*
   * Parse a 40-character hexadecimal object ID starting from hex, updating the
@@@ -1464,7 -1464,6 +1489,7 @@@ extern struct object *peel_to_type(cons
  
  enum date_mode_type {
        DATE_NORMAL = 0,
 +      DATE_HUMAN,
        DATE_RELATIVE,
        DATE_SHORT,
        DATE_ISO8601,
@@@ -1490,9 -1489,7 +1515,9 @@@ struct date_mode 
  struct date_mode *date_mode_from_type(enum date_mode_type type);
  
  const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
 -void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
 +void show_date_relative(timestamp_t time, const struct timeval *now,
 +                      struct strbuf *timebuf);
 +void show_date_human(timestamp_t time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
  int parse_date(const char *date, struct strbuf *out);
  int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
@@@ -1507,19 -1504,10 +1532,19 @@@ int date_overflows(timestamp_t date)
  #define IDENT_STRICT         1
  #define IDENT_NO_DATE        2
  #define IDENT_NO_NAME        4
 +
 +enum want_ident {
 +      WANT_BLANK_IDENT,
 +      WANT_AUTHOR_IDENT,
 +      WANT_COMMITTER_IDENT
 +};
 +
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
 -extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 -extern const char *fmt_name(const char *name, const char *email);
 +extern const char *fmt_ident(const char *name, const char *email,
 +              enum want_ident whose_ident,
 +              const char *date_str, int);
 +extern const char *fmt_name(enum want_ident);
  extern const char *ident_default_name(void);
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
@@@ -1576,14 -1564,9 +1601,14 @@@ struct checkout 
  #define CHECKOUT_INIT { NULL, "" }
  
  #define TEMPORARY_FILENAME_LENGTH 25
 -extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 +extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
  extern void enable_delayed_checkout(struct checkout *state);
 -extern int finish_delayed_checkout(struct checkout *state);
 +extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
 +/*
 + * Unlink the last component and schedule the leading directories for
 + * removal, such that empty directories get removed.
 + */
 +extern void unlink_entry(const struct cache_entry *ce);
  
  struct cache_def {
        struct strbuf path;
@@@ -1634,7 -1617,7 +1659,7 @@@ extern int odb_mkstemp(struct strbuf *t
  extern int odb_pack_keep(const char *name);
  
  /*
 - * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
 + * Set this to 0 to prevent oid_object_info_extended() from fetching missing
   * blobs. This has a difference only if extensions.partialClone is set.
   *
   * Its default value is 1.
@@@ -1830,7 -1813,4 +1855,7 @@@ void safe_create_dir(const char *dir, i
   */
  extern int print_sha1_ellipsis(void);
  
 +/* Return 1 if the file is empty or does not exists, 0 otherwise. */
 +extern int is_empty_or_missing_file(const char *filename);
 +
  #endif /* CACHE_H */
diff --combined repository.c
index 5cad2dcc86a530ab088cda98a5c64bbe78102038,df88705574e1f8d95eff44e524963ea950e2b26b..682c239fe3eda6c16b7be45fc5c920a325d61817
@@@ -1,20 -1,13 +1,20 @@@
 +/*
 + * not really _using_ the compat macros, just make sure the_index
 + * declaration matches the definition in this file.
 + */
 +#define USE_THE_INDEX_COMPATIBILITY_MACROS
  #include "cache.h"
  #include "repository.h"
  #include "object-store.h"
  #include "config.h"
  #include "object.h"
 +#include "lockfile.h"
  #include "submodule-config.h"
  
  /* The main repository */
  static struct repository the_repo;
  struct repository *the_repository;
 +struct index_state the_index;
  
  void initialize_the_repository(void)
  {
@@@ -126,8 -119,6 +126,8 @@@ out
  void repo_set_worktree(struct repository *repo, const char *path)
  {
        repo->worktree = real_pathdup(path, 1);
 +
 +      trace2_def_repo(repo);
  }
  
  static int read_and_verify_repository_format(struct repository_format *format,
@@@ -157,7 -148,7 +157,7 @@@ int repo_init(struct repository *repo
              const char *gitdir,
              const char *worktree)
  {
-       struct repository_format format;
+       struct repository_format format = REPOSITORY_FORMAT_INIT;
        memset(repo, 0, sizeof(*repo));
  
        repo->objects = raw_object_store_new();
        if (worktree)
                repo_set_worktree(repo, worktree);
  
+       clear_repository_format(&format);
        return 0;
  
  error:
        return -1;
  }
  
 -/*
 - * Initialize 'submodule' as the submodule given by 'path' in parent repository
 - * 'superproject'.
 - * Return 0 upon success and a non-zero value upon failure.
 - */
 -int repo_submodule_init(struct repository *submodule,
 +int repo_submodule_init(struct repository *subrepo,
                        struct repository *superproject,
 -                      const char *path)
 +                      const struct submodule *sub)
  {
 -      const struct submodule *sub;
        struct strbuf gitdir = STRBUF_INIT;
        struct strbuf worktree = STRBUF_INIT;
        int ret = 0;
  
 -      sub = submodule_from_path(superproject, &null_oid, path);
        if (!sub) {
                ret = -1;
                goto out;
        }
  
 -      strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path);
 -      strbuf_repo_worktree_path(&worktree, superproject, "%s", path);
 +      strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", sub->path);
 +      strbuf_repo_worktree_path(&worktree, superproject, "%s", sub->path);
  
 -      if (repo_init(submodule, gitdir.buf, worktree.buf)) {
 +      if (repo_init(subrepo, gitdir.buf, worktree.buf)) {
                /*
                 * If initilization fails then it may be due to the submodule
                 * not being populated in the superproject's worktree.  Instead
                strbuf_repo_git_path(&gitdir, superproject,
                                     "modules/%s", sub->name);
  
 -              if (repo_init(submodule, gitdir.buf, NULL)) {
 +              if (repo_init(subrepo, gitdir.buf, NULL)) {
                        ret = -1;
                        goto out;
                }
        }
  
 -      submodule->submodule_prefix = xstrfmt("%s%s/",
 -                                            superproject->submodule_prefix ?
 -                                            superproject->submodule_prefix :
 -                                            "", path);
 +      subrepo->submodule_prefix = xstrfmt("%s%s/",
 +                                          superproject->submodule_prefix ?
 +                                          superproject->submodule_prefix :
 +                                          "", sub->path);
  
  out:
        strbuf_release(&gitdir);
@@@ -265,12 -264,3 +266,12 @@@ int repo_read_index(struct repository *
  
        return read_index_from(repo->index, repo->index_file, repo->gitdir);
  }
 +
 +int repo_hold_locked_index(struct repository *repo,
 +                         struct lock_file *lf,
 +                         int flags)
 +{
 +      if (!repo->index_file)
 +              BUG("the repo hasn't been setup");
 +      return hold_lock_file_for_update(lf, repo->index_file, flags);
 +}
diff --combined setup.c
index ca9e8a949ed869183f16c57520f19fb361100a02,ab6e8fd4c38f73be2c9dc9d9d0b39f8475db6524..d0c958c3b2a4a7d4b53970b0e623ed8f6e6a4be4
+++ b/setup.c
@@@ -39,7 -39,7 +39,7 @@@ static int abspath_part_inside_repo(cha
        off = offset_1st_component(path);
  
        /* check if work tree is already the prefix */
 -      if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
 +      if (wtlen <= len && !fspathncmp(path, work_tree, wtlen)) {
                if (path[wtlen] == '/') {
                        memmove(path, path + wtlen + 1, len - wtlen);
                        return 0;
@@@ -59,7 -59,7 +59,7 @@@
                path++;
                if (*path == '/') {
                        *path = '\0';
 -                      if (strcmp(real_path(path0), work_tree) == 0) {
 +                      if (fspathcmp(real_path(path0), work_tree) == 0) {
                                memmove(path0, path + 1, len - (path - path0));
                                return 0;
                        }
@@@ -68,7 -68,7 +68,7 @@@
        }
  
        /* check whole path */
 -      if (strcmp(real_path(path0), work_tree) == 0) {
 +      if (fspathcmp(real_path(path0), work_tree) == 0) {
                *path0 = '\0';
                return 0;
        }
@@@ -411,6 -411,7 +411,7 @@@ static int read_worktree_config(const c
        } else if (strcmp(var, "core.worktree") == 0) {
                if (!value)
                        return config_error_nonbool(var);
+               free(data->work_tree);
                data->work_tree = xstrdup(value);
        }
        return 0;
@@@ -476,7 -477,7 +477,7 @@@ static int check_repository_format_gent
        }
  
        repository_format_precious_objects = candidate->precious_objects;
-       repository_format_partial_clone = candidate->partial_clone;
+       repository_format_partial_clone = xstrdup_or_null(candidate->partial_clone);
        repository_format_worktree_config = candidate->worktree_config;
        string_list_clear(&candidate->unknown_extensions, 0);
  
                }
                if (candidate->work_tree) {
                        free(git_work_tree_cfg);
-                       git_work_tree_cfg = candidate->work_tree;
+                       git_work_tree_cfg = xstrdup(candidate->work_tree);
                        inside_work_tree = -1;
                }
-       } else {
-               free(candidate->work_tree);
        }
  
        return 0;
  }
  
+ static void init_repository_format(struct repository_format *format)
+ {
+       const struct repository_format fresh = REPOSITORY_FORMAT_INIT;
+       memcpy(format, &fresh, sizeof(fresh));
+ }
  int read_repository_format(struct repository_format *format, const char *path)
  {
-       memset(format, 0, sizeof(*format));
-       format->version = -1;
-       format->is_bare = -1;
-       format->hash_algo = GIT_HASH_SHA1;
-       string_list_init(&format->unknown_extensions, 1);
+       clear_repository_format(format);
        git_config_from_file(check_repo_format, path, format);
+       if (format->version == -1)
+               clear_repository_format(format);
        return format->version;
  }
  
+ void clear_repository_format(struct repository_format *format)
+ {
+       string_list_clear(&format->unknown_extensions, 0);
+       free(format->work_tree);
+       free(format->partial_clone);
+       init_repository_format(format);
+ }
  int verify_repository_format(const struct repository_format *format,
                             struct strbuf *err)
  {
@@@ -831,6 -843,16 +843,6 @@@ static const char *setup_bare_git_dir(s
        return NULL;
  }
  
 -static const char *setup_nongit(const char *cwd, int *nongit_ok)
 -{
 -      if (!nongit_ok)
 -              die(_("not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
 -      if (chdir(cwd))
 -              die_errno(_("cannot come back to cwd"));
 -      *nongit_ok = 1;
 -      return NULL;
 -}
 -
  static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
  {
        struct stat buf;
@@@ -997,7 -1019,7 +1009,7 @@@ int discover_git_directory(struct strbu
        struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
        size_t gitdir_offset = gitdir->len, cwd_len;
        size_t commondir_offset = commondir->len;
-       struct repository_format candidate;
+       struct repository_format candidate = REPOSITORY_FORMAT_INIT;
  
        if (strbuf_getcwd(&dir))
                return -1;
                strbuf_release(&err);
                strbuf_setlen(commondir, commondir_offset);
                strbuf_setlen(gitdir, gitdir_offset);
+               clear_repository_format(&candidate);
                return -1;
        }
  
+       clear_repository_format(&candidate);
        return 0;
  }
  
@@@ -1044,8 -1068,8 +1058,8 @@@ const char *setup_git_directory_gently(
  {
        static struct strbuf cwd = STRBUF_INIT;
        struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
 -      const char *prefix;
 +      const char *prefix = NULL;
-       struct repository_format repo_fmt;
+       struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
  
        /*
         * We may have read an incomplete configuration before
        strbuf_addbuf(&dir, &cwd);
  
        switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) {
 -      case GIT_DIR_NONE:
 -              prefix = NULL;
 -              break;
        case GIT_DIR_EXPLICIT:
                prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
                break;
                prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_HIT_CEILING:
 -              prefix = setup_nongit(cwd.buf, nongit_ok);
 +              if (!nongit_ok)
 +                      die(_("not a git repository (or any of the parent directories): %s"),
 +                          DEFAULT_GIT_DIR_ENVIRONMENT);
 +              *nongit_ok = 1;
                break;
        case GIT_DIR_HIT_MOUNT_POINT:
 -              if (nongit_ok) {
 -                      *nongit_ok = 1;
 -                      strbuf_release(&cwd);
 -                      strbuf_release(&dir);
 -                      return NULL;
 -              }
 -              die(_("not a git repository (or any parent up to mount point %s)\n"
 -                    "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
 -                  dir.buf);
 +              if (!nongit_ok)
 +                      die(_("not a git repository (or any parent up to mount point %s)\n"
 +                            "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
 +                          dir.buf);
 +              *nongit_ok = 1;
 +              break;
 +      case GIT_DIR_NONE:
 +              /*
 +               * As a safeguard against setup_git_directory_gently_1 returning
 +               * this value, fallthrough to BUG. Otherwise it is possible to
 +               * set startup_info->have_repository to 1 when we did nothing to
 +               * find a repository.
 +               */
        default:
                BUG("unhandled setup_git_directory_1() result");
        }
  
 -      if (prefix)
 -              setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
 -      else
 +      /*
 +       * At this point, nongit_ok is stable. If it is non-NULL and points
 +       * to a non-zero value, then this means that we haven't found a
 +       * repository and that the caller expects startup_info to reflect
 +       * this.
 +       *
 +       * Regardless of the state of nongit_ok, startup_info->prefix and
 +       * the GIT_PREFIX environment variable must always match. For details
 +       * see Documentation/config/alias.txt.
 +       */
 +      if (nongit_ok && *nongit_ok) {
 +              startup_info->have_repository = 0;
 +              startup_info->prefix = NULL;
                setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
 -
 -      startup_info->have_repository = !nongit_ok || !*nongit_ok;
 -      startup_info->prefix = prefix;
 +      } else {
 +              startup_info->have_repository = 1;
 +              startup_info->prefix = prefix;
 +              if (prefix)
 +                      setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
 +              else
 +                      setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
 +      }
  
        /*
         * Not all paths through the setup code will call 'set_git_dir()' (which
         * the user has set GIT_DIR.  It may be beneficial to disallow bogus
         * GIT_DIR values at some point in the future.
         */
 -      if (startup_info->have_repository || getenv(GIT_DIR_ENVIRONMENT)) {
 +      if (/* GIT_DIR_EXPLICIT, GIT_DIR_DISCOVERED, GIT_DIR_BARE */
 +          startup_info->have_repository ||
 +          /* GIT_DIR_EXPLICIT */
 +          getenv(GIT_DIR_ENVIRONMENT)) {
                if (!the_repository->gitdir) {
                        const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
                        if (!gitdir)
  
        strbuf_release(&dir);
        strbuf_release(&gitdir);
+       clear_repository_format(&repo_fmt);
  
        return prefix;
  }
@@@ -1214,9 -1217,10 +1229,10 @@@ int git_config_perm(const char *var, co
  
  void check_repository_format(void)
  {
-       struct repository_format repo_fmt;
+       struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
        check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
        startup_info->have_repository = 1;
+       clear_repository_format(&repo_fmt);
  }
  
  /*