|
|
|
#include "cache.h"
|
|
|
|
#include "remote.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "revision.h"
|
|
|
|
#include "dir.h"
|
|
|
|
#include "tag.h"
|
|
|
|
#include "string-list.h"
|
|
|
|
#include "mergesort.h"
|
|
|
|
|
|
|
|
enum map_direction { FROM_SRC, FROM_DST };
|
|
|
|
|
|
|
|
static struct refspec s_tag_refspec = {
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
"refs/tags/*",
|
|
|
|
"refs/tags/*"
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct refspec *tag_refspec = &s_tag_refspec;
|
|
|
|
|
|
|
|
struct counted_string {
|
|
|
|
size_t len;
|
|
|
|
const char *s;
|
|
|
|
};
|
|
|
|
struct rewrite {
|
|
|
|
const char *base;
|
|
|
|
size_t baselen;
|
|
|
|
struct counted_string *instead_of;
|
|
|
|
int instead_of_nr;
|
|
|
|
int instead_of_alloc;
|
|
|
|
};
|
|
|
|
struct rewrites {
|
|
|
|
struct rewrite **rewrite;
|
|
|
|
int rewrite_alloc;
|
|
|
|
int rewrite_nr;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct remote **remotes;
|
|
|
|
static int remotes_alloc;
|
|
|
|
static int remotes_nr;
|
|
|
|
|
|
|
|
static struct branch **branches;
|
|
|
|
static int branches_alloc;
|
|
|
|
static int branches_nr;
|
|
|
|
|
|
|
|
static struct branch *current_branch;
|
|
|
|
static const char *default_remote_name;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
16 years ago
|
|
|
static int explicit_default_remote_name;
|
|
|
|
|
|
|
|
static struct rewrites rewrites;
|
|
|
|
static struct rewrites rewrites_push;
|
|
|
|
|
|
|
|
#define BUF_SIZE (2048)
|
|
|
|
static char buffer[BUF_SIZE];
|
|
|
|
|
|
|
|
static int valid_remote(const struct remote *remote)
|
|
|
|
{
|
|
|
|
return (!!remote->url) || (!!remote->foreign_vcs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *alias_url(const char *url, struct rewrites *r)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
char *ret;
|
|
|
|
struct counted_string *longest;
|
|
|
|
int longest_i;
|
|
|
|
|
|
|
|
longest = NULL;
|
|
|
|
longest_i = -1;
|
|
|
|
for (i = 0; i < r->rewrite_nr; i++) {
|
|
|
|
if (!r->rewrite[i])
|
|
|
|
continue;
|
|
|
|
for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
|
|
|
|
if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
|
|
|
|
(!longest ||
|
|
|
|
longest->len < r->rewrite[i]->instead_of[j].len)) {
|
|
|
|
longest = &(r->rewrite[i]->instead_of[j]);
|
|
|
|
longest_i = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!longest)
|
|
|
|
return url;
|
|
|
|
|
|
|
|
ret = xmalloc(r->rewrite[longest_i]->baselen +
|
|
|
|
(strlen(url) - longest->len) + 1);
|
|
|
|
strcpy(ret, r->rewrite[longest_i]->base);
|
|
|
|
strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_push_refspec(struct remote *remote, const char *ref)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(remote->push_refspec,
|
|
|
|
remote->push_refspec_nr + 1,
|
|
|
|
remote->push_refspec_alloc);
|
|
|
|
remote->push_refspec[remote->push_refspec_nr++] = ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_fetch_refspec(struct remote *remote, const char *ref)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(remote->fetch_refspec,
|
|
|
|
remote->fetch_refspec_nr + 1,
|
|
|
|
remote->fetch_refspec_alloc);
|
|
|
|
remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_url(struct remote *remote, const char *url)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
|
|
|
|
remote->url[remote->url_nr++] = url;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_pushurl(struct remote *remote, const char *pushurl)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
|
|
|
|
remote->pushurl[remote->pushurl_nr++] = pushurl;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_pushurl_alias(struct remote *remote, const char *url)
|
|
|
|
{
|
|
|
|
const char *pushurl = alias_url(url, &rewrites_push);
|
|
|
|
if (pushurl != url)
|
|
|
|
add_pushurl(remote, pushurl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_url_alias(struct remote *remote, const char *url)
|
|
|
|
{
|
|
|
|
add_url(remote, alias_url(url, &rewrites));
|
|
|
|
add_pushurl_alias(remote, url);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct remote *make_remote(const char *name, int len)
|
|
|
|
{
|
|
|
|
struct remote *ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < remotes_nr; i++) {
|
|
|
|
if (len ? (!strncmp(name, remotes[i]->name, len) &&
|
|
|
|
!remotes[i]->name[len]) :
|
|
|
|
!strcmp(name, remotes[i]->name))
|
|
|
|
return remotes[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = xcalloc(1, sizeof(struct remote));
|
|
|
|
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
|
|
|
|
remotes[remotes_nr++] = ret;
|
|
|
|
if (len)
|
|
|
|
ret->name = xstrndup(name, len);
|
|
|
|
else
|
|
|
|
ret->name = xstrdup(name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_merge(struct branch *branch, const char *name)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
|
|
|
|
branch->merge_alloc);
|
|
|
|
branch->merge_name[branch->merge_nr++] = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct branch *make_branch(const char *name, int len)
|
|
|
|
{
|
|
|
|
struct branch *ret;
|
|
|
|
int i;
|
|
|
|
char *refname;
|
|
|
|
|
|
|
|
for (i = 0; i < branches_nr; i++) {
|
|
|
|
if (len ? (!strncmp(name, branches[i]->name, len) &&
|
|
|
|
!branches[i]->name[len]) :
|
|
|
|
!strcmp(name, branches[i]->name))
|
|
|
|
return branches[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
|
|
|
|
ret = xcalloc(1, sizeof(struct branch));
|
|
|
|
branches[branches_nr++] = ret;
|
|
|
|
if (len)
|
|
|
|
ret->name = xstrndup(name, len);
|
|
|
|
else
|
|
|
|
ret->name = xstrdup(name);
|
|
|
|
refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
|
|
|
|
strcpy(refname, "refs/heads/");
|
|
|
|
strcpy(refname + strlen("refs/heads/"), ret->name);
|
|
|
|
ret->refname = refname;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
|
|
|
|
{
|
|
|
|
struct rewrite *ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < r->rewrite_nr; i++) {
|
|
|
|
if (len
|
|
|
|
? (len == r->rewrite[i]->baselen &&
|
|
|
|
!strncmp(base, r->rewrite[i]->base, len))
|
|
|
|
: !strcmp(base, r->rewrite[i]->base))
|
|
|
|
return r->rewrite[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
|
|
|
|
ret = xcalloc(1, sizeof(struct rewrite));
|
|
|
|
r->rewrite[r->rewrite_nr++] = ret;
|
|
|
|
if (len) {
|
|
|
|
ret->base = xstrndup(base, len);
|
|
|
|
ret->baselen = len;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret->base = xstrdup(base);
|
|
|
|
ret->baselen = strlen(base);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
|
|
|
|
rewrite->instead_of[rewrite->instead_of_nr].s = instead_of;
|
|
|
|
rewrite->instead_of[rewrite->instead_of_nr].len = strlen(instead_of);
|
|
|
|
rewrite->instead_of_nr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_remotes_file(struct remote *remote)
|
|
|
|
{
|
|
|
|
FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
|
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return;
|
|
|
|
remote->origin = REMOTE_REMOTES;
|
|
|
|
while (fgets(buffer, BUF_SIZE, f)) {
|
|
|
|
int value_list;
|
|
|
|
char *s, *p;
|
|
|
|
|
|
|
|
if (!prefixcmp(buffer, "URL:")) {
|
|
|
|
value_list = 0;
|
|
|
|
s = buffer + 4;
|
|
|
|
} else if (!prefixcmp(buffer, "Push:")) {
|
|
|
|
value_list = 1;
|
|
|
|
s = buffer + 5;
|
|
|
|
} else if (!prefixcmp(buffer, "Pull:")) {
|
|
|
|
value_list = 2;
|
|
|
|
s = buffer + 5;
|
|
|
|
} else
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
if (!*s)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p = s + strlen(s);
|
|
|
|
while (isspace(p[-1]))
|
|
|
|
*--p = 0;
|
|
|
|
|
|
|
|
switch (value_list) {
|
|
|
|
case 0:
|
|
|
|
add_url_alias(remote, xstrdup(s));
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
add_push_refspec(remote, xstrdup(s));
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
add_fetch_refspec(remote, xstrdup(s));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_branches_file(struct remote *remote)
|
|
|
|
{
|
|
|
|
const char *slash = strchr(remote->name, '/');
|
|
|
|
char *frag;
|
|
|
|
struct strbuf branch = STRBUF_INIT;
|
|
|
|
int n = slash ? slash - remote->name : 1000;
|
|
|
|
FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
|
|
|
|
char *s, *p;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return;
|
|
|
|
s = fgets(buffer, BUF_SIZE, f);
|
|
|
|
fclose(f);
|
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
if (!*s)
|
|
|
|
return;
|
|
|
|
remote->origin = REMOTE_BRANCHES;
|
|
|
|
p = s + strlen(s);
|
|
|
|
while (isspace(p[-1]))
|
|
|
|
*--p = 0;
|
|
|
|
len = p - s;
|
|
|
|
if (slash)
|
|
|
|
len += strlen(slash);
|
|
|
|
p = xmalloc(len + 1);
|
|
|
|
strcpy(p, s);
|
|
|
|
if (slash)
|
|
|
|
strcat(p, slash);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* With "slash", e.g. "git fetch jgarzik/netdev-2.6" when
|
|
|
|
* reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from
|
|
|
|
* the partial URL obtained from the branches file plus
|
|
|
|
* "/netdev-2.6" and does not store it in any tracking ref.
|
|
|
|
* #branch specifier in the file is ignored.
|
|
|
|
*
|
|
|
|
* Otherwise, the branches file would have URL and optionally
|
|
|
|
* #branch specified. The "master" (or specified) branch is
|
|
|
|
* fetched and stored in the local branch of the same name.
|
|
|
|
*/
|
|
|
|
frag = strchr(p, '#');
|
|
|
|
if (frag) {
|
|
|
|
*(frag++) = '\0';
|
|
|
|
strbuf_addf(&branch, "refs/heads/%s", frag);
|
|
|
|
} else
|
|
|
|
strbuf_addstr(&branch, "refs/heads/master");
|
|
|
|
if (!slash) {
|
|
|
|
strbuf_addf(&branch, ":refs/heads/%s", remote->name);
|
|
|
|
} else {
|
|
|
|
strbuf_reset(&branch);
|
|
|
|
strbuf_addstr(&branch, "HEAD:");
|
|
|
|
}
|
|
|
|
add_url_alias(remote, p);
|
|
|
|
add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
|
|
|
|
/*
|
|
|
|
* Cogito compatible push: push current HEAD to remote #branch
|
|
|
|
* (master if missing)
|
|
|
|
*/
|
|
|
|
strbuf_init(&branch, 0);
|
|
|
|
strbuf_addstr(&branch, "HEAD");
|
|
|
|
if (frag)
|
|
|
|
strbuf_addf(&branch, ":refs/heads/%s", frag);
|
|
|
|
else
|
|
|
|
strbuf_addstr(&branch, ":refs/heads/master");
|
|
|
|
add_push_refspec(remote, strbuf_detach(&branch, NULL));
|
|
|
|
remote->fetch_tags = 1; /* always auto-follow */
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_config(const char *key, const char *value, void *cb)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
const char *subkey;
|
|
|
|
struct remote *remote;
|
|
|
|
struct branch *branch;
|
|
|
|
if (!prefixcmp(key, "branch.")) {
|
|
|
|
name = key + 7;
|
|
|
|
subkey = strrchr(name, '.');
|
|
|
|
if (!subkey)
|
|
|
|
return 0;
|
|
|
|
branch = make_branch(name, subkey - name);
|
|
|
|
if (!strcmp(subkey, ".remote")) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
|
|
|
branch->remote_name = xstrdup(value);
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
16 years ago
|
|
|
if (branch == current_branch) {
|
|
|
|
default_remote_name = branch->remote_name;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
16 years ago
|
|
|
explicit_default_remote_name = 1;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(subkey, ".merge")) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
|
|
|
add_merge(branch, xstrdup(value));
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!prefixcmp(key, "url.")) {
|
|
|
|
struct rewrite *rewrite;
|
|
|
|
name = key + 4;
|
|
|
|
subkey = strrchr(name, '.');
|
|
|
|
if (!subkey)
|
|
|
|
return 0;
|
|
|
|
if (!strcmp(subkey, ".insteadof")) {
|
|
|
|
rewrite = make_rewrite(&rewrites, name, subkey - name);
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
|
|
|
add_instead_of(rewrite, xstrdup(value));
|
|
|
|
} else if (!strcmp(subkey, ".pushinsteadof")) {
|
|
|
|
rewrite = make_rewrite(&rewrites_push, name, subkey - name);
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
|
|
|
add_instead_of(rewrite, xstrdup(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (prefixcmp(key, "remote."))
|
|
|
|
return 0;
|
|
|
|
name = key + 7;
|
|
|
|
if (*name == '/') {
|
|
|
|
warning("Config remote shorthand cannot begin with '/': %s",
|
|
|
|
name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
subkey = strrchr(name, '.');
|
|
|
|
if (!subkey)
|
|
|
|
return 0;
|
|
|
|
remote = make_remote(name, subkey - name);
|
|
|
|
remote->origin = REMOTE_CONFIG;
|
|
|
|
if (!strcmp(subkey, ".mirror"))
|
|
|
|
remote->mirror = git_config_bool(key, value);
|
|
|
|
else if (!strcmp(subkey, ".skipdefaultupdate"))
|
|
|
|
remote->skip_default_update = git_config_bool(key, value);
|
|
|
|
else if (!strcmp(subkey, ".skipfetchall"))
|
|
|
|
remote->skip_default_update = git_config_bool(key, value);
|
|
|
|
else if (!strcmp(subkey, ".url")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_url(remote, v);
|
|
|
|
} else if (!strcmp(subkey, ".pushurl")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_pushurl(remote, v);
|
|
|
|
} else if (!strcmp(subkey, ".push")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_push_refspec(remote, v);
|
|
|
|
} else if (!strcmp(subkey, ".fetch")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_fetch_refspec(remote, v);
|
|
|
|
} else if (!strcmp(subkey, ".receivepack")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
if (!remote->receivepack)
|
|
|
|
remote->receivepack = v;
|
|
|
|
else
|
|
|
|
error("more than one receivepack given, using the first");
|
|
|
|
} else if (!strcmp(subkey, ".uploadpack")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
if (!remote->uploadpack)
|
|
|
|
remote->uploadpack = v;
|
|
|
|
else
|
|
|
|
error("more than one uploadpack given, using the first");
|
|
|
|
} else if (!strcmp(subkey, ".tagopt")) {
|
|
|
|
if (!strcmp(value, "--no-tags"))
|
|
|
|
remote->fetch_tags = -1;
|
|
|
|
else if (!strcmp(value, "--tags"))
|
|
|
|
remote->fetch_tags = 2;
|
|
|
|
} else if (!strcmp(subkey, ".proxy")) {
|
|
|
|
return git_config_string((const char **)&remote->http_proxy,
|
|
|
|
key, value);
|
|
|
|
} else if (!strcmp(subkey, ".vcs")) {
|
|
|
|
return git_config_string(&remote->foreign_vcs, key, value);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void alias_all_urls(void)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < remotes_nr; i++) {
|
|
|
|
int add_pushurl_aliases;
|
|
|
|
if (!remotes[i])
|
|
|
|
continue;
|
|
|
|
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
|
|
|
|
remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
|
|
|
|
}
|
|
|
|
add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
|
|
|
|
for (j = 0; j < remotes[i]->url_nr; j++) {
|
|
|
|
if (add_pushurl_aliases)
|
|
|
|
add_pushurl_alias(remotes[i], remotes[i]->url[j]);
|
|
|
|
remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_config(void)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
const char *head_ref;
|
|
|
|
int flag;
|
|
|
|
if (default_remote_name) /* did this already */
|
|
|
|
return;
|
|
|
|
default_remote_name = xstrdup("origin");
|
|
|
|
current_branch = NULL;
|
|
|
|
head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
|
|
|
|
if (head_ref && (flag & REF_ISSYMREF) &&
|
|
|
|
!prefixcmp(head_ref, "refs/heads/")) {
|
|
|
|
current_branch =
|
|
|
|
make_branch(head_ref + strlen("refs/heads/"), 0);
|
|
|
|
}
|
|
|
|
git_config(handle_config, NULL);
|
|
|
|
alias_all_urls();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function frees a refspec array.
|
|
|
|
* Warning: code paths should be checked to ensure that the src
|
|
|
|
* and dst pointers are always freeable pointers as well
|
|
|
|
* as the refspec pointer itself.
|
|
|
|
*/
|
|
|
|
static void free_refspecs(struct refspec *refspec, int nr_refspec)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!refspec)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_refspec; i++) {
|
|
|
|
free(refspec[i].src);
|
|
|
|
free(refspec[i].dst);
|
|
|
|
}
|
|
|
|
free(refspec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
|
|
|
|
for (i = 0; i < nr_refspec; i++) {
|
|
|
|
size_t llen;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
int is_glob;
|
|
|
|
const char *lhs, *rhs;
|
|
|
|
int flags;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
|
|
|
|
is_glob = 0;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
|
|
|
|
lhs = refspec[i];
|
|
|
|
if (*lhs == '+') {
|
|
|
|
rs[i].force = 1;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
lhs++;
|
|
|
|
}
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
|
|
|
|
rhs = strrchr(lhs, ':');
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Before going on, special case ":" (or "+:") as a refspec
|
|
|
|
* for matching refs.
|
|
|
|
*/
|
|
|
|
if (!fetch && rhs == lhs && rhs[1] == '\0') {
|
|
|
|
rs[i].matching = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
if (rhs) {
|
|
|
|
size_t rlen = strlen(++rhs);
|
|
|
|
is_glob = (1 <= rlen && strchr(rhs, '*'));
|
|
|
|
rs[i].dst = xstrndup(rhs, rlen);
|
|
|
|
}
|
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
|
|
|
|
if (1 <= llen && memchr(lhs, '*', llen)) {
|
|
|
|
if ((rhs && !is_glob) || (!rhs && fetch))
|
|
|
|
goto invalid;
|
|
|
|
is_glob = 1;
|
|
|
|
} else if (rhs && is_glob) {
|
|
|
|
goto invalid;
|
|
|
|
}
|
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
rs[i].pattern = is_glob;
|
|
|
|
rs[i].src = xstrndup(lhs, llen);
|
|
|
|
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
|
|
|
|
if (fetch) {
|
|
|
|
/*
|
|
|
|
* LHS
|
|
|
|
* - empty is allowed; it means HEAD.
|
|
|
|
* - otherwise it must be a valid looking ref.
|
|
|
|
*/
|
|
|
|
if (!*rs[i].src)
|
|
|
|
; /* empty is ok */
|
|
|
|
else if (check_refname_format(rs[i].src, flags))
|
|
|
|
goto invalid;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
/*
|
|
|
|
* RHS
|
|
|
|
* - missing is ok, and is same as empty.
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
* - empty is ok; it means not to store.
|
|
|
|
* - otherwise it must be a valid looking ref.
|
|
|
|
*/
|
|
|
|
if (!rs[i].dst)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
; /* ok */
|
|
|
|
else if (!*rs[i].dst)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
; /* ok */
|
|
|
|
else if (check_refname_format(rs[i].dst, flags))
|
|
|
|
goto invalid;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* LHS
|
|
|
|
* - empty is allowed; it means delete.
|
|
|
|
* - when wildcarded, it must be a valid looking ref.
|
|
|
|
* - otherwise, it must be an extended SHA-1, but
|
|
|
|
* there is no existing way to validate this.
|
|
|
|
*/
|
|
|
|
if (!*rs[i].src)
|
|
|
|
; /* empty is ok */
|
|
|
|
else if (is_glob) {
|
|
|
|
if (check_refname_format(rs[i].src, flags))
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
goto invalid;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
; /* anything goes, for now */
|
|
|
|
/*
|
|
|
|
* RHS
|
|
|
|
* - missing is allowed, but LHS then must be a
|
|
|
|
* valid looking ref.
|
|
|
|
* - empty is not allowed.
|
|
|
|
* - otherwise it must be a valid looking ref.
|
|
|
|
*/
|
|
|
|
if (!rs[i].dst) {
|
|
|
|
if (check_refname_format(rs[i].src, flags))
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
goto invalid;
|
|
|
|
} else if (!*rs[i].dst) {
|
|
|
|
goto invalid;
|
|
|
|
} else {
|
|
|
|
if (check_refname_format(rs[i].dst, flags))
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
goto invalid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rs;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
|
|
|
|
invalid:
|
|
|
|
if (verify) {
|
|
|
|
/*
|
|
|
|
* nr_refspec must be greater than zero and i must be valid
|
|
|
|
* since it is only possible to reach this point from within
|
|
|
|
* the for loop above.
|
|
|
|
*/
|
|
|
|
free_refspecs(rs, i+1);
|
|
|
|
return NULL;
|
|
|
|
}
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
die("Invalid refspec '%s'", refspec[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int valid_fetch_refspec(const char *fetch_refspec_str)
|
|
|
|
{
|
|
|
|
struct refspec *refspec;
|
|
|
|
|
|
|
|
refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
|
|
|
|
free_refspecs(refspec, 1);
|
|
|
|
return !!refspec;
|
|
|
|
}
|
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
|
|
|
|
{
|
|
|
|
return parse_refspec_internal(nr_refspec, refspec, 1, 0);
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
{
|
|
|
|
return parse_refspec_internal(nr_refspec, refspec, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_refspec(int nr_refspec, struct refspec *refspec)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nr_refspec; i++) {
|
|
|
|
free(refspec[i].src);
|
|
|
|
free(refspec[i].dst);
|
|
|
|
}
|
|
|
|
free(refspec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int valid_remote_nick(const char *name)
|
|
|
|
{
|
|
|
|
if (!name[0] || is_dot_or_dotdot(name))
|
|
|
|
return 0;
|
|
|
|
return !strchr(name, '/'); /* no slash */
|
|
|
|
}
|
|
|
|
|
|
|
|
struct remote *remote_get(const char *name)
|
|
|
|
{
|
|
|
|
struct remote *ret;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
16 years ago
|
|
|
int name_given = 0;
|
|
|
|
|
|
|
|
read_config();
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
16 years ago
|
|
|
if (name)
|
|
|
|
name_given = 1;
|
|
|
|
else {
|
|
|
|
name = default_remote_name;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
16 years ago
|
|
|
name_given = explicit_default_remote_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = make_remote(name, 0);
|
|
|
|
if (valid_remote_nick(name)) {
|
|
|
|
if (!valid_remote(ret))
|
|
|
|
read_remotes_file(ret);
|
|
|
|
if (!valid_remote(ret))
|
|
|
|
read_branches_file(ret);
|
|
|
|
}
|
|
|
|
if (name_given && !valid_remote(ret))
|
|
|
|
add_url_alias(ret, name);
|
|
|
|
if (!valid_remote(ret))
|
|
|
|
return NULL;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
|
|
|
|
ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int remote_is_configured(const char *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
read_config();
|
|
|
|
|
|
|
|
for (i = 0; i < remotes_nr; i++)
|
|
|
|
if (!strcmp(name, remotes[i]->name))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_remote(each_remote_fn fn, void *priv)
|
|
|
|
{
|
|
|
|
int i, result = 0;
|
|
|
|
read_config();
|
|
|
|
for (i = 0; i < remotes_nr && !result; i++) {
|
|
|
|
struct remote *r = remotes[i];
|
|
|
|
if (!r)
|
|
|
|
continue;
|
|
|
|
if (!r->fetch)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
r->fetch = parse_fetch_refspec(r->fetch_refspec_nr,
|
|
|
|
r->fetch_refspec);
|
|
|
|
if (!r->push)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
r->push = parse_push_refspec(r->push_refspec_nr,
|
|
|
|
r->push_refspec);
|
|
|
|
result = fn(r, priv);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ref_remove_duplicates(struct ref *ref_map)
|
|
|
|
{
|
|
|
|
struct string_list refs = STRING_LIST_INIT_NODUP;
|
|
|
|
struct string_list_item *item = NULL;
|
|
|
|
struct ref *prev = NULL, *next = NULL;
|
|
|
|
for (; ref_map; prev = ref_map, ref_map = next) {
|
|
|
|
next = ref_map->next;
|
|
|
|
if (!ref_map->peer_ref)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
item = string_list_lookup(&refs, ref_map->peer_ref->name);
|
|
|
|
if (item) {
|
|
|
|
if (strcmp(((struct ref *)item->util)->name,
|
|
|
|
ref_map->name))
|
|
|
|
die("%s tracks both %s and %s",
|
|
|
|
ref_map->peer_ref->name,
|
|
|
|
((struct ref *)item->util)->name,
|
|
|
|
ref_map->name);
|
|
|
|
prev->next = ref_map->next;
|
|
|
|
free(ref_map->peer_ref);
|
|
|
|
free(ref_map);
|
|
|
|
ref_map = prev; /* skip this; we freed it */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
item = string_list_insert(&refs, ref_map->peer_ref->name);
|
|
|
|
item->util = ref_map;
|
|
|
|
}
|
|
|
|
string_list_clear(&refs, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int remote_has_url(struct remote *remote, const char *url)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < remote->url_nr; i++) {
|
|
|
|
if (!strcmp(remote->url[i], url))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int match_name_with_pattern(const char *key, const char *name,
|
|
|
|
const char *value, char **result)
|
|
|
|
{
|
|
|
|
const char *kstar = strchr(key, '*');
|
|
|
|
size_t klen;
|
|
|
|
size_t ksuffixlen;
|
|
|
|
size_t namelen;
|
|
|
|
int ret;
|
|
|
|
if (!kstar)
|
|
|
|
die("Key '%s' of pattern had no '*'", key);
|
|
|
|
klen = kstar - key;
|
|
|
|
ksuffixlen = strlen(kstar + 1);
|
|
|
|
namelen = strlen(name);
|
|
|
|
ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
|
|
|
|
!memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
|
|
|
|
if (ret && value) {
|
|
|
|
const char *vstar = strchr(value, '*');
|
|
|
|
size_t vlen;
|
|
|
|
size_t vsuffixlen;
|
|
|
|
if (!vstar)
|
|
|
|
die("Value '%s' of pattern has no '*'", value);
|
|
|
|
vlen = vstar - value;
|
|
|
|
vsuffixlen = strlen(vstar + 1);
|
|
|
|
*result = xmalloc(vlen + vsuffixlen +
|
|
|
|
strlen(name) -
|
|
|
|
klen - ksuffixlen + 1);
|
|
|
|
strncpy(*result, value, vlen);
|
|
|
|
strncpy(*result + vlen,
|
|
|
|
name + klen, namelen - klen - ksuffixlen);
|
|
|
|
strcpy(*result + vlen + namelen - klen - ksuffixlen,
|
|
|
|
vstar + 1);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int find_src = !query->src;
|
|
|
|
|
|
|
|
if (find_src && !query->dst)
|
|
|
|
return error("query_refspecs: need either src or dst");
|
|
|
|
|
|
|
|
for (i = 0; i < ref_count; i++) {
|
|
|
|
struct refspec *refspec = &refs[i];
|
|
|
|
const char *key = find_src ? refspec->dst : refspec->src;
|
|
|
|
const char *value = find_src ? refspec->src : refspec->dst;
|
|
|
|
const char *needle = find_src ? query->dst : query->src;
|
|
|
|
char **result = find_src ? &query->src : &query->dst;
|
|
|
|
|
|
|
|
if (!refspec->dst)
|
|
|
|
continue;
|
|
|
|
if (refspec->pattern) {
|
|
|
|
if (match_name_with_pattern(key, needle, value, result)) {
|
|
|
|
query->force = refspec->force;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(needle, key)) {
|
|
|
|
*result = xstrdup(value);
|
|
|
|
query->force = refspec->force;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct refspec query;
|
|
|
|
|
|
|
|
memset(&query, 0, sizeof(struct refspec));
|
|
|
|
query.src = (char *)name;
|
|
|
|
|
|
|
|
if (query_refspecs(refspecs, nr_refspec, &query))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return query.dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
|
|
|
|
{
|
|
|
|
return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
size_t len = strlen(name);
|
|
|
|
struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
|
|
|
|
memcpy(ref->name, prefix, prefixlen);
|
|
|
|
memcpy(ref->name + prefixlen, name, len);
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *alloc_ref(const char *name)
|
|
|
|
{
|
|
|
|
return alloc_ref_with_prefix("", 0, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *copy_ref(const struct ref *ref)
|
|
|
|
{
|
|
|
|
struct ref *cpy;
|
|
|
|
size_t len;
|
|
|
|
if (!ref)
|
|
|
|
return NULL;
|
|
|
|
len = strlen(ref->name);
|
|
|
|
cpy = xmalloc(sizeof(struct ref) + len + 1);
|
|
|
|
memcpy(cpy, ref, sizeof(struct ref) + len + 1);
|
|
|
|
cpy->next = NULL;
|
|
|
|
cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
|
|
|
|
cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
|
|
|
|
cpy->peer_ref = copy_ref(ref->peer_ref);
|
|
|
|
return cpy;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *copy_ref_list(const struct ref *ref)
|
|
|
|
{
|
|
|
|
struct ref *ret = NULL;
|
|
|
|
struct ref **tail = &ret;
|
|
|
|
while (ref) {
|
|
|
|
*tail = copy_ref(ref);
|
|
|
|
ref = ref->next;
|
|
|
|
tail = &((*tail)->next);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_ref(struct ref *ref)
|
|
|
|
{
|
|
|
|
if (!ref)
|
|
|
|
return;
|
|
|
|
free_ref(ref->peer_ref);
|
|
|
|
free(ref->remote_status);
|
|
|
|
free(ref->symref);
|
|
|
|
free(ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_refs(struct ref *ref)
|
|
|
|
{
|
|
|
|
struct ref *next;
|
|
|
|
while (ref) {
|
|
|
|
next = ref->next;
|
|
|
|
free_ref(ref);
|
|
|
|
ref = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ref_compare_name(const void *va, const void *vb)
|
|
|
|
{
|
|
|
|
const struct ref *a = va, *b = vb;
|
|
|
|
return strcmp(a->name, b->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *ref_list_get_next(const void *a)
|
|
|
|
{
|
|
|
|
return ((const struct ref *)a)->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ref_list_set_next(void *a, void *next)
|
|
|
|
{
|
|
|
|
((struct ref *)a)->next = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
|
|
|
|
{
|
|
|
|
*l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int count_refspec_match(const char *pattern,
|
|
|
|
struct ref *refs,
|
|
|
|
struct ref **matched_ref)
|
|
|
|
{
|
|
|
|
int patlen = strlen(pattern);
|
|
|
|
struct ref *matched_weak = NULL;
|
|
|
|
struct ref *matched = NULL;
|
|
|
|
int weak_match = 0;
|
|
|
|
int match = 0;
|
|
|
|
|
|
|
|
for (weak_match = match = 0; refs; refs = refs->next) {
|
|
|
|
char *name = refs->name;
|
|
|
|
int namelen = strlen(name);
|
|
|
|
|
|
|
|
if (!refname_match(pattern, name, ref_rev_parse_rules))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* A match is "weak" if it is with refs outside
|
|
|
|
* heads or tags, and did not specify the pattern
|
|
|
|
* in full (e.g. "refs/remotes/origin/master") or at
|
|
|
|
* least from the toplevel (e.g. "remotes/origin/master");
|
|
|
|
* otherwise "git push $URL master" would result in
|
|
|
|
* ambiguity between remotes/origin/master and heads/master
|
|
|
|
* at the remote site.
|
|
|
|
*/
|
|
|
|
if (namelen != patlen &&
|
|
|
|
patlen != namelen - 5 &&
|
|
|
|
prefixcmp(name, "refs/heads/") &&
|
|
|
|
prefixcmp(name, "refs/tags/")) {
|
|
|
|
/* We want to catch the case where only weak
|
|
|
|
* matches are found and there are multiple
|
|
|
|
* matches, and where more than one strong
|
|
|
|
* matches are found, as ambiguous. One
|
|
|
|
* strong match with zero or more weak matches
|
|
|
|
* are acceptable as a unique match.
|
|
|
|
*/
|
|
|
|
matched_weak = refs;
|
|
|
|
weak_match++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
matched = refs;
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!matched) {
|
|
|
|
*matched_ref = matched_weak;
|
|
|
|
return weak_match;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*matched_ref = matched;
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tail_link_ref(struct ref *ref, struct ref ***tail)
|
|
|
|
{
|
|
|
|
**tail = ref;
|
|
|
|
while (ref->next)
|
|
|
|
ref = ref->next;
|
|
|
|
*tail = &ref->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *alloc_delete_ref(void)
|
|
|
|
{
|
|
|
|
struct ref *ref = alloc_ref("(delete)");
|
|
|
|
hashclr(ref->new_sha1);
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *try_explicit_object_name(const char *name)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
struct ref *ref;
|
|
|
|
|
|
|
|
if (!*name)
|
|
|
|
return alloc_delete_ref();
|
|
|
|
if (get_sha1(name, sha1))
|
|
|
|
return NULL;
|
|
|
|
ref = alloc_ref(name);
|
|
|
|
hashcpy(ref->new_sha1, sha1);
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *make_linked_ref(const char *name, struct ref ***tail)
|
|
|
|
{
|
|
|
|
struct ref *ret = alloc_ref(name);
|
|
|
|
tail_link_ref(ret, tail);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
static char *guess_ref(const char *name, struct ref *peer)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
unsigned char sha1[20];
|
|
|
|
|
|
|
|
const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
if (!r)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!prefixcmp(r, "refs/heads/"))
|
|
|
|
strbuf_addstr(&buf, "refs/heads/");
|
|
|
|
else if (!prefixcmp(r, "refs/tags/"))
|
|
|
|
strbuf_addstr(&buf, "refs/tags/");
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
strbuf_addstr(&buf, name);
|
|
|
|
return strbuf_detach(&buf, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int match_explicit(struct ref *src, struct ref *dst,
|
|
|
|
struct ref ***dst_tail,
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
struct refspec *rs)
|
|
|
|
{
|
|
|
|
struct ref *matched_src, *matched_dst;
|
|
|
|
int copy_src;
|
|
|
|
|
|
|
|
const char *dst_value = rs->dst;
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
char *dst_guess;
|
|
|
|
|
|
|
|
if (rs->pattern || rs->matching)
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
return 0;
|
|
|
|
|
|
|
|
matched_src = matched_dst = NULL;
|
|
|
|
switch (count_refspec_match(rs->src, src, &matched_src)) {
|
|
|
|
case 1:
|
|
|
|
copy_src = 1;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
/* The source could be in the get_sha1() format
|
|
|
|
* not a reference name. :refs/other is a
|
|
|
|
* way to delete 'other' ref at the remote end.
|
|
|
|
*/
|
|
|
|
matched_src = try_explicit_object_name(rs->src);
|
|
|
|
if (!matched_src)
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
return error("src refspec %s does not match any.", rs->src);
|
|
|
|
copy_src = 0;
|
|
|
|
break;
|
|
|
|
default:
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
return error("src refspec %s matches more than one.", rs->src);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dst_value) {
|
|
|
|
unsigned char sha1[20];
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
|
|
|
|
if (!dst_value ||
|
|
|
|
((flag & REF_ISSYMREF) &&
|
|
|
|
prefixcmp(dst_value, "refs/heads/")))
|
|
|
|
die("%s cannot be resolved to branch.",
|
|
|
|
matched_src->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (count_refspec_match(dst_value, dst, &matched_dst)) {
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
if (!memcmp(dst_value, "refs/", 5))
|
|
|
|
matched_dst = make_linked_ref(dst_value, dst_tail);
|
push: don't guess at qualifying remote refs on deletion
When we try to push a ref and the right-hand side of the
refspec does not find a match, we try to create it. If it is
not fully qualified, we try to guess where it would go in
the refs hierarchy based on the left-hand source side. If
the source side is not a ref, then we give up and give a
long explanatory message.
For deletions, however, this doesn't make any sense. We
would never want to create on the remote side, and if an
unqualified ref can't be matched, it is simply an error. The
current code handles this already because the left-hand side
is empty, and therefore does not give us a hint as to where
the right-hand side should go, and we properly error out.
Unfortunately, the error message is the long "we tried to
qualify this, but the source side didn't let us guess"
message, which is quite confusing.
Instead, we can just be more succinct and say "we can't
delete this because we couldn't find it". So before:
$ git push origin :bogus
error: unable to push to unqualified destination: bogus
The destination refspec neither matches an existing ref on the remote nor
begins with refs/, and we are unable to guess a prefix based on the source ref.
error: failed to push some refs to '$URL'
and now:
$ git push origin :bogus
error: unable to delete 'bogus': remote ref does not exist
error: failed to push some refs to '$URL'
It is tempting to also catch a fully-qualified ref like
"refs/heads/bogus" and generate the same error message.
However, that currently does not error out at all, and
instead gets sent to the remote side, which typically
generates a warning:
$ git push origin:refs/heads/bogus
remote: warning: Deleting a non-existent ref.
To $URL
- [deleted] bogus
While it would be nice to catch this error early, a
client-side error would mean aborting the push entirely and
changing push's exit code. For example, right now you can
do:
$ git push origin refs/heads/foo refs/heads/bar
and end up in a state where "foo" and "bar" are deleted,
whether both of them currently exist or not (and see an
error only if we actually failed to contact the server).
Generating an error would cause a regression for this use
case.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
13 years ago
|
|
|
else if (is_null_sha1(matched_src->new_sha1))
|
|
|
|
error("unable to delete '%s': remote ref does not exist",
|
|
|
|
dst_value);
|
|
|
|
else if ((dst_guess = guess_ref(dst_value, matched_src)))
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
matched_dst = make_linked_ref(dst_guess, dst_tail);
|
|
|
|
else
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
error("unable to push to unqualified destination: %s\n"
|
|
|
|
"The destination refspec neither matches an "
|
|
|
|
"existing ref on the remote nor\n"
|
|
|
|
"begins with refs/, and we are unable to "
|
|
|
|
"guess a prefix based on the source ref.",
|
|
|
|
dst_value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
matched_dst = NULL;
|
|
|
|
error("dst refspec %s matches more than one.",
|
|
|
|
dst_value);
|
|
|
|
break;
|
|
|
|
}
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
if (!matched_dst)
|
|
|
|
return -1;
|
|
|
|
if (matched_dst->peer_ref)
|
|
|
|
return error("dst ref %s receives from more than one src.",
|
|
|
|
matched_dst->name);
|
|
|
|
else {
|
|
|
|
matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
|
|
|
|
matched_dst->force = rs->force;
|
|
|
|
}
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int match_explicit_refs(struct ref *src, struct ref *dst,
|
|
|
|
struct ref ***dst_tail, struct refspec *rs,
|
|
|
|
int rs_nr)
|
|
|
|
{
|
|
|
|
int i, errs;
|
|
|
|
for (i = errs = 0; i < rs_nr; i++)
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
errs += match_explicit(src, dst, dst_tail, &rs[i]);
|
|
|
|
return errs;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
|
|
|
|
int send_mirror, int direction, const struct refspec **ret_pat)
|
|
|
|
{
|
|
|
|
const struct refspec *pat;
|
|
|
|
char *name;
|
|
|
|
int i;
|
|
|
|
int matching_refs = -1;
|
|
|
|
for (i = 0; i < rs_nr; i++) {
|
|
|
|
if (rs[i].matching &&
|
|
|
|
(matching_refs == -1 || rs[i].force)) {
|
|
|
|
matching_refs = i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rs[i].pattern) {
|
|
|
|
const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
|
|
|
|
int match;
|
|
|
|
if (direction == FROM_SRC)
|
|
|
|
match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);
|
|
|
|
else
|
|
|
|
match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);
|
|
|
|
if (match) {
|
|
|
|
matching_refs = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (matching_refs == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pat = rs + matching_refs;
|
|
|
|
if (pat->matching) {
|
|
|
|
/*
|
|
|
|
* "matching refs"; traditionally we pushed everything
|
|
|
|
* including refs outside refs/heads/ hierarchy, but
|
|
|
|
* that does not make much sense these days.
|
|
|
|
*/
|
|
|
|
if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
|
|
|
|
return NULL;
|
|
|
|
name = xstrdup(ref->name);
|
|
|
|
}
|
|
|
|
if (ret_pat)
|
|
|
|
*ret_pat = pat;
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref **tail_ref(struct ref **head)
|
|
|
|
{
|
|
|
|
struct ref **tail = head;
|
|
|
|
while (*tail)
|
|
|
|
tail = &((*tail)->next);
|
|
|
|
return tail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given the set of refs the local repository has, the set of refs the
|
|
|
|
* remote repository has, and the refspec used for push, determine
|
|
|
|
* what remote refs we will update and with what value by setting
|
|
|
|
* peer_ref (which object is being pushed) and force (if the push is
|
|
|
|
* forced) in elements of "dst". The function may add new elements to
|
|
|
|
* dst (e.g. pushing to a new branch, done in match_explicit_refs).
|
|
|
|
*/
|
|
|
|
int match_push_refs(struct ref *src, struct ref **dst,
|
|
|
|
int nr_refspec, const char **refspec, int flags)
|
|
|
|
{
|
|
|
|
struct refspec *rs;
|
|
|
|
int send_all = flags & MATCH_REFS_ALL;
|
|
|
|
int send_mirror = flags & MATCH_REFS_MIRROR;
|
|
|
|
int send_prune = flags & MATCH_REFS_PRUNE;
|
|
|
|
int errs;
|
|
|
|
static const char *default_refspec[] = { ":", NULL };
|
|
|
|
struct ref *ref, **dst_tail = tail_ref(dst);
|
|
|
|
|
|
|
|
if (!nr_refspec) {
|
|
|
|
nr_refspec = 1;
|
|
|
|
refspec = default_refspec;
|
|
|
|
}
|
|
|
|
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
|
|
|
|
errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
|
|
|
|
|
|
|
|
/* pick the remainder */
|
|
|
|
for (ref = src; ref; ref = ref->next) {
|
|
|
|
struct ref *dst_peer;
|
|
|
|
const struct refspec *pat = NULL;
|
|
|
|
char *dst_name;
|
|
|
|
|
|
|
|
if (ref->peer_ref)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
|
|
|
|
if (!dst_name)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dst_peer = find_ref_by_name(*dst, dst_name);
|
|
|
|
if (dst_peer) {
|
|
|
|
if (dst_peer->peer_ref)
|
|
|
|
/* We're already sending something to this ref. */
|
|
|
|
goto free_name;
|
|
|
|
} else {
|
|
|
|
if (pat->matching && !(send_all || send_mirror))
|
|
|
|
/*
|
|
|
|
* Remote doesn't have it, and we have no
|
|
|
|
* explicit pattern, and we don't have
|
|
|
|
* --all nor --mirror.
|
|
|
|
*/
|
|
|
|
goto free_name;
|
|
|
|
|
|
|
|
/* Create a new one and link it */
|
|
|
|
dst_peer = make_linked_ref(dst_name, &dst_tail);
|
|
|
|
hashcpy(dst_peer->new_sha1, ref->new_sha1);
|
|
|
|
}
|
|
|
|
dst_peer->peer_ref = copy_ref(ref);
|
|
|
|
dst_peer->force = pat->force;
|
|
|
|
free_name:
|
|
|
|
free(dst_name);
|
|
|
|
}
|
|
|
|
if (send_prune) {
|
|
|
|
/* check for missing refs on the remote */
|
|
|
|
for (ref = *dst; ref; ref = ref->next) {
|
|
|
|
char *src_name;
|
|
|
|
|
|
|
|
if (ref->peer_ref)
|
|
|
|
/* We're already sending something to this ref. */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
|
|
|
|
if (src_name) {
|
|
|
|
if (!find_ref_by_name(src, src_name))
|
|
|
|
ref->peer_ref = alloc_delete_ref();
|
|
|
|
free(src_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (errs)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
|
|
|
int force_update)
|
|
|
|
{
|
|
|
|
struct ref *ref;
|
|
|
|
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
|
|
if (ref->peer_ref)
|
|
|
|
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
|
|
|
|
else if (!send_mirror)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ref->deletion = is_null_sha1(ref->new_sha1);
|
|
|
|
if (!ref->deletion &&
|
|
|
|
!hashcmp(ref->old_sha1, ref->new_sha1)) {
|
|
|
|
ref->status = REF_STATUS_UPTODATE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This part determines what can overwrite what.
|
|
|
|
* The rules are:
|
|
|
|
*
|
|
|
|
* (0) you can always use --force or +A:B notation to
|
|
|
|
* selectively force individual ref pairs.
|
|
|
|
*
|
|
|
|
* (1) if the old thing does not exist, it is OK.
|
|
|
|
*
|
|
|
|
* (2) if you do not have the old thing, you are not allowed
|
|
|
|
* to overwrite it; you would not know what you are losing
|
|
|
|
* otherwise.
|
|
|
|
*
|
|
|
|
* (3) if both new and old are commit-ish, and new is a
|
|
|
|
* descendant of old, it is OK.
|
|
|
|
*
|
|
|
|
* (4) regardless of all of the above, removing :B is
|
|
|
|
* always allowed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ref->nonfastforward =
|
|
|
|
!ref->deletion &&
|
|
|
|
!is_null_sha1(ref->old_sha1) &&
|
|
|
|
(!has_sha1_file(ref->old_sha1)
|
|
|
|
|| !ref_newer(ref->new_sha1, ref->old_sha1));
|
|
|
|
|
|
|
|
if (ref->nonfastforward && !ref->force && !force_update) {
|
|
|
|
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct branch *branch_get(const char *name)
|
|
|
|
{
|
|
|
|
struct branch *ret;
|
|
|
|
|
|
|
|
read_config();
|
|
|
|
if (!name || !*name || !strcmp(name, "HEAD"))
|
|
|
|
ret = current_branch;
|
|
|
|
else
|
|
|
|
ret = make_branch(name, 0);
|
|
|
|
if (ret && ret->remote_name) {
|
|
|
|
ret->remote = remote_get(ret->remote_name);
|
|
|
|
if (ret->merge_nr) {
|
|
|
|
int i;
|
|
|
|
ret->merge = xcalloc(sizeof(*ret->merge),
|
|
|
|
ret->merge_nr);
|
|
|
|
for (i = 0; i < ret->merge_nr; i++) {
|
|
|
|
ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
|
|
|
|
ret->merge[i]->src = xstrdup(ret->merge_name[i]);
|
|
|
|
if (remote_find_tracking(ret->remote, ret->merge[i])
|
|
|
|
&& !strcmp(ret->remote_name, "."))
|
|
|
|
ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int branch_has_merge_config(struct branch *branch)
|
|
|
|
{
|
|
|
|
return branch && !!branch->merge;
|
|
|
|
}
|
|
|
|
|
|
|
|
int branch_merge_matches(struct branch *branch,
|
|
|
|
int i,
|
|
|
|
const char *refname)
|
|
|
|
{
|
|
|
|
if (!branch || i < 0 || i >= branch->merge_nr)
|
|
|
|
return 0;
|
|
|
|
return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *get_expanded_map(const struct ref *remote_refs,
|
|
|
|
const struct refspec *refspec)
|
|
|
|
{
|
|
|
|
const struct ref *ref;
|
|
|
|
struct ref *ret = NULL;
|
|
|
|
struct ref **tail = &ret;
|
|
|
|
|
|
|
|
char *expn_name;
|
|
|
|
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
|
|
if (strchr(ref->name, '^'))
|
|
|
|
continue; /* a dereference item */
|
|
|
|
if (match_name_with_pattern(refspec->src, ref->name,
|
|
|
|
refspec->dst, &expn_name)) {
|
|
|
|
struct ref *cpy = copy_ref(ref);
|
|
|
|
|
|
|
|
cpy->peer_ref = alloc_ref(expn_name);
|
|
|
|
free(expn_name);
|
|
|
|
if (refspec->force)
|
|
|
|
cpy->peer_ref->force = 1;
|
|
|
|
*tail = cpy;
|
|
|
|
tail = &cpy->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name)
|
|
|
|
{
|
|
|
|
const struct ref *ref;
|
|
|
|
for (ref = refs; ref; ref = ref->next) {
|
|
|
|
if (refname_match(name, ref->name, ref_fetch_rules))
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
|
|
|
|
{
|
|
|
|
const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
|
|
|
|
|
|
|
|
if (!ref)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return copy_ref(ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *get_local_ref(const char *name)
|
|
|
|
{
|
|
|
|
if (!name || name[0] == '\0')
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!prefixcmp(name, "refs/"))
|
|
|
|
return alloc_ref(name);
|
|
|
|
|
|
|
|
if (!prefixcmp(name, "heads/") ||
|
|
|
|
!prefixcmp(name, "tags/") ||
|
|
|
|
!prefixcmp(name, "remotes/"))
|
|
|
|
return alloc_ref_with_prefix("refs/", 5, name);
|
|
|
|
|
|
|
|
return alloc_ref_with_prefix("refs/heads/", 11, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_fetch_map(const struct ref *remote_refs,
|
|
|
|
const struct refspec *refspec,
|
|
|
|
struct ref ***tail,
|
|
|
|
int missing_ok)
|
|
|
|
{
|
|
|
|
struct ref *ref_map, **rmp;
|
|
|
|
|
|
|
|
if (refspec->pattern) {
|
|
|
|
ref_map = get_expanded_map(remote_refs, refspec);
|
|
|
|
} else {
|
|
|
|
const char *name = refspec->src[0] ? refspec->src : "HEAD";
|
|
|
|
|
|
|
|
ref_map = get_remote_ref(remote_refs, name);
|
|
|
|
if (!missing_ok && !ref_map)
|
|
|
|
die("Couldn't find remote ref %s", name);
|
|
|
|
if (ref_map) {
|
|
|
|
ref_map->peer_ref = get_local_ref(refspec->dst);
|
|
|
|
if (ref_map->peer_ref && refspec->force)
|
|
|
|
ref_map->peer_ref->force = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (rmp = &ref_map; *rmp; ) {
|
|
|
|
if ((*rmp)->peer_ref) {
|
|
|
|
if (check_refname_format((*rmp)->peer_ref->name + 5,
|
|
|
|
REFNAME_ALLOW_ONELEVEL)) {
|
|
|
|
struct ref *ignore = *rmp;
|
|
|
|
error("* Ignoring funny ref '%s' locally",
|
|
|
|
(*rmp)->peer_ref->name);
|
|
|
|
*rmp = (*rmp)->next;
|
|
|
|
free(ignore->peer_ref);
|
|
|
|
free(ignore);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rmp = &((*rmp)->next);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ref_map)
|
|
|
|
tail_link_ref(ref_map, tail);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int resolve_remote_symref(struct ref *ref, struct ref *list)
|
|
|
|
{
|
|
|
|
if (!ref->symref)
|
|
|
|
return 0;
|
|
|
|
for (; list; list = list->next)
|
|
|
|
if (!strcmp(ref->symref, list->name)) {
|
|
|
|
hashcpy(ref->old_sha1, list->old_sha1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unmark_and_free(struct commit_list *list, unsigned int mark)
|
|
|
|
{
|
|
|
|
while (list) {
|
|
|
|
struct commit_list *temp = list;
|
|
|
|
temp->item->object.flags &= ~mark;
|
|
|
|
list = temp->next;
|
|
|
|
free(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
|
|
|
|
{
|
|
|
|
struct object *o;
|
|
|
|
struct commit *old, *new;
|
|
|
|
struct commit_list *list, *used;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
/* Both new and old must be commit-ish and new is descendant of
|
|
|
|
* old. Otherwise we require --force.
|
|
|
|
*/
|
|
|
|
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
|
|
|
if (!o || o->type != OBJ_COMMIT)
|
|
|
|
return 0;
|
|
|
|
old = (struct commit *) o;
|
|
|
|
|
|
|
|
o = deref_tag(parse_object(new_sha1), NULL, 0);
|
|
|
|
if (!o || o->type != OBJ_COMMIT)
|
|
|
|
return 0;
|
|
|
|
new = (struct commit *) o;
|
|
|
|
|
|
|
|
if (parse_commit(new) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
used = list = NULL;
|
|
|
|
commit_list_insert(new, &list);
|
|
|
|
while (list) {
|
|
|
|
new = pop_most_recent_commit(&list, TMP_MARK);
|
|
|
|
commit_list_insert(new, &used);
|
|
|
|
if (new == old) {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unmark_and_free(list, TMP_MARK);
|
|
|
|
unmark_and_free(used, TMP_MARK);
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if there is anything to report, otherwise false.
|
|
|
|
*/
|
|
|
|
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
struct commit *ours, *theirs;
|
|
|
|
char symmetric[84];
|
|
|
|
struct rev_info revs;
|
|
|
|
const char *rev_argv[10], *base;
|
|
|
|
int rev_argc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Nothing to report unless we are marked to build on top of
|
|
|
|
* somebody else.
|
|
|
|
*/
|
|
|
|
if (!branch ||
|
|
|
|
!branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If what we used to build on no longer exists, there is
|
|
|
|
* nothing to report.
|
|
|
|
*/
|
|
|
|
base = branch->merge[0]->dst;
|
|
|
|
if (read_ref(base, sha1))
|
|
|
|
return 0;
|
|
|
|
theirs = lookup_commit_reference(sha1);
|
|
|
|
if (!theirs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (read_ref(branch->refname, sha1))
|
|
|
|
return 0;
|
|
|
|
ours = lookup_commit_reference(sha1);
|
|
|
|
if (!ours)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* are we the same? */
|
|
|
|
if (theirs == ours)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Run "rev-list --left-right ours...theirs" internally... */
|
|
|
|
rev_argc = 0;
|
|
|
|
rev_argv[rev_argc++] = NULL;
|
|
|
|
rev_argv[rev_argc++] = "--left-right";
|
|
|
|
rev_argv[rev_argc++] = symmetric;
|
|
|
|
rev_argv[rev_argc++] = "--";
|
|
|
|
rev_argv[rev_argc] = NULL;
|
|
|
|
|
|
|
|
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
|
|
|
strcpy(symmetric + 40, "...");
|
|
|
|
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
|
|
|
|
|
|
|
init_revisions(&revs, NULL);
|
|
|
|
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
|
|
|
prepare_revision_walk(&revs);
|
|
|
|
|
|
|
|
/* ... and count the commits on each side. */
|
|
|
|
*num_ours = 0;
|
|
|
|
*num_theirs = 0;
|
|
|
|
while (1) {
|
|
|
|
struct commit *c = get_revision(&revs);
|
|
|
|
if (!c)
|
|
|
|
break;
|
|
|
|
if (c->object.flags & SYMMETRIC_LEFT)
|
|
|
|
(*num_ours)++;
|
|
|
|
else
|
|
|
|
(*num_theirs)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear object flags smudged by the above traversal */
|
|
|
|
clear_commit_marks(ours, ALL_REV_FLAGS);
|
|
|
|
clear_commit_marks(theirs, ALL_REV_FLAGS);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true when there is anything to report, otherwise false.
|
|
|
|
*/
|
|
|
|
int format_tracking_info(struct branch *branch, struct strbuf *sb)
|
|
|
|
{
|
|
|
|
int num_ours, num_theirs;
|
|
|
|
const char *base;
|
|
|
|
|
|
|
|
if (!stat_tracking_info(branch, &num_ours, &num_theirs))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
base = branch->merge[0]->dst;
|
|
|
|
base = shorten_unambiguous_ref(base, 0);
|
|
|
|
if (!num_theirs)
|
|
|
|
strbuf_addf(sb,
|
|
|
|
Q_("Your branch is ahead of '%s' by %d commit.\n",
|
|
|
|
"Your branch is ahead of '%s' by %d commits.\n",
|
|
|
|
num_ours),
|
|
|
|
base, num_ours);
|
|
|
|
else if (!num_ours)
|
|
|
|
strbuf_addf(sb,
|
|
|
|
Q_("Your branch is behind '%s' by %d commit, "
|
|
|
|
"and can be fast-forwarded.\n",
|
|
|
|
"Your branch is behind '%s' by %d commits, "
|
|
|
|
"and can be fast-forwarded.\n",
|
|
|
|
num_theirs),
|
|
|
|
base, num_theirs);
|
|
|
|
else
|
|
|
|
strbuf_addf(sb,
|
|
|
|
Q_("Your branch and '%s' have diverged,\n"
|
|
|
|
"and have %d and %d different commit each, "
|
|
|
|
"respectively.\n",
|
|
|
|
"Your branch and '%s' have diverged,\n"
|
|
|
|
"and have %d and %d different commits each, "
|
|
|
|
"respectively.\n",
|
|
|
|
num_theirs),
|
|
|
|
base, num_ours, num_theirs);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
|
|
|
{
|
|
|
|
struct ref ***local_tail = cb_data;
|
|
|
|
struct ref *ref;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
/* we already know it starts with refs/ to get here */
|
|
|
|
if (check_refname_format(refname + 5, 0))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
len = strlen(refname) + 1;
|
|
|
|
ref = xcalloc(1, sizeof(*ref) + len);
|
|
|
|
hashcpy(ref->new_sha1, sha1);
|
|
|
|
memcpy(ref->name, refname, len);
|
|
|
|
**local_tail = ref;
|
|
|
|
*local_tail = &ref->next;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *get_local_heads(void)
|
|
|
|
{
|
|
|
|
struct ref *local_refs = NULL, **local_tail = &local_refs;
|
|
|
|
for_each_ref(one_local_ref, &local_tail);
|
|
|
|
return local_refs;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *guess_remote_head(const struct ref *head,
|
|
|
|
const struct ref *refs,
|
|
|
|
int all)
|
|
|
|
{
|
|
|
|
const struct ref *r;
|
|
|
|
struct ref *list = NULL;
|
|
|
|
struct ref **tail = &list;
|
|
|
|
|
|
|
|
if (!head)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some transports support directly peeking at
|
|
|
|
* where HEAD points; if that is the case, then
|
|
|
|
* we don't have to guess.
|
|
|
|
*/
|
|
|
|
if (head->symref)
|
|
|
|
return copy_ref(find_ref_by_name(refs, head->symref));
|
|
|
|
|
|
|
|
/* If refs/heads/master could be right, it is. */
|
|
|
|
if (!all) {
|
|
|
|
r = find_ref_by_name(refs, "refs/heads/master");
|
|
|
|
if (r && !hashcmp(r->old_sha1, head->old_sha1))
|
|
|
|
return copy_ref(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look for another ref that points there */
|
|
|
|
for (r = refs; r; r = r->next) {
|
|
|
|
if (r != head &&
|
|
|
|
!prefixcmp(r->name, "refs/heads/") &&
|
|
|
|
!hashcmp(r->old_sha1, head->old_sha1)) {
|
|
|
|
*tail = copy_ref(r);
|
|
|
|
tail = &((*tail)->next);
|
|
|
|
if (!all)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stale_heads_info {
|
|
|
|
struct string_list *ref_names;
|
|
|
|
struct ref **stale_refs_tail;
|
|
|
|
struct refspec *refs;
|
|
|
|
int ref_count;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int get_stale_heads_cb(const char *refname,
|
|
|
|
const unsigned char *sha1, int flags, void *cb_data)
|
|
|
|
{
|
|
|
|
struct stale_heads_info *info = cb_data;
|
|
|
|
struct refspec query;
|
|
|
|
memset(&query, 0, sizeof(struct refspec));
|
|
|
|
query.dst = (char *)refname;
|
|
|
|
|
|
|
|
if (query_refspecs(info->refs, info->ref_count, &query))
|
|
|
|
return 0; /* No matches */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we did find a suitable refspec and it's not a symref and
|
|
|
|
* it's not in the list of refs that currently exist in that
|
|
|
|
* remote we consider it to be stale.
|
|
|
|
*/
|
|
|
|
if (!((flags & REF_ISSYMREF) ||
|
|
|
|
string_list_has_string(info->ref_names, query.src))) {
|
|
|
|
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
|
|
|
|
hashcpy(ref->new_sha1, sha1);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(query.src);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)
|
|
|
|
{
|
|
|
|
struct ref *ref, *stale_refs = NULL;
|
|
|
|
struct string_list ref_names = STRING_LIST_INIT_NODUP;
|
|
|
|
struct stale_heads_info info;
|
|
|
|
info.ref_names = &ref_names;
|
|
|
|
info.stale_refs_tail = &stale_refs;
|
|
|
|
info.refs = refs;
|
|
|
|
info.ref_count = ref_count;
|
|
|
|
for (ref = fetch_map; ref; ref = ref->next)
|
|
|
|
string_list_append(&ref_names, ref->name);
|
|
|
|
sort_string_list(&ref_names);
|
|
|
|
for_each_ref(get_stale_heads_cb, &info);
|
|
|
|
string_list_clear(&ref_names, 0);
|
|
|
|
return stale_refs;
|
|
|
|
}
|