remove .keep pack lock files when done with refs update
This makes both git-fetch and git-push (fetch-pack and receive-pack) safe against a possible race with aparallel git-repack -a -d that could prune the new pack while it is not yet referenced, and remove the .keep file after refs have been updated. Signed-off-by: Nicolas Pitre <nico@cam.org> Signed-off-by: Junio C Hamano <junkio@cox.net>maint
							parent
							
								
									9ca4a201ea
								
							
						
					
					
						commit
						576162a45f
					
				|  | @ -69,6 +69,17 @@ OPTIONS | ||||||
| 	locate any which have outlived their usefulness. | 	locate any which have outlived their usefulness. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Note | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | Once the index has been created, the list of object names is sorted | ||||||
|  | and the SHA1 hash of that list is printed to stdout. If --stdin was | ||||||
|  | also used then this is prefixed by either "pack\t", or "keep\t" if a | ||||||
|  | new .keep file was successfully created. This is useful to remove a | ||||||
|  | .keep file used as a lock to prevent the race with gitlink:git-repack[1] | ||||||
|  | mentioned above. | ||||||
|  |  | ||||||
|  |  | ||||||
| Author | Author | ||||||
| ------ | ------ | ||||||
| Written by Sergey Vlasov <vsu@altlinux.ru> | Written by Sergey Vlasov <vsu@altlinux.ru> | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								git-fetch.sh
								
								
								
								
							
							
						
						
									
										10
									
								
								git-fetch.sh
								
								
								
								
							|  | @ -51,7 +51,7 @@ do | ||||||
