mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-08 12:14:16 +00:00
commit
bdaa40d3da
@ -19,14 +19,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
|
||||
*
|
||||
|
@ -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
|
||||
|
27
src/fetch.c
27
src/fetch.c
@ -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) {
|
||||
if (!git_reference_is_valid_name(head->name))
|
||||
return 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))
|
||||
return 0;
|
||||
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;
|
||||
|
||||
/* Don't even try to ask for the annotation target */
|
||||
if (!git__suffixcmp(head->name, "^{}"))
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
|
10
src/refs.c
10
src/refs.c
@ -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->src);
|
||||
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.
|
||||
|
148
src/remote.c
148
src/remote.c
@ -18,33 +18,7 @@
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
static int refspec_parse(git_refspec *refspec, const char *str)
|
||||
{
|
||||
char *delim;
|
||||
|
||||
memset(refspec, 0x0, sizeof(git_refspec));
|
||||
|
||||
if (*str == '+') {
|
||||
refspec->force = 1;
|
||||
str++;
|
||||
}
|
||||
|
||||
delim = strchr(str, ':');
|
||||
if (delim == NULL) {
|
||||
giterr_set(GITERR_NET, "Invalid refspec, missing ':'");
|
||||
return -1;
|
||||
}
|
||||
|
||||
refspec->src = git__strndup(str, delim - str);
|
||||
GITERR_CHECK_ALLOC(refspec->src);
|
||||
|
||||
refspec->dst = git__strdup(delim + 1);
|
||||
GITERR_CHECK_ALLOC(refspec->dst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var)
|
||||
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var, bool is_fetch)
|
||||
{
|
||||
int error;
|
||||
const char *val;
|
||||
@ -52,7 +26,34 @@ static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const cha
|
||||
if ((error = git_config_get_string(&val, cfg, var)) < 0)
|
||||
return error;
|
||||
|
||||
return refspec_parse(refspec, val);
|
||||
return git_refspec__parse(refspec, val, is_fetch);
|
||||
}
|
||||
|
||||
static int download_tags_value(git_remote *remote, git_config *cfg)
|
||||
{
|
||||
const char *val;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSET)
|
||||
return 0;
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
if (error == GIT_ENOTFOUND)
|
||||
error = 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
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 (git_refspec_transform_r(&refname, spec, head->name) < 0)
|
||||
goto on_error;
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user