diff --git a/src/branch.c b/src/branch.c index a1c2abce6..9562805fb 100644 --- a/src/branch.c +++ b/src/branch.c @@ -89,30 +89,59 @@ cleanup: return error; } -static int delete_config_entries_cb( +typedef struct rename_data +{ + git_config *config; + const char *old_name; + const char *new_name; +} rename_data; + +static int rename_config_entries_cb( const char *var_name, const char *value, void *payload) { - git_config *config; + rename_data *data = (rename_data *)payload; GIT_UNUSED(value); - config = (git_config *)payload; + if (data->new_name != NULL) { + git_buf name = GIT_BUF_INIT; + int error; - return git_config_delete(config, var_name); + if (git_buf_printf( + &name, + "branch.%s.%s", + data->new_name, + var_name + strlen("branch") + strlen(data->old_name) + 2) < 0) + return -1; + + error = git_config_set_string( + data->config, + git_buf_cstr(&name), + value); + + git_buf_free(&name); + + if (error) + return error; + } + + return git_config_delete(data->config, var_name); } -static int delete_branch_config_entries( +static int rename_branch_config_entries( git_repository *repo, - const char *branch_name) + const char *old_branch_name, + const char *new_branch_name) { git_config *config; git_buf pattern = GIT_BUF_INIT; int error = -1; + rename_data data; git_buf_sets(&pattern, "branch\\."); - git_buf_puts_escape_regex(&pattern, branch_name); + git_buf_puts_escape_regex(&pattern, old_branch_name); git_buf_puts(&pattern, "\\..+"); if (git_buf_oom(&pattern)) goto cleanup; @@ -120,10 +149,14 @@ static int delete_branch_config_entries( if (git_repository_config__weakptr(&config, repo) < 0) goto cleanup; + data.config = config; + data.old_name = old_branch_name; + data.new_name = new_branch_name; + if ((error = git_config_foreach_match( config, git_buf_cstr(&pattern), - delete_config_entries_cb, config)) < 0) + rename_config_entries_cb, &data)) < 0) goto cleanup; error = 0; @@ -154,9 +187,10 @@ int git_branch_delete(git_reference *branch) return -1; } - if (delete_branch_config_entries( + if (rename_branch_config_entries( git_reference_owner(branch), - git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR), + NULL) < 0) goto on_error; return git_reference_delete(branch); @@ -210,7 +244,8 @@ int git_branch_move( const char *new_branch_name, int force) { - git_buf new_reference_name = GIT_BUF_INIT; + git_buf new_reference_name = GIT_BUF_INIT, + old_branch_name = GIT_BUF_INIT; int error; assert(branch && new_branch_name); @@ -221,10 +256,21 @@ int git_branch_move( if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0) goto cleanup; - error = git_reference_rename(branch, git_buf_cstr(&new_reference_name), force); + if ((error = git_buf_puts(&old_branch_name, git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR))) < 0) + goto cleanup; + + if ((error = git_reference_rename(branch, git_buf_cstr(&new_reference_name), force)) < 0) + goto cleanup; + + if ((error = rename_branch_config_entries( + git_reference_owner(branch), + git_buf_cstr(&old_branch_name), + new_branch_name)) < 0) + goto cleanup; cleanup: git_buf_free(&new_reference_name); + git_buf_free(&old_branch_name); return error; } diff --git a/tests-clar/refs/branches/move.c b/tests-clar/refs/branches/move.c index 62b6042c6..042469016 100644 --- a/tests-clar/refs/branches/move.c +++ b/tests-clar/refs/branches/move.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "refs.h" +#include "config/config_helpers.h" static git_repository *repo; static git_reference *ref; @@ -63,6 +64,27 @@ void test_refs_branches_move__can_force_move_over_an_existing_branch(void) cl_git_pass(git_branch_move(ref, "master", 1)); } +void test_refs_branches_move__moving_a_branch_moves_related_configuration_data(void) +{ + git_reference *branch; + + cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL)); + + assert_config_entry_existence(repo, "branch.track-local.remote", true); + assert_config_entry_existence(repo, "branch.track-local.merge", true); + assert_config_entry_existence(repo, "branch.moved.remote", false); + assert_config_entry_existence(repo, "branch.moved.merge", false); + + cl_git_pass(git_branch_move(branch, "moved", 0)); + + assert_config_entry_existence(repo, "branch.track-local.remote", false); + assert_config_entry_existence(repo, "branch.track-local.merge", false); + assert_config_entry_existence(repo, "branch.moved.remote", true); + assert_config_entry_existence(repo, "branch.moved.merge", true); + + git_reference_free(branch); +} + void test_refs_branches_move__moving_the_branch_pointed_at_by_HEAD_updates_HEAD(void) { git_reference *branch;