builtin-push.con commit git-svn: enable delta transfers during fetches when using SVN:: libs (27a1a80)
   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] [-f | --force] <repository> [<refspec>...]";
  12
  13static int all, tags, force, thin = 1, verbose;
  14static const char *execute;
  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                size_t bytes = nr * sizeof(char *);
  61
  62                refspec = xrealloc(refspec, bytes);
  63                memcpy(refspec, refs, bytes);
  64                refspec_nr = nr;
  65        }
  66        expand_refspecs();
  67}
  68
  69static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
  70{
  71        int n = 0;
  72        FILE *f = fopen(git_path("remotes/%s", repo), "r");
  73        int has_explicit_refspec = refspec_nr || all || tags;
  74
  75        if (!f)
  76                return -1;
  77        while (fgets(buffer, BUF_SIZE, f)) {
  78                int is_refspec;
  79                char *s, *p;
  80
  81                if (!strncmp("URL:", buffer, 4)) {
  82                        is_refspec = 0;
  83                        s = buffer + 4;
  84                } else if (!strncmp("Push:", buffer, 5)) {
  85                        is_refspec = 1;
  86                        s = buffer + 5;
  87                } else
  88                        continue;
  89
  90                /* Remove whitespace at the head.. */
  91                while (isspace(*s))
  92                        s++;
  93                if (!*s)
  94                        continue;
  95
  96                /* ..and at the end */
  97                p = s + strlen(s);
  98                while (isspace(p[-1]))
  99                        *--p = 0;
 100
 101                if (!is_refspec) {
 102                        if (n < MAX_URI)
 103                                uri[n++] = xstrdup(s);
 104                        else
 105                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 106                }
 107                else if (is_refspec && !has_explicit_refspec)
 108                        add_refspec(xstrdup(s));
 109        }
 110        fclose(f);
 111        if (!n)
 112                die("remote '%s' has no URL", repo);
 113        return n;
 114}
 115
 116static const char **config_uri;
 117static const char *config_repo;
 118static int config_repo_len;
 119static int config_current_uri;
 120static int config_get_refspecs;
 121
 122static int get_remote_config(const char* key, const char* value)
 123{
 124        if (!strncmp(key, "remote.", 7) &&
 125            !strncmp(key + 7, config_repo, config_repo_len)) {
 126                if (!strcmp(key + 7 + config_repo_len, ".url")) {
 127                        if (config_current_uri < MAX_URI)
 128                                config_uri[config_current_uri++] = xstrdup(value);
 129                        else
 130                                error("more than %d URL's specified, ignoring the rest", MAX_URI);
 131                }
 132                else if (config_get_refspecs &&
 133                         !strcmp(key + 7 + config_repo_len, ".push"))
 134                        add_refspec(xstrdup(value));
 135        }
 136        return 0;
 137}
 138
 139static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
 140{
 141        config_repo_len = strlen(repo);
 142        config_repo = repo;
 143        config_current_uri = 0;
 144        config_uri = uri;
 145        config_get_refspecs = !(refspec_nr || all || tags);
 146
 147        git_config(get_remote_config);
 148        return config_current_uri;
 149}
 150
 151static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
 152{
 153        const char *slash = strchr(repo, '/');
 154        int n = slash ? slash - repo : 1000;
 155        FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
 156        char *s, *p;
 157        int len;
 158
 159        if (!f)
 160                return 0;
 161        s = fgets(buffer, BUF_SIZE, f);
 162        fclose(f);
 163        if (!s)
 164                return 0;
 165        while (isspace(*s))
 166                s++;
 167        if (!*s)
 168                return 0;
 169        p = s + strlen(s);
 170        while (isspace(p[-1]))
 171                *--p = 0;
 172        len = p - s;
 173        if (slash)
 174                len += strlen(slash);
 175        p = xmalloc(len + 1);
 176        strcpy(p, s);
 177        if (slash)
 178                strcat(p, slash);
 179        uri[0] = p;
 180        return 1;
 181}
 182
 183/*
 184 * Read remotes and branches file, fill the push target URI
 185 * list.  If there is no command line refspecs, read Push: lines
 186 * to set up the *refspec list as well.
 187 * return the number of push target URIs
 188 */
 189static int read_config(const char *repo, const char *uri[MAX_URI])
 190{
 191        int n;
 192
 193        if (*repo != '/') {
 194                n = get_remotes_uri(repo, uri);
 195                if (n > 0)
 196                        return n;
 197
 198                n = get_config_remotes_uri(repo, uri);
 199                if (n > 0)
 200                        return n;
 201
 202                n = get_branches_uri(repo, uri);
 203                if (n > 0)
 204                        return n;
 205        }
 206
 207        uri[0] = repo;
 208        return 1;
 209}
 210
 211static int do_push(const char *repo)
 212{
 213        const char *uri[MAX_URI];
 214        int i, n;
 215        int common_argc;
 216        const char **argv;
 217        int argc;
 218
 219        n = read_config(repo, uri);
 220        if (n <= 0)
 221                die("bad repository '%s'", repo);
 222
 223        argv = xmalloc((refspec_nr + 10) * sizeof(char *));
 224        argv[0] = "dummy-send-pack";
 225        argc = 1;
 226        if (all)
 227                argv[argc++] = "--all";
 228        if (force)
 229                argv[argc++] = "--force";
 230        if (execute)
 231                argv[argc++] = execute;
 232        common_argc = argc;
 233
 234        for (i = 0; i < n; i++) {
 235                int err;
 236                int dest_argc = common_argc;
 237                int dest_refspec_nr = refspec_nr;
 238                const char **dest_refspec = refspec;
 239                const char *dest = uri[i];
 240                const char *sender = "git-send-pack";
 241                if (!strncmp(dest, "http://", 7) ||
 242                    !strncmp(dest, "https://", 8))
 243                        sender = "git-http-push";
 244                else if (thin)
 245                        argv[dest_argc++] = "--thin";
 246                argv[0] = sender;
 247                argv[dest_argc++] = dest;
 248                while (dest_refspec_nr--)
 249                        argv[dest_argc++] = *dest_refspec++;
 250                argv[dest_argc] = NULL;
 251                if (verbose)
 252                        fprintf(stderr, "Pushing to %s\n", dest);
 253                err = run_command_v(argc, argv);
 254                if (!err)
 255                        continue;
 256                switch (err) {
 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 -err;
 268                }
 269        }
 270        return 0;
 271}
 272
 273int cmd_push(int argc, const char **argv, const char *prefix)
 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, "-v")) {
 287                        verbose=1;
 288                        continue;
 289                }
 290                if (!strncmp(arg, "--repo=", 7)) {
 291                        repo = arg+7;
 292                        continue;
 293                }
 294                if (!strcmp(arg, "--all")) {
 295                        all = 1;
 296                        continue;
 297                }
 298                if (!strcmp(arg, "--tags")) {
 299                        tags = 1;
 300                        continue;
 301                }
 302                if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
 303                        force = 1;
 304                        continue;
 305                }
 306                if (!strcmp(arg, "--thin")) {
 307                        thin = 1;
 308                        continue;
 309                }
 310                if (!strcmp(arg, "--no-thin")) {
 311                        thin = 0;
 312                        continue;
 313                }
 314                if (!strncmp(arg, "--exec=", 7)) {
 315                        execute = arg;
 316                        continue;
 317                }
 318                usage(push_usage);
 319        }
 320        set_refspecs(argv + i, argc - i);
 321        return do_push(repo);
 322}