Merge branch 'mh/packed-ref-store'
[git/git.git] / refs / packed-backend.c
index 5cf6b3d..412c850 100644 (file)
@@ -1,4 +1,5 @@
 #include "../cache.h"
+#include "../config.h"
 #include "../refs.h"
 #include "refs-internal.h"
 #include "ref-cache.h"
@@ -229,6 +230,9 @@ static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
                const char *refname;
                const char *traits;
 
+               if (!line.len || line.buf[line.len - 1] != '\n')
+                       die("unterminated line in %s: %s", packed_refs_file, line.buf);
+
                if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
                        if (strstr(traits, " fully-peeled "))
                                peeled = PEELED_FULLY;
@@ -253,9 +257,7 @@ static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
                            (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
                                last->flag |= REF_KNOWS_PEELED;
                        add_ref_entry(dir, last);
-                       continue;
-               }
-               if (last &&
+               } else if (last &&
                    line.buf[0] == '^' &&
                    line.len == PEELED_LINE_LENGTH &&
                    line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
@@ -267,6 +269,9 @@ static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
                         * reference:
                         */
                        last->flag |= REF_KNOWS_PEELED;
+               } else {
+                       strbuf_setlen(&line, line.len - 1);
+                       die("unexpected line in %s: %s", packed_refs_file, line.buf);
                }
        }
 
@@ -606,19 +611,27 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(refs);
        int ok;
+       int ret = -1;
        struct strbuf sb = STRBUF_INIT;
        FILE *out;
        struct ref_iterator *iter;
+       char *packed_refs_path;
 
        if (!is_lock_file_locked(&refs->lock))
                die("BUG: commit_packed_refs() called when unlocked");
 
-       strbuf_addf(&sb, "%s.new", refs->path);
+       /*
+        * If packed-refs is a symlink, we want to overwrite the
+        * symlinked-to file, not the symlink itself. Also, put the
+        * staging file next to it:
+        */
+       packed_refs_path = get_locked_file_path(&refs->lock);
+       strbuf_addf(&sb, "%s.new", packed_refs_path);
        if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
                strbuf_addf(err, "unable to create file %s: %s",
                            sb.buf, strerror(errno));
                strbuf_release(&sb);
-               return -1;
+               goto out;
        }
        strbuf_release(&sb);
 
@@ -656,38 +669,29 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
                goto error;
        }
 
-       if (rename_tempfile(&refs->tempfile, refs->path)) {
+       if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
                strbuf_addf(err, "error replacing %s: %s",
                            refs->path, strerror(errno));
-               return -1;
+               goto out;
        }
 
-       return 0;
+       ret = 0;
+       goto out;
 
 error:
        delete_tempfile(&refs->tempfile);
-       return -1;
-}
-
-/*
- * Rollback the lockfile for the packed-refs file, and discard the
- * in-memory packed reference cache.  (The packed-refs file will be
- * read anew if it is needed again after this function is called.)
- */
-static void rollback_packed_refs(struct packed_ref_store *refs)
-{
-       packed_assert_main_repository(refs, "rollback_packed_refs");
 
-       if (!is_lock_file_locked(&refs->lock))
-               die("BUG: packed-refs not locked");
-       packed_refs_unlock(&refs->base);
-       clear_packed_ref_cache(refs);
+out:
+       free(packed_refs_path);
+       return ret;
 }
 
 /*
  * Rewrite the packed-refs file, omitting any refs listed in
  * 'refnames'. On error, leave packed-refs unchanged, write an error
- * message to 'err', and return a nonzero value.
+ * message to 'err', and return a nonzero value. The packed refs lock
+ * must be held when calling this function; it will still be held when
+ * the function returns.
  *
  * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
  */
@@ -700,11 +704,13 @@ int repack_without_refs(struct ref_store *ref_store,
        struct ref_dir *packed;
        struct string_list_item *refname;
        int needs_repacking = 0, removed = 0;
-       int ret;
 
        packed_assert_main_repository(refs, "repack_without_refs");
        assert(err);
 
+       if (!is_lock_file_locked(&refs->lock))
+               die("BUG: repack_without_refs called without holding lock");
+
        /* Look for a packed ref */
        for_each_string_list_item(refname, refnames) {
                if (get_packed_ref(refs, refname->string)) {
@@ -717,9 +723,6 @@ int repack_without_refs(struct ref_store *ref_store,
        if (!needs_repacking)
                return 0; /* no refname exists in packed refs */
 
-       if (packed_refs_lock(&refs->base, 0, err))
-               return -1;
-
        packed = get_packed_refs(refs);
 
        /* Remove refnames from the cache */
@@ -731,14 +734,12 @@ int repack_without_refs(struct ref_store *ref_store,
                 * All packed entries disappeared while we were
                 * acquiring the lock.
                 */
-               rollback_packed_refs(refs);
+               clear_packed_ref_cache(refs);
                return 0;
        }
 
        /* Write what remains */
-       ret = commit_packed_refs(&refs->base, err);
-       packed_refs_unlock(ref_store);
-       return ret;
+       return commit_packed_refs(&refs->base, err);
 }
 
 static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)