builtin-push.con commit Documentation/config.txt: Correct info about subsection name (191453f)
   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 (!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;
 146static int config_get_receivepack;
 147
 148static int get_remote_config(const char* key, const char* value)
 149{
 150        if (!strncmp(key, "remote.", 7) &&
 151            !strncmp(key + 7, config_repo, config_repo_len)) {
 152                if (!strcmp(key + 7 + config_repo_len, ".url")) {
 153                        if (config_current_uri < MAX_URI)
 154                                config_uri[config_current_uri++] = xstrdup(value);
 155                        else
 156                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 157                }
 158                else if (config_get_refspecs &&
 159                         !strcmp(key + 7 + config_repo_len, ".push"))
 160                        add_refspec(xstrdup(value));
 161                else if (config_get_receivepack &&
 162                         !strcmp(key + 7 + config_repo_len, ".receivepack")) {
 163                        if (!receivepack) {
 164                                char *rp = xmalloc(strlen(value) + 16);
 165                                sprintf(rp, "--receive-pack=%s", value);
 166                                receivepack = rp;
 167                        } else
 168                                error("more than one receivepack given, using the first");
 169                }
 170        }
 171        return 0;
 172}
 173
 174static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
 175{
 176        config_repo_len = strlen(repo);
 177        config_repo = repo;
 178        config_current_uri = 0;
 179        config_uri = uri;
 180        config_get_refspecs = !(refspec_nr || all || tags);
 181        config_get_receivepack = (receivepack == NULL);
 182
 183        git_config(get_remote_config);
 184        return config_current_uri;
 185}
 186
 187static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
 188{
 189        const char *slash = strchr(repo, '/');
 190        int n = slash ? slash - repo : 1000;
 191        FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
 192        char *s, *p;
 193        int len;
 194
 195        if (!f)
 196                return 0;
 197        s = fgets(buffer, BUF_SIZE, f);
 198        fclose(f);
 199        if (!s)
 200                return 0;
 201        while (isspace(*s))
 202                s++;
 203        if (!*s)
 204                return 0;
 205        p = s + strlen(s);
 206        while (isspace(p[-1]))
 207                *--p = 0;
 208        len = p - s;
 209        if (slash)
 210                len += strlen(slash);
 211        p = xmalloc(len + 1);
 212        strcpy(p, s);
 213        if (slash)
 214                strcat(p, slash);
 215        uri[0] = p;
 216        return 1;
 217}
 218
 219/*
 220 * Read remotes and branches file, fill the push target URI
 221 * list.  If there is no command line refspecs, read Push: lines
 222 * to set up the *refspec list as well.
 223 * return the number of push target URIs
 224 */
 225static int read_config(const char *repo, const char *uri[MAX_URI])
 226{
 227        int n;
 228
 229        if (*repo != '/') {
 230                n = get_remotes_uri(repo, uri);
 231                if (n > 0)
 232                        return n;
 233
 234                n = get_config_remotes_uri(repo, uri);
 235                if (n > 0)
 236                        return n;
 237
 238                n = get_branches_uri(repo, uri);
 239                if (n > 0)
 240                        return n;
 241        }
 242
 243        uri[0] = repo;
 244        return 1;
 245}
 246
 247static int do_push(const char *repo)
 248{
 249        const char *uri[MAX_URI];
 250        int i, n;
 251        int common_argc;
 252        const char **argv;
 253        int argc;
 254
 255        n = read_config(repo, uri);
 256        if (n <= 0)
 257                die("bad repository '%s'", repo);
 258
 259        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 260        argv[0] = "dummy-send-pack";
 261        argc = 1;
 262        if (all)
 263                argv[argc++] = "--all";
 264        if (force)
 265                argv[argc++] = "--force";
 266        if (receivepack)
 267                argv[argc++] = receivepack;
 268        common_argc = argc;
 269
 270        for (i = 0; i < n; i++) {
 271                int err;
 272                int dest_argc = common_argc;
 273                int dest_refspec_nr = refspec_nr;
 274                const char **dest_refspec = refspec;
 275                const char *dest = uri[i];
 276                const char *sender = "git-send-pack";
 277                if (!strncmp(dest, "http://", 7) ||
 278                    !strncmp(dest, "https://", 8))
 279                        sender = "git-http-push";
 280                else if (thin)
 281                        argv[dest_argc++] = "--thin";
 282                argv[0] = sender;
 283                argv[dest_argc++] = dest;
 284                while (dest_refspec_nr--)
 285                        argv[dest_argc++] = *dest_refspec++;
 286                argv[dest_argc] = NULL;
 287                if (verbose)
 288                        fprintf(stderr, "Pushing to %s\n", dest);
 289                err = run_command_v(argv);
 290                if (!err)
 291                        continue;
 292                switch (err) {
 293                case -ERR_RUN_COMMAND_FORK:
 294                        die("unable to fork for %s", sender);
 295                case -ERR_RUN_COMMAND_EXEC:
 296                        die("unable to exec %s", sender);
 297                case -ERR_RUN_COMMAND_WAITPID:
 298                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 299                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 300                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 301                        die("%s died with strange error", sender);
 302                default:
 303                        return -err;
 304                }
 305        }
 306        return 0;
 307}
 308
 309int cmd_push(int argc, const char **argv, const char *prefix)
 310{
 311        int i;
 312        const char *repo = "origin";    /* default repository */
 313
 314        for (i = 1; i < argc; i++) {
 315                const char *arg = argv[i];
 316
 317                if (arg[0] != '-') {
 318                        repo = arg;
 319                        i++;
 320                        break;
 321                }
 322                if (!strcmp(arg, "-v")) {
 323                        verbose=1;
 324                        continue;
 325                }
 326                if (!strncmp(arg, "--repo=", 7)) {
 327                        repo = arg+7;
 328                        continue;
 329                }
 330                if (!strcmp(arg, "--all")) {
 331                        all = 1;
 332                        continue;
 333                }
 334                if (!strcmp(arg, "--tags")) {
 335                        tags = 1;
 336                        continue;
 337                }
 338                if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
 339                        force = 1;
 340                        continue;
 341                }
 342                if (!strcmp(arg, "--thin")) {
 343                        thin = 1;
 344                        continue;
 345                }
 346                if (!strcmp(arg, "--no-thin")) {
 347                        thin = 0;
 348                        continue;
 349                }
 350                if (!strncmp(arg, "--receive-pack=", 15)) {
 351                        receivepack = arg;
 352                        continue;
 353                }
 354                if (!strncmp(arg, "--exec=", 7)) {
 355                        receivepack = arg;
 356                        continue;
 357                }
 358                usage(push_usage);
 359        }
 360        set_refspecs(argv + i, argc - i);
 361        return do_push(repo);
 362}