builtin-push.con commit git-svn: Move all git-svn-related paths into $GIT_DIR/svn (883d0a7)
   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 || all || tags;
  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 const char **config_uri;
 119static const char *config_repo;
 120static int config_repo_len;
 121static int config_current_uri;
 122static int config_get_refspecs;
 123
 124static int get_remote_config(const char* key, const char* value)
 125{
 126        if (!strncmp(key, "remote.", 7) &&
 127            !strncmp(key + 7, config_repo, config_repo_len)) {
 128                if (!strcmp(key + 7 + config_repo_len, ".url")) {
 129                        if (config_current_uri < MAX_URI)
 130                                config_uri[config_current_uri++] = strdup(value);
 131                        else
 132                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 133                }
 134                else if (config_get_refspecs &&
 135                         !strcmp(key + 7 + config_repo_len, ".push"))
 136                        add_refspec(strdup(value));
 137        }
 138        return 0;
 139}
 140
 141static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
 142{
 143        config_repo_len = strlen(repo);
 144        config_repo = repo;
 145        config_current_uri = 0;
 146        config_uri = uri;
 147        config_get_refspecs = !(refspec_nr || all || tags);
 148
 149        git_config(get_remote_config);
 150        return config_current_uri;
 151}
 152
 153static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
 154{
 155        const char *slash = strchr(repo, '/');
 156        int n = slash ? slash - repo : 1000;
 157        FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
 158        char *s, *p;
 159        int len;
 160
 161        if (!f)
 162                return 0;
 163        s = fgets(buffer, BUF_SIZE, f);
 164        fclose(f);
 165        if (!s)
 166                return 0;
 167        while (isspace(*s))
 168                s++;
 169        if (!*s)
 170                return 0;
 171        p = s + strlen(s);
 172        while (isspace(p[-1]))
 173                *--p = 0;
 174        len = p - s;
 175        if (slash)
 176                len += strlen(slash);
 177        p = xmalloc(len + 1);
 178        strcpy(p, s);
 179        if (slash)
 180                strcat(p, slash);
 181        uri[0] = p;
 182        return 1;
 183}
 184
 185/*
 186 * Read remotes and branches file, fill the push target URI
 187 * list.  If there is no command line refspecs, read Push: lines
 188 * to set up the *refspec list as well.
 189 * return the number of push target URIs
 190 */
 191static int read_config(const char *repo, const char *uri[MAX_URI])
 192{
 193        int n;
 194
 195        if (*repo != '/') {
 196                n = get_remotes_uri(repo, uri);
 197                if (n > 0)
 198                        return n;
 199
 200                n = get_config_remotes_uri(repo, uri);
 201                if (n > 0)
 202                        return n;
 203
 204                n = get_branches_uri(repo, uri);
 205                if (n > 0)
 206                        return n;
 207        }
 208
 209        uri[0] = repo;
 210        return 1;
 211}
 212
 213static int do_push(const char *repo)
 214{
 215        const char *uri[MAX_URI];
 216        int i, n;
 217        int common_argc;
 218        const char **argv;
 219        int argc;
 220
 221        n = read_config(repo, uri);
 222        if (n <= 0)
 223                die("bad repository '%s'", repo);
 224
 225        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 226        argv[0] = "dummy-send-pack";
 227        argc = 1;
 228        if (all)
 229                argv[argc++] = "--all";
 230        if (force)
 231                argv[argc++] = "--force";
 232        if (execute)
 233                argv[argc++] = execute;
 234        common_argc = argc;
 235
 236        for (i = 0; i < n; i++) {
 237                int error;
 238                int dest_argc = common_argc;
 239                int dest_refspec_nr = refspec_nr;
 240                const char **dest_refspec = refspec;
 241                const char *dest = uri[i];
 242                const char *sender = "git-send-pack";
 243                if (!strncmp(dest, "http://", 7) ||
 244                    !strncmp(dest, "https://", 8))
 245                        sender = "git-http-push";
 246                else if (thin)
 247                        argv[dest_argc++] = "--thin";
 248                argv[0] = sender;
 249                argv[dest_argc++] = dest;
 250                while (dest_refspec_nr--)
 251                        argv[dest_argc++] = *dest_refspec++;
 252                argv[dest_argc] = NULL;
 253                error = run_command_v(argc, argv);
 254                if (!error)
 255                        continue;
 256                switch (error) {
 257                case -ERR_RUN_COMMAND_FORK:
 258                        die("unable to fork for %s", sender);
 259                case -ERR_RUN_COMMAND_EXEC:
 260                        die("unable to exec %s", sender);
 261                case -ERR_RUN_COMMAND_WAITPID:
 262                case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 263                case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 264                case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 265                        die("%s died with strange error", sender);
 266                default:
 267                        return -error;
 268                }
 269        }
 270        return 0;
 271}
 272
 273int cmd_push(int argc, const char **argv, char **envp)
 274{
 275        int i;
 276        const char *repo = "origin";    // default repository
 277
 278        for (i = 1; i < argc; i++) {
 279                const char *arg = argv[i];
 280
 281                if (arg[0] != '-') {
 282                        repo = arg;
 283                        i++;
 284                        break;
 285                }
 286                if (!strcmp(arg, "--all")) {
 287                        all = 1;
 288                        continue;
 289                }
 290                if (!strcmp(arg, "--tags")) {
 291                        tags = 1;
 292                        continue;
 293                }
 294                if (!strcmp(arg, "--force")) {
 295                        force = 1;
 296                        continue;
 297                }
 298                if (!strcmp(arg, "--thin")) {
 299                        thin = 1;
 300                        continue;
 301                }
 302                if (!strcmp(arg, "--no-thin")) {
 303                        thin = 0;
 304                        continue;
 305                }
 306                if (!strncmp(arg, "--exec=", 7)) {
 307                        execute = arg;
 308                        continue;
 309                }
 310                usage(push_usage);
 311        }
 312        set_refspecs(argv + i, argc - i);
 313        return do_push(repo);
 314}