Merge branch 'dk/gc-idx-wo-pack' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 4 Dec 2015 19:33:07 +0000 (11:33 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 4 Dec 2015 19:33:08 +0000 (11:33 -0800)
Having a leftover .idx file without corresponding .pack file in
the repository hurts performance; "git gc" learned to prune them.

We may want to do the same for .bitmap (and notice but not prune
.keep) without corresponding .pack, but that can be a separate
topic.

* dk/gc-idx-wo-pack:
gc: remove garbage .idx files from pack dir
t5304: test cleaning pack garbage
prepare_packed_git(): refactor garbage reporting in pack directory

1  2 
builtin/gc.c
cache.h
path.c
sha1_file.c
diff --combined builtin/gc.c
index cb13ab72c3cce47ac0b91f2ae7c0f03395ba5b46,203265db76bc49bb190b677cd095fd03a2c86063..42258fe3481dea642e85d8eb97ff175725420121
@@@ -11,7 -11,6 +11,7 @@@
   */
  
  #include "builtin.h"
 +#include "tempfile.h"
  #include "lockfile.h"
  #include "parse-options.h"
  #include "run-command.h"
@@@ -43,9 -42,37 +43,25 @@@ static struct argv_array prune = ARGV_A
  static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
  static struct argv_array rerere = ARGV_ARRAY_INIT;
  
 -static char *pidfile;
 -
 -static void remove_pidfile(void)
 -{
 -      if (pidfile)
 -              unlink(pidfile);
 -}
 -
 -static void remove_pidfile_on_signal(int signo)
 -{
 -      remove_pidfile();
 -      sigchain_pop(signo);
 -      raise(signo);
 -}
 +static struct tempfile pidfile;
 +static struct lock_file log_lock;
  
+ static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
+ static void clean_pack_garbage(void)
+ {
+       int i;
+       for (i = 0; i < pack_garbage.nr; i++)
+               unlink_or_warn(pack_garbage.items[i].string);
+       string_list_clear(&pack_garbage, 0);
+ }
+ static void report_pack_garbage(unsigned seen_bits, const char *path)
+ {
+       if (seen_bits == PACKDIR_FILE_IDX)
+               string_list_append(&pack_garbage, path);
+ }
  static void git_config_date_string(const char *key, const char **output)
  {
        if (git_config_get_string_const(key, output))
        }
  }
  
 +static void process_log_file(void)
 +{
 +      struct stat st;
 +      if (!fstat(get_lock_file_fd(&log_lock), &st) && st.st_size)
 +              commit_lock_file(&log_lock);
 +      else
 +              rollback_lock_file(&log_lock);
 +}
 +
 +static void process_log_file_at_exit(void)
 +{
 +      fflush(stderr);
 +      process_log_file();
 +}
 +
 +static void process_log_file_on_signal(int signo)
 +{
 +      process_log_file();
 +      sigchain_pop(signo);
 +      raise(signo);
 +}
 +
  static void gc_config(void)
  {
        const char *value;
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
        git_config_get_bool("gc.autodetach", &detach_auto);
        git_config_date_string("gc.pruneexpire", &prune_expire);
 -      git_config_date_string("gc.pruneworktreesexpire", &prune_worktrees_expire);
 +      git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
        git_config(git_default_config, NULL);
  }
  
