Commit bdaa40d3 by Vicent Martí

Merge pull request #957 from carlosmn/include-tag

Include tag
parents 2af1c266 3230a44f
...@@ -20,14 +20,6 @@ ...@@ -20,14 +20,6 @@
GIT_BEGIN_DECL 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 * Get the source specifier
* *
* @param refspec the refspec * @param refspec the refspec
......
...@@ -304,6 +304,30 @@ struct git_remote_callbacks { ...@@ -304,6 +304,30 @@ struct git_remote_callbacks {
*/ */
GIT_EXTERN(void) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *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 GIT_END_DECL
#endif #endif
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
struct filter_payload { struct filter_payload {
git_remote *remote; git_remote *remote;
const git_refspec *spec; const git_refspec *spec, *tagspec;
git_odb *odb; git_odb *odb;
int found_head; int found_head;
}; };
...@@ -29,18 +29,21 @@ struct filter_payload { ...@@ -29,18 +29,21 @@ struct filter_payload {
static int filter_ref__cb(git_remote_head *head, void *payload) static int filter_ref__cb(git_remote_head *head, void *payload)
{ {
struct filter_payload *p = payload; struct filter_payload *p = payload;
int match = 0;
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) { if (!git_reference_is_valid_name(head->name))
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))
return 0; return 0;
/* Don't even try to ask for the annotation target */ if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
if (!git__suffixcmp(head->name, "^{}")) 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; return 0;
}
/* If we have the object, mark it so we don't ask for it */ /* If we have the object, mark it so we don't ask for it */
if (git_odb_exists(p->odb, &head->oid)) if (git_odb_exists(p->odb, &head->oid))
...@@ -54,8 +57,11 @@ static int filter_ref__cb(git_remote_head *head, void *payload) ...@@ -54,8 +57,11 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
static int filter_wants(git_remote *remote) static int filter_wants(git_remote *remote)
{ {
struct filter_payload p; struct filter_payload p;
git_refspec tagspec;
git_vector_clear(&remote->refs); 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 * The fetch refspec can be NULL, and what this means is that the
...@@ -64,6 +70,7 @@ static int filter_wants(git_remote *remote) ...@@ -64,6 +70,7 @@ static int filter_wants(git_remote *remote)
* HEAD, which will be stored in FETCH_HEAD after the fetch. * HEAD, which will be stored in FETCH_HEAD after the fetch.
*/ */
p.spec = git_remote_fetchspec(remote); p.spec = git_remote_fetchspec(remote);
p.tagspec = &tagspec;
p.found_head = 0; p.found_head = 0;
p.remote = remote; p.remote = remote;
......
...@@ -354,6 +354,9 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps ...@@ -354,6 +354,9 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
if (caps->multi_ack) if (caps->multi_ack)
git_buf_puts(&str, GIT_CAP_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)) if (git_buf_oom(&str))
return -1; return -1;
......
...@@ -80,6 +80,12 @@ int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps) ...@@ -80,6 +80,12 @@ int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps)
continue; 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 */ /* Keep side-band check after side-band-64k */
if(!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) { if(!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
caps->common = caps->side_band_64k = 1; caps->common = caps->side_band_64k = 1;
......
...@@ -1199,6 +1199,7 @@ int git_reference_create_symbolic( ...@@ -1199,6 +1199,7 @@ int git_reference_create_symbolic(
{ {
char normalized[GIT_REFNAME_MAX]; char normalized[GIT_REFNAME_MAX];
git_reference *ref = NULL; git_reference *ref = NULL;
int error;
if (git_reference__normalize_name_lax( if (git_reference__normalize_name_lax(
normalized, normalized,
...@@ -1206,8 +1207,8 @@ int git_reference_create_symbolic( ...@@ -1206,8 +1207,8 @@ int git_reference_create_symbolic(
name) < 0) name) < 0)
return -1; return -1;
if (reference_can_write(repo, normalized, NULL, force) < 0) if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
return -1; return error;
if (reference_alloc(&ref, repo, normalized) < 0) if (reference_alloc(&ref, repo, normalized) < 0)
return -1; return -1;
...@@ -1236,6 +1237,7 @@ int git_reference_create_oid( ...@@ -1236,6 +1237,7 @@ int git_reference_create_oid(
const git_oid *id, const git_oid *id,
int force) int force)
{ {
int error;
git_reference *ref = NULL; git_reference *ref = NULL;
char normalized[GIT_REFNAME_MAX]; char normalized[GIT_REFNAME_MAX];
...@@ -1245,8 +1247,8 @@ int git_reference_create_oid( ...@@ -1245,8 +1247,8 @@ int git_reference_create_oid(
name) < 0) name) < 0)
return -1; return -1;
if (reference_can_write(repo, normalized, NULL, force) < 0) if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
return -1; return error;
if (reference_alloc(&ref, repo, name) < 0) if (reference_alloc(&ref, repo, name) < 0)
return -1; return -1;
......
...@@ -125,35 +125,10 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) ...@@ -125,35 +125,10 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
return -1; 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); git__free(refspec->src);
refspec->src = NULL; git__free(refspec->dst);
return -1;
}
return 0;
} }
const char *git_refspec_src(const git_refspec *refspec) const char *git_refspec_src(const git_refspec *refspec)
......
...@@ -19,12 +19,16 @@ struct git_refspec { ...@@ -19,12 +19,16 @@ struct git_refspec {
matching :1; 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);
int git_refspec__parse( int git_refspec__parse(
struct git_refspec *refspec, struct git_refspec *refspec,
const char *str, const char *str,
bool is_fetch); bool is_fetch);
void git_refspec__free(git_refspec *refspec);
/** /**
* Transform a reference to its target following the refspec's rules, * Transform a reference to its target following the refspec's rules,
* and writes the results into a git_buf. * and writes the results into a git_buf.
......
...@@ -18,41 +18,42 @@ ...@@ -18,41 +18,42 @@
#include <regex.h> #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 == '+') { return git_refspec__parse(refspec, val, is_fetch);
refspec->force = 1; }
str++;
}
delim = strchr(str, ':'); static int download_tags_value(git_remote *remote, git_config *cfg)
if (delim == NULL) { {
giterr_set(GITERR_NET, "Invalid refspec, missing ':'"); const char *val;
return -1; git_buf buf = GIT_BUF_INIT;
} int error;
refspec->src = git__strndup(str, delim - str); if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSET)
GITERR_CHECK_ALLOC(refspec->src); return 0;
refspec->dst = git__strdup(delim + 1); /* This is the default, let's see if we need to change it */
GITERR_CHECK_ALLOC(refspec->dst); 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) if (error == GIT_ENOTFOUND)
{ error = 0;
int error;
const char *val;
if ((error = git_config_get_string(&val, cfg, var)) < 0)
return error; 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) 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 ...@@ -81,7 +82,7 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *name, con
} }
if (fetch != NULL) { if (fetch != NULL) {
if (refspec_parse(&remote->fetch, fetch) < 0) if (git_refspec__parse(&remote->fetch, fetch, true) < 0)
goto on_error; goto on_error;
} }
...@@ -157,7 +158,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) ...@@ -157,7 +158,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup; 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) if (error == GIT_ENOTFOUND)
error = 0; error = 0;
...@@ -172,7 +173,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) ...@@ -172,7 +173,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup; 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) if (error == GIT_ENOTFOUND)
error = 0; error = 0;
...@@ -181,6 +182,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) ...@@ -181,6 +182,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup; goto cleanup;
} }
if (download_tags_value(remote, config) < 0)
goto cleanup;
*out = remote; *out = remote;
cleanup: cleanup:
...@@ -317,11 +321,10 @@ int git_remote_set_fetchspec(git_remote *remote, const char *spec) ...@@ -317,11 +321,10 @@ int git_remote_set_fetchspec(git_remote *remote, const char *spec)
assert(remote && spec); assert(remote && spec);
if (refspec_parse(&refspec, spec) < 0) if (git_refspec__parse(&refspec, spec, true) < 0)
return -1; return -1;
git__free(remote->fetch.src); git_refspec__free(&remote->fetch);
git__free(remote->fetch.dst);
remote->fetch.src = refspec.src; remote->fetch.src = refspec.src;
remote->fetch.dst = refspec.dst; remote->fetch.dst = refspec.dst;
...@@ -340,11 +343,10 @@ int git_remote_set_pushspec(git_remote *remote, const char *spec) ...@@ -340,11 +343,10 @@ int git_remote_set_pushspec(git_remote *remote, const char *spec)
assert(remote && spec); assert(remote && spec);
if (refspec_parse(&refspec, spec) < 0) if (git_refspec__parse(&refspec, spec, false) < 0)
return -1; return -1;
git__free(remote->push.src); git_refspec__free(&remote->push);
git__free(remote->push.dst);
remote->push.src = refspec.src; remote->push.src = refspec.src;
remote->push.dst = refspec.dst; remote->push.dst = refspec.dst;
...@@ -445,25 +447,35 @@ int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats ...@@ -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 git_remote_update_tips(git_remote *remote)
{ {
int error = 0; int error = 0, autotag;
unsigned int i = 0; unsigned int i = 0;
git_buf refname = GIT_BUF_INIT; git_buf refname = GIT_BUF_INIT;
git_oid old; git_oid old;
git_pkt *pkt;
git_odb *odb;
git_vector *refs; git_vector *refs;
git_remote_head *head; git_remote_head *head;
git_reference *ref; git_reference *ref;
struct git_refspec *spec; struct git_refspec *spec;
git_refspec tagspec;
assert(remote); assert(remote);
refs = &remote->refs; refs = &remote->transport->refs;
spec = &remote->fetch; spec = &remote->fetch;
if (refs->length == 0) if (refs->length == 0)
return 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 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 (!strcmp(head->name, GIT_HEAD_FILE)) {
if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0) if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
return -1; return -1;
...@@ -473,10 +485,38 @@ int git_remote_update_tips(git_remote *remote) ...@@ -473,10 +485,38 @@ int git_remote_update_tips(git_remote *remote)
} }
for (; i < refs->length; ++i) { 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) if (git_refspec_transform_r(&refname, spec, head->name) < 0)
goto on_error; 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); error = git_reference_name_to_oid(&old, remote->repo, refname.ptr);
if (error < 0 && error != GIT_ENOTFOUND) if (error < 0 && error != GIT_ENOTFOUND)
...@@ -488,7 +528,9 @@ int git_remote_update_tips(git_remote *remote) ...@@ -488,7 +528,9 @@ int git_remote_update_tips(git_remote *remote)
if (!git_oid_cmp(&old, &head->oid)) if (!git_oid_cmp(&old, &head->oid))
continue; 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; goto on_error;
git_reference_free(ref); git_reference_free(ref);
...@@ -499,10 +541,12 @@ int git_remote_update_tips(git_remote *remote) ...@@ -499,10 +541,12 @@ int git_remote_update_tips(git_remote *remote)
} }
} }
git_refspec__free(&tagspec);
git_buf_free(&refname); git_buf_free(&refname);
return 0; return 0;
on_error: on_error:
git_refspec__free(&tagspec);
git_buf_free(&refname); git_buf_free(&refname);
return -1; return -1;
...@@ -536,10 +580,8 @@ void git_remote_free(git_remote *remote) ...@@ -536,10 +580,8 @@ void git_remote_free(git_remote *remote)
git_vector_free(&remote->refs); git_vector_free(&remote->refs);
git__free(remote->fetch.src); git_refspec__free(&remote->fetch);
git__free(remote->fetch.dst); git_refspec__free(&remote->push);
git__free(remote->push.src);
git__free(remote->push.dst);
git__free(remote->url); git__free(remote->url);
git__free(remote->pushurl); git__free(remote->pushurl);
git__free(remote->name); git__free(remote->name);
...@@ -655,3 +697,13 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback ...@@ -655,3 +697,13 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback
remote->transport->cb_data = remote->callbacks.data; 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 { ...@@ -24,7 +24,8 @@ struct git_remote {
git_repository *repo; git_repository *repo;
git_remote_callbacks callbacks; git_remote_callbacks callbacks;
unsigned int need_pack:1, 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); const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
......
...@@ -23,13 +23,15 @@ ...@@ -23,13 +23,15 @@
#define GIT_CAP_MULTI_ACK "multi_ack" #define GIT_CAP_MULTI_ACK "multi_ack"
#define GIT_CAP_SIDE_BAND "side-band" #define GIT_CAP_SIDE_BAND "side-band"
#define GIT_CAP_SIDE_BAND_64K "side-band-64k" #define GIT_CAP_SIDE_BAND_64K "side-band-64k"
#define GIT_CAP_INCLUDE_TAG "include-tag"
typedef struct git_transport_caps { typedef struct git_transport_caps {
int common:1, int common:1,
ofs_delta:1, ofs_delta:1,
multi_ack: 1, multi_ack: 1,
side_band:1, side_band:1,
side_band_64k:1; side_band_64k:1,
include_tag:1;
} git_transport_caps; } git_transport_caps;
#ifdef GIT_SSL #ifdef GIT_SSL
......
...@@ -8,6 +8,7 @@ static void assert_refspec(unsigned int direction, const char *input, bool is_ex ...@@ -8,6 +8,7 @@ static void assert_refspec(unsigned int direction, const char *input, bool is_ex
int error; int error;
error = git_refspec__parse(&refspec, input, direction == GIT_DIR_FETCH); error = git_refspec__parse(&refspec, input, direction == GIT_DIR_FETCH);
git_refspec__free(&refspec);
if (is_expected_to_be_valid) if (is_expected_to_be_valid)
cl_assert_equal_i(0, error); cl_assert_equal_i(0, error);
......
...@@ -147,3 +147,18 @@ void test_refs_create__oid_unknown(void) ...@@ -147,3 +147,18 @@ void test_refs_create__oid_unknown(void)
/* Ensure the reference can't be looked-up... */ /* Ensure the reference can't be looked-up... */
cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, new_head)); 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