summaryrefslogtreecommitdiff
path: root/remote.c
diff options
context:
space:
mode:
Diffstat (limited to 'remote.c')
-rw-r--r--remote.c306
1 files changed, 221 insertions, 85 deletions
diff --git a/remote.c b/remote.c
index d8a4671e68..2d9af4023e 100644
--- a/remote.c
+++ b/remote.c
@@ -2,123 +2,184 @@
#include "remote.h"
#include "refs.h"
+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;
+};
+
static struct remote **remotes;
-static int allocated_remotes;
+static int remotes_alloc;
+static int remotes_nr;
static struct branch **branches;
-static int allocated_branches;
+static int branches_alloc;
+static int branches_nr;
static struct branch *current_branch;
static const char *default_remote_name;
+static struct rewrite **rewrite;
+static int rewrite_alloc;
+static int rewrite_nr;
+
#define BUF_SIZE (2048)
static char buffer[BUF_SIZE];
+static const char *alias_url(const char *url)
+{
+ int i, j;
+ char *ret;
+ struct counted_string *longest;
+ int longest_i;
+
+ longest = NULL;
+ longest_i = -1;
+ for (i = 0; i < rewrite_nr; i++) {
+ if (!rewrite[i])
+ continue;
+ for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
+ if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+ (!longest ||
+ longest->len < rewrite[i]->instead_of[j].len)) {
+ longest = &(rewrite[i]->instead_of[j]);
+ longest_i = i;
+ }
+ }
+ }
+ if (!longest)
+ return url;
+
+ ret = malloc(rewrite[longest_i]->baselen +
+ (strlen(url) - longest->len) + 1);
+ strcpy(ret, rewrite[longest_i]->base);
+ strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+ return ret;
+}
+
static void add_push_refspec(struct remote *remote, const char *ref)
{
- int nr = remote->push_refspec_nr + 1;
- remote->push_refspec =
- xrealloc(remote->push_refspec, nr * sizeof(char *));
- remote->push_refspec[nr-1] = ref;
- remote->push_refspec_nr = nr;
+ 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)
{
- int nr = remote->fetch_refspec_nr + 1;
- remote->fetch_refspec =
- xrealloc(remote->fetch_refspec, nr * sizeof(char *));
- remote->fetch_refspec[nr-1] = ref;
- remote->fetch_refspec_nr = nr;
+ 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)
{
- int nr = remote->url_nr + 1;
- remote->url =
- xrealloc(remote->url, nr * sizeof(char *));
- remote->url[nr-1] = url;
- remote->url_nr = nr;
+ ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
+ remote->url[remote->url_nr++] = url;
+}
+
+static void add_url_alias(struct remote *remote, const char *url)
+{
+ add_url(remote, alias_url(url));
}
static struct remote *make_remote(const char *name, int len)
{
- int i, empty = -1;
+ struct remote *ret;
+ int i;
- for (i = 0; i < allocated_remotes; i++) {
- if (!remotes[i]) {
- if (empty < 0)
- empty = i;
- } else {
- if (len ? (!strncmp(name, remotes[i]->name, len) &&
- !remotes[i]->name[len]) :
- !strcmp(name, remotes[i]->name))
- return remotes[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];
}
- if (empty < 0) {
- empty = allocated_remotes;
- allocated_remotes += allocated_remotes ? allocated_remotes : 1;
- remotes = xrealloc(remotes,
- sizeof(*remotes) * allocated_remotes);
- memset(remotes + empty, 0,
- (allocated_remotes - empty) * sizeof(*remotes));
- }
- remotes[empty] = xcalloc(1, sizeof(struct remote));
+ ret = xcalloc(1, sizeof(struct remote));
+ ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
+ remotes[remotes_nr++] = ret;
if (len)
- remotes[empty]->name = xstrndup(name, len);
+ ret->name = xstrndup(name, len);
else
- remotes[empty]->name = xstrdup(name);
- return remotes[empty];
+ ret->name = xstrdup(name);
+ return ret;
}
static void add_merge(struct branch *branch, const char *name)
{
- int nr = branch->merge_nr + 1;
- branch->merge_name =
- xrealloc(branch->merge_name, nr * sizeof(char *));
- branch->merge_name[nr-1] = name;
- branch->merge_nr = nr;
+ 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)
{
- int i, empty = -1;
+ struct branch *ret;
+ int i;
char *refname;
- for (i = 0; i < allocated_branches; i++) {
- if (!branches[i]) {
- if (empty < 0)
- empty = i;
- } else {
- if (len ? (!strncmp(name, branches[i]->name, len) &&
- !branches[i]->name[len]) :
- !strcmp(name, branches[i]->name))
- return branches[i];
- }
+ 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];
}
- if (empty < 0) {
- empty = allocated_branches;
- allocated_branches += allocated_branches ? allocated_branches : 1;
- branches = xrealloc(branches,
- sizeof(*branches) * allocated_branches);
- memset(branches + empty, 0,
- (allocated_branches - empty) * sizeof(*branches));
- }
- branches[empty] = xcalloc(1, sizeof(struct branch));
+ ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
+ ret = xcalloc(1, sizeof(struct branch));
+ branches[branches_nr++] = ret;
if (len)
- branches[empty]->name = xstrndup(name, len);
+ ret->name = xstrndup(name, len);
else
- branches[empty]->name = xstrdup(name);
+ ret->name = xstrdup(name);
refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
strcpy(refname, "refs/heads/");
- strcpy(refname + strlen("refs/heads/"),
- branches[empty]->name);
- branches[empty]->refname = refname;
+ strcpy(refname + strlen("refs/heads/"), ret->name);
+ ret->refname = refname;
+
+ return ret;
+}
+
+static struct rewrite *make_rewrite(const char *base, int len)
+{
+ struct rewrite *ret;
+ int i;
+
+ for (i = 0; i < rewrite_nr; i++) {
+ if (len
+ ? (len == rewrite[i]->baselen &&
+ !strncmp(base, rewrite[i]->base, len))
+ : !strcmp(base, rewrite[i]->base))
+ return rewrite[i];
+ }
+
+ ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+ ret = xcalloc(1, sizeof(struct rewrite));
+ rewrite[rewrite_nr++] = ret;
+ if (len) {
+ ret->base = xstrndup(base, len);
+ ret->baselen = len;
+ }
+ else {
+ ret->base = xstrdup(base);
+ ret->baselen = strlen(base);
+ }
+ return ret;
+}
- return branches[empty];
+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)
@@ -154,7 +215,7 @@ static void read_remotes_file(struct remote *remote)
switch (value_list) {
case 0:
- add_url(remote, xstrdup(s));
+ add_url_alias(remote, xstrdup(s));
break;
case 1:
add_push_refspec(remote, xstrdup(s));
@@ -222,7 +283,7 @@ static void read_branches_file(struct remote *remote)
strbuf_reset(&branch);
strbuf_addstr(&branch, "HEAD:");
}
- add_url(remote, p);
+ add_url_alias(remote, p);
add_fetch_refspec(remote, strbuf_detach(&branch, 0));
remote->fetch_tags = 1; /* always auto-follow */
}
@@ -252,6 +313,19 @@ static int handle_config(const char *key, const char *value)
}
return 0;
}
+ if (!prefixcmp(key, "url.")) {
+ struct rewrite *rewrite;
+ name = key + 4;
+ subkey = strrchr(name, '.');
+ if (!subkey)
+ return 0;
+ rewrite = make_rewrite(name, subkey - name);
+ if (!strcmp(subkey, ".insteadof")) {
+ if (!value)
+ return config_error_nonbool(key);
+ add_instead_of(rewrite, xstrdup(value));
+ }
+ }
if (prefixcmp(key, "remote."))
return 0;
name = key + 7;
@@ -299,10 +373,23 @@ static int handle_config(const char *key, const char *value)
remote->fetch_tags = -1;
} else if (!strcmp(subkey, ".proxy")) {
remote->http_proxy = xstrdup(value);
- }
+ } else if (!strcmp(subkey, ".skipdefaultupdate"))
+ remote->skip_default_update = 1;
return 0;
}
+static void alias_all_urls(void)
+{
+ int i, j;
+ for (i = 0; i < remotes_nr; i++) {
+ if (!remotes[i])
+ continue;
+ for (j = 0; j < remotes[i]->url_nr; j++) {
+ remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
+ }
+ }
+}
+
static void read_config(void)
{
unsigned char sha1[20];
@@ -319,9 +406,10 @@ static void read_config(void)
make_branch(head_ref + strlen("refs/heads/"), 0);
}
git_config(handle_config);
+ alias_all_urls();
}
-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{
int i;
int st;
@@ -431,17 +519,32 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
return rs;
invalid:
+ if (verify) {
+ free(rs);
+ return NULL;
+ }
die("Invalid refspec '%s'", refspec[i]);
}
+int valid_fetch_refspec(const char *fetch_refspec_str)
+{
+ const char *fetch_refspec[] = { fetch_refspec_str };
+ struct refspec *refspec;
+
+ refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
+ if (refspec)
+ free(refspec);
+ return !!refspec;
+}
+
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
{
- return parse_refspec_internal(nr_refspec, refspec, 1);
+ return parse_refspec_internal(nr_refspec, refspec, 1, 0);
}
struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
{
- return parse_refspec_internal(nr_refspec, refspec, 0);
+ return parse_refspec_internal(nr_refspec, refspec, 0, 0);
}
static int valid_remote_nick(const char *name)
@@ -469,7 +572,7 @@ struct remote *remote_get(const char *name)
read_branches_file(ret);
}
if (!ret->url)
- add_url(ret, name);
+ add_url_alias(ret, name);
if (!ret->url)
return NULL;
ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
@@ -481,7 +584,7 @@ int for_each_remote(each_remote_fn fn, void *priv)
{
int i, result = 0;
read_config();
- for (i = 0; i < allocated_remotes && !result; i++) {
+ for (i = 0; i < remotes_nr && !result; i++) {
struct remote *r = remotes[i];
if (!r)
continue;
@@ -608,8 +711,7 @@ void free_refs(struct ref *ref)
struct ref *next;
while (ref) {
next = ref->next;
- if (ref->peer_ref)
- free(ref->peer_ref);
+ free(ref->peer_ref);
free(ref);
ref = next;
}
@@ -710,6 +812,26 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail)
return ret;
}
+static char *guess_ref(const char *name, struct ref *peer)
+{
+ struct strbuf buf = STRBUF_INIT;
+ unsigned char sha1[20];
+
+ const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+ 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,
struct refspec *rs,
@@ -718,6 +840,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
struct ref *matched_src, *matched_dst;
const char *dst_value = rs->dst;
+ char *dst_guess;
if (rs->pattern)
return errs;
@@ -745,9 +868,17 @@ static int match_explicit(struct ref *src, struct ref *dst,
errs = 1;
if (!dst_value) {
+ unsigned char sha1[20];
+ int flag;
+
if (!matched_src)
return errs;
- dst_value = matched_src->name;
+ dst_value = resolve_ref(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)) {
@@ -756,10 +887,15 @@ static int match_explicit(struct ref *src, struct ref *dst,
case 0:
if (!memcmp(dst_value, "refs/", 5))
matched_dst = make_linked_ref(dst_value, dst_tail);
+ else if((dst_guess = guess_ref(dst_value, matched_src)))
+ matched_dst = make_linked_ref(dst_guess, dst_tail);
else
- error("dst refspec %s does not match any "
- "existing ref on the remote and does "
- "not start with refs/.", dst_value);
+ 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;