git-pack-refs --prune
authorJunio C Hamano <junkio@cox.net>
Thu, 21 Sep 2006 07:06:06 +0000 (00:06 -0700)
committerJunio C Hamano <junkio@cox.net>
Thu, 21 Sep 2006 07:06:54 +0000 (00:06 -0700)
"git pack-refs --prune", after successfully packing the existing
refs, removes the loose ref files.  It tries to protect against
race by doing the usual lock_ref_sha1() which makes sure the
contents of the ref has not changed since we last looked at.

Also we do not bother trying to prune what was already packed, and
we do not try pruning symbolic refs.

Signed-off-by: Junio C Hamano <junkio@cox.net>
builtin-pack-refs.c

index 0fc8a555e7615a084e3b5ba43866f8ef9043320a..246dd6372ea9eafbd8326662f053ed547631ab20 100644 (file)
@@ -2,6 +2,20 @@
 #include "refs.h"
 
 static const char *result_path, *lock_path;
+static const char builtin_pack_refs_usage[] =
+"git-pack-refs [--prune]";
+
+struct ref_to_prune {
+       struct ref_to_prune *next;
+       unsigned char sha1[20];
+       char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+       int prune;
+       struct ref_to_prune *ref_to_prune;
+       FILE *refs_file;
+};
 
 static void remove_lock_file(void)
 {
@@ -9,21 +23,70 @@ static void remove_lock_file(void)
                unlink(lock_path);
 }
 
+static int do_not_prune(int flags)
+{
+       /* If it is already packed or if it is a symref,
+        * do not prune it.
+        */
+       return (flags & (REF_ISSYMREF|REF_ISPACKED));
+}
+
 static int handle_one_ref(const char *path, const unsigned char *sha1,
                          int flags, void *cb_data)
 {
-       FILE *refs_file = cb_data;
+       struct pack_refs_cb_data *cb = cb_data;
 
        /* Do not pack the symbolic refs */
        if (!(flags & REF_ISSYMREF))
-               fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+               fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+       if (cb->prune && !do_not_prune(flags)) {
+               int namelen = strlen(path) + 1;
+               struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+               hashcpy(n->sha1, sha1);
+               strcpy(n->name, path);
+               n->next = cb->ref_to_prune;
+               cb->ref_to_prune = n;
+       }
        return 0;
 }
 
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+       struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1);
+
+       if (lock) {
+               unlink(git_path(r->name));
+               unlock_ref(lock);
+       }
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+       while (r) {
+               prune_ref(r);
+               r = r->next;
+       }
+}
+
 int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 {
-       int fd;
-       FILE *refs_file;
+       int fd, i;
+       struct pack_refs_cb_data cbdata;
+
+       memset(&cbdata, 0, sizeof(cbdata));
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--prune")) {
+                       cbdata.prune = 1;
+                       continue;
+               }
+               /* perhaps other parameters later... */
+               break;
+       }
+       if (i != argc)
+               usage(builtin_pack_refs_usage);
 
        result_path = xstrdup(git_path("packed-refs"));
        lock_path = xstrdup(mkpath("%s.lock", result_path));
@@ -33,14 +96,17 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
                die("unable to create new ref-pack file (%s)", strerror(errno));
        atexit(remove_lock_file);
 
-       refs_file = fdopen(fd, "w");
-       if (!refs_file)
-               die("unable to create ref-pack file structure (%s)", strerror(errno));
-       for_each_ref(handle_one_ref, refs_file);
+       cbdata.refs_file = fdopen(fd, "w");
+       if (!cbdata.refs_file)
+               die("unable to create ref-pack file structure (%s)",
+                   strerror(errno));
+       for_each_ref(handle_one_ref, &cbdata);
        fsync(fd);
-       fclose(refs_file);
+       fclose(cbdata.refs_file);
        if (rename(lock_path, result_path) < 0)
                die("unable to overwrite old ref-pack file (%s)", strerror(errno));
        lock_path = NULL;
+       if (cbdata.prune)
+               prune_refs(cbdata.ref_to_prune);
        return 0;
 }