1/*
2* "git push"
3*/
4#include "cache.h"
5#include "refs.h"
6#include "run-command.h"
7#include "builtin.h"
89
#define MAX_URI (16)
1011
static const char push_usage[] = "git push [--all] [--tags] [--force] <repository> [<refspec>...]";
1213
static int all = 0, tags = 0, force = 0, thin = 1;
14static const char *execute = NULL;
1516
#define BUF_SIZE (2084)
17static char buffer[BUF_SIZE];
1819
static const char **refspec = NULL;
20static int refspec_nr = 0;
2122
static void add_refspec(const char *ref)
23{
24int nr = refspec_nr + 1;
25refspec = xrealloc(refspec, nr * sizeof(char *));
26refspec[nr-1] = ref;
27refspec_nr = nr;
28}
2930
static int expand_one_ref(const char *ref, const unsigned char *sha1)
31{
32/* Ignore the "refs/" at the beginning of the refname */
33ref += 5;
3435
if (strncmp(ref, "tags/", 5))
36return 0;
3738
add_refspec(strdup(ref));
39return 0;
40}
4142
static void expand_refspecs(void)
43{
44if (all) {
45if (refspec_nr)
46die("cannot mix '--all' and a refspec");
4748
/*
49* No need to expand "--all" - we'll just use
50* the "--all" flag to send-pack
51*/
52return;
53}
54if (!tags)
55return;
56for_each_ref(expand_one_ref);
57}
5859
static void set_refspecs(const char **refs, int nr)
60{
61if (nr) {
62size_t bytes = nr * sizeof(char *);
6364
refspec = xrealloc(refspec, bytes);
65memcpy(refspec, refs, bytes);
66refspec_nr = nr;
67}
68expand_refspecs();
69}
7071
static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
72{
73int n = 0;
74FILE *f = fopen(git_path("remotes/%s", repo), "r");
75int has_explicit_refspec = refspec_nr;
7677
if (!f)
78return -1;
79while (fgets(buffer, BUF_SIZE, f)) {
80int is_refspec;
81char *s, *p;
8283
if (!strncmp("URL: ", buffer, 5)) {
84is_refspec = 0;
85s = buffer + 5;
86} else if (!strncmp("Push: ", buffer, 6)) {
87is_refspec = 1;
88s = buffer + 6;
89} else
90continue;
9192
/* Remove whitespace at the head.. */
93while (isspace(*s))
94s++;
95if (!*s)
96continue;
9798
/* ..and at the end */
99p = s + strlen(s);
100while (isspace(p[-1]))
101*--p = 0;
102103
if (!is_refspec) {
104if (n < MAX_URI)
105uri[n++] = strdup(s);
106else
107error("more than %d URL's specified, ignoreing the rest", MAX_URI);
108}
109else if (is_refspec && !has_explicit_refspec)
110add_refspec(strdup(s));
111}
112fclose(f);
113if (!n)
114die("remote '%s' has no URL", repo);
115return n;
116}
117118
static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
119{
120const char *slash = strchr(repo, '/');
121int n = slash ? slash - repo : 1000;
122FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
123char *s, *p;
124int len;
125126
if (!f)
127return 0;
128s = fgets(buffer, BUF_SIZE, f);
129fclose(f);
130if (!s)
131return 0;
132while (isspace(*s))
133s++;
134if (!*s)
135return 0;
136p = s + strlen(s);
137while (isspace(p[-1]))
138*--p = 0;
139len = p - s;
140if (slash)
141len += strlen(slash);
142p = xmalloc(len + 1);
143strcpy(p, s);
144if (slash)
145strcat(p, slash);
146uri[0] = p;
147return 1;
148}
149150
/*
151* Read remotes and branches file, fill the push target URI
152* list. If there is no command line refspecs, read Push: lines
153* to set up the *refspec list as well.
154* return the number of push target URIs
155*/
156static int read_config(const char *repo, const char *uri[MAX_URI])
157{
158int n;
159160
if (*repo != '/') {
161n = get_remotes_uri(repo, uri);
162if (n > 0)
163return n;
164165
n = get_branches_uri(repo, uri);
166if (n > 0)
167return n;
168}
169170
uri[0] = repo;
171return 1;
172}
173174
static int do_push(const char *repo)
175{
176const char *uri[MAX_URI];
177int i, n;
178int remote;
179const char **argv;
180int argc;
181182
n = read_config(repo, uri);
183if (n <= 0)
184die("bad repository '%s'", repo);
185186
argv = xmalloc((refspec_nr + 10) * sizeof(char *));
187argv[0] = "dummy-send-pack";
188argc = 1;
189if (all)
190argv[argc++] = "--all";
191if (force)
192argv[argc++] = "--force";
193if (execute)
194argv[argc++] = execute;
195if (thin)
196argv[argc++] = "--thin";
197remote = argc;
198argv[argc++] = "dummy-remote";
199while (refspec_nr--)
200argv[argc++] = *refspec++;
201argv[argc] = NULL;
202203
for (i = 0; i < n; i++) {
204int error;
205const char *dest = uri[i];
206const char *sender = "git-send-pack";
207if (!strncmp(dest, "http://", 7) ||
208!strncmp(dest, "https://", 8))
209sender = "git-http-push";
210argv[0] = sender;
211argv[remote] = dest;
212error = run_command_v(argc, argv);
213if (!error)
214continue;
215switch (error) {
216case -ERR_RUN_COMMAND_FORK:
217die("unable to fork for %s", sender);
218case -ERR_RUN_COMMAND_EXEC:
219die("unable to exec %s", sender);
220case -ERR_RUN_COMMAND_WAITPID:
221case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
222case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
223case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
224die("%s died with strange error", sender);
225default:
226return -error;
227}
228}
229return 0;
230}
231232
int cmd_push(int argc, const char **argv, char **envp)
233{
234int i;
235const char *repo = "origin"; // default repository
236237
for (i = 1; i < argc; i++) {
238const char *arg = argv[i];
239240
if (arg[0] != '-') {
241repo = arg;
242i++;
243break;
244}
245if (!strcmp(arg, "--all")) {
246all = 1;
247continue;
248}
249if (!strcmp(arg, "--tags")) {
250tags = 1;
251continue;
252}
253if (!strcmp(arg, "--force")) {
254force = 1;
255continue;
256}
257if (!strcmp(arg, "--thin")) {
258thin = 1;
259continue;
260}
261if (!strcmp(arg, "--no-thin")) {
262thin = 0;
263continue;
264}
265if (!strncmp(arg, "--exec=", 7)) {
266execute = arg;
267continue;
268}
269usage(push_usage);
270}
271set_refspecs(argv + i, argc - i);
272return do_push(repo);
273}