git-svn: simplify the (multi-)init methods of fetching
[git/dscho.git] / builtin-push.c
blob979efcc45fca1a39b125e02d14b1f5d096f813ba
1 /*
2  * "git push"
3  */
4 #include "cache.h"
5 #include "refs.h"
6 #include "run-command.h"
7 #include "builtin.h"
9 #define MAX_URI (16)
11 static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
13 static int all, tags, force, thin = 1, verbose;
14 static const char *receivepack;
16 #define BUF_SIZE (2084)
17 static char buffer[BUF_SIZE];
19 static const char **refspec;
20 static int refspec_nr;
22 static void add_refspec(const char *ref)
24         int nr = refspec_nr + 1;
25         refspec = xrealloc(refspec, nr * sizeof(char *));
26         refspec[nr-1] = ref;
27         refspec_nr = nr;
30 static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
32         /* Ignore the "refs/" at the beginning of the refname */
33         ref += 5;
35         if (!prefixcmp(ref, "tags/"))
36                 add_refspec(xstrdup(ref));
37         return 0;
40 static void expand_refspecs(void)
42         if (all) {
43                 if (refspec_nr)
44                         die("cannot mix '--all' and a refspec");
46                 /*
47                  * No need to expand "--all" - we'll just use
48                  * the "--all" flag to send-pack
49                  */
50                 return;
51         }
52         if (!tags)
53                 return;
54         for_each_ref(expand_one_ref, NULL);
57 struct wildcard_cb {
58         const char *from_prefix;
59         int from_prefix_len;
60         const char *to_prefix;
61         int to_prefix_len;
62         int force;
65 static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
67         struct wildcard_cb *cb = cb_data;
68         int len = strlen(ref);
69         char *expanded, *newref;
71         if (len < cb->from_prefix_len ||
72             memcmp(cb->from_prefix, ref, cb->from_prefix_len))
73                 return 0;
74         expanded = xmalloc(len * 2 + cb->force +
75                            (cb->to_prefix_len - cb->from_prefix_len) + 2);
76         newref = expanded + cb->force;
77         if (cb->force)
78                 expanded[0] = '+';
79         memcpy(newref, ref, len);
80         newref[len] = ':';
81         memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
82         strcpy(newref + len + 1 + cb->to_prefix_len,
83                ref + cb->from_prefix_len);
84         add_refspec(expanded);
85         return 0;
88 static int wildcard_ref(const char *ref)
90         int len;
91         const char *colon;
92         struct wildcard_cb cb;
94         memset(&cb, 0, sizeof(cb));
95         if (ref[0] == '+') {
96                 cb.force = 1;
97                 ref++;
98         }
99         len = strlen(ref);
100         colon = strchr(ref, ':');
101         if (! (colon && ref < colon &&
102                colon[-2] == '/' && colon[-1] == '*' &&
103                /* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
104                7 <= len &&
105                ref[len-2] == '/' && ref[len-1] == '*') )
106                 return 0 ;
107         cb.from_prefix = ref;
108         cb.from_prefix_len = colon - ref - 1;
109         cb.to_prefix = colon + 1;
110         cb.to_prefix_len = len - (colon - ref) - 2;
111         for_each_ref(expand_wildcard_ref, &cb);
112         return 1;
115 static void set_refspecs(const char **refs, int nr)
117         if (nr) {
118                 int i;
119                 for (i = 0; i < nr; i++) {
120                         const char *ref = refs[i];
121                         if (!strcmp("tag", ref)) {
122                                 char *tag;
123                                 int len;
124                                 if (nr <= ++i)
125                                         die("tag shorthand without <tag>");
126                                 len = strlen(refs[i]) + 11;
127                                 tag = xmalloc(len);
128                                 strcpy(tag, "refs/tags/");
129                                 strcat(tag, refs[i]);
130                                 ref = tag;
131                         }
132                         else if (wildcard_ref(ref))
133                                 continue;
134                         add_refspec(ref);
135                 }
136         }
137         expand_refspecs();
140 static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
142         int n = 0;
143         FILE *f = fopen(git_path("remotes/%s", repo), "r");
144         int has_explicit_refspec = refspec_nr || all || tags;
146         if (!f)
147                 return -1;
148         while (fgets(buffer, BUF_SIZE, f)) {
149                 int is_refspec;
150                 char *s, *p;
152                 if (!prefixcmp(buffer, "URL:")) {
153                         is_refspec = 0;
154                         s = buffer + 4;
155                 } else if (!prefixcmp(buffer, "Push:")) {
156                         is_refspec = 1;
157                         s = buffer + 5;
158                 } else
159                         continue;
161                 /* Remove whitespace at the head.. */
162                 while (isspace(*s))
163                         s++;
164                 if (!*s)
165                         continue;
167                 /* ..and at the end */
168                 p = s + strlen(s);
169                 while (isspace(p[-1]))
170                         *--p = 0;
172                 if (!is_refspec) {
173                         if (n < MAX_URI)
174                                 uri[n++] = xstrdup(s);
175                         else
176                                 error("more than %d URL's specified, ignoring the rest", MAX_URI);
177                 }
178                 else if (is_refspec && !has_explicit_refspec) {
179                         if (!wildcard_ref(s))
180                                 add_refspec(xstrdup(s));
181                 }
182         }
183         fclose(f);
184         if (!n)
185                 die("remote '%s' has no URL", repo);
186         return n;
189 static const char **config_uri;
190 static const char *config_repo;
191 static int config_repo_len;
192 static int config_current_uri;
193 static int config_get_refspecs;
194 static int config_get_receivepack;
196 static int get_remote_config(const char* key, const char* value)
198         if (!prefixcmp(key, "remote.") &&
199             !strncmp(key + 7, config_repo, config_repo_len)) {
200                 if (!strcmp(key + 7 + config_repo_len, ".url")) {
201                         if (config_current_uri < MAX_URI)
202                                 config_uri[config_current_uri++] = xstrdup(value);
203                         else
204                                 error("more than %d URL's specified, ignoring the rest", MAX_URI);
205                 }
206                 else if (config_get_refspecs &&
207                          !strcmp(key + 7 + config_repo_len, ".push")) {
208                         if (!wildcard_ref(value))
209                                 add_refspec(xstrdup(value));
210                 }
211                 else if (config_get_receivepack &&
212                          !strcmp(key + 7 + config_repo_len, ".receivepack")) {
213                         if (!receivepack) {
214                                 char *rp = xmalloc(strlen(value) + 16);
215                                 sprintf(rp, "--receive-pack=%s", value);
216                                 receivepack = rp;
217                         } else
218                                 error("more than one receivepack given, using the first");
219                 }
220         }
221         return 0;
224 static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
226         config_repo_len = strlen(repo);
227         config_repo = repo;
228         config_current_uri = 0;
229         config_uri = uri;
230         config_get_refspecs = !(refspec_nr || all || tags);
231         config_get_receivepack = (receivepack == NULL);
233         git_config(get_remote_config);
234         return config_current_uri;
237 static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
239         const char *slash = strchr(repo, '/');
240         int n = slash ? slash - repo : 1000;
241         FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
242         char *s, *p;
243         int len;
245         if (!f)
246                 return 0;
247         s = fgets(buffer, BUF_SIZE, f);
248         fclose(f);
249         if (!s)
250                 return 0;
251         while (isspace(*s))
252                 s++;
253         if (!*s)
254                 return 0;
255         p = s + strlen(s);
256         while (isspace(p[-1]))
257                 *--p = 0;
258         len = p - s;
259         if (slash)
260                 len += strlen(slash);
261         p = xmalloc(len + 1);
262         strcpy(p, s);
263         if (slash)
264                 strcat(p, slash);
265         uri[0] = p;
266         return 1;
270  * Read remotes and branches file, fill the push target URI
271  * list.  If there is no command line refspecs, read Push: lines
272  * to set up the *refspec list as well.
273  * return the number of push target URIs
274  */
275 static int read_config(const char *repo, const char *uri[MAX_URI])
277         int n;
279         if (*repo != '/') {
280                 n = get_remotes_uri(repo, uri);
281                 if (n > 0)
282                         return n;
284                 n = get_config_remotes_uri(repo, uri);
285                 if (n > 0)
286                         return n;
288                 n = get_branches_uri(repo, uri);
289                 if (n > 0)
290                         return n;
291         }
293         uri[0] = repo;
294         return 1;
297 static int do_push(const char *repo)
299         const char *uri[MAX_URI];
300         int i, n;
301         int common_argc;
302         const char **argv;
303         int argc;
305         n = read_config(repo, uri);
306         if (n <= 0)
307                 die("bad repository '%s'", repo);
309         argv = xmalloc((refspec_nr + 10) * sizeof(char *));
310         argv[0] = "dummy-send-pack";
311         argc = 1;
312         if (all)
313                 argv[argc++] = "--all";
314         if (force)
315                 argv[argc++] = "--force";
316         if (receivepack)
317                 argv[argc++] = receivepack;
318         common_argc = argc;
320         for (i = 0; i < n; i++) {
321                 int err;
322                 int dest_argc = common_argc;
323                 int dest_refspec_nr = refspec_nr;
324                 const char **dest_refspec = refspec;
325                 const char *dest = uri[i];
326                 const char *sender = "git-send-pack";
327                 if (!prefixcmp(dest, "http://") ||
328                     !prefixcmp(dest, "https://"))
329                         sender = "git-http-push";
330                 else if (thin)
331                         argv[dest_argc++] = "--thin";
332                 argv[0] = sender;
333                 argv[dest_argc++] = dest;
334                 while (dest_refspec_nr--)
335                         argv[dest_argc++] = *dest_refspec++;
336                 argv[dest_argc] = NULL;
337                 if (verbose)
338                         fprintf(stderr, "Pushing to %s\n", dest);
339                 err = run_command_v(argv);
340                 if (!err)
341                         continue;
342                 switch (err) {
343                 case -ERR_RUN_COMMAND_FORK:
344                         die("unable to fork for %s", sender);
345                 case -ERR_RUN_COMMAND_EXEC:
346                         die("unable to exec %s", sender);
347                 case -ERR_RUN_COMMAND_WAITPID:
348                 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
349                 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
350                 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
351                         die("%s died with strange error", sender);
352                 default:
353                         return -err;
354                 }
355         }
356         return 0;
359 int cmd_push(int argc, const char **argv, const char *prefix)
361         int i;
362         const char *repo = "origin";    /* default repository */
364         for (i = 1; i < argc; i++) {
365                 const char *arg = argv[i];
367                 if (arg[0] != '-') {
368                         repo = arg;
369                         i++;
370                         break;
371                 }
372                 if (!strcmp(arg, "-v")) {
373                         verbose=1;
374                         continue;
375                 }
376                 if (!prefixcmp(arg, "--repo=")) {
377                         repo = arg+7;
378                         continue;
379                 }
380                 if (!strcmp(arg, "--all")) {
381                         all = 1;
382                         continue;
383                 }
384                 if (!strcmp(arg, "--tags")) {
385                         tags = 1;
386                         continue;
387                 }
388                 if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
389                         force = 1;
390                         continue;
391                 }
392                 if (!strcmp(arg, "--thin")) {
393                         thin = 1;
394                         continue;
395                 }
396                 if (!strcmp(arg, "--no-thin")) {
397                         thin = 0;
398                         continue;
399                 }
400                 if (!prefixcmp(arg, "--receive-pack=")) {
401                         receivepack = arg;
402                         continue;
403                 }
404                 if (!prefixcmp(arg, "--exec=")) {
405                         receivepack = arg;
406                         continue;
407                 }
408                 usage(push_usage);
409         }
410         set_refspecs(argv + i, argc - i);
411         return do_push(repo);