builtin-push.con commit builtin-push: resurrect parsing of Push: lines (5c477b9)
   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] [--force] <repository> [<refspec>...]";
  12
  13static int all = 0, tags = 0, force = 0, thin = 1;
  14static const char *execute = NULL;
  15
  16#define BUF_SIZE (2084)
  17static char buffer[BUF_SIZE];
  18
  19static const char **refspec = NULL;
  20static int refspec_nr = 0;
  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)
  31{
  32        /* Ignore the "refs/" at the beginning of the refname */
  33        ref += 5;
  34
  35        if (strncmp(ref, "tags/", 5))
  36                return 0;
  37
  38        add_refspec(strdup(ref));
  39        return 0;
  40}
  41
  42static void expand_refspecs(void)
  43{
  44        if (all) {
  45                if (refspec_nr)
  46                        die("cannot mix '--all' and a refspec");
  47
  48                /*
  49                 * No need to expand "--all" - we'll just use
  50                 * the "--all" flag to send-pack
  51                 */
  52                return;
  53        }
  54        if (!tags)
  55                return;
  56        for_each_ref(expand_one_ref);
  57}
  58
  59static void set_refspecs(const char **refs, int nr)
  60{
  61        if (nr) {
  62                size_t bytes = nr * sizeof(char *);
  63
  64                refspec = xrealloc(refspec, bytes);
  65                memcpy(refspec, refs, bytes);
  66                refspec_nr = nr;
  67        }
  68        expand_refspecs();
  69}
  70
  71#define MAX_REFSPECS 10
  72static int current_refspec = 0;
  73static char *refspecs_[MAX_REFSPECS];
  74
  75static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
  76{
  77        int n = 0;
  78        FILE *f = fopen(git_path("remotes/%s", repo), "r");
  79
  80        if (!f)
  81                return -1;
  82        while (fgets(buffer, BUF_SIZE, f)) {
  83                int is_refspec;
  84                char *s, *p;
  85
  86                if (!strncmp("URL: ", buffer, 5)) {
  87                        is_refspec = 0;
  88                        s = buffer + 5;
  89                } else if (!strncmp("Push: ", buffer, 6)) {
  90                        is_refspec = 1;
  91                        s = buffer + 6;
  92                } else
  93                        continue;
  94
  95                /* Remove whitespace at the head.. */
  96                while (isspace(*s))
  97                        s++;
  98                if (!*s)
  99                        continue;
 100
 101                /* ..and at the end */
 102                p = s + strlen(s);
 103                while (isspace(p[-1]))
 104                        *--p = 0;
 105
 106                if (!is_refspec && n < MAX_URI)
 107                        uri[n++] = strdup(s);
 108                else if (is_refspec && current_refspec < MAX_REFSPECS)
 109                        refspecs_[current_refspec++] = strdup(s);
 110        }
 111        fclose(f);
 112        if (!n)
 113                die("remote '%s' has no URL", repo);
 114        return n;
 115}
 116
 117static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
 118{
 119        const char *slash = strchr(repo, '/');
 120        int n = slash ? slash - repo : 1000;
 121        FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
 122        char *s, *p;
 123        int len;
 124
 125        if (!f)
 126                return 0;
 127        s = fgets(buffer, BUF_SIZE, f);
 128        fclose(f);
 129        if (!s)
 130                return 0;
 131        while (isspace(*s))
 132                s++;
 133        if (!*s)
 134                return 0;
 135        p = s + strlen(s);
 136        while (isspace(p[-1]))
 137                *--p = 0;
 138        len = p - s;
 139        if (slash)
 140                len += strlen(slash);
 141        p = xmalloc(len + 1);
 142        strcpy(p, s);
 143        if (slash)
 144                strcat(p, slash);
 145        uri[0] = p;
 146        return 1;
 147}
 148
 149static int get_uri(const char *repo, const char *uri[MAX_URI])
 150{
 151        int n;
 152
 153        if (*repo != '/') {
 154                current_refspec = 0;
 155
 156                n = get_remotes_uri(repo, uri);
 157                if (n > 0)
 158                        return n;
 159
 160                n = get_branches_uri(repo, uri);
 161                if (n > 0)
 162                        return n;
 163        }
 164
 165        uri[0] = repo;
 166        return 1;
 167}
 168
 169static int do_push(const char *repo)
 170{
 171        const char *uri[MAX_URI];
 172        int i, n = get_uri(repo, uri);
 173        int remote;
 174        const char **argv;
 175        int argc;
 176
 177        n = get_uri(repo, uri);
 178        if (n <= 0)
 179                die("bad repository '%s'", repo);
 180
 181        if (refspec_nr == 0)
 182                set_refspecs((const char**)refspecs_, current_refspec);
 183
 184        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 185        argv[0] = "dummy-send-pack";
 186        argc = 1;
 187        if (all)
 188                argv[argc++] = "--all";
 189        if (force)
 190                argv[argc++] = "--force";
 191        if (execute)
 192                argv[argc++] = execute;
 193        if (thin)
 194                argv[argc++] = "--thin";
 195        remote = argc;
 196        argv[argc++] = "dummy-remote";
 197        while (refspec_nr--)
 198                argv[argc++] = *refspec++;
 199        argv[argc] = NULL;
 200
 201        for (i = 0; i < n; i++) {
 202                int error;
 203                const char *dest = uri[i];
 204                const char *sender = "git-send-pack";
 205                if (!strncmp(dest, "http://", 7) ||
 206                    !strncmp(dest, "https://", 8))
 207                        sender = "git-http-push";
 208                argv[0] = sender;
 209                argv[remote] = dest;
 210                error = run_command_v(argc, argv);
 211                if (!error)
 212                        continue;
 213                switch (error) {
 214                case -ERR_RUN_COMMAND_FORK:
 215                        die("unable to fork for %s", sender);
 216                case -ERR_RUN_COMMAND_EXEC:
 217                        die("unable to exec %s", sender);
 218                case -ERR_RUN_COMMAND_WAITPID:
 219                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 220                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 221                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 222                        die("%s died with strange error", sender);
 223                default:
 224                        return -error;
 225                }
 226        }
 227        return 0;
 228}
 229
 230int cmd_push(int argc, const char **argv, char **envp)
 231{
 232        int i;
 233        const char *repo = "origin";    // default repository
 234
 235        for (i = 1; i < argc; i++) {
 236                const char *arg = argv[i];
 237
 238                if (arg[0] != '-') {
 239                        repo = arg;
 240                        i++;
 241                        break;
 242                }
 243                if (!strcmp(arg, "--all")) {
 244                        all = 1;
 245                        continue;
 246                }
 247                if (!strcmp(arg, "--tags")) {
 248                        tags = 1;
 249                        continue;
 250                }
 251                if (!strcmp(arg, "--force")) {
 252                        force = 1;
 253                        continue;
 254                }
 255                if (!strcmp(arg, "--thin")) {
 256                        thin = 1;
 257                        continue;
 258                }
 259                if (!strcmp(arg, "--no-thin")) {
 260                        thin = 0;
 261                        continue;
 262                }
 263                if (!strncmp(arg, "--exec=", 7)) {
 264                        execute = arg;
 265                        continue;
 266                }
 267                usage(push_usage);
 268        }
 269        set_refspecs(argv + i, argc - i);
 270        return do_push(repo);
 271}