|  | /* | 
|  | * "git push" | 
|  | */ | 
|  | #include "cache.h" | 
|  | #include "refs.h" | 
|  | #include "run-command.h" | 
|  | #include "builtin.h" | 
|  | #include "remote.h" | 
|  | #include "transport.h" | 
|  | #include "parse-options.h" | 
|  |  | 
|  | static const char * const push_usage[] = { | 
|  | "git push [--all | --mirror] [-n | --dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]", | 
|  | NULL, | 
|  | }; | 
|  |  | 
|  | static int thin; | 
|  | static const char *receivepack; | 
|  |  | 
|  | static const char **refspec; | 
|  | static int refspec_nr; | 
|  |  | 
|  | static void add_refspec(const char *ref) | 
|  | { | 
|  | int nr = refspec_nr + 1; | 
|  | refspec = xrealloc(refspec, nr * sizeof(char *)); | 
|  | refspec[nr-1] = ref; | 
|  | refspec_nr = nr; | 
|  | } | 
|  |  | 
|  | static void set_refspecs(const char **refs, int nr) | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < nr; i++) { | 
|  | const char *ref = refs[i]; | 
|  | if (!strcmp("tag", ref)) { | 
|  | char *tag; | 
|  | int len; | 
|  | if (nr <= ++i) | 
|  | die("tag shorthand without <tag>"); | 
|  | len = strlen(refs[i]) + 11; | 
|  | tag = xmalloc(len); | 
|  | strcpy(tag, "refs/tags/"); | 
|  | strcat(tag, refs[i]); | 
|  | ref = tag; | 
|  | } | 
|  | add_refspec(ref); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void setup_push_tracking(void) | 
|  | { | 
|  | struct strbuf refspec = STRBUF_INIT; | 
|  | struct branch *branch = branch_get(NULL); | 
|  | if (!branch) | 
|  | die("You are not currently on a branch."); | 
|  | if (!branch->merge_nr) | 
|  | die("The current branch %s is not tracking anything.", | 
|  | branch->name); | 
|  | if (branch->merge_nr != 1) | 
|  | die("The current branch %s is tracking multiple branches, " | 
|  | "refusing to push.", branch->name); | 
|  | strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src); | 
|  | add_refspec(refspec.buf); | 
|  | } | 
|  |  | 
|  | static void setup_default_push_refspecs(void) | 
|  | { | 
|  | git_config(git_default_config, NULL); | 
|  | switch (push_default) { | 
|  | default: | 
|  | case PUSH_DEFAULT_MATCHING: | 
|  | add_refspec(":"); | 
|  | break; | 
|  |  | 
|  | case PUSH_DEFAULT_TRACKING: | 
|  | setup_push_tracking(); | 
|  | break; | 
|  |  | 
|  | case PUSH_DEFAULT_CURRENT: | 
|  | add_refspec("HEAD"); | 
|  | break; | 
|  |  | 
|  | case PUSH_DEFAULT_NOTHING: | 
|  | die("You didn't specify any refspecs to push, and " | 
|  | "push.default is \"nothing\"."); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int do_push(const char *repo, int flags) | 
|  | { | 
|  | int i, errs; | 
|  | struct remote *remote = remote_get(repo); | 
|  | const char **url; | 
|  | int url_nr; | 
|  |  | 
|  | if (!remote) { | 
|  | if (repo) | 
|  | die("bad repository '%s'", repo); | 
|  | die("No destination configured to push to."); | 
|  | } | 
|  |  | 
|  | if (remote->mirror) | 
|  | flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); | 
|  |  | 
|  | if ((flags & TRANSPORT_PUSH_ALL) && refspec) { | 
|  | if (!strcmp(*refspec, "refs/tags/*")) | 
|  | return error("--all and --tags are incompatible"); | 
|  | return error("--all can't be combined with refspecs"); | 
|  | } | 
|  |  | 
|  | if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) { | 
|  | if (!strcmp(*refspec, "refs/tags/*")) | 
|  | return error("--mirror and --tags are incompatible"); | 
|  | return error("--mirror can't be combined with refspecs"); | 
|  | } | 
|  |  | 
|  | if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) == | 
|  | (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) { | 
|  | return error("--all and --mirror are incompatible"); | 
|  | } | 
|  |  | 
|  | if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) { | 
|  | if (remote->push_refspec_nr) { | 
|  | refspec = remote->push_refspec; | 
|  | refspec_nr = remote->push_refspec_nr; | 
|  | } else if (!(flags & TRANSPORT_PUSH_MIRROR)) | 
|  | setup_default_push_refspecs(); | 
|  | } | 
|  | errs = 0; | 
|  | if (remote->pushurl_nr) { | 
|  | url = remote->pushurl; | 
|  | url_nr = remote->pushurl_nr; | 
|  | } else { | 
|  | url = remote->url; | 
|  | url_nr = remote->url_nr; | 
|  | } | 
|  | for (i = 0; i < url_nr; i++) { | 
|  | struct transport *transport = | 
|  | transport_get(remote, url[i]); | 
|  | int err; | 
|  | int nonfastforward; | 
|  | if (receivepack) | 
|  | transport_set_option(transport, | 
|  | TRANS_OPT_RECEIVEPACK, receivepack); | 
|  | if (thin) | 
|  | transport_set_option(transport, TRANS_OPT_THIN, "yes"); | 
|  |  | 
|  | if (flags & TRANSPORT_PUSH_VERBOSE) | 
|  | fprintf(stderr, "Pushing to %s\n", url[i]); | 
|  | err = transport_push(transport, refspec_nr, refspec, flags, | 
|  | &nonfastforward); | 
|  | err |= transport_disconnect(transport); | 
|  |  | 
|  | if (!err) | 
|  | continue; | 
|  |  | 
|  | error("failed to push some refs to '%s'", url[i]); | 
|  | if (nonfastforward && advice_push_nonfastforward) { | 
|  | printf("To prevent you from losing history, non-fast-forward updates were rejected\n" | 
|  | "Merge the remote changes before pushing again.  See the 'non-fast forward'\n" | 
|  | "section of 'git push --help' for details.\n"); | 
|  | } | 
|  | errs++; | 
|  | } | 
|  | return !!errs; | 
|  | } | 
|  |  | 
|  | int cmd_push(int argc, const char **argv, const char *prefix) | 
|  | { | 
|  | int flags = 0; | 
|  | int tags = 0; | 
|  | int rc; | 
|  | const char *repo = NULL;	/* default repository */ | 
|  |  | 
|  | struct option options[] = { | 
|  | OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET), | 
|  | OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE), | 
|  | OPT_STRING( 0 , "repo", &repo, "repository", "repository"), | 
|  | OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL), | 
|  | OPT_BIT( 0 , "mirror", &flags, "mirror all refs", | 
|  | (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)), | 
|  | OPT_BOOLEAN( 0 , "tags", &tags, "push tags"), | 
|  | OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN), | 
|  | OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN), | 
|  | OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE), | 
|  | OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"), | 
|  | OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"), | 
|  | OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"), | 
|  | OPT_END() | 
|  | }; | 
|  |  | 
|  | argc = parse_options(argc, argv, prefix, options, push_usage, 0); | 
|  |  | 
|  | if (tags) | 
|  | add_refspec("refs/tags/*"); | 
|  |  | 
|  | if (argc > 0) { | 
|  | repo = argv[0]; | 
|  | set_refspecs(argv + 1, argc - 1); | 
|  | } | 
|  |  | 
|  | rc = do_push(repo, flags); | 
|  | if (rc == -1) | 
|  | usage_with_options(push_usage, options); | 
|  | else | 
|  | return rc; | 
|  | } |