git_path(): be aware of file relocation in $GIT_DIR
We allow the user to relocate certain paths out of $GIT_DIR via
environment variables, e.g. GIT_OBJECT_DIRECTORY, GIT_INDEX_FILE and
GIT_GRAFT_FILE. Callers are not supposed to use git_path() or
git_pathdup() to get those paths. Instead they must use
get_object_directory(), get_index_file() and get_graft_file()
respectively. This is inconvenient and could be missed in review (for
example, there's git_path("objects/info/alternates") somewhere in
sha1_file.c).
This patch makes git_path() and git_pathdup() understand those
environment variables. So if you set GIT_OBJECT_DIRECTORY to /foo/bar,
git_path("objects/abc") should return /foo/bar/abc. The same is done
for the two remaining env variables.
"git rev-parse --git-path" is the wrapper for script use.
This patch kinda reverts a0279e1 (setup_git_env: use git_pathdup
instead of xmalloc + sprintf - 2014-06-19) because using git_pathdup
here would result in infinite recursion:
  setup_git_env() -> git_pathdup("objects") -> .. -> adjust_git_path()
  -> get_object_directory() -> oops, git_object_directory is NOT set
  yet -> setup_git_env()
I wanted to make git_pathdup_literal() that skips adjust_git_path().
But that won't work because later on when $GIT_COMMON_DIR is
introduced, git_pathdup_literal("objects") needs adjust_git_path() to
replace $GIT_DIR with $GIT_COMMON_DIR.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
			
			
				maint
			
			
		
							parent
							
								
									57a23b770a
								
							
						
					
					
						commit
						557bd833bb
					
				|  | @ -233,6 +233,13 @@ print a message to stderr and exit with nonzero status. | ||||||
| 	repository.  If <path> is a gitfile then the resolved path | 	repository.  If <path> is a gitfile then the resolved path | ||||||
| 	to the real repository is printed. | 	to the real repository is printed. | ||||||
|  |  | ||||||
|  | --git-path <path>:: | ||||||
|  | 	Resolve "$GIT_DIR/<path>" and takes other path relocation | ||||||
|  | 	variables such as $GIT_OBJECT_DIRECTORY, | ||||||
|  | 	$GIT_INDEX_FILE... into account. For example, if | ||||||
|  | 	$GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse | ||||||
|  | 	--git-path objects/abc" returns /foo/bar/abc. | ||||||
|  |  | ||||||
| --show-cdup:: | --show-cdup:: | ||||||
| 	When the command is invoked from a subdirectory, show the | 	When the command is invoked from a subdirectory, show the | ||||||
| 	path of the top-level directory relative to the current | 	path of the top-level directory relative to the current | ||||||
|  |  | ||||||
|  | @ -533,6 +533,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) | ||||||
| 	for (i = 1; i < argc; i++) { | 	for (i = 1; i < argc; i++) { | ||||||
| 		const char *arg = argv[i]; | 		const char *arg = argv[i]; | ||||||
|  |  | ||||||
|  | 		if (!strcmp(arg, "--git-path")) { | ||||||
|  | 			if (!argv[i + 1]) | ||||||
|  | 				die("--git-path requires an argument"); | ||||||
|  | 			puts(git_path("%s", argv[i + 1])); | ||||||
|  | 			i++; | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
| 		if (as_is) { | 		if (as_is) { | ||||||
| 			if (show_file(arg, output_prefix) && as_is < 2) | 			if (show_file(arg, output_prefix) && as_is < 2) | ||||||
| 				verify_filename(prefix, arg, 0); | 				verify_filename(prefix, arg, 0); | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								cache.h
								
								
								
								
							
							
						
						
									
										1
									
								
								cache.h
								
								
								
								
							|  | @ -617,6 +617,7 @@ extern int fsync_object_files; | ||||||
| extern int core_preload_index; | extern int core_preload_index; | ||||||
| extern int core_apply_sparse_checkout; | extern int core_apply_sparse_checkout; | ||||||
| extern int precomposed_unicode; | extern int precomposed_unicode; | ||||||
|  | extern int git_db_env, git_index_env, git_graft_env; | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * The character that begins a commented line in user-editable file |  * The character that begins a commented line in user-editable file | ||||||
|  |  | ||||||
|  | @ -83,6 +83,7 @@ static size_t namespace_len; | ||||||
|  |  | ||||||
| static const char *git_dir; | static const char *git_dir; | ||||||
| static char *git_object_dir, *git_index_file, *git_graft_file; | static char *git_object_dir, *git_index_file, *git_graft_file; | ||||||
|  | int git_db_env, git_index_env, git_graft_env; | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Repository-local GIT_* environment variables; see cache.h for details. |  * Repository-local GIT_* environment variables; see cache.h for details. | ||||||
|  | @ -124,10 +125,18 @@ static char *expand_namespace(const char *raw_namespace) | ||||||
| 	return strbuf_detach(&buf, NULL); | 	return strbuf_detach(&buf, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static char *git_path_from_env(const char *envvar, const char *path) | static char *git_path_from_env(const char *envvar, const char *path, | ||||||
|  | 			       int *fromenv) | ||||||
| { | { | ||||||
| 	const char *value = getenv(envvar); | 	const char *value = getenv(envvar); | ||||||
| 	return value ? xstrdup(value) : git_pathdup("%s", path); | 	if (!value) { | ||||||
|  | 		char *buf = xmalloc(strlen(git_dir) + strlen(path) + 2); | ||||||
|  | 		sprintf(buf, "%s/%s", git_dir, path); | ||||||
|  | 		return buf; | ||||||
|  | 	} | ||||||
|  | 	if (fromenv) | ||||||
|  | 		*fromenv = 1; | ||||||
|  | 	return xstrdup(value); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void setup_git_env(void) | static void setup_git_env(void) | ||||||
|  | @ -140,9 +149,9 @@ static void setup_git_env(void) | ||||||
| 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; | 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; | ||||||
| 	gitfile = read_gitfile(git_dir); | 	gitfile = read_gitfile(git_dir); | ||||||
| 	git_dir = xstrdup(gitfile ? gitfile : git_dir); | 	git_dir = xstrdup(gitfile ? gitfile : git_dir); | ||||||
| 	git_object_dir = git_path_from_env(DB_ENVIRONMENT, "objects"); | 	git_object_dir = git_path_from_env(DB_ENVIRONMENT, "objects", &git_db_env); | ||||||
| 	git_index_file = git_path_from_env(INDEX_ENVIRONMENT, "index"); | 	git_index_file = git_path_from_env(INDEX_ENVIRONMENT, "index", &git_index_env); | ||||||
| 	git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, "info/grafts"); | 	git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, "info/grafts", &git_graft_env); | ||||||
| 	if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) | 	if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) | ||||||
| 		check_replace_refs = 0; | 		check_replace_refs = 0; | ||||||
| 	namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT)); | 	namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT)); | ||||||
|  |  | ||||||
							
								
								
									
										49
									
								
								path.c
								
								
								
								
							
							
						
						
									
										49
									
								
								path.c
								
								
								
								
							|  | @ -60,13 +60,58 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...) | ||||||
