]> asedeno.scripts.mit.edu Git - git.git/blob - transport.c
Add fetch methods to transport library.
[git.git] / transport.c
1 #include "cache.h"
2 #include "transport.h"
3 #include "run-command.h"
4 #include "http.h"
5 #include "pkt-line.h"
6 #include "fetch-pack.h"
7 #include "walker.h"
8
9 /* Generic functions for using commit walkers */
10
11 static int fetch_objs_via_walker(const struct transport *transport,
12                                  int nr_objs, char **objs)
13 {
14         char *dest = xstrdup(transport->url);
15         struct walker *walker = transport->data;
16
17         walker->get_all = 1;
18         walker->get_tree = 1;
19         walker->get_history = 1;
20         walker->get_verbosely = transport->verbose;
21         walker->get_recover = 0;
22
23         if (walker_fetch(walker, nr_objs, objs, NULL, dest))
24                 die("Fetch failed.");
25
26         free(dest);
27         return 0;
28 }
29
30 static int disconnect_walker(struct transport *transport)
31 {
32         struct walker *walker = transport->data;
33         if (walker)
34                 walker_free(walker);
35         return 0;
36 }
37
38 static const struct transport_ops rsync_transport;
39
40 static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
41         const char **argv;
42         int argc;
43         int err;
44
45         argv = xmalloc((refspec_nr + 11) * sizeof(char *));
46         argv[0] = "http-push";
47         argc = 1;
48         if (flags & TRANSPORT_PUSH_ALL)
49                 argv[argc++] = "--all";
50         if (flags & TRANSPORT_PUSH_FORCE)
51                 argv[argc++] = "--force";
52         argv[argc++] = transport->url;
53         while (refspec_nr--)
54                 argv[argc++] = *refspec++;
55         argv[argc] = NULL;
56         err = run_command_v_opt(argv, RUN_GIT_CMD);
57         switch (err) {
58         case -ERR_RUN_COMMAND_FORK:
59                 error("unable to fork for %s", argv[0]);
60         case -ERR_RUN_COMMAND_EXEC:
61                 error("unable to exec %s", argv[0]);
62                 break;
63         case -ERR_RUN_COMMAND_WAITPID:
64         case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
65         case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
66         case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
67                 error("%s died with strange error", argv[0]);
68         }
69         return !!err;
70 }
71
72 #ifndef NO_CURL
73 static int missing__target(int code, int result)
74 {
75         return  /* file:// URL -- do we ever use one??? */
76                 (result == CURLE_FILE_COULDNT_READ_FILE) ||
77                 /* http:// and https:// URL */
78                 (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
79                 /* ftp:// URL */
80                 (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
81                 ;
82 }
83
84 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
85
86 static struct ref *get_refs_via_curl(const struct transport *transport)
87 {
88         struct buffer buffer;
89         char *data, *start, *mid;
90         char *ref_name;
91         char *refs_url;
92         int i = 0;
93
94         struct active_request_slot *slot;
95         struct slot_results results;
96
97         struct ref *refs = NULL;
98         struct ref *ref = NULL;
99         struct ref *last_ref = NULL;
100
101         data = xmalloc(4096);
102         buffer.size = 4096;
103         buffer.posn = 0;
104         buffer.buffer = data;
105
106         refs_url = xmalloc(strlen(transport->url) + 11);
107         sprintf(refs_url, "%s/info/refs", transport->url);
108
109         http_init();
110
111         slot = get_active_slot();
112         slot->results = &results;
113         curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
114         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
115         curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
116         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
117         if (start_active_slot(slot)) {
118                 run_active_slot(slot);
119                 if (results.curl_result != CURLE_OK) {
120                         if (missing_target(&results)) {
121                                 free(buffer.buffer);
122                                 return NULL;
123                         } else {
124                                 free(buffer.buffer);
125                                 error("%s", curl_errorstr);
126                                 return NULL;
127                         }
128                 }
129         } else {
130                 free(buffer.buffer);
131                 error("Unable to start request");
132                 return NULL;
133         }
134
135         http_cleanup();
136
137         data = buffer.buffer;
138         start = NULL;
139         mid = data;
140         while (i < buffer.posn) {
141                 if (!start)
142                         start = &data[i];
143                 if (data[i] == '\t')
144                         mid = &data[i];
145                 if (data[i] == '\n') {
146                         data[i] = 0;
147                         ref_name = mid + 1;
148                         ref = xmalloc(sizeof(struct ref) +
149                                       strlen(ref_name) + 1);
150                         memset(ref, 0, sizeof(struct ref));
151                         strcpy(ref->name, ref_name);
152                         get_sha1_hex(start, ref->old_sha1);
153                         if (!refs)
154                                 refs = ref;
155                         if (last_ref)
156                                 last_ref->next = ref;
157                         last_ref = ref;
158                         start = NULL;
159                 }
160                 i++;
161         }
162
163         free(buffer.buffer);
164
165         return refs;
166 }
167
168 #else
169
170 static struct ref *get_refs_via_curl(const struct transport *transport)
171 {
172         die("Cannot fetch from '%s' without curl ...", transport->url);
173         return NULL;
174 }
175
176 #endif
177
178 static const struct transport_ops curl_transport = {
179         /* set_option */        NULL,
180         /* get_refs_list */     get_refs_via_curl,
181         /* fetch_refs */        NULL,
182         /* fetch_objs */        fetch_objs_via_walker,
183         /* push */              curl_transport_push,
184         /* disconnect */        disconnect_walker
185 };
186
187 static const struct transport_ops bundle_transport = {
188 };
189
190 struct git_transport_data {
191         unsigned thin : 1;
192         unsigned keep : 1;
193
194         int unpacklimit;
195
196         int depth;
197
198         const char *uploadpack;
199         const char *receivepack;
200 };
201
202 static int set_git_option(struct transport *connection,
203                           const char *name, const char *value)
204 {
205         struct git_transport_data *data = connection->data;
206         if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
207                 data->uploadpack = value;
208                 return 0;
209         } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
210                 data->receivepack = value;
211                 return 0;
212         } else if (!strcmp(name, TRANS_OPT_THIN)) {
213                 data->thin = !!value;
214                 return 0;
215         } else if (!strcmp(name, TRANS_OPT_KEEP)) {
216                 data->keep = !!value;
217                 return 0;
218         } else if (!strcmp(name, TRANS_OPT_UNPACKLIMIT)) {
219                 data->unpacklimit = atoi(value);
220                 return 0;
221         } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
222                 if (!value)
223                         data->depth = 0;
224                 else
225                         data->depth = atoi(value);
226                 return 0;
227         }
228         return 1;
229 }
230
231 static struct ref *get_refs_via_connect(const struct transport *transport)
232 {
233         struct git_transport_data *data = transport->data;
234         struct ref *refs;
235         int fd[2];
236         pid_t pid;
237         char *dest = xstrdup(transport->url);
238
239         pid = git_connect(fd, dest, data->uploadpack, 0);
240
241         if (pid < 0)
242                 die("Failed to connect to \"%s\"", transport->url);
243
244         get_remote_heads(fd[0], &refs, 0, NULL, 0);
245         packet_flush(fd[1]);
246
247         finish_connect(pid);
248
249         free(dest);
250
251         return refs;
252 }
253
254 static int fetch_refs_via_pack(const struct transport *transport,
255                                int nr_heads, char **heads)
256 {
257         struct git_transport_data *data = transport->data;
258         struct ref *refs;
259         char *dest = xstrdup(transport->url);
260         struct fetch_pack_args args;
261
262         args.uploadpack = data->uploadpack;
263         args.quiet = 0;
264         args.keep_pack = data->keep;
265         args.unpacklimit = data->unpacklimit;
266         args.use_thin_pack = data->thin;
267         args.fetch_all = 0;
268         args.verbose = transport->verbose;
269         args.depth = data->depth;
270         args.no_progress = 0;
271
272         setup_fetch_pack(&args);
273
274         refs = fetch_pack(dest, nr_heads, heads);
275
276         // ???? check that refs got everything?
277
278         /* free the memory used for the refs list ... */
279
280         free_refs(refs);
281
282         free(dest);
283         return 0;
284 }
285
286 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
287         struct git_transport_data *data = transport->data;
288         const char **argv;
289         char *rem;
290         int argc;
291         int err;
292
293         argv = xmalloc((refspec_nr + 11) * sizeof(char *));
294         argv[0] = "send-pack";
295         argc = 1;
296         if (flags & TRANSPORT_PUSH_ALL)
297                 argv[argc++] = "--all";
298         if (flags & TRANSPORT_PUSH_FORCE)
299                 argv[argc++] = "--force";
300         if (data->receivepack) {
301                 char *rp = xmalloc(strlen(data->receivepack) + 16);
302                 sprintf(rp, "--receive-pack=%s", data->receivepack);
303                 argv[argc++] = rp;
304         }
305         if (data->thin)
306                 argv[argc++] = "--thin";
307         rem = xmalloc(strlen(transport->remote->name) + 10);
308         sprintf(rem, "--remote=%s", transport->remote->name);
309         argv[argc++] = rem;
310         argv[argc++] = transport->url;
311         while (refspec_nr--)
312                 argv[argc++] = *refspec++;
313         argv[argc] = NULL;
314         err = run_command_v_opt(argv, RUN_GIT_CMD);
315         switch (err) {
316         case -ERR_RUN_COMMAND_FORK:
317                 error("unable to fork for %s", argv[0]);
318         case -ERR_RUN_COMMAND_EXEC:
319                 error("unable to exec %s", argv[0]);
320                 break;
321         case -ERR_RUN_COMMAND_WAITPID:
322         case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
323         case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
324         case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
325                 error("%s died with strange error", argv[0]);
326         }
327         return !!err;
328 }
329
330 static const struct transport_ops git_transport = {
331         /* set_option */        set_git_option,
332         /* get_refs_list */     get_refs_via_connect,
333         /* fetch_refs */        fetch_refs_via_pack,
334         /* fetch_objs */        NULL,
335         /* push */              git_transport_push
336 };
337
338 static int is_local(const char *url)
339 {
340         const char *colon = strchr(url, ':');
341         const char *slash = strchr(url, '/');
342         return !colon || (slash && slash < colon);
343 }
344
345 static int is_file(const char *url)
346 {
347         struct stat buf;
348         if (stat(url, &buf))
349                 return 0;
350         return S_ISREG(buf.st_mode);
351 }
352
353 struct transport *transport_get(struct remote *remote, const char *url,
354                                 int fetch)
355 {
356         struct transport *ret = NULL;
357         if (!prefixcmp(url, "rsync://")) {
358                 ret = xmalloc(sizeof(*ret));
359                 ret->data = NULL;
360                 ret->ops = &rsync_transport;
361         } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") ||
362                    !prefixcmp(url, "ftp://")) {
363                 ret = xmalloc(sizeof(*ret));
364                 ret->ops = &curl_transport;
365                 if (fetch)
366                         ret->data = get_http_walker(url);
367                 else
368                         ret->data = NULL;
369         } else if (is_local(url) && is_file(url)) {
370                 ret = xmalloc(sizeof(*ret));
371                 ret->data = NULL;
372                 ret->ops = &bundle_transport;
373         } else {
374                 struct git_transport_data *data = xcalloc(1, sizeof(*data));
375                 ret = xcalloc(1, sizeof(*ret));
376                 ret->data = data;
377                 data->thin = 1;
378                 data->uploadpack = "git-upload-pack";
379                 if (remote && remote->uploadpack)
380                         data->uploadpack = remote->uploadpack;
381                 data->receivepack = "git-receive-pack";
382                 if (remote && remote->receivepack)
383                         data->receivepack = remote->receivepack;
384                 data->unpacklimit = -1;
385                 ret->ops = &git_transport;
386         }
387         if (ret) {
388                 ret->remote = remote;
389                 ret->url = url;
390                 ret->remote_refs = NULL;
391                 ret->fetch = !!fetch;
392         }
393         return ret;
394 }
395
396 int transport_set_option(struct transport *transport,
397                          const char *name, const char *value)
398 {
399         int ret = 1;
400         if (transport->ops->set_option)
401                 ret = transport->ops->set_option(transport, name, value);
402         if (ret < 0)
403                 fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n",
404                         transport->url, name, value);
405         if (ret > 0)
406                 fprintf(stderr, "For '%s' option %s is ignored\n",
407                         transport->url, name);
408         return ret;
409 }
410
411 int transport_push(struct transport *transport,
412                    int refspec_nr, const char **refspec, int flags)
413 {
414         if (!transport->ops->push)
415                 return 1;
416         return transport->ops->push(transport, refspec_nr, refspec, flags);
417 }
418
419 struct ref *transport_get_remote_refs(struct transport *transport)
420 {
421         if (!transport->remote_refs)
422                 transport->remote_refs =
423                         transport->ops->get_refs_list(transport);
424         return transport->remote_refs;
425 }
426
427 #define PACK_HEADS_CHUNK_COUNT 256
428
429 int transport_fetch_refs(struct transport *transport, struct ref *refs)
430 {
431         int i;
432         int nr_heads = 0;
433         char **heads = xmalloc(PACK_HEADS_CHUNK_COUNT * sizeof(char *));
434         struct ref *rm;
435         int use_objs = !transport->ops->fetch_refs;
436
437         for (rm = refs; rm; rm = rm->next) {
438                 if (rm->peer_ref &&
439                     !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
440                         continue;
441                 if (use_objs) {
442                         heads[nr_heads++] = xstrdup(sha1_to_hex(rm->old_sha1));
443                 } else {
444                         heads[nr_heads++] = xstrdup(rm->name);
445                 }
446                 if (nr_heads % PACK_HEADS_CHUNK_COUNT == 0)
447                         heads = xrealloc(heads,
448                                          (nr_heads + PACK_HEADS_CHUNK_COUNT) *
449                                          sizeof(char *));
450         }
451
452         if (use_objs) {
453                 if (transport->ops->fetch_objs(transport, nr_heads, heads))
454                         return -1;
455         } else {
456                 if (transport->ops->fetch_refs(transport, nr_heads, heads))
457                         return -1;
458         }
459
460         /* free the memory used for the heads list ... */
461         for (i = 0; i < nr_heads; i++)
462                 free(heads[i]);
463         free(heads);
464         return 0;
465 }
466
467 int transport_disconnect(struct transport *transport)
468 {
469         int ret = 0;
470         if (transport->ops->disconnect)
471                 ret = transport->ops->disconnect(transport);
472         free(transport);
473         return ret;
474 }