X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=remote.c;h=7688f3b04d197007bce64a2bcc54c0b62c0b5922;hb=8c178687959732edab8bf91da9ed9f4fbcf7c14e;hp=ff2c8021678fee5ef12060d35c6eb1960e8792c7;hpb=5389db544defc9cd9415ffb9d8070c3e179610f2;p=git.git diff --git a/remote.c b/remote.c index ff2c80216..7688f3b04 100644 --- a/remote.c +++ b/remote.c @@ -1,6 +1,9 @@ #include "cache.h" #include "remote.h" #include "refs.h" +#include "commit.h" +#include "diff.h" +#include "revision.h" static struct refspec s_tag_refspec = { 0, @@ -339,13 +342,14 @@ static int handle_config(const char *key, const char *value, void *cb) 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 error("Config with no key for remote %s", name); - if (*subkey == '/') { - warning("Config remote shorthand cannot begin with '/': %s", name); - return 0; - } remote = make_remote(name, subkey - name); if (!strcmp(subkey, ".mirror")) remote->mirror = git_config_bool(key, value); @@ -424,6 +428,28 @@ static void read_config(void) alias_all_urls(); } +/* + * We need to make sure the tracking branches are well formed, but a + * wildcard refspec in "struct refspec" must have a trailing slash. We + * temporarily drop the trailing '/' while calling check_ref_format(), + * and put it back. The caller knows that a CHECK_REF_FORMAT_ONELEVEL + * error return is Ok for a wildcard refspec. + */ +static int verify_refname(char *name, int is_glob) +{ + int result, len = -1; + + if (is_glob) { + len = strlen(name); + assert(name[len - 1] == '/'); + name[len - 1] = '\0'; + } + result = check_ref_format(name); + if (is_glob) + name[len - 1] = '/'; + return result; +} + static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) { int i; @@ -431,11 +457,11 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); for (i = 0; i < nr_refspec; i++) { - size_t llen, rlen; + size_t llen; int is_glob; const char *lhs, *rhs; - llen = rlen = is_glob = 0; + llen = is_glob = 0; lhs = refspec[i]; if (*lhs == '+') { @@ -455,12 +481,9 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp } if (rhs) { - rhs++; - rlen = strlen(rhs); + size_t rlen = strlen(++rhs); is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); - if (is_glob) - rlen -= 2; - rs[i].dst = xstrndup(rhs, rlen); + rs[i].dst = xstrndup(rhs, rlen - is_glob); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); @@ -468,7 +491,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; - llen -= 2; + llen--; } else if (rhs && is_glob) { goto invalid; } @@ -485,7 +508,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (!*rs[i].src) ; /* empty is ok */ else { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -500,7 +523,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp } else if (!*rs[i].dst) { ; /* ok */ } else { - st = check_ref_format(rs[i].dst); + st = verify_refname(rs[i].dst, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -515,7 +538,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (!*rs[i].src) ; /* empty is ok */ else if (is_glob) { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -529,13 +552,13 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp * - otherwise it must be a valid looking ref. */ if (!rs[i].dst) { - st = check_ref_format(rs[i].src); + st = verify_refname(rs[i].src, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } else if (!*rs[i].dst) { goto invalid; } else { - st = check_ref_format(rs[i].dst); + st = verify_refname(rs[i].dst, is_glob); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } @@ -557,8 +580,7 @@ int valid_fetch_refspec(const char *fetch_refspec_str) struct refspec *refspec; refspec = parse_refspec_internal(1, fetch_refspec, 1, 1); - if (refspec) - free(refspec); + free(refspec); return !!refspec; } @@ -684,8 +706,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec) if (!fetch->dst) continue; if (fetch->pattern) { - if (!prefixcmp(needle, key) && - needle[strlen(key)] == '/') { + if (!prefixcmp(needle, key)) { *result = xmalloc(strlen(value) + strlen(needle) - strlen(key) + 1); @@ -963,9 +984,7 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && - !prefixcmp(src->name, rs[i].src) && - src->name[strlen(rs[i].src)] == '/') + if (rs[i].pattern && !prefixcmp(src->name, rs[i].src)) return rs + i; } if (matching_refs != -1) @@ -1222,3 +1241,111 @@ int resolve_remote_symref(struct ref *ref, struct ref *list) } return 1; } + +/* + * 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 (!resolve_ref(base, sha1, 1, NULL)) + return 0; + theirs = lookup_commit(sha1); + if (!theirs) + return 0; + + if (!resolve_ref(branch->refname, sha1, 1, NULL)) + return 0; + ours = lookup_commit(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; + if (!prefixcmp(base, "refs/remotes/")) { + base += strlen("refs/remotes/"); + } + if (!num_theirs) + strbuf_addf(sb, "Your branch is ahead of '%s' " + "by %d commit%s.\n", + base, num_ours, (num_ours == 1) ? "" : "s"); + else if (!num_ours) + strbuf_addf(sb, "Your branch is behind '%s' " + "by %d commit%s, " + "and can be fast-forwarded.\n", + base, num_theirs, (num_theirs == 1) ? "" : "s"); + else + strbuf_addf(sb, "Your branch and '%s' have diverged,\n" + "and have %d and %d different commit(s) each, " + "respectively.\n", + base, num_ours, num_theirs); + return 1; +}