builtin-push.con commit Merge branch 'do/maint-merge-recursive-fix' into maint (5c44cc9)
   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 [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
  14        NULL,
  15};
  16
  17static int thin;
  18static const char *receivepack;
  19
  20static const char **refspec;
  21static int refspec_nr;
  22
  23static void add_refspec(const char *ref)
  24{
  25        int nr = refspec_nr + 1;
  26        refspec = xrealloc(refspec, nr * sizeof(char *));
  27        refspec[nr-1] = ref;
  28        refspec_nr = nr;
  29}
  30
  31static void set_refspecs(const char **refs, int nr)
  32{
  33        int i;
  34        for (i = 0; i < nr; i++) {
  35                const char *ref = refs[i];
  36                if (!strcmp("tag", ref)) {
  37                        char *tag;
  38                        int len;
  39                        if (nr <= ++i)
  40                                die("tag shorthand without <tag>");
  41                        len = strlen(refs[i]) + 11;
  42                        tag = xmalloc(len);
  43                        strcpy(tag, "refs/tags/");
  44                        strcat(tag, refs[i]);
  45                        ref = tag;
  46                }
  47                add_refspec(ref);
  48        }
  49}
  50
  51static void setup_push_tracking(void)
  52{
  53        struct strbuf refspec = STRBUF_INIT;
  54        struct branch *branch = branch_get(NULL);
  55        if (!branch)
  56                die("You are not currently on a branch.");
  57        if (!branch->merge_nr)
  58                die("The current branch %s is not tracking anything.",
  59                    branch->name);
  60        if (branch->merge_nr != 1)
  61                die("The current branch %s is tracking multiple branches, "
  62                    "refusing to push.", branch->name);
  63        strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
  64        add_refspec(refspec.buf);
  65}
  66
  67static const char *warn_unconfigured_push_msg[] = {
  68        "You did not specify any refspecs to push, and the current remote",
  69        "has not configured any push refspecs. The default action in this",
  70        "case is to push all matching refspecs, that is, all branches",
  71        "that exist both locally and remotely will be updated.  This may",
  72        "not necessarily be what you want to happen.",
  73        "",
  74        "You can specify what action you want to take in this case, and",
  75        "avoid seeing this message again, by configuring 'push.default' to:",
  76        "  'nothing'  : Do not push anything",
  77        "  'matching' : Push all matching branches (default)",
  78        "  'tracking' : Push the current branch to whatever it is tracking",
  79        "  'current'  : Push the current branch"
  80};
  81
  82static void warn_unconfigured_push(void)
  83{
  84        int i;
  85        for (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++)
  86                warning("%s", warn_unconfigured_push_msg[i]);
  87}
  88
  89static void setup_default_push_refspecs(void)
  90{
  91        git_config(git_default_config, NULL);
  92        switch (push_default) {
  93        case PUSH_DEFAULT_UNSPECIFIED:
  94                warn_unconfigured_push();
  95                /* fallthrough */
  96
  97        case PUSH_DEFAULT_MATCHING:
  98                add_refspec(":");
  99                break;
 100
 101        case PUSH_DEFAULT_TRACKING:
 102                setup_push_tracking();
 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 do_push(const char *repo, int flags)
 117{
 118        int i, errs;
 119        struct remote *remote = remote_get(repo);
 120
 121        if (!remote) {
 122                if (repo)
 123                        die("bad repository '%s'", repo);
 124                die("No destination configured to push to.");
 125        }
 126
 127        if (remote->mirror)
 128                flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 129
 130        if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
 131                if (!strcmp(*refspec, "refs/tags/*"))
 132                        return error("--all and --tags are incompatible");
 133                return error("--all can't be combined with refspecs");
 134        }
 135
 136        if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
 137                if (!strcmp(*refspec, "refs/tags/*"))
 138                        return error("--mirror and --tags are incompatible");
 139                return error("--mirror can't be combined with refspecs");
 140        }
 141
 142        if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
 143                                (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
 144                return error("--all and --mirror are incompatible");
 145        }
 146
 147        if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
 148                if (remote->push_refspec_nr) {
 149                        refspec = remote->push_refspec;
 150                        refspec_nr = remote->push_refspec_nr;
 151                } else if (!(flags & TRANSPORT_PUSH_MIRROR))
 152                        setup_default_push_refspecs();
 153        }
 154        errs = 0;
 155        for (i = 0; i < remote->url_nr; i++) {
 156                struct transport *transport =
 157                        transport_get(remote, remote->url[i]);
 158                int err;
 159                if (receivepack)
 160                        transport_set_option(transport,
 161                                             TRANS_OPT_RECEIVEPACK, receivepack);
 162                if (thin)
 163                        transport_set_option(transport, TRANS_OPT_THIN, "yes");
 164
 165                if (flags & TRANSPORT_PUSH_VERBOSE)
 166                        fprintf(stderr, "Pushing to %s\n", remote->url[i]);
 167                err = transport_push(transport, refspec_nr, refspec, flags);
 168                err |= transport_disconnect(transport);
 169
 170                if (!err)
 171                        continue;
 172
 173                error("failed to push some refs to '%s'", remote->url[i]);
 174                errs++;
 175        }
 176        return !!errs;
 177}
 178
 179int cmd_push(int argc, const char **argv, const char *prefix)
 180{
 181        int flags = 0;
 182        int tags = 0;
 183        int rc;
 184        const char *repo = NULL;        /* default repository */
 185
 186        struct option options[] = {
 187                OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE),
 188                OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
 189                OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
 190                OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
 191                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
 192                OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
 193                OPT_BIT( 0 , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
 194                OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
 195                OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
 196                OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
 197                OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
 198                OPT_END()
 199        };
 200
 201        argc = parse_options(argc, argv, options, push_usage, 0);
 202
 203        if (tags)
 204                add_refspec("refs/tags/*");
 205
 206        if (argc > 0) {
 207                repo = argv[0];
 208                set_refspecs(argv + 1, argc - 1);
 209        }
 210
 211        rc = do_push(repo, flags);
 212        if (rc == -1)
 213                usage_with_options(push_usage, options);
 214        else
 215                return rc;
 216}