path: move `enter_repo()` into "setup.c"

The function `enter_repo()` is used to enter a repository at a given path. As such it sits way closer to setting up a repository than it does with handling paths, but regardless of that it's located in "path.c" instead of in "setup.c". Move the function into "setup.c". Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Nov 19, 2025 at 08:50 UTC 831e02340b9de46c9ea0a1bbce3894f390f5a45e
8 files changed +123 -118
builtin/receive-pack.c
+1 -1
@@ -34,7 +34,6 @@
34 #include "object-file.h"
35 #include "object-name.h"
36 #include "odb.h"
37 -#include "path.h"
37 #include "protocol.h"
38 #include "commit-reach.h"
39 #include "server-info.h"
@@ -42,6 +41,7 @@
41 #include "trace2.h"
42 #include "worktree.h"
43 #include "shallow.h"
44 +#include "setup.h"
45 #include "parse-options.h"
46
47 static const char * const receive_pack_usage[] = {
builtin/upload-archive.c
+1 -1
@@ -4,8 +4,8 @@
4 #define USE_THE_REPOSITORY_VARIABLE
5 #include "builtin.h"
6 #include "archive.h"
7 -#include "path.h"
7 #include "pkt-line.h"
8 +#include "setup.h"
9 #include "sideband.h"
10 #include "run-command.h"
11 #include "strvec.h"
builtin/upload-pack.c
+1 -1
@@ -5,11 +5,11 @@
5 #include "gettext.h"
6 #include "pkt-line.h"
7 #include "parse-options.h"
8 -#include "path.h"
8 #include "protocol.h"
9 #include "replace-object.h"
10 #include "upload-pack.h"
11 #include "serve.h"
12 +#include "setup.h"
13 #include "commit.h"
14 #include "environment.h"
15
http-backend.c
+1
@@ -16,6 +16,7 @@
16 #include "run-command.h"
17 #include "string-list.h"
18 #include "url.h"
19 +#include "setup.h"
20 #include "strvec.h"
21 #include "packfile.h"
22 #include "odb.h"
path.c
-100
@@ -738,106 +738,6 @@ return_null:
738 return NULL;
739 }
740
741 -/*
742 - * First, one directory to try is determined by the following algorithm.
743 - *
744 - * (0) If "strict" is given, the path is used as given and no DWIM is
745 - * done. Otherwise:
746 - * (1) "~/path" to mean path under the running user's home directory;
747 - * (2) "~user/path" to mean path under named user's home directory;
748 - * (3) "relative/path" to mean cwd relative directory; or
749 - * (4) "/absolute/path" to mean absolute directory.
750 - *
751 - * Unless "strict" is given, we check "%s/.git", "%s", "%s.git/.git", "%s.git"
752 - * in this order. We select the first one that is a valid git repository, and
753 - * chdir() to it. If none match, or we fail to chdir, we return NULL.
754 - *
755 - * If all goes well, we return the directory we used to chdir() (but
756 - * before ~user is expanded), avoiding getcwd() resolving symbolic
757 - * links. User relative paths are also returned as they are given,
758 - * except DWIM suffixing.
759 - */
760 -const char *enter_repo(const char *path, unsigned flags)
761 -{
762 - static struct strbuf validated_path = STRBUF_INIT;
763 - static struct strbuf used_path = STRBUF_INIT;
764 -
765 - if (!path)
766 - return NULL;
767 -
768 - if (!(flags & ENTER_REPO_STRICT)) {
769 - static const char *suffix[] = {
770 - "/.git", "", ".git/.git", ".git", NULL,
771 - };
772 - const char *gitfile;
773 - int len = strlen(path);
774 - int i;
775 - while ((1 < len) && (path[len-1] == '/'))
776 - len--;
777 -
778 - /*
779 - * We can handle arbitrary-sized buffers, but this remains as a
780 - * sanity check on untrusted input.
781 - */
782 - if (PATH_MAX <= len)
783 - return NULL;
784 -
785 - strbuf_reset(&used_path);
786 - strbuf_reset(&validated_path);
787 - strbuf_add(&used_path, path, len);
788 - strbuf_add(&validated_path, path, len);
789 -
790 - if (used_path.buf[0] == '~') {
791 - char *newpath = interpolate_path(used_path.buf, 0);
792 - if (!newpath)
793 - return NULL;
794 - strbuf_attach(&used_path, newpath, strlen(newpath),
795 - strlen(newpath));
796 - }
797 - for (i = 0; suffix[i]; i++) {
798 - struct stat st;
799 - size_t baselen = used_path.len;
800 - strbuf_addstr(&used_path, suffix[i]);
801 - if (!stat(used_path.buf, &st) &&
802 - (S_ISREG(st.st_mode) ||
803 - (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) {
804 - strbuf_addstr(&validated_path, suffix[i]);
805 - break;
806 - }
807 - strbuf_setlen(&used_path, baselen);
808 - }
809 - if (!suffix[i])
810 - return NULL;
811 - gitfile = read_gitfile(used_path.buf);
812 - if (!(flags & ENTER_REPO_ANY_OWNER_OK))
813 - die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
814 - if (gitfile) {
815 - strbuf_reset(&used_path);
816 - strbuf_addstr(&used_path, gitfile);
817 - }
818 - if (chdir(used_path.buf))
819 - return NULL;
820 - path = validated_path.buf;
821 - }
822 - else {
823 - const char *gitfile = read_gitfile(path);
824 - if (!(flags & ENTER_REPO_ANY_OWNER_OK))
825 - die_upon_dubious_ownership(gitfile, NULL, path);
826 - if (gitfile)
827 - path = gitfile;
828 - if (chdir(path))
829 - return NULL;
830 - }
831 -
832 - if (is_git_directory(".")) {
833 - set_git_dir(".", 0);
834 - check_repository_format(NULL);
835 - return path;
836 - }
837 -
838 - return NULL;
839 -}
840 -
741 int calc_shared_perm(struct repository *repo,
742 int mode)
743 {
path.h
-15
@@ -146,21 +146,6 @@ int adjust_shared_perm(struct repository *repo, const char *path);
146
147 char *interpolate_path(const char *path, int real_home);
148
149 -/* The bits are as follows:
150 - *
151 - * - ENTER_REPO_STRICT: callers that require exact paths (as opposed
152 - * to allowing known suffixes like ".git", ".git/.git" to be
153 - * omitted) can set this bit.
154 - *
155 - * - ENTER_REPO_ANY_OWNER_OK: callers that are willing to run without
156 - * ownership check can set this bit.
157 - */
158 -enum {
159 - ENTER_REPO_STRICT = (1<<0),
160 - ENTER_REPO_ANY_OWNER_OK = (1<<1),
161 -};
162 -
163 -const char *enter_repo(const char *path, unsigned flags);
149 const char *remove_leading_path(const char *in, const char *prefix);
150 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
151 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
setup.c
+81
@@ -1703,6 +1703,87 @@ void set_git_dir(const char *path, int make_realpath)
1703 strbuf_release(&realpath);
1704 }
1705
1706 +const char *enter_repo(const char *path, unsigned flags)
1707 +{
1708 + static struct strbuf validated_path = STRBUF_INIT;
1709 + static struct strbuf used_path = STRBUF_INIT;
1710 +
1711 + if (!path)
1712 + return NULL;
1713 +
1714 + if (!(flags & ENTER_REPO_STRICT)) {
1715 + static const char *suffix[] = {
1716 + "/.git", "", ".git/.git", ".git", NULL,
1717 + };
1718 + const char *gitfile;
1719 + int len = strlen(path);
1720 + int i;
1721 + while ((1 < len) && (path[len-1] == '/'))
1722 + len--;
1723 +
1724 + /*
1725 + * We can handle arbitrary-sized buffers, but this remains as a
1726 + * sanity check on untrusted input.
1727 + */
1728 + if (PATH_MAX <= len)
1729 + return NULL;
1730 +
1731 + strbuf_reset(&used_path);
1732 + strbuf_reset(&validated_path);
1733 + strbuf_add(&used_path, path, len);
1734 + strbuf_add(&validated_path, path, len);
1735 +
1736 + if (used_path.buf[0] == '~') {
1737 + char *newpath = interpolate_path(used_path.buf, 0);
1738 + if (!newpath)
1739 + return NULL;
1740 + strbuf_attach(&used_path, newpath, strlen(newpath),
1741 + strlen(newpath));
1742 + }
1743 + for (i = 0; suffix[i]; i++) {
1744 + struct stat st;
1745 + size_t baselen = used_path.len;
1746 + strbuf_addstr(&used_path, suffix[i]);
1747 + if (!stat(used_path.buf, &st) &&
1748 + (S_ISREG(st.st_mode) ||
1749 + (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) {
1750 + strbuf_addstr(&validated_path, suffix[i]);
1751 + break;
1752 + }
1753 + strbuf_setlen(&used_path, baselen);
1754 + }
1755 + if (!suffix[i])
1756 + return NULL;
1757 + gitfile = read_gitfile(used_path.buf);
1758 + if (!(flags & ENTER_REPO_ANY_OWNER_OK))
1759 + die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
1760 + if (gitfile) {
1761 + strbuf_reset(&used_path);
1762 + strbuf_addstr(&used_path, gitfile);
1763 + }
1764 + if (chdir(used_path.buf))
1765 + return NULL;
1766 + path = validated_path.buf;
1767 + }
1768 + else {
1769 + const char *gitfile = read_gitfile(path);
1770 + if (!(flags & ENTER_REPO_ANY_OWNER_OK))
1771 + die_upon_dubious_ownership(gitfile, NULL, path);
1772 + if (gitfile)
1773 + path = gitfile;
1774 + if (chdir(path))
1775 + return NULL;
1776 + }
1777 +
1778 + if (is_git_directory(".")) {
1779 + set_git_dir(".", 0);
1780 + check_repository_format(NULL);
1781 + return path;
1782 + }
1783 +
1784 + return NULL;
1785 +}
1786 +
1787 static int git_work_tree_initialized;
1788
1789 /*
setup.h
+38
@@ -97,6 +97,44 @@ static inline int discover_git_directory(struct strbuf *commondir,
97 void set_git_dir(const char *path, int make_realpath);
98 void set_git_work_tree(const char *tree);
99
100 +/* Flags that can be passed to `enter_repo()`. */
101 +enum {
102 + /*
103 + * Callers that require exact paths (as opposed to allowing known
104 + * suffixes like ".git", ".git/.git" to be omitted) can set this bit.
105 + */
106 + ENTER_REPO_STRICT = (1<<0),
107 +
108 + /*
109 + * Callers that are willing to run without ownership check can set this
110 + * bit.
111 + */
112 + ENTER_REPO_ANY_OWNER_OK = (1<<1),
113 +};
114 +
115 +/*
116 + * Discover and enter a repository.
117 + *
118 + * First, one directory to try is determined by the following algorithm.
119 + *
120 + * (0) If "strict" is given, the path is used as given and no DWIM is
121 + * done. Otherwise:
122 + * (1) "~/path" to mean path under the running user's home directory;
123 + * (2) "~user/path" to mean path under named user's home directory;
124 + * (3) "relative/path" to mean cwd relative directory; or
125 + * (4) "/absolute/path" to mean absolute directory.
126 + *
127 + * Unless "strict" is given, we check "%s/.git", "%s", "%s.git/.git", "%s.git"
128 + * in this order. We select the first one that is a valid git repository, and
129 + * chdir() to it. If none match, or we fail to chdir, we return NULL.
130 + *
131 + * If all goes well, we return the directory we used to chdir() (but
132 + * before ~user is expanded), avoiding getcwd() resolving symbolic
133 + * links. User relative paths are also returned as they are given,
134 + * except DWIM suffixing.
135 + */
136 +const char *enter_repo(const char *path, unsigned flags);
137 +
138 const char *setup_git_directory_gently(int *);
139 const char *setup_git_directory(void);
140 char *prefix_path(const char *prefix, int len, const char *path);