builtin-push.con commit builtin-push: make it official. (54eb2d3)
   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
  71static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
  72{
  73        int n = 0;
  74        FILE *f = fopen(git_path("remotes/%s", repo), "r");
  75        int has_explicit_refspec = refspec_nr;
  76
  77        if (!f)
  78                return -1;
  79        while (fgets(buffer, BUF_SIZE, f)) {
  80                int is_refspec;
  81                char *s, *p;
  82
  83                if (!strncmp("URL: ", buffer, 5)) {
  84                        is_refspec = 0;
  85                        s = buffer + 5;
  86                } else if (!strncmp("Push: ", buffer, 6)) {
  87                        is_refspec = 1;
  88                        s = buffer + 6;
  89                } else
  90                        continue;
  91
  92                /* Remove whitespace at the head.. */
  93                while (isspace(*s))
  94                        s++;
  95                if (!*s)
  96                        continue;
  97
  98                /* ..and at the end */
  99                p = s + strlen(s);
 100                while (isspace(p[-1]))
 101                        *--p = 0;
 102
 103                if (!is_refspec) {
 104                        if (n < MAX_URI)
 105                                uri[n++] = strdup(s);
 106                        else
 107                                error("more than %d URL's specified, ignoreing the rest", MAX_URI);
 108                }
 109                else if (is_refspec && !has_explicit_refspec)
 110                        add_refspec(strdup(s));
 111        }
 112        fclose(f);
 113        if (!n)
 114                die("remote '%s' has no URL", repo);
 115        return n;
 116}
 117
 118static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
 119{
 120        const char *slash = strchr(repo, '/');
 121        int n = slash ? slash - repo : 1000;
 122        FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
 123        char *s, *p;
 124        int len;
 125
 126        if (!f)
 127                return 0;
 128        s = fgets(buffer, BUF_SIZE, f);
 129        fclose(f);
 130        if (!s)
 131                return 0;
 132        while (isspace(*s))
 133                s++;
 134        if (!*s)
 135                return 0;
 136        p = s + strlen(s);
 137        while (isspace(p[-1]))
 138                *--p = 0;
 139        len = p - s;
 140        if (slash)
 141                len += strlen(slash);
 142        p = xmalloc(len + 1);
 143        strcpy(p, s);
 144        if (slash)
 145                strcat(p, slash);
 146        uri[0] = p;
 147        return 1;
 148}
 149
 150/*
 151 * Read remotes and branches file, fill the push target URI
 152 * list.  If there is no command line refspecs, read Push: lines
 153 * to set up the *refspec list as well.
 154 * return the number of push target URIs
 155 */
 156static int read_config(const char *repo, const char *uri[MAX_URI])
 157{
 158        int n;
 159
 160        if (*repo != '/') {
 161                n = get_remotes_uri(repo, uri);
 162                if (n > 0)
 163                        return n;
 164
 165                n = get_branches_uri(repo, uri);
 166                if (n > 0)
 167                        return n;
 168        }
 169
 170        uri[0] = repo;
 171        return 1;
 172}
 173
 174static int do_push(const char *repo)
 175{
 176        const char *uri[MAX_URI];
 177        int i, n;
 178        int remote;
 179        const char **argv;
 180        int argc;
 181
 182        n = read_config(repo, uri);
 183        if (n <= 0)
 184                die("bad repository '%s'", repo);
 185
 186        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 187        argv[0] = "dummy-send-pack";
 188        argc = 1;
 189        if (all)
 190                argv[argc++] = "--all";
 191        if (force)
 192                argv[argc++] = "--force";
 193        if (execute)
 194                argv[argc++] = execute;
 195        if (thin)
 196                argv[argc++] = "--thin";
 197        remote = argc;
 198        argv[argc++] = "dummy-remote";
 199        while (refspec_nr--)
 200                argv[argc++] = *refspec++;
 201        argv[argc] = NULL;
 202
 203        for (i = 0; i < n; i++) {
 204                int error;
 205                const char *dest = uri[i];
 206                const char *sender = "git-send-pack";
 207                if (!strncmp(dest, "http://", 7) ||
 208                    !strncmp(dest, "https://", 8))
 209                        sender = "git-http-push";
 210                argv[0] = sender;
 211                argv[remote] = dest;
 212                error = run_command_v(argc, argv);
 213                if (!error)
 214                        continue;
 215                switch (error) {
 216                case -ERR_RUN_COMMAND_FORK:
 217                        die("unable to fork for %s", sender);
 218                case -ERR_RUN_COMMAND_EXEC:
 219                        die("unable to exec %s", sender);
 220                case -ERR_RUN_COMMAND_WAITPID:
 221                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 222                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 223                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 224                        die("%s died with strange error", sender);
 225                default:
 226                        return -error;
 227                }
 228        }
 229        return 0;
 230}
 231
 232int cmd_push(int argc, const char **argv, char **envp)
 233{
 234        int i;
 235        const char *repo = "origin";    // default repository
 236
 237        for (i = 1; i < argc; i++) {
 238                const char *arg = argv[i];
 239
 240                if (arg[0] != '-') {
 241                        repo = arg;
 242                        i++;
 243                        break;
 244                }
 245                if (!strcmp(arg, "--all")) {
 246                        all = 1;
 247                        continue;
 248                }
 249                if (!strcmp(arg, "--tags")) {
 250                        tags = 1;
 251                        continue;
 252                }
 253                if (!strcmp(arg, "--force")) {
 254                        force = 1;
 255                        continue;
 256                }
 257                if (!strcmp(arg, "--thin")) {
 258                        thin = 1;
 259                        continue;
 260                }
 261                if (!strcmp(arg, "--no-thin")) {
 262                        thin = 0;
 263                        continue;
 264                }
 265                if (!strncmp(arg, "--exec=", 7)) {
 266                        execute = arg;
 267                        continue;
 268                }
 269                usage(push_usage);
 270        }
 271        set_refspecs(argv + i, argc - i);
 272        return do_push(repo);
 273}