setup.c: re-fix d95138e (setup: set env $GIT_WORK_TREE when ..
				
					
				
			Commitmaintd95138e[1] attempted to fix a .git file problem by setting GIT_WORK_TREE whenever GIT_DIR is set. It sounded harmless because we handle GIT_DIR and GIT_WORK_TREE side by side for most commands, with two exceptions: git-init and git-clone. "git clone" is not happy withd95138e. This command ignores GIT_DIR but respects GIT_WORK_TREE [2] [3] which means it used to run fine from a hook, where GIT_DIR was set but GIT_WORK_TREE was not (*). Withd95138e, GIT_WORK_TREE is set all the time and git-clone interprets that as "I give you order to put the worktree here", usually against the user's intention. The solution ind95138eis reverted earlier, and instead we reuse the solution fromc056261[4]. It fixed another setup-messed- up-by-alias by saving and restoring env and spawning a new process, but for git-clone and git-init only. Now we conclude that setup-messed-up-by-alias is always evil. So the env restoration is done for _all_ commands, including external ones, whenever aliases are involved. It fixes whatd95138etried to fix, without upsetting git-clone-inside-hooks. The test fromd95138eremains to verify it's not broken by this. A new test is added to make sure git-clone-inside-hooks remains happy. (*) GIT_WORK_TREE was not set _most of the time_. In some cases GIT_WORK_TREE is set and git-clone will behave differently. The use of GIT_WORK_TREE to direct git-clone to put work tree elsewhere looks like a mistake because it causes surprises this way. But that's a separate story. [1]d95138e(setup: set env $GIT_WORK_TREE when work tree is set, like $GIT_DIR - 2015-06-26) [2]2beebd2(clone: create intermediate directories of destination repo - 2008-06-25) [3]20ccef4(make git-clone GIT_WORK_TREE aware - 2007-07-06) [4]c056261(git potty: restore environments after alias expansion - 2014-06-08) Reported-by: Anthony Sottile <asottile@umich.edu> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
							parent
							
								
									0d5466d244
								
							
						
					
					
						commit
						86d26f240f
					
				
							
								
								
									
										23
									
								
								git.c
								
								
								
								
							
							
						
						
									
										23
									
								
								git.c
								
								
								
								
							|  | @ -226,7 +226,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) | ||||||
