summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Martí <vicent@github.com>2013-04-11 06:34:59 -0700
committerVicent Martí <vicent@github.com>2013-04-11 06:34:59 -0700
commitea8bac37b0c3a302b8f740cbeb4af50d0626815b (patch)
treea6d3eb230c55f683d4c65921552a132bcc2c7a37
parentacd4077182b508f5c88353ddfcf31e14614d0d46 (diff)
parentd59942c2aba2fa5f9570b37e3bc9eaf34f16d671 (diff)
downloadlibgit2-ea8bac37b0c3a302b8f740cbeb4af50d0626815b.tar.gz
Merge pull request #1450 from carlosmn/branch-upstream
Branch upstream configuration
-rw-r--r--include/git2/branch.h16
-rw-r--r--src/branch.c179
-rw-r--r--src/branch.h2
-rw-r--r--src/remote.c2
-rw-r--r--src/revparse.c2
-rw-r--r--src/submodule.c2
-rw-r--r--tests-clar/clone/empty.c2
-rw-r--r--tests-clar/refs/branches/remote.c2
-rw-r--r--tests-clar/refs/branches/tracking.c95
-rw-r--r--tests-clar/refs/branches/trackingname.c42
-rw-r--r--tests-clar/refs/branches/upstream.c130
-rw-r--r--tests-clar/refs/branches/upstreamname.c42
12 files changed, 337 insertions, 179 deletions
diff --git a/include/git2/branch.h b/include/git2/branch.h
index 89a1396af..b15171360 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -171,11 +171,23 @@ GIT_EXTERN(int) git_branch_name(const char **out,
* @return 0 on success; GIT_ENOTFOUND when no remote tracking
* reference exists, otherwise an error code.
*/
-GIT_EXTERN(int) git_branch_tracking(
+GIT_EXTERN(int) git_branch_upstream(
git_reference **out,
git_reference *branch);
/**
+ * Set the upstream configuration for a given local branch
+ *
+ * @param branch the branch to configure
+ *
+ * @param upstream_name remote-tracking or local branch to set as
+ * upstream. Pass NULL to unset.
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_branch_set_upstream(git_reference *branch, const char *upstream_name);
+
+/**
* Return the name of the reference supporting the remote tracking branch,
* given the name of a local branch reference.
*
@@ -193,7 +205,7 @@ GIT_EXTERN(int) git_branch_tracking(
* including the trailing NUL byte; GIT_ENOTFOUND when no remote tracking
* reference exists, otherwise an error code.
*/
-GIT_EXTERN(int) git_branch_tracking_name(
+GIT_EXTERN(int) git_branch_upstream_name(
char *tracking_branch_name_out,
size_t buffer_size,
git_repository *repo,
diff --git a/src/branch.c b/src/branch.c
index 45ecca751..e7088790e 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -228,7 +228,7 @@ int git_branch_name(const char **out, git_reference *ref)
return 0;
}
-static int retrieve_tracking_configuration(
+static int retrieve_upstream_configuration(
const char **out,
git_repository *repo,
const char *canonical_branch_name,
@@ -250,7 +250,7 @@ static int retrieve_tracking_configuration(
return error;
}
-int git_branch_tracking__name(
+int git_branch_upstream__name(
git_buf *tracking_name,
git_repository *repo,
const char *canonical_branch_name)
@@ -266,11 +266,11 @@ int git_branch_tracking__name(
if (!git_reference__is_branch(canonical_branch_name))
return not_a_local_branch(canonical_branch_name);
- if ((error = retrieve_tracking_configuration(
+ if ((error = retrieve_upstream_configuration(
&remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 0)
goto cleanup;
- if ((error = retrieve_tracking_configuration(
+ if ((error = retrieve_upstream_configuration(
&merge_name, repo, canonical_branch_name, "branch.%s.merge")) < 0)
goto cleanup;
@@ -305,23 +305,16 @@ cleanup:
return error;
}
-int git_branch_remote_name(
- char *remote_name_out,
- size_t buffer_size,
- git_repository *repo,
- const char *canonical_branch_name)
+static int remote_name(git_buf *buf, git_repository *repo, const char *canonical_branch_name)
{
git_strarray remote_list = {0};
- size_t i, remote_name_size;
+ size_t i;
git_remote *remote;
const git_refspec *fetchspec;
int error = 0;
char *remote_name = NULL;
- assert(repo && canonical_branch_name);
-
- if (remote_name_out && buffer_size)
- *remote_name_out = '\0';
+ assert(buf && repo && canonical_branch_name);
/* Verify that this is a remote branch */
if (!git_reference__is_remote(canonical_branch_name)) {
@@ -338,7 +331,7 @@ int git_branch_remote_name(
/* Find matching remotes */
for (i = 0; i < remote_list.count; i++) {
if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
- goto cleanup;
+ continue;
fetchspec = git_remote_fetchspec(remote);
@@ -362,23 +355,10 @@ int git_branch_remote_name(
}
if (remote_name) {
- remote_name_size = strlen(remote_name) + 1;
- error = (int) remote_name_size;
-
- if (remote_name_out) {
- if(remote_name_size > buffer_size) {
- giterr_set(
- GITERR_INVALID,
- "Buffer too short to hold the remote name.");
- error = GIT_ERROR;
- goto cleanup;
- }
-
- memcpy(remote_name_out, remote_name, remote_name_size);
- }
+ git_buf_clear(buf);
+ error = git_buf_puts(buf, remote_name);
} else {
error = GIT_ENOTFOUND;
- goto cleanup;
}
cleanup:
@@ -386,7 +366,24 @@ cleanup:
return error;
}
-int git_branch_tracking_name(
+int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo, const char *refname)
+{
+ int ret;
+ git_buf buf = GIT_BUF_INIT;
+
+ if ((ret = remote_name(&buf, repo, refname)) < 0)
+ return ret;
+
+ if (buffer)
+ git_buf_copy_cstr(buffer, buffer_len, &buf);
+
+ ret = git_buf_len(&buf) + 1;
+ git_buf_free(&buf);
+
+ return ret;
+}
+
+int git_branch_upstream_name(
char *tracking_branch_name_out,
size_t buffer_size,
git_repository *repo,
@@ -400,7 +397,7 @@ int git_branch_tracking_name(
if (tracking_branch_name_out && buffer_size)
*tracking_branch_name_out = '\0';
- if ((error = git_branch_tracking__name(
+ if ((error = git_branch_upstream__name(
&buf, repo, canonical_branch_name)) < 0)
goto cleanup;
@@ -422,14 +419,14 @@ cleanup:
return (int)error;
}
-int git_branch_tracking(
+int git_branch_upstream(
git_reference **tracking_out,
git_reference *branch)
{
int error;
git_buf tracking_name = GIT_BUF_INIT;
- if ((error = git_branch_tracking__name(&tracking_name,
+ if ((error = git_branch_upstream__name(&tracking_name,
git_reference_owner(branch), git_reference_name(branch))) < 0)
return error;
@@ -442,6 +439,120 @@ int git_branch_tracking(
return error;
}
+static int unset_upstream(git_config *config, const char *shortname)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
+ return -1;
+
+ if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
+ goto on_error;
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
+ goto on_error;
+
+ if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
+ goto on_error;
+
+ git_buf_free(&buf);
+ return 0;
+
+on_error:
+ git_buf_free(&buf);
+ return -1;
+}
+
+int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
+{
+ git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
+ git_reference *upstream;
+ git_repository *repo;
+ git_remote *remote = NULL;
+ git_config *config;
+ const char *name, *shortname;
+ int local;
+ const git_refspec *fetchspec;
+
+ name = git_reference_name(branch);
+ if (!git_reference__is_branch(name))
+ return not_a_local_branch(name);
+
+ if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
+ return -1;
+
+ shortname = name + strlen(GIT_REFS_HEADS_DIR);
+
+ if (upstream_name == NULL)
+ return unset_upstream(config, shortname);
+
+ repo = git_reference_owner(branch);
+
+ /* First we need to figure out whether it's a branch or remote-tracking */
+ if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
+ local = 1;
+ else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
+ local = 0;
+ else
+ return GIT_ENOTFOUND;
+
+ /*
+ * If it's local, the remote is "." and the branch name is
+ * simply the refname. Otherwise we need to figure out what
+ * the remote-tracking branch's name on the remote is and use
+ * that.
+ */
+ if (local)
+ git_buf_puts(&value, ".");
+ else
+ remote_name(&value, repo, git_reference_name(upstream));
+
+ if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
+ goto on_error;
+
+ if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
+ goto on_error;
+
+ if (local) {
+ if (git_buf_puts(&value, git_reference_name(branch)) < 0)
+ goto on_error;
+ } else {
+ /* Get the remoe-tracking branch's refname in its repo */
+ if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0)
+ goto on_error;
+
+ fetchspec = git_remote_fetchspec(remote);
+ git_buf_clear(&value);
+ if (git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
+ goto on_error;
+
+ git_remote_free(remote);
+ remote = NULL;
+ }
+
+ git_buf_clear(&key);
+ if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
+ goto on_error;
+
+ if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
+ goto on_error;
+
+ git_reference_free(upstream);
+ git_buf_free(&key);
+ git_buf_free(&value);
+
+ return 0;
+
+on_error:
+ git_reference_free(upstream);
+ git_buf_free(&key);
+ git_buf_free(&value);
+ git_remote_free(remote);
+
+ return -1;
+}
+
int git_branch_is_head(
git_reference *branch)
{
diff --git a/src/branch.h b/src/branch.h
index 8a26c4fea..d02f2af0d 100644
--- a/src/branch.h
+++ b/src/branch.h
@@ -9,7 +9,7 @@
#include "buffer.h"
-int git_branch_tracking__name(
+int git_branch_upstream__name(
git_buf *tracking_name,
git_repository *repo,
const char *canonical_branch_name);
diff --git a/src/remote.c b/src/remote.c
index a6f62d6a5..896361e30 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -705,7 +705,7 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_ve
if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 ||
(!git_reference_is_branch(resolved_ref)) ||
- (error = git_branch_tracking(&tracking_ref, resolved_ref)) < 0 ||
+ (error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
(error = git_refspec_transform_l(&remote_name, &remote->fetch, git_reference_name(tracking_ref))) < 0) {
/* Not an error if HEAD is orphaned or no tracking branch */
if (error == GIT_ENOTFOUND)
diff --git a/src/revparse.c b/src/revparse.c
index b1eb51b41..fd62a2fd2 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -356,7 +356,7 @@ static int retrieve_remote_tracking_reference(git_reference **base_ref, const ch
goto cleanup;
}
- if ((error = git_branch_tracking(&tracking, ref)) < 0)
+ if ((error = git_branch_upstream(&tracking, ref)) < 0)
goto cleanup;
*base_ref = tracking;
diff --git a/src/submodule.c b/src/submodule.c
index 066a881cb..2fdaf2f77 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -1327,7 +1327,7 @@ static int lookup_head_remote(git_buf *url, git_repository *repo)
goto cleanup;
}
- if ((error = git_branch_tracking(&remote, head)) < 0)
+ if ((error = git_branch_upstream(&remote, head)) < 0)
goto cleanup;
/* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
diff --git a/tests-clar/clone/empty.c b/tests-clar/clone/empty.c
index 0f867257a..f190523b6 100644
--- a/tests-clar/clone/empty.c
+++ b/tests-clar/clone/empty.c
@@ -49,7 +49,7 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
/* ...one can still retrieve the name of the remote tracking reference */
cl_assert_equal_i((int)strlen(expected_tracked_branch_name) + 1,
- git_branch_tracking_name(buffer, 1024, g_repo_cloned, local_name));
+ git_branch_upstream_name(buffer, 1024, g_repo_cloned, local_name));
cl_assert_equal_s(expected_tracked_branch_name, buffer);
diff --git a/tests-clar/refs/branches/remote.c b/tests-clar/refs/branches/remote.c
index 5272d1236..2beef3724 100644
--- a/tests-clar/refs/branches/remote.c
+++ b/tests-clar/refs/branches/remote.c
@@ -42,7 +42,7 @@ void test_refs_branches_remote__insufficient_buffer_returns_error(void)
cl_git_fail_with(git_branch_remote_name(remotename,
expected_remote_name_length - 1, g_repo, remote_tracking_branch_name),
- GIT_ERROR);
+ expected_remote_name_length);
}
void test_refs_branches_remote__no_matching_remote_returns_error(void)
diff --git a/tests-clar/refs/branches/tracking.c b/tests-clar/refs/branches/tracking.c
deleted file mode 100644
index 30599d9fc..000000000
--- a/tests-clar/refs/branches/tracking.c
+++ /dev/null
@@ -1,95 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-
-static git_repository *repo;
-static git_reference *branch, *tracking;
-
-void test_refs_branches_tracking__initialize(void)
-{
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
-
- branch = NULL;
- tracking = NULL;
-}
-
-void test_refs_branches_tracking__cleanup(void)
-{
- git_reference_free(tracking);
- git_reference_free(branch);
- branch = NULL;
-
- git_repository_free(repo);
- repo = NULL;
-}
-
-void test_refs_branches_tracking__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
-
- cl_git_pass(git_branch_tracking(&tracking, branch));
-
- cl_assert_equal_s("refs/remotes/test/master", git_reference_name(tracking));
-}
-
-void test_refs_branches_tracking__can_retrieve_the_local_tracking_reference_of_a_local_branch(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local"));
-
- cl_git_pass(git_branch_tracking(&tracking, branch));
-
- cl_assert_equal_s("refs/heads/master", git_reference_name(tracking));
-}
-
-void test_refs_branches_tracking__cannot_retrieve_a_remote_tracking_reference_from_a_non_branch(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
-
- cl_git_fail(git_branch_tracking(&tracking, branch));
-}
-
-void test_refs_branches_tracking__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees"));
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_branch_tracking(&tracking, branch));
-}
-
-void test_refs_branches_tracking__trying_to_retrieve_a_remote_tracking_reference_from_a_branch_with_no_fetchspec_returns_GIT_ENOTFOUND(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/cannot-fetch"));
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_branch_tracking(&tracking, branch));
-}
-
-static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name)
-{
- git_reference *branch;
-
- cl_assert_equal_i(GIT_OBJ_COMMIT, git_object_type((git_object*)target));
- cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0));
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_branch_tracking(&tracking, branch));
-
- git_reference_free(branch);
-}
-
-void test_refs_branches_tracking__retrieve_a_remote_tracking_reference_from_a_branch_with_no_remote_returns_GIT_ENOTFOUND(void)
-{
- git_reference *head;
- git_repository *repository;
- git_commit *target;
-
- repository = cl_git_sandbox_init("testrepo.git");
-
- cl_git_pass(git_repository_head(&head, repository));
- cl_git_pass(git_reference_peel(((git_object **)&target), head, GIT_OBJ_COMMIT));
- git_reference_free(head);
-
- assert_merge_and_or_remote_key_missing(repository, target, "remoteless");
- assert_merge_and_or_remote_key_missing(repository, target, "mergeless");
- assert_merge_and_or_remote_key_missing(repository, target, "mergeandremoteless");
-
- git_commit_free(target);
-
- cl_git_sandbox_cleanup();
-}
diff --git a/tests-clar/refs/branches/trackingname.c b/tests-clar/refs/branches/trackingname.c
deleted file mode 100644
index 5aee33343..000000000
--- a/tests-clar/refs/branches/trackingname.c
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "clar_libgit2.h"
-#include "branch.h"
-
-static git_repository *repo;
-static git_buf tracking_name;
-
-void test_refs_branches_trackingname__initialize(void)
-{
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
-
- git_buf_init(&tracking_name, 0);
-}
-
-void test_refs_branches_trackingname__cleanup(void)
-{
- git_buf_free(&tracking_name);
-
- git_repository_free(repo);
- repo = NULL;
-}
-
-void test_refs_branches_trackingname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void)
-{
- cl_git_pass(git_branch_tracking__name(
- &tracking_name, repo, "refs/heads/master"));
-
- cl_assert_equal_s("refs/remotes/test/master", git_buf_cstr(&tracking_name));
-}
-
-void test_refs_branches_trackingname__can_retrieve_the_local_tracking_reference_name_of_a_local_branch(void)
-{
- cl_git_pass(git_branch_tracking__name(
- &tracking_name, repo, "refs/heads/track-local"));
-
- cl_assert_equal_s("refs/heads/master", git_buf_cstr(&tracking_name));
-}
-
-void test_refs_branches_trackingname__can_return_the_size_of_thelocal_tracking_reference_name_of_a_local_branch(void)
-{
- cl_assert_equal_i((int)strlen("refs/heads/master") + 1,
- git_branch_tracking_name(NULL, 0, repo, "refs/heads/track-local"));
-}
diff --git a/tests-clar/refs/branches/upstream.c b/tests-clar/refs/branches/upstream.c
new file mode 100644
index 000000000..2d0ebd240
--- /dev/null
+++ b/tests-clar/refs/branches/upstream.c
@@ -0,0 +1,130 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *branch, *upstream;
+
+void test_refs_branches_upstream__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ branch = NULL;
+ upstream = NULL;
+}
+
+void test_refs_branches_upstream__cleanup(void)
+{
+ git_reference_free(upstream);
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstream__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__can_retrieve_the_local_upstream_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/heads/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__cannot_retrieve_a_remote_upstream_reference_from_a_non_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
+
+ cl_git_fail(git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_branch_with_no_fetchspec_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/cannot-fetch"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name)
+{
+ git_reference *branch;
+
+ cl_assert_equal_i(GIT_OBJ_COMMIT, git_object_type((git_object*)target));
+ cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+
+ git_reference_free(branch);
+}
+
+void test_refs_branches_upstream__retrieve_a_remote_tracking_reference_from_a_branch_with_no_remote_returns_GIT_ENOTFOUND(void)
+{
+ git_reference *head;
+ git_repository *repository;
+ git_commit *target;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_head(&head, repository));
+ cl_git_pass(git_reference_peel(((git_object **)&target), head, GIT_OBJ_COMMIT));
+ git_reference_free(head);
+
+ assert_merge_and_or_remote_key_missing(repository, target, "remoteless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeandremoteless");
+
+ git_commit_free(target);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_upstream__set_unset_upstream(void)
+{
+ git_reference *branch;
+ git_repository *repository;
+ const char *value;
+ git_config *config;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "test/master"));
+
+ cl_git_pass(git_repository_config(&config, repository));
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.remote"));
+ cl_assert_equal_s(value, "test");
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.merge"));
+ cl_assert_equal_s(value, "refs/heads/master");
+
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.test.merge"), GIT_ENOTFOUND);
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.test.remote"), GIT_ENOTFOUND);
+
+ git_reference_free(branch);
+
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/master"));
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.master.merge"), GIT_ENOTFOUND);
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.master.remote"), GIT_ENOTFOUND);
+
+ git_reference_free(branch);
+
+ git_config_free(config);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests-clar/refs/branches/upstreamname.c b/tests-clar/refs/branches/upstreamname.c
new file mode 100644
index 000000000..f05607d44
--- /dev/null
+++ b/tests-clar/refs/branches/upstreamname.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_buf upstream_name;
+
+void test_refs_branches_upstreamname__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ git_buf_init(&upstream_name, 0);
+}
+
+void test_refs_branches_upstreamname__cleanup(void)
+{
+ git_buf_free(&upstream_name);
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream__name(
+ &upstream_name, repo, "refs/heads/master"));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_buf_cstr(&upstream_name));
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_local_upstream_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream__name(
+ &upstream_name, repo, "refs/heads/track-local"));
+
+ cl_assert_equal_s("refs/heads/master", git_buf_cstr(&upstream_name));
+}
+
+void test_refs_branches_upstreamname__can_return_the_size_of_thelocal_upstream_reference_name_of_a_local_branch(void)
+{
+ cl_assert_equal_i((int)strlen("refs/heads/master") + 1,
+ git_branch_upstream_name(NULL, 0, repo, "refs/heads/track-local"));
+}