提交 e5cc7d7d 编写于 作者: M Michael Haggerty 提交者: Junio C Hamano

repack_without_refs(): don't lock or unlock the packed refs

Change `repack_without_refs()` to expect the packed-refs lock to be
held already, and not to release the lock before returning. Change the
callers to deal with lock management.

This change makes it possible for callers to hold the packed-refs lock
for a longer span of time, a possibility that will eventually make it
possible to fix some longstanding races.

The only semantic change here is that `repack_without_refs()` used to
forget to release the lock in the `if (!removed)` exit path. That
omission is now fixed.
Signed-off-by: NMichael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: NJunio C Hamano <gitster@pobox.com>
上级 42c7f7ff
......@@ -1149,24 +1149,16 @@ static int files_delete_refs(struct ref_store *ref_store, const char *msg,
if (!refnames->nr)
return 0;
result = repack_without_refs(refs->packed_ref_store, refnames, &err);
if (result) {
/*
* If we failed to rewrite the packed-refs file, then
* it is unsafe to try to remove loose refs, because
* doing so might expose an obsolete packed value for
* a reference that might even point at an object that
* has been garbage collected.
*/
if (refnames->nr == 1)
error(_("could not delete reference %s: %s"),
refnames->items[0].string, err.buf);
else
error(_("could not delete references: %s"), err.buf);
if (packed_refs_lock(refs->packed_ref_store, 0, &err))
goto error;
goto out;
if (repack_without_refs(refs->packed_ref_store, refnames, &err)) {
packed_refs_unlock(refs->packed_ref_store);
goto error;
}
packed_refs_unlock(refs->packed_ref_store);
for (i = 0; i < refnames->nr; i++) {
const char *refname = refnames->items[i].string;
......@@ -1174,9 +1166,24 @@ static int files_delete_refs(struct ref_store *ref_store, const char *msg,
result |= error(_("could not remove reference %s"), refname);
}
out:
strbuf_release(&err);
return result;
error:
/*
* If we failed to rewrite the packed-refs file, then it is
* unsafe to try to remove loose refs, because doing so might
* expose an obsolete packed value for a reference that might
* even point at an object that has been garbage collected.
*/
if (refnames->nr == 1)
error(_("could not delete reference %s: %s"),
refnames->items[0].string, err.buf);
else
error(_("could not delete references: %s"), err.buf);
strbuf_release(&err);
return -1;
}
/*
......@@ -2569,11 +2576,19 @@ static int files_transaction_finish(struct ref_store *ref_store,
}
}
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
if (repack_without_refs(refs->packed_ref_store, &refs_to_delete, err)) {
ret = TRANSACTION_GENERIC_ERROR;
packed_refs_unlock(refs->packed_ref_store);
goto cleanup;
}
packed_refs_unlock(refs->packed_ref_store);
/* Delete the reflogs of any references that were deleted: */
for_each_string_list_item(ref_to_delete, &refs_to_delete) {
strbuf_reset(&sb);
......
......@@ -669,25 +669,12 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
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);
}
/*
* 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 +687,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 +706,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 +717,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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册