Merge branch 'mh/init-delete-refs-api'
Clean up refs API and make "git clone" less intimate with the implementation detail. * mh/init-delete-refs-api: delete_ref(): use the usual convention for old_sha1 cmd_update_ref(): make logic more straightforward update_ref(): don't read old reference value before delete check_branch_commit(): make first parameter const refs.h: add some parameter names to function declarations refs: move the remaining ref module declarations to refs.h initial_ref_transaction_commit(): check for ref D/F conflicts initial_ref_transaction_commit(): check for duplicate refs refs: remove some functions from the module's public interface initial_ref_transaction_commit(): function for initial ref creation repack_without_refs(): make function private prune_refs(): use delete_refs() prune_remote(): use delete_refs() delete_refs(): bail early if the packed-refs file cannot be rewritten delete_refs(): make error message more generic delete_refs(): new function for the refs API delete_ref(): handle special case more explicitly remove_branches(): remove temporary delete_ref(): move declaration to refs.h
This commit is contained in:
182
refs.c
182
refs.c
@ -1314,7 +1314,13 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)
|
||||
return get_packed_ref_dir(get_packed_ref_cache(refs));
|
||||
}
|
||||
|
||||
void add_packed_ref(const char *refname, const unsigned char *sha1)
|
||||
/*
|
||||
* Add a reference to the in-memory packed reference cache. This may
|
||||
* only be called while the packed-refs file is locked (see
|
||||
* lock_packed_refs()). To actually write the packed-refs file, call
|
||||
* commit_packed_refs().
|
||||
*/
|
||||
static void add_packed_ref(const char *refname, const unsigned char *sha1)
|
||||
{
|
||||
struct packed_ref_cache *packed_ref_cache =
|
||||
get_packed_ref_cache(&ref_cache);
|
||||
@ -1741,9 +1747,11 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
|
||||
char *resolve_refdup(const char *refname, int resolve_flags,
|
||||
unsigned char *sha1, int *flags)
|
||||
{
|
||||
return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
|
||||
return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
|
||||
sha1, flags));
|
||||
}
|
||||
|
||||
/* The argument to filter_refs */
|
||||
@ -2531,8 +2539,12 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This should return a meaningful errno on failure */
|
||||
int lock_packed_refs(int flags)
|
||||
/*
|
||||
* Lock the packed-refs file for writing. Flags is passed to
|
||||
* hold_lock_file_for_update(). Return 0 on success. On errors, set
|
||||
* errno appropriately and return a nonzero value.
|
||||
*/
|
||||
static int lock_packed_refs(int flags)
|
||||
{
|
||||
static int timeout_configured = 0;
|
||||
static int timeout_value = 1000;
|
||||
@ -2562,10 +2574,12 @@ int lock_packed_refs(int flags)
|
||||
}
|
||||
|
||||
/*
|
||||
* Commit the packed refs changes.
|
||||
* On error we must make sure that errno contains a meaningful value.
|
||||
* Write the current version of the packed refs cache from memory to
|
||||
* disk. The packed-refs file must already be locked for writing (see
|
||||
* lock_packed_refs()). Return zero on success. On errors, set errno
|
||||
* and return a nonzero value
|
||||
*/
|
||||
int commit_packed_refs(void)
|
||||
static int commit_packed_refs(void)
|
||||
{
|
||||
struct packed_ref_cache *packed_ref_cache =
|
||||
get_packed_ref_cache(&ref_cache);
|
||||
@ -2594,7 +2608,12 @@ int commit_packed_refs(void)
|
||||
return error;
|
||||
}
|
||||
|
||||
void rollback_packed_refs(void)
|
||||
/*
|
||||
* 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(void)
|
||||
{
|
||||
struct packed_ref_cache *packed_ref_cache =
|
||||
get_packed_ref_cache(&ref_cache);
|
||||
@ -2752,7 +2771,14 @@ int pack_refs(unsigned int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int repack_without_refs(struct string_list *refnames, struct strbuf *err)
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* The refs in 'refnames' needn't be sorted. `err` must not be NULL.
|
||||
*/
|
||||
static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
|
||||
{
|
||||
struct ref_dir *packed;
|
||||
struct string_list_item *refname;
|
||||
@ -2817,15 +2843,15 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
|
||||
int delete_ref(const char *refname, const unsigned char *old_sha1,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_delete(transaction, refname,
|
||||
(sha1 && !is_null_sha1(sha1)) ? sha1 : NULL,
|
||||
ref_transaction_delete(transaction, refname, old_sha1,
|
||||
flags, NULL, &err) ||
|
||||
ref_transaction_commit(transaction, &err)) {
|
||||
error("%s", err.buf);
|
||||
@ -2838,6 +2864,44 @@ int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flag
|
||||
return 0;
|
||||
}
|
||||
|
||||
int delete_refs(struct string_list *refnames)
|
||||
{
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
int i, result = 0;
|
||||
|
||||
if (!refnames->nr)
|
||||
return 0;
|
||||
|
||||
result = repack_without_refs(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);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < refnames->nr; i++) {
|
||||
const char *refname = refnames->items[i].string;
|
||||
|
||||
if (delete_ref(refname, NULL, 0))
|
||||
result |= error(_("could not remove reference %s"), refname);
|
||||
}
|
||||
|
||||
out:
|
||||
strbuf_release(&err);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* People using contrib's git-new-workdir have .git/logs/refs ->
|
||||
* /some/other/path/.git/logs/refs, and that may live on another device.
|
||||
@ -4039,6 +4103,98 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ref_present(const char *refname,
|
||||
const struct object_id *oid, int flags, void *cb_data)
|
||||
{
|
||||
struct string_list *affected_refnames = cb_data;
|
||||
|
||||
return string_list_has_string(affected_refnames, refname);
|
||||
}
|
||||
|
||||
int initial_ref_transaction_commit(struct ref_transaction *transaction,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
|
||||
struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
|
||||
int ret = 0, i;
|
||||
int n = transaction->nr;
|
||||
struct ref_update **updates = transaction->updates;
|
||||
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
|
||||
|
||||
assert(err);
|
||||
|
||||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
die("BUG: commit called for transaction that is not open");
|
||||
|
||||
/* Fail if a refname appears more than once in the transaction: */
|
||||
for (i = 0; i < n; i++)
|
||||
string_list_append(&affected_refnames, updates[i]->refname);
|
||||
string_list_sort(&affected_refnames);
|
||||
if (ref_update_reject_duplicates(&affected_refnames, err)) {
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* It's really undefined to call this function in an active
|
||||
* repository or when there are existing references: we are
|
||||
* only locking and changing packed-refs, so (1) any
|
||||
* simultaneous processes might try to change a reference at
|
||||
* the same time we do, and (2) any existing loose versions of
|
||||
* the references that we are setting would have precedence
|
||||
* over our values. But some remote helpers create the remote
|
||||
* "HEAD" and "master" branches before calling this function,
|
||||
* so here we really only check that none of the references
|
||||
* that we are creating already exists.
|
||||
*/
|
||||
if (for_each_rawref(ref_present, &affected_refnames))
|
||||
die("BUG: initial ref transaction called with existing refs");
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
struct ref_update *update = updates[i];
|
||||
|
||||
if ((update->flags & REF_HAVE_OLD) &&
|
||||
!is_null_sha1(update->old_sha1))
|
||||
die("BUG: initial ref transaction with old_sha1 set");
|
||||
if (verify_refname_available(update->refname,
|
||||
&affected_refnames, NULL,
|
||||
loose_refs, err) ||
|
||||
verify_refname_available(update->refname,
|
||||
&affected_refnames, NULL,
|
||||
packed_refs, err)) {
|
||||
ret = TRANSACTION_NAME_CONFLICT;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (lock_packed_refs(0)) {
|
||||
strbuf_addf(err, "unable to lock packed-refs file: %s",
|
||||
strerror(errno));
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
struct ref_update *update = updates[i];
|
||||
|
||||
if ((update->flags & REF_HAVE_NEW) &&
|
||||
!is_null_sha1(update->new_sha1))
|
||||
add_packed_ref(update->refname, update->new_sha1);
|
||||
}
|
||||
|
||||
if (commit_packed_refs()) {
|
||||
strbuf_addf(err, "unable to commit packed-refs file: %s",
|
||||
strerror(errno));
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
transaction->state = REF_TRANSACTION_CLOSED;
|
||||
string_list_clear(&affected_refnames, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *shorten_unambiguous_ref(const char *refname, int strict)
|
||||
{
|
||||
int i;
|
||||
|
Reference in New Issue
Block a user