send-email: respect in-reply-to regardless of threading
[gitweb.git] / remote.c
index ff2c8021678fee5ef12060d35c6eb1960e8792c7..91f1b7cd81f5461b13c2553bf3dd3e4b5c76ab14 100644 (file)
--- 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,
@@ -295,6 +298,17 @@ static void read_branches_file(struct remote *remote)
        }
        add_url_alias(remote, p);
        add_fetch_refspec(remote, strbuf_detach(&branch, 0));
+       /*
+        * 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, 0));
        remote->fetch_tags = 1; /* always auto-follow */
 }
 
@@ -339,13 +353,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 +439,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 +468,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 +492,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 +502,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 +519,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 +534,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 +549,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 +563,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 +591,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 +717,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 +995,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 +1252,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;
+}