threads / patch / 22985

patch, 16 partsnd/setup part two

Subject: [PATCH 00/16] nd/setup part two

## tl;dr

20 messages between Mar 11, 2010 and Mar 20, 2010. Diffs are folded; open one to read it.

replies: 19people: 2as markdown or json

Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · lore

While the first part is more of preventing faults. This part fixes the remaining faults (or introduces more faults, who knows). I think I have got it to a readable/testable state.

On top of nd/setup, obviously.
Nguyễn Thái Ngọc Duy (16):
  Move enter_repo() to setup.c
  enter_repo(): initialize other variables as setup_git_directory_gently() does
  rev-parse --git-dir: print relative gitdir correctly
  worktree setup: call set_git_dir explicitly
  Add git_config_early()
    Preparation
  Use git_config_early() instead of git_config() during repo setup
  worktree setup: restore original state when things go wrong
  init/clone: turn on startup->have_repository properly
    Improve setup stuff
  git_config(): do not read .git/config if there is no repository
  Do not read .git/info/exclude if there is no repository
  Do not read .git/info/attributes if there is no repository
  apply: do not check sha1 when repository has not been found
  config: do not read .git/config if there is no repository
    Stop improper access to repo (and incorrectly set git_dir
    along the way)
  Allow to undo setup_git_directory_gently() gracefully (and fix alias code)
  alias: keep repository found while collecting aliases as long as possible
    Alias fix/improvement. I think I can add some tests for this.
  Guard unallowed access to repository when it's not set up
    The original patch [1] that has grown up to a 37-patch, 2-part series
[1] http://mid.gmane.org/1265370468-6147-1-git-send-email-pclouds@gmail.com
 attr.c                  |    5 +-
 builtin/apply.c         |    2 +-
 builtin/clone.c         |    3 +-
 builtin/config.c        |    9 ++-
 builtin/init-db.c       |   10 ++-
 builtin/rev-parse.c     |    8 ++
 cache.h                 |    6 ++-
 config.c                |   22 +++++--
 dir.c                   |    8 ++-
 environment.c           |   33 ++++++++-
 git.c                   |   22 ++++---
 path.c                  |   91 -----------------------
 setup.c                 |  184 ++++++++++++++++++++++++++++++++++++++++++++---
 t/t1300-repo-config.sh  |   14 ++++
 t/t1302-repo-version.sh |    2 +-
 t/t7002-grep.sh         |   24 ++++++
 16 files changed, 309 insertions(+), 134 deletions(-)
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 01/16] Move enter_repo() to setup.c

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 path.c  |   91 ---------------------------------------------------------------
 setup.c |   91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 91 deletions(-)
Show changes to 2 files +91 −91

path.c, setup.c

diff --git a/path.c b/path.c
index b4c8d91..f42eb1b 100644
--- a/path.c
+++ b/path.c
@@ -332,97 +332,6 @@ return_null:
 	return NULL;
 }
 
-/*
- * First, one directory to try is determined by the following algorithm.
- *
- * (0) If "strict" is given, the path is used as given and no DWIM is
- *     done. Otherwise:
- * (1) "~/path" to mean path under the running user's home directory;
- * (2) "~user/path" to mean path under named user's home directory;
- * (3) "relative/path" to mean cwd relative directory; or
- * (4) "/absolute/path" to mean absolute directory.
- *
- * Unless "strict" is given, we try access() for existence of "%s.git/.git",
- * "%s/.git", "%s.git", "%s" in this order.  The first one that exists is
- * what we try.
- *
- * Second, we try chdir() to that.  Upon failure, we return NULL.
- *
- * Then, we try if the current directory is a valid git repository.
- * Upon failure, we return NULL.
- *
- * If all goes well, we return the directory we used to chdir() (but
- * before ~user is expanded), avoiding getcwd() resolving symbolic
- * links.  User relative paths are also returned as they are given,
- * except DWIM suffixing.
- */
-char *enter_repo(char *path, int strict)
-{
-	static char used_path[PATH_MAX];
-	static char validated_path[PATH_MAX];
-
-	if (!path)
-		return NULL;
-
-	if (!strict) {
-		static const char *suffix[] = {
-			".git/.git", "/.git", ".git", "", NULL,
-		};
-		int len = strlen(path);
-		int i;
-		while ((1 < len) && (path[len-1] == '/')) {
-			path[len-1] = 0;
-			len--;
-		}
-		if (PATH_MAX <= len)
-			return NULL;
-		if (path[0] == '~') {
-			char *newpath = expand_user_path(path);
-			if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
-				free(newpath);
-				return NULL;
-			}
-			/*
-			 * Copy back into the static buffer. A pity
-			 * since newpath was not bounded, but other
-			 * branches of the if are limited by PATH_MAX
-			 * anyway.
-			 */
-			strcpy(used_path, newpath); free(newpath);
-			strcpy(validated_path, path);
-			path = used_path;
-		}
-		else if (PATH_MAX - 10 < len)
-			return NULL;
-		else {
-			path = strcpy(used_path, path);
-			strcpy(validated_path, path);
-		}
-		len = strlen(path);
-		for (i = 0; suffix[i]; i++) {
-			strcpy(path + len, suffix[i]);
-			if (!access(path, F_OK)) {
-				strcat(validated_path, suffix[i]);
-				break;
-			}
-		}
-		if (!suffix[i] || chdir(path))
-			return NULL;
-		path = validated_path;
-	}
-	else if (chdir(path))
-		return NULL;
-
-	if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
-	    validate_headref("HEAD") == 0) {
-		set_git_dir(".");
-		check_repository_format();
-		return path;
-	}
-
-	return NULL;
-}
-
 int set_shared_perm(const char *path, int mode)
 {
 	struct stat st;
diff --git a/setup.c b/setup.c
index 8796c6f..3019da2 100644
--- a/setup.c
+++ b/setup.c
@@ -452,6 +452,97 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	return prefix;
 }
 
+/*
+ * First, one directory to try is determined by the following algorithm.
+ *
+ * (0) If "strict" is given, the path is used as given and no DWIM is
+ *     done. Otherwise:
+ * (1) "~/path" to mean path under the running user's home directory;
+ * (2) "~user/path" to mean path under named user's home directory;
+ * (3) "relative/path" to mean cwd relative directory; or
+ * (4) "/absolute/path" to mean absolute directory.
+ *
+ * Unless "strict" is given, we try access() for existence of "%s.git/.git",
+ * "%s/.git", "%s.git", "%s" in this order.  The first one that exists is
+ * what we try.
+ *
+ * Second, we try chdir() to that.  Upon failure, we return NULL.
+ *
+ * Then, we try if the current directory is a valid git repository.
+ * Upon failure, we return NULL.
+ *
+ * If all goes well, we return the directory we used to chdir() (but
+ * before ~user is expanded), avoiding getcwd() resolving symbolic
+ * links.  User relative paths are also returned as they are given,
+ * except DWIM suffixing.
+ */
+char *enter_repo(char *path, int strict)
+{
+	static char used_path[PATH_MAX];
+	static char validated_path[PATH_MAX];
+
+	if (!path)
+		return NULL;
+
+	if (!strict) {
+		static const char *suffix[] = {
+			".git/.git", "/.git", ".git", "", NULL,
+		};
+		int len = strlen(path);
+		int i;
+		while ((1 < len) && (path[len-1] == '/')) {
+			path[len-1] = 0;
+			len--;
+		}
+		if (PATH_MAX <= len)
+			return NULL;
+		if (path[0] == '~') {
+			char *newpath = expand_user_path(path);
+			if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
+				free(newpath);
+				return NULL;
+			}
+			/*
+			 * Copy back into the static buffer. A pity
+			 * since newpath was not bounded, but other
+			 * branches of the if are limited by PATH_MAX
+			 * anyway.
+			 */
+			strcpy(used_path, newpath); free(newpath);
+			strcpy(validated_path, path);
+			path = used_path;
+		}
+		else if (PATH_MAX - 10 < len)
+			return NULL;
+		else {
+			path = strcpy(used_path, path);
+			strcpy(validated_path, path);
+		}
+		len = strlen(path);
+		for (i = 0; suffix[i]; i++) {
+			strcpy(path + len, suffix[i]);
+			if (!access(path, F_OK)) {
+				strcat(validated_path, suffix[i]);
+				break;
+			}
+		}
+		if (!suffix[i] || chdir(path))
+			return NULL;
+		path = validated_path;
+	}
+	else if (chdir(path))
+		return NULL;
+
+	if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
+	    validate_headref("HEAD") == 0) {
+		set_git_dir(".");
+		check_repository_format();
+		return path;
+	}
+
+	return NULL;
+}
+
 int git_config_perm(const char *var, const char *value)
 {
 	int i;
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 02/16] enter_repo(): initialize other variables as setup_git_directory_gently() does

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 setup.c |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)
Show changes to setup.c +7 −1
diff --git a/setup.c b/setup.c
index 3019da2..e067292 100644
--- a/setup.c
+++ b/setup.c
@@ -535,8 +535,14 @@ char *enter_repo(char *path, int strict)
 
 	if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
 	    validate_headref("HEAD") == 0) {
-		set_git_dir(".");
+		inside_work_tree = 0;
+		inside_git_dir = 1;
 		check_repository_format();
+		set_git_dir(".");
+		if (startup_info) {
+			startup_info->prefix = NULL;
+			startup_info->have_repository = 1;
+		}
 		return path;
 	}
 
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 03/16] rev-parse --git-dir: print relative gitdir correctly

When git_dir is relative, it is relative to Git's current working directory, which is worktree top directory. "git rev-parse --git-dir" is expected to output relative to user's current working directory.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/rev-parse.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)
Show changes to builtin/rev-parse.c +8 −0
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 8fbf9d0..8819e8a 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -646,6 +646,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				static char cwd[PATH_MAX];
 				int len;
 				if (gitdir) {
+					if (prefix && !is_absolute_path(gitdir)) {
+						int len;
+						if (!getcwd(cwd, PATH_MAX))
+							die_errno("unable to get current working directory");
+						len = strlen(cwd);
+						printf("%s%s%s\n", cwd, len && cwd[len-1] != '/' ? "/" : "", gitdir);
+						continue;
+					}
 					puts(gitdir);
 					continue;
 				}
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 04/16] worktree setup: call set_git_dir explicitly

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 setup.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)
Show changes to setup.c +8 −3
diff --git a/setup.c b/setup.c
index e067292..43a8609 100644
--- a/setup.c
+++ b/setup.c
@@ -350,14 +350,17 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 				/* config may override worktree */
 				if (check_repository_format_gently(nongit_ok))
 					return NULL;
+				set_git_dir(gitdirenv);
 				return retval;
 			}
 			if (check_repository_format_gently(nongit_ok))
 				return NULL;
 			retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
 					get_git_work_tree());
-			if (!retval || !*retval)
+			if (!retval || !*retval) {
+				set_git_dir(gitdirenv);
 				return NULL;
+			}
 			set_git_dir(make_absolute_path(gitdirenv));
 			if (chdir(work_tree_env) < 0)
 				die_errno ("Could not chdir to '%s'", work_tree_env);
