Skip to content

Commit 9f69d29

Browse files
mhaggergitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2fff781 commit 9f69d29

File tree

3 files changed

+98
-21
lines changed

3 files changed

+98
-21
lines changed

builtin/clone.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,13 +493,16 @@ static void write_remote_refs(const struct ref *local_refs)
493493
{
494494
const struct ref *r;
495495

496+
lock_packed_refs(LOCK_DIE_ON_ERROR);
497+
496498
for (r = local_refs; r; r = r->next) {
497499
if (!r->peer_ref)
498500
continue;
499501
add_packed_ref(r->peer_ref->name, r->old_sha1);
500502
}
501503

502-
pack_refs(PACK_REFS_ALL);
504+
if (commit_packed_refs())
505+
die_errno("unable to overwrite old ref-pack file");
503506
}
504507

505508
static void write_followtags(const struct ref *refs, const char *msg)

refs.c

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,13 @@ static int is_refname_available(const char *refname, const char *oldrefname,
808808

809809
struct packed_ref_cache {
810810
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;
811818
};
812819

813820
/*
@@ -826,9 +833,14 @@ static struct ref_cache {
826833
char name[1];
827834
} ref_cache, *submodule_ref_caches;
828835

836+
/* Lock used for the main packed-refs file: */
837+
static struct lock_file packlock;
838+
829839
static void clear_packed_ref_cache(struct ref_cache *refs)
830840
{
831841
if (refs->packed) {
842+
if (refs->packed->lock)
843+
die("internal error: packed-ref cache cleared while locked");
832844
free_ref_entry(refs->packed->root);
833845
free(refs->packed);
834846
refs->packed = NULL;
@@ -1038,7 +1050,12 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)
10381050

10391051
void add_packed_ref(const char *refname, const unsigned char *sha1)
10401052
{
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),
10421059
create_ref_entry(refname, sha1, REF_ISPACKED, 1));
10431060
}
10441061

@@ -2035,6 +2052,52 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
20352052
return 0;
20362053
}
20372054

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+
20382101
struct ref_to_prune {
20392102
struct ref_to_prune *next;
20402103
unsigned char sha1[20];
@@ -2148,28 +2211,22 @@ static void prune_refs(struct ref_to_prune *r)
21482211
}
21492212
}
21502213

2151-
static struct lock_file packlock;
2152-
21532214
int pack_refs(unsigned int flags)
21542215
{
21552216
struct pack_refs_cb_data cbdata;
2156-
int fd;
21572217

21582218
memset(&cbdata, 0, sizeof(cbdata));
21592219
cbdata.flags = flags;
21602220

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);
21632222
cbdata.packed_refs = get_packed_refs(&ref_cache);
21642223

21652224
do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
21662225
pack_if_possible_fn, &cbdata);
21672226

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())
21722228
die_errno("unable to overwrite old ref-pack file");
2229+
21732230
prune_refs(cbdata.ref_to_prune);
21742231
return 0;
21752232
}
@@ -2233,20 +2290,17 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
22332290

22342291
static int repack_without_ref(const char *refname)
22352292
{
2236-
int fd;
22372293
struct ref_dir *packed;
22382294
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
22392295
struct string_list_item *ref_to_delete;
22402296

22412297
if (!get_packed_ref(refname))
22422298
return 0; /* refname does not exist in packed refs */
22432299

2244-
fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
2245-
if (fd < 0) {
2300+
if (lock_packed_refs(0)) {
22462301
unable_to_lock_error(git_path("packed-refs"), errno);
22472302
return error("cannot delete '%s' from packed refs", refname);
22482303
}
2249-
clear_packed_ref_cache(&ref_cache);
22502304
packed = get_packed_refs(&ref_cache);
22512305

22522306
/* Remove refname from the cache: */
@@ -2255,7 +2309,7 @@ static int repack_without_ref(const char *refname)
22552309
* The packed entry disappeared while we were
22562310
* acquiring the lock.
22572311
*/
2258-
rollback_lock_file(&packlock);
2312+
rollback_packed_refs();
22592313
return 0;
22602314
}
22612315

@@ -2267,9 +2321,7 @@ static int repack_without_ref(const char *refname)
22672321
}
22682322

22692323
/* 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();
22732325
}
22742326

22752327
int delete_ref(const char *refname, const unsigned char *sha1, int delopt)

refs.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,33 @@ extern int for_each_rawref(each_ref_fn, void *);
7777
extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
7878

7979
/*
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().
8290
*/
8391
extern void add_packed_ref(const char *refname, const unsigned char *sha1);
8492

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+
85107
/*
86108
* Flags for controlling behaviour of pack_refs()
87109
* PACK_REFS_PRUNE: Prune loose refs after packing

0 commit comments

Comments
 (0)