refspec: convert valid_fetch_refspec to use parse_refspec
[gitweb.git] / remote.c
index 4e93753e1988afd4a01559951f96142c6dc2e73d..89820c47691f0bfd28b0f94c750f57ec20c005bd 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -2,6 +2,7 @@
 #include "config.h"
 #include "remote.h"
 #include "refs.h"
+#include "refspec.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
 
 enum map_direction { FROM_SRC, FROM_DST };
 
-static struct refspec s_tag_refspec = {
-       0,
-       1,
-       0,
-       0,
-       "refs/tags/*",
-       "refs/tags/*"
-};
-
-const struct refspec *tag_refspec = &s_tag_refspec;
-
 struct counted_string {
        size_t len;
        const char *s;
@@ -103,6 +93,17 @@ static void add_fetch_refspec(struct remote *remote, const char *ref)
        remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
 }
 
+void add_prune_tags_to_fetch_refspec(struct remote *remote)
+{
+       int nr = remote->fetch_refspec_nr;
+       int bufsize = nr  + 1;
+       int size = sizeof(struct refspec_item);
+
+       remote->fetch = xrealloc(remote->fetch, size  * bufsize);
+       memcpy(&remote->fetch[nr], tag_refspec, size);
+       add_fetch_refspec(remote, xstrdup(TAG_REFSPEC));
+}
+
 static void add_url(struct remote *remote, const char *url)
 {
        ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
@@ -173,6 +174,7 @@ static struct remote *make_remote(const char *name, int len)
 
        ret = xcalloc(1, sizeof(struct remote));
        ret->prune = -1;  /* unspecified */
+       ret->prune_tags = -1;  /* unspecified */
        ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
        remotes[remotes_nr++] = ret;
        ret->name = xstrndup(name, len);
@@ -391,6 +393,8 @@ static int handle_config(const char *key, const char *value, void *cb)
                remote->skip_default_update = git_config_bool(key, value);
        else if (!strcmp(subkey, "prune"))
                remote->prune = git_config_bool(key, value);
+       else if (!strcmp(subkey, "prunetags"))
+               remote->prune_tags = git_config_bool(key, value);
        else if (!strcmp(subkey, "url")) {
                const char *v;
                if (git_config_string(&v, key, value))
@@ -484,158 +488,6 @@ static void read_config(void)
        alias_all_urls();
 }
 
-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
-{
-       int i;
-       struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));
-
-       for (i = 0; i < nr_refspec; i++) {
-               size_t llen;
-               int is_glob;
-               const char *lhs, *rhs;
-               int flags;
-
-               is_glob = 0;
-
-               lhs = refspec[i];
-               if (*lhs == '+') {
-                       rs[i].force = 1;
-                       lhs++;
-               }
-
-               rhs = strrchr(lhs, ':');
-
-               /*
-                * Before going on, special case ":" (or "+:") as a refspec
-                * for pushing matching refs.
-                */
-               if (!fetch && rhs == lhs && rhs[1] == '\0') {
-                       rs[i].matching = 1;
-                       continue;
-               }
-
-               if (rhs) {
-                       size_t rlen = strlen(++rhs);
-                       is_glob = (1 <= rlen && strchr(rhs, '*'));
-                       rs[i].dst = xstrndup(rhs, rlen);
-               }
-
-               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;
-               }
-
-               rs[i].pattern = is_glob;
-               rs[i].src = xstrndup(lhs, llen);
-               flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
-
-               if (fetch) {
-                       struct object_id unused;
-
-                       /* LHS */
-                       if (!*rs[i].src)
-                               ; /* empty is ok; it means "HEAD" */
-                       else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))
-                               rs[i].exact_sha1 = 1; /* ok */
-                       else if (!check_refname_format(rs[i].src, flags))
-                               ; /* valid looking ref is ok */
-                       else
-                               goto invalid;
-                       /* RHS */
-                       if (!rs[i].dst)
-                               ; /* missing is ok; it is the same as empty */
-                       else if (!*rs[i].dst)
-                               ; /* empty is ok; it means "do not store" */
-                       else if (!check_refname_format(rs[i].dst, flags))
-                               ; /* valid looking ref is ok */
-                       else
-                               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) {
-                               if (check_refname_format(rs[i].src, flags))
-                                       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))
-                                       goto invalid;
-                       } else if (!*rs[i].dst) {
-                               goto invalid;
-                       } else {
-                               if (check_refname_format(rs[i].dst, flags))
-                                       goto invalid;
-                       }
-               }
-       }
-       return rs;
-
- 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_refspec(i+1, rs);
-               return NULL;
-       }
-       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_refspec(1, refspec);
-       return !!refspec;
-}
-
-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
-{
-       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, 0);
-}
-
-void free_refspec(int nr_refspec, struct refspec *refspec)
-{
-       int i;
-
-       if (!refspec)
-               return;
-
-       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))
@@ -872,7 +724,7 @@ static int match_name_with_pattern(const char *key, const char *name,
        return ret;
 }
 
