]> asedeno.scripts.mit.edu Git - git.git/blob - builtin-push.c
Update local tracking refs when pushing
[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 {
182                         char *rem = xmalloc(strlen(remote->name) + 10);
183                         sprintf(rem, "--remote=%s", remote->name);
184                         argv[dest_argc++] = rem;
185                         if (thin)
186                                 argv[dest_argc++] = "--thin";
187                 }
188                 argv[0] = sender;
189                 argv[dest_argc++] = dest;
190                 while (dest_refspec_nr--)
191                         argv[dest_argc++] = *dest_refspec++;
192                 argv[dest_argc] = NULL;
193                 if (verbose)
194                         fprintf(stderr, "Pushing to %s\n", dest);
195                 err = run_command_v_opt(argv, RUN_GIT_CMD);
196                 if (!err)
197                         continue;
198
199                 error("failed to push to '%s'", remote->uri[i]);
200                 switch (err) {
201                 case -ERR_RUN_COMMAND_FORK:
202                         error("unable to fork for %s", sender);
203                 case -ERR_RUN_COMMAND_EXEC:
204                         error("unable to exec %s", sender);
205                         break;
206                 case -ERR_RUN_COMMAND_WAITPID:
207                 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
208                 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
209                 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
210                         error("%s died with strange error", sender);
211                 }
212                 errs++;
213         }
214         return !!errs;
215 }
216
217 int cmd_push(int argc, const char **argv, const char *prefix)
218 {
219         int i;
220         const char *repo = NULL;        /* default repository */
221
222         for (i = 1; i < argc; i++) {
223                 const char *arg = argv[i];
224
225                 if (arg[0] != '-') {
226                         repo = arg;
227                         i++;
228                         break;
229                 }
230                 if (!strcmp(arg, "-v")) {
231                         verbose=1;
232                         continue;
233                 }
234                 if (!prefixcmp(arg, "--repo=")) {
235                         repo = arg+7;
236                         continue;
237                 }
238                 if (!strcmp(arg, "--all")) {
239                         all = 1;
240                         continue;
241                 }
242                 if (!strcmp(arg, "--tags")) {
243                         tags = 1;
244                         continue;
245                 }
246                 if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
247                         force = 1;
248                         continue;
249                 }
250                 if (!strcmp(arg, "--thin")) {
251                         thin = 1;
252                         continue;
253                 }
254                 if (!strcmp(arg, "--no-thin")) {
255                         thin = 0;
256                         continue;
257                 }
258                 if (!prefixcmp(arg, "--receive-pack=")) {
259                         receivepack = arg;
260                         continue;
261                 }
262                 if (!prefixcmp(arg, "--exec=")) {
263                         receivepack = arg;
264                         continue;
265                 }
266                 usage(push_usage);
267         }
268         set_refspecs(argv + i, argc - i);
269         return do_push(repo);
270 }