@@@ -210,22 -215,20 +226,22 @@@ static const char *lock_repo_for_gc(in
        uintmax_t pid;
        FILE *fp;
        int fd;
 +      char *pidfile_path;
  
 -      if (pidfile)
 +      if (is_tempfile_active(&pidfile))
                /* already locked */
                return NULL;
  
        if (gethostname(my_host, sizeof(my_host)))
                strcpy(my_host, "unknown");
  
 -      fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
 +      pidfile_path = git_pathdup("gc.pid");
 +      fd = hold_lock_file_for_update(&lock, pidfile_path,
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
                static char locking_host[128];
                int should_exit;
 -              fp = fopen(git_path("gc.pid"), "r");
 +              fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
                        fp != NULL &&
                         * running.
                         */
                        time(NULL) - st.st_mtime <= 12 * 3600 &&
 -                      fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 &&
 +                      fscanf(fp, "%"SCNuMAX" %127c", &pid, locking_host) == 2 &&
                        /* be gentle to concurrent "gc" on remote hosts */
                        (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
                if (fp != NULL)
                        if (fd >= 0)
                                rollback_lock_file(&lock);
                        *ret_pid = pid;
 +                      free(pidfile_path);
                        return locking_host;
                }
        }
        write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
        commit_lock_file(&lock);
 -
 -      pidfile = git_pathdup("gc.pid");
 -      sigchain_push_common(remove_pidfile_on_signal);
 -      atexit(remove_pidfile);
 -
 +      register_tempfile(&pidfile, pidfile_path);
 +      free(pidfile_path);
        return NULL;
  }
  
 +static int report_last_gc_error(void)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret;
 +
 +      ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
 +      if (ret > 0)
 +              return error(_("The last gc run reported the following. "
 +                             "Please correct the root cause\n"
 +                             "and remove %s.\n"
 +                             "Automatic cleanup will not be performed "
 +                             "until the file is removed.\n\n"
 +                             "%s"),
 +                           git_path("gc.log"), sb.buf);
 +      strbuf_release(&sb);
 +      return 0;
 +}
 +
  static int gc_before_repack(void)
  {
        if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
@@@ -303,7 -290,6 +319,7 @@@ int cmd_gc(int argc, const char **argv
        int force = 0;
        const char *name;
        pid_t pid;
 +      int daemonized = 0;
  
        struct option builtin_gc_options[] = {
                OPT__QUIET(&quiet, N_("suppress progress reporting")),
                        fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
                }
                if (detach_auto) {
 +                      if (report_last_gc_error())
 +                              return -1;
 +
                        if (gc_before_repack())
                                return -1;
                        /*
                         * failure to daemonize is ok, we'll continue
                         * in foreground
                         */
 -                      daemonize();
 +                      daemonized = !daemonize();
                }
        } else
                add_repack_all_option();
                    name, (uintmax_t)pid);
        }
  
 +      if (daemonized) {
 +              hold_lock_file_for_update(&log_lock,
 +                                        git_path("gc.log"),
 +                                        LOCK_DIE_ON_ERROR);
 +              dup2(get_lock_file_fd(&log_lock), 2);
 +              sigchain_push_common(process_log_file_on_signal);
 +              atexit(process_log_file_at_exit);
 +      }
 +
        if (gc_before_repack())
                return -1;
  
 -      if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
 -              return error(FAILED_RUN, repack.argv[0]);
 +      if (!repository_format_precious_objects) {
 +              if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
 +                      return error(FAILED_RUN, repack.argv[0]);
  
 -      if (prune_expire) {
 -              argv_array_push(&prune, prune_expire);
 -              if (quiet)
 -                      argv_array_push(&prune, "--no-progress");
 -              if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
 -                      return error(FAILED_RUN, prune.argv[0]);
 +              if (prune_expire) {
 +                      argv_array_push(&prune, prune_expire);
 +                      if (quiet)
 +                              argv_array_push(&prune, "--no-progress");
 +                      if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
 +                              return error(FAILED_RUN, prune.argv[0]);
 +              }
        }
  
        if (prune_worktrees_expire) {
        if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
                return error(FAILED_RUN, rerere.argv[0]);
  
+       report_garbage = report_pack_garbage;
+       reprepare_packed_git();
+       if (pack_garbage.nr > 0)
+               clean_pack_garbage();
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
                        "run 'git prune' to remove them."));
diff --combined cache.h
index a9aaa0396a893c2669789ea18ee14ecafac965b0,c6de1e9c0e0387956a015271fcf60f737663a038..649faa77e370ee9d8bf9b604fe211911002384ff
+++ b/cache.h
@@@ -397,7 -397,6 +397,7 @@@ static inline enum object_type object_t
  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
  #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
  #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
 +#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
  #define GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
@@@ -443,22 -442,11 +443,22 @@@ extern char *get_object_directory(void)
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
 +extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
  extern int get_common_dir(struct strbuf *sb, const char *gitdir);
  extern const char *get_git_namespace(void);
  extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
 -extern const char *read_gitfile(const char *path);
 +
 +#define READ_GITFILE_ERR_STAT_FAILED 1
 +#define READ_GITFILE_ERR_NOT_A_FILE 2
 +#define READ_GITFILE_ERR_OPEN_FAILED 3
 +#define READ_GITFILE_ERR_READ_FAILED 4
 +#define READ_GITFILE_ERR_INVALID_FORMAT 5
 +#define READ_GITFILE_ERR_NO_PATH 6
 +#define READ_GITFILE_ERR_NOT_A_REPO 7
 +#define READ_GITFILE_ERR_TOO_LARGE 8
 +extern const char *read_gitfile_gently(const char *path, int *return_error_code);
 +#define read_gitfile(path) read_gitfile_gently((path), NULL)
  extern const char *resolve_gitdir(const char *suspect);
  extern void set_git_work_tree(const char *tree);
  