-static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results)
+static void query_refspecs_multiple(struct refspec_item *refs, int ref_count, struct refspec_item *query, struct string_list *results)
 {
        int i;
        int find_src = !query->src;
@@ -881,7 +733,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct
                error("query_refspecs_multiple: need either src or dst");
 
        for (i = 0; i < ref_count; i++) {
-               struct refspec *refspec = &refs[i];
+               struct refspec_item *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;
@@ -898,7 +750,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct
        }
 }
 
-int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
+int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item *query)
 {
        int i;
        int find_src = !query->src;
@@ -909,7 +761,7 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
                return error("query_refspecs: need either src or dst");
 
        for (i = 0; i < ref_count; i++) {
-               struct refspec *refspec = &refs[i];
+               struct refspec_item *refspec = &refs[i];
                const char *key = find_src ? refspec->dst : refspec->src;
                const char *value = find_src ? refspec->src : refspec->dst;
 
@@ -929,12 +781,12 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
        return -1;
 }
 
-char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
+char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,
                     const char *name)
 {
-       struct refspec query;
+       struct refspec_item query;
 
-       memset(&query, 0, sizeof(struct refspec));
+       memset(&query, 0, sizeof(struct refspec_item));
        query.src = (char *)name;
 
        if (query_refspecs(refspecs, nr_refspec, &query))
@@ -943,7 +795,7 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
        return query.dst;
 }
 
-int remote_find_tracking(struct remote *remote, struct refspec *refspec)
+int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)
 {
        return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);
 }
