Move remote parsing into a library file out of builtin-push.
[git/git.git] / builtin-push.c
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
10 static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
11
12 static int all, tags, force, thin = 1, verbose;
13 static const char *receivepack;
14
15 static const char **refspec;
16 static int refspec_nr;
17
18 static void add_refspec(const char *ref)
19 {
20 int nr = refspec_nr + 1;
21 refspec = xrealloc(refspec, nr * sizeof(char *));
22 refspec[nr-1] = ref;
23 refspec_nr = nr;
24 }
25
26 static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
27 {
28 /* Ignore the "refs/" at the beginning of the refname */
29 ref += 5;
30
31 if (!prefixcmp(ref, "tags/"))
32 add_refspec(xstrdup(ref));
33 return 0;
34 }
35
36 static void expand_refspecs(void)
37 {
38 if (all) {
39 if (refspec_nr)
40 die("cannot mix '--all' and a refspec");
41
42 /*
43 * No need to expand "--all" - we'll just use
44 * the "--all" flag to send-pack
45 */
46 return;
47 }
48 if (!tags)
49 return;
50 for_each_ref(expand_one_ref, NULL);
51 }
52
53 struct wildcard_cb {
54 const char *from_prefix;
55 int from_prefix_len;
56 const char *to_prefix;
57 int to_prefix_len;
58 int force;
59 };
60
61 static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
62 {
63 struct wildcard_cb *cb = cb_data;
64 int len = strlen(ref);
65 char *expanded, *newref;
66
67 if (len < cb->from_prefix_len ||
68 memcmp(cb->from_prefix, ref, cb->from_prefix_len))
69 return 0;
70 expanded = xmalloc(len * 2 + cb->force +
71 (cb->to_prefix_len - cb->from_prefix_len) + 2);
72 newref = expanded + cb->force;
73 if (cb->force)
74 expanded[0] = '+';
75 memcpy(newref, ref, len);
76 newref[len] = ':';
77 memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
78 strcpy(newref + len + 1 + cb->to_prefix_len,
79 ref + cb->from_prefix_len);
80 add_refspec(expanded);
81 return 0;
82 }
83
84 static int wildcard_ref(const char *ref)
85 {
86 int len;
87 const char *colon;
88 struct wildcard_cb cb;
89
90 memset(&cb, 0, sizeof(cb));
91 if (ref[0] == '+') {
92 cb.force = 1;
93 ref++;
94 }
95 len = strlen(ref);
96 colon = strchr(ref, ':');
97 if (! (colon && ref < colon &&
98 colon[-2] == '/' && colon[-1] == '*' &&
99 /* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
100 7 <= len &&
101 ref[len-2] == '/' && ref[len-1] == '*') )
102 return 0 ;
103 cb.from_prefix = ref;
104 cb.from_prefix_len = colon - ref - 1;
105 cb.to_prefix = colon + 1;
106 cb.to_prefix_len = len - (colon - ref) - 2;
107 for_each_ref(expand_wildcard_ref, &cb);
108 return 1;
109 }
110
111 static void set_refspecs(const char **refs, int nr)
112 {
113 if (nr) {
114 int i;
115 for (i = 0; i < nr; i++) {
116 const char *ref = refs[i];
117 if (!strcmp("tag", ref)) {
118 char *tag;
119 int len;
120 if (nr <= ++i)
121 die("tag shorthand without <tag>");
122 len = strlen(refs[i]) + 11;
123 tag = xmalloc(len);
124 strcpy(tag, "refs/tags/");
125 strcat(tag, refs[i]);
126 ref = tag;
127 }
128 else if (wildcard_ref(ref))
129 continue;
130 add_refspec(ref);
131 }
132 }
133 expand_refspecs();
134 }
135
136 static int do_push(const char *repo)
137 {
138 int i, errs;
139 int common_argc;
140 const char **argv;
141 int argc;
142 struct remote *remote = remote_get(repo);
143
144 if (!remote)
145 die("bad repository '%s'", repo);
146
147 if (remote->receivepack) {
148 char *rp = xmalloc(strlen(remote->receivepack) + 16);
149 sprintf(rp, "--receive-pack=%s", remote->receivepack);
150 receivepack = rp;
151 }
152 if (!refspec && !all && !tags && remote->push_refspec_nr) {
153 for (i = 0; i < remote->push_refspec_nr; i++) {
154 if (!wildcard_ref(remote->push_refspec[i]))
155 add_refspec(remote->push_refspec[i]);
156 }
157 }
158
159 argv = xmalloc((refspec_nr + 10) * sizeof(char *));
160 argv[0] = "dummy-send-pack";
161 argc = 1;
162 if (all)
163 argv[argc++] = "--all";
164 if (force)
165 argv[argc++] = "--force";
166 if (receivepack)
167 argv[argc++] = receivepack;
168 common_argc = argc;
169
170 errs = 0;
171 for (i = 0; i < remote->uri_nr; i++) {
172 int err;
173 int dest_argc = common_argc;
174 int dest_refspec_nr = refspec_nr;
175 const char **dest_refspec = refspec;
176 const char *dest = remote->uri[i];
177 const char *sender = "send-pack";
178 if (!prefixcmp(dest, "http://") ||
179 !prefixcmp(dest, "https://"))
180 sender = "http-push";
181 else if (thin)
182 argv[dest_argc++] = "--thin";
183 argv[0] = sender;
184 argv[dest_argc++] = dest;
185 while (dest_refspec_nr--)
186 argv[dest_argc++] = *dest_refspec++;
187 argv[dest_argc] = NULL;
188 if (verbose)
189 fprintf(stderr, "Pushing to %s\n", dest);
190 err = run_command_v_opt(argv, RUN_GIT_CMD);
191 if (!err)
192 continue;
193
194 error("failed to push to '%s'", remote->uri[i]);
195 switch (err) {
196 case -ERR_RUN_COMMAND_FORK:
197 error("unable to fork for %s", sender);
198 case -ERR_RUN_COMMAND_EXEC:
199 error("unable to exec %s", sender);
200 break;
201 case -ERR_RUN_COMMAND_WAITPID:
202 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
203 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
204 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
205 error("%s died with strange error", sender);
206 }
207 errs++;
208 }
209 return !!errs;
210 }
211
212 int cmd_push(int argc, const char **argv, const char *prefix)
213 {
214 int i;
215 const char *repo = NULL; /* default repository */
216
217 for (i = 1; i < argc; i++) {
218 const char *arg = argv[i];
219
220 if (arg[0] != '-') {
221 repo = arg;
222 i++;
223 break;
224 }
225 if (!strcmp(arg, "-v")) {
226 verbose=1;
227 continue;
228 }
229 if (!prefixcmp(arg, "--repo=")) {
230 repo = arg+7;
231 continue;
232 }
233 if (!strcmp(arg, "--all")) {
234 all = 1;
235 continue;
236 }
237 if (!strcmp(arg, "--tags")) {
238 tags = 1;
239 continue;
240 }
241 if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
242 force = 1;
243 continue;
244 }
245 if (!strcmp(arg, "--thin")) {
246 thin = 1;
247 continue;
248 }
249 if (!strcmp(arg, "--no-thin")) {
250 thin = 0;
251 continue;
252 }
253 if (!prefixcmp(arg, "--receive-pack=")) {
254 receivepack = arg;
255 continue;
256 }
257 if (!prefixcmp(arg, "--exec=")) {
258 receivepack = arg;
259 continue;
260 }
261 usage(push_usage);
262 }
263 set_refspecs(argv + i, argc - i);
264 return do_push(repo);
265 }