reflog expire: prune commits that are not incomplete
authorJunio C Hamano <junkio@cox.net>
Fri, 22 Dec 2006 08:46:33 +0000 (00:46 -0800)
committerJunio C Hamano <junkio@cox.net>
Fri, 22 Dec 2006 08:46:33 +0000 (00:46 -0800)
Older fsck-objects and prune did not protect commits in reflog
entries, and it is quite possible that a commit still exists in
the repository (because it was in a pack, or something) while
some of its trees and blobs are long gone.  Make sure the commit
and its associated tree is complete and expire incomplete ones.

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

index d4f73535c4e627b060df52a4c3cd01749cd6fb9c..4097c328ccdfc8c377ccf9c23a4330fe2020c5c8 100644 (file)
@@ -3,7 +3,7 @@
 #include "commit.h"
 #include "refs.h"
 #include "dir.h"
-#include <time.h>
+#include "tree-walk.h"
 
 struct expire_reflog_cb {
        FILE *newlog;
@@ -13,13 +13,50 @@ struct expire_reflog_cb {
        unsigned long expire_unreachable;
 };
 
+static int tree_is_complete(const unsigned char *sha1)
+{
+       struct tree_desc desc;
+       void *buf;
+       char type[20];
+
+       buf = read_sha1_file(sha1, type, &desc.size);
+       if (!buf)
+               return 0;
+       desc.buf = buf;
+       while (desc.size) {
+               const unsigned char *elem;
+               const char *name;
+               unsigned mode;
+
+               elem = tree_entry_extract(&desc, &name, &mode);
+               if (!has_sha1_file(elem) ||
+                   (S_ISDIR(mode) && !tree_is_complete(elem))) {
+                       free(buf);
+                       return 0;
+               }
+               update_tree_entry(&desc);
+       }
+       free(buf);
+       return 1;
+}
+
 static int keep_entry(struct commit **it, unsigned char *sha1)
 {
+       struct commit *commit;
+
        *it = NULL;
        if (is_null_sha1(sha1))
                return 1;
-       *it = lookup_commit_reference_gently(sha1, 1);
-       return (*it != NULL);
+       commit = lookup_commit_reference_gently(sha1, 1);
+       if (!commit)
+               return 0;
+
+       /* Make sure everything in this commit exists. */
+       parse_object(commit->object.sha1);
+       if (!tree_is_complete(commit->tree->object.sha1))
+               return 0;
+       *it = commit;
+       return 1;
 }
 
 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,