Merge branch 'jk/git-path'
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:48:56 +0000 (14:48 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:48:56 +0000 (14:48 -0700)
git_path() and mkpath() are handy helper functions but it is easy
to misuse, as the callers need to be careful to keep the number of
active results below 4. Their uses have been reduced.

* jk/git-path:
memoize common git-path "constant" files
get_repo_path: refactor path-allocation
find_hook: keep our own static buffer
refs.c: remove_empty_directories can take a strbuf
refs.c: avoid git_path assignment in lock_ref_sha1_basic
refs.c: avoid repeated git_path calls in rename_tmp_log
refs.c: simplify strbufs in reflog setup and writing
path.c: drop git_path_submodule
refs.c: remove extra git_path calls from read_loose_refs
remote.c: drop extraneous local variable from migrate_file
prefer mkpathdup to mkpath in assignments
prefer git_pathdup to git_path in some possibly-dangerous cases
add_to_alternates_file: don't add duplicate entries
t5700: modernize style
cache.h: complete set of git_path_submodule helpers
cache.h: clarify documentation for git_path, et al

1  2 
bisect.c
branch.c
builtin/clone.c
builtin/fetch.c
cache.h
dir.c
fast-import.c
refs.c
sha1_file.c
unpack-trees.c
diff --combined bisect.c
index 857cf59aa3e33add42e465c68855dddde5ea7cb4,7a6498c8e93d872dc8321d2c9d121c39554c2aca..f9da9d134680c7581231f27584b10e83a6110bcd
+++ b/bisect.c
@@@ -21,9 -21,6 +21,9 @@@ static const char *argv_checkout[] = {"
  static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
  static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
  
 +static const char *term_bad;
 +static const char *term_good;
 +
  /* Remember to update object flag allocation in object.h */
  #define COUNTED               (1u<<16)
  
@@@ -406,21 -403,15 +406,21 @@@ struct commit_list *find_bisection(stru
  static int register_ref(const char *refname, const struct object_id *oid,
                        int flags, void *cb_data)
  {
 -      if (!strcmp(refname, "bad")) {
 +      struct strbuf good_prefix = STRBUF_INIT;
 +      strbuf_addstr(&good_prefix, term_good);
 +      strbuf_addstr(&good_prefix, "-");
 +
 +      if (!strcmp(refname, term_bad)) {
                current_bad_oid = xmalloc(sizeof(*current_bad_oid));
                oidcpy(current_bad_oid, oid);
 -      } else if (starts_with(refname, "good-")) {
 +      } else if (starts_with(refname, good_prefix.buf)) {
                sha1_array_append(&good_revs, oid->hash);
        } else if (starts_with(refname, "skip-")) {
                sha1_array_append(&skipped_revs, oid->hash);
        }
  
 +      strbuf_release(&good_prefix);
 +
        return 0;
  }
  
@@@ -429,10 -420,13 +429,13 @@@ static int read_bisect_refs(void
        return for_each_ref_in("refs/bisect/", register_ref, NULL);
  }
  
+ static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
+ static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
  static void read_bisect_paths(struct argv_array *array)
  {
        struct strbuf str = STRBUF_INIT;
-       const char *filename = git_path("BISECT_NAMES");
+       const char *filename = git_path_bisect_names();
        FILE *fp = fopen(filename, "r");
  
        if (!fp)
@@@ -643,7 -637,7 +646,7 @@@ static void exit_if_skipped_commits(str
                return;
  
        printf("There are only 'skip'ped commits left to test.\n"
 -             "The first bad commit could be any of:\n");
 +             "The first %s commit could be any of:\n", term_bad);
        print_commit_list(tried, "%s\n", "%s\n");
        if (bad)
                printf("%s\n", oid_to_hex(bad));
  
  static int is_expected_rev(const struct object_id *oid)
  {
-       const char *filename = git_path("BISECT_EXPECTED_REV");
+       const char *filename = git_path_bisect_expected_rev();
        struct stat st;
        struct strbuf str = STRBUF_INIT;
        FILE *fp;
@@@ -741,24 -735,18 +744,24 @@@ static void handle_bad_merge_base(void
        if (is_expected_rev(current_bad_oid)) {
                char *bad_hex = oid_to_hex(current_bad_oid);
                char *good_hex = join_sha1_array_hex(&good_revs, ' ');
 -
 -              fprintf(stderr, "The merge base %s is bad.\n"
 -                      "This means the bug has been fixed "
 -                      "between %s and [%s].\n",
 -                      bad_hex, bad_hex, good_hex);
 -
 +              if (!strcmp(term_bad, "bad") && !strcmp(term_good, "good")) {
 +                      fprintf(stderr, "The merge base %s is bad.\n"
 +                              "This means the bug has been fixed "
 +                              "between %s and [%s].\n",
 +                              bad_hex, bad_hex, good_hex);
 +              } else {
 +                      fprintf(stderr, "The merge base %s is %s.\n"
 +                              "This means the first '%s' commit is "
 +                              "between %s and [%s].\n",
 +                              bad_hex, term_bad, term_good, bad_hex, good_hex);
 +              }
                exit(3);
        }
  
 -      fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n"
 +      fprintf(stderr, "Some %s revs are not ancestor of the %s rev.\n"
                "git bisect cannot work properly in this case.\n"
 -              "Maybe you mistake good and bad revs?\n");
 +              "Maybe you mistook %s and %s revs?\n",
 +              term_good, term_bad, term_good, term_bad);
        exit(1);
  }
  
@@@ -770,10 -758,10 +773,10 @@@ static void handle_skipped_merge_base(c
  
        warning("the merge base between %s and [%s] "
                "must be skipped.\n"
 -              "So we cannot be sure the first bad commit is "
 +              "So we cannot be sure the first %s commit is "
                "between %s and %s.\n"
                "We continue anyway.",
 -              bad_hex, good_hex, mb_hex, bad_hex);
 +              bad_hex, good_hex, term_bad, mb_hex, bad_hex);
        free(good_hex);
  }
  
@@@ -854,7 -842,7 +857,7 @@@ static void check_good_are_ancestors_of
        int fd;
  
        if (!current_bad_oid)
 -              die("a bad revision is needed");
 +              die("a %s revision is needed", term_bad);
  
        /* Check if file BISECT_ANCESTORS_OK exists. */
        if (!stat(filename, &st) && S_ISREG(st.st_mode))
@@@ -904,36 -892,6 +907,36 @@@ static void show_diff_tree(const char *
        log_tree_commit(&opt, commit);
  }
  
 +/*
 + * The terms used for this bisect session are stored in BISECT_TERMS.
 + * We read them and store them to adapt the messages accordingly.
 + * Default is bad/good.
 + */
 +void read_bisect_terms(const char **read_bad, const char **read_good)
 +{
 +      struct strbuf str = STRBUF_INIT;
 +      const char *filename = git_path("BISECT_TERMS");
 +      FILE *fp = fopen(filename, "r");
 +
 +      if (!fp) {
 +              if (errno == ENOENT) {
 +                      *read_bad = "bad";
 +                      *read_good = "good";
 +                      return;
 +              } else {
 +                      die("could not read file '%s': %s", filename,
 +                              strerror(errno));
 +              }
 +      } else {
 +              strbuf_getline(&str, fp, '\n');
 +              *read_bad = strbuf_detach(&str, NULL);
 +              strbuf_getline(&str, fp, '\n');
 +              *read_good = strbuf_detach(&str, NULL);
 +      }
 +      strbuf_release(&str);
 +      fclose(fp);
 +}
 +
  /*
   * We use the convention that exiting with an exit code 10 means that
   * the bisection process finished successfully.
@@@ -950,7 -908,6 +953,7 @@@ int bisect_next_all(const char *prefix
        const unsigned char *bisect_rev;
        char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
  
 +      read_bisect_terms(&term_bad, &term_good);
        if (read_bisect_refs())
                die("reading bisect refs failed");
  
                 */
                exit_if_skipped_commits(tried, NULL);
  
 -              printf("%s was both good and bad\n",
 -                     oid_to_hex(current_bad_oid));
 +              printf("%s was both %s and %s\n",
 +                     oid_to_hex(current_bad_oid),
 +                     term_good,
 +                     term_bad);
                exit(1);
        }
  
  
        if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
                exit_if_skipped_commits(tried, current_bad_oid);
 -              printf("%s is the first bad commit\n", bisect_rev_hex);
 +              printf("%s is the first %s commit\n", bisect_rev_hex,
 +                      term_bad);
                show_diff_tree(prefix, revs.commits->item);
                /* This means the bisection process succeeded. */
                exit(10);
diff --combined branch.c
index c85be0785b066380241aa66ff3d57155824fb226,e283683d9d2c5a587f6c3fc54256d67cc9c6c36b..3364adf1821b9a8b9bb0d81e8f1987f611eecdbc
+++ b/branch.c
@@@ -302,78 -302,11 +302,78 @@@ void create_branch(const char *head
  
  void remove_branch_state(void)
  {
-       unlink(git_path("CHERRY_PICK_HEAD"));
-       unlink(git_path("REVERT_HEAD"));
-       unlink(git_path("MERGE_HEAD"));
-       unlink(git_path("MERGE_RR"));
-       unlink(git_path("MERGE_MSG"));
-       unlink(git_path("MERGE_MODE"));
-       unlink(git_path("SQUASH_MSG"));
+       unlink(git_path_cherry_pick_head());
+       unlink(git_path_revert_head());
+       unlink(git_path_merge_head());
+       unlink(git_path_merge_rr());
+       unlink(git_path_merge_msg());
+       unlink(git_path_merge_mode());
+       unlink(git_path_squash_msg());
  }
 +
 +static void check_linked_checkout(const char *branch, const char *id)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      struct strbuf path = STRBUF_INIT;
 +      struct strbuf gitdir = STRBUF_INIT;
 +
 +      /*
 +       * $GIT_COMMON_DIR/HEAD is practically outside
 +       * $GIT_DIR so resolve_ref_unsafe() won't work (it
 +       * uses git_path). Parse the ref ourselves.
 +       */
 +      if (id)
 +              strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
 +      else
 +              strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
 +
 +      if (!strbuf_readlink(&sb, path.buf, 0)) {
 +              if (!starts_with(sb.buf, "refs/") ||
 +                  check_refname_format(sb.buf, 0))
 +                      goto done;
 +      } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&
 +          starts_with(sb.buf, "ref:")) {
 +              strbuf_remove(&sb, 0, strlen("ref:"));
 +              strbuf_trim(&sb);
 +      } else
 +              goto done;
 +      if (strcmp(sb.buf, branch))
 +              goto done;
 +      if (id) {
 +              strbuf_reset(&path);
 +              strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
 +              if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
 +                      goto done;
 +              strbuf_rtrim(&gitdir);
 +      } else
 +              strbuf_addstr(&gitdir, get_git_common_dir());
 +      skip_prefix(branch, "refs/heads/", &branch);
 +      strbuf_strip_suffix(&gitdir, ".git");
 +      die(_("'%s' is already checked out at '%s'"), branch, gitdir.buf);
 +done:
 +      strbuf_release(&path);
 +      strbuf_release(&sb);
 +      strbuf_release(&gitdir);
 +}
 +
 +void die_if_checked_out(const char *branch)
 +{
 +      struct strbuf path = STRBUF_INIT;
 +      DIR *dir;
 +      struct dirent *d;
 +
 +      check_linked_checkout(branch, NULL);
 +
 +      strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
 +      dir = opendir(path.buf);
 +      strbuf_release(&path);
 +      if (!dir)
 +              return;
 +
 +      while ((d = readdir(dir)) != NULL) {
 +              if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
 +                      continue;
 +              check_linked_checkout(branch, d->d_name);
 +      }
 +      closedir(dir);
 +}
diff --combined builtin/clone.c
index 5169746b64888b3f8cf9d4fabd6ea52a45fff4d3,5864ad15e192ceb3d0375d19399356b0256fd46f..578da85254418a620d3c7c847af8eed6a3a29d58
@@@ -99,125 -99,97 +99,140 @@@ static const char *argv_submodule[] = 
        "submodule", "update", "--init", "--recursive", NULL
  };
  
- static char *get_repo_path(const char *repo, int *is_bundle)
+ static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
  {
        static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
        static char *bundle_suffix[] = { ".bundle", "" };
+       size_t baselen = path->len;
        struct stat st;
        int i;
  
        for (i = 0; i < ARRAY_SIZE(suffix); i++) {
-               const char *path;
-               path = mkpath("%s%s", repo, suffix[i]);
-               if (stat(path, &st))
+               strbuf_setlen(path, baselen);
+               strbuf_addstr(path, suffix[i]);
+               if (stat(path->buf, &st))
                        continue;
-               if (S_ISDIR(st.st_mode) && is_git_directory(path)) {
+               if (S_ISDIR(st.st_mode) && is_git_directory(path->buf)) {
                        *is_bundle = 0;
-                       return xstrdup(absolute_path(path));
+                       return path->buf;
                } else if (S_ISREG(st.st_mode) && st.st_size > 8) {
                        /* Is it a "gitfile"? */
                        char signature[8];
-                       int len, fd = open(path, O_RDONLY);
+                       const char *dst;
+                       int len, fd = open(path->buf, O_RDONLY);
                        if (fd < 0)
                                continue;
                        len = read_in_full(fd, signature, 8);
                        close(fd);
                        if (len != 8 || strncmp(signature, "gitdir: ", 8))
                                continue;
-                       path = read_gitfile(path);
-                       if (path) {
+                       dst = read_gitfile(path->buf);
+                       if (dst) {
                                *is_bundle = 0;
-                               return xstrdup(absolute_path(path));
+                               return dst;
                        }
                }
        }
  
        for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
-               const char *path;
-               path = mkpath("%s%s", repo, bundle_suffix[i]);
-               if (!stat(path, &st) && S_ISREG(st.st_mode)) {
+               strbuf_setlen(path, baselen);
+               strbuf_addstr(path, bundle_suffix[i]);
+               if (!stat(path->buf, &st) && S_ISREG(st.st_mode)) {
                        *is_bundle = 1;
-                       return xstrdup(absolute_path(path));
+                       return path->buf;
                }
        }
  
        return NULL;
  }
  
+ static char *get_repo_path(const char *repo, int *is_bundle)
+ {
+       struct strbuf path = STRBUF_INIT;
+       const char *raw;
+       char *canon;
+       strbuf_addstr(&path, repo);
+       raw = get_repo_path_1(&path, is_bundle);
+       canon = raw ? xstrdup(absolute_path(raw)) : NULL;
+       strbuf_release(&path);
+       return canon;
+ }
  static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
  {
 -      const char *end = repo + strlen(repo), *start;
 +      const char *end = repo + strlen(repo), *start, *ptr;
        size_t len;
        char *dir;
  
 +      /*
 +       * Skip scheme.
 +       */
 +      start = strstr(repo, "://");
 +      if (start == NULL)
 +              start = repo;
 +      else
 +              start += 3;
 +
 +      /*
 +       * Skip authentication data. The stripping does happen
 +       * greedily, such that we strip up to the last '@' inside
 +       * the host part.
 +       */
 +      for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
 +              if (*ptr == '@')
 +                      start = ptr + 1;
 +      }
 +
        /*
         * Strip trailing spaces, slashes and /.git
         */
 -      while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
 +      while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
                end--;
 -      if (end - repo > 5 && is_dir_sep(end[-5]) &&
 +      if (end - start > 5 && is_dir_sep(end[-5]) &&
            !strncmp(end - 4, ".git", 4)) {
                end -= 5;
 -              while (repo < end && is_dir_sep(end[-1]))
 +              while (start < end && is_dir_sep(end[-1]))
                        end--;
        }
  
        /*
 -       * Find last component, but be prepared that repo could have
 -       * the form  "remote.example.com:foo.git", i.e. no slash
 -       * in the directory part.
 +       * Strip trailing port number if we've got only a
 +       * hostname (that is, there is no dir separator but a
 +       * colon). This check is required such that we do not
 +       * strip URI's like '/foo/bar:2222.git', which should
 +       * result in a dir '2222' being guessed due to backwards
 +       * compatibility.
 +       */
 +      if (memchr(start, '/', end - start) == NULL
 +          && memchr(start, ':', end - start) != NULL) {
 +              ptr = end;
 +              while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
 +                      ptr--;
 +              if (start < ptr && ptr[-1] == ':')
 +                      end = ptr - 1;
 +      }
 +
 +      /*
 +       * Find last component. To remain backwards compatible we
 +       * also regard colons as path separators, such that
 +       * cloning a repository 'foo:bar.git' would result in a
 +       * directory 'bar' being guessed.
         */
 -      start = end;
 -      while (repo < start && !is_dir_sep(start[-1]) && start[-1] != ':')
 -              start--;
 +      ptr = end;
 +      while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
 +              ptr--;
 +      start = ptr;
  
        /*
         * Strip .{bundle,git}.
         */
 -      strip_suffix(start, is_bundle ? ".bundle" : ".git" , &len);
 +      len = end - start;
 +      strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
 +
 +      if (!len || (len == 1 && *start == '/'))
 +          die("No directory name could be guessed.\n"
 +              "Please specify a directory on the command line");
  
        if (is_bare)
                dir = xstrfmt("%.*s.git", (int)len, start);
diff --combined builtin/fetch.c
index 6d743aa4eb76a25794284527b6d29f559358b071,df16d0a56e97c1ebbafd3498307901754b6daf64..d3a08545ad66b518a88ff94d21af4234391a986f
@@@ -591,7 -591,7 +591,7 @@@ static int store_updated_refs(const cha
        const char *what, *kind;
        struct ref *rm;
        char *url;
-       const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+       const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
        int want_status;
  
        fp = fopen(filename, "a");
@@@ -834,7 -834,7 +834,7 @@@ static void check_not_current_branch(st
  
  static int truncate_fetch_head(void)
  {
-       const char *filename = git_path("FETCH_HEAD");
+       const char *filename = git_path_fetch_head();
        FILE *fp = fopen(filename, "w");
  
        if (!fp)
@@@ -988,15 -988,17 +988,15 @@@ static int get_remote_group(const char 
  {
        struct remote_group_data *g = priv;
  
 -      if (starts_with(key, "remotes.") &&
 -                      !strcmp(key + 8, g->name)) {
 +      if (skip_prefix(key, "remotes.", &key) && !strcmp(key, g->name)) {
                /* split list by white space */
 -              int space = strcspn(value, " \t\n");
                while (*value) {
 -                      if (space > 1) {
 +                      size_t wordlen = strcspn(value, " \t\n");
 +
 +                      if (wordlen >= 1)
                                string_list_append(g->list,
 -                                                 xstrndup(value, space));
 -                      }
 -                      value += space + (value[space] != '\0');
 -                      space = strcspn(value, " \t\n");
 +                                                 xstrndup(value, wordlen));
 +                      value += wordlen + (value[wordlen] != '\0');
                }
        }
  
diff --combined cache.h
index 14f070ef3456403e36d6f005ab12a114b822893b,402521e34dec3b9e215d1aaf18cc9a387955995e..4e25271e596ae0b4929c74907c4eaf9003e8dfc6
+++ b/cache.h
@@@ -708,22 -708,59 +708,59 @@@ extern int check_repository_format(void
  #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
@@@ -945,7 -982,7 +982,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);
  
diff --combined dir.c
index 3881f55c71f1745f68a0dcaf5b439e3518b8909d,1ac8d5dd286837893af711037f718c01d3e62f10..c00c7e2b73603589ae1e3f49abcd931f4e761850
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -1078,9 -1078,10 +1078,9 @@@ static void prep_exclude(struct dir_str
                    (!untracked || !untracked->valid ||
                     /*
                      * .. and .gitignore does not exist before
 -                    * (i.e. null exclude_sha1 and skip_worktree is
 -                    * not set). Then we can skip loading .gitignore,
 -                    * which would result in ENOENT anyway.
 -                    * skip_worktree is taken care in read_directory()
 +                    * (i.e. null exclude_sha1). Then we can skip
 +                    * loading .gitignore, which would result in
 +                    * ENOENT anyway.
                      */
                     !is_null_sha1(untracked->exclude_sha1))) {
                        /*
@@@ -1879,6 -1880,7 +1879,6 @@@ static struct untracked_cache_dir *vali
                                                      const struct pathspec *pathspec)
  {
        struct untracked_cache_dir *root;
 -      int i;
  
        if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
                return NULL;
        if (dir->exclude_list_group[EXC_CMDL].nr)
                return NULL;
  
 -      /*
 -       * An optimization in prep_exclude() does not play well with
 -       * CE_SKIP_WORKTREE. It's a rare case anyway, if a single
 -       * entry has that bit set, disable the whole untracked cache.
 -       */
 -      for (i = 0; i < active_nr; i++)
 -              if (ce_skip_worktree(active_cache[i]))
 -                      return NULL;
 -
        if (!ident_in_untracked(dir->untracked)) {
                warning(_("Untracked cache is disabled on this system."));
                return NULL;
@@@ -2174,6 -2185,8 +2174,8 @@@ int remove_dir_recursively(struct strbu
        return remove_dir_recurse(path, flag, NULL);
  }
  
+ static GIT_PATH_FUNC(git_path_info_exclude, "info/exclude")
  void setup_standard_excludes(struct dir_struct *dir)
  {
        const char *path;
                                         dir->untracked ? &dir->ss_excludes_file : NULL);
  
        /* per repository user preference */
-       path = git_path("info/exclude");
+       path = git_path_info_exclude();
        if (!access_or_warn(path, R_OK, 0))
                add_excludes_from_file_1(dir, path,
                                         dir->untracked ? &dir->ss_info_exclude : NULL);
diff --combined fast-import.c
index 79d2bff2bf5c4164a54b3a898485e7e96767b3ac,ad8848bef11d7980c8e4f37282ab9e435c48372e..6c7c3c9b669eb272f4da530aef459c9c39c4d294
@@@ -407,7 -407,7 +407,7 @@@ static void dump_marks_helper(FILE *, u
  
  static void write_crash_report(const char *err)
  {
-       const char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+       char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
        FILE *rpt = fopen(loc, "w");
        struct branch *b;
        unsigned long lu;
  
        if (!rpt) {
                error("can't write crash report %s: %s", loc, strerror(errno));
+               free(loc);
                return;
        }
  
        fputs("-------------------\n", rpt);
        fputs("END OF CRASH REPORT\n", rpt);
        fclose(rpt);
+       free(loc);
  }
  
  static void end_packfile(void);
@@@ -923,12 -925,12 +925,12 @@@ static char *keep_pack(const char *curr
  
        snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
                 get_object_directory(), sha1_to_hex(pack_data->sha1));
 -      if (move_temp_to_file(pack_data->pack_name, name))
 +      if (finalize_object_file(pack_data->pack_name, name))
                die("cannot store pack file");
  
        snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
                 get_object_directory(), sha1_to_hex(pack_data->sha1));
 -      if (move_temp_to_file(curr_index_name, name))
 +      if (finalize_object_file(curr_index_name, name))
                die("cannot store index file");
        free((void *)curr_index_name);
        return name;
diff --combined refs.c
index 5f9831e66b7a3406748b8a6bd0685d6e9451bf61,84b1aff8421de069c0e89e5a145642c8efe5d412..835801905b597769aa5f3b5dd216075c263e6df5
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -1288,12 -1288,12 +1288,12 @@@ static void read_packed_refs(FILE *f, s
   */
  static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
  {
-       const char *packed_refs_file;
+       char *packed_refs_file;
  
        if (*refs->name)
-               packed_refs_file = git_path_submodule(refs->name, "packed-refs");
+               packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
        else
-               packed_refs_file = git_path("packed-refs");
+               packed_refs_file = git_pathdup("packed-refs");
  
        if (refs->packed &&
            !stat_validity_check(&refs->packed->validity, packed_refs_file))
                        fclose(f);
                }
        }
+       free(packed_refs_file);
        return refs->packed;
  }
  
@@@ -1351,19 -1352,23 +1352,23 @@@ static void read_loose_refs(const char 
  {
        struct ref_cache *refs = dir->ref_cache;
        DIR *d;
-       const char *path;
        struct dirent *de;
        int dirnamelen = strlen(dirname);
        struct strbuf refname;
+       struct strbuf path = STRBUF_INIT;
+       size_t path_baselen;
  
        if (*refs->name)
-               path = git_path_submodule(refs->name, "%s", dirname);
+               strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
        else
-               path = git_path("%s", dirname);
+               strbuf_git_path(&path, "%s", dirname);
+       path_baselen = path.len;
  
-       d = opendir(path);
-       if (!d)
+       d = opendir(path.buf);
+       if (!d) {
+               strbuf_release(&path);
                return;
+       }
  
        strbuf_init(&refname, dirnamelen + 257);
        strbuf_add(&refname, dirname, dirnamelen);
                unsigned char sha1[20];
                struct stat st;
                int flag;
-               const char *refdir;
  
                if (de->d_name[0] == '.')
                        continue;
                if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
-               refdir = *refs->name
-                       ? git_path_submodule(refs->name, "%s", refname.buf)
-                       : git_path("%s", refname.buf);
-               if (stat(refdir, &st) < 0) {
+               strbuf_addstr(&path, de->d_name);
+               if (stat(path.buf, &st) < 0) {
                        ; /* silently ignore */
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
                                         create_ref_entry(refname.buf, sha1, flag, 0));
                }
                strbuf_setlen(&refname, dirnamelen);
+               strbuf_setlen(&path, path_baselen);
        }
        strbuf_release(&refname);
+       strbuf_release(&path);
        closedir(d);
  }
  
@@@ -1481,14 -1485,15 +1485,15 @@@ static int resolve_gitlink_ref_recursiv
  {
        int fd, len;
        char buffer[128], *p;
-       const char *path;
+       char *path;
  
        if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
        path = *refs->name
-               ? git_path_submodule(refs->name, "%s", refname)
-               : git_path("%s", refname);
+               ? git_pathdup_submodule(refs->name, "%s", refname)
+               : git_pathdup("%s", refname);
        fd = open(path, O_RDONLY);
+       free(path);
        if (fd < 0)
                return resolve_gitlink_packed_ref(refs, refname, sha1);
  
@@@ -2285,25 -2290,14 +2290,14 @@@ static int verify_lock(struct ref_lock 
        return 0;
  }
  
- static int remove_empty_directories(const char *file)
+ static int remove_empty_directories(struct strbuf *path)
  {
-       /* we want to create a file but there is a directory there;
+       /*
+        * we want to create a file but there is a directory there;
         * if that is an empty directory (or a directory that contains
         * only empty directories), remove them.
         */
-       struct strbuf path;
-       int result, save_errno;
-       strbuf_init(&path, 20);
-       strbuf_addstr(&path, file);
-       result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
-       save_errno = errno;
-       strbuf_release(&path);
-       errno = save_errno;
-       return result;
+       return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
  }
  
  /*
@@@ -2403,7 -2397,8 +2397,8 @@@ static struct ref_lock *lock_ref_sha1_b
                                            unsigned int flags, int *type_p,
                                            struct strbuf *err)
  {
-       const char *ref_file;
+       struct strbuf ref_file = STRBUF_INIT;
+       struct strbuf orig_ref_file = STRBUF_INIT;
        const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
        refname = resolve_ref_unsafe(refname, resolve_flags,
                                     lock->old_oid.hash, &type);
        if (!refname && errno == EISDIR) {
-               /* we are trying to lock foo but we used to
+               /*
+                * we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
                 * it is normal for the empty directory 'foo'
                 * to remain.
                 */
-               ref_file = git_path("%s", orig_refname);
-               if (remove_empty_directories(ref_file)) {
+               strbuf_git_path(&orig_ref_file, "%s", orig_refname);
+               if (remove_empty_directories(&orig_ref_file)) {
                        last_errno = errno;
                        if (!verify_refname_available(orig_refname, extras, skip,
                                                      get_loose_refs(&ref_cache), err))
                                strbuf_addf(err, "there are still refs under '%s'",
                                            orig_refname);
                        goto error_return;
                }
                refname = resolve_ref_unsafe(orig_refname, resolve_flags,
        }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
-       ref_file = git_path("%s", refname);
+       strbuf_git_path(&ref_file, "%s", refname);
  
   retry:
-       switch (safe_create_leading_directories_const(ref_file)) {
+       switch (safe_create_leading_directories_const(ref_file.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
                /* fall through */
        default:
                last_errno = errno;
-               strbuf_addf(err, "unable to create directory for %s", ref_file);
+               strbuf_addf(err, "unable to create directory for %s",
+                           ref_file.buf);
                goto error_return;
        }
  
-       if (hold_lock_file_for_update(lock->lk, ref_file, lflags) < 0) {
+       if (hold_lock_file_for_update(lock->lk, ref_file.buf, lflags) < 0) {
                last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
                         */
                        goto retry;
                else {
-                       unable_to_lock_message(ref_file, errno, err);
+                       unable_to_lock_message(ref_file.buf, errno, err);
                        goto error_return;
                }
        }
                last_errno = errno;
                goto error_return;
        }
-       return lock;
+       goto out;
  
   error_return:
        unlock_ref(lock);
+       lock = NULL;
+  out:
+       strbuf_release(&ref_file);
+       strbuf_release(&orig_ref_file);
        errno = last_errno;
-       return NULL;
+       return lock;
  }
  
  /*
  static int rename_tmp_log(const char *newrefname)
  {
        int attempts_remaining = 4;
+       struct strbuf path = STRBUF_INIT;
+       int ret = -1;
  
   retry:
-       switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
+       strbuf_reset(&path);
+       strbuf_git_path(&path, "logs/%s", newrefname);
+       switch (safe_create_leading_directories_const(path.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
                /* fall through */
        default:
                error("unable to create directory for %s", newrefname);
-               return -1;
+               goto out;
        }
  
-       if (rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
+       if (rename(git_path(TMP_RENAMED_LOG), path.buf)) {
                if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
                        /*
                         * rename(a, b) when b is an existing
                         * directory ought to result in ISDIR, but
                         * Solaris 5.8 gives ENOTDIR.  Sheesh.
                         */
-                       if (remove_empty_directories(git_path("logs/%s", newrefname))) {
+                       if (remove_empty_directories(&path)) {
                                error("Directory not empty: logs/%s", newrefname);
-                               return -1;
+                               goto out;
                        }
                        goto retry;
                } else if (errno == ENOENT && --attempts_remaining > 0) {
                } else {
                        error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
                                newrefname, strerror(errno));
-                       return -1;
+                       goto out;
                }
        }
-       return 0;
+       ret = 0;
+ out:
+       strbuf_release(&path);
+       return ret;
  }
  
  static int rename_ref_available(const char *oldname, const char *newname)
@@@ -3028,7 -3035,14 +3035,14 @@@ int rename_ref(const char *oldrefname, 
        if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
            delete_ref(newrefname, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
-                       if (remove_empty_directories(git_path("%s", newrefname))) {
+                       struct strbuf path = STRBUF_INIT;
+                       int result;
+                       strbuf_git_path(&path, "%s", newrefname);
+                       result = remove_empty_directories(&path);
+                       strbuf_release(&path);
+                       if (result) {
                                error("Directory not empty: %s", newrefname);
                                goto rollback;
                        }
@@@ -3145,25 -3159,21 +3159,21 @@@ static int should_autocreate_reflog(con
   * should_autocreate_reflog returns non-zero.  Otherwise, create it
   * regardless of the ref name.  Fill in *err and return -1 on failure.
   */
- static int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct strbuf *err, int force_create)
+ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
  {
        int logfd, oflags = O_APPEND | O_WRONLY;
-       char *logfile;
  
-       strbuf_git_path(sb_logfile, "logs/%s", refname);
-       logfile = sb_logfile->buf;
-       /* make sure the rest of the function can't change "logfile" */
-       sb_logfile = NULL;
+       strbuf_git_path(logfile, "logs/%s", refname);
        if (force_create || should_autocreate_reflog(refname)) {
-               if (safe_create_leading_directories(logfile) < 0) {
+               if (safe_create_leading_directories(logfile->buf) < 0) {
                        strbuf_addf(err, "unable to create directory for %s: "
-                                   "%s", logfile, strerror(errno));
+                                   "%s", logfile->buf, strerror(errno));
                        return -1;
                }
                oflags |= O_CREAT;
        }
  
-       logfd = open(logfile, oflags, 0666);
+       logfd = open(logfile->buf, oflags, 0666);
        if (logfd < 0) {
                if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
                        return 0;
                if (errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
                                strbuf_addf(err, "There are still logs under "
-                                           "'%s'", logfile);
+                                           "'%s'", logfile->buf);
                                return -1;
                        }
-                       logfd = open(logfile, oflags, 0666);
+                       logfd = open(logfile->buf, oflags, 0666);
                }
  
                if (logfd < 0) {
                        strbuf_addf(err, "unable to append to %s: %s",
-                                   logfile, strerror(errno));
+                                   logfile->buf, strerror(errno));
                        return -1;
                }
        }
  
-       adjust_shared_perm(logfile);
+       adjust_shared_perm(logfile->buf);
        close(logfd);
        return 0;
  }
@@@ -3228,36 -3238,32 +3238,32 @@@ static int log_ref_write_fd(int fd, con
  
  static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
                           const unsigned char *new_sha1, const char *msg,
-                          struct strbuf *sb_log_file, int flags,
+                          struct strbuf *logfile, int flags,
                           struct strbuf *err)
  {
        int logfd, result, oflags = O_APPEND | O_WRONLY;
-       char *log_file;
  
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
  
-       result = log_ref_setup(refname, sb_log_file, err, flags & REF_FORCE_CREATE_REFLOG);
+       result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
  
        if (result)
                return result;
-       log_file = sb_log_file->buf;
-       /* make sure the rest of the function can't change "log_file" */
-       sb_log_file = NULL;
  
-       logfd = open(log_file, oflags);
+       logfd = open(logfile->buf, oflags);
        if (logfd < 0)
                return 0;
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
-               strbuf_addf(err, "unable to append to %s: %s", log_file,
+               strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
                            strerror(errno));
                close(logfd);
                return -1;
        }
        if (close(logfd)) {
-               strbuf_addf(err, "unable to append to %s: %s", log_file,
+               strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
                            strerror(errno));
                return -1;
        }
@@@ -3378,7 -3384,7 +3384,7 @@@ static int commit_ref_update(struct ref
  int create_symref(const char *ref_target, const char *refs_heads_master,
                  const char *logmsg)
  {
-       const char *lockpath;
+       char *lockpath = NULL;
        char ref[1000];
        int fd, len, written;
        char *git_HEAD = git_pathdup("%s", ref_target);
                error("refname too long: %s", refs_heads_master);
                goto error_free_return;
        }
-       lockpath = mkpath("%s.lock", git_HEAD);
+       lockpath = mkpathdup("%s.lock", git_HEAD);
        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
        if (fd < 0) {
                error("Unable to open %s for writing", lockpath);
        error_unlink_return:
                unlink_or_warn(lockpath);
        error_free_return:
+               free(lockpath);
                free(git_HEAD);
                return -1;
        }
+       free(lockpath);
  
  #ifndef NO_SYMLINK_HEAD
        done:
@@@ -4371,25 -4379,17 +4379,25 @@@ int parse_hide_refs_config(const char *
  
  int ref_is_hidden(const char *refname)
  {
 -      struct string_list_item *item;
 +      int i;
  
        if (!hide_refs)
                return 0;
 -      for_each_string_list_item(item, hide_refs) {
 +      for (i = hide_refs->nr - 1; i >= 0; i--) {
 +              const char *match = hide_refs->items[i].string;
 +              int neg = 0;
                int len;
 -              if (!starts_with(refname, item->string))
 +
 +              if (*match == '!') {
 +                      neg = 1;
 +                      match++;
 +              }
 +
 +              if (!starts_with(refname, match))
                        continue;
 -              len = strlen(item->string);
 +              len = strlen(match);
                if (!refname[len] || refname[len] == '/')
 -                      return 1;
 +                      return !neg;
        }
        return 0;
  }
diff --combined sha1_file.c
index 1081b95713c01e7421b8d672de5d9b3b7b9d5132,3400b8be4c51494d6c71c9a3c69c47fc156fcd3c..ea78ba7cf062c92fe78e67ff4c0f5a0607e6aebd
@@@ -404,13 -404,46 +404,46 @@@ void read_info_alternates(const char * 
  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)
@@@ -2908,8 -2941,11 +2941,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;
  
@@@ -3082,7 -3118,7 +3115,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)
diff --combined unpack-trees.c
index 261804e666b77b22d69227562c3ec03365202f53,7bb446a4afd5db0577d38ca84bf781a63e059c1c..bb09b1ed27a6c973c93f5ec855973e277279549f
@@@ -1029,10 -1029,12 +1029,12 @@@ int unpack_trees(unsigned len, struct t
        if (!core_apply_sparse_checkout || !o->update)
                o->skip_sparse_checkout = 1;
        if (!o->skip_sparse_checkout) {
-               if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, &el, 0) < 0)
+               char *sparse = git_pathdup("info/sparse-checkout");
+               if (add_excludes_from_file_to_list(sparse, "", 0, &el, 0) < 0)
                        o->skip_sparse_checkout = 1;
                else
                        o->el = &el;
+               free(sparse);
        }
  
        memset(&o->result, 0, sizeof(o->result));
        o->src_index = NULL;
        ret = check_updates(o) ? (-2) : 0;
        if (o->dst_index) {
 +              if (!ret) {
 +                      if (!o->result.cache_tree)
 +                              o->result.cache_tree = cache_tree();
 +                      if (!cache_tree_fully_valid(o->result.cache_tree))
 +                              cache_tree_update(&o->result,
 +                                                WRITE_TREE_SILENT |
 +                                                WRITE_TREE_REPAIR);
 +              }
                discard_index(o->dst_index);
                *o->dst_index = o->result;
        } else {