From: Johannes Schindelin <johannes.schindelin@gmx.de>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>, Jeff King <peff@peff.net>,
Duy Nguyen <pclouds@gmail.com>
Subject: [PATCH v2 3/9] setup_git_directory(): avoid changing global state during discovery
Date: Fri, 3 Mar 2017 03:04:11 +0100 (CET) [thread overview]
Message-ID: <49f66f2b723af38a58c17bc8ae413bcee69e2a2f.1488506615.git.johannes.schindelin@gmx.de> (raw)
In-Reply-To: <cover.1488506615.git.johannes.schindelin@gmx.de>
For historical reasons, Git searches for the .git/ directory (or the
.git file) by changing the working directory successively to the parent
directory of the current directory, until either anything was found or
until a ceiling or a mount point is hit.
Further global state may be changed, depending on the actual type of
discovery, e.g. the global variable `repository_format_precious_objects`
is set in the `check_repository_format_gently()` function (which is a
bit surprising, given the function name).
We do have a use case where we would like to find the .git/ directory
without having any global state touched, though: when we read the early
config e.g. for the pager or for alias expansion.
Let's just rename the function `setup_git_directory_gently_1()` to
`discover_git_directory()` and move all code that changes any global
state back into `setup_git_directory_gently()`.
In subsequent patches, we will export the `discover_git_directory()`
function and make use of it.
Note: the new loop is a *little* tricky, as we have to handle the root
directory specially: we cannot simply strip away the last component
including the slash, as the root directory only has that slash. To remedy
that, we introduce the `min_offset` variable that holds the minimal length
of an absolute path, and using that to special-case the root directory,
including an early exit before trying to find the parent of the root
directory.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
setup.c | 187 ++++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 112 insertions(+), 75 deletions(-)
diff --git a/setup.c b/setup.c
index 89a0cef9231..edac3c27dc1 100644
--- a/setup.c
+++ b/setup.c
@@ -816,50 +816,49 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
}
}
+enum discovery_result {
+ GIT_DIR_NONE = 0,
+ GIT_DIR_EXPLICIT,
+ GIT_DIR_DISCOVERED,
+ GIT_DIR_BARE,
+ /* these are errors */
+ GIT_DIR_HIT_CEILING = -1,
+ GIT_DIR_HIT_MOUNT_POINT = -2
+};
+
/*
* We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called.
+ *
+ * Also, we avoid changing any global state (such as the current working
+ * directory) to allow early callers.
+ *
+ * The directory where the search should start needs to be passed in via the
+ * `dir` parameter; upon return, the `dir` buffer will contain the path of
+ * the directory where the search ended, and `gitdir` will contain the path of
+ * the discovered .git/ directory, if any. This path may be relative against
+ * `dir` (i.e. *not* necessarily the cwd).
*/
-static const char *setup_git_directory_gently_1(int *nongit_ok)
+static enum discovery_result discover_git_directory(struct strbuf *dir,
+ struct strbuf *gitdir)
{
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
- static struct strbuf cwd = STRBUF_INIT;
- const char *gitdirenv, *ret;
- char *gitfile;
- int offset, offset_parent, ceil_offset = -1;
+ const char *gitdirenv;
+ int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1;
dev_t current_device = 0;
int one_filesystem = 1;
/*
- * We may have read an incomplete configuration before
- * setting-up the git directory. If so, clear the cache so
- * that the next queries to the configuration reload complete
- * configuration (including the per-repo config file that we
- * ignored previously).
- */
- git_config_clear();
-
- /*
- * Let's assume that we are in a git repository.
- * If it turns out later that we are somewhere else, the value will be
- * updated accordingly.
- */
- if (nongit_ok)
- *nongit_ok = 0;
-
- if (strbuf_getcwd(&cwd))
- die_errno(_("Unable to read current working directory"));
- offset = cwd.len;
-
- /*
* If GIT_DIR is set explicitly, we're not going
* to do any discovery, but we still do repository
* validation.
*/
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
- if (gitdirenv)
- return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok);
+ if (gitdirenv) {
+ strbuf_addstr(gitdir, gitdirenv);
+ return GIT_DIR_EXPLICIT;
+ }
if (env_ceiling_dirs) {
int empty_entry_found = 0;
@@ -867,15 +866,15 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
filter_string_list(&ceiling_dirs, 0,
canonicalize_ceiling_entry, &empty_entry_found);
- ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs);
+ ceil_offset = longest_ancestor_length(dir->buf, &ceiling_dirs);
string_list_clear(&ceiling_dirs, 0);
}
- if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf))
- ceil_offset = 1;
+ if (ceil_offset < 0)
+ ceil_offset = min_offset - 2;
/*
- * Test in the following order (relative to the cwd):
+ * Test in the following order (relative to the dir):
* - .git (file containing "gitdir: <path>")
* - .git/
* - ./ (bare)
@@ -887,62 +886,100 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
*/
one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
if (one_filesystem)
- current_device = get_device_or_die(".", NULL, 0);
+ current_device = get_device_or_die(dir->buf, NULL, 0);
for (;;) {
- gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
- if (gitfile)
- gitdirenv = gitfile = xstrdup(gitfile);
- else {
- if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
- gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
- }
+ int offset = dir->len;
+ if (offset > min_offset)
+ strbuf_addch(dir, '/');
+ strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
+ gitdirenv = read_gitfile(dir->buf);
+ if (!gitdirenv && is_git_directory(dir->buf))
+ gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
+ strbuf_setlen(dir, offset);
if (gitdirenv) {
- ret = setup_discovered_git_dir(gitdirenv,
- &cwd, offset,
- nongit_ok);
- free(gitfile);
- return ret;
+ strbuf_addstr(gitdir, gitdirenv);
+ return GIT_DIR_DISCOVERED;
}
- free(gitfile);
- if (is_git_directory("."))
- return setup_bare_git_dir(&cwd, offset, nongit_ok);
-
- offset_parent = offset;
- while (--offset_parent > ceil_offset &&
- !is_dir_sep(dir->buf[offset_parent]));
- if (offset_parent <= ceil_offset)
- return setup_nongit(cwd.buf, nongit_ok);
- if (one_filesystem) {
- dev_t parent_device = get_device_or_die("..", cwd.buf,
- offset);
- if (parent_device != current_device) {
- if (nongit_ok) {
- if (chdir(cwd.buf))
- die_errno(_("Cannot come back to cwd"));
- *nongit_ok = 1;
- return NULL;
- }
- strbuf_setlen(&cwd, offset);
- die(_("Not a git repository (or any parent up to mount point %s)\n"
- "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
- cwd.buf);
- }
- }
- if (chdir("..")) {
- strbuf_setlen(&cwd, offset);
- die_errno(_("Cannot change to '%s/..'"), cwd.buf);
+ if (is_git_directory(dir->buf)) {
+ strbuf_addstr(gitdir, ".");
+ return GIT_DIR_BARE;
}
- offset = offset_parent;
+
+ if (offset <= min_offset)
+ return GIT_DIR_HIT_CEILING;
+
+ while (--offset > ceil_offset && !is_dir_sep(dir->buf[offset]));
+ if (offset <= ceil_offset)
+ return GIT_DIR_HIT_CEILING;
+
+ strbuf_setlen(dir, offset > min_offset ? offset : min_offset);
+ if (one_filesystem &&
+ current_device != get_device_or_die(dir->buf, NULL, offset))
+ return GIT_DIR_HIT_MOUNT_POINT;
}
}
const char *setup_git_directory_gently(int *nongit_ok)
{
+ struct strbuf cwd = STRBUF_INIT, dir = STRBUF_INIT, gitdir = STRBUF_INIT;
const char *prefix;
- prefix = setup_git_directory_gently_1(nongit_ok);
+ /*
+ * We may have read an incomplete configuration before
+ * setting-up the git directory. If so, clear the cache so
+ * that the next queries to the configuration reload complete
+ * configuration (including the per-repo config file that we
+ * ignored previously).
+ */
+ git_config_clear();
+
+ /*
+ * Let's assume that we are in a git repository.
+ * If it turns out later that we are somewhere else, the value will be
+ * updated accordingly.
+ */
+ if (nongit_ok)
+ *nongit_ok = 0;
+
+ if (strbuf_getcwd(&cwd))
+ die_errno(_("Unable to read current working directory"));
+ strbuf_addbuf(&dir, &cwd);
+
+ switch (discover_git_directory(&dir, &gitdir)) {
+ case GIT_DIR_NONE:
+ prefix = NULL;
+ break;
+ case GIT_DIR_EXPLICIT:
+ prefix = setup_explicit_git_dir(gitdir.buf, &cwd, nongit_ok);
+ break;
+ case GIT_DIR_DISCOVERED:
+ if (dir.len < cwd.len && chdir(dir.buf))
+ die(_("Cannot change to '%s'"), dir.buf);
+ prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
+ nongit_ok);
+ break;
+ case GIT_DIR_BARE:
+ if (dir.len < cwd.len && chdir(dir.buf))
+ die(_("Cannot change to '%s'"), dir.buf);
+ prefix = setup_bare_git_dir(&cwd, dir.len, nongit_ok);
+ break;
+ case GIT_DIR_HIT_CEILING:
+ prefix = setup_nongit(cwd.buf, nongit_ok);
+ break;
+ case GIT_DIR_HIT_MOUNT_POINT:
+ if (nongit_ok) {
+ *nongit_ok = 1;
+ return NULL;
+ }
+ die(_("Not a git repository (or any parent up to mount point %s)\n"
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
+ dir.buf);
+ default:
+ die("BUG: unhandled discover_git_directory() result");
+ }
+
if (prefix)
setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
else
--
2.12.0.windows.1.3.g8a117c48243
next prev parent reply other threads:[~2017-03-03 2:04 UTC|newest]
Thread overview: 123+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-08 15:35 [PATCH/RFC 0/7] Pie-in-the-sky attempt to fix the early config Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 1/7] Make read_early_config() reusable Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 2/7] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 3/7] Mark builtins that create .git/ directories Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 4/7] read_early_config(): special-case `init` and `clone` Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 5/7] read_early_config(): really discover .git/ Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 6/7] WIP read_config_early(): respect ceiling directories Johannes Schindelin
2016-12-08 15:36 ` [PATCH/RFC 7/7] WIP: read_early_config(): add tests Johannes Schindelin
2016-12-08 17:26 ` [PATCH/RFC 0/7] Pie-in-the-sky attempt to fix the early config Jeff King
2016-12-09 17:28 ` Johannes Schindelin
2016-12-09 17:55 ` Jeff King
2016-12-09 12:42 ` Duy Nguyen
2016-12-09 16:52 ` Johannes Schindelin
2017-03-03 2:03 ` [PATCH v2 0/9] Fix " Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 1/9] t7006: replace dubious test Johannes Schindelin
2017-03-03 3:36 ` Jeff King
2017-03-03 11:10 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 2/9] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-03 3:37 ` Jeff King
2017-03-03 11:16 ` Johannes Schindelin
2017-03-03 11:26 ` Jeff King
2017-03-03 15:35 ` Johannes Schindelin
2017-03-03 2:04 ` Johannes Schindelin [this message]
2017-03-03 4:24 ` [PATCH v2 3/9] setup_git_directory(): avoid changing global state during discovery Jeff King
2017-03-03 13:54 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 4/9] Export the discover_git_directory() function Johannes Schindelin
2017-03-03 4:45 ` Jeff King
2017-03-03 14:49 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 5/9] Make read_early_config() reusable Johannes Schindelin
2017-03-03 4:46 ` Jeff King
2017-03-03 14:11 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 6/9] read_early_config(): special-case builtins that create a repository Johannes Schindelin
2017-03-03 4:51 ` Jeff King
2017-03-03 15:11 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 7/9] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-03 4:51 ` Jeff King
2017-03-03 2:04 ` [PATCH v2 8/9] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-03 5:06 ` Jeff King
2017-03-03 15:26 ` Johannes Schindelin
2017-03-03 2:04 ` [PATCH v2 9/9] Test read_early_config() Johannes Schindelin
2017-03-03 5:07 ` Jeff King
2017-03-03 15:04 ` Johannes Schindelin
2017-03-03 5:14 ` [PATCH v2 0/9] Fix the early config Jeff King
2017-03-03 15:31 ` Johannes Schindelin
2017-03-03 17:31 ` [PATCH v3 " Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 1/9] t7006: replace dubious test Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 2/9] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 3/9] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-03 17:32 ` [PATCH v3 4/9] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 5/9] Export the discover_git_directory() function Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 6/9] Make read_early_config() reusable Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 7/9] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 8/9] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-03 17:33 ` [PATCH v3 9/9] Test read_early_config() Johannes Schindelin
2017-03-03 21:35 ` [PATCH v3 0/9] Fix the early config Junio C Hamano
2017-03-07 11:55 ` Johannes Schindelin
2017-03-07 15:18 ` Johannes Schindelin
2017-03-04 7:39 ` Jeff King
2017-03-05 3:36 ` Junio C Hamano
2017-03-07 14:31 ` Johannes Schindelin
2017-03-08 7:30 ` Jeff King
2017-03-08 16:18 ` Johannes Schindelin
2017-03-08 16:29 ` Jeff King
2017-03-08 17:09 ` Junio C Hamano
2017-03-08 17:42 ` Jeff King
2017-03-08 22:43 ` Junio C Hamano
2017-03-09 11:51 ` Johannes Schindelin
2017-03-09 12:16 ` Jeff King
2017-03-10 16:39 ` Junio C Hamano
2017-03-07 14:32 ` [PATCH v4 00/10] " Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 01/10] t7006: replace dubious test Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 02/10] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 03/10] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-07 14:32 ` [PATCH v4 04/10] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-07 23:24 ` Junio C Hamano
2017-03-07 23:35 ` Brandon Williams
2017-03-08 0:57 ` Johannes Schindelin
2017-03-08 2:10 ` Brandon Williams
2017-03-07 14:33 ` [PATCH v4 05/10] Introduce the discover_git_directory() function Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 06/10] Make read_early_config() reusable Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 07/10] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 08/10] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 09/10] Test read_early_config() Johannes Schindelin
2017-03-07 14:33 ` [PATCH v4 10/10] setup_git_directory_gently_1(): avoid die()ing Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 00/11] Fix the early config Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 01/11] t7006: replace dubious test Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 02/11] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-09 22:23 ` [PATCH v5 03/11] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-09 22:24 ` [PATCH v5 04/11] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-10 19:34 ` Junio C Hamano
2017-03-09 22:24 ` [PATCH v5 05/11] Introduce the discover_git_directory() function Johannes Schindelin
2017-03-09 22:24 ` [PATCH v5 06/11] Make read_early_config() reusable Johannes Schindelin
2017-03-09 22:24 ` [PATCH v5 07/11] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-09 22:25 ` [PATCH v5 08/11] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-09 22:25 ` [PATCH v5 09/11] Test read_early_config() Johannes Schindelin
2017-03-10 19:02 ` Junio C Hamano
2017-03-13 17:19 ` Johannes Schindelin
2017-03-13 17:32 ` Junio C Hamano
2017-03-09 22:25 ` [PATCH v5 10/11] setup_git_directory_gently_1(): avoid die()ing Johannes Schindelin
2017-03-10 18:58 ` Junio C Hamano
2017-03-13 19:38 ` Johannes Schindelin
2017-03-13 19:47 ` Junio C Hamano
2017-03-13 20:20 ` Junio C Hamano
2017-03-13 21:46 ` Johannes Schindelin
2017-03-13 23:31 ` Junio C Hamano
2017-03-09 22:25 ` [PATCH v5 11/11] t1309: document cases where we would want early config not to die() Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 00/12] Fix the early config Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 01/12] t7006: replace dubious test Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 02/12] setup_git_directory(): use is_dir_sep() helper Johannes Schindelin
2017-03-13 20:09 ` [PATCH v6 03/12] Prepare setup_discovered_git_directory() the root directory Johannes Schindelin
2017-03-13 20:34 ` Junio C Hamano
2017-03-13 21:44 ` Johannes Schindelin
2017-03-13 20:10 ` [PATCH v6 04/12] setup_git_directory_1(): avoid changing global state Johannes Schindelin
2017-03-13 20:10 ` [PATCH v6 05/12] Introduce the discover_git_directory() function Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 06/12] Make read_early_config() reusable Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 07/12] read_early_config(): avoid .git/config hack when unneeded Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 08/12] read_early_config(): really discover .git/ Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 09/12] Add t1309 to test read_early_config() Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 10/12] setup_git_directory_gently_1(): avoid die()ing Johannes Schindelin
2017-03-13 20:11 ` [PATCH v6 11/12] t1309: document cases where we would want early config not to die() Johannes Schindelin
2017-03-13 20:12 ` [PATCH v6 12/12] setup.c: mention unresolved problems Johannes Schindelin
2017-03-13 22:31 ` [PATCH v6 00/12] Fix the early config Junio C Hamano
2017-03-14 18:01 ` Jeff King
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=49f66f2b723af38a58c17bc8ae413bcee69e2a2f.1488506615.git.johannes.schindelin@gmx.de \
--to=johannes.schindelin@gmx.de \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=pclouds@gmail.com \
--cc=peff@peff.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).