Hash :
1eee631d
Author :
Date :
2016-08-04T13:45:28
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
#include "clar_libgit2.h"
#include "fileops.h"
#include "fetchhead.h"
#include "../fetchhead/fetchhead_data.h"
#include "git2/clone.h"
#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
static git_repository *g_repo;
static git_clone_options g_options;
void test_online_fetchhead__initialize(void)
{
git_fetch_options dummy_fetch = GIT_FETCH_OPTIONS_INIT;
g_repo = NULL;
memset(&g_options, 0, sizeof(git_clone_options));
g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.fetch_opts = dummy_fetch;
}
void test_online_fetchhead__cleanup(void)
{
if (g_repo) {
git_repository_free(g_repo);
g_repo = NULL;
}
cl_fixture_cleanup("./foo");
}
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;
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
git_buf fetchhead_buf = GIT_BUF_INIT;
int equals = 0;
git_strarray array, *active_refs = NULL;
cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
if(fetchspec != NULL) {
array.count = 1;
array.strings = (char **) &fetchspec;
active_refs = &array;
}
cl_git_pass(git_remote_fetch(remote, active_refs, &fetch_opts, NULL));
git_remote_free(remote);
cl_git_pass(git_futils_readbuffer(&fetchhead_buf, "./foo/.git/FETCH_HEAD"));
equals = (strcmp(fetchhead_buf.ptr, expected_fetchhead) == 0);
git_buf_free(&fetchhead_buf);
cl_assert(equals);
}
void test_online_fetchhead__wildcard_spec(void)
{
fetchhead_test_clone();
fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA2);
cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
cl_git_pass(git_tag_delete(g_repo, "blob"));
cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
}
void test_online_fetchhead__explicit_spec(void)
{
fetchhead_test_clone();
fetchhead_test_fetch("refs/heads/first-merge:refs/remotes/origin/first-merge", FETCH_HEAD_EXPLICIT_DATA);
}
void test_online_fetchhead__no_merges(void)
{
git_config *config;
fetchhead_test_clone();
cl_git_pass(git_repository_config(&config, g_repo));
cl_git_pass(git_config_delete_entry(config, "branch.master.remote"));
cl_git_pass(git_config_delete_entry(config, "branch.master.merge"));
git_config_free(config);
fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA2);
cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
cl_git_pass(git_tag_delete(g_repo, "blob"));
cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
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());
}