| 		verbose=Yes | 		verbose=Yes | ||||||
| 		;; | 		;; | ||||||
| 	-k|--k|--ke|--kee|--keep) | 	-k|--k|--ke|--kee|--keep) | ||||||
| 		keep=--keep | 		keep='-k -k' | ||||||
| 		;; | 		;; | ||||||
| 	--reflog-action=*) | 	--reflog-action=*) | ||||||
| 		rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'` | 		rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'` | ||||||
|  | @ -368,6 +368,7 @@ fetch_main () { | ||||||
|       ;; # we are already done. |       ;; # we are already done. | ||||||
|   *) |   *) | ||||||
|     ( : subshell because we muck with IFS |     ( : subshell because we muck with IFS | ||||||
|  |       pack_lockfile= | ||||||
|       IFS=" 	$LF" |       IFS=" 	$LF" | ||||||
|       ( |       ( | ||||||
| 	  git-fetch-pack --thin $exec $keep "$remote" $rref || echo failed "$remote" | 	  git-fetch-pack --thin $exec $keep "$remote" $rref || echo failed "$remote" | ||||||
|  | @ -378,6 +379,12 @@ fetch_main () { | ||||||
| 	  failed) | 	  failed) | ||||||
| 		  echo >&2 "Fetch failure: $remote" | 		  echo >&2 "Fetch failure: $remote" | ||||||
| 		  exit 1 ;; | 		  exit 1 ;; | ||||||
|  | 	  # special line coming from index-pack with the pack name | ||||||
|  | 	  pack) | ||||||
|  | 		  continue ;; | ||||||
|  | 	  keep) | ||||||
|  | 		  pack_lockfile="$GIT_OBJECT_DIRECTORY/pack/pack-$remote_name.keep" | ||||||
|  | 		  continue ;; | ||||||
| 	  esac | 	  esac | ||||||
| 	  found= | 	  found= | ||||||
| 	  single_force= | 	  single_force= | ||||||
|  | @ -408,6 +415,7 @@ fetch_main () { | ||||||
| 	  append_fetch_head "$sha1" "$remote" \ | 	  append_fetch_head "$sha1" "$remote" \ | ||||||
| 		  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge" | 		  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge" | ||||||
|       done |       done | ||||||
|  |       if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi | ||||||
|     ) || exit ;; |     ) || exit ;; | ||||||
|   esac |   esac | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										38
									
								
								index-pack.c
								
								
								
								
							
							
						
						
									
										38
									
								
								index-pack.c
								
								
								
								
							|  | @ -757,6 +757,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, | ||||||
| 		  const char *keep_name, const char *keep_msg, | 		  const char *keep_name, const char *keep_msg, | ||||||
| 		  unsigned char *sha1) | 		  unsigned char *sha1) | ||||||
| { | { | ||||||
|  | 	char *report = "pack"; | ||||||
| 	char name[PATH_MAX]; | 	char name[PATH_MAX]; | ||||||
| 	int err; | 	int err; | ||||||
|  |  | ||||||
|  | @ -767,18 +768,6 @@ static void final(const char *final_pack_name, const char *curr_pack_name, | ||||||
| 		if (err) | 		if (err) | ||||||
| 			die("error while closing pack file: %s", strerror(errno)); | 			die("error while closing pack file: %s", strerror(errno)); | ||||||
| 		chmod(curr_pack_name, 0444); | 		chmod(curr_pack_name, 0444); | ||||||
|  |  | ||||||
| 		/* |  | ||||||
| 		 * Let's just mimic git-unpack-objects here and write |  | ||||||
| 		 * the last part of the buffer to stdout. |  | ||||||
| 		 */ |  | ||||||
| 		while (input_len) { |  | ||||||
| 			err = xwrite(1, input_buffer + input_offset, input_len); |  | ||||||
| 			if (err <= 0) |  | ||||||
| 				break; |  | ||||||
| 			input_len -= err; |  | ||||||
| 			input_offset += err; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (keep_msg) { | 	if (keep_msg) { | ||||||
|  | @ -798,6 +787,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, | ||||||
| 				write_or_die(keep_fd, "\n", 1); | 				write_or_die(keep_fd, "\n", 1); | ||||||
| 			} | 			} | ||||||
| 			close(keep_fd); | 			close(keep_fd); | ||||||
|  | 			report = "keep"; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -821,6 +811,27 @@ static void final(const char *final_pack_name, const char *curr_pack_name, | ||||||
| 		if (move_temp_to_file(curr_index_name, final_index_name)) | 		if (move_temp_to_file(curr_index_name, final_index_name)) | ||||||
| 			die("cannot store index file"); | 			die("cannot store index file"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if (!from_stdin) { | ||||||
|  | 		printf("%s\n", sha1_to_hex(sha1)); | ||||||
|  | 	} else { | ||||||
|  | 		char buf[48]; | ||||||
|  | 		int len = snprintf(buf, sizeof(buf), "%s\t%s\n", | ||||||
|  | 				   report, sha1_to_hex(sha1)); | ||||||
|  | 		xwrite(1, buf, len); | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 		 * Let's just mimic git-unpack-objects here and write | ||||||
|  | 		 * the last part of the input buffer to stdout. | ||||||
|  | 		 */ | ||||||
|  | 		while (input_len) { | ||||||
|  | 			err = xwrite(1, input_buffer + input_offset, input_len); | ||||||
|  | 			if (err <= 0) | ||||||
|  | 				break; | ||||||
|  | 			input_len -= err; | ||||||
|  | 			input_offset += err; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
|  | @ -937,8 +948,5 @@ int main(int argc, char **argv) | ||||||
| 	free(index_name_buf); | 	free(index_name_buf); | ||||||
| 	free(keep_name_buf); | 	free(keep_name_buf); | ||||||
|  |  | ||||||
| 	if (!from_stdin) |  | ||||||
| 		printf("%s\n", sha1_to_hex(sha1)); |  | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,8 +3,10 @@ | ||||||
| #include "refs.h" | #include "refs.h" | ||||||
| #include "pkt-line.h" | #include "pkt-line.h" | ||||||
| #include "run-command.h" | #include "run-command.h" | ||||||
|  | #include "exec_cmd.h" | ||||||
| #include "commit.h" | #include "commit.h" | ||||||
| #include "object.h" | #include "object.h" | ||||||
|  | #include <sys/wait.h> | ||||||
|  |  | ||||||
| static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; | static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; | ||||||
|  |  | ||||||
|  | @ -251,12 +253,13 @@ static const char *parse_pack_header(struct pack_header *hdr) | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static const char *pack_lockfile; | ||||||
|  |  | ||||||
| static const char *unpack(void) | static const char *unpack(void) | ||||||
| { | { | ||||||
| 	struct pack_header hdr; | 	struct pack_header hdr; | ||||||
| 	const char *hdr_err; | 	const char *hdr_err; | ||||||
| 	char hdr_arg[38]; | 	char hdr_arg[38]; | ||||||
| 	int code; |  | ||||||
|  |  | ||||||
| 	hdr_err = parse_pack_header(&hdr); | 	hdr_err = parse_pack_header(&hdr); | ||||||
| 	if (hdr_err) | 	if (hdr_err) | ||||||
|  | @ -265,33 +268,13 @@ static const char *unpack(void) | ||||||
| 			ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); | 			ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); | ||||||
|  |  | ||||||
| 	if (ntohl(hdr.hdr_entries) < unpack_limit) { | 	if (ntohl(hdr.hdr_entries) < unpack_limit) { | ||||||
|  | 		int code; | ||||||
| 		const char *unpacker[3]; | 		const char *unpacker[3]; | ||||||
| 		unpacker[0] = "unpack-objects"; | 		unpacker[0] = "unpack-objects"; | ||||||
| 		unpacker[1] = hdr_arg; | 		unpacker[1] = hdr_arg; | ||||||
| 		unpacker[2] = NULL; | 		unpacker[2] = NULL; | ||||||
| 		code = run_command_v_opt(1, unpacker, RUN_GIT_CMD); | 		code = run_command_v_opt(1, unpacker, RUN_GIT_CMD); | ||||||
| 	} else { | 		switch (code) { | ||||||
| 		const char *keeper[6]; |  | ||||||
| 		char my_host[255], keep_arg[128 + 255]; |  | ||||||
|  |  | ||||||
| 		if (gethostname(my_host, sizeof(my_host))) |  | ||||||
| 			strcpy(my_host, "localhost"); |  | ||||||
| 		snprintf(keep_arg, sizeof(keep_arg), |  | ||||||
| 				"--keep=receive-pack %i on %s", |  | ||||||
| 				getpid(), my_host); |  | ||||||
|  |  | ||||||
| 		keeper[0] = "index-pack"; |  | ||||||
| 		keeper[1] = "--stdin"; |  | ||||||
| 		keeper[2] = "--fix-thin"; |  | ||||||
| 		keeper[3] = hdr_arg; |  | ||||||
| 		keeper[4] = keep_arg; |  | ||||||
| 		keeper[5] = NULL; |  | ||||||
| 		code = run_command_v_opt(1, keeper, RUN_GIT_CMD); |  | ||||||
| 		if (!code) |  | ||||||
| 			reprepare_packed_git(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch (code) { |  | ||||||
| 		case 0: | 		case 0: | ||||||
| 			return NULL; | 			return NULL; | ||||||
| 		case -ERR_RUN_COMMAND_FORK: | 		case -ERR_RUN_COMMAND_FORK: | ||||||
|  | @ -308,6 +291,71 @@ static const char *unpack(void) | ||||||
| 			return "unpacker died strangely"; | 			return "unpacker died strangely"; | ||||||
| 		default: | 		default: | ||||||
| 			return "unpacker exited with error code"; | 			return "unpacker exited with error code"; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		const char *keeper[6]; | ||||||
|  | 		int fd[2], s, len, status; | ||||||
|  | 		pid_t pid; | ||||||
|  | 		char keep_arg[256]; | ||||||
|  | 		char packname[46]; | ||||||
|  |  | ||||||
|  | 		s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid()); | ||||||
|  | 		if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) | ||||||
|  | 			strcpy(keep_arg + s, "localhost"); | ||||||
|  |  | ||||||
|  | 		keeper[0] = "index-pack"; | ||||||
|  | 		keeper[1] = "--stdin"; | ||||||
|  | 		keeper[2] = "--fix-thin"; | ||||||
|  | 		keeper[3] = hdr_arg; | ||||||
|  | 		keeper[4] = keep_arg; | ||||||
|  | 		keeper[5] = NULL; | ||||||
|  |  | ||||||
|  | 		if (pipe(fd) < 0) | ||||||
|  | 			return "index-pack pipe failed"; | ||||||
|  | 		pid = fork(); | ||||||
|  | 		if (pid < 0) | ||||||
|  | 			return "index-pack fork failed"; | ||||||
|  | 		if (!pid) { | ||||||
|  | 			dup2(fd[1], 1); | ||||||
|  | 			close(fd[1]); | ||||||
|  | 			close(fd[0]); | ||||||
|  | 			execv_git_cmd(keeper); | ||||||
|  | 			die("execv of index-pack failed"); | ||||||
|  | 		} | ||||||
|  | 		close(fd[1]); | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 		 * The first thing we expects from index-pack's output | ||||||
|  | 		 * is "pack\t%40s\n" or "keep\t%40s\n" (46 bytes) where | ||||||
|  | 		 * %40s is the newly created pack SHA1 name.  In the "keep" | ||||||
|  | 		 * case, we need it to remove the corresponding .keep file | ||||||
|  | 		 * later on.  If we don't get that then tough luck with it. | ||||||
|  | 		 */ | ||||||
|  | 		for (len = 0; | ||||||
|  | 		     len < 46 && (s = xread(fd[0], packname+len, 46-len)) > 0; | ||||||
|  | 		     len += s); | ||||||
|  | 		close(fd[0]); | ||||||
|  | 		if (len == 46 && packname[45] == '\n' && | ||||||
|  | 		    memcmp(packname, "keep\t", 5) == 0) { | ||||||
|  | 			char path[PATH_MAX]; | ||||||
|  | 			packname[45] = 0; | ||||||
|  | 			snprintf(path, sizeof(path), "%s/pack/pack-%s.keep", | ||||||
|  | 				 get_object_directory(), packname + 5); | ||||||
|  | 			pack_lockfile = xstrdup(path); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Then wrap our index-pack process. */ | ||||||
|  | 		while (waitpid(pid, &status, 0) < 0) | ||||||
|  | 			if (errno != EINTR) | ||||||
|  | 				return "waitpid failed"; | ||||||
|  | 		if (WIFEXITED(status)) { | ||||||
|  | 			int code = WEXITSTATUS(status); | ||||||
|  | 			if (code) | ||||||
|  | 				return "index-pack exited with error code"; | ||||||
|  | 			reprepare_packed_git(); | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  | 		return "index-pack abnormal exit"; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -363,6 +411,8 @@ int main(int argc, char **argv) | ||||||
| 		const char *unpack_status = unpack(); | 		const char *unpack_status = unpack(); | ||||||
| 		if (!unpack_status) | 		if (!unpack_status) | ||||||
| 			execute_commands(); | 			execute_commands(); | ||||||
|  | 		if (pack_lockfile) | ||||||
|  | 			unlink(pack_lockfile); | ||||||
| 		if (report_status) | 		if (report_status) | ||||||
| 			report(unpack_status); | 			report(unpack_status); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Nicolas Pitre
						Nicolas Pitre