@@ -1152,7 +1004,7 @@ static char *guess_ref(const char *name, struct ref *peer)
 }
 
 static int match_explicit_lhs(struct ref *src,
-                             struct refspec *rs,
+                             struct refspec_item *rs,
                              struct ref **match,
                              int *allocated_match)
 {
@@ -1178,7 +1030,7 @@ static int match_explicit_lhs(struct ref *src,
 
 static int match_explicit(struct ref *src, struct ref *dst,
                          struct ref ***dst_tail,
-                         struct refspec *rs)
+                         struct refspec_item *rs)
 {
        struct ref *matched_src, *matched_dst;
        int allocated_src;
@@ -1247,7 +1099,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
 }
 
 static int match_explicit_refs(struct ref *src, struct ref *dst,
-                              struct ref ***dst_tail, struct refspec *rs,
+                              struct ref ***dst_tail, struct refspec_item *rs,
                               int rs_nr)
 {
        int i, errs;
@@ -1256,10 +1108,10 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
        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)
+static char *get_ref_match(const struct refspec_item *rs, int rs_nr, const struct ref *ref,
+               int send_mirror, int direction, const struct refspec_item **ret_pat)
 {
-       const struct refspec *pat;
+       const struct refspec_item *pat;
        char *name;
        int i;
        int matching_refs = -1;
@@ -1361,7 +1213,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
                        continue; /* not a tag */
                if (string_list_has_string(&dst_tag, ref->name))
                        continue; /* they already have it */
-               if (sha1_object_info(ref->new_oid.hash, NULL) != OBJ_TAG)
+               if (oid_object_info(&ref->new_oid, NULL) != OBJ_TAG)
                        continue; /* be conservative */
                item = string_list_append(&src_tag, ref->name);
                item->util = ref;
@@ -1430,12 +1282,12 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
  */
 int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)
 {
-       struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names);
+       struct refspec_item *refspec = parse_push_refspec(nr_refspec, refspec_names);
        int ret = 0;
        int i;
 
        for (i = 0; i < nr_refspec; i++) {
-               struct refspec *rs = refspec + i;
+               struct refspec_item *rs = refspec + i;
 
                if (rs->pattern || rs->matching)
                        continue;
@@ -1458,7 +1310,7 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)
 int match_push_refs(struct ref *src, struct ref **dst,
                    int nr_refspec, const char **refspec, int flags)
 {
-       struct refspec *rs;
+       struct refspec_item *rs;
        int send_all = flags & MATCH_REFS_ALL;
        int send_mirror = flags & MATCH_REFS_MIRROR;
        int send_prune = flags & MATCH_REFS_PRUNE;
@@ -1478,7 +1330,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
        for (ref = src; ref; ref = ref->next) {
                struct string_list_item *dst_item;
                struct ref *dst_peer;
-               const struct refspec *pat = NULL;
+               const struct refspec_item *pat = NULL;
                char *dst_name;
 
                dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
@@ -1834,7 +1686,7 @@ static int ignore_symref_update(const char *refname)
  * local symbolic ref.
  */
 static struct ref *get_expanded_map(const struct ref *remote_refs,
-                                   const struct refspec *refspec)
+                                   const struct refspec_item *refspec)
 {
        const struct ref *ref;
        struct ref *ret = NULL;
@@ -1899,7 +1751,7 @@ static struct ref *get_local_ref(const char *name)
 }
 
 int get_fetch_map(const struct ref *remote_refs,
-                 const struct refspec *refspec,
+                 const struct refspec_item *refspec,
                  struct ref ***tail,
                  int missing_ok)
 {
@@ -1970,33 +1822,33 @@ static void unmark_and_free(struct commit_list *list, unsigned int mark)
 int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
 {
        struct object *o;
-       struct commit *old, *new;
+       struct commit *old_commit, *new_commit;
        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.
+        * Both new_commit and old_commit must be commit-ish and new_commit is descendant of
+        * old_commit.  Otherwise we require --force.
         */
        o = deref_tag(parse_object(old_oid), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
-       old = (struct commit *) o;
+       old_commit = (struct commit *) o;
 
        o = deref_tag(parse_object(new_oid), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
-       new = (struct commit *) o;
+       new_commit = (struct commit *) o;
 
-       if (parse_commit(new) < 0)
+       if (parse_commit(new_commit) < 0)
                return 0;
 
        used = list = NULL;
-       commit_list_insert(new, &list);
+       commit_list_insert(new_commit, &list);
        while (list) {
-               new = pop_most_recent_commit(&list, TMP_MARK);
-               commit_list_insert(new, &used);
-               if (new == old) {
+               new_commit = pop_most_recent_commit(&list, TMP_MARK);
+               commit_list_insert(new_commit, &used);
+               if (new_commit == old_commit) {
                        found = 1;
                        break;
                }
@@ -2007,16 +1859,23 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
 }
 
 /*
- * Compare a branch with its upstream, and save their differences (number
- * of commits) in *num_ours and *num_theirs. The name of the upstream branch
- * (or NULL if no upstream is defined) is returned via *upstream_name, if it
- * is not itself NULL.
+ * Lookup the upstream branch for the given branch and if present, optionally
+ * compute the commit ahead/behind values for the pair.
+ *
+ * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the
+ * counts in *num_ours and *num_theirs.  If abf is AHEAD_BEHIND_QUICK, skip
+ * the (potentially expensive) a/b computation (*num_ours and *num_theirs are
+ * set to zero).
+ *
+ * The name of the upstream branch (or NULL if no upstream is defined) is
+ * returned via *upstream_name, if it is not itself NULL.
  *
  * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
- * upstream defined, or ref does not exist), 0 otherwise.
+ * upstream defined, or ref does not exist).  Returns 0 if the commits are
+ * identical.  Returns 1 if commits are different.
  */
 int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
-                      const char **upstream_name)
+                      const char **upstream_name, enum ahead_behind_flags abf)
 {
        struct object_id oid;
        struct commit *ours, *theirs;
@@ -2044,11 +1903,15 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
        if (!ours)
                return -1;
 
+       *num_theirs = *num_ours = 0;
+
        /* are we the same? */
-       if (theirs == ours) {
-               *num_theirs = *num_ours = 0;
+       if (theirs == ours)
                return 0;
-       }
+       if (abf == AHEAD_BEHIND_QUICK)
+               return 1;
+       if (abf != AHEAD_BEHIND_FULL)
+               BUG("stat_tracking_info: invalid abf '%d'", abf);
 
        /* Run "rev-list --left-right ours...theirs" internally... */
        argv_array_push(&argv, ""); /* ignored */
@@ -2064,8 +1927,6 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
                die("revision walk setup failed");
 
        /* ... and count the commits on each side. */
-       *num_ours = 0;
-       *num_theirs = 0;
        while (1) {
                struct commit *c = get_revision(&revs);
                if (!c)
@@ -2081,20 +1942,22 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
        clear_commit_marks(theirs, ALL_REV_FLAGS);
 
        argv_array_clear(&argv);
-       return 0;
+       return 1;
 }
 
 /*
  * Return true when there is anything to report, otherwise false.
  */
-int format_tracking_info(struct branch *branch, struct strbuf *sb)
+int format_tracking_info(struct branch *branch, struct strbuf *sb,
+                        enum ahead_behind_flags abf)
 {
-       int ours, theirs;
+       int ours, theirs, sti;
        const char *full_base;
        char *base;
        int upstream_is_gone = 0;
 
-       if (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) {
+       sti = stat_tracking_info(branch, &ours, &theirs, &full_base, abf);
+       if (sti < 0) {
                if (!full_base)
                        return 0;
                upstream_is_gone = 1;
@@ -2108,10 +1971,17 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                if (advice_status_hints)
                        strbuf_addstr(sb,
                                _("  (use \"git branch --unset-upstream\" to fixup)\n"));
-       } else if (!ours && !theirs) {
+       } else if (!sti) {
                strbuf_addf(sb,
                        _("Your branch is up to date with '%s'.\n"),
                        base);
+       } else if (abf == AHEAD_BEHIND_QUICK) {
+               strbuf_addf(sb,
+                           _("Your branch and '%s' refer to different commits.\n"),
+                           base);
+               if (advice_status_hints)
+                       strbuf_addf(sb, _("  (use \"%s\" for details)\n"),
+                                   "git status --ahead-behind");
        } else if (!theirs) {
                strbuf_addf(sb,
                        Q_("Your branch is ahead of '%s' by %d commit.\n",
@@ -2219,7 +2089,7 @@ struct ref *guess_remote_head(const struct ref *head,
 struct stale_heads_info {
        struct string_list *ref_names;
        struct ref **stale_refs_tail;
-       struct refspec *refs;
+       struct refspec_item *refs;
        int ref_count;
 };
 
@@ -2228,9 +2098,9 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
 {
        struct stale_heads_info *info = cb_data;
        struct string_list matches = STRING_LIST_INIT_DUP;
-       struct refspec query;
+       struct refspec_item query;
        int i, stale = 1;
-       memset(&query, 0, sizeof(struct refspec));
+       memset(&query, 0, sizeof(struct refspec_item));
        query.dst = (char *)refname;
 
        query_refspecs_multiple(info->refs, info->ref_count, &query, &matches);
@@ -2261,7 +2131,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
        return 0;
 }
 
-struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)
+struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map)
 {
        struct ref *ref, *stale_refs = NULL;
        struct string_list ref_names = STRING_LIST_INIT_NODUP;