Still updating 1.5.0 release notes.
[gitweb.git] / builtin-push.c
index ba7981f5bf5c165aefe5c90d5fcb9baa76baebdd..c45649e26cf5284323aa80a5fe73b5842346eb14 100644 (file)
@@ -8,10 +8,10 @@
 
 #define MAX_URI (16)
 
-static const char push_usage[] = "git-push [--all] [--tags] [--exec=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
+static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
 
 static int all, tags, force, thin = 1, verbose;
-static const char *execute;
+static const char *receivepack;
 
 #define BUF_SIZE (2084)
 static char buffer[BUF_SIZE];
@@ -54,38 +54,84 @@ static void expand_refspecs(void)
        for_each_ref(expand_one_ref, NULL);
 }
 
+struct wildcard_cb {
+       const char *from_prefix;
+       int from_prefix_len;
+       const char *to_prefix;
+       int to_prefix_len;
+       int force;
+};
+
+static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct wildcard_cb *cb = cb_data;
+       int len = strlen(ref);
+       char *expanded, *newref;
+
+       if (len < cb->from_prefix_len ||
+           memcmp(cb->from_prefix, ref, cb->from_prefix_len))
+               return 0;
+       expanded = xmalloc(len * 2 + cb->force +
+                          (cb->to_prefix_len - cb->from_prefix_len) + 2);
+       newref = expanded + cb->force;
+       if (cb->force)
+               expanded[0] = '+';
+       memcpy(newref, ref, len);
+       newref[len] = ':';
+       memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
+       strcpy(newref + len + 1 + cb->to_prefix_len,
+              ref + cb->from_prefix_len);
+       add_refspec(expanded);
+       return 0;
+}
+
+static int wildcard_ref(const char *ref)
+{
+       int len;
+       const char *colon;
+       struct wildcard_cb cb;
+
+       memset(&cb, 0, sizeof(cb));
+       if (ref[0] == '+') {
+               cb.force = 1;
+               ref++;
+       }
+       len = strlen(ref);
+       colon = strchr(ref, ':');
+       if (! (colon && ref < colon &&
+              colon[-2] == '/' && colon[-1] == '*' &&
+              /* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
+              7 <= len &&
+              ref[len-2] == '/' && ref[len-1] == '*') )
+               return 0 ;
+       cb.from_prefix = ref;
+       cb.from_prefix_len = colon - ref - 1;
+       cb.to_prefix = colon + 1;
+       cb.to_prefix_len = len - (colon - ref) - 2;
+       for_each_ref(expand_wildcard_ref, &cb);
+       return 1;
+}
+
 static void set_refspecs(const char **refs, int nr)
 {
        if (nr) {
-               int pass;
-               for (pass = 0; pass < 2; pass++) {
-                       /* pass 0 counts and allocates, pass 1 fills */
-                       int i, cnt;
-                       for (i = cnt = 0; i < nr; i++) {
-                               if (!strcmp("tag", refs[i])) {
-                                       int len;
-                                       char *tag;
-                                       if (nr <= ++i)
-                                               die("tag <tag> shorthand without <tag>");
-                                       if (pass) {
-                                               len = strlen(refs[i]) + 11;
-                                               tag = xmalloc(len);
-                                               strcpy(tag, "refs/tags/");
-                                               strcat(tag, refs[i]);
-                                               refspec[cnt] = tag;
-                                       }
-                                       cnt++;
-                                       continue;
-                               }
-                               if (pass)
-                                       refspec[cnt] = refs[i];
-                               cnt++;
-                       }
-                       if (!pass) {
-                               size_t bytes = cnt * sizeof(char *);
-                               refspec_nr = cnt;
-                               refspec = xrealloc(refspec, bytes);
+               int i;
+               for (i = 0; i < nr; i++) {
+                       const char *ref = refs[i];
+                       if (!strcmp("tag", ref)) {
+                               char *tag;
+                               int len;
+                               if (nr <= ++i)
+                                       die("tag shorthand without <tag>");
+                               len = strlen(refs[i]) + 11;
+                               tag = xmalloc(len);
+                               strcpy(tag, "refs/tags/");
+                               strcat(tag, refs[i]);
+                               ref = tag;
                        }
+                       else if (wildcard_ref(ref))
+                               continue;
+                       add_refspec(ref);
                }
        }
        expand_refspecs();
@@ -129,8 +175,10 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
                        else
                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
                }
-               else if (is_refspec && !has_explicit_refspec)
-                       add_refspec(xstrdup(s));
+               else if (is_refspec && !has_explicit_refspec) {
+                       if (!wildcard_ref(s))
+                               add_refspec(xstrdup(s));
+               }
        }
        fclose(f);
        if (!n)
@@ -143,6 +191,7 @@ static const char *config_repo;
 static int config_repo_len;
 static int config_current_uri;
 static int config_get_refspecs;
+static int config_get_receivepack;
 
 static int get_remote_config(const char* key, const char* value)
 {
@@ -155,8 +204,19 @@ static int get_remote_config(const char* key, const char* value)
                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
                }
                else if (config_get_refspecs &&
-                        !strcmp(key + 7 + config_repo_len, ".push"))
-                       add_refspec(xstrdup(value));
+                        !strcmp(key + 7 + config_repo_len, ".push")) {
+                       if (!wildcard_ref(value))
+                               add_refspec(xstrdup(value));
+               }
+               else if (config_get_receivepack &&
+                        !strcmp(key + 7 + config_repo_len, ".receivepack")) {
+                       if (!receivepack) {
+                               char *rp = xmalloc(strlen(value) + 16);
+                               sprintf(rp, "--receive-pack=%s", value);
+                               receivepack = rp;
+                       } else
+                               error("more than one receivepack given, using the first");
+               }
        }
        return 0;
 }
@@ -168,6 +228,7 @@ static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
        config_current_uri = 0;
        config_uri = uri;
        config_get_refspecs = !(refspec_nr || all || tags);
+       config_get_receivepack = (receivepack == NULL);
 
        git_config(get_remote_config);
        return config_current_uri;
@@ -252,8 +313,8 @@ static int do_push(const char *repo)
                argv[argc++] = "--all";
        if (force)
                argv[argc++] = "--force";
-       if (execute)
-               argv[argc++] = execute;
+       if (receivepack)
+               argv[argc++] = receivepack;
        common_argc = argc;
 
        for (i = 0; i < n; i++) {
@@ -336,8 +397,12 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                        thin = 0;
                        continue;
                }
+               if (!strncmp(arg, "--receive-pack=", 15)) {
+                       receivepack = arg;
+                       continue;
+               }
                if (!strncmp(arg, "--exec=", 7)) {
-                       execute = arg;
+                       receivepack = arg;
                        continue;
                }
                usage(push_usage);