@@@ -521,8 -509,7 +521,8 @@@ extern int write_locked_index(struct in
  extern int discard_index(struct index_state *);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
 -extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
 +extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
 +extern void adjust_dirname_case(struct index_state *istate, char *name);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
@@@ -598,6 -585,8 +598,6 @@@ extern void update_index_if_able(struc
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
 -extern int delete_ref(const char *, const unsigned char *sha1, unsigned int flags);
 -
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
  extern int trust_ctime;
@@@ -633,7 -622,6 +633,7 @@@ extern unsigned long pack_size_limit_cf
   * been sought but there were none.
   */
  extern int check_replace_refs;
 +extern char *git_replace_ref_base;
  
  extern int fsync_object_files;
  extern int core_preload_index;
@@@ -698,15 -686,8 +698,15 @@@ extern char *notes_ref_name
  
  extern int grafts_replace_parents;
  
 +/*
 + * GIT_REPO_VERSION is the version we write by default. The
 + * _READ variant is the highest number we know how to
 + * handle.
 + */
  #define GIT_REPO_VERSION 0
 +#define GIT_REPO_VERSION_READ 1
  extern int repository_format_version;
 +extern int repository_format_precious_objects;
  extern int check_repository_format(void);
  
  #define MTIME_CHANGED 0x0001
  #define DATA_CHANGED    0x0020
  #define TYPE_CHANGED    0x0040
  
 +/*
 + * Return a statically allocated filename, either generically (mkpath), in
 + * the repository directory (git_path), or in a submodule's repository
 + * directory (git_path_submodule). In all cases, note that the result
 + * may be overwritten by another call to _any_ of the functions. Consider
 + * using the safer "dup" or "strbuf" formats below (in some cases, the
 + * unsafe versions have already been removed).
 + */
 +extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +
  extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 +extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 +                                    const char *fmt, ...)
 +      __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
  extern char *mkpathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 -
 -/* Return a statically allocated filename matching the sha1 signature */
 -extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 -extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 -extern const char *git_path_submodule(const char *path, const char *fmt, ...)
 +extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 +
  extern void report_linked_checkout_garbage(void);
  
 +/*
 + * You can define a static memoized git path like:
 + *
 + *    static GIT_PATH_FUNC(git_path_foo, "FOO");
 + *
 + * or use one of the global ones below.
 + */
 +#define GIT_PATH_FUNC(func, filename) \
 +      const char *func(void) \
 +      { \
 +              static char *ret; \
 +              if (!ret) \
 +                      ret = git_pathdup(filename); \
 +              return ret; \
 +      }
 +
 +const char *git_path_cherry_pick_head(void);
 +const char *git_path_revert_head(void);
 +const char *git_path_squash_msg(void);
 +const char *git_path_merge_msg(void);
 +const char *git_path_merge_rr(void);
 +const char *git_path_merge_mode(void);
 +const char *git_path_merge_head(void);
 +const char *git_path_fetch_head(void);
 +const char *git_path_shallow(void);
 +
  /*
   * Return the name of the file in the local object database that would
   * be used to store a loose object with the specified sha1.  The
@@@ -991,7 -935,7 +991,7 @@@ extern int do_check_packed_object_crc
  
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
 -extern int move_temp_to_file(const char *tmpfile, const char *filename);
 +extern int finalize_object_file(const char *tmpfile, const char *filename);
  
  extern int has_sha1_pack(const unsigned char *sha1);
  
@@@ -1076,10 -1020,76 +1076,10 @@@ extern int get_oid_hex(const char *hex
  
  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 */
 -extern int read_ref_full(const char *refname, int resolve_flags,
 -                       unsigned char *sha1, int *flags);
 -extern int read_ref(const char *refname, unsigned char *sha1);
  
 -/*
 - * Resolve a reference, recursively following symbolic refererences.
 - *
 - * Store the referred-to object's name in sha1 and return the name of
 - * the non-symbolic reference that ultimately pointed at it.  The
 - * return value, if not NULL, is a pointer into either a static buffer
 - * or the input ref.
 - *
 - * If the reference cannot be resolved to an object, the behavior
 - * depends on the RESOLVE_REF_READING flag:
 - *
 - * - If RESOLVE_REF_READING is set, return NULL.
 - *
 - * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
 - *   the last reference name in the chain, which will either be a non-symbolic
 - *   reference or an undefined reference.  If this is a prelude to
 - *   "writing" to the ref, the return value is the name of the ref
 - *   that will actually be created or changed.
 - *
 - * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
 - * level of symbolic reference.  The value stored in sha1 for a symbolic
 - * reference will always be null_sha1 in this case, and the return
 - * value is the reference that the symref refers to directly.
 - *
 - * If flags is non-NULL, set the value that it points to the
 - * combination of REF_ISPACKED (if the reference was found among the
 - * packed references), REF_ISSYMREF (if the initial reference was a
 - * symbolic reference), REF_BAD_NAME (if the reference name is ill
 - * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
 - * (if the ref is malformed or has a bad name). See refs.h for more detail
 - * on each flag.
 - *
 - * If ref is not a properly-formatted, normalized reference, return
 - * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
 - * give up and return NULL.
 - *
 - * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
 - * name is invalid according to git-check-ref-format(1).  If the name
 - * is bad then the value stored in sha1 will be null_sha1 and the two
 - * flags REF_ISBROKEN and REF_BAD_NAME will be set.
 - *
 - * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
 - * directory and do not consist of all caps and underscores cannot be
 - * resolved. The function returns NULL for such ref names.
 - * Caps and underscores refers to the special refs, such as HEAD,
 - * FETCH_HEAD and friends, that all live outside of the refs/ directory.
 - */
 -#define RESOLVE_REF_READING 0x01
 -#define RESOLVE_REF_NO_RECURSE 0x02
 -#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 -extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
 -extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
 -
 -extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 -extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  extern int interpret_branch_name(const char *str, int len, struct strbuf *);
  extern int get_sha1_mb(const char *str, unsigned char *sha1);
  
 -/*
 - * Return true iff abbrev_name is a possible abbreviation for
 - * full_name according to the rules defined by ref_rev_parse_rules in
 - * refs.c.
 - */
 -extern int refname_match(const char *abbrev_name, const char *full_name);
 -
 -extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
  extern int validate_headref(const char *ref);
  
  extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
@@@ -1095,30 -1105,18 +1095,30 @@@ extern void *read_object_with_reference
  extern struct object *peel_to_type(const char *name, int namelen,
                                   struct object *o, enum object_type);
  
 -enum date_mode {
 -      DATE_NORMAL = 0,
 -      DATE_RELATIVE,
 -      DATE_SHORT,
 -      DATE_LOCAL,
 -      DATE_ISO8601,
 -      DATE_ISO8601_STRICT,
 -      DATE_RFC2822,
 -      DATE_RAW
 +struct date_mode {
 +      enum date_mode_type {
 +              DATE_NORMAL = 0,
 +              DATE_RELATIVE,
 +              DATE_SHORT,
 +              DATE_LOCAL,
 +              DATE_ISO8601,
 +              DATE_ISO8601_STRICT,
 +              DATE_RFC2822,
 +              DATE_STRFTIME,
 +              DATE_RAW
 +      } type;
 +      const char *strftime_fmt;
  };
  
 -const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 +/*
 + * Convenience helper for passing a constant type, like:
 + *
 + *   show_date(t, tz, DATE_MODE(NORMAL));
 + */
 +#define DATE_MODE(t) date_mode_from_type(DATE_##t)
 +struct date_mode *date_mode_from_type(enum date_mode_type type);
 +
 +const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
  void show_date_relative(unsigned long time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
  int parse_date(const char *date, struct strbuf *out);
@@@ -1128,7 -1126,7 +1128,7 @@@ void datestamp(struct strbuf *out)
  #define approxidate(s) approxidate_careful((s), NULL)
  unsigned long approxidate_careful(const char *, int *);
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
 -enum date_mode parse_date_format(const char *format);
 +void parse_date_format(const char *format, struct date_mode *mode);
  int date_overflows(unsigned long date);
  
  #define IDENT_STRICT         1
@@@ -1165,8 -1163,7 +1165,8 @@@ extern int split_ident_line(struct iden
   * the ident_split. It will also sanity-check the values and produce
   * a well-known sentinel date if they appear bogus.
   */
 -const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
 +const char *show_ident_date(const struct ident_split *id,
 +                          const struct date_mode *mode);
  
  /*
   * Compare split idents for equality or strict ordering. Note that we
@@@ -1258,8 -1255,11 +1258,11 @@@ struct pack_entry 
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
- /* A hook for count-objects to report invalid files in pack directory */
- extern void (*report_garbage)(const char *desc, const char *path);
+ /* A hook to report invalid files in pack directory */
+ #define PACKDIR_FILE_PACK 1
+ #define PACKDIR_FILE_IDX 2
+ #define PACKDIR_FILE_GARBAGE 4
+ extern void (*report_garbage)(unsigned seen_bits, const char *path);
  
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
@@@ -1284,7 -1284,6 +1287,7 @@@ extern void close_pack_index(struct pac
  
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  extern void close_pack_windows(struct packed_git *);
 +extern void close_all_packs(void);
  extern void unuse_pack(struct pack_window **);
  extern void free_pack_by_name(const char *);
  extern void clear_delta_base_cache(void);
@@@ -1439,7 -1438,6 +1442,7 @@@ extern int git_config_with_options(conf
                                   int respect_includes);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
 +extern int git_parse_maybe_bool(const char *);
  extern int git_config_int(const char *, const char *);
  extern int64_t git_config_int64(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
@@@ -1451,7 -1449,6 +1454,7 @@@ extern int git_config_pathname(const ch
  extern int git_config_set_in_file(const char *, const char *, const char *);
  extern int git_config_set(const char *, const char *);
  extern int git_config_parse_key(const char *, char **, int *);
 +extern int git_config_key_is_valid(const char *key);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
@@@ -1588,9 -1585,8 +1591,9 @@@ static inline ssize_t write_str_in_full
  {
        return write_in_full(fd, str, strlen(str));
  }
 -__attribute__((format (printf, 3, 4)))
 -extern int write_file(const char *path, int fatal, const char *fmt, ...);
 +
 +extern int write_file(const char *path, const char *fmt, ...);
 +extern int write_file_gently(const char *path, const char *fmt, ...);
  
  /* pager.c */
  extern void setup_pager(void);
diff --combined path.c
index 56317a66be27e933d94e815d41a8806d235adbb7,75ec2363fdd94a891e7177fd9469dbc17855ea99..38f2ebd6bfad2b1557b06ce34a0154603533dd65
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -98,7 -98,7 +98,7 @@@ static const char *common_list[] = 
        NULL
  };
  
 -static void update_common_dir(struct strbuf *buf, int git_dir_len)
 +static void update_common_dir(struct strbuf *buf, int git_dir_len, const char *common_dir)
  {
        char *base = buf->buf + git_dir_len;
        const char **p;
                        path++;
                        is_dir = 1;
                }
 +
 +              if (!common_dir)
 +                      common_dir = get_git_common_dir();
 +
                if (is_dir && dir_prefix(base, path)) {
 -                      replace_dir(buf, git_dir_len, get_git_common_dir());
 +                      replace_dir(buf, git_dir_len, common_dir);
                        return;
                }
                if (!is_dir && !strcmp(base, path)) {
 -                      replace_dir(buf, git_dir_len, get_git_common_dir());
 +                      replace_dir(buf, git_dir_len, common_dir);
                        return;
                }
        }
@@@ -147,7 -143,7 +147,7 @@@ void report_linked_checkout_garbage(voi
                strbuf_setlen(&sb, len);
                strbuf_addstr(&sb, path);
                if (file_exists(sb.buf))
-                       report_garbage("unused in linked checkout", sb.buf);
+                       report_garbage(PACKDIR_FILE_GARBAGE, sb.buf);
        }
        strbuf_release(&sb);
  }
@@@ -164,7 -160,7 +164,7 @@@ static void adjust_git_path(struct strb
        else if (git_db_env && dir_prefix(base, "objects"))
                replace_dir(buf, git_dir_len + 7, get_object_directory());
        else if (git_common_dir_env)
 -              update_common_dir(buf, git_dir_len);
 +              update_common_dir(buf, git_dir_len, NULL);
  }
  
  static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
@@@ -228,12 -224,11 +228,12 @@@ const char *mkpath(const char *fmt, ...
        return cleanup_path(pathname->buf);
  }
  
 -const char *git_path_submodule(const char *path, const char *fmt, ...)
 +static void do_submodule_path(struct strbuf *buf, const char *path,
 +                            const char *fmt, va_list args)
  {
 -      struct strbuf *buf = get_pathname();
        const char *git_dir;
 -      va_list args;
 +      struct strbuf git_submodule_common_dir = STRBUF_INIT;
 +      struct strbuf git_submodule_dir = STRBUF_INIT;
  
        strbuf_addstr(buf, path);
        if (buf->len && buf->buf[buf->len - 1] != '/')
                strbuf_addstr(buf, git_dir);
        }
        strbuf_addch(buf, '/');
 +      strbuf_addstr(&git_submodule_dir, buf->buf);
  
 -      va_start(args, fmt);
        strbuf_vaddf(buf, fmt, args);
 -      va_end(args);
 +
 +      if (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))
 +              update_common_dir(buf, git_submodule_dir.len, git_submodule_common_dir.buf);
 +
        strbuf_cleanup_path(buf);
 -      return buf->buf;
 +
 +      strbuf_release(&git_submodule_dir);
 +      strbuf_release(&git_submodule_common_dir);
 +}
 +
 +char *git_pathdup_submodule(const char *path, const char *fmt, ...)
 +{
 +      va_list args;
 +      struct strbuf buf = STRBUF_INIT;
 +      va_start(args, fmt);
 +      do_submodule_path(&buf, path, fmt, args);
 +      va_end(args);
 +      return strbuf_detach(&buf, NULL);
 +}
 +
 +void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 +                             const char *fmt, ...)
 +{
 +      va_list args;
 +      va_start(args, fmt);
 +      do_submodule_path(buf, path, fmt, args);
 +      va_end(args);
  }
  
  int validate_headref(const char *path)
@@@ -445,22 -416,18 +445,22 @@@ const char *enter_repo(const char *path
                }
                if (!suffix[i])
                        return NULL;
 -              gitfile = read_gitfile(used_path) ;
 +              gitfile = read_gitfile(used_path);
                if (gitfile)
                        strcpy(used_path, gitfile);
                if (chdir(used_path))
                        return NULL;
                path = validated_path;
        }
 -      else if (chdir(path))
 -              return NULL;
 +      else {
 +              const char *gitfile = read_gitfile(path);
 +              if (gitfile)
 +                      path = gitfile;
 +              if (chdir(path))
 +                      return NULL;
 +      }
  
 -      if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
 -          validate_headref("HEAD") == 0) {
 +      if (is_git_directory(".")) {
                set_git_dir(".");
                check_repository_format();
                return path;
@@@ -694,11 -661,6 +694,11 @@@ const char *remove_leading_path(const c
   * normalized, any time "../" eats up to the prefix_len part,
   * prefix_len is reduced. In the end prefix_len is the remaining
   * prefix that has not been overridden by user pathspec.
 + *
 + * NEEDSWORK: This function doesn't perform normalization w.r.t. trailing '/'.
 + * For everything but the root folder itself, the normalized path should not
 + * end with a '/', then the callers need to be fixed up accordingly.
 + *
   */
  int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
  {
@@@ -956,13 -918,3 +956,13 @@@ char *xdg_config_home(const char *filen
                return mkpathdup("%s/.config/git/%s", home, filename);
        return NULL;
  }
 +
 +GIT_PATH_FUNC(git_path_cherry_pick_head, "CHERRY_PICK_HEAD")
 +GIT_PATH_FUNC(git_path_revert_head, "REVERT_HEAD")
 +GIT_PATH_FUNC(git_path_squash_msg, "SQUASH_MSG")
 +GIT_PATH_FUNC(git_path_merge_msg, "MERGE_MSG")
 +GIT_PATH_FUNC(git_path_merge_rr, "MERGE_RR")
 +GIT_PATH_FUNC(git_path_merge_mode, "MERGE_MODE")
 +GIT_PATH_FUNC(git_path_merge_head, "MERGE_HEAD")
 +GIT_PATH_FUNC(git_path_fetch_head, "FETCH_HEAD")
 +GIT_PATH_FUNC(git_path_shallow, "shallow")
diff --combined sha1_file.c
index 40a0169ceb11e338e22a5937eb43b57c4d3a6846,0c0b6529490a7d5908cd0ed62f17ed33ada86548..4160e6882de40b2e032727616084a08c94a1e565
@@@ -377,12 -377,15 +377,12 @@@ void read_info_alternates(const char * 
        char *map;
        size_t mapsz;
        struct stat st;
 -      const char alt_file_name[] = "info/alternates";
 -      /* Given that relative_base is no longer than PATH_MAX,
 -         ensure that "path" has enough space to append "/", the
 -         file name, "info/alternates", and a trailing NUL.  */
 -      char path[PATH_MAX + 1 + sizeof alt_file_name];
 +      char *path;
        int fd;
  
 -      sprintf(path, "%s/%s", relative_base, alt_file_name);
 +      path = xstrfmt("%s/info/alternates", relative_base);
        fd = git_open_noatime(path);
 +      free(path);
        if (fd < 0)
                return;
        if (fstat(fd, &st) || (st.st_size == 0)) {
  void add_to_alternates_file(const char *reference)
  {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 -      int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
 -      const char *alt = mkpath("%s\n", reference);
 -      write_or_die(fd, alt, strlen(alt));
 -      if (commit_lock_file(lock))
 -              die("could not close alternates file");
 -      if (alt_odb_tail)
 -              link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
 +      char *alts = git_pathdup("objects/info/alternates");
 +      FILE *in, *out;
 +
 +      hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
 +      out = fdopen_lock_file(lock, "w");
 +      if (!out)
 +              die_errno("unable to fdopen alternates lockfile");
 +
 +      in = fopen(alts, "r");
 +      if (in) {
 +              struct strbuf line = STRBUF_INIT;
 +              int found = 0;
 +
 +              while (strbuf_getline(&line, in, '\n') != EOF) {
 +                      if (!strcmp(reference, line.buf)) {
 +                              found = 1;
 +                              break;
 +                      }
 +                      fprintf_or_die(out, "%s\n", line.buf);
 +              }
 +
 +              strbuf_release(&line);
 +              fclose(in);
 +
 +              if (found) {
 +                      rollback_lock_file(lock);
 +                      lock = NULL;
 +              }
 +      }
 +      else if (errno != ENOENT)
 +              die_errno("unable to read alternates file");
 +
 +      if (lock) {
 +              fprintf_or_die(out, "%s\n", reference);
 +              if (commit_lock_file(lock))
 +                      die_errno("unable to move new alternates file into place");
 +              if (alt_odb_tail)
 +                      link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
 +      }
 +      free(alts);
  }
  
  int foreach_alt_odb(alt_odb_fn fn, void *cb)
@@@ -786,37 -756,6 +786,37 @@@ void close_pack_windows(struct packed_g
        }
  }
  
 +static int close_pack_fd(struct packed_git *p)
 +{
 +      if (p->pack_fd < 0)
 +              return 0;
 +
 +      close(p->pack_fd);
 +      pack_open_fds--;
 +      p->pack_fd = -1;
 +
 +      return 1;
 +}
 +
 +static void close_pack(struct packed_git *p)
 +{
 +      close_pack_windows(p);
 +      close_pack_fd(p);
 +      close_pack_index(p);
 +}
 +
 +void close_all_packs(void)
 +{
 +      struct packed_git *p;
 +
 +      for (p = packed_git; p; p = p->next)
 +              if (p->do_not_close)
 +                      die("BUG! Want to close pack marked 'do-not-close'");
 +              else
 +                      close_pack(p);
 +}
 +
 +
  /*
   * The LRU pack is the one with the oldest MRU window, preferring packs
   * with no used windows, or the oldest mtime if it has no windows allocated.
@@@ -884,8 -823,12 +884,8 @@@ static int close_one_pack(void
                find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
        }
  
 -      if (lru_p) {
 -              close(lru_p->pack_fd);
 -              pack_open_fds--;
 -              lru_p->pack_fd = -1;
 -              return 1;
 -      }
 +      if (lru_p)
 +              return close_pack_fd(lru_p);
  
        return 0;
  }
@@@ -925,7 -868,12 +925,7 @@@ void free_pack_by_name(const char *pack
                p = *pp;
                if (strcmp(pack_name, p->pack_name) == 0) {
                        clear_delta_base_cache();
 -                      close_pack_windows(p);
 -                      if (p->pack_fd != -1) {
 -                              close(p->pack_fd);
 -                              pack_open_fds--;
 -                      }
 -                      close_pack_index(p);
 +                      close_pack(p);
                        free(p->bad_object_sha1);
                        *pp = p->next;
                        if (last_found_pack == p)
@@@ -1059,7 -1007,11 +1059,7 @@@ static int open_packed_git(struct packe
  {
        if (!open_packed_git_1(p))
                return 0;
 -      if (p->pack_fd != -1) {
 -              close(p->pack_fd);
 -              pack_open_fds--;
 -              p->pack_fd = -1;
 -      }
 +      close_pack_fd(p);
        return -1;
  }
  
@@@ -1125,8 -1077,11 +1125,8 @@@ unsigned char *use_pack(struct packed_g
                                        p->pack_name,
                                        strerror(errno));
                        if (!win->offset && win->len == p->pack_size
 -                              && !p->do_not_close) {
 -                              close(p->pack_fd);
 -                              pack_open_fds--;
 -                              p->pack_fd = -1;
 -                      }
 +                              && !p->do_not_close)
 +                              close_pack_fd(p);
                        pack_mmap_calls++;
                        pack_open_windows++;
                        if (pack_mapped > peak_pack_mapped)
@@@ -1228,27 -1183,16 +1228,16 @@@ void install_packed_git(struct packed_g
        packed_git = pack;
  }
  
- void (*report_garbage)(const char *desc, const char *path);
+ void (*report_garbage)(unsigned seen_bits, const char *path);
  
  static void report_helper(const struct string_list *list,
                          int seen_bits, int first, int last)
  {
-       const char *msg;
-       switch (seen_bits) {
-       case 0:
-               msg = "no corresponding .idx or .pack";
-               break;
-       case 1:
-               msg = "no corresponding .idx";
-               break;
-       case 2:
-               msg = "no corresponding .pack";
-               break;
-       default:
+       if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))
                return;
-       }
        for (; first < last; first++)
-               report_garbage(msg, list->items[first].string);
+               report_garbage(seen_bits, list->items[first].string);
  }
  
  static void report_pack_garbage(struct string_list *list)
                if (baselen == -1) {
                        const char *dot = strrchr(path, '.');
                        if (!dot) {
-                               report_garbage("garbage found", path);
+                               report_garbage(PACKDIR_FILE_GARBAGE, path);
                                continue;
                        }
                        baselen = dot - path + 1;
@@@ -1343,7 -1287,7 +1332,7 @@@ static void prepare_packed_git_one(cha
                    ends_with(de->d_name, ".keep"))
                        string_list_append(&garbage, path.buf);
                else
-                       report_garbage("garbage found", path.buf);
+                       report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
        }
        closedir(dir);
        report_pack_garbage(&garbage);
@@@ -1506,10 -1450,7 +1495,10 @@@ int git_open_noatime(const char *name
        static int sha1_file_open_flag = O_NOATIME;
  
        for (;;) {
 -              int fd = open(name, O_RDONLY | sha1_file_open_flag);
 +              int fd;
 +
 +              errno = 0;
 +              fd = open(name, O_RDONLY | sha1_file_open_flag);
                if (fd >= 0)
                        return fd;
  
@@@ -2137,7 -2078,7 +2126,7 @@@ static unsigned long pack_entry_hash(st
  {
        unsigned long hash;
  
 -      hash = (unsigned long)p + (unsigned long)base_offset;
 +      hash = (unsigned long)(intptr_t)p + (unsigned long)base_offset;
        hash += (hash >> 8) + (hash >> 16);
        return hash % MAX_DELTA_CACHE;
  }
@@@ -2956,8 -2897,11 +2945,8 @@@ static void write_sha1_file_prepare(con
  
  /*
   * Move the just written object into its final resting place.
 - * NEEDSWORK: this should be renamed to finalize_temp_file() as
 - * "moving" is only a part of what it does, when no patch between
 - * master to pu changes the call sites of this function.
   */
 -int move_temp_to_file(const char *tmpfile, const char *filename)
 +int finalize_object_file(const char *tmpfile, const char *filename)
  {
        int ret = 0;
  
@@@ -3130,7 -3074,7 +3119,7 @@@ static int write_loose_object(const uns
                                tmp_file, strerror(errno));
        }
  
 -      return move_temp_to_file(tmp_file, filename);
 +      return finalize_object_file(tmp_file, filename);
  }
  
  static int freshen_loose_object(const unsigned char *sha1)