{
const char *slash = strchr(remote->name, '/');
char *frag;
- char *branch;
+ struct strbuf branch;
int n = slash ? slash - remote->name : 1000;
FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
char *s, *p;
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.
+ */
+ strbuf_init(&branch, 0);
frag = strchr(p, '#');
if (frag) {
*(frag++) = '\0';
- branch = xmalloc(strlen(frag) + 12);
- strcpy(branch, "refs/heads/");
- strcat(branch, frag);
+ 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 {
- branch = "refs/heads/master";
+ strbuf_reset(&branch);
+ strbuf_addstr(&branch, "HEAD:");
}
add_url_alias(remote, p);
- add_fetch_refspec(remote, branch);
+ add_fetch_refspec(remote, strbuf_detach(&branch, 0));
remote->fetch_tags = 1; /* always auto-follow */
}
alias_all_urls();
}
-struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
{
int i;
int st;
struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
+
for (i = 0; i < nr_refspec; i++) {
- const char *sp, *ep, *gp;
- sp = refspec[i];
- if (*sp == '+') {
+ size_t llen, rlen;
+ int is_glob;
+ const char *lhs, *rhs;
+
+ llen = rlen = is_glob = 0;
+
+ lhs = refspec[i];
+ if (*lhs == '+') {
rs[i].force = 1;
- sp++;
+ lhs++;
}
- gp = strstr(sp, "/*");
- ep = strchr(sp, ':');
- if (gp && ep && gp > ep)
- gp = NULL;
- if (ep) {
- if (ep[1]) {
- const char *glob = strstr(ep + 1, "/*");
- if (glob && glob[2])
- glob = NULL;
- if (!glob)
- gp = NULL;
- if (gp)
- rs[i].dst = xstrndup(ep + 1,
- glob - ep - 1);
- else
- rs[i].dst = xstrdup(ep + 1);
- }
- } else {
- ep = sp + strlen(sp);
- }
- if (gp && gp + 2 != ep)
- gp = NULL;
- if (gp) {
- rs[i].pattern = 1;
- ep = gp;
+
+ rhs = strrchr(lhs, ':');
+ if (rhs) {
+ rhs++;
+ rlen = strlen(rhs);
+ is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
+ if (is_glob)
+ rlen -= 2;
+ rs[i].dst = xstrndup(rhs, rlen);
}
- rs[i].src = xstrndup(sp, ep - sp);
- if (*rs[i].src) {
- st = check_ref_format(rs[i].src);
- if (st && st != CHECK_REF_FORMAT_ONELEVEL)
- die("Invalid refspec '%s'", refspec[i]);
+ llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
+ if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
+ if ((rhs && !is_glob) || (!rhs && fetch))
+ goto invalid;
+ is_glob = 1;
+ llen -= 2;
+ } else if (rhs && is_glob) {
+ goto invalid;
}
- if (rs[i].dst && *rs[i].dst) {
- st = check_ref_format(rs[i].dst);
- if (st && st != CHECK_REF_FORMAT_ONELEVEL)
- die("Invalid refspec '%s'", refspec[i]);
+
+ rs[i].pattern = is_glob;
+ rs[i].src = xstrndup(lhs, llen);
+
+ 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 {
+ st = check_ref_format(rs[i].src);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
+ }
+ /*
+ * RHS
+ * - missing is ok, and is same as empty.
+ * - empty is ok; it means not to store.
+ * - otherwise it must be a valid looking ref.
+ */
+ if (!rs[i].dst) {
+ ; /* ok */
+ } else if (!*rs[i].dst) {
+ ; /* ok */
+ } else {
+ st = check_ref_format(rs[i].dst);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
+ }
+ } 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) {
+ st = check_ref_format(rs[i].src);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ 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) {
+ st = check_ref_format(rs[i].src);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
+ } else if (!*rs[i].dst) {
+ goto invalid;
+ } else {
+ st = check_ref_format(rs[i].dst);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
+ }
}
}
return rs;
+
+ invalid:
+ die("Invalid refspec '%s'", refspec[i]);
+}
+
+struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
+{
+ return parse_refspec_internal(nr_refspec, refspec, 1);
+}
+
+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
+{
+ return parse_refspec_internal(nr_refspec, refspec, 0);
}
static int valid_remote_nick(const char *name)
add_url_alias(ret, name);
if (!ret->url)
return NULL;
- ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
- ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec);
+ 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;
}
if (!r)
continue;
if (!r->fetch)
- r->fetch = parse_ref_spec(r->fetch_refspec_nr,
- r->fetch_refspec);
+ r->fetch = parse_fetch_refspec(r->fetch_refspec_nr,
+ r->fetch_refspec);
if (!r->push)
- r->push = parse_ref_spec(r->push_refspec_nr,
- r->push_refspec);
+ r->push = parse_push_refspec(r->push_refspec_nr,
+ r->push_refspec);
result = fn(r, priv);
}
return result;
int nr_refspec, const char **refspec, int flags)
{
struct refspec *rs =
- parse_ref_spec(nr_refspec, (const char **) refspec);
+ parse_push_refspec(nr_refspec, (const char **) refspec);
int send_all = flags & MATCH_REFS_ALL;
int send_mirror = flags & MATCH_REFS_MIRROR;