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@xxxxxxx> --- Documentation/git-index-pack.txt | 11 ++++ git-fetch.sh | 10 ++++- index-pack.c | 38 +++++++++------ receive-pack.c | 96 +++++++++++++++++++++++++++++--------- 4 files changed, 116 insertions(+), 39 deletions(-) diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt index 1235416..2229ee8 100644 --- a/Documentation/git-index-pack.txt +++ b/Documentation/git-index-pack.txt @@ -69,6 +69,17 @@ OPTIONS 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 ------ Written by Sergey Vlasov <vsu@xxxxxxxxxxx> diff --git a/git-fetch.sh b/git-fetch.sh index 2b5538f..e8ce296 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -51,7 +51,7 @@ do verbose=Yes ;; -k|--k|--ke|--kee|--keep) - keep=--keep + keep=-k -k ;; --reflog-action=*) rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'` @@ -368,6 +368,7 @@ fetch_main () { ;; # we are already done. *) ( : subshell because we muck with IFS + pack_lockfile= IFS=" $LF" ( git-fetch-pack --thin $exec $keep "$remote" $rref || echo failed "$remote" @@ -378,6 +379,12 @@ fetch_main () { failed) echo >&2 "Fetch failure: $remote" 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 found= single_force= @@ -408,6 +415,7 @@ fetch_main () { append_fetch_head "$sha1" "$remote" \ "$remote_name" "$remote_nick" "$local_name" "$not_for_merge" done + if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi ) || exit ;; esac diff --git a/index-pack.c b/index-pack.c index 8d64a88..042aea8 100644 --- a/index-pack.c +++ b/index-pack.c @@ -757,6 +757,7 @@ static void final(const char *final_pack const char *keep_name, const char *keep_msg, unsigned char *sha1) { + char *report = "pack"; char name[PATH_MAX]; int err; @@ -767,18 +768,6 @@ static void final(const char *final_pack if (err) die("error while closing pack file: %s", strerror(errno)); 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) { @@ -798,6 +787,7 @@ static void final(const char *final_pack write_or_die(keep_fd, "\n", 1); } close(keep_fd); + report = "keep"; } } @@ -821,6 +811,27 @@ static void final(const char *final_pack if (move_temp_to_file(curr_index_name, final_index_name)) 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) @@ -937,8 +948,5 @@ int main(int argc, char **argv) free(index_name_buf); free(keep_name_buf); - if (!from_stdin) - printf("%s\n", sha1_to_hex(sha1)); - return 0; } diff --git a/receive-pack.c b/receive-pack.c index be4c3fb..8bb5683 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -3,8 +3,10 @@ #include "refs.h" #include "pkt-line.h" #include "run-command.h" +#include "exec_cmd.h" #include "commit.h" #include "object.h" +#include <sys/wait.h> static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; @@ -251,12 +253,13 @@ static const char *parse_pack_header(str return NULL; } +static const char *pack_lockfile; + static const char *unpack(void) { struct pack_header hdr; const char *hdr_err; char hdr_arg[38]; - int code; hdr_err = parse_pack_header(&hdr); if (hdr_err) @@ -265,33 +268,13 @@ static const char *unpack(void) ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); if (ntohl(hdr.hdr_entries) < unpack_limit) { + int code; const char *unpacker[3]; unpacker[0] = "unpack-objects"; unpacker[1] = hdr_arg; unpacker[2] = NULL; code = run_command_v_opt(1, unpacker, RUN_GIT_CMD); - } else { - 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) { + switch (code) { case 0: return NULL; case -ERR_RUN_COMMAND_FORK: @@ -308,6 +291,71 @@ static const char *unpack(void) return "unpacker died strangely"; default: 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(); if (!unpack_status) execute_commands(); + if (pack_lockfile) + unlink(pack_lockfile); if (report_status) report(unpack_status); } -- 1.4.3.3.g87b2-dirty - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html