Commit 1cc974ab625c2fa0794130eb97ca88c449fc1a06

Ben Straub 2014-01-27T14:40:31

Augment clone API with reflog parameters

diff --git a/include/git2/clone.h b/include/git2/clone.h
index 59a73aa..3e885d1 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -45,6 +45,8 @@ GIT_BEGIN_DECL
  *   default is "origin".
  * - `checkout_branch` gives the name of the branch to checkout.  NULL
  *   means use the remote's HEAD.
+ * - `signature` is the identity used when updating the reflog. NULL means to
+ *   use the default signature using the config.
  */
 
 typedef struct git_clone_options {
@@ -57,6 +59,7 @@ typedef struct git_clone_options {
 	int ignore_cert_errors;
 	const char *remote_name;
 	const char* checkout_branch;
+	git_signature *signature;
 } git_clone_options;
 
 #define GIT_CLONE_OPTIONS_VERSION 1
@@ -96,6 +99,7 @@ GIT_EXTERN(int) git_clone(
  * @param co_opts options to use during checkout
  * @param branch the branch to checkout after the clone, pass NULL for the
  *        remote's default branch
+ * @param signature The identity used when updating the reflog.
  * @return 0 on success, any non-zero return value from a callback
  *         function, or a negative value to indicate an error (use
  *         `giterr_last` for a detailed error message)
@@ -104,7 +108,8 @@ GIT_EXTERN(int) git_clone_into(
 	git_repository *repo,
 	git_remote *remote,
 	const git_checkout_opts *co_opts,
-	const char *branch);
+	const char *branch,
+	const git_signature *signature);
 
 /** @} */
 GIT_END_DECL
diff --git a/src/clone.c b/src/clone.c
index 288e9d2..97d25bd 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -27,7 +27,9 @@ static int create_branch(
 	git_reference **branch,
 	git_repository *repo,
 	const git_oid *target,
-	const char *name)
+	const char *name,
+	const git_signature *signature,
+	const char *log_message)
 {
 	git_commit *head_obj = NULL;
 	git_reference *branch_ref = NULL;
@@ -38,7 +40,7 @@ static int create_branch(
 		return error;
 
 	/* Create the new branch */
-	error = git_branch_create(&branch_ref, repo, name, head_obj, 0, NULL, NULL);
+	error = git_branch_create(&branch_ref, repo, name, head_obj, 0, signature, log_message);
 
 	git_commit_free(head_obj);
 
@@ -87,11 +89,13 @@ static int create_tracking_branch(
 	git_reference **branch,
 	git_repository *repo,
 	const git_oid *target,
-	const char *branch_name)
+	const char *branch_name,
+	const git_signature *signature,
+	const char *log_message)
 {
 	int error;
 
-	if ((error = create_branch(branch, repo, target, branch_name)) < 0)
+	if ((error = create_branch(branch, repo, target, branch_name, signature, log_message)) < 0)
 		return error;
 
 	return setup_tracking_config(
@@ -153,15 +157,17 @@ static int update_head_to_new_branch(
 	git_repository *repo,
 	const git_oid *target,
 	const char *name,
+	const git_signature *signature,
 	const char *reflog_message)
 {
 	git_reference *tracking_branch = NULL;
-	int error = create_tracking_branch(&tracking_branch, repo, target, name);
+	int error = create_tracking_branch(&tracking_branch, repo, target, name,
+			signature, reflog_message);
 
 	if (!error)
 		error = git_repository_set_head(
 			repo, git_reference_name(tracking_branch),
-			NULL, reflog_message);
+			signature, reflog_message);
 
 	git_reference_free(tracking_branch);
 
@@ -171,6 +177,7 @@ static int update_head_to_new_branch(
 static int update_head_to_remote(
 		git_repository *repo,
 		git_remote *remote,
+		const git_signature *signature,
 		const char *reflog_message)
 {
 	int error = 0;
@@ -221,7 +228,7 @@ static int update_head_to_remote(
 			repo,
 			&head_info.remote_head_oid,
 			git_buf_cstr(&head_info.branchname),
-			reflog_message);
+			signature, reflog_message);
 		goto cleanup;
 	}
 
@@ -236,7 +243,7 @@ static int update_head_to_remote(
 			repo,
 			&head_info.remote_head_oid,
 			git_buf_cstr(&head_info.branchname),
-			reflog_message);
+			signature, reflog_message);
 	} else {
 		error = git_repository_set_head_detached(
 			repo, &head_info.remote_head_oid, NULL, reflog_message);
@@ -252,6 +259,7 @@ static int update_head_to_branch(
 		git_repository *repo,
 		const char *remote_name,
 		const char *branch,
+		const git_signature *signature,
 		const char *reflog_message)
 {
 	int retcode;
@@ -267,7 +275,8 @@ static int update_head_to_branch(
 	if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
 		goto cleanup;
 
-	retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch, reflog_message);
+	retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch,
+			signature, reflog_message);
 
 cleanup:
 	git_reference_free(remote_ref);
@@ -327,7 +336,7 @@ static bool should_checkout(
 	return !git_repository_head_unborn(repo);
 }
 
-int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch)
+int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch, const git_signature *signature)
 {
 	int error = 0, old_fetchhead;
 	git_strarray refspecs;
@@ -355,10 +364,11 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_
 		goto cleanup;
 
 	if (branch)
-		error = update_head_to_branch(repo, git_remote_name(remote), branch, git_buf_cstr(&reflog_message));
+		error = update_head_to_branch(repo, git_remote_name(remote), branch,
+				signature, git_buf_cstr(&reflog_message));
 	/* Point HEAD to the same ref as the remote's head */
 	else
-		error = update_head_to_remote(repo, remote, git_buf_cstr(&reflog_message));
+		error = update_head_to_remote(repo, remote, signature, git_buf_cstr(&reflog_message));
 
 	if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
 		error = git_checkout_head(repo, co_opts);
@@ -414,7 +424,7 @@ int git_clone(
 
 	if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
 		error = git_clone_into(
-			repo, origin, &options.checkout_opts, options.checkout_branch);
+			repo, origin, &options.checkout_opts, options.checkout_branch, options.signature);
 
 		git_remote_free(origin);
 	}
diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c
index 863412c..14d2bbf 100644
--- a/tests/clone/nonetwork.c
+++ b/tests/clone/nonetwork.c
@@ -232,3 +232,47 @@ void test_clone_nonetwork__can_detached_head(void)
 
 	cl_fixture_cleanup("./foo1");
 }
+
+static void assert_correct_reflog(const char *name)
+{
+	git_reflog *log;
+	const git_reflog_entry *entry;
+	char expected_log_message[128] = {0};
+
+	sprintf(expected_log_message, "clone: from %s", cl_git_fixture_url("testrepo.git"));
+
+	cl_git_pass(git_reflog_read(&log, g_repo, name));
+	cl_assert_equal_i(1, git_reflog_entrycount(log));
+	entry = git_reflog_entry_byindex(log, 0);
+	cl_assert_equal_s(expected_log_message, git_reflog_entry_message(entry));
+	cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
+
+	git_reflog_free(log);
+}
+
+void test_clone_nonetwork__clone_updates_reflog_properly(void)
+{
+	cl_git_pass(git_signature_now(&g_options.signature, "Me", "foo@example.com"));
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+	assert_correct_reflog("HEAD");
+	assert_correct_reflog("refs/heads/master");
+
+	git_signature_free(g_options.signature);
+}
+
+void test_clone_nonetwork__clone_into_updates_reflog_properly(void)
+{
+	git_remote *remote;
+	git_signature *sig;
+	cl_git_pass(git_signature_now(&sig, "Me", "foo@example.com"));
+
+	cl_git_pass(git_repository_init(&g_repo, "./foo", false));
+	cl_git_pass(git_remote_create(&remote, g_repo, "origin", cl_git_fixture_url("testrepo.git")));
+	cl_git_pass(git_clone_into(g_repo, remote, NULL, NULL, sig));
+
+	assert_correct_reflog("HEAD");
+	assert_correct_reflog("refs/heads/master");
+
+	git_remote_free(remote);
+	git_signature_free(g_options.signature);
+}
diff --git a/tests/online/clone.c b/tests/online/clone.c
index be4421a..1222d17 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -147,7 +147,7 @@ void test_online_clone__clone_into(void)
 	callbacks.payload = &fetch_progress_cb_was_called;
 	git_remote_set_callbacks(remote, &callbacks);
 
-	cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL));
+	cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL, NULL));
 
 	cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
 	cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));