summaryrefslogtreecommitdiff
path: root/src
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 /src
parentacd4077182b508f5c88353ddfcf31e14614d0d46 (diff)
parentd59942c2aba2fa5f9570b37e3bc9eaf34f16d671 (diff)
downloadlibgit2-ea8bac37b0c3a302b8f740cbeb4af50d0626815b.tar.gz
Merge pull request #1450 from carlosmn/branch-upstream
Branch upstream configuration
Diffstat (limited to 'src')
-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
5 files changed, 149 insertions, 38 deletions
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 */