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 void fill_active_slots(void)
322 struct object_request
*obj_req
= object_queue_head
;
323 struct active_request_slot
*slot
= active_queue_head
;
326 while (active_requests
< max_requests
&& obj_req
!= NULL
) {
327 if (obj_req
->state
== WAITING
) {
328 if (has_sha1_file(obj_req
->sha1
))
329 obj_req
->state
= COMPLETE
;
331 start_object_request(obj_req
);
332 curl_multi_perform(curlm
, &num_transfers
);
334 obj_req
= obj_req
->next
;
337 while (slot
!= NULL
) {
338 if (!slot
->in_use
&& slot
->curl
!= NULL
) {
339 curl_easy_cleanup(slot
->curl
);
347 void prefetch(unsigned char *sha1
)
349 struct object_request
*newreq
;
350 struct object_request
*tail
;
351 char *filename
= sha1_file_name(sha1
);
353 newreq
= xmalloc(sizeof(*newreq
));
354 hashcpy(newreq
->sha1
, sha1
);
358 newreq
->state
= WAITING
;
359 snprintf(newreq
->filename
, sizeof(newreq
->filename
), "%s", filename
);
360 snprintf(newreq
->tmpfile
, sizeof(newreq
->tmpfile
),
361 "%s.temp", filename
);
365 if (object_queue_head
== NULL
) {
366 object_queue_head
= newreq
;
368 tail
= object_queue_head
;
369 while (tail
->next
!= NULL
) {
375 #ifdef USE_CURL_MULTI
381 static int fetch_index(struct alt_base
*repo
, unsigned char *sha1
)
383 char *hex
= sha1_to_hex(sha1
);
386 char tmpfile
[PATH_MAX
];
388 char range
[RANGE_HEADER_SIZE
];
389 struct curl_slist
*range_header
= NULL
;
392 struct active_request_slot
*slot
;
393 struct slot_results results
;
395 if (has_pack_index(sha1
))
399 fprintf(stderr
, "Getting index for pack %s\n", hex
);
401 url
= xmalloc(strlen(repo
->base
) + 64);
402 sprintf(url
, "%s/objects/pack/pack-%s.idx", repo
->base
, hex
);
404 filename
= sha1_pack_index_name(sha1
);
405 snprintf(tmpfile
, sizeof(tmpfile
), "%s.temp", filename
);
406 indexfile
= fopen(tmpfile
, "a");
408 return error("Unable to open local file %s for pack index",
411 slot
= get_active_slot();
412 slot
->results
= &results
;
413 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, indexfile
);
414 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
415 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
416 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
417 slot
->local
= indexfile
;
419 /* If there is data present from a previous transfer attempt,
420 resume where it left off */
421 prev_posn
= ftell(indexfile
);
425 "Resuming fetch of index for pack %s at byte %ld\n",
427 sprintf(range
, "Range: bytes=%ld-", prev_posn
);
428 range_header
= curl_slist_append(range_header
, range
);
429 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, range_header
);
432 if (start_active_slot(slot
)) {
433 run_active_slot(slot
);
434 if (results
.curl_result
!= CURLE_OK
) {
436 return error("Unable to get pack index %s\n%s", url
,
441 return error("Unable to start request");
446 return move_temp_to_file(tmpfile
, filename
);
449 static int setup_index(struct alt_base
*repo
, unsigned char *sha1
)
451 struct packed_git
*new_pack
;
452 if (has_pack_file(sha1
))
453 return 0; /* don't list this as something we can get */
455 if (fetch_index(repo
, sha1
))
458 new_pack
= parse_pack_index(sha1
);
459 new_pack
->next
= repo
->packs
;
460 repo
->packs
= new_pack
;
464 static void process_alternates_response(void *callback_data
)
466 struct alternates_request
*alt_req
=
467 (struct alternates_request
*)callback_data
;
468 struct active_request_slot
*slot
= alt_req
->slot
;
469 struct alt_base
*tail
= alt
;
470 const char *base
= alt_req
->base
;
471 static const char null_byte
= '\0';
475 if (alt_req
->http_specific
) {
476 if (slot
->curl_result
!= CURLE_OK
||
477 !alt_req
->buffer
->posn
) {
479 /* Try reusing the slot to get non-http alternates */
480 alt_req
->http_specific
= 0;
481 sprintf(alt_req
->url
, "%s/objects/info/alternates",
483 curl_easy_setopt(slot
->curl
, CURLOPT_URL
,
487 if (slot
->finished
!= NULL
)
488 (*slot
->finished
) = 0;
489 if (!start_active_slot(slot
)) {
492 if (slot
->finished
!= NULL
)
493 (*slot
->finished
) = 1;
497 } else if (slot
->curl_result
!= CURLE_OK
) {
498 if (!missing_target(slot
)) {
504 fwrite_buffer(&null_byte
, 1, 1, alt_req
->buffer
);
505 alt_req
->buffer
->posn
--;
506 data
= alt_req
->buffer
->buffer
;
508 while (i
< alt_req
->buffer
->posn
) {
510 while (posn
< alt_req
->buffer
->posn
&& data
[posn
] != '\n')
512 if (data
[posn
] == '\n') {
515 struct alt_base
*newalt
;
517 if (data
[i
] == '/') {
519 * http://git.host/pub/scm/linux.git/
521 * so memcpy(dst, base, serverlen) will
522 * copy up to "...git.host".
524 const char *colon_ss
= strstr(base
,"://");
526 serverlen
= (strchr(colon_ss
+ 3, '/')
530 } else if (!memcmp(data
+ i
, "../", 3)) {
531 /* Relative URL; chop the corresponding
532 * number of subpath from base (and ../
533 * from data), and concatenate the result.
535 * The code first drops ../ from data, and
536 * then drops one ../ from data and one path
537 * from base. IOW, one extra ../ is dropped
538 * from data than path is dropped from base.
540 * This is not wrong. The alternate in
541 * http://git.host/pub/scm/linux.git/
543 * http://git.host/pub/scm/linus.git/
544 * is ../../linus.git/objects/. You need
545 * two ../../ to borrow from your direct
549 serverlen
= strlen(base
);
550 while (i
+ 2 < posn
&&
551 !memcmp(data
+ i
, "../", 3)) {
554 } while (serverlen
&&
555 base
[serverlen
- 1] != '/');
558 /* If the server got removed, give up. */
559 okay
= strchr(base
, ':') - base
+ 3 <
561 } else if (alt_req
->http_specific
) {
562 char *colon
= strchr(data
+ i
, ':');
563 char *slash
= strchr(data
+ i
, '/');
564 if (colon
&& slash
&& colon
< data
+ posn
&&
565 slash
< data
+ posn
&& colon
< slash
) {
569 /* skip "objects\n" at end */
571 target
= xmalloc(serverlen
+ posn
- i
- 6);
572 memcpy(target
, base
, serverlen
);
573 memcpy(target
+ serverlen
, data
+ i
,
575 target
[serverlen
+ posn
- i
- 7] = 0;
578 "Also look at %s\n", target
);
579 newalt
= xmalloc(sizeof(*newalt
));
581 newalt
->base
= target
;
582 newalt
->got_indices
= 0;
583 newalt
->packs
= NULL
;
585 while (tail
->next
!= NULL
)
596 static void fetch_alternates(const char *base
)
598 struct buffer buffer
;
601 struct active_request_slot
*slot
;
602 struct alternates_request alt_req
;
604 /* If another request has already started fetching alternates,
605 wait for them to arrive and return to processing this request's
607 #ifdef USE_CURL_MULTI
608 while (got_alternates
== 0) {
613 /* Nothing to do if they've already been fetched */
614 if (got_alternates
== 1)
617 /* Start the fetch */
620 data
= xmalloc(4096);
623 buffer
.buffer
= data
;
626 fprintf(stderr
, "Getting alternates list for %s\n", base
);
628 url
= xmalloc(strlen(base
) + 31);
629 sprintf(url
, "%s/objects/info/http-alternates", base
);
631 /* Use a callback to process the result, since another request
632 may fail and need to have alternates loaded before continuing */
633 slot
= get_active_slot();
634 slot
->callback_func
= process_alternates_response
;
635 slot
->callback_data
= &alt_req
;
637 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
638 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
639 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
643 alt_req
.buffer
= &buffer
;
644 alt_req
.http_specific
= 1;
647 if (start_active_slot(slot
))
648 run_active_slot(slot
);
656 static int fetch_indices(struct alt_base
*repo
)
658 unsigned char sha1
[20];
660 struct buffer buffer
;
664 struct active_request_slot
*slot
;
665 struct slot_results results
;
667 if (repo
->got_indices
)
670 data
= xmalloc(4096);
673 buffer
.buffer
= data
;
676 fprintf(stderr
, "Getting pack list for %s\n", repo
->base
);
678 url
= xmalloc(strlen(repo
->base
) + 21);
679 sprintf(url
, "%s/objects/info/packs", repo
->base
);
681 slot
= get_active_slot();
682 slot
->results
= &results
;
683 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
684 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
685 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
686 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, NULL
);
687 if (start_active_slot(slot
)) {
688 run_active_slot(slot
);
689 if (results
.curl_result
!= CURLE_OK
) {
690 if (missing_target(&results
)) {
691 repo
->got_indices
= 1;
695 repo
->got_indices
= 0;
697 return error("%s", curl_errorstr
);
701 repo
->got_indices
= 0;
703 return error("Unable to start request");
706 data
= buffer
.buffer
;
707 while (i
< buffer
.posn
) {
711 if (i
+ 52 <= buffer
.posn
&&
712 !prefixcmp(data
+ i
, " pack-") &&
713 !prefixcmp(data
+ i
+ 46, ".pack\n")) {
714 get_sha1_hex(data
+ i
+ 6, sha1
);
715 setup_index(repo
, sha1
);
720 while (i
< buffer
.posn
&& data
[i
] != '\n')
727 repo
->got_indices
= 1;
731 static int fetch_pack(struct alt_base
*repo
, unsigned char *sha1
)
734 struct packed_git
*target
;
735 struct packed_git
**lst
;
738 char tmpfile
[PATH_MAX
];
741 char range
[RANGE_HEADER_SIZE
];
742 struct curl_slist
*range_header
= NULL
;
744 struct active_request_slot
*slot
;
745 struct slot_results results
;
747 if (fetch_indices(repo
))
749 target
= find_sha1_pack(sha1
, repo
->packs
);
754 fprintf(stderr
, "Getting pack %s\n",
755 sha1_to_hex(target
->sha1
));
756 fprintf(stderr
, " which contains %s\n",
760 url
= xmalloc(strlen(repo
->base
) + 65);
761 sprintf(url
, "%s/objects/pack/pack-%s.pack",
762 repo
->base
, sha1_to_hex(target
->sha1
));
764 filename
= sha1_pack_name(target
->sha1
);
765 snprintf(tmpfile
, sizeof(tmpfile
), "%s.temp", filename
);
766 packfile
= fopen(tmpfile
, "a");
768 return error("Unable to open local file %s for pack",
771 slot
= get_active_slot();
772 slot
->results
= &results
;
773 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, packfile
);
774 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
775 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
776 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
777 slot
->local
= packfile
;
779 /* If there is data present from a previous transfer attempt,
780 resume where it left off */
781 prev_posn
= ftell(packfile
);
785 "Resuming fetch of pack %s at byte %ld\n",
786 sha1_to_hex(target
->sha1
), prev_posn
);
787 sprintf(range
, "Range: bytes=%ld-", prev_posn
);
788 range_header
= curl_slist_append(range_header
, range
);
789 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, range_header
);
792 if (start_active_slot(slot
)) {
793 run_active_slot(slot
);
794 if (results
.curl_result
!= CURLE_OK
) {
796 return error("Unable to get pack file %s\n%s", url
,
801 return error("Unable to start request");
804 target
->pack_size
= ftell(packfile
);
807 ret
= move_temp_to_file(tmpfile
, filename
);
812 while (*lst
!= target
)
813 lst
= &((*lst
)->next
);
816 if (verify_pack(target
, 0))
818 install_packed_git(target
);
823 static void abort_object_request(struct object_request
*obj_req
)
825 if (obj_req
->local
>= 0) {
826 close(obj_req
->local
);
829 unlink(obj_req
->tmpfile
);
831 release_active_slot(obj_req
->slot
);
832 obj_req
->slot
= NULL
;
834 release_object_request(obj_req
);
837 static int fetch_object(struct alt_base
*repo
, unsigned char *sha1
)
839 char *hex
= sha1_to_hex(sha1
);
841 struct object_request
*obj_req
= object_queue_head
;
843 while (obj_req
!= NULL
&& hashcmp(obj_req
->sha1
, sha1
))
844 obj_req
= obj_req
->next
;
846 return error("Couldn't find request for %s in the queue", hex
);
848 if (has_sha1_file(obj_req
->sha1
)) {
849 abort_object_request(obj_req
);
853 #ifdef USE_CURL_MULTI
854 while (obj_req
->state
== WAITING
) {
858 start_object_request(obj_req
);
861 while (obj_req
->state
== ACTIVE
) {
862 run_active_slot(obj_req
->slot
);
864 if (obj_req
->local
!= -1) {
865 close(obj_req
->local
); obj_req
->local
= -1;
868 if (obj_req
->state
== ABORTED
) {
869 ret
= error("Request for %s aborted", hex
);
870 } else if (obj_req
->curl_result
!= CURLE_OK
&&
871 obj_req
->http_code
!= 416) {
872 if (missing_target(obj_req
))
873 ret
= -1; /* Be silent, it is probably in a pack. */
875 ret
= error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
876 obj_req
->errorstr
, obj_req
->curl_result
,
877 obj_req
->http_code
, hex
);
878 } else if (obj_req
->zret
!= Z_STREAM_END
) {
879 corrupt_object_found
++;
880 ret
= error("File %s (%s) corrupt", hex
, obj_req
->url
);
881 } else if (hashcmp(obj_req
->sha1
, obj_req
->real_sha1
)) {
882 ret
= error("File %s has bad hash", hex
);
883 } else if (obj_req
->rename
< 0) {
884 ret
= error("unable to write sha1 filename %s",
888 release_object_request(obj_req
);
892 int fetch(unsigned char *sha1
)
894 struct alt_base
*altbase
= alt
;
896 if (!fetch_object(altbase
, sha1
))
899 if (!fetch_pack(altbase
, sha1
))
901 fetch_alternates(alt
->base
);
902 altbase
= altbase
->next
;
904 return error("Unable to find %s under %s", sha1_to_hex(sha1
),
908 static inline int needs_quote(int ch
)
910 if (((ch
>= 'A') && (ch
<= 'Z'))
911 || ((ch
>= 'a') && (ch
<= 'z'))
912 || ((ch
>= '0') && (ch
<= '9'))
920 static inline int hex(int v
)
922 if (v
< 10) return '0' + v
;
923 else return 'A' + v
- 10;
926 static char *quote_ref_url(const char *base
, const char *ref
)
930 int len
, baselen
, ch
;
932 baselen
= strlen(base
);
933 len
= baselen
+ 7; /* "/refs/" + NUL */
934 for (cp
= ref
; (ch
= *cp
) != 0; cp
++, len
++)
936 len
+= 2; /* extra two hex plus replacement % */
938 memcpy(qref
, base
, baselen
);
939 memcpy(qref
+ baselen
, "/refs/", 6);
940 for (cp
= ref
, dp
= qref
+ baselen
+ 6; (ch
= *cp
) != 0; cp
++) {
941 if (needs_quote(ch
)) {
943 *dp
++ = hex((ch
>> 4) & 0xF);
944 *dp
++ = hex(ch
& 0xF);
954 int fetch_ref(char *ref
, unsigned char *sha1
)
958 struct buffer buffer
;
959 const char *base
= alt
->base
;
960 struct active_request_slot
*slot
;
961 struct slot_results results
;
967 url
= quote_ref_url(base
, ref
);
968 slot
= get_active_slot();
969 slot
->results
= &results
;
970 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
971 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
972 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, NULL
);
973 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
974 if (start_active_slot(slot
)) {
975 run_active_slot(slot
);
976 if (results
.curl_result
!= CURLE_OK
)
977 return error("Couldn't get %s for %s\n%s",
978 url
, ref
, curl_errorstr
);
980 return error("Unable to start request");
984 get_sha1_hex(hex
, sha1
);
988 int main(int argc
, const char **argv
)
991 const char **write_ref
= NULL
;
998 setup_git_directory();
999 git_config(git_default_config
);
1001 while (arg
< argc
&& argv
[arg
][0] == '-') {
1002 if (argv
[arg
][1] == 't') {
1004 } else if (argv
[arg
][1] == 'c') {
1006 } else if (argv
[arg
][1] == 'a') {
1010 } else if (argv
[arg
][1] == 'v') {
1012 } else if (argv
[arg
][1] == 'w') {
1013 write_ref
= &argv
[arg
+ 1];
1015 } else if (!strcmp(argv
[arg
], "--recover")) {
1017 } else if (!strcmp(argv
[arg
], "--stdin")) {
1018 commits_on_stdin
= 1;
1022 if (argc
< arg
+ 2 - commits_on_stdin
) {
1023 usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
1026 if (commits_on_stdin
) {
1027 commits
= pull_targets_stdin(&commit_id
, &write_ref
);
1029 commit_id
= (char **) &argv
[arg
++];
1036 no_pragma_header
= curl_slist_append(no_pragma_header
, "Pragma:");
1038 alt
= xmalloc(sizeof(*alt
));
1039 alt
->base
= xmalloc(strlen(url
) + 1);
1040 strcpy(alt
->base
, url
);
1041 for (s
= alt
->base
+ strlen(alt
->base
) - 1; *s
== '/'; --s
)
1043 alt
->got_indices
= 0;
1047 if (pull(commits
, commit_id
, write_ref
, url
))
1052 curl_slist_free_all(no_pragma_header
);
1054 if (commits_on_stdin
)
1055 pull_targets_free(commits
, commit_id
, write_ref
);
1057 if (corrupt_object_found
) {
1059 "Some loose object were found to be corrupt, but they might be just\n"
1060 "a false '404 Not Found' error message sent with incorrect HTTP\n"
1061 "status code. Suggest running git-fsck.\n");