Refactor index-pack "keep $sha1" handling for reuse
authorShawn O. Pearce <spearce@spearce.org>
Fri, 14 Sep 2007 07:31:16 +0000 (03:31 -0400)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Sep 2007 10:22:30 +0000 (03:22 -0700)
There is a subtle (but important) linkage between receive-pack and
index-pack that allows index-pack to create a packfile but protect
it from being deleted by a concurrent `git repack -a -d` operation.
The linkage works by having index-pack mark the newly created pack
with a ".keep" file and then it passes the SHA-1 name of that new
packfile to receive-pack along its stdout channel.

The receive-pack process must unkeep the packfile by deleting the
.keep file, but can it can only do so after all elgible refs have
been updated in the receiving repository.  This ensures that the
packfile is either kept or its objects are reachable, preventing
a concurrent repacker from deleting the packfile before it can
determine that its objects are actually needed by the repository.

The new builtin-fetch code needs to perform the same actions if
it choose to run index-pack rather than unpack-objects, so I am
moving this code out to its own function where both receive-pack
and fetch-pack are able to invoke it when necessary.  The caller
is responsible for deleting the returned ".keep" and freeing the
path if the returned path is not NULL.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
pack-write.c
pack.h
receive-pack.c

index e59b197..979bdff 100644 (file)
@@ -179,3 +179,29 @@ void fixup_pack_header_footer(int pack_fd,
        SHA1_Final(pack_file_sha1, &c);
        write_or_die(pack_fd, pack_file_sha1, 20);
 }
+
+char *index_pack_lockfile(int ip_out)
+{
+       int len, s;
+       char packname[46];
+
+       /*
+        * 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(ip_out, packname+len, 46-len)) > 0;
+                len += s);
+       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);
+               return xstrdup(path);
+       }
+       return NULL;
+}
diff --git a/pack.h b/pack.h
index f357c9f..b57ba2d 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -59,6 +59,7 @@ extern const char *write_idx_file(const char *index_name, struct pack_idx_entry
 
 extern int verify_pack(struct packed_git *, int);
 extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t);
+extern char *index_pack_lockfile(int fd);
 
 #define PH_ERROR_EOF           (-1)
 #define PH_ERROR_PACK_SIGNATURE        (-2)
index d3c422b..61e9929 100644 (file)
@@ -382,9 +382,8 @@ static const char *unpack(void)
                }
        } else {
                const char *keeper[6];
-               int s, len, status;
+               int s, status;
                char keep_arg[256];
-               char packname[46];
                struct child_process ip;
 
                s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
@@ -403,26 +402,7 @@ static const char *unpack(void)
                ip.git_cmd = 1;
                if (start_command(&ip))
                        return "index-pack fork failed";
-
-               /*
-                * 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(ip.out, packname+len, 46-len)) > 0;
-                    len += s);
-               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);
-               }
-
+               pack_lockfile = index_pack_lockfile(ip.out);
                status = finish_command(&ip);
                if (!status) {
                        reprepare_packed_git();