@@ -392,8 +395,10 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 	offset = len = strlen(cwd);
 	for (;;) {
 		gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
-		if (gitfile_dir || is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) {
-			if (gitfile_dir && set_git_dir(gitfile_dir))
+		if (!gitfile_dir && is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
+			gitfile_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+		if (gitfile_dir) {
+			if (set_git_dir(gitfile_dir))
 				die("Repository setup failed");
 			inside_git_dir = 0;
 			if (!work_tree_env)
-- 
1.7.0.1.384.g6abcaa
Junio C Hamano· Mar 11, 2010, 21:24 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH 04/16] worktree setup: call set_git_dir explicitly

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
Show 41 quoted lines
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  setup.c |   11 ++++++++---
>  1 files changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/setup.c b/setup.c
> index e067292..43a8609 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -350,14 +350,17 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
>  				/* config may override worktree */
>  				if (check_repository_format_gently(nongit_ok))
>  					return NULL;
> +				set_git_dir(gitdirenv);
>  				return retval;
>  			}
>  			if (check_repository_format_gently(nongit_ok))
>  				return NULL;
>  			retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
>  					get_git_work_tree());
> -			if (!retval || !*retval)
> +			if (!retval || !*retval) {
> +				set_git_dir(gitdirenv);
>  				return NULL;
> +			}
>  			set_git_dir(make_absolute_path(gitdirenv));
>  			if (chdir(work_tree_env) < 0)
>  				die_errno ("Could not chdir to '%s'", work_tree_env);
> @@ -392,8 +395,10 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
>  	offset = len = strlen(cwd);
>  	for (;;) {
>  		gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
> -		if (gitfile_dir || is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) {
> -			if (gitfile_dir && set_git_dir(gitfile_dir))
> +		if (!gitfile_dir && is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
> +			gitfile_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
> +		if (gitfile_dir) {
> +			if (set_git_dir(gitfile_dir))
>  				die("Repository setup failed");
>  			inside_git_dir = 0;
>  			if (!work_tree_env)

Yes, you have more calls to set_git_dir() than before. But it is not explained why "calling set_git_dir explicitly" is a good thing anywhere in the series.

Can you write down the set of rules for your setup sequence, the goal after everything from this 37-patch series is applied? Something along the lines of (just illustrating the kinds of things to be described):

 - Definition.
   The following state variables belong to the setup system:
   - git_dir: holds the location of $GIT_DIR as a path relative to cwd
   - is_bare_repository(): returns foo;
   - is_inside_working_tree(): returns bar;
   - ...
 - Rule for the callers of the setup system:
   Once main() starts, this and that needs to be called in this order
   before trying to access any of the above state.  Specifically, the
   following call could access state variables, and should not be called
   before this set-up is done:
    - git_config(): needs to know where git_dir is;
    - setup_revisions(), parse_options(), ...: needs to know the prefix;
    - ...
 - Rule for the implementation of the setup system:
   Upon the first call the caller makes into the setup system:
   1. If the caller does not care about being in a git repository,
      skip everything up to step #n.
   2. First inspect GIT_DIR; if set, go to step #5.
   3. Otherwise try to find GIT_DIR by checking .git or "." is a git
      directory; repeat this step by checking one directory closer to the
      root level until GIT_DIR is found or CEILING is reached.
   4. If there is no GIT_DIR, then chdir back to the original location,
      and skip everything up to step #m.
   5. As a side effect of finding the GIT_DIR, the configuration file is
      read from there, and we will know the value of core.worktree.
   6. Inspect GIT_WORK_TREE; if set then git_work_tree is known.
      Otherwise, if core.worktree is found in step #5, that is the value
      of git_work_tree.  Otherwise determine git_work_tree in this
      fashon. ...
   ...

Without seeing such a design that you can clearly explain to others, the series looks nothing more than a sequence of "I noticed this so am applying this random patch" and is impossible to review.

Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 05/16] Add git_config_early()

This version of git_config() will be used during repository setup. As a repository is being set up, $GIT_DIR is not nailed down yet, git_pathdup() should not be used to get $GIT_DIR/config.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 cache.h  |    1 +
 config.c |   19 ++++++++++++++-----
 2 files changed, 15 insertions(+), 5 deletions(-)
Show changes to 2 files +15 −5

cache.h, config.c

diff --git a/cache.h b/cache.h
index 68412c0..3bd219c 100644
--- a/cache.h
+++ b/cache.h
@@ -933,6 +933,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
 extern int git_config(config_fn_t fn, void *);
+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_config_int(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
diff --git a/config.c b/config.c
index 6963fbe..2d38096 100644
--- a/config.c
+++ b/config.c
@@ -699,10 +699,9 @@ int git_config_global(void)
 	return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
 }
 
-int git_config(config_fn_t fn, void *data)
+int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
 	int ret = 0, found = 0;
-	char *repo_config = NULL;
 	const char *home = NULL;
 
 	/* Setting $GIT_CONFIG makes git read _only_ the given config file. */
@@ -724,17 +723,27 @@ int git_config(config_fn_t fn, void *data)
 		free(user_config);
 	}
 
-	repo_config = git_pathdup("config");
-	if (!access(repo_config, R_OK)) {
+	if (repo_config && !access(repo_config, R_OK)) {
 		ret += git_config_from_file(fn, repo_config, data);
 		found += 1;
 	}
-	free(repo_config);
 	if (found == 0)
 		return -1;
 	return ret;
 }
 
+int git_config(config_fn_t fn, void *data)
+{
+	char *repo_config = NULL;
+	int ret;
+
+	repo_config = git_pathdup("config");
+	ret = git_config_early(fn, data, repo_config);
+	if (repo_config)
+		free(repo_config);
+	return ret;
+}
+
 /*
  * Find all the stuff for git_config_set() below.
  */
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 06/16] Use git_config_early() instead of git_config() during repo setup

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 setup.c |   30 ++++++++++++++++++++++--------
 1 files changed, 22 insertions(+), 8 deletions(-)
Show changes to setup.c +22 −8
diff --git a/setup.c b/setup.c
index 43a8609..b8f88db 100644
--- a/setup.c
+++ b/setup.c
@@ -241,9 +241,21 @@ void setup_work_tree(void)
 	initialized = 1;
 }
 
-static int check_repository_format_gently(int *nongit_ok)
+static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 {
-	git_config(check_repository_format_version, NULL);
+	char repo_config[PATH_MAX+1];
+
+	/*
+	 * git_config() can't be used here because it calls git_pathdup()
+	 * to get $GIT_CONFIG/config. That call will make setup_git_env()
+	 * set git_dir to ".git".
+	 *
+	 * We are in gitdir setup, no git dir has been found useable yet.
+	 * Use a gentler version of git_config() to check if this repo
+	 * is a good one.
+	 */
+	snprintf(repo_config, PATH_MAX, "%s/config", gitdir);
+	git_config_early(check_repository_format_version, NULL, repo_config);
 	if (GIT_REPO_VERSION < repository_format_version) {
 		if (!nongit_ok)
 			die ("Expected git repo version <= %d, found %d",
@@ -348,12 +360,12 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 			if (!work_tree_env) {
 				retval = set_work_tree(gitdirenv);
 				/* config may override worktree */
-				if (check_repository_format_gently(nongit_ok))
+				if (check_repository_format_gently(gitdirenv, nongit_ok))
 					return NULL;
 				set_git_dir(gitdirenv);
 				return retval;
 			}
-			if (check_repository_format_gently(nongit_ok))
+			if (check_repository_format_gently(gitdirenv, nongit_ok))
 				return NULL;
 			retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
 					get_git_work_tree());
@@ -403,6 +415,8 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 			inside_git_dir = 0;
 			if (!work_tree_env)
 				inside_work_tree = 1;
+			if (check_repository_format_gently(gitfile_dir, nongit_ok))
+				return NULL;
 			root_len = offset_1st_component(cwd);
 			git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
 			break;
@@ -411,6 +425,8 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 			inside_git_dir = 1;
 			if (!work_tree_env)
 				inside_work_tree = 0;
+			if (check_repository_format_gently(gitfile_dir, nongit_ok))
+				return NULL;
 			if (offset != len) {
 				root_len = offset_1st_component(cwd);
 				cwd[offset > root_len ? offset : root_len] = '\0';
@@ -433,8 +449,6 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 			die_errno("Cannot change to '%s/..'", cwd);
 	}
 
-	if (check_repository_format_gently(nongit_ok))
-		return NULL;
 	if (offset == len)
 		return NULL;
 
@@ -542,7 +556,7 @@ char *enter_repo(char *path, int strict)
 	    validate_headref("HEAD") == 0) {
 		inside_work_tree = 0;
 		inside_git_dir = 1;
-		check_repository_format();
+		check_repository_format_gently(".", NULL);
 		set_git_dir(".");
 		if (startup_info) {
 			startup_info->prefix = NULL;
@@ -627,7 +641,7 @@ int check_repository_format_version(const char *var, const char *value, void *cb
 
 int check_repository_format(void)
 {
-	return check_repository_format_gently(NULL);
+	return check_repository_format_gently(get_git_dir(), NULL);
 }
 
 const char *setup_git_directory(void)
-- 
1.7.0.1.384.g6abcaa
Nguyen Thai Ngoc Duy· Mar 12, 2010, 03:35 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH 06/16] Use git_config_early() instead of git_config() during repo setup

2010/3/11 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:
Show 9 quoted lines
> @@ -411,6 +425,8 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
>                        inside_git_dir = 1;
>                        if (!work_tree_env)
>                                inside_work_tree = 0;
> +                       if (check_repository_format_gently(gitfile_dir, nongit_ok))
> +                               return NULL;
>                        if (offset != len) {
>                                root_len = offset_1st_component(cwd);
>                                cwd[offset > root_len ? offset : root_len] = '\0';

Something went wrong in rebases and conflicts. gitfile_dir in the above call should be replaced by "."

if (check_repository_format_gently(".", nongit_ok))
-- 
Duy
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 07/16] worktree setup: restore original state when things go wrong

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 cache.h |    1 +
 setup.c |   39 +++++++++++++++++++++++++++++++++++----
 2 files changed, 36 insertions(+), 4 deletions(-)
Show changes to 2 files +36 −4

cache.h, setup.c

diff --git a/cache.h b/cache.h
index 3bd219c..8e9d818 100644
--- a/cache.h
+++ b/cache.h
@@ -418,6 +418,7 @@ extern const char **get_pathspec(const char *prefix, const char **pathspec);
 extern void setup_work_tree(void);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
+extern void unset_git_directory(const char *prefix);
 extern const char *prefix_path(const char *prefix, int len, const char *path);
 extern const char *prefix_filename(const char *prefix, int len, const char *path);
 extern int check_filename(const char *prefix, const char *name);
diff --git a/setup.c b/setup.c
index b8f88db..2f850ab 100644
--- a/setup.c
+++ b/setup.c
@@ -323,6 +323,26 @@ const char *read_gitfile_gently(const char *path)
 	return path;
 }
 
+void unset_git_directory(const char *prefix)
+{
+	if (prefix && chdir(prefix))
+		die("Cannot change to '%s'", prefix);
+
+	if (startup_info) {
+		startup_info->prefix = NULL;
+		startup_info->have_repository = 0;
+	}
+
+	/* Initialized in setup_git_directory_gently_1() */
+	inside_work_tree = -1;
+	inside_git_dir = -1;
+
+	/* Initialized in check_repository_format_version() */
+	repository_format_version = 0xFF;
+	shared_repository = PERM_UMASK;
+	is_bare_repository_cfg = -1;
+	git_work_tree_cfg = NULL;
+}
 /*
  * 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.
@@ -403,6 +423,13 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 	 * - ../ (bare)
 	 * - ../../.git/
 	 *   etc.
+	 *
+	 * When a repository is found:
+	 * - inside_git_dir/inside_work_tree are set
+	 * - check_repository_format_gently() is called
+	 *   if repo version is not supported, restore cwd
+	 * - set_git_dir
+	 * - calculate and return prefix
 	 */
 	offset = len = strlen(cwd);
 	for (;;) {
@@ -410,13 +437,15 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 		if (!gitfile_dir && is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
 			gitfile_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 		if (gitfile_dir) {
-			if (set_git_dir(gitfile_dir))
-				die("Repository setup failed");
 			inside_git_dir = 0;
 			if (!work_tree_env)
 				inside_work_tree = 1;
-			if (check_repository_format_gently(gitfile_dir, nongit_ok))
+			if (check_repository_format_gently(gitfile_dir, nongit_ok)) {
+				unset_git_directory(offset != len ? cwd + offset + 1: NULL);
 				return NULL;
+			}
+			if (set_git_dir(gitfile_dir))
+				die("Repository setup failed");
 			root_len = offset_1st_component(cwd);
 			git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
 			break;
@@ -425,8 +454,10 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 			inside_git_dir = 1;
 			if (!work_tree_env)
 				inside_work_tree = 0;
-			if (check_repository_format_gently(gitfile_dir, nongit_ok))
+			if (check_repository_format_gently(gitfile_dir, nongit_ok)) {
+				unset_git_directory(offset != len ? cwd + offset + 1: NULL);
 				return NULL;
+			}
 			if (offset != len) {
 				root_len = offset_1st_component(cwd);
 				cwd[offset > root_len ? offset : root_len] = '\0';
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 08/16] init/clone: turn on startup->have_repository properly

With startup_info != NULL, many code path may be disabled, depending on repo setup. Also move set_git_dir() closer to have_repository assignment to make it clear about repo setup.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c   |    3 +--
 builtin/init-db.c |    9 +++++----
 cache.h           |    2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)
Show changes to 3 files +7 −7

builtin/clone.c, builtin/init-db.c, cache.h

diff --git a/builtin/clone.c b/builtin/clone.c
index 58bacbd..ab7a3c4 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -460,9 +460,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 	if (safe_create_leading_directories_const(git_dir) < 0)
 		die("could not create leading directories of '%s'", git_dir);
-	set_git_dir(make_absolute_path(git_dir));
 
-	init_db(option_template, option_quiet ? INIT_DB_QUIET : 0);
+	init_db(git_dir, option_template, option_quiet ? INIT_DB_QUIET : 0);
 
 	/*
 	 * At this point, the config exists, so we do not need the
diff --git a/builtin/init-db.c b/builtin/init-db.c
index edc40ff..064b919 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -294,12 +294,15 @@ static int create_default_files(const char *template_path)
 	return reinit;
 }
 
-int init_db(const char *template_dir, unsigned int flags)
+int init_db(const char *git_dir, const char *template_dir, unsigned int flags)
 {
 	const char *sha1_dir;
 	char *path;
 	int len, reinit;
 
+	set_git_dir(make_absolute_path(git_dir));
+	startup_info->have_repository = 1;
+
 	safe_create_dir(get_git_dir(), 0);
 
 	init_is_bare_repository = is_bare_repository();
@@ -509,7 +512,5 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 				   get_git_work_tree());
 	}
 
-	set_git_dir(make_absolute_path(git_dir));
-
-	return init_db(template_dir, flags);
+	return init_db(git_dir, template_dir, flags);
 }
diff --git a/cache.h b/cache.h
index 8e9d818..dd27e11 100644
--- a/cache.h
+++ b/cache.h
@@ -427,7 +427,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
 
 #define INIT_DB_QUIET 0x0001
 
-extern int init_db(const char *template_dir, unsigned int flags);
+extern int init_db(const char *git_dir, const char *template_dir, unsigned int flags);
 
 #define alloc_nr(x) (((x)+16)*3/2)
 
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 09/16] git_config(): do not read .git/config if there is no repository

If no repository is found, do not bother calling git_pathdup(). If a command forgets to call setup_git_directory*() or enter_repo(), $GIT_DIR/config will be missed, though.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 config.c               |    3 ++-
 t/t1300-repo-config.sh |   13 +++++++++++++
 2 files changed, 15 insertions(+), 1 deletions(-)
Show changes to 2 files +15 −1

config.c, t/t1300-repo-config.sh

diff --git a/config.c b/config.c
index 2d38096..07d854a 100644
--- a/config.c
+++ b/config.c
@@ -737,7 +737,8 @@ int git_config(config_fn_t fn, void *data)
 	char *repo_config = NULL;
 	int ret;
 
-	repo_config = git_pathdup("config");
+	if (!startup_info || startup_info->have_repository)
+		repo_config = git_pathdup("config");
 	ret = git_config_early(fn, data, repo_config);
 	if (repo_config)
 		free(repo_config);
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index f11f98c..cfb70e2 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -824,4 +824,17 @@ test_expect_success 'check split_cmdline return' "
 	test_must_fail git merge master
 	"
 
+test_expect_success 'skip .git/config if there is no repository' '
+	(
+		mkdir -p a/b/.git &&
+		cd a &&
+		GIT_CEILING_DIRECTORIES="`pwd`" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd b &&
+		echo "[core]" > .git/config &&
+		echo "wrong = true" >> .git/config &&
+		test -z "$(git var -l | grep core.wrong)"
+	)
+'
+
 test_done
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 10/16] Do not read .git/info/exclude if there is no repository

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 dir.c           |    8 +++++---
 t/t7002-grep.sh |   24 ++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)
Show changes to 2 files +29 −3

dir.c, t/t7002-grep.sh

diff --git a/dir.c b/dir.c
index 133c333..fee19fe 100644
--- a/dir.c
+++ b/dir.c
@@ -1024,9 +1024,11 @@ void setup_standard_excludes(struct dir_struct *dir)
 	const char *path;
 
 	dir->exclude_per_dir = ".gitignore";
-	path = git_path("info/exclude");
-	if (!access(path, R_OK))
-		add_excludes_from_file(dir, path);
+	if (!startup_info || startup_info->have_repository) {
+		path = git_path("info/exclude");
+		if (!access(path, R_OK))
+			add_excludes_from_file(dir, path);
+	}
 	if (excludes_file && !access(excludes_file, R_OK))
 		add_excludes_from_file(dir, excludes_file);
 }
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index e249c3e..06ec4cb 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -527,4 +527,28 @@ test_expect_success 'grep -e -- -- path' '
 	test_cmp expected actual
 '
 
+test_expect_success 'Setup fake .git' '
+	cd t &&
+	GIT_CEILING_DIRECTORIES="`pwd`" &&
+	export GIT_CEILING_DIRECTORIES &&
+	cd a &&
+	mkdir -p .git/info &&
+	cd ../..
+
+'
+
+test_expect_success 'Ignore fake .git/info/exclude' '
+	(
+		cd t/a &&
+		echo v > .git/info/exclude &&
+		git grep --no-index vvv . &&
+		rm .git/info/exclude
+	)
+'
+
+test_expect_success 'Unsetup fake .git' '
+	rm -rf t/a &&
+	unset GIT_CEILING_DIRECTORIES
+'
+
 test_done
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 11/16] Do not read .git/info/attributes if there is no repository

This one is difficult to demonstrate by tests. So I'll describe the call flow instead.

Say we do "git grep --no-index --show-function foo -- bar" in no
repository. The call flow would be:
 - ...
 - cmd_grep
 - grep_directory
 - grep_file
 - grep_buffer
 - grep_buffer_1
 - userdiff_find_by_path
 - git_checkattr("diff")
 - bootstrap_attr_stack
 - git_path("info/attributes")

Because no repository is found, git_dir in environment.c would be NULL. When it reaches git_path("info/attributes"), it will automatically set git_dir to ".git". If we have $(cwd)/.git/info/attributes then that file will be read, no matter $(cwd)/.git is a valid repository.

This bug is hard to be exposed, because after git_checkattr("diff") finds something (wrong). It will look up for diff drivers from .git/config. If we do thing correctly, .git/config will not be read, thus no diff drivers, no visible impact. If .git/config is read, git_dir must be set already, all this would not happen.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 attr.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
Show changes to attr.c +4 −1
diff --git a/attr.c b/attr.c
index f5346ed..f1362de 100644
--- a/attr.c
+++ b/attr.c
@@ -480,7 +480,10 @@ static void bootstrap_attr_stack(void)
 			debug_push(elem);
 		}
 
-		elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
+		if (!startup_info || startup_info->have_repository)
+			elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
+		else
+			elem = NULL;
 		if (!elem)
 			elem = xcalloc(1, sizeof(*elem));
 		elem->origin = NULL;
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 12/16] apply: do not check sha1 when repository has not been found

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/apply.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
Show changes to builtin/apply.c +1 −1
diff --git a/builtin/apply.c b/builtin/apply.c
index d27aac6..ea7bf57 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -2441,7 +2441,7 @@ static int apply_binary(struct image *img, struct patch *patch)
 		return 0; /* deletion patch */
 	}
 
-	if (has_sha1_file(sha1)) {
+	if (startup_info->have_repository && has_sha1_file(sha1)) {
 		/* We already have the postimage */
 		enum object_type type;
 		unsigned long size;
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 13/16] config: do not read .git/config if there is no repository

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/config.c        |    9 ++++++---
 t/t1300-repo-config.sh  |    3 ++-
 t/t1302-repo-version.sh |    2 +-
 3 files changed, 9 insertions(+), 5 deletions(-)
Show changes to 3 files +9 −5

builtin/config.c, t/t1300-repo-config.sh, t/t1302-repo-version.sh

diff --git a/builtin/config.c b/builtin/config.c
index ecc8f87..3fca3b4 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -159,7 +159,8 @@ static int get_value(const char *key_, const char *regex_)
 	local = config_exclusive_filename;
 	if (!local) {
 		const char *home = getenv("HOME");
-		local = repo_config = git_pathdup("config");
+		if (startup_info->have_repository)
+			local = repo_config = git_pathdup("config");
 		if (git_config_global() && home)
 			global = xstrdup(mkpath("%s/.gitconfig", home));
 		if (git_config_system())
@@ -197,7 +198,8 @@ static int get_value(const char *key_, const char *regex_)
 		git_config_from_file(show_config, system_wide, NULL);
 	if (do_all && global)
 		git_config_from_file(show_config, global, NULL);
-	git_config_from_file(show_config, local, NULL);
+	if (local)
+		git_config_from_file(show_config, local, NULL);
 	if (!do_all && !seen && global)
 		git_config_from_file(show_config, global, NULL);
 	if (!do_all && !seen && system_wide)
@@ -215,7 +217,8 @@ static int get_value(const char *key_, const char *regex_)
 		ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
 
 free_strings:
-	free(repo_config);
+	if (repo_config)
+		free(repo_config);
 	free(global);
 	return ret;
 }
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index cfb70e2..b040f72 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -833,7 +833,8 @@ test_expect_success 'skip .git/config if there is no repository' '
 		cd b &&
 		echo "[core]" > .git/config &&
 		echo "wrong = true" >> .git/config &&
-		test -z "$(git var -l | grep core.wrong)"
+		test -z "$(git var -l | grep core.wrong)" &&
+		test -z "$(git config --bool core.wrong)"
 	)
 '
 
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 8d305b4..74bf51f 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -29,7 +29,7 @@ test_expect_success 'gitdir selection on normal repos' '
 # Make sure it would stop at test2, not trash
 test_expect_success 'gitdir selection on unsupported repo' '
 	(cd test2 &&
-	test "$(git config core.repositoryformatversion)" = 99)'
+	test "$(git config --file=.git/config core.repositoryformatversion)" = 99)'
 
 test_expect_success 'gitdir not required mode' '
 	(git apply --stat test.patch &&
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 14/16] Allow to undo setup_git_directory_gently() gracefully (and fix alias code)

unset_git_directory() can only clean up things as long as set_git_dir() has not been called because set_git_dir() keeps internal state itself. Even worse, set_git_dir() may override $GIT_DIR env variable. All those are now handled by unset_git_env().

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 cache.h       |    1 +
 environment.c |   20 ++++++++++++++++++++
 git.c         |   11 +++++------
 setup.c       |    2 ++
 4 files changed, 28 insertions(+), 6 deletions(-)
Show changes to 4 files +28 −6

cache.h, environment.c, git.c, setup.c

diff --git a/cache.h b/cache.h
index dd27e11..bd9df24 100644
--- a/cache.h
+++ b/cache.h
@@ -419,6 +419,7 @@ extern void setup_work_tree(void);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
 extern void unset_git_directory(const char *prefix);
+extern void unset_git_env();
 extern const char *prefix_path(const char *prefix, int len, const char *path);
 extern const char *prefix_filename(const char *prefix, int len, const char *path);
 extern int check_filename(const char *prefix, const char *name);
diff --git a/environment.c b/environment.c
index c36c902..6127025 100644
--- a/environment.c
+++ b/environment.c
@@ -62,6 +62,7 @@ char *git_work_tree_cfg;
 static char *work_tree;
 
 static const char *git_dir;
+static const char *original_git_dir;
 static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 
 /*
@@ -81,6 +82,20 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
 	NULL
 };
 
+void unset_git_env(void)
+{
+	git_dir = NULL;
+	if (original_git_dir)
+		setenv(GIT_DIR_ENVIRONMENT, original_git_dir, 1);
+	else
+		unsetenv(GIT_DIR_ENVIRONMENT);
+	git_object_dir = NULL;
+	git_refs_dir = NULL;
+	git_index_file = NULL;
+	git_graft_file = NULL;
+	read_replace_refs = 1;
+}
+
 static void setup_git_env(void)
 {
 	git_dir = getenv(GIT_DIR_ENVIRONMENT);
@@ -184,6 +199,11 @@ char *get_graft_file(void)
 
 int set_git_dir(const char *path)
 {
+	static int original_git_dir_set = 0;
+	if (!original_git_dir_set) {
+		original_git_dir = getenv(GIT_DIR_ENVIRONMENT);
+		original_git_dir_set = 1;
+	}
 	if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
 		return error("Could not set GIT_DIR to '%s'", path);
 	setup_git_env();
diff --git a/git.c b/git.c
index 746470f..6a40304 100644
--- a/git.c
+++ b/git.c
@@ -146,14 +146,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 static int handle_alias(int *argcp, const char ***argv)
 {
 	int envchanged = 0, ret = 0, saved_errno = errno;
-	const char *subdir;
 	int count, option_count;
 	const char **new_argv;
 	const char *alias_command;
 	char *alias_string;
 	int unused_nongit;
 
-	subdir = setup_git_directory_gently(&unused_nongit);
+	setup_git_directory_gently(&unused_nongit);
 
 	alias_command = (*argv)[0];
 	alias_string = alias_lookup(alias_command);
@@ -210,8 +209,7 @@ static int handle_alias(int *argcp, const char ***argv)
 		ret = 1;
 	}
 
-	if (subdir && chdir(subdir))
-		die_errno("Cannot change to '%s'", subdir);
+	unset_git_directory(startup_info->prefix);
 
 	errno = saved_errno;
 
@@ -240,8 +238,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 	int status, help;
 	struct stat st;
 
-	memset(&git_startup_info, 0, sizeof(git_startup_info));
-	startup_info = &git_startup_info;
 	help = argc == 2 && !strcmp(argv[1], "-h");
 	if (!help) {
 		if (p->option & RUN_SETUP)
@@ -486,6 +482,9 @@ int main(int argc, const char **argv)
 {
 	const char *cmd;
 
+	memset(&git_startup_info, 0, sizeof(git_startup_info));
+	startup_info = &git_startup_info;
+
 	cmd = git_extract_argv0_path(argv[0]);
 	if (!cmd)
 		cmd = "git-help";
diff --git a/setup.c b/setup.c
index 2f850ab..3264187 100644
--- a/setup.c
+++ b/setup.c
@@ -329,6 +329,8 @@ void unset_git_directory(const char *prefix)
 		die("Cannot change to '%s'", prefix);
 
 	if (startup_info) {
+		if (startup_info->have_repository)
+			unset_git_env();
 		startup_info->prefix = NULL;
 		startup_info->have_repository = 0;
 	}
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 15/16] alias: keep repository found while collecting aliases as long as possible

In order to read aliases, $GIT_CONFIG/config must be read (if found). Currently, after alias handling is done, we chdir() back to original cwd, pretending no repository setup is done.

Given plain chdir() is not a proper way to undo setup_git_directory_gently(), and 80% builtin commands will need to search for a repository, we could keep the repository found in alias handling code. Until we are clear, there's no need for a repository, then we can undo the setup with unset_git_directory().

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 git.c |   15 ++++++++++-----
 1 files changed, 10 insertions(+), 5 deletions(-)
Show changes to git.c +10 −5
diff --git a/git.c b/git.c
index 6a40304..aa6d029 100644
--- a/git.c
+++ b/git.c
@@ -152,7 +152,8 @@ static int handle_alias(int *argcp, const char ***argv)
 	char *alias_string;
 	int unused_nongit;
 
-	setup_git_directory_gently(&unused_nongit);
+	if (!startup_info->have_repository)
+		setup_git_directory_gently(&unused_nongit);
 
 	alias_command = (*argv)[0];
 	alias_string = alias_lookup(alias_command);
@@ -209,8 +210,6 @@ static int handle_alias(int *argcp, const char ***argv)
 		ret = 1;
 	}
 
-	unset_git_directory(startup_info->prefix);
-
 	errno = saved_errno;
 
 	return ret;
@@ -240,12 +239,18 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
 	help = argc == 2 && !strcmp(argv[1], "-h");
 	if (!help) {
-		if (p->option & RUN_SETUP)
+		if ((p->option & RUN_SETUP) && !startup_info->have_repository)
 			setup_git_directory();
-		if (p->option & RUN_SETUP_GENTLY) {
+		else if ((p->option & RUN_SETUP_GENTLY) && !startup_info->have_repository) {
 			int nongit_ok;
 			setup_git_directory_gently(&nongit_ok);
 		}
+		else if (startup_info->have_repository) {
+			if (p->option & (RUN_SETUP_GENTLY | RUN_SETUP))
+				; /* done already */
+			else
+				unset_git_directory(startup_info->prefix);
+		}
 
 		if (use_pager == -1 && p->option & RUN_SETUP)
 			use_pager = check_pager_config(p->cmd);
-- 
1.7.0.1.384.g6abcaa
Nguyễn Thái Ngọc Duy· Mar 11, 2010, 13:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH 16/16] Guard unallowed access to repository when it's not set up

Many code path will skip repo access if startup_info->have_repository is false. This may be a fault if startup_info->have_repository has not been properly initialized.

So the rule is one of the following commands must be run before any repo access. And none of them can be called twice.

 - setup_git_directory*
 - enter_repo
 - init_db
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/init-db.c |    1 +
 cache.h           |    1 +
 config.c          |    2 ++
 environment.c     |   13 +++++++++++--
 setup.c           |   13 +++++++++++++
 5 files changed, 28 insertions(+), 2 deletions(-)
Show changes to 5 files +28 −2

builtin/init-db.c, cache.h, config.c, environment.c, setup.c

diff --git a/builtin/init-db.c b/builtin/init-db.c
index 064b919..d4c415c 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -302,6 +302,7 @@ int init_db(const char *git_dir, const char *template_dir, unsigned int flags)
 
 	set_git_dir(make_absolute_path(git_dir));
 	startup_info->have_repository = 1;
+	startup_info->have_run_setup_gitdir = 1;
 
 	safe_create_dir(get_git_dir(), 0);
 
diff --git a/cache.h b/cache.h
index bd9df24..1a6ae8c 100644
--- a/cache.h
+++ b/cache.h
@@ -1060,6 +1060,7 @@ int split_cmdline(char *cmdline, const char ***argv);
 /* git.c */
 struct startup_info {
 	const char *prefix;
+	int have_run_setup_gitdir;
 	int have_repository;
 };
 extern struct startup_info *startup_info;
diff --git a/config.c b/config.c
index 07d854a..9981b09 100644
--- a/config.c
+++ b/config.c
@@ -737,6 +737,8 @@ int git_config(config_fn_t fn, void *data)
 	char *repo_config = NULL;
 	int ret;
 
+	if (startup_info && !startup_info->have_run_setup_gitdir)
+		die("internal error: access to .git/config without repo setup");
 	if (!startup_info || startup_info->have_repository)
 		repo_config = git_pathdup("config");
 	ret = git_config_early(fn, data, repo_config);
diff --git a/environment.c b/environment.c
index 6127025..17f0cbe 100644
--- a/environment.c
+++ b/environment.c
@@ -98,9 +98,18 @@ void unset_git_env(void)
 
 static void setup_git_env(void)
 {
+	if (startup_info && startup_info->have_run_setup_gitdir)
+		die("internal error: setup_git_env can't be called twice");
 	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if (!git_dir)
-		git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+	if (!git_dir) {
+		/*
+		 * Repo detection should be done by setup_git_directory*
+		 * or enter_repo, not by this function
+		 */
+		 if (startup_info)
+			 die("internal error: $GIT_DIR is empty");
+		 git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+	}
 	if (!git_dir)
 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 	git_object_dir = getenv(DB_ENVIRONMENT);
diff --git a/setup.c b/setup.c
index 3264187..96af5e3 100644
--- a/setup.c
+++ b/setup.c
@@ -237,7 +237,17 @@ void setup_work_tree(void)
 		git_dir = make_absolute_path(git_dir);
 	if (!work_tree || chdir(work_tree))
 		die("This operation must be run in a work tree");
+
+	/*
+	 * have_run_setup_gitdir is unset in order to avoid die()ing
+	 * inside set_git_env(). We don't actually initialize
+	 * repo twice, we're just relative-izing gitdir
+	 */
+	if (startup_info)
+		startup_info->have_run_setup_gitdir = 0;
 	set_git_dir(make_relative_path(git_dir, work_tree));
+	if (startup_info)
+		startup_info->have_run_setup_gitdir = 1;
 	initialized = 1;
 }
 
@@ -333,6 +343,7 @@ void unset_git_directory(const char *prefix)
 			unset_git_env();
 		startup_info->prefix = NULL;
 		startup_info->have_repository = 0;
+		startup_info->have_run_setup_gitdir = 0;
 	}
 
 	/* Initialized in setup_git_directory_gently_1() */