| static int handle_alias(int *argcp, const char ***argv) | static int handle_alias(int *argcp, const char ***argv) | ||||||
| { | { | ||||||
| 	int envchanged = 0, ret = 0, saved_errno = errno; | 	int envchanged = 0, ret = 0, saved_errno = errno; | ||||||
| 	const char *subdir; |  | ||||||
| 	int count, option_count; | 	int count, option_count; | ||||||
| 	const char **new_argv; | 	const char **new_argv; | ||||||
| 	const char *alias_command; | 	const char *alias_command; | ||||||
|  | @ -234,7 +233,7 @@ static int handle_alias(int *argcp, const char ***argv) | ||||||
| 	int unused_nongit; | 	int unused_nongit; | ||||||
|  |  | ||||||
| 	save_env_before_alias(); | 	save_env_before_alias(); | ||||||
| 	subdir = setup_git_directory_gently(&unused_nongit); | 	setup_git_directory_gently(&unused_nongit); | ||||||
|  |  | ||||||
| 	alias_command = (*argv)[0]; | 	alias_command = (*argv)[0]; | ||||||
| 	alias_string = alias_lookup(alias_command); | 	alias_string = alias_lookup(alias_command); | ||||||
|  | @ -292,8 +291,7 @@ static int handle_alias(int *argcp, const char ***argv) | ||||||
| 		ret = 1; | 		ret = 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (subdir && chdir(subdir)) | 	restore_env(); | ||||||
| 		die_errno("Cannot change to '%s'", subdir); |  | ||||||
|  |  | ||||||
| 	errno = saved_errno; | 	errno = saved_errno; | ||||||
|  |  | ||||||
|  | @ -308,7 +306,6 @@ static int handle_alias(int *argcp, const char ***argv) | ||||||
|  * RUN_SETUP for reading from the configuration file. |  * RUN_SETUP for reading from the configuration file. | ||||||
|  */ |  */ | ||||||
| #define NEED_WORK_TREE		(1<<3) | #define NEED_WORK_TREE		(1<<3) | ||||||
| #define NO_SETUP		(1<<4) |  | ||||||
|  |  | ||||||
| struct cmd_struct { | struct cmd_struct { | ||||||
| 	const char *cmd; | 	const char *cmd; | ||||||
|  | @ -389,7 +386,7 @@ static struct cmd_struct commands[] = { | ||||||
| 	{ "cherry", cmd_cherry, RUN_SETUP }, | 	{ "cherry", cmd_cherry, RUN_SETUP }, | ||||||
| 	{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, | 	{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, | ||||||
| 	{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, | 	{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, | ||||||
| 	{ "clone", cmd_clone, NO_SETUP }, | 	{ "clone", cmd_clone }, | ||||||
| 	{ "column", cmd_column, RUN_SETUP_GENTLY }, | 	{ "column", cmd_column, RUN_SETUP_GENTLY }, | ||||||
| 	{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE }, | 	{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE }, | ||||||
| 	{ "commit-tree", cmd_commit_tree, RUN_SETUP }, | 	{ "commit-tree", cmd_commit_tree, RUN_SETUP }, | ||||||
|  | @ -415,8 +412,8 @@ static struct cmd_struct commands[] = { | ||||||
| 	{ "hash-object", cmd_hash_object }, | 	{ "hash-object", cmd_hash_object }, | ||||||
| 	{ "help", cmd_help }, | 	{ "help", cmd_help }, | ||||||
| 	{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY }, | 	{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY }, | ||||||
| 	{ "init", cmd_init_db, NO_SETUP }, | 	{ "init", cmd_init_db }, | ||||||
| 	{ "init-db", cmd_init_db, NO_SETUP }, | 	{ "init-db", cmd_init_db }, | ||||||
| 	{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP }, | 	{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP }, | ||||||
| 	{ "log", cmd_log, RUN_SETUP }, | 	{ "log", cmd_log, RUN_SETUP }, | ||||||
| 	{ "ls-files", cmd_ls_files, RUN_SETUP }, | 	{ "ls-files", cmd_ls_files, RUN_SETUP }, | ||||||
|  | @ -528,9 +525,13 @@ static void handle_builtin(int argc, const char **argv) | ||||||
|  |  | ||||||
| 	builtin = get_builtin(cmd); | 	builtin = get_builtin(cmd); | ||||||
| 	if (builtin) { | 	if (builtin) { | ||||||
| 		if (saved_env_before_alias && (builtin->option & NO_SETUP)) | 		/* | ||||||
| 			restore_env(); | 		 * XXX: if we can figure out cases where it is _safe_ | ||||||
| 		else | 		 * to do, we can avoid spawning a new process when | ||||||
|  | 		 * saved_env_before_alias is true | ||||||
|  | 		 * (i.e. setup_git_dir* has been run once) | ||||||
|  | 		 */ | ||||||
|  | 		if (!saved_env_before_alias) | ||||||
| 			exit(run_builtin(builtin, argc, argv)); | 			exit(run_builtin(builtin, argc, argv)); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -99,7 +99,7 @@ test_expect_success 'check rev-list' ' | ||||||
| 	test "$SHA" = "$(git rev-list HEAD)" | 	test "$SHA" = "$(git rev-list HEAD)" | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_expect_failure 'setup_git_dir twice in subdir' ' | test_expect_success 'setup_git_dir twice in subdir' ' | ||||||
| 	git init sgd && | 	git init sgd && | ||||||
| 	( | 	( | ||||||
| 		cd sgd && | 		cd sgd && | ||||||
|  |  | ||||||
|  | @ -65,6 +65,29 @@ test_expect_success 'clone respects GIT_WORK_TREE' ' | ||||||
|  |  | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'clone from hooks' ' | ||||||
|  |  | ||||||
|  | 	test_create_repo r0 && | ||||||
|  | 	cd r0 && | ||||||
|  | 	test_commit initial && | ||||||
|  | 	cd .. && | ||||||
|  | 	git init r1 && | ||||||
|  | 	cd r1 && | ||||||
|  | 	cat >.git/hooks/pre-commit <<-\EOF && | ||||||
|  | 	#!/bin/sh | ||||||
|  | 	git clone ../r0 ../r2 | ||||||
|  | 	exit 1 | ||||||
|  | 	EOF | ||||||
|  | 	chmod u+x .git/hooks/pre-commit && | ||||||
|  | 	: >file && | ||||||
|  | 	git add file && | ||||||
|  | 	test_must_fail git commit -m invoke-hook && | ||||||
|  | 	cd .. && | ||||||
|  | 	test_cmp r0/.git/HEAD r2/.git/HEAD && | ||||||
|  | 	test_cmp r0/initial.t r2/initial.t | ||||||
|  |  | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_expect_success 'clone creates intermediate directories' ' | test_expect_success 'clone creates intermediate directories' ' | ||||||
|  |  | ||||||
| 	git clone src long/path/to/dst && | 	git clone src long/path/to/dst && | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Nguyễn Thái Ngọc Duy
						Nguyễn Thái Ngọc Duy