summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2016-08-04 13:45:28 +0200
committerPatrick Steinhardt <ps@pks.im>2016-08-04 13:54:19 +0200
commit1eee631d1189312219773e699d76848680c02e19 (patch)
treec67d400b9d137e9020a85d9003c99e5e9b917d62
parentbaa87dfc5033364ab5ac71099725f8f6c3e6361c (diff)
downloadlibgit2-1eee631d1189312219773e699d76848680c02e19.tar.gz
refspec: do not set empty rhs for fetch refspecs
According to git-fetch(1), "[t]he colon can be omitted when <dst> is empty." So according to git, the refspec "refs/heads/master" is the same as the refspec "refs/heads/master:" when fetching changes. When trying to fetch from a remote with a trailing colon with libgit2, though, the fetch actually fails while it works when the trailing colon is left out. So obviously, libgit2 does _not_ treat these two refspec formats the same for fetches. The problem results from parsing refspecs, where the resulting refspec has its destination set to an empty string in the case of a trailing colon and to a `NULL` pointer in the case of no trailing colon. When passing this to our DWIM machinery, the empty string gets translated to "refs/heads/", which is simply wrong. Fix the problem by having the parsing machinery treat both cases the same for fetch refspecs.
-rw-r--r--src/refspec.c6
-rw-r--r--tests/online/fetchhead.c51
2 files changed, 55 insertions, 2 deletions
diff --git a/src/refspec.c b/src/refspec.c
index debde8692..d200e5609 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -53,8 +53,10 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
if (rhs) {
size_t rlen = strlen(++rhs);
- is_glob = (1 <= rlen && strchr(rhs, '*'));
- refspec->dst = git__strndup(rhs, rlen);
+ if (rlen || !is_fetch) {
+ is_glob = (1 <= rlen && strchr(rhs, '*'));
+ refspec->dst = git__strndup(rhs, rlen);
+ }
}
llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs));
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
index 200edacfd..9aaad253c 100644
--- a/tests/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -35,6 +35,19 @@ static void fetchhead_test_clone(void)
cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
}
+static int count_references(void)
+{
+ git_strarray array;
+ int refs;
+
+ cl_git_pass(git_reference_list(&array, g_repo));
+ refs = array.count;
+
+ git_strarray_free(&array);
+
+ return refs;
+}
+
static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
{
git_remote *remote;
@@ -101,3 +114,41 @@ void test_online_fetchhead__no_merges(void)
cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3);
}
+
+void test_online_fetchhead__explicit_dst_refspec_creates_branch(void)
+{
+ git_reference *ref;
+ int refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+ fetchhead_test_fetch("refs/heads/first-merge:refs/heads/explicit-refspec", FETCH_HEAD_EXPLICIT_DATA);
+
+ cl_git_pass(git_branch_lookup(&ref, g_repo, "explicit-refspec", GIT_BRANCH_ALL));
+ cl_assert_equal_i(refs + 1, count_references());
+}
+
+void test_online_fetchhead__empty_dst_refspec_creates_no_branch(void)
+{
+ git_reference *ref;
+ int refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+
+ fetchhead_test_fetch("refs/heads/first-merge", FETCH_HEAD_EXPLICIT_DATA);
+ cl_git_fail(git_branch_lookup(&ref, g_repo, "first-merge", GIT_BRANCH_ALL));
+
+ cl_assert_equal_i(refs, count_references());
+}
+
+void test_online_fetchhead__colon_only_dst_refspec_creates_no_branch(void)
+{
+ int refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+ fetchhead_test_fetch("refs/heads/first-merge:", FETCH_HEAD_EXPLICIT_DATA);
+
+ cl_assert_equal_i(refs, count_references());
+}