]> asedeno.scripts.mit.edu Git - git.git/blob - builtin-push.c
Move remote parsing into a library file out of builtin-push.
[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 }