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