Merge branch 'kk/revwalk-slop-too-many-commit-within-a-second' into maint-1.8.1
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 6cec1c8bdf70589a41c2ace4ba4f21f4035e4ed4..6770e962a94cc4f1709fe7e7741f0a896a416aed 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -804,11 +804,38 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
        return line;
 }
 
+/*
+ * Read f, which is a packed-refs file, into dir.
+ *
+ * A comment line of the form "# pack-refs with: " may contain zero or
+ * more traits. We interpret the traits as follows:
+ *
+ *   No traits:
+ *
+ *      Probably no references are peeled. But if the file contains a
+ *      peeled value for a reference, we will use it.
+ *
+ *   peeled:
+ *
+ *      References under "refs/tags/", if they *can* be peeled, *are*
+ *      peeled in this file. References outside of "refs/tags/" are
+ *      probably not peeled even if they could have been, but if we find
+ *      a peeled value for such a reference we will use it.
+ *
+ *   fully-peeled:
+ *
+ *      All references in the file that can be peeled are peeled.
+ *      Inversely (and this is more important), any references in the
+ *      file for which no peeled value is recorded is not peelable. This
+ *      trait should typically be written alongside "peeled" for
+ *      compatibility with older clients, but we do not require it
+ *      (i.e., "peeled" is a no-op if "fully-peeled" is set).
+ */
 static void read_packed_refs(FILE *f, struct ref_dir *dir)
 {
        struct ref_entry *last = NULL;
        char refline[PATH_MAX];
-       int flag = REF_ISPACKED;
+       enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
 
        while (fgets(refline, sizeof(refline), f)) {
                unsigned char sha1[20];
@@ -817,15 +844,20 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
 
                if (!strncmp(refline, header, sizeof(header)-1)) {
                        const char *traits = refline + sizeof(header) - 1;
-                       if (strstr(traits, " peeled "))
-                               flag |= REF_KNOWS_PEELED;
+                       if (strstr(traits, " fully-peeled "))
+                               peeled = PEELED_FULLY;
+                       else if (strstr(traits, " peeled "))
+                               peeled = PEELED_TAGS;
                        /* perhaps other traits later as well */
                        continue;
                }
 
                refname = parse_ref_line(refline, sha1);
                if (refname) {
-                       last = create_ref_entry(refname, sha1, flag, 1);
+                       last = create_ref_entry(refname, sha1, REF_ISPACKED, 1);
+                       if (peeled == PEELED_FULLY ||
+                           (peeled == PEELED_TAGS && !prefixcmp(refname, "refs/tags/")))
+                               last->flag |= REF_KNOWS_PEELED;
                        add_ref(dir, last);
                        continue;
                }
@@ -833,8 +865,15 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                    refline[0] == '^' &&
                    strlen(refline) == 42 &&
                    refline[41] == '\n' &&
-                   !get_sha1_hex(refline + 1, sha1))
+                   !get_sha1_hex(refline + 1, sha1)) {
                        hashcpy(last->u.value.peeled, sha1);
+                       /*
+                        * Regardless of what the file header said,
+                        * we definitely know the value of *this*
+                        * reference:
+                        */
+                       last->flag |= REF_KNOWS_PEELED;
+               }
        }
 }
 
@@ -1744,7 +1783,8 @@ static struct lock_file packlock;
 static int repack_without_ref(const char *refname)
 {
        struct repack_without_ref_sb data;
-       struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
+       struct ref_cache *refs = get_ref_cache(NULL);
+       struct ref_dir *packed = get_packed_refs(refs);
        if (find_ref(packed, refname) == NULL)
                return 0;
        data.refname = refname;
@@ -1753,6 +1793,8 @@ static int repack_without_ref(const char *refname)
                unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refname);
        }
+       clear_packed_ref_cache(refs);
+       packed = get_packed_refs(refs);
        do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data);
        return commit_lock_file(&packlock);
 }