builtin-push.con commit "init-db" can really be just "init" (515377e)
   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] [-f | --force] <repository> [<refspec>...]";
  12
  13static int all, tags, force, thin = 1, verbose;
  14static const char *execute;
  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 (!strncmp(ref, "tags/", 5))
  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
  57static void set_refspecs(const char **refs, int nr)
  58{
  59        if (nr) {
  60                int pass;
  61                for (pass = 0; pass < 2; pass++) {
  62                        /* pass 0 counts and allocates, pass 1 fills */
  63                        int i, cnt;
  64                        for (i = cnt = 0; i < nr; i++) {
  65                                if (!strcmp("tag", refs[i])) {
  66                                        int len;
  67                                        char *tag;
  68                                        if (nr <= ++i)
  69                                                die("tag <tag> shorthand without <tag>");
  70                                        if (pass) {
  71                                                len = strlen(refs[i]) + 11;
  72                                                tag = xmalloc(len);
  73                                                strcpy(tag, "refs/tags/");
  74                                                strcat(tag, refs[i]);
  75                                                refspec[cnt] = tag;
  76                                        }
  77                                        cnt++;
  78                                        continue;
  79                                }
  80                                if (pass)
  81                                        refspec[cnt] = refs[i];
  82                                cnt++;
  83                        }
  84                        if (!pass) {
  85                                size_t bytes = cnt * sizeof(char *);
  86                                refspec_nr = cnt;
  87                                refspec = xrealloc(refspec, bytes);
  88                        }
  89                }
  90        }
  91        expand_refspecs();
  92}
  93
  94static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
  95{
  96        int n = 0;
  97        FILE *f = fopen(git_path("remotes/%s", repo), "r");
  98        int has_explicit_refspec = refspec_nr || all || tags;
  99
 100        if (!f)
 101                return -1;
 102        while (fgets(buffer, BUF_SIZE, f)) {
 103                int is_refspec;
 104                char *s, *p;
 105
 106                if (!strncmp("URL:", buffer, 4)) {
 107                        is_refspec = 0;
 108                        s = buffer + 4;
 109                } else if (!strncmp("Push:", buffer, 5)) {
 110                        is_refspec = 1;
 111                        s = buffer + 5;
 112                } else
 113                        continue;
 114
 115                /* Remove whitespace at the head.. */
 116                while (isspace(*s))
 117                        s++;
 118                if (!*s)
 119                        continue;
 120
 121                /* ..and at the end */
 122                p = s + strlen(s);
 123                while (isspace(p[-1]))
 124                        *--p = 0;
 125
 126                if (!is_refspec) {
 127                        if (n < MAX_URI)
 128                                uri[n++] = xstrdup(s);
 129                        else
 130                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 131                }
 132                else if (is_refspec && !has_explicit_refspec)
 133                        add_refspec(xstrdup(s));
 134        }
 135        fclose(f);
 136        if (!n)
 137                die("remote '%s' has no URL", repo);
 138        return n;
 139}
 140
 141static const char **config_uri;
 142static const char *config_repo;
 143static int config_repo_len;
 144static int config_current_uri;
 145static int config_get_refspecs;
 146
 147static int get_remote_config(const char* key, const char* value)
 148{
 149        if (!strncmp(key, "remote.", 7) &&
 150            !strncmp(key + 7, config_repo, config_repo_len)) {
 151                if (!strcmp(key + 7 + config_repo_len, ".url")) {
 152                        if (config_current_uri < MAX_URI)
 153                                config_uri[config_current_uri++] = xstrdup(value);
 154                        else
 155                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 156                }
 157                else if (config_get_refspecs &&
 158                         !strcmp(key + 7 + config_repo_len, ".push"))
 159                        add_refspec(xstrdup(value));
 160        }
 161        return 0;
 162}
 163
 164static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
 165{
 166        config_repo_len = strlen(repo);
 167        config_repo = repo;
 168        config_current_uri = 0;
 169        config_uri = uri;
 170        config_get_refspecs = !(refspec_nr || all || tags);
 171
 172        git_config(get_remote_config);
 173        return config_current_uri;
 174}
 175
 176static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
 177{
 178        const char *slash = strchr(repo, '/');
 179        int n = slash ? slash - repo : 1000;
 180        FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
 181        char *s, *p;
 182        int len;
 183
 184        if (!f)
 185                return 0;
 186        s = fgets(buffer, BUF_SIZE, f);
 187        fclose(f);
 188        if (!s)
 189                return 0;
 190        while (isspace(*s))
 191                s++;
 192        if (!*s)
 193                return 0;
 194        p = s + strlen(s);
 195        while (isspace(p[-1]))
 196                *--p = 0;
 197        len = p - s;
 198        if (slash)
 199                len += strlen(slash);
 200        p = xmalloc(len + 1);
 201        strcpy(p, s);
 202        if (slash)
 203                strcat(p, slash);
 204        uri[0] = p;
 205        return 1;
 206}
 207
 208/*
 209 * Read remotes and branches file, fill the push target URI
 210 * list.  If there is no command line refspecs, read Push: lines
 211 * to set up the *refspec list as well.
 212 * return the number of push target URIs
 213 */
 214static int read_config(const char *repo, const char *uri[MAX_URI])
 215{
 216        int n;
 217
 218        if (*repo != '/') {
 219                n = get_remotes_uri(repo, uri);
 220                if (n > 0)
 221                        return n;
 222
 223                n = get_config_remotes_uri(repo, uri);
 224                if (n > 0)
 225                        return n;
 226
 227                n = get_branches_uri(repo, uri);
 228                if (n > 0)
 229                        return n;
 230        }
 231
 232        uri[0] = repo;
 233        return 1;
 234}
 235
 236static int do_push(const char *repo)
 237{
 238        const char *uri[MAX_URI];
 239        int i, n;
 240        int common_argc;
 241        const char **argv;
 242        int argc;
 243
 244        n = read_config(repo, uri);
 245        if (n <= 0)
 246                die("bad repository '%s'", repo);
 247
 248        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 249        argv[0] = "dummy-send-pack";
 250        argc = 1;
 251        if (all)
 252                argv[argc++] = "--all";
 253        if (force)
 254                argv[argc++] = "--force";
 255        if (execute)
 256                argv[argc++] = execute;
 257        common_argc = argc;
 258
 259        for (i = 0; i < n; i++) {
 260                int err;
 261                int dest_argc = common_argc;
 262                int dest_refspec_nr = refspec_nr;
 263                const char **dest_refspec = refspec;
 264                const char *dest = uri[i];
 265                const char *sender = "git-send-pack";
 266                if (!strncmp(dest, "http://", 7) ||
 267                    !strncmp(dest, "https://", 8))
 268                        sender = "git-http-push";
 269                else if (thin)
 270                        argv[dest_argc++] = "--thin";
 271                argv[0] = sender;
 272                argv[dest_argc++] = dest;
 273                while (dest_refspec_nr--)
 274                        argv[dest_argc++] = *dest_refspec++;
 275                argv[dest_argc] = NULL;
 276                if (verbose)
 277                        fprintf(stderr, "Pushing to %s\n", dest);
 278                err = run_command_v(argv);
 279                if (!err)
 280                        continue;
 281                switch (err) {
 282                case -ERR_RUN_COMMAND_FORK:
 283                        die("unable to fork for %s", sender);
 284                case -ERR_RUN_COMMAND_EXEC:
 285                        die("unable to exec %s", sender);
 286                case -ERR_RUN_COMMAND_WAITPID:
 287                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 288                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 289                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 290                        die("%s died with strange error", sender);
 291                default:
 292                        return -err;
 293                }
 294        }
 295        return 0;
 296}
 297
 298int cmd_push(int argc, const char **argv, const char *prefix)
 299{
 300        int i;
 301        const char *repo = "origin";    /* default repository */
 302
 303        for (i = 1; i < argc; i++) {
 304                const char *arg = argv[i];
 305
 306                if (arg[0] != '-') {
 307                        repo = arg;
 308                        i++;
 309                        break;
 310                }
 311                if (!strcmp(arg, "-v")) {
 312                        verbose=1;
 313                        continue;
 314                }
 315                if (!strncmp(arg, "--repo=", 7)) {
 316                        repo = arg+7;
 317                        continue;
 318                }
 319                if (!strcmp(arg, "--all")) {
 320                        all = 1;
 321                        continue;
 322                }
 323                if (!strcmp(arg, "--tags")) {
 324                        tags = 1;
 325                        continue;
 326                }
 327                if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
 328                        force = 1;
 329                        continue;
 330                }
 331                if (!strcmp(arg, "--thin")) {
 332                        thin = 1;
 333                        continue;
 334                }
 335                if (!strcmp(arg, "--no-thin")) {
 336                        thin = 0;
 337                        continue;
 338                }
 339                if (!strncmp(arg, "--exec=", 7)) {
 340                        execute = arg;
 341                        continue;
 342                }
 343                usage(push_usage);
 344        }
 345        set_refspecs(argv + i, argc - i);
 346        return do_push(repo);
 347}