refs: implement simple transactions for the packed-refs file

Handle simple transactions for the packed-refs file at the packed_ref_cache level via new functions lock_packed_refs(), commit_packed_refs(), and rollback_packed_refs(). Only allow the packed ref cache to be modified (via add_packed_ref()) while the packed refs file is locked. Change clone to add the new references within a transaction. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Jun 20, 2013 at 10:37 UTC 9f69d297703bff37c5506276c2565c721347e03f
3 files changed +98 -21
builtin/clone.c
+4 -1
@@ -493,13 +493,16 @@ static void write_remote_refs(const struct ref *local_refs)
493 {
494 const struct ref *r;
495
496 + lock_packed_refs(LOCK_DIE_ON_ERROR);
497 +
498 for (r = local_refs; r; r = r->next) {
499 if (!r->peer_ref)
500 continue;
501 add_packed_ref(r->peer_ref->name, r->old_sha1);
502 }
503
502 - pack_refs(PACK_REFS_ALL);
504 + if (commit_packed_refs())
505 + die_errno("unable to overwrite old ref-pack file");
506 }
507
508 static void write_followtags(const struct ref *refs, const char *msg)
refs.c
+70 -18
@@ -808,6 +808,13 @@ static int is_refname_available(const char *refname, const char *oldrefname,
808
809 struct packed_ref_cache {
810 struct ref_entry *root;
811 +
812 + /*
813 + * Iff the packed-refs file associated with this instance is
814 + * currently locked for writing, this points at the associated
815 + * lock (which is owned by somebody else).
816 + */
817 + struct lock_file *lock;
818 };
819
820 /*
@@ -826,9 +833,14 @@ static struct ref_cache {
833 char name[1];
834 } ref_cache, *submodule_ref_caches;
835
836 +/* Lock used for the main packed-refs file: */
837 +static struct lock_file packlock;
838 +
839 static void clear_packed_ref_cache(struct ref_cache *refs)
840 {
841 if (refs->packed) {
842 + if (refs->packed->lock)
843 + die("internal error: packed-ref cache cleared while locked");
844 free_ref_entry(refs->packed->root);
845 free(refs->packed);
846 refs->packed = NULL;
@@ -1038,7 +1050,12 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)
1050
1051 void add_packed_ref(const char *refname, const unsigned char *sha1)
1052 {
1041 - add_ref(get_packed_refs(&ref_cache),
1053 + struct packed_ref_cache *packed_ref_cache =
1054 + get_packed_ref_cache(&ref_cache);
1055 +
1056 + if (!packed_ref_cache->lock)
1057 + die("internal error: packed refs not locked");
1058 + add_ref(get_packed_ref_dir(packed_ref_cache),
1059 create_ref_entry(refname, sha1, REF_ISPACKED, 1));
1060 }
1061
@@ -2035,6 +2052,52 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
2052 return 0;
2053 }
2054
2055 +int lock_packed_refs(int flags)
2056 +{
2057 + struct packed_ref_cache *packed_ref_cache;
2058 +
2059 + /* Discard the old cache because it might be invalid: */
2060 + clear_packed_ref_cache(&ref_cache);
2061 + if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
2062 + return -1;
2063 + /* Read the current packed-refs while holding the lock: */
2064 + packed_ref_cache = get_packed_ref_cache(&ref_cache);
2065 + packed_ref_cache->lock = &packlock;
2066 + return 0;
2067 +}
2068 +
2069 +int commit_packed_refs(void)
2070 +{
2071 + struct packed_ref_cache *packed_ref_cache =
2072 + get_packed_ref_cache(&ref_cache);
2073 + int error = 0;
2074 +
2075 + if (!packed_ref_cache->lock)
2076 + die("internal error: packed-refs not locked");
2077 + write_or_die(packed_ref_cache->lock->fd,
2078 + PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
2079 +
2080 + do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
2081 + 0, write_packed_entry_fn,
2082 + &packed_ref_cache->lock->fd);
2083 + if (commit_lock_file(packed_ref_cache->lock))
2084 + error = -1;
2085 + packed_ref_cache->lock = NULL;
2086 + return error;
2087 +}
2088 +
2089 +void rollback_packed_refs(void)
2090 +{
2091 + struct packed_ref_cache *packed_ref_cache =
2092 + get_packed_ref_cache(&ref_cache);
2093 +
2094 + if (!packed_ref_cache->lock)
2095 + die("internal error: packed-refs not locked");
2096 + rollback_lock_file(packed_ref_cache->lock);
2097 + packed_ref_cache->lock = NULL;
2098 + clear_packed_ref_cache(&ref_cache);
2099 +}
2100 +
2101 struct ref_to_prune {
2102 struct ref_to_prune *next;
2103 unsigned char sha1[20];
@@ -2148,28 +2211,22 @@ static void prune_refs(struct ref_to_prune *r)
2211 }
2212 }
2213
2151 -static struct lock_file packlock;
2152 -
2214 int pack_refs(unsigned int flags)
2215 {
2216 struct pack_refs_cb_data cbdata;
2156 - int fd;
2217
2218 memset(&cbdata, 0, sizeof(cbdata));
2219 cbdata.flags = flags;
2220
2161 - fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"),
2162 - LOCK_DIE_ON_ERROR);
2221 + lock_packed_refs(LOCK_DIE_ON_ERROR);
2222 cbdata.packed_refs = get_packed_refs(&ref_cache);
2223
2224 do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
2225 pack_if_possible_fn, &cbdata);
2226
2168 - write_or_die(fd, PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
2169 - do_for_each_entry_in_dir(cbdata.packed_refs, 0, write_packed_entry_fn, &fd);
2170 -
2171 - if (commit_lock_file(&packlock) < 0)
2227 + if (commit_packed_refs())
2228 die_errno("unable to overwrite old ref-pack file");
2229 +
2230 prune_refs(cbdata.ref_to_prune);
2231 return 0;
2232 }
@@ -2233,7 +2290,6 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
2290
2291 static int repack_without_ref(const char *refname)
2292 {
2236 - int fd;
2293 struct ref_dir *packed;
2294 struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
2295 struct string_list_item *ref_to_delete;
@@ -2241,12 +2297,10 @@ static int repack_without_ref(const char *refname)
2297 if (!get_packed_ref(refname))
2298 return 0; /* refname does not exist in packed refs */
2299
2244 - fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
2245 - if (fd < 0) {
2300 + if (lock_packed_refs(0)) {
2301 unable_to_lock_error(git_path("packed-refs"), errno);
2302 return error("cannot delete '%s' from packed refs", refname);
2303 }
2249 - clear_packed_ref_cache(&ref_cache);
2304 packed = get_packed_refs(&ref_cache);
2305
2306 /* Remove refname from the cache: */
@@ -2255,7 +2309,7 @@ static int repack_without_ref(const char *refname)
2309 * The packed entry disappeared while we were
2310 * acquiring the lock.
2311 */
2258 - rollback_lock_file(&packlock);
2312 + rollback_packed_refs();
2313 return 0;
2314 }
2315
@@ -2267,9 +2321,7 @@ static int repack_without_ref(const char *refname)
2321 }
2322
2323 /* Write what remains: */
2270 - write_or_die(fd, PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
2271 - do_for_each_entry_in_dir(packed, 0, write_packed_entry_fn, &fd);
2272 - return commit_lock_file(&packlock);
2324 + return commit_packed_refs();
2325 }
2326
2327 int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
refs.h
+24 -2
@@ -77,11 +77,33 @@ extern int for_each_rawref(each_ref_fn, void *);
77 extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
78
79 /*
80 - * Add a reference to the in-memory packed reference cache. To actually
81 - * write the reference to the packed-refs file, call pack_refs().
80 + * Lock the packed-refs file for writing. Flags is passed to
81 + * hold_lock_file_for_update(). Return 0 on success.
82 + */
83 +extern int lock_packed_refs(int flags);
84 +
85 +/*
86 + * Add a reference to the in-memory packed reference cache. This may
87 + * only be called while the packed-refs file is locked (see
88 + * lock_packed_refs()). To actually write the packed-refs file, call
89 + * commit_packed_refs().
90 */
91 extern void add_packed_ref(const char *refname, const unsigned char *sha1);
92
93 +/*
94 + * Write the current version of the packed refs cache from memory to
95 + * disk. The packed-refs file must already be locked for writing (see
96 + * lock_packed_refs()). Return zero on success.
97 + */
98 +extern int commit_packed_refs(void);
99 +
100 +/*
101 + * Rollback the lockfile for the packed-refs file, and discard the
102 + * in-memory packed reference cache. (The packed-refs file will be
103 + * read anew if it is needed again after this function is called.)
104 + */
105 +extern void rollback_packed_refs(void);
106 +
107 /*
108 * Flags for controlling behaviour of pack_refs()
109 * PACK_REFS_PRUNE: Prune loose refs after packing