6084899b2b58c32f85f40d7ae487dbfbac460520
   1/*
   2 * "git push"
   3 */
   4#include "cache.h"
   5#include "refs.h"
   6#include "run-command.h"
   7#include "builtin.h"
   8#include "remote.h"
   9
  10static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
  11
  12static int all, tags, force, thin = 1, verbose;
  13static const char *receivepack;
  14
  15static const char **refspec;
  16static int refspec_nr;
  17
  18static void add_refspec(const char *ref)
  19{
  20        int nr = refspec_nr + 1;
  21        refspec = xrealloc(refspec, nr * sizeof(char *));
  22        refspec[nr-1] = ref;
  23        refspec_nr = nr;
  24}
  25
  26static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
  27{
  28        /* Ignore the "refs/" at the beginning of the refname */
  29        ref += 5;
  30
  31        if (!prefixcmp(ref, "tags/"))
  32                add_refspec(xstrdup(ref));
  33        return 0;
  34}
  35
  36static void expand_refspecs(void)
  37{
  38        if (all) {
  39                if (refspec_nr)
  40                        die("cannot mix '--all' and a refspec");
  41
  42                /*
  43                 * No need to expand "--all" - we'll just use
  44                 * the "--all" flag to send-pack
  45                 */
  46                return;
  47        }
  48        if (!tags)
  49                return;
  50        for_each_ref(expand_one_ref, NULL);
  51}
  52
  53struct wildcard_cb {
  54        const char *from_prefix;
  55        int from_prefix_len;
  56        const char *to_prefix;
  57        int to_prefix_len;
  58        int force;
  59};
  60
  61static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
  62{
  63        struct wildcard_cb *cb = cb_data;
  64        int len = strlen(ref);
  65        char *expanded, *newref;
  66
  67        if (len < cb->from_prefix_len ||
  68            memcmp(cb->from_prefix, ref, cb->from_prefix_len))
  69                return 0;
  70        expanded = xmalloc(len * 2 + cb->force +
  71                           (cb->to_prefix_len - cb->from_prefix_len) + 2);
  72        newref = expanded + cb->force;
  73        if (cb->force)
  74                expanded[0] = '+';
  75        memcpy(newref, ref, len);
  76        newref[len] = ':';
  77        memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
  78        strcpy(newref + len + 1 + cb->to_prefix_len,
  79               ref + cb->from_prefix_len);
  80        add_refspec(expanded);
  81        return 0;
  82}
  83
  84static int wildcard_ref(const char *ref)
  85{
  86        int len;
  87        const char *colon;
  88        struct wildcard_cb cb;
  89
  90        memset(&cb, 0, sizeof(cb));
  91        if (ref[0] == '+') {
  92                cb.force = 1;
  93                ref++;
  94        }
  95        len = strlen(ref);
  96        colon = strchr(ref, ':');
  97        if (! (colon && ref < colon &&
  98               colon[-2] == '/' && colon[-1] == '*' &&
  99               /* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
 100               7 <= len &&
 101               ref[len-2] == '/' && ref[len-1] == '*') )
 102                return 0 ;
 103        cb.from_prefix = ref;
 104        cb.from_prefix_len = colon - ref - 1;
 105        cb.to_prefix = colon + 1;
 106        cb.to_prefix_len = len - (colon - ref) - 2;
 107        for_each_ref(expand_wildcard_ref, &cb);
 108        return 1;
 109}
 110
 111static void set_refspecs(const char **refs, int nr)
 112{
 113        if (nr) {
 114                int i;
 115                for (i = 0; i < nr; i++) {
 116                        const char *ref = refs[i];
 117                        if (!strcmp("tag", ref)) {
 118                                char *tag;
 119                                int len;
 120                                if (nr <= ++i)
 121                                        die("tag shorthand without <tag>");
 122                                len = strlen(refs[i]) + 11;
 123                                tag = xmalloc(len);
 124                                strcpy(tag, "refs/tags/");
 125                                strcat(tag, refs[i]);
 126                                ref = tag;
 127                        }
 128                        else if (wildcard_ref(ref))
 129                                continue;
 130                        add_refspec(ref);
 131                }
 132        }
 133        expand_refspecs();
 134}
 135
 136static int do_push(const char *repo)
 137{
 138        int i, errs;
 139        int common_argc;
 140        const char **argv;
 141        int argc;
 142        struct remote *remote = remote_get(repo);
 143
 144        if (!remote)
 145                die("bad repository '%s'", repo);
 146
 147        if (remote->receivepack) {
 148                char *rp = xmalloc(strlen(remote->receivepack) + 16);
 149                sprintf(rp, "--receive-pack=%s", remote->receivepack);
 150                receivepack = rp;
 151        }
 152        if (!refspec && !all && !tags && remote->push_refspec_nr) {
 153                for (i = 0; i < remote->push_refspec_nr; i++) {
 154                        if (!wildcard_ref(remote->push_refspec[i]))
 155                                add_refspec(remote->push_refspec[i]);
 156                }
 157        }
 158
 159        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 160        argv[0] = "dummy-send-pack";
 161        argc = 1;
 162        if (all)
 163                argv[argc++] = "--all";
 164        if (force)
 165                argv[argc++] = "--force";
 166        if (receivepack)
 167                argv[argc++] = receivepack;
 168        common_argc = argc;
 169
 170        errs = 0;
 171        for (i = 0; i < remote->uri_nr; i++) {
 172                int err;
 173                int dest_argc = common_argc;
 174                int dest_refspec_nr = refspec_nr;
 175                const char **dest_refspec = refspec;
 176                const char *dest = remote->uri[i];
 177                const char *sender = "send-pack";
 178                if (!prefixcmp(dest, "http://") ||
 179                    !prefixcmp(dest, "https://"))
 180                        sender = "http-push";
 181                else {
 182                        char *rem = xmalloc(strlen(remote->name) + 10);
 183                        sprintf(rem, "--remote=%s", remote->name);
 184                        argv[dest_argc++] = rem;
 185                        if (thin)
 186                                argv[dest_argc++] = "--thin";
 187                }
 188                argv[0] = sender;
 189                argv[dest_argc++] = dest;
 190                while (dest_refspec_nr--)
 191                        argv[dest_argc++] = *dest_refspec++;
 192                argv[dest_argc] = NULL;
 193                if (verbose)
 194                        fprintf(stderr, "Pushing to %s\n", dest);
 195                err = run_command_v_opt(argv, RUN_GIT_CMD);
 196                if (!err)
 197                        continue;
 198
 199                error("failed to push to '%s'", remote->uri[i]);
 200                switch (err) {
 201                case -ERR_RUN_COMMAND_FORK:
 202                        error("unable to fork for %s", sender);
 203                case -ERR_RUN_COMMAND_EXEC:
 204                        error("unable to exec %s", sender);
 205                        break;
 206                case -ERR_RUN_COMMAND_WAITPID:
 207                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 208                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 209                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 210                        error("%s died with strange error", sender);
 211                }
 212                errs++;
 213        }
 214        return !!errs;
 215}
 216
 217int cmd_push(int argc, const char **argv, const char *prefix)
 218{
 219        int i;
 220        const char *repo = NULL;        /* default repository */
 221
 222        for (i = 1; i < argc; i++) {
 223                const char *arg = argv[i];
 224
 225                if (arg[0] != '-') {
 226                        repo = arg;
 227                        i++;
 228                        break;
 229                }
 230                if (!strcmp(arg, "-v")) {
 231                        verbose=1;
 232                        continue;
 233                }
 234                if (!prefixcmp(arg, "--repo=")) {
 235                        repo = arg+7;
 236                        continue;
 237                }
 238                if (!strcmp(arg, "--all")) {
 239                        all = 1;
 240                        continue;
 241                }
 242                if (!strcmp(arg, "--tags")) {
 243                        tags = 1;
 244                        continue;
 245                }
 246                if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
 247                        force = 1;
 248                        continue;
 249                }
 250                if (!strcmp(arg, "--thin")) {
 251                        thin = 1;
 252                        continue;
 253                }
 254                if (!strcmp(arg, "--no-thin")) {
 255                        thin = 0;
 256                        continue;
 257                }
 258                if (!prefixcmp(arg, "--receive-pack=")) {
 259                        receivepack = arg;
 260                        continue;
 261                }
 262                if (!prefixcmp(arg, "--exec=")) {
 263                        receivepack = arg;
 264                        continue;
 265                }
 266                usage(push_usage);
 267        }
 268        set_refspecs(argv + i, argc - i);
 269        return do_push(repo);
 270}