7 #define PREV_BUF_SIZE 4096
8 #define RANGE_HEADER_SIZE 30
10 static int commits_on_stdin
;
12 static int got_alternates
= -1;
13 static int corrupt_object_found
;
15 static struct curl_slist
*no_pragma_header
;
21 struct packed_git
*packs
;
22 struct alt_base
*next
;
25 static struct alt_base
*alt
;
27 enum object_request_state
{
36 unsigned char sha1
[20];
37 struct alt_base
*repo
;
39 char filename
[PATH_MAX
];
40 char tmpfile
[PATH_MAX
];
42 enum object_request_state state
;
44 char errorstr
[CURL_ERROR_SIZE
];
46 unsigned char real_sha1
[20];
51 struct active_request_slot
*slot
;
52 struct object_request
*next
;
55 struct alternates_request
{
58 struct buffer
*buffer
;
59 struct active_request_slot
*slot
;
63 static struct object_request
*object_queue_head
;
65 static size_t fwrite_sha1_file(void *ptr
, size_t eltsize
, size_t nmemb
,
68 unsigned char expn
[4096];
69 size_t size
= eltsize
* nmemb
;
71 struct object_request
*obj_req
= (struct object_request
*)data
;
73 ssize_t retval
= xwrite(obj_req
->local
,
74 (char *) ptr
+ posn
, size
- posn
);
78 } while (posn
< size
);
80 obj_req
->stream
.avail_in
= size
;
81 obj_req
->stream
.next_in
= ptr
;
83 obj_req
->stream
.next_out
= expn
;
84 obj_req
->stream
.avail_out
= sizeof(expn
);
85 obj_req
->zret
= inflate(&obj_req
->stream
, Z_SYNC_FLUSH
);
86 SHA1_Update(&obj_req
->c
, expn
,
87 sizeof(expn
) - obj_req
->stream
.avail_out
);
88 } while (obj_req
->stream
.avail_in
&& obj_req
->zret
== Z_OK
);
93 static int missing__target(int code
, int result
)
95 return /* file:// URL -- do we ever use one??? */
96 (result
== CURLE_FILE_COULDNT_READ_FILE
) ||
97 /* http:// and https:// URL */
98 (code
== 404 && result
== CURLE_HTTP_RETURNED_ERROR
) ||
100 (code
== 550 && result
== CURLE_FTP_COULDNT_RETR_FILE
)
104 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
106 static void fetch_alternates(const char *base
);
108 static void process_object_response(void *callback_data
);
110 static void start_object_request(struct object_request
*obj_req
)
112 char *hex
= sha1_to_hex(obj_req
->sha1
);
113 char prevfile
[PATH_MAX
];
117 unsigned char prev_buf
[PREV_BUF_SIZE
];
118 ssize_t prev_read
= 0;
120 char range
[RANGE_HEADER_SIZE
];
121 struct curl_slist
*range_header
= NULL
;
122 struct active_request_slot
*slot
;
124 snprintf(prevfile
, sizeof(prevfile
), "%s.prev", obj_req
->filename
);
126 rename(obj_req
->tmpfile
, prevfile
);
127 unlink(obj_req
->tmpfile
);
129 if (obj_req
->local
!= -1)
130 error("fd leakage in start: %d", obj_req
->local
);
131 obj_req
->local
= open(obj_req
->tmpfile
,
132 O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
133 /* This could have failed due to the "lazy directory creation";
134 * try to mkdir the last path component.
136 if (obj_req
->local
< 0 && errno
== ENOENT
) {
137 char *dir
= strrchr(obj_req
->tmpfile
, '/');
140 mkdir(obj_req
->tmpfile
, 0777);
143 obj_req
->local
= open(obj_req
->tmpfile
,
144 O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
147 if (obj_req
->local
< 0) {
148 obj_req
->state
= ABORTED
;
149 error("Couldn't create temporary file %s for %s: %s",
150 obj_req
->tmpfile
, obj_req
->filename
, strerror(errno
));
154 memset(&obj_req
->stream
, 0, sizeof(obj_req
->stream
));
156 inflateInit(&obj_req
->stream
);
158 SHA1_Init(&obj_req
->c
);
160 url
= xmalloc(strlen(obj_req
->repo
->base
) + 51);
161 obj_req
->url
= xmalloc(strlen(obj_req
->repo
->base
) + 51);
162 strcpy(url
, obj_req
->repo
->base
);
163 posn
= url
+ strlen(obj_req
->repo
->base
);
164 strcpy(posn
, "/objects/");
166 memcpy(posn
, hex
, 2);
169 strcpy(posn
, hex
+ 2);
170 strcpy(obj_req
->url
, url
);
172 /* If a previous temp file is present, process what was already
174 prevlocal
= open(prevfile
, O_RDONLY
);
175 if (prevlocal
!= -1) {
177 prev_read
= xread(prevlocal
, prev_buf
, PREV_BUF_SIZE
);
179 if (fwrite_sha1_file(prev_buf
,
182 obj_req
) == prev_read
) {
183 prev_posn
+= prev_read
;
188 } while (prev_read
> 0);
193 /* Reset inflate/SHA1 if there was an error reading the previous temp
194 file; also rewind to the beginning of the local file. */
195 if (prev_read
== -1) {
196 memset(&obj_req
->stream
, 0, sizeof(obj_req
->stream
));
197 inflateInit(&obj_req
->stream
);
198 SHA1_Init(&obj_req
->c
);
201 lseek(obj_req
->local
, 0, SEEK_SET
);
202 ftruncate(obj_req
->local
, 0);
206 slot
= get_active_slot();
207 slot
->callback_func
= process_object_response
;
208 slot
->callback_data
= obj_req
;
209 obj_req
->slot
= slot
;
211 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, obj_req
);
212 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_sha1_file
);
213 curl_easy_setopt(slot
->curl
, CURLOPT_ERRORBUFFER
, obj_req
->errorstr
);
214 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
215 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
217 /* If we have successfully processed data from a previous fetch
218 attempt, only fetch the data we don't already have. */
222 "Resuming fetch of object %s at byte %ld\n",
224 sprintf(range
, "Range: bytes=%ld-", prev_posn
);
225 range_header
= curl_slist_append(range_header
, range
);
226 curl_easy_setopt(slot
->curl
,
227 CURLOPT_HTTPHEADER
, range_header
);
230 /* Try to get the request started, abort the request on error */
231 obj_req
->state
= ACTIVE
;
232 if (!start_active_slot(slot
)) {
233 obj_req
->state
= ABORTED
;
234 obj_req
->slot
= NULL
;
235 close(obj_req
->local
); obj_req
->local
= -1;
241 static void finish_object_request(struct object_request
*obj_req
)
245 fchmod(obj_req
->local
, 0444);
246 close(obj_req
->local
); obj_req
->local
= -1;
248 if (obj_req
->http_code
== 416) {
249 fprintf(stderr
, "Warning: requested range invalid; we may already have all the data.\n");
250 } else if (obj_req
->curl_result
!= CURLE_OK
) {
251 if (stat(obj_req
->tmpfile
, &st
) == 0)
253 unlink(obj_req
->tmpfile
);
257 inflateEnd(&obj_req
->stream
);
258 SHA1_Final(obj_req
->real_sha1
, &obj_req
->c
);
259 if (obj_req
->zret
!= Z_STREAM_END
) {
260 unlink(obj_req
->tmpfile
);
263 if (hashcmp(obj_req
->sha1
, obj_req
->real_sha1
)) {
264 unlink(obj_req
->tmpfile
);
268 move_temp_to_file(obj_req
->tmpfile
, obj_req
->filename
);
270 if (obj_req
->rename
== 0)
271 pull_say("got %s\n", sha1_to_hex(obj_req
->sha1
));
274 static void process_object_response(void *callback_data
)
276 struct object_request
*obj_req
=
277 (struct object_request
*)callback_data
;
279 obj_req
->curl_result
= obj_req
->slot
->curl_result
;
280 obj_req
->http_code
= obj_req
->slot
->http_code
;
281 obj_req
->slot
= NULL
;
282 obj_req
->state
= COMPLETE
;
284 /* Use alternates if necessary */
285 if (missing_target(obj_req
)) {
286 fetch_alternates(alt
->base
);
287 if (obj_req
->repo
->next
!= NULL
) {
290 close(obj_req
->local
);
292 start_object_request(obj_req
);
297 finish_object_request(obj_req
);
300 static void release_object_request(struct object_request
*obj_req
)
302 struct object_request
*entry
= object_queue_head
;
304 if (obj_req
->local
!= -1)
305 error("fd leakage in release: %d", obj_req
->local
);
306 if (obj_req
== object_queue_head
) {
307 object_queue_head
= obj_req
->next
;
309 while (entry
->next
!= NULL
&& entry
->next
!= obj_req
)
311 if (entry
->next
== obj_req
)
312 entry
->next
= entry
->next
->next
;
319 #ifdef USE_CURL_MULTI
320 static int fill_active_slot(void *unused
)
322 struct object_request
*obj_req
;
324 for (obj_req
= object_queue_head
; obj_req
; obj_req
= obj_req
->next
) {
325 if (obj_req
->state
== WAITING
) {
326 if (has_sha1_file(obj_req
->sha1
))
327 obj_req
->state
= COMPLETE
;
329 start_object_request(obj_req
);
338 void prefetch(unsigned char *sha1
)
340 struct object_request
*newreq
;
341 struct object_request
*tail
;
342 char *filename
= sha1_file_name(sha1
);
344 newreq
= xmalloc(sizeof(*newreq
));
345 hashcpy(newreq
->sha1
, sha1
);
349 newreq
->state
= WAITING
;
350 snprintf(newreq
->filename
, sizeof(newreq
->filename
), "%s", filename
);
351 snprintf(newreq
->tmpfile
, sizeof(newreq
->tmpfile
),
352 "%s.temp", filename
);
356 if (object_queue_head
== NULL
) {
357 object_queue_head
= newreq
;
359 tail
= object_queue_head
;
360 while (tail
->next
!= NULL
) {
366 #ifdef USE_CURL_MULTI
372 static int fetch_index(struct alt_base
*repo
, unsigned char *sha1
)
374 char *hex
= sha1_to_hex(sha1
);
377 char tmpfile
[PATH_MAX
];
379 char range
[RANGE_HEADER_SIZE
];
380 struct curl_slist
*range_header
= NULL
;
383 struct active_request_slot
*slot
;
384 struct slot_results results
;
386 if (has_pack_index(sha1
))
390 fprintf(stderr
, "Getting index for pack %s\n", hex
);
392 url
= xmalloc(strlen(repo
->base
) + 64);
393 sprintf(url
, "%s/objects/pack/pack-%s.idx", repo
->base
, hex
);
395 filename
= sha1_pack_index_name(sha1
);
396 snprintf(tmpfile
, sizeof(tmpfile
), "%s.temp", filename
);
397 indexfile
= fopen(tmpfile
, "a");
399 return error("Unable to open local file %s for pack index",
402 slot
= get_active_slot();
403 slot
->results
= &results
;
404 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, indexfile
);
405 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
406 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
407 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
408 slot
->local
= indexfile
;
410 /* If there is data present from a previous transfer attempt,
411 resume where it left off */
412 prev_posn
= ftell(indexfile
);
416 "Resuming fetch of index for pack %s at byte %ld\n",
418 sprintf(range
, "Range: bytes=%ld-", prev_posn
);
419 range_header
= curl_slist_append(range_header
, range
);
420 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, range_header
);
423 if (start_active_slot(slot
)) {
424 run_active_slot(slot
);
425 if (results
.curl_result
!= CURLE_OK
) {
427 return error("Unable to get pack index %s\n%s", url
,
432 return error("Unable to start request");
437 return move_temp_to_file(tmpfile
, filename
);
440 static int setup_index(struct alt_base
*repo
, unsigned char *sha1
)
442 struct packed_git
*new_pack
;
443 if (has_pack_file(sha1
))
444 return 0; /* don't list this as something we can get */
446 if (fetch_index(repo
, sha1
))
449 new_pack
= parse_pack_index(sha1
);
450 new_pack
->next
= repo
->packs
;
451 repo
->packs
= new_pack
;
455 static void process_alternates_response(void *callback_data
)
457 struct alternates_request
*alt_req
=
458 (struct alternates_request
*)callback_data
;
459 struct active_request_slot
*slot
= alt_req
->slot
;
460 struct alt_base
*tail
= alt
;
461 const char *base
= alt_req
->base
;
462 static const char null_byte
= '\0';
466 if (alt_req
->http_specific
) {
467 if (slot
->curl_result
!= CURLE_OK
||
468 !alt_req
->buffer
->posn
) {
470 /* Try reusing the slot to get non-http alternates */
471 alt_req
->http_specific
= 0;
472 sprintf(alt_req
->url
, "%s/objects/info/alternates",
474 curl_easy_setopt(slot
->curl
, CURLOPT_URL
,
478 if (slot
->finished
!= NULL
)
479 (*slot
->finished
) = 0;
480 if (!start_active_slot(slot
)) {
483 if (slot
->finished
!= NULL
)
484 (*slot
->finished
) = 1;
488 } else if (slot
->curl_result
!= CURLE_OK
) {
489 if (!missing_target(slot
)) {
495 fwrite_buffer(&null_byte
, 1, 1, alt_req
->buffer
);
496 alt_req
->buffer
->posn
--;
497 data
= alt_req
->buffer
->buffer
;
499 while (i
< alt_req
->buffer
->posn
) {
501 while (posn
< alt_req
->buffer
->posn
&& data
[posn
] != '\n')
503 if (data
[posn
] == '\n') {
506 struct alt_base
*newalt
;
508 if (data
[i
] == '/') {
510 * http://git.host/pub/scm/linux.git/
512 * so memcpy(dst, base, serverlen) will
513 * copy up to "...git.host".
515 const char *colon_ss
= strstr(base
,"://");
517 serverlen
= (strchr(colon_ss
+ 3, '/')
521 } else if (!memcmp(data
+ i
, "../", 3)) {
522 /* Relative URL; chop the corresponding
523 * number of subpath from base (and ../
524 * from data), and concatenate the result.
526 * The code first drops ../ from data, and
527 * then drops one ../ from data and one path
528 * from base. IOW, one extra ../ is dropped
529 * from data than path is dropped from base.
531 * This is not wrong. The alternate in
532 * http://git.host/pub/scm/linux.git/
534 * http://git.host/pub/scm/linus.git/
535 * is ../../linus.git/objects/. You need
536 * two ../../ to borrow from your direct
540 serverlen
= strlen(base
);
541 while (i
+ 2 < posn
&&
542 !memcmp(data
+ i
, "../", 3)) {
545 } while (serverlen
&&
546 base
[serverlen
- 1] != '/');
549 /* If the server got removed, give up. */
550 okay
= strchr(base
, ':') - base
+ 3 <
552 } else if (alt_req
->http_specific
) {
553 char *colon
= strchr(data
+ i
, ':');
554 char *slash
= strchr(data
+ i
, '/');
555 if (colon
&& slash
&& colon
< data
+ posn
&&
556 slash
< data
+ posn
&& colon
< slash
) {
560 /* skip "objects\n" at end */
562 target
= xmalloc(serverlen
+ posn
- i
- 6);
563 memcpy(target
, base
, serverlen
);
564 memcpy(target
+ serverlen
, data
+ i
,
566 target
[serverlen
+ posn
- i
- 7] = 0;
569 "Also look at %s\n", target
);
570 newalt
= xmalloc(sizeof(*newalt
));
572 newalt
->base
= target
;
573 newalt
->got_indices
= 0;
574 newalt
->packs
= NULL
;
576 while (tail
->next
!= NULL
)
587 static void fetch_alternates(const char *base
)
589 struct buffer buffer
;
592 struct active_request_slot
*slot
;
593 struct alternates_request alt_req
;
595 /* If another request has already started fetching alternates,
596 wait for them to arrive and return to processing this request's
598 #ifdef USE_CURL_MULTI
599 while (got_alternates
== 0) {
604 /* Nothing to do if they've already been fetched */
605 if (got_alternates
== 1)
608 /* Start the fetch */
611 data
= xmalloc(4096);
614 buffer
.buffer
= data
;
617 fprintf(stderr
, "Getting alternates list for %s\n", base
);
619 url
= xmalloc(strlen(base
) + 31);
620 sprintf(url
, "%s/objects/info/http-alternates", base
);
622 /* Use a callback to process the result, since another request
623 may fail and need to have alternates loaded before continuing */
624 slot
= get_active_slot();
625 slot
->callback_func
= process_alternates_response
;
626 slot
->callback_data
= &alt_req
;
628 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
629 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
630 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
634 alt_req
.buffer
= &buffer
;
635 alt_req
.http_specific
= 1;
638 if (start_active_slot(slot
))
639 run_active_slot(slot
);
647 static int fetch_indices(struct alt_base
*repo
)
649 unsigned char sha1
[20];
651 struct buffer buffer
;
655 struct active_request_slot
*slot
;
656 struct slot_results results
;
658 if (repo
->got_indices
)
661 data
= xmalloc(4096);
664 buffer
.buffer
= data
;
667 fprintf(stderr
, "Getting pack list for %s\n", repo
->base
);
669 url
= xmalloc(strlen(repo
->base
) + 21);
670 sprintf(url
, "%s/objects/info/packs", repo
->base
);
672 slot
= get_active_slot();
673 slot
->results
= &results
;
674 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
675 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
676 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
677 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, NULL
);
678 if (start_active_slot(slot
)) {
679 run_active_slot(slot
);
680 if (results
.curl_result
!= CURLE_OK
) {
681 if (missing_target(&results
)) {
682 repo
->got_indices
= 1;
686 repo
->got_indices
= 0;
688 return error("%s", curl_errorstr
);
692 repo
->got_indices
= 0;
694 return error("Unable to start request");
697 data
= buffer
.buffer
;
698 while (i
< buffer
.posn
) {
702 if (i
+ 52 <= buffer
.posn
&&
703 !prefixcmp(data
+ i
, " pack-") &&
704 !prefixcmp(data
+ i
+ 46, ".pack\n")) {
705 get_sha1_hex(data
+ i
+ 6, sha1
);
706 setup_index(repo
, sha1
);
711 while (i
< buffer
.posn
&& data
[i
] != '\n')
718 repo
->got_indices
= 1;
722 static int fetch_pack(struct alt_base
*repo
, unsigned char *sha1
)
725 struct packed_git
*target
;
726 struct packed_git
**lst
;
729 char tmpfile
[PATH_MAX
];
732 char range
[RANGE_HEADER_SIZE
];
733 struct curl_slist
*range_header
= NULL
;
735 struct active_request_slot
*slot
;
736 struct slot_results results
;
738 if (fetch_indices(repo
))
740 target
= find_sha1_pack(sha1
, repo
->packs
);
745 fprintf(stderr
, "Getting pack %s\n",
746 sha1_to_hex(target
->sha1
));
747 fprintf(stderr
, " which contains %s\n",
751 url
= xmalloc(strlen(repo
->base
) + 65);
752 sprintf(url
, "%s/objects/pack/pack-%s.pack",
753 repo
->base
, sha1_to_hex(target
->sha1
));
755 filename
= sha1_pack_name(target
->sha1
);
756 snprintf(tmpfile
, sizeof(tmpfile
), "%s.temp", filename
);
757 packfile
= fopen(tmpfile
, "a");
759 return error("Unable to open local file %s for pack",
762 slot
= get_active_slot();
763 slot
->results
= &results
;
764 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, packfile
);
765 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
766 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
767 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
768 slot
->local
= packfile
;
770 /* If there is data present from a previous transfer attempt,
771 resume where it left off */
772 prev_posn
= ftell(packfile
);
776 "Resuming fetch of pack %s at byte %ld\n",
777 sha1_to_hex(target
->sha1
), prev_posn
);
778 sprintf(range
, "Range: bytes=%ld-", prev_posn
);
779 range_header
= curl_slist_append(range_header
, range
);
780 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, range_header
);
783 if (start_active_slot(slot
)) {
784 run_active_slot(slot
);
785 if (results
.curl_result
!= CURLE_OK
) {
787 return error("Unable to get pack file %s\n%s", url
,
792 return error("Unable to start request");
795 target
->pack_size
= ftell(packfile
);
798 ret
= move_temp_to_file(tmpfile
, filename
);
803 while (*lst
!= target
)
804 lst
= &((*lst
)->next
);
807 if (verify_pack(target
, 0))
809 install_packed_git(target
);
814 static void abort_object_request(struct object_request
*obj_req
)
816 if (obj_req
->local
>= 0) {
817 close(obj_req
->local
);
820 unlink(obj_req
->tmpfile
);
822 release_active_slot(obj_req
->slot
);
823 obj_req
->slot
= NULL
;
825 release_object_request(obj_req
);
828 static int fetch_object(struct alt_base
*repo
, unsigned char *sha1
)
830 char *hex
= sha1_to_hex(sha1
);
832 struct object_request
*obj_req
= object_queue_head
;
834 while (obj_req
!= NULL
&& hashcmp(obj_req
->sha1
, sha1
))
835 obj_req
= obj_req
->next
;
837 return error("Couldn't find request for %s in the queue", hex
);
839 if (has_sha1_file(obj_req
->sha1
)) {
840 abort_object_request(obj_req
);
844 #ifdef USE_CURL_MULTI
845 while (obj_req
->state
== WAITING
) {
849 start_object_request(obj_req
);
852 while (obj_req
->state
== ACTIVE
) {
853 run_active_slot(obj_req
->slot
);
855 if (obj_req
->local
!= -1) {
856 close(obj_req
->local
); obj_req
->local
= -1;
859 if (obj_req
->state
== ABORTED
) {
860 ret
= error("Request for %s aborted", hex
);
861 } else if (obj_req
->curl_result
!= CURLE_OK
&&
862 obj_req
->http_code
!= 416) {
863 if (missing_target(obj_req
))
864 ret
= -1; /* Be silent, it is probably in a pack. */
866 ret
= error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
867 obj_req
->errorstr
, obj_req
->curl_result
,
868 obj_req
->http_code
, hex
);
869 } else if (obj_req
->zret
!= Z_STREAM_END
) {
870 corrupt_object_found
++;
871 ret
= error("File %s (%s) corrupt", hex
, obj_req
->url
);
872 } else if (hashcmp(obj_req
->sha1
, obj_req
->real_sha1
)) {
873 ret
= error("File %s has bad hash", hex
);
874 } else if (obj_req
->rename
< 0) {
875 ret
= error("unable to write sha1 filename %s",
879 release_object_request(obj_req
);
883 int fetch(unsigned char *sha1
)
885 struct alt_base
*altbase
= alt
;
887 if (!fetch_object(altbase
, sha1
))
890 if (!fetch_pack(altbase
, sha1
))
892 fetch_alternates(alt
->base
);
893 altbase
= altbase
->next
;
895 return error("Unable to find %s under %s", sha1_to_hex(sha1
),
899 static inline int needs_quote(int ch
)
901 if (((ch
>= 'A') && (ch
<= 'Z'))
902 || ((ch
>= 'a') && (ch
<= 'z'))
903 || ((ch
>= '0') && (ch
<= '9'))
911 static inline int hex(int v
)
913 if (v
< 10) return '0' + v
;
914 else return 'A' + v
- 10;
917 static char *quote_ref_url(const char *base
, const char *ref
)
921 int len
, baselen
, ch
;
923 baselen
= strlen(base
);
924 len
= baselen
+ 7; /* "/refs/" + NUL */
925 for (cp
= ref
; (ch
= *cp
) != 0; cp
++, len
++)
927 len
+= 2; /* extra two hex plus replacement % */
929 memcpy(qref
, base
, baselen
);
930 memcpy(qref
+ baselen
, "/refs/", 6);
931 for (cp
= ref
, dp
= qref
+ baselen
+ 6; (ch
= *cp
) != 0; cp
++) {
932 if (needs_quote(ch
)) {
934 *dp
++ = hex((ch
>> 4) & 0xF);
935 *dp
++ = hex(ch
& 0xF);
945 int fetch_ref(char *ref
, unsigned char *sha1
)
949 struct buffer buffer
;
950 const char *base
= alt
->base
;
951 struct active_request_slot
*slot
;
952 struct slot_results results
;
958 url
= quote_ref_url(base
, ref
);
959 slot
= get_active_slot();
960 slot
->results
= &results
;
961 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
962 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
963 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, NULL
);
964 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
965 if (start_active_slot(slot
)) {
966 run_active_slot(slot
);
967 if (results
.curl_result
!= CURLE_OK
)
968 return error("Couldn't get %s for %s\n%s",
969 url
, ref
, curl_errorstr
);
971 return error("Unable to start request");
975 get_sha1_hex(hex
, sha1
);
979 int main(int argc
, const char **argv
)
982 const char **write_ref
= NULL
;
989 setup_git_directory();
990 git_config(git_default_config
);
992 while (arg
< argc
&& argv
[arg
][0] == '-') {
993 if (argv
[arg
][1] == 't') {
995 } else if (argv
[arg
][1] == 'c') {
997 } else if (argv
[arg
][1] == 'a') {
1001 } else if (argv
[arg
][1] == 'v') {
1003 } else if (argv
[arg
][1] == 'w') {
1004 write_ref
= &argv
[arg
+ 1];
1006 } else if (!strcmp(argv
[arg
], "--recover")) {
1008 } else if (!strcmp(argv
[arg
], "--stdin")) {
1009 commits_on_stdin
= 1;
1013 if (argc
< arg
+ 2 - commits_on_stdin
) {
1014 usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
1017 if (commits_on_stdin
) {
1018 commits
= pull_targets_stdin(&commit_id
, &write_ref
);
1020 commit_id
= (char **) &argv
[arg
++];
1027 no_pragma_header
= curl_slist_append(no_pragma_header
, "Pragma:");
1029 alt
= xmalloc(sizeof(*alt
));
1030 alt
->base
= xmalloc(strlen(url
) + 1);
1031 strcpy(alt
->base
, url
);
1032 for (s
= alt
->base
+ strlen(alt
->base
) - 1; *s
== '/'; --s
)
1034 alt
->got_indices
= 0;
1038 #ifdef USE_CURL_MULTI
1039 add_fill_function(NULL
, fill_active_slot
);
1042 if (pull(commits
, commit_id
, write_ref
, url
))
1047 curl_slist_free_all(no_pragma_header
);
1049 if (commits_on_stdin
)
1050 pull_targets_free(commits
, commit_id
, write_ref
);
1052 if (corrupt_object_found
) {
1054 "Some loose object were found to be corrupt, but they might be just\n"
1055 "a false '404 Not Found' error message sent with incorrect HTTP\n"
1056 "status code. Suggest running git-fsck.\n");