Commit 383f164a09e46e7f5008073d00504a43478e0564

nulltoken 2012-09-07T17:55:30

branch: rename config section upon moving

diff --git a/src/branch.c b/src/branch.c
index a1c2abc..9562805 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;
+
+		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(config, var_name);
+	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 62b6042..0424690 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;