Commit f44fd59e by Carlos Martín Nieto

refs: check the ref's old value when deleting

Recognize when the reference has changed since we loaded it.
parent 7ee8c7e6
......@@ -398,8 +398,11 @@ GIT_EXTERN(int) git_reference_rename(
* will be immediately removed on disk but the memory will not be freed.
* Callers must call `git_reference_free`.
*
* This function will return an error if the reference has changed
* from the time it was looked up.
*
* @param ref The reference to remove
* @return 0 or an error code
* @return 0, GIT_EMODIFIED or an error code
*/
GIT_EXTERN(int) git_reference_delete(git_reference *ref);
......
......@@ -116,7 +116,15 @@ void git_reference_free(git_reference *reference)
int git_reference_delete(git_reference *ref)
{
return git_refdb_delete(ref->db, ref->name, NULL, NULL);
const git_oid *old_id = NULL;
const char *old_target = NULL;
if (ref->type == GIT_REF_OID)
old_id = &ref->target.oid;
else
old_target = ref->target.symbolic;
return git_refdb_delete(ref->db, ref->name, old_id, old_target);
}
int git_reference_lookup(git_reference **ref_out,
......
......@@ -59,3 +59,36 @@ void test_refs_races__symbolic_create_matching(void)
git_reference_free(ref2);
git_reference_free(ref3);
}
void test_refs_races__delete(void)
{
git_reference *ref, *ref2;
git_oid id, other_id;
git_oid_fromstr(&id, commit_id);
git_oid_fromstr(&other_id, other_commit_id);
/* We can delete a value that matches */
cl_git_pass(git_reference_lookup(&ref, g_repo, refname));
cl_git_pass(git_reference_delete(ref));
git_reference_free(ref);
/* We cannot delete a symbolic value that doesn't match */
cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "HEAD", other_refname, 1, NULL, NULL, refname));
cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
git_reference_free(ref);
git_reference_free(ref2);
cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, NULL, NULL));
git_reference_free(ref);
/* We cannot delete an oid value that doesn't match */
cl_git_pass(git_reference_lookup(&ref, g_repo, refname));
cl_git_pass(git_reference_create_matching(&ref2, g_repo, refname, &other_id, 1, NULL, NULL, &id));
cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
git_reference_free(ref);
git_reference_free(ref2);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment