0e602f3bf211d68a217c5aa1817469e514fa21a5
   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 if (thin)
 182                        argv[dest_argc++] = "--thin";
 183                argv[0] = sender;
 184                argv[dest_argc++] = dest;
 185                while (dest_refspec_nr--)
 186                        argv[dest_argc++] = *dest_refspec++;
 187                argv[dest_argc] = NULL;
 188                if (verbose)
 189                        fprintf(stderr, "Pushing to %s\n", dest);
 190                err = run_command_v_opt(argv, RUN_GIT_CMD);
 191                if (!err)
 192                        continue;
 193
 194                error("failed to push to '%s'", remote->uri[i]);
 195                switch (err) {
 196                case -ERR_RUN_COMMAND_FORK:
 197                        error("unable to fork for %s", sender);
 198                case -ERR_RUN_COMMAND_EXEC:
 199                        error("unable to exec %s", sender);
 200                        break;
 201                case -ERR_RUN_COMMAND_WAITPID:
 202                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 203                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 204                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 205                        error("%s died with strange error", sender);
 206                }
 207                errs++;
 208        }
 209        return !!errs;
 210}
 211
 212int cmd_push(int argc, const char **argv, const char *prefix)
 213{
 214        int i;
 215        const char *repo = NULL;        /* default repository */
 216
 217        for (i = 1; i < argc; i++) {
 218                const char *arg = argv[i];
 219
 220                if (arg[0] != '-') {
 221                        repo = arg;
 222                        i++;
 223                        break;
 224                }
 225                if (!strcmp(arg, "-v")) {
 226                        verbose=1;
 227                        continue;
 228                }
 229                if (!prefixcmp(arg, "--repo=")) {
 230                        repo = arg+7;
 231                        continue;
 232                }
 233                if (!strcmp(arg, "--all")) {
 234                        all = 1;
 235                        continue;
 236                }
 237                if (!strcmp(arg, "--tags")) {
 238                        tags = 1;
 239                        continue;
 240                }
 241                if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
 242                        force = 1;
 243                        continue;
 244                }
 245                if (!strcmp(arg, "--thin")) {
 246                        thin = 1;
 247                        continue;
 248                }
 249                if (!strcmp(arg, "--no-thin")) {
 250                        thin = 0;
 251                        continue;
 252                }
 253                if (!prefixcmp(arg, "--receive-pack=")) {
 254                        receivepack = arg;
 255                        continue;
 256                }
 257                if (!prefixcmp(arg, "--exec=")) {
 258                        receivepack = arg;
 259                        continue;
 260                }
 261                usage(push_usage);
 262        }
 263        set_refspecs(argv + i, argc - i);
 264        return do_push(repo);
 265}