builtin / push.con commit t6010 (merge-base): modernize style (9cbdd76)
   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;
  25
  26static void add_refspec(const char *ref)
  27{
  28        int nr = refspec_nr + 1;
  29        refspec = xrealloc(refspec, nr * sizeof(char *));
  30        refspec[nr-1] = ref;
  31        refspec_nr = nr;
  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_tracking(void)
  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.");
  73        if (!branch->merge_nr || !branch->merge)
  74                die("The current branch %s is not tracking anything.",
  75                    branch->name);
  76        if (branch->merge_nr != 1)
  77                die("The current branch %s is tracking multiple branches, "
  78                    "refusing to push.", branch->name);
  79        strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
  80        add_refspec(refspec.buf);
  81}
  82
  83static void setup_default_push_refspecs(void)
  84{
  85        switch (push_default) {
  86        default:
  87        case PUSH_DEFAULT_MATCHING:
  88                add_refspec(":");
  89                break;
  90
  91        case PUSH_DEFAULT_TRACKING:
  92                setup_push_tracking();
  93                break;
  94
  95        case PUSH_DEFAULT_CURRENT:
  96                add_refspec("HEAD");
  97                break;
  98
  99        case PUSH_DEFAULT_NOTHING:
 100                die("You didn't specify any refspecs to push, and "
 101                    "push.default is \"nothing\".");
 102                break;
 103        }
 104}
 105
 106static int push_with_options(struct transport *transport, int flags)
 107{
 108        int err;
 109        int nonfastforward;
 110
 111        transport_set_verbosity(transport, verbosity, progress);
 112
 113        if (receivepack)
 114                transport_set_option(transport,
 115                                     TRANS_OPT_RECEIVEPACK, receivepack);
 116        if (thin)
 117                transport_set_option(transport, TRANS_OPT_THIN, "yes");
 118
 119        if (verbosity > 0)
 120                fprintf(stderr, "Pushing to %s\n", transport->url);
 121        err = transport_push(transport, refspec_nr, refspec, flags,
 122                             &nonfastforward);
 123        if (err != 0)
 124                error("failed to push some refs to '%s'", transport->url);
 125
 126        err |= transport_disconnect(transport);
 127
 128        if (!err)
 129                return 0;
 130
 131        if (nonfastforward && advice_push_nonfastforward) {
 132                fprintf(stderr, "To prevent you from losing history, non-fast-forward updates were rejected\n"
 133                                "Merge the remote changes before pushing again.  See the 'Note about\n"
 134                                "fast-forwards' section of 'git push --help' for details.\n");
 135        }
 136
 137        return 1;
 138}
 139
 140static int do_push(const char *repo, int flags)
 141{
 142        int i, errs;
 143        struct remote *remote = remote_get(repo);
 144        const char **url;
 145        int url_nr;
 146
 147        if (!remote) {
 148                if (repo)
 149                        die("bad repository '%s'", repo);
 150                die("No destination configured to push to.");
 151        }
 152
 153        if (remote->mirror)
 154                flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 155
 156        if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
 157                if (!strcmp(*refspec, "refs/tags/*"))
 158                        return error("--all and --tags are incompatible");
 159                return error("--all can't be combined with refspecs");
 160        }
 161
 162        if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
 163                if (!strcmp(*refspec, "refs/tags/*"))
 164                        return error("--mirror and --tags are incompatible");
 165                return error("--mirror can't be combined with refspecs");
 166        }
 167
 168        if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
 169                                (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
 170                return error("--all and --mirror are incompatible");
 171        }
 172
 173        if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
 174                if (remote->push_refspec_nr) {
 175                        refspec = remote->push_refspec;
 176                        refspec_nr = remote->push_refspec_nr;
 177                } else if (!(flags & TRANSPORT_PUSH_MIRROR))
 178                        setup_default_push_refspecs();
 179        }
 180        errs = 0;
 181        if (remote->pushurl_nr) {
 182                url = remote->pushurl;
 183                url_nr = remote->pushurl_nr;
 184        } else {
 185                url = remote->url;
 186                url_nr = remote->url_nr;
 187        }
 188        if (url_nr) {
 189                for (i = 0; i < url_nr; i++) {
 190                        struct transport *transport =
 191                                transport_get(remote, url[i]);
 192                        if (push_with_options(transport, flags))
 193                                errs++;
 194                }
 195        } else {
 196                struct transport *transport =
 197                        transport_get(remote, NULL);
 198
 199                if (push_with_options(transport, flags))
 200                        errs++;
 201        }
 202        return !!errs;
 203}
 204
 205int cmd_push(int argc, const char **argv, const char *prefix)
 206{
 207        int flags = 0;
 208        int tags = 0;
 209        int rc;
 210        const char *repo = NULL;        /* default repository */
 211        struct option options[] = {
 212                OPT__VERBOSITY(&verbosity),
 213                OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
 214                OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
 215                OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
 216                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
 217                OPT_BOOLEAN( 0, "delete", &deleterefs, "delete refs"),
 218                OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror)"),
 219                OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
 220                OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
 221                OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
 222                OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
 223                OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
 224                OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
 225                OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
 226                        TRANSPORT_PUSH_SET_UPSTREAM),
 227                OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
 228                OPT_END()
 229        };
 230
 231        git_config(git_default_config, NULL);
 232        argc = parse_options(argc, argv, prefix, options, push_usage, 0);
 233
 234        if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
 235                die("--delete is incompatible with --all, --mirror and --tags");
 236        if (deleterefs && argc < 2)
 237                die("--delete doesn't make sense without any refs");
 238
 239        if (tags)
 240                add_refspec("refs/tags/*");
 241
 242        if (argc > 0) {
 243                repo = argv[0];
 244                set_refspecs(argv + 1, argc - 1);
 245        }
 246
 247        rc = do_push(repo, flags);
 248        if (rc == -1)
 249                usage_with_options(push_usage, options);
 250        else
 251                return rc;
 252}