| 	return cleanup_path(buf); | 	return 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] = '/'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void adjust_git_path(struct strbuf *buf, int git_dir_len) | ||||||
|  | { | ||||||
|  | 	const char *base = buf->buf + git_dir_len; | ||||||
|  | 	if (git_graft_env && is_dir_file(base, "info", "grafts")) | ||||||
|  | 		strbuf_splice(buf, 0, buf->len, | ||||||
|  | 			      get_graft_file(), strlen(get_graft_file())); | ||||||
|  | 	else if (git_index_env && !strcmp(base, "index")) | ||||||
|  | 		strbuf_splice(buf, 0, buf->len, | ||||||
|  | 			      get_index_file(), strlen(get_index_file())); | ||||||
|  | 	else if (git_db_env && dir_prefix(base, "objects")) | ||||||
|  | 		replace_dir(buf, git_dir_len + 7, get_object_directory()); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void do_git_path(struct strbuf *buf, const char *fmt, va_list args) | static void do_git_path(struct strbuf *buf, const char *fmt, va_list args) | ||||||
| { | { | ||||||
| 	const char *git_dir = get_git_dir(); | 	int gitdir_len; | ||||||
| 	strbuf_addstr(buf, git_dir); | 	strbuf_addstr(buf, get_git_dir()); | ||||||
| 	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1])) | 	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1])) | ||||||
| 		strbuf_addch(buf, '/'); | 		strbuf_addch(buf, '/'); | ||||||
|  | 	gitdir_len = buf->len; | ||||||
| 	strbuf_vaddf(buf, fmt, args); | 	strbuf_vaddf(buf, fmt, args); | ||||||
|  | 	adjust_git_path(buf, gitdir_len); | ||||||
| 	strbuf_cleanup_path(buf); | 	strbuf_cleanup_path(buf); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -19,6 +19,14 @@ relative_path() { | ||||||
| 	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'" | 	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | test_git_path() { | ||||||
|  | 	test_expect_success "git-path $1 $2 => $3" " | ||||||
|  | 		$1 git rev-parse --git-path $2 >actual && | ||||||
|  | 		echo $3 >expect && | ||||||
|  | 		test_cmp expect actual | ||||||
|  | 	" | ||||||
|  | } | ||||||
|  |  | ||||||
| # On Windows, we are using MSYS's bash, which mangles the paths. | # On Windows, we are using MSYS's bash, which mangles the paths. | ||||||
| # Absolute paths are anchored at the MSYS installation directory, | # Absolute paths are anchored at the MSYS installation directory, | ||||||
| # which means that the path / accounts for this many characters: | # which means that the path / accounts for this many characters: | ||||||
|  | @ -244,4 +252,15 @@ relative_path "<null>"		"<empty>"	./ | ||||||
| relative_path "<null>"		"<null>"	./ | relative_path "<null>"		"<null>"	./ | ||||||
| relative_path "<null>"		/foo/a/b	./ | relative_path "<null>"		/foo/a/b	./ | ||||||
|  |  | ||||||
|  | test_git_path A=B                info/grafts .git/info/grafts | ||||||
|  | test_git_path GIT_GRAFT_FILE=foo info/grafts foo | ||||||
|  | test_git_path GIT_GRAFT_FILE=foo info/////grafts foo | ||||||
|  | test_git_path GIT_INDEX_FILE=foo index foo | ||||||
|  | test_git_path GIT_INDEX_FILE=foo index/foo .git/index/foo | ||||||
|  | test_git_path GIT_INDEX_FILE=foo index2 .git/index2 | ||||||
|  | test_expect_success 'setup fake objects directory foo' 'mkdir foo' | ||||||
|  | test_git_path GIT_OBJECT_DIRECTORY=foo objects foo | ||||||
|  | test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo | ||||||
|  | test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2 | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Nguyễn Thái Ngọc Duy
						Nguyễn Thái Ngọc Duy