mirror of
https://git.proxmox.com/git/libgit2
synced 2025-07-09 23:14:18 +00:00
refs: Remove duplicate rename method
`git_reference_rename` now takes a `force` flag
This commit is contained in:
parent
5f25149e46
commit
7376ad9927
@ -195,21 +195,7 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
|
|||||||
* and on disk.
|
* and on disk.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name);
|
GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force);
|
||||||
|
|
||||||
/**
|
|
||||||
* Rename an existing reference, overwriting an existing one with the
|
|
||||||
* same name, if it exists.
|
|
||||||
*
|
|
||||||
* This method works for both direct and symbolic references.
|
|
||||||
* The new name will be checked for validity and may be
|
|
||||||
* modified into a normalized form.
|
|
||||||
*
|
|
||||||
* The refernece will be immediately renamed in-memory
|
|
||||||
* and on disk.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_reference_rename_f(git_reference *ref, const char *new_name);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete an existing reference
|
* Delete an existing reference
|
||||||
|
274
src/refs.c
274
src/refs.c
@ -80,7 +80,6 @@ static int packed_sort(const void *a, const void *b);
|
|||||||
static int packed_write(git_repository *repo);
|
static int packed_write(git_repository *repo);
|
||||||
|
|
||||||
/* internal helpers */
|
/* internal helpers */
|
||||||
static int reference_rename(git_reference *ref, const char *new_name, int force);
|
|
||||||
static int reference_available(git_repository *repo, const char *ref, const char *old_ref);
|
static int reference_available(git_repository *repo, const char *ref, const char *old_ref);
|
||||||
|
|
||||||
/* name normalization */
|
/* name normalization */
|
||||||
@ -962,137 +961,6 @@ static int reference_available(git_repository *repo, const char *ref, const char
|
|||||||
return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref);
|
return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Rename a reference
|
|
||||||
*
|
|
||||||
* If the reference is packed, we need to rewrite the
|
|
||||||
* packfile to remove the reference from it and create
|
|
||||||
* the reference back as a loose one.
|
|
||||||
*
|
|
||||||
* If the reference is loose, we just rename it on
|
|
||||||
* the filesystem.
|
|
||||||
*
|
|
||||||
* We also need to re-insert the reference on its corresponding
|
|
||||||
* in-memory cache, since the caches are indexed by refname.
|
|
||||||
*/
|
|
||||||
static int reference_rename(git_reference *ref, const char *new_name, int force)
|
|
||||||
{
|
|
||||||
int error;
|
|
||||||
char *old_name;
|
|
||||||
char old_path[GIT_PATH_MAX], new_path[GIT_PATH_MAX], normalized_name[GIT_REFNAME_MAX];
|
|
||||||
git_reference *looked_up_ref, *old_ref = NULL;
|
|
||||||
|
|
||||||
assert(ref);
|
|
||||||
|
|
||||||
/* Ensure the name is valid */
|
|
||||||
error = normalize_name(normalized_name, sizeof(normalized_name), new_name, ref->type & GIT_REF_OID);
|
|
||||||
if (error < GIT_SUCCESS)
|
|
||||||
return git__rethrow(error, "Failed to rename reference");
|
|
||||||
|
|
||||||
new_name = normalized_name;
|
|
||||||
|
|
||||||
/* Ensure we're not going to overwrite an existing reference
|
|
||||||
unless the user has allowed us */
|
|
||||||
error = git_reference_lookup(&looked_up_ref, ref->owner, new_name);
|
|
||||||
if (error == GIT_SUCCESS && !force)
|
|
||||||
return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists");
|
|
||||||
|
|
||||||
if (error < GIT_SUCCESS &&
|
|
||||||
error != GIT_ENOTFOUND)
|
|
||||||
return git__rethrow(error, "Failed to rename reference");
|
|
||||||
|
|
||||||
if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
|
|
||||||
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Reference already exists");
|
|
||||||
|
|
||||||
old_name = ref->name;
|
|
||||||
ref->name = git__strdup(new_name);
|
|
||||||
|
|
||||||
if (ref->name == NULL) {
|
|
||||||
ref->name = old_name;
|
|
||||||
return GIT_ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ref->type & GIT_REF_PACKED) {
|
|
||||||
/* write the packfile to disk; note
|
|
||||||
* that the state of the in-memory cache is not
|
|
||||||
* consistent, because the reference is indexed
|
|
||||||
* by its old name but it already has the new one.
|
|
||||||
* This doesn't affect writing, though, and allows
|
|
||||||
* us to rollback if writing fails
|
|
||||||
*/
|
|
||||||
|
|
||||||
ref->type &= ~GIT_REF_PACKED;
|
|
||||||
|
|
||||||
/* Create the loose ref under its new name */
|
|
||||||
error = loose_write(ref);
|
|
||||||
if (error < GIT_SUCCESS) {
|
|
||||||
ref->type |= GIT_REF_PACKED;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove from the packfile cache in order to avoid packing it back
|
|
||||||
* Note : we do not rely on git_reference_delete() because this would
|
|
||||||
* invalidate the reference.
|
|
||||||
*/
|
|
||||||
git_hashtable_remove(ref->owner->references.packfile, old_name);
|
|
||||||
|
|
||||||
/* Recreate the packed-refs file without the reference */
|
|
||||||
error = packed_write(ref->owner);
|
|
||||||
if (error < GIT_SUCCESS)
|
|
||||||
goto rename_loose_to_old_name;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
git__joinpath(old_path, ref->owner->path_repository, old_name);
|
|
||||||
git__joinpath(new_path, ref->owner->path_repository, ref->name);
|
|
||||||
|
|
||||||
error = gitfo_mv_force(old_path, new_path);
|
|
||||||
if (error < GIT_SUCCESS)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
/* Once succesfully renamed, remove from the cache the reference known by its old name*/
|
|
||||||
git_hashtable_remove(ref->owner->references.loose_cache, old_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store the renamed reference into the loose ref cache */
|
|
||||||
error = git_hashtable_insert2(ref->owner->references.loose_cache, ref->name, ref, (void **) &old_ref);
|
|
||||||
|
|
||||||
/* If we force-replaced, we need to free the old reference */
|
|
||||||
if(old_ref)
|
|
||||||
reference_free(old_ref);
|
|
||||||
|
|
||||||
free(old_name);
|
|
||||||
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
/* restore the old name if this failed */
|
|
||||||
free(ref->name);
|
|
||||||
ref->name = old_name;
|
|
||||||
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
|
|
||||||
|
|
||||||
rename_loose_to_old_name:
|
|
||||||
/* If we hit this point. Something *bad* happened! Think "Ghostbusters
|
|
||||||
* crossing the streams" definition of bad.
|
|
||||||
* Either the packed-refs has been correctly generated and something else
|
|
||||||
* has gone wrong, or the writing of the new packed-refs has failed, and
|
|
||||||
* we're stuck with the old one. As a loose ref always takes priority over
|
|
||||||
* a packed ref, we'll eventually try and rename the generated loose ref to
|
|
||||||
* its former name. It even that fails, well... we might have lost the reference
|
|
||||||
* for good. :-/
|
|
||||||
*/
|
|
||||||
|
|
||||||
git__joinpath(old_path, ref->owner->path_repository, ref->name);
|
|
||||||
git__joinpath(new_path, ref->owner->path_repository, old_name);
|
|
||||||
|
|
||||||
/* No error checking. We'll return the initial error */
|
|
||||||
gitfo_mv_force(old_path, new_path);
|
|
||||||
|
|
||||||
/* restore the old name */
|
|
||||||
free(ref->name);
|
|
||||||
ref->name = old_name;
|
|
||||||
|
|
||||||
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************
|
/*****************************************
|
||||||
* External Library API
|
* External Library API
|
||||||
*****************************************/
|
*****************************************/
|
||||||
@ -1421,6 +1289,138 @@ int git_reference_set_target(git_reference *ref, const char *target)
|
|||||||
* Other
|
* Other
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rename a reference
|
||||||
|
*
|
||||||
|
* If the reference is packed, we need to rewrite the
|
||||||
|
* packfile to remove the reference from it and create
|
||||||
|
* the reference back as a loose one.
|
||||||
|
*
|
||||||
|
* If the reference is loose, we just rename it on
|
||||||
|
* the filesystem.
|
||||||
|
*
|
||||||
|
* We also need to re-insert the reference on its corresponding
|
||||||
|
* in-memory cache, since the caches are indexed by refname.
|
||||||
|
*/
|
||||||
|
int git_reference_rename(git_reference *ref, const char *new_name, int force)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
char *old_name;
|
||||||
|
char old_path[GIT_PATH_MAX], new_path[GIT_PATH_MAX], normalized_name[GIT_REFNAME_MAX];
|
||||||
|
git_reference *looked_up_ref, *old_ref = NULL;
|
||||||
|
|
||||||
|
assert(ref);
|
||||||
|
|
||||||
|
/* Ensure the name is valid */
|
||||||
|
error = normalize_name(normalized_name, sizeof(normalized_name), new_name, ref->type & GIT_REF_OID);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
return git__rethrow(error, "Failed to rename reference");
|
||||||
|
|
||||||
|
new_name = normalized_name;
|
||||||
|
|
||||||
|
/* Ensure we're not going to overwrite an existing reference
|
||||||
|
unless the user has allowed us */
|
||||||
|
error = git_reference_lookup(&looked_up_ref, ref->owner, new_name);
|
||||||
|
if (error == GIT_SUCCESS && !force)
|
||||||
|
return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists");
|
||||||
|
|
||||||
|
if (error < GIT_SUCCESS &&
|
||||||
|
error != GIT_ENOTFOUND)
|
||||||
|
return git__rethrow(error, "Failed to rename reference");
|
||||||
|
|
||||||
|
if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
|
||||||
|
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Reference already exists");
|
||||||
|
|
||||||
|
old_name = ref->name;
|
||||||
|
ref->name = git__strdup(new_name);
|
||||||
|
|
||||||
|
if (ref->name == NULL) {
|
||||||
|
ref->name = old_name;
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ref->type & GIT_REF_PACKED) {
|
||||||
|
/* write the packfile to disk; note
|
||||||
|
* that the state of the in-memory cache is not
|
||||||
|
* consistent, because the reference is indexed
|
||||||
|
* by its old name but it already has the new one.
|
||||||
|
* This doesn't affect writing, though, and allows
|
||||||
|
* us to rollback if writing fails
|
||||||
|
*/
|
||||||
|
|
||||||
|
ref->type &= ~GIT_REF_PACKED;
|
||||||
|
|
||||||
|
/* Create the loose ref under its new name */
|
||||||
|
error = loose_write(ref);
|
||||||
|
if (error < GIT_SUCCESS) {
|
||||||
|
ref->type |= GIT_REF_PACKED;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove from the packfile cache in order to avoid packing it back
|
||||||
|
* Note : we do not rely on git_reference_delete() because this would
|
||||||
|
* invalidate the reference.
|
||||||
|
*/
|
||||||
|
git_hashtable_remove(ref->owner->references.packfile, old_name);
|
||||||
|
|
||||||
|
/* Recreate the packed-refs file without the reference */
|
||||||
|
error = packed_write(ref->owner);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
goto rename_loose_to_old_name;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
git__joinpath(old_path, ref->owner->path_repository, old_name);
|
||||||
|
git__joinpath(new_path, ref->owner->path_repository, ref->name);
|
||||||
|
|
||||||
|
error = gitfo_mv_force(old_path, new_path);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* Once succesfully renamed, remove from the cache the reference known by its old name*/
|
||||||
|
git_hashtable_remove(ref->owner->references.loose_cache, old_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the renamed reference into the loose ref cache */
|
||||||
|
error = git_hashtable_insert2(ref->owner->references.loose_cache, ref->name, ref, (void **) &old_ref);
|
||||||
|
|
||||||
|
/* If we force-replaced, we need to free the old reference */
|
||||||
|
if(old_ref)
|
||||||
|
reference_free(old_ref);
|
||||||
|
|
||||||
|
free(old_name);
|
||||||
|
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
/* restore the old name if this failed */
|
||||||
|
free(ref->name);
|
||||||
|
ref->name = old_name;
|
||||||
|
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
|
||||||
|
|
||||||
|
rename_loose_to_old_name:
|
||||||
|
/* If we hit this point. Something *bad* happened! Think "Ghostbusters
|
||||||
|
* crossing the streams" definition of bad.
|
||||||
|
* Either the packed-refs has been correctly generated and something else
|
||||||
|
* has gone wrong, or the writing of the new packed-refs has failed, and
|
||||||
|
* we're stuck with the old one. As a loose ref always takes priority over
|
||||||
|
* a packed ref, we'll eventually try and rename the generated loose ref to
|
||||||
|
* its former name. It even that fails, well... we might have lost the reference
|
||||||
|
* for good. :-/
|
||||||
|
*/
|
||||||
|
|
||||||
|
git__joinpath(old_path, ref->owner->path_repository, ref->name);
|
||||||
|
git__joinpath(new_path, ref->owner->path_repository, old_name);
|
||||||
|
|
||||||
|
/* No error checking. We'll return the initial error */
|
||||||
|
gitfo_mv_force(old_path, new_path);
|
||||||
|
|
||||||
|
/* restore the old name */
|
||||||
|
free(ref->name);
|
||||||
|
ref->name = old_name;
|
||||||
|
|
||||||
|
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete a reference.
|
* Delete a reference.
|
||||||
*
|
*
|
||||||
@ -1474,16 +1474,6 @@ cleanup:
|
|||||||
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to delete reference");
|
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to delete reference");
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_reference_rename(git_reference *ref, const char *new_name)
|
|
||||||
{
|
|
||||||
return reference_rename(ref, new_name, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int git_reference_rename_f(git_reference *ref, const char *new_name)
|
|
||||||
{
|
|
||||||
return reference_rename(ref, new_name, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
|
int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
|
||||||
{
|
{
|
||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
|
@ -494,7 +494,7 @@ BEGIN_TEST(rename0, "rename a loose reference")
|
|||||||
must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);
|
must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);
|
||||||
|
|
||||||
/* Now that the reference is renamed... */
|
/* Now that the reference is renamed... */
|
||||||
must_pass(git_reference_rename(looked_up_ref, new_name));
|
must_pass(git_reference_rename(looked_up_ref, new_name, 0));
|
||||||
must_be_true(!strcmp(looked_up_ref->name, new_name));
|
must_be_true(!strcmp(looked_up_ref->name, new_name));
|
||||||
|
|
||||||
/* ...It can't be looked-up with the old name... */
|
/* ...It can't be looked-up with the old name... */
|
||||||
@ -534,7 +534,7 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)")
|
|||||||
must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0);
|
must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0);
|
||||||
|
|
||||||
/* Now that the reference is renamed... */
|
/* Now that the reference is renamed... */
|
||||||
must_pass(git_reference_rename(looked_up_ref, brand_new_name));
|
must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0));
|
||||||
must_be_true(!strcmp(looked_up_ref->name, brand_new_name));
|
must_be_true(!strcmp(looked_up_ref->name, brand_new_name));
|
||||||
|
|
||||||
/* ...It can't be looked-up with the old name... */
|
/* ...It can't be looked-up with the old name... */
|
||||||
@ -580,7 +580,7 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference
|
|||||||
must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0);
|
must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0);
|
||||||
|
|
||||||
/* Now that the reference is renamed... */
|
/* Now that the reference is renamed... */
|
||||||
must_pass(git_reference_rename(looked_up_ref, brand_new_name));
|
must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0));
|
||||||
|
|
||||||
/* Lookup the other reference */
|
/* Lookup the other reference */
|
||||||
must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
|
must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
|
||||||
@ -604,7 +604,7 @@ BEGIN_TEST(rename3, "can not rename a reference with the name of an existing ref
|
|||||||
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
|
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
|
||||||
|
|
||||||
/* Can not be renamed to the name of another existing reference. */
|
/* Can not be renamed to the name of another existing reference. */
|
||||||
must_fail(git_reference_rename(looked_up_ref, packed_test_head_name));
|
must_fail(git_reference_rename(looked_up_ref, packed_test_head_name, 0));
|
||||||
|
|
||||||
/* Failure to rename it hasn't corrupted its state */
|
/* Failure to rename it hasn't corrupted its state */
|
||||||
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
|
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
|
||||||
@ -623,10 +623,10 @@ BEGIN_TEST(rename4, "can not rename a reference with an invalid name")
|
|||||||
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
|
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
|
||||||
|
|
||||||
/* Can not be renamed with an invalid name. */
|
/* Can not be renamed with an invalid name. */
|
||||||
must_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name."));
|
must_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.", 0));
|
||||||
|
|
||||||
/* Can not be renamed outside of the refs hierarchy. */
|
/* Can not be renamed outside of the refs hierarchy. */
|
||||||
must_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you"));
|
must_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you", 0));
|
||||||
|
|
||||||
/* Failure to rename it hasn't corrupted its state */
|
/* Failure to rename it hasn't corrupted its state */
|
||||||
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
|
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
|
||||||
@ -645,7 +645,7 @@ BEGIN_TEST(rename5, "can force-rename a reference with the name of an existing r
|
|||||||
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
|
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
|
||||||
|
|
||||||
/* Can be force-renamed to the name of another existing reference. */
|
/* Can be force-renamed to the name of another existing reference. */
|
||||||
must_pass(git_reference_rename_f(looked_up_ref, packed_test_head_name));
|
must_pass(git_reference_rename(looked_up_ref, packed_test_head_name, 1));
|
||||||
|
|
||||||
/* Check we actually renamed it */
|
/* Check we actually renamed it */
|
||||||
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
|
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
|
||||||
|
Loading…
Reference in New Issue
Block a user