Commit bdaa40d3 by Vicent Martí

Merge pull request #957 from carlosmn/include-tag

Include tag
parents 2af1c266 3230a44f
......@@ -20,14 +20,6 @@
GIT_BEGIN_DECL
/**
* Parse a refspec string and create a refspec object
*
* @param refspec pointer to the refspec structure to be used
* @param str the refspec as a string
*/
GIT_EXTERN(int) git_refspec_parse(git_refspec *refspec, const char *str);
/**
* Get the source specifier
*
* @param refspec the refspec
......
......@@ -304,6 +304,30 @@ struct git_remote_callbacks {
*/
GIT_EXTERN(void) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
enum {
GIT_REMOTE_DOWNLOAD_TAGS_UNSET,
GIT_REMOTE_DOWNLOAD_TAGS_NONE,
GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
GIT_REMOTE_DOWNLOAD_TAGS_ALL
};
/**
* Retrieve the tag auto-follow setting
*
* @param remote the remote to query
* @return the auto-follow setting
*/
GIT_EXTERN(int) git_remote_autotag(git_remote *remote);
/**
* Set the tag auto-follow setting
*
* @param remote the remote to configure
* @param value a GIT_REMOTE_DOWNLOAD_TAGS value
*/
GIT_EXTERN(void) git_remote_set_autotag(git_remote *remote, int value);
/** @} */
GIT_END_DECL
#endif
......@@ -21,7 +21,7 @@
struct filter_payload {
git_remote *remote;
const git_refspec *spec;
const git_refspec *spec, *tagspec;
git_odb *odb;
int found_head;
};
......@@ -29,18 +29,21 @@ struct filter_payload {
static int filter_ref__cb(git_remote_head *head, void *payload)
{
struct filter_payload *p = payload;
int match = 0;
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
p->found_head = 1;
} else {
/* If it doesn't match the refpec, we don't want it */
if (!git_refspec_src_matches(p->spec, head->name))
if (!git_reference_is_valid_name(head->name))
return 0;
/* Don't even try to ask for the annotation target */
if (!git__suffixcmp(head->name, "^{}"))
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
p->found_head = 1;
else if (git_refspec_src_matches(p->spec, head->name))
match = 1;
else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL &&
git_refspec_src_matches(p->tagspec, head->name))
match = 1;
if (!match)
return 0;
}
/* If we have the object, mark it so we don't ask for it */
if (git_odb_exists(p->odb, &head->oid))
......@@ -54,8 +57,11 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
static int filter_wants(git_remote *remote)
{
struct filter_payload p;
git_refspec tagspec;
git_vector_clear(&remote->refs);
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
return -1;
/*
* The fetch refspec can be NULL, and what this means is that the
......@@ -64,6 +70,7 @@ static int filter_wants(git_remote *remote)
* HEAD, which will be stored in FETCH_HEAD after the fetch.
*/
p.spec = git_remote_fetchspec(remote);
p.tagspec = &tagspec;
p.found_head = 0;
p.remote = remote;
......
......@@ -354,6 +354,9 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
if (caps->multi_ack)
git_buf_puts(&str, GIT_CAP_MULTI_ACK " ");
if (caps->include_tag)
git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " ");
if (git_buf_oom(&str))
return -1;
......
......@@ -80,6 +80,12 @@ int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps)
continue;
}
if(!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
caps->common = caps->include_tag = 1;
ptr += strlen(GIT_CAP_INCLUDE_TAG);
continue;
}
/* Keep side-band check after side-band-64k */
if(!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
caps->common = caps->side_band_64k = 1;
......
......@@ -1199,6 +1199,7 @@ int git_reference_create_symbolic(
{
char normalized[GIT_REFNAME_MAX];
git_reference *ref = NULL;
int error;
if (git_reference__normalize_name_lax(
normalized,
......@@ -1206,8 +1207,8 @@ int git_reference_create_symbolic(
name) < 0)
return -1;
if (reference_can_write(repo, normalized, NULL, force) < 0)
return -1;
if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
return error;
if (reference_alloc(&ref, repo, normalized) < 0)
return -1;
......@@ -1236,6 +1237,7 @@ int git_reference_create_oid(
const git_oid *id,
int force)
{
int error;
git_reference *ref = NULL;
char normalized[GIT_REFNAME_MAX];
......@@ -1245,8 +1247,8 @@ int git_reference_create_oid(
name) < 0)
return -1;
if (reference_can_write(repo, normalized, NULL, force) < 0)
return -1;
if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
return error;
if (reference_alloc(&ref, repo, name) < 0)
return -1;
......
......@@ -125,35 +125,10 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
return -1;
}
int git_refspec_parse(git_refspec *refspec, const char *str)
void git_refspec__free(git_refspec *refspec)
{
char *delim;
memset(refspec, 0x0, sizeof(git_refspec));
if (*str == '+') {
refspec->force = 1;
str++;
}
delim = strchr(str, ':');
if (delim == NULL) {
refspec->src = git__strdup(str);
GITERR_CHECK_ALLOC(refspec->src);
return 0;
}
refspec->src = git__strndup(str, delim - str);
GITERR_CHECK_ALLOC(refspec->src);
refspec->dst = git__strdup(delim + 1);
if (refspec->dst == NULL) {
git__free(refspec->src);
refspec->src = NULL;
return -1;
}
return 0;
git__free(refspec->dst);
}
const char *git_refspec_src(const git_refspec *refspec)
......
......@@ -19,12 +19,16 @@ struct git_refspec {
matching :1;
};
#define GIT_REFSPEC_TAGS "refs/tags/*:refs/tags/*"
int git_refspec_parse(struct git_refspec *refspec, const char *str);
int git_refspec__parse(
struct git_refspec *refspec,
const char *str,
bool is_fetch);
void git_refspec__free(git_refspec *refspec);
/**
* Transform a reference to its target following the refspec's rules,
* and writes the results into a git_buf.
......
......@@ -18,41 +18,42 @@
#include <regex.h>
static int refspec_parse(git_refspec *refspec, const char *str)
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var, bool is_fetch)
{
char *delim;
int error;
const char *val;
memset(refspec, 0x0, sizeof(git_refspec));
if ((error = git_config_get_string(&val, cfg, var)) < 0)
return error;
if (*str == '+') {
refspec->force = 1;
str++;
}
return git_refspec__parse(refspec, val, is_fetch);
}
delim = strchr(str, ':');
if (delim == NULL) {
giterr_set(GITERR_NET, "Invalid refspec, missing ':'");
return -1;
}
static int download_tags_value(git_remote *remote, git_config *cfg)
{
const char *val;
git_buf buf = GIT_BUF_INIT;
int error;
refspec->src = git__strndup(str, delim - str);
GITERR_CHECK_ALLOC(refspec->src);
if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSET)
return 0;
refspec->dst = git__strdup(delim + 1);
GITERR_CHECK_ALLOC(refspec->dst);
/* This is the default, let's see if we need to change it */
remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
return -1;
return 0;
}
error = git_config_get_string(&val, cfg, git_buf_cstr(&buf));
git_buf_free(&buf);
if (!error && !strcmp(val, "--no-tags"))
remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
else if (!error && !strcmp(val, "--tags"))
remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var)
{
int error;
const char *val;
if (error == GIT_ENOTFOUND)
error = 0;
if ((error = git_config_get_string(&val, cfg, var)) < 0)
return error;
return refspec_parse(refspec, val);
}
int git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
......@@ -81,7 +82,7 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *name, con
}
if (fetch != NULL) {
if (refspec_parse(&remote->fetch, fetch) < 0)
if (git_refspec__parse(&remote->fetch, fetch, true) < 0)
goto on_error;
}
......@@ -157,7 +158,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup;
}
error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf));
error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf), true);
if (error == GIT_ENOTFOUND)
error = 0;
......@@ -172,7 +173,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup;
}
error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf));
error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf), false);
if (error == GIT_ENOTFOUND)
error = 0;
......@@ -181,6 +182,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup;
}
if (download_tags_value(remote, config) < 0)
goto cleanup;
*out = remote;
cleanup:
......@@ -317,11 +321,10 @@ int git_remote_set_fetchspec(git_remote *remote, const char *spec)
assert(remote && spec);
if (refspec_parse(&refspec, spec) < 0)
if (git_refspec__parse(&refspec, spec, true) < 0)
return -1;
git__free(remote->fetch.src);
git__free(remote->fetch.dst);
git_refspec__free(&remote->fetch);
remote->fetch.src = refspec.src;
remote->fetch.dst = refspec.dst;
......@@ -340,11 +343,10 @@ int git_remote_set_pushspec(git_remote *remote, const char *spec)
assert(remote && spec);
if (refspec_parse(&refspec, spec) < 0)
if (git_refspec__parse(&refspec, spec, false) < 0)
return -1;
git__free(remote->push.src);
git__free(remote->push.dst);
git_refspec__free(&remote->push);
remote->push.src = refspec.src;
remote->push.dst = refspec.dst;
......@@ -445,25 +447,35 @@ int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats
int git_remote_update_tips(git_remote *remote)
{
int error = 0;
int error = 0, autotag;
unsigned int i = 0;
git_buf refname = GIT_BUF_INIT;
git_oid old;
git_pkt *pkt;
git_odb *odb;
git_vector *refs;
git_remote_head *head;
git_reference *ref;
struct git_refspec *spec;
git_refspec tagspec;
assert(remote);
refs = &remote->refs;
refs = &remote->transport->refs;
spec = &remote->fetch;
if (refs->length == 0)
return 0;
if (git_repository_odb(&odb, remote->repo) < 0)
return -1;
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
return -1;
/* HEAD is only allowed to be the first in the list */
head = refs->contents[0];
pkt = refs->contents[0];
head = &((git_pkt_ref *)pkt)->head;
if (!strcmp(head->name, GIT_HEAD_FILE)) {
if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
return -1;
......@@ -473,10 +485,38 @@ int git_remote_update_tips(git_remote *remote)
}
for (; i < refs->length; ++i) {
head = refs->contents[i];
autotag = 0;
git_pkt *pkt = refs->contents[i];
if (pkt->type == GIT_PKT_REF)
head = &((git_pkt_ref *)pkt)->head;
else
continue;
/* Ignore malformed ref names (which also saves us from tag^{} */
if (!git_reference_is_valid_name(head->name))
continue;
if (git_refspec_src_matches(spec, head->name)) {
if (git_refspec_transform_r(&refname, spec, head->name) < 0)
goto on_error;
} else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL)
autotag = 1;
if (!git_refspec_src_matches(&tagspec, head->name))
continue;
git_buf_clear(&refname);
if (git_buf_puts(&refname, head->name) < 0)
goto on_error;
} else {
continue;
}
if (autotag && !git_odb_exists(odb, &head->oid))
continue;
error = git_reference_name_to_oid(&old, remote->repo, refname.ptr);
if (error < 0 && error != GIT_ENOTFOUND)
......@@ -488,7 +528,9 @@ int git_remote_update_tips(git_remote *remote)
if (!git_oid_cmp(&old, &head->oid))
continue;
if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0)
/* In autotag mode, don't overwrite any locally-existing tags */
error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, !autotag);
if (error < 0 && error != GIT_EEXISTS)
goto on_error;
git_reference_free(ref);
......@@ -499,10 +541,12 @@ int git_remote_update_tips(git_remote *remote)
}
}
git_refspec__free(&tagspec);
git_buf_free(&refname);
return 0;
on_error:
git_refspec__free(&tagspec);
git_buf_free(&refname);
return -1;
......@@ -536,10 +580,8 @@ void git_remote_free(git_remote *remote)
git_vector_free(&remote->refs);
git__free(remote->fetch.src);
git__free(remote->fetch.dst);
git__free(remote->push.src);
git__free(remote->push.dst);
git_refspec__free(&remote->fetch);
git_refspec__free(&remote->push);
git__free(remote->url);
git__free(remote->pushurl);
git__free(remote->name);
......@@ -655,3 +697,13 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback
remote->transport->cb_data = remote->callbacks.data;
}
}
int git_remote_autotag(git_remote *remote)
{
return remote->download_tags;
}
void git_remote_set_autotag(git_remote *remote, int value)
{
remote->download_tags = value;
}
......@@ -24,7 +24,8 @@ struct git_remote {
git_repository *repo;
git_remote_callbacks callbacks;
unsigned int need_pack:1,
check_cert;
download_tags:2, /* There are four possible values */
check_cert:1;
};
const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
......
......@@ -23,13 +23,15 @@
#define GIT_CAP_MULTI_ACK "multi_ack"
#define GIT_CAP_SIDE_BAND "side-band"
#define GIT_CAP_SIDE_BAND_64K "side-band-64k"
#define GIT_CAP_INCLUDE_TAG "include-tag"
typedef struct git_transport_caps {
int common:1,
ofs_delta:1,
multi_ack: 1,
side_band:1,
side_band_64k:1;
side_band_64k:1,
include_tag:1;
} git_transport_caps;
#ifdef GIT_SSL
......
......@@ -8,6 +8,7 @@ static void assert_refspec(unsigned int direction, const char *input, bool is_ex
int error;
error = git_refspec__parse(&refspec, input, direction == GIT_DIR_FETCH);
git_refspec__free(&refspec);
if (is_expected_to_be_valid)
cl_assert_equal_i(0, error);
......
......@@ -147,3 +147,18 @@ void test_refs_create__oid_unknown(void)
/* Ensure the reference can't be looked-up... */
cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, new_head));
}
void test_refs_create__propagate_eexists(void)
{
int error;
git_oid oid;
git_reference *ref;
/* Make sure it works for oid and for symbolic both */
git_oid_fromstr(&oid, current_master_tip);
error = git_reference_create_oid(&ref, g_repo, current_head_target, &oid, false);
cl_assert(error == GIT_EEXISTS);
error = git_reference_create_symbolic(&ref, g_repo, "HEAD", current_head_target, false);
cl_assert(error == GIT_EEXISTS);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment