+ return (char *)cleanup_path(buf);
+}
+
+static int dir_prefix(const char *buf, const char *dir)
+{
+ int len = strlen(dir);
+ return !strncmp(buf, dir, len) &&
+ (is_dir_sep(buf[len]) || buf[len] == '\0');
+}
+
+/* $buf =~ m|$dir/+$file| but without regex */
+static int is_dir_file(const char *buf, const char *dir, const char *file)
+{
+ int len = strlen(dir);
+ if (strncmp(buf, dir, len) || !is_dir_sep(buf[len]))
+ return 0;
+ while (is_dir_sep(buf[len]))
+ len++;
+ return !strcmp(buf + len, file);
+}
+
+static void replace_dir(struct strbuf *buf, int len, const char *newdir)
+{
+ int newlen = strlen(newdir);
+ int need_sep = (buf->buf[len] && !is_dir_sep(buf->buf[len])) &&
+ !is_dir_sep(newdir[newlen - 1]);
+ if (need_sep)
+ len--; /* keep one char, to be replaced with '/' */
+ strbuf_splice(buf, 0, len, newdir, newlen);
+ if (need_sep)
+ buf->buf[newlen] = '/';
+}
+
+struct common_dir {
+ /* Not considered garbage for report_linked_checkout_garbage */
+ unsigned ignore_garbage:1;
+ unsigned is_dir:1;
+ /* Belongs to the common dir, though it may contain paths that don't */
+ unsigned is_common:1;
+ const char *path;
+};
+
+static struct common_dir common_list[] = {
+ { 0, 1, 1, "branches" },
+ { 0, 1, 1, "common" },
+ { 0, 1, 1, "hooks" },
+ { 0, 1, 1, "info" },
+ { 0, 0, 0, "info/sparse-checkout" },
+ { 1, 1, 1, "logs" },
+ { 1, 0, 0, "logs/HEAD" },
+ { 0, 1, 0, "logs/refs/bisect" },
+ { 0, 1, 0, "logs/refs/rewritten" },
+ { 0, 1, 0, "logs/refs/worktree" },
+ { 0, 1, 1, "lost-found" },
+ { 0, 1, 1, "objects" },
+ { 0, 1, 1, "refs" },
+ { 0, 1, 0, "refs/bisect" },
+ { 0, 1, 0, "refs/rewritten" },
+ { 0, 1, 0, "refs/worktree" },
+ { 0, 1, 1, "remotes" },
+ { 0, 1, 1, "worktrees" },
+ { 0, 1, 1, "rr-cache" },
+ { 0, 1, 1, "svn" },
+ { 0, 0, 1, "config" },
+ { 1, 0, 1, "gc.pid" },
+ { 0, 0, 1, "packed-refs" },
+ { 0, 0, 1, "shallow" },
+ { 0, 0, 0, NULL }
+};
+
+/*
+ * A compressed trie. A trie node consists of zero or more characters that
+ * are common to all elements with this prefix, optionally followed by some
+ * children. If value is not NULL, the trie node is a terminal node.
+ *
+ * For example, consider the following set of strings:
+ * abc
+ * def
+ * definite
+ * definition
+ *
+ * The trie would look like:
+ * root: len = 0, children a and d non-NULL, value = NULL.
+ * a: len = 2, contents = bc, value = (data for "abc")
+ * d: len = 2, contents = ef, children i non-NULL, value = (data for "def")
+ * i: len = 3, contents = nit, children e and i non-NULL, value = NULL
+ * e: len = 0, children all NULL, value = (data for "definite")
+ * i: len = 2, contents = on, children all NULL,
+ * value = (data for "definition")
+ */
+struct trie {
+ struct trie *children[256];
+ int len;
+ char *contents;
+ void *value;
+};
+
+static struct trie *make_trie_node(const char *key, void *value)
+{
+ struct trie *new_node = xcalloc(1, sizeof(*new_node));
+ new_node->len = strlen(key);
+ if (new_node->len) {
+ new_node->contents = xmalloc(new_node->len);
+ memcpy(new_node->contents, key, new_node->len);
+ }
+ new_node->value = value;
+ return new_node;
+}
+
+/*
+ * Add a key/value pair to a trie. The key is assumed to be \0-terminated.
+ * If there was an existing value for this key, return it.
+ */
+static void *add_to_trie(struct trie *root, const char *key, void *value)
+{
+ struct trie *child;
+ void *old;
+ int i;
+
+ if (!*key) {
+ /* we have reached the end of the key */
+ old = root->value;
+ root->value = value;
+ return old;
+ }
+
+ for (i = 0; i < root->len; i++) {
+ if (root->contents[i] == key[i])
+ continue;
+
+ /*
+ * Split this node: child will contain this node's
+ * existing children.
+ */
+ child = xmalloc(sizeof(*child));
+ memcpy(child->children, root->children, sizeof(root->children));
+
+ child->len = root->len - i - 1;
+ if (child->len) {
+ child->contents = xstrndup(root->contents + i + 1,
+ child->len);
+ }
+ child->value = root->value;
+ root->value = NULL;
+ root->len = i;
+
+ memset(root->children, 0, sizeof(root->children));
+ root->children[(unsigned char)root->contents[i]] = child;
+
+ /* This is the newly-added child. */
+ root->children[(unsigned char)key[i]] =
+ make_trie_node(key + i + 1, value);
+ return NULL;
+ }
+
+ /* We have matched the entire compressed section */
+ if (key[i]) {
+ child = root->children[(unsigned char)key[root->len]];
+ if (child) {
+ return add_to_trie(child, key + root->len + 1, value);
+ } else {
+ child = make_trie_node(key + root->len + 1, value);
+ root->children[(unsigned char)key[root->len]] = child;
+ return NULL;
+ }
+ }
+
+ old = root->value;
+ root->value = value;
+ return old;
+}
+
+typedef int (*match_fn)(const char *unmatched, void *value, void *baton);
+
+/*
+ * Search a trie for some key. Find the longest /-or-\0-terminated
+ * prefix of the key for which the trie contains a value. If there is
+ * no such prefix, return -1. Otherwise call fn with the unmatched
+ * portion of the key and the found value. If fn returns 0 or
+ * positive, then return its return value. If fn returns negative,
+ * then call fn with the next-longest /-terminated prefix of the key
+ * (i.e. a parent directory) for which the trie contains a value, and
+ * handle its return value the same way. If there is no shorter
+ * /-terminated prefix with a value left, then return the negative
+ * return value of the most recent fn invocation.
+ *
+ * The key is partially normalized: consecutive slashes are skipped.
+ *
+ * For example, consider the trie containing only [logs,
+ * logs/refs/bisect], both with values, but not logs/refs.
+ *
+ * | key | unmatched | prefix to node | return value |
+ * |--------------------|----------------|------------------|--------------|
+ * | a | not called | n/a | -1 |
+ * | logstore | not called | n/a | -1 |
+ * | logs | \0 | logs | as per fn |
+ * | logs/ | / | logs | as per fn |
+ * | logs/refs | /refs | logs | as per fn |
+ * | logs/refs/ | /refs/ | logs | as per fn |
+ * | logs/refs/b | /refs/b | logs | as per fn |
+ * | logs/refs/bisected | /refs/bisected | logs | as per fn |
+ * | logs/refs/bisect | \0 | logs/refs/bisect | as per fn |
+ * | logs/refs/bisect/ | / | logs/refs/bisect | as per fn |
+ * | logs/refs/bisect/a | /a | logs/refs/bisect | as per fn |
+ * | (If fn in the previous line returns -1, then fn is called once more:) |
+ * | logs/refs/bisect/a | /refs/bisect/a | logs | as per fn |
+ * |--------------------|----------------|------------------|--------------|
+ */
+static int trie_find(struct trie *root, const char *key, match_fn fn,
+ void *baton)
+{
+ int i;
+ int result;
+ struct trie *child;
+
+ if (!*key) {
+ /* we have reached the end of the key */
+ if (root->value && !root->len)
+ return fn(key, root->value, baton);
+ else
+ return -1;
+ }
+
+ for (i = 0; i < root->len; i++) {
+ /* Partial path normalization: skip consecutive slashes. */
+ if (key[i] == '/' && key[i+1] == '/') {
+ key++;
+ continue;
+ }
+ if (root->contents[i] != key[i])
+ return -1;
+ }
+
+ /* Matched the entire compressed section */
+ key += i;
+ if (!*key) {
+ /* End of key */
+ if (root->value)
+ return fn(key, root->value, baton);
+ else
+ return -1;
+ }
+
+ /* Partial path normalization: skip consecutive slashes */
+ while (key[0] == '/' && key[1] == '/')
+ key++;
+
+ child = root->children[(unsigned char)*key];
+ if (child)
+ result = trie_find(child, key + 1, fn, baton);
+ else
+ result = -1;
+
+ if (result >= 0 || (*key != '/' && *key != 0))
+ return result;
+ if (root->value)
+ return fn(key, root->value, baton);
+ else
+ return -1;
+}
+
+static struct trie common_trie;
+static int common_trie_done_setup;
+
+static void init_common_trie(void)
+{
+ struct common_dir *p;
+
+ if (common_trie_done_setup)
+ return;
+
+ for (p = common_list; p->path; p++)
+ add_to_trie(&common_trie, p->path, p);
+
+ common_trie_done_setup = 1;
+}
+
+/*
+ * Helper function for update_common_dir: returns 1 if the dir
+ * prefix is common.
+ */
+static int check_common(const char *unmatched, void *value, void *baton)
+{
+ struct common_dir *dir = value;
+
+ if (dir->is_dir && (unmatched[0] == 0 || unmatched[0] == '/'))
+ return dir->is_common;
+
+ if (!dir->is_dir && unmatched[0] == 0)
+ return dir->is_common;
+
+ return 0;
+}
+
+static void update_common_dir(struct strbuf *buf, int git_dir_len,
+ const char *common_dir)
+{
+ char *base = buf->buf + git_dir_len;
+ int has_lock_suffix = strbuf_strip_suffix(buf, LOCK_SUFFIX);
+
+ init_common_trie();
+ if (trie_find(&common_trie, base, check_common, NULL) > 0)
+ replace_dir(buf, git_dir_len, common_dir);
+
+ if (has_lock_suffix)
+ strbuf_addstr(buf, LOCK_SUFFIX);
+}
+
+void report_linked_checkout_garbage(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const struct common_dir *p;
+ int len;
+
+ if (!the_repository->different_commondir)
+ return;
+ strbuf_addf(&sb, "%s/", get_git_dir());
+ len = sb.len;
+ for (p = common_list; p->path; p++) {
+ const char *path = p->path;
+ if (p->ignore_garbage)
+ continue;
+ strbuf_setlen(&sb, len);
+ strbuf_addstr(&sb, path);
+ if (file_exists(sb.buf))
+ report_garbage(PACKDIR_FILE_GARBAGE, sb.buf);
+ }
+ strbuf_release(&sb);
+}
+
+static void adjust_git_path(const struct repository *repo,
+ struct strbuf *buf, int git_dir_len)
+{
+ const char *base = buf->buf + git_dir_len;
+ if (is_dir_file(base, "info", "grafts"))
+ strbuf_splice(buf, 0, buf->len,
+ repo->graft_file, strlen(repo->graft_file));
+ else if (!strcmp(base, "index"))
+ strbuf_splice(buf, 0, buf->len,
+ repo->index_file, strlen(repo->index_file));
+ else if (dir_prefix(base, "objects"))
+ replace_dir(buf, git_dir_len + 7, repo->objects->odb->path);
+ else if (git_hooks_path && dir_prefix(base, "hooks"))
+ replace_dir(buf, git_dir_len + 5, git_hooks_path);
+ else if (repo->different_commondir)
+ update_common_dir(buf, git_dir_len, repo->commondir);
+}
+
+static void strbuf_worktree_gitdir(struct strbuf *buf,
+ const struct repository *repo,
+ const struct worktree *wt)
+{
+ if (!wt)
+ strbuf_addstr(buf, repo->gitdir);
+ else if (!wt->id)
+ strbuf_addstr(buf, repo->commondir);
+ else
+ strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);