@@ -499,6 +510,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	prefix = setup_git_directory_gently_1(nongit_ok);
 	if (startup_info) {
 		startup_info->prefix = prefix;
+		startup_info->have_run_setup_gitdir = 1;
 		startup_info->have_repository = !nongit_ok || !*nongit_ok;
 	}
 	return prefix;
@@ -593,6 +605,7 @@ char *enter_repo(char *path, int strict)
 		set_git_dir(".");
 		if (startup_info) {
 			startup_info->prefix = NULL;
+			startup_info->have_run_setup_gitdir = 1;
 			startup_info->have_repository = 1;
 		}
 		return path;
-- 
1.7.0.1.384.g6abcaa
Nguyen Thai Ngoc Duy· Mar 20, 2010, 08:10 UTC · lore

Re: [PATCH 04/16] worktree setup: call set_git_dir explicitly

Hmm.. I did not notice I did not sent this to git@vger.
On 3/12/10, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:
Show 129 quoted lines
> On 3/12/10, Junio C Hamano <gitster@pobox.com> wrote:
>  > Yes, you have more calls to set_git_dir() than before.  But it is not
>  >  explained why "calling set_git_dir explicitly" is a good thing anywhere in
>  >  the series.
>
>
> OK. Let me try in mail first.
>
>  Goal: do not rely on setup_git_env() to set git_dir to ".git". That
>  means set_git_dir() explicitly.
>
>
>  >   - Definition.
>  >
>  >    The following state variables belong to the setup system:
>  >
>  >    - git_dir: holds the location of $GIT_DIR as a path relative to cwd
>  >    - is_bare_repository(): returns foo;
>  >    - is_inside_working_tree(): returns bar;
>  >    - ...
>
>
> Best described in 7/16 and 14/16, function unset_git_directory() and
>  unset_git_env():
>
>   - git_dir and other git_*_dir in environment.c for object store path...
>   - shared_repository, is_bare_repository_cfg, git_work_tree_cfg,
>  repository_format:  initial configuration used during setup.
>   - is_inside_work_tree: whether cwd is inside a working directory
>   - is_inside_git_dir: whether cwd is inside $GIT_DIR
>   - startup_info->prefix: relative path from new cwd to original cwd
>  when main() is called
>   - startup_info->have_repository: whether a repository is found by setup system
>   - current working directory: directory base for git_dir, work_tree
>  and stuff if they are relative
>
>
>  >   - Rule for the callers of the setup system:
>
>
> Once main() starts. startup_info should be pointed to a struct. This
>  struct will be used by various part of libgit. If startup_info is
>  NULL, everything works like before.
>
>  Since startup_info initialization until calling setup functions,
>  access to repository will not be allowed, patch 16/16. "access to
>  repository" is everything that calls git_path() and friends.
>
>  One of the setup functions is called. These functions are
>  setup_git_directory*, enter_repo() or init_db(). Only one setup call
>  can be made for the rest of program's life time. unset_git_directory()
>  can be called to undo setup and allow setup functions to be called
>  once more. But it should be avoided.
>
>  If setup system fails to find a useable repository, all involved
>  states are restored. Refer to definition part for those states.
>
>  After setup function is called:
>
>   - git_path() and friends are allowed if startup_info->have_repository is true.
>
>   - Current directory directory may be moved. Programs need to be aware
>  of "prefix" (**) to calculate original cwd.
>
>   - Changing cwd is not allowed, as some directory settings may be
>  relative to cwd.
>
>   - The current working directory may or may not be at worktree's top
>  directory (*). If a command needs worktree, it must call
>  setup_work_tree() to move cwd (and adjust prefix along the way).
>  enter_repo() will never set worktree.
>
>  (*) setup_git_directory() does the setup_work_tree()-equivalent part
>  automatically. Users of setup_git_directory_gently() must call
>  setup_work_tree().
>
>  (**) The implicit rule of prefix is, it does not contain any "../". So
>  if original cwd is outside worktree, using this prefix alone is not
>  enough to get files from command line arguments.
>
>
>  >   - Rule for the implementation of the setup system:
>  >
>  >    Upon the first call the caller makes into the setup system:
>
>
> This is hard. Let's see. Prerequisites for entering setup system:
>
>   - No setup function has been called.
>
>  The main rule is, when a repo candidate is found, these must be done in order:
>   - setup is_inside_work_tree and is_inside_git_dir
>   - check_repo_format_gently(), if fails, call unset_git_dir and finish.
>   - set_git_dir()
>   - calculate prefix, then finish.
>
>  Setup procedure:
>
>   1. Check for GIT_DIR and GIT_WORK_TREE environment variables, if these are set:
>    1.1. if GIT_WORK_TREE is not set (only GIT_DIR is set), make cwd
>  GIT_WORK_TREE
>    1.2. adjust is_inside_work_tree and is_inside_git_dir accordingly
>    1.3. check repository config for repo format version, if
>  incompatible format is found, roll back using unset_git_directory and
>  return.
>    1.4. set_git_dir() to save GIT_DIR (this is irreversible, until patch 14/16)
>    1.5. calculate prefix, set startup_info->have_repository, finish.
>
>  2. Get current working directory, check whether $(cwd)/.git or $(cwd)
>  looks like a repository, going up until GIT_CEILING_DIRECTORIES (or /)
>  is reached, then finish.
>
>    2.1. If one dir looks like a repository:
>       2.1.1. adjust is_inside_work_tree and is_inside_git_dir accordingly
>       2.1.2. check repository config for repo format version, if
>  incompatible format is found, roll back using unset_git_directory and
>  return.
>       2.1.3. set_git_dir() to save GIT_DIR
>       2.1.4. calculate prefix, set ->have_repository, finish.
>
>    2.2. If no repository is found and GIT_CEILING_DIRECTORIES is
>  reached, moving back to original cwd, finish.
>
>  During setup procedure, normal access to repository is not allowed.
>  Though in theory, it could be allowed after set_git_dir() step.
>   --
>
> Duy
>
-- 
Duy

← back to recent threads