builtin / push.con commit t6300: add more body-parsing tests (7140c22)
   1/*
   2 * "git push"
   3 */
   4#include "cache.h"
   5#include "refs.h"
   6#include "run-command.h"
   7#include "builtin.h"
   8#include "remote.h"
   9#include "transport.h"
  10#include "parse-options.h"
  11
  12static const char * const push_usage[] = {
  13        "git push [<options>] [<repository> [<refspec>...]]",
  14        NULL,
  15};
  16
  17static int thin;
  18static int deleterefs;
  19static const char *receivepack;
  20static int verbosity;
  21static int progress;
  22
  23static const char **refspec;
  24static int refspec_nr;
  25static int refspec_alloc;
  26
  27static void add_refspec(const char *ref)
  28{
  29        refspec_nr++;
  30        ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
  31        refspec[refspec_nr-1] = ref;
  32}
  33
  34static void set_refspecs(const char **refs, int nr)
  35{
  36        int i;
  37        for (i = 0; i < nr; i++) {
  38                const char *ref = refs[i];
  39                if (!strcmp("tag", ref)) {
  40                        char *tag;
  41                        int len;
  42                        if (nr <= ++i)
  43                                die(_("tag shorthand without <tag>"));
  44                        len = strlen(refs[i]) + 11;
  45                        if (deleterefs) {
  46                                tag = xmalloc(len+1);
  47                                strcpy(tag, ":refs/tags/");
  48                        } else {
  49                                tag = xmalloc(len);
  50                                strcpy(tag, "refs/tags/");
  51                        }
  52                        strcat(tag, refs[i]);
  53                        ref = tag;
  54                } else if (deleterefs && !strchr(ref, ':')) {
  55                        char *delref;
  56                        int len = strlen(ref)+1;
  57                        delref = xmalloc(len+1);
  58                        strcpy(delref, ":");
  59                        strcat(delref, ref);
  60                        ref = delref;
  61                } else if (deleterefs)
  62                        die(_("--delete only accepts plain target ref names"));
  63                add_refspec(ref);
  64        }
  65}
  66
  67static void setup_push_upstream(struct remote *remote)
  68{
  69        struct strbuf refspec = STRBUF_INIT;
  70        struct branch *branch = branch_get(NULL);
  71        if (!branch)
  72                die(_("You are not currently on a branch.\n"
  73                    "To push the history leading to the current (detached HEAD)\n"
  74                    "state now, use\n"
  75                    "\n"
  76                    "    git push %s HEAD:<name-of-remote-branch>\n"),
  77                    remote->name);
  78        if (!branch->merge_nr || !branch->merge)
  79                die(_("The current branch %s has no upstream branch.\n"
  80                    "To push the current branch and set the remote as upstream, use\n"
  81                    "\n"
  82                    "    git push --set-upstream %s %s\n"),
  83                    branch->name,
  84                    remote->name,
  85                    branch->name);
  86        if (branch->merge_nr != 1)
  87                die(_("The current branch %s has multiple upstream branches, "
  88                    "refusing to push."), branch->name);
  89        strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
  90        add_refspec(refspec.buf);
  91}
  92
  93static void setup_default_push_refspecs(struct remote *remote)
  94{
  95        switch (push_default) {
  96        default:
  97        case PUSH_DEFAULT_MATCHING:
  98                add_refspec(":");
  99                break;
 100
 101        case PUSH_DEFAULT_UPSTREAM:
 102                setup_push_upstream(remote);
 103                break;
 104
 105        case PUSH_DEFAULT_CURRENT:
 106                add_refspec("HEAD");
 107                break;
 108
 109        case PUSH_DEFAULT_NOTHING:
 110                die(_("You didn't specify any refspecs to push, and "
 111                    "push.default is \"nothing\"."));
 112                break;
 113        }
 114}
 115
 116static int push_with_options(struct transport *transport, int flags)
 117{
 118        int err;
 119        int nonfastforward;
 120
 121        transport_set_verbosity(transport, verbosity, progress);
 122
 123        if (receivepack)
 124                transport_set_option(transport,
 125                                     TRANS_OPT_RECEIVEPACK, receivepack);
 126        if (thin)
 127                transport_set_option(transport, TRANS_OPT_THIN, "yes");
 128
 129        if (verbosity > 0)
 130                fprintf(stderr, _("Pushing to %s\n"), transport->url);
 131        err = transport_push(transport, refspec_nr, refspec, flags,
 132                             &nonfastforward);
 133        if (err != 0)
 134                error(_("failed to push some refs to '%s'"), transport->url);
 135
 136        err |= transport_disconnect(transport);
 137
 138        if (!err)
 139                return 0;
 140
 141        if (nonfastforward && advice_push_nonfastforward) {
 142                fprintf(stderr, _("To prevent you from losing history, non-fast-forward updates were rejected\n"
 143                                "Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
 144                                "'Note about fast-forwards' section of 'git push --help' for details.\n"));
 145        }
 146
 147        return 1;
 148}
 149
 150static int do_push(const char *repo, int flags)
 151{
 152        int i, errs;
 153        struct remote *remote = remote_get(repo);
 154        const char **url;
 155        int url_nr;
 156
 157        if (!remote) {
 158                if (repo)
 159                        die(_("bad repository '%s'"), repo);
 160                die(_("No configured push destination.\n"
 161                    "Either specify the URL from the command-line or configure a remote repository using\n"
 162                    "\n"
 163                    "    git remote add <name> <url>\n"
 164                    "\n"
 165                    "and then push using the remote name\n"
 166                    "\n"
 167                    "    git push <name>\n"));
 168        }
 169
 170        if (remote->mirror)
 171                flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 172
 173        if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
 174                if (!strcmp(*refspec, "refs/tags/*"))
 175                        return error(_("--all and --tags are incompatible"));
 176                return error(_("--all can't be combined with refspecs"));
 177        }
 178
 179        if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
 180                if (!strcmp(*refspec, "refs/tags/*"))
 181                        return error(_("--mirror and --tags are incompatible"));
 182                return error(_("--mirror can't be combined with refspecs"));
 183        }
 184
 185        if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
 186                                (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
 187                return error(_("--all and --mirror are incompatible"));
 188        }
 189
 190        if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
 191                if (remote->push_refspec_nr) {
 192                        refspec = remote->push_refspec;
 193                        refspec_nr = remote->push_refspec_nr;
 194                } else if (!(flags & TRANSPORT_PUSH_MIRROR))
 195                        setup_default_push_refspecs(remote);
 196        }
 197        errs = 0;
 198        if (remote->pushurl_nr) {
 199                url = remote->pushurl;
 200                url_nr = remote->pushurl_nr;
 201        } else {
 202                url = remote->url;
 203                url_nr = remote->url_nr;
 204        }
 205        if (url_nr) {
 206                for (i = 0; i < url_nr; i++) {
 207                        struct transport *transport =
 208                                transport_get(remote, url[i]);
 209                        if (push_with_options(transport, flags))
 210                                errs++;
 211                }
 212        } else {
 213                struct transport *transport =
 214                        transport_get(remote, NULL);
 215
 216                if (push_with_options(transport, flags))
 217                        errs++;
 218        }
 219        return !!errs;
 220}
 221
 222int cmd_push(int argc, const char **argv, const char *prefix)
 223{
 224        int flags = 0;
 225        int tags = 0;
 226        int rc;
 227        const char *repo = NULL;        /* default repository */
 228        struct option options[] = {
 229                OPT__VERBOSITY(&verbosity),
 230                OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
 231                OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
 232                OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
 233                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
 234                OPT_BOOLEAN( 0, "delete", &deleterefs, "delete refs"),
 235                OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror)"),
 236                OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
 237                OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
 238                OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
 239                OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
 240                OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
 241                OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
 242                OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
 243                        TRANSPORT_PUSH_SET_UPSTREAM),
 244                OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
 245                OPT_END()
 246        };
 247
 248        packet_trace_identity("push");
 249        git_config(git_default_config, NULL);
 250        argc = parse_options(argc, argv, prefix, options, push_usage, 0);
 251
 252        if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
 253                die(_("--delete is incompatible with --all, --mirror and --tags"));
 254        if (deleterefs && argc < 2)
 255                die(_("--delete doesn't make sense without any refs"));
 256
 257        if (tags)
 258                add_refspec("refs/tags/*");
 259
 260        if (argc > 0) {
 261                repo = argv[0];
 262                set_refspecs(argv + 1, argc - 1);
 263        }
 264
 265        rc = do_push(repo, flags);
 266        if (rc == -1)
 267                usage_with_options(push_usage, options);
 268        else
 269                return rc;
 270}