builtin-push.con commit git-svn: add support for --stat in the log command (488a63e)
   1/*
   2 * "git push"
   3 */
   4#include "cache.h"
   5#include "refs.h"
   6#include "run-command.h"
   7#include "builtin.h"
   8
   9#define MAX_URI (16)
  10
  11static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
  12
  13static int all, tags, force, thin = 1, verbose;
  14static const char *receivepack;
  15
  16#define BUF_SIZE (2084)
  17static char buffer[BUF_SIZE];
  18
  19static const char **refspec;
  20static int refspec_nr;
  21
  22static void add_refspec(const char *ref)
  23{
  24        int nr = refspec_nr + 1;
  25        refspec = xrealloc(refspec, nr * sizeof(char *));
  26        refspec[nr-1] = ref;
  27        refspec_nr = nr;
  28}
  29
  30static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
  31{
  32        /* Ignore the "refs/" at the beginning of the refname */
  33        ref += 5;
  34
  35        if (!prefixcmp(ref, "tags/"))
  36                add_refspec(xstrdup(ref));
  37        return 0;
  38}
  39
  40static void expand_refspecs(void)
  41{
  42        if (all) {
  43                if (refspec_nr)
  44                        die("cannot mix '--all' and a refspec");
  45
  46                /*
  47                 * No need to expand "--all" - we'll just use
  48                 * the "--all" flag to send-pack
  49                 */
  50                return;
  51        }
  52        if (!tags)
  53                return;
  54        for_each_ref(expand_one_ref, NULL);
  55}
  56
  57struct wildcard_cb {
  58        const char *from_prefix;
  59        int from_prefix_len;
  60        const char *to_prefix;
  61        int to_prefix_len;
  62        int force;
  63};
  64
  65static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
  66{
  67        struct wildcard_cb *cb = cb_data;
  68        int len = strlen(ref);
  69        char *expanded, *newref;
  70
  71        if (len < cb->from_prefix_len ||
  72            memcmp(cb->from_prefix, ref, cb->from_prefix_len))
  73                return 0;
  74        expanded = xmalloc(len * 2 + cb->force +
  75                           (cb->to_prefix_len - cb->from_prefix_len) + 2);
  76        newref = expanded + cb->force;
  77        if (cb->force)
  78                expanded[0] = '+';
  79        memcpy(newref, ref, len);
  80        newref[len] = ':';
  81        memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
  82        strcpy(newref + len + 1 + cb->to_prefix_len,
  83               ref + cb->from_prefix_len);
  84        add_refspec(expanded);
  85        return 0;
  86}
  87
  88static int wildcard_ref(const char *ref)
  89{
  90        int len;
  91        const char *colon;
  92        struct wildcard_cb cb;
  93
  94        memset(&cb, 0, sizeof(cb));
  95        if (ref[0] == '+') {
  96                cb.force = 1;
  97                ref++;
  98        }
  99        len = strlen(ref);
 100        colon = strchr(ref, ':');
 101        if (! (colon && ref < colon &&
 102               colon[-2] == '/' && colon[-1] == '*' &&
 103               /* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
 104               7 <= len &&
 105               ref[len-2] == '/' && ref[len-1] == '*') )
 106                return 0 ;
 107        cb.from_prefix = ref;
 108        cb.from_prefix_len = colon - ref - 1;
 109        cb.to_prefix = colon + 1;
 110        cb.to_prefix_len = len - (colon - ref) - 2;
 111        for_each_ref(expand_wildcard_ref, &cb);
 112        return 1;
 113}
 114
 115static void set_refspecs(const char **refs, int nr)
 116{
 117        if (nr) {
 118                int i;
 119                for (i = 0; i < nr; i++) {
 120                        const char *ref = refs[i];
 121                        if (!strcmp("tag", ref)) {
 122                                char *tag;
 123                                int len;
 124                                if (nr <= ++i)
 125                                        die("tag shorthand without <tag>");
 126                                len = strlen(refs[i]) + 11;
 127                                tag = xmalloc(len);
 128                                strcpy(tag, "refs/tags/");
 129                                strcat(tag, refs[i]);
 130                                ref = tag;
 131                        }
 132                        else if (wildcard_ref(ref))
 133                                continue;
 134                        add_refspec(ref);
 135                }
 136        }
 137        expand_refspecs();
 138}
 139
 140static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
 141{
 142        int n = 0;
 143        FILE *f = fopen(git_path("remotes/%s", repo), "r");
 144        int has_explicit_refspec = refspec_nr || all || tags;
 145
 146        if (!f)
 147                return -1;
 148        while (fgets(buffer, BUF_SIZE, f)) {
 149                int is_refspec;
 150                char *s, *p;
 151
 152                if (!prefixcmp(buffer, "URL:")) {
 153                        is_refspec = 0;
 154                        s = buffer + 4;
 155                } else if (!prefixcmp(buffer, "Push:")) {
 156                        is_refspec = 1;
 157                        s = buffer + 5;
 158                } else
 159                        continue;
 160
 161                /* Remove whitespace at the head.. */
 162                while (isspace(*s))
 163                        s++;
 164                if (!*s)
 165                        continue;
 166
 167                /* ..and at the end */
 168                p = s + strlen(s);
 169                while (isspace(p[-1]))
 170                        *--p = 0;
 171
 172                if (!is_refspec) {
 173                        if (n < MAX_URI)
 174                                uri[n++] = xstrdup(s);
 175                        else
 176                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 177                }
 178                else if (is_refspec && !has_explicit_refspec) {
 179                        if (!wildcard_ref(s))
 180                                add_refspec(xstrdup(s));
 181                }
 182        }
 183        fclose(f);
 184        if (!n)
 185                die("remote '%s' has no URL", repo);
 186        return n;
 187}
 188
 189static const char **config_uri;
 190static const char *config_repo;
 191static int config_repo_len;
 192static int config_current_uri;
 193static int config_get_refspecs;
 194static int config_get_receivepack;
 195
 196static int get_remote_config(const char* key, const char* value)
 197{
 198        if (!prefixcmp(key, "remote.") &&
 199            !strncmp(key + 7, config_repo, config_repo_len)) {
 200                if (!strcmp(key + 7 + config_repo_len, ".url")) {
 201                        if (config_current_uri < MAX_URI)
 202                                config_uri[config_current_uri++] = xstrdup(value);
 203                        else
 204                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 205                }
 206                else if (config_get_refspecs &&
 207                         !strcmp(key + 7 + config_repo_len, ".push")) {
 208                        if (!wildcard_ref(value))
 209                                add_refspec(xstrdup(value));
 210                }
 211                else if (config_get_receivepack &&
 212                         !strcmp(key + 7 + config_repo_len, ".receivepack")) {
 213                        if (!receivepack) {
 214                                char *rp = xmalloc(strlen(value) + 16);
 215                                sprintf(rp, "--receive-pack=%s", value);
 216                                receivepack = rp;
 217                        } else
 218                                error("more than one receivepack given, using the first");
 219                }
 220        }
 221        return 0;
 222}
 223
 224static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
 225{
 226        config_repo_len = strlen(repo);
 227        config_repo = repo;
 228        config_current_uri = 0;
 229        config_uri = uri;
 230        config_get_refspecs = !(refspec_nr || all || tags);
 231        config_get_receivepack = (receivepack == NULL);
 232
 233        git_config(get_remote_config);
 234        return config_current_uri;
 235}
 236
 237static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
 238{
 239        const char *slash = strchr(repo, '/');
 240        int n = slash ? slash - repo : 1000;
 241        FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
 242        char *s, *p;
 243        int len;
 244
 245        if (!f)
 246                return 0;
 247        s = fgets(buffer, BUF_SIZE, f);
 248        fclose(f);
 249        if (!s)
 250                return 0;
 251        while (isspace(*s))
 252                s++;
 253        if (!*s)
 254                return 0;
 255        p = s + strlen(s);
 256        while (isspace(p[-1]))
 257                *--p = 0;
 258        len = p - s;
 259        if (slash)
 260                len += strlen(slash);
 261        p = xmalloc(len + 1);
 262        strcpy(p, s);
 263        if (slash)
 264                strcat(p, slash);
 265        uri[0] = p;
 266        return 1;
 267}
 268
 269/*
 270 * Read remotes and branches file, fill the push target URI
 271 * list.  If there is no command line refspecs, read Push: lines
 272 * to set up the *refspec list as well.
 273 * return the number of push target URIs
 274 */
 275static int read_config(const char *repo, const char *uri[MAX_URI])
 276{
 277        int n;
 278
 279        if (*repo != '/') {
 280                n = get_remotes_uri(repo, uri);
 281                if (n > 0)
 282                        return n;
 283
 284                n = get_config_remotes_uri(repo, uri);
 285                if (n > 0)
 286                        return n;
 287
 288                n = get_branches_uri(repo, uri);
 289                if (n > 0)
 290                        return n;
 291        }
 292
 293        uri[0] = repo;
 294        return 1;
 295}
 296
 297static int do_push(const char *repo)
 298{
 299        const char *uri[MAX_URI];
 300        int i, n;
 301        int common_argc;
 302        const char **argv;
 303        int argc;
 304
 305        n = read_config(repo, uri);
 306        if (n <= 0)
 307                die("bad repository '%s'", repo);
 308
 309        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 310        argv[0] = "dummy-send-pack";
 311        argc = 1;
 312        if (all)
 313                argv[argc++] = "--all";
 314        if (force)
 315                argv[argc++] = "--force";
 316        if (receivepack)
 317                argv[argc++] = receivepack;
 318        common_argc = argc;
 319
 320        for (i = 0; i < n; i++) {
 321                int err;
 322                int dest_argc = common_argc;
 323                int dest_refspec_nr = refspec_nr;
 324                const char **dest_refspec = refspec;
 325                const char *dest = uri[i];
 326                const char *sender = "git-send-pack";
 327                if (!prefixcmp(dest, "http://") ||
 328                    !prefixcmp(dest, "https://"))
 329                        sender = "git-http-push";
 330                else if (thin)
 331                        argv[dest_argc++] = "--thin";
 332                argv[0] = sender;
 333                argv[dest_argc++] = dest;
 334                while (dest_refspec_nr--)
 335                        argv[dest_argc++] = *dest_refspec++;
 336                argv[dest_argc] = NULL;
 337                if (verbose)
 338                        fprintf(stderr, "Pushing to %s\n", dest);
 339                err = run_command_v(argv);
 340                if (!err)
 341                        continue;
 342                switch (err) {
 343                case -ERR_RUN_COMMAND_FORK:
 344                        die("unable to fork for %s", sender);
 345                case -ERR_RUN_COMMAND_EXEC:
 346                        die("unable to exec %s", sender);
 347                case -ERR_RUN_COMMAND_WAITPID:
 348                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 349                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 350                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 351                        die("%s died with strange error", sender);
 352                default:
 353                        return -err;
 354                }
 355        }
 356        return 0;
 357}
 358
 359int cmd_push(int argc, const char **argv, const char *prefix)
 360{
 361        int i;
 362        const char *repo = "origin";    /* default repository */
 363
 364        for (i = 1; i < argc; i++) {
 365                const char *arg = argv[i];
 366
 367                if (arg[0] != '-') {
 368                        repo = arg;
 369                        i++;
 370                        break;
 371                }
 372                if (!strcmp(arg, "-v")) {
 373                        verbose=1;
 374                        continue;
 375                }
 376                if (!prefixcmp(arg, "--repo=")) {
 377                        repo = arg+7;
 378                        continue;
 379                }
 380                if (!strcmp(arg, "--all")) {
 381                        all = 1;
 382                        continue;
 383                }
 384                if (!strcmp(arg, "--tags")) {
 385                        tags = 1;
 386                        continue;
 387                }
 388                if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
 389                        force = 1;
 390                        continue;
 391                }
 392                if (!strcmp(arg, "--thin")) {
 393                        thin = 1;
 394                        continue;
 395                }
 396                if (!strcmp(arg, "--no-thin")) {
 397                        thin = 0;
 398                        continue;
 399                }
 400                if (!prefixcmp(arg, "--receive-pack=")) {
 401                        receivepack = arg;
 402                        continue;
 403                }
 404                if (!prefixcmp(arg, "--exec=")) {
 405                        receivepack = arg;
 406                        continue;
 407                }
 408                usage(push_usage);
 409        }
 410        set_refspecs(argv + i, argc - i);
 411        return do_push(repo);
 412}