1 /* git.c - A minimal git clone
2 *
3 * Copyright 2022 Moritz C. Weber <[email protected]>
4 *
5 * See https://git-scm.com/docs/git-init
6 * https://git-scm.com/docs/git-remote
7 * https://git-scm.com/docs/git-fetch
8 * https://git-scm.com/docs/git-checkout
9 * https://git-scm.com/docs/pack-format
10 * https://git-scm.com/docs/index-format
11 * https://www.alibabacloud.com/blog/a-detailed-explanation-of-the-underlying-data-structures-and-principles-of-git_597391
12 * https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt
13 * https://stackoverflow.com/a/14303988
14 * https://stackoverflow.com/a/21599232
15 * https://github.com/tarruda/node-git-core/blob/master/src/js/delta.js
16
17
18 USE_GITCLONE(NEWTOY(gitclone, "<1", TOYFLAG_USR|TOYFLAG_BIN))
19 USE_GITINIT(NEWTOY(gitinit, "<1", TOYFLAG_USR|TOYFLAG_BIN))
20 USE_GITREMOTE(NEWTOY(gitremote, "<1", TOYFLAG_USR|TOYFLAG_BIN))
21 USE_GITFETCH(NEWTOY(gitfetch, 0, TOYFLAG_USR|TOYFLAG_BIN))
22 USE_GITCHECKOUT(NEWTOY(gitcheckout, "<1", TOYFLAG_USR|TOYFLAG_BIN))
23
24 config GITCOMPAT
25 bool "gitcompat"
26 default n
27 help
28 Enable git compatible repos instead of minimal clone downloader.
29
30 config GITCLONE
31 bool "gitclone"
32 default n
33 help
34 usage: gitclone URL
35 A minimal git clone.
36
37 config GITINIT
38 bool "gitinit"
39 default n
40 help
41 usage: gitinit NAME
42 A minimal git init.
43
44 config GITREMOTE
45 bool "gitremote"
46 default n
47 help
48 usage: gitremote URL
49 A minimal git remote add origin.
50
51 config GITFETCH
52 bool "gitfetch"
53 default n
54 help
55 usage: gitfetch
56 A minimal git fetch.
57
58 config GITCHECKOUT
59 bool "gitcheckout"
60 default n
61 help
62 usage: gitcheckout <branch>
63 A minimal git checkout.
64 */
65
66 #define FOR_gitclone
67 #include "toys.h"
68 #include "openssl/sha.h" //ToDo: borrowed from OpenSSL to not pipe or refactor the SHA1SUM in toybox
69 #include "zlib.h" //ToDo: borrowed from libz to not refactor deflate.c
70
71 GLOBALS(
72 char *url, *name; //git repo remote url and init directory name
73 struct IndexV2 *i; //git creates a index for each pack file, git clone just needs one index for the received pack file
74 )
75
76 //git index format v2 described at https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L266
77 struct IndexV2 {
78 char header[8];// Git 4 byte magic number and 4 byte version number
79 unsigned fot[256];//A fan-out table
80 char (*sha1)[20];//Table of sorted object names(SHA1 hashes)
81 unsigned *crc, *offset;//Table of 4-bit CRC32 values and object offsets in pack file
82 long long *offset64; //8 byte offests -- not supported yet
83 char packsha1[20], idxsha1[20];//SHA1 hash of pack file and SHA1 hash of index file
84 };
85
86 //TODO:This function is not used before git clone persists an index V2
read_index(struct IndexV2 * i)87 static void read_index(struct IndexV2 *i)
88 {
89 FILE *fpi;
90
91 i = xmalloc(sizeof(i));
92 i->sha1 = malloc(20);
93 i->crc = malloc(sizeof(unsigned));
94 i->offset = malloc(sizeof(unsigned));
95 i->offset64 = malloc(sizeof(long long));
96 //TODO: not used yet as index is not persisted yet
97 if (access(".git/object/pack/temp.idx", F_OK)==0) {
98 //persistance needed for other git commands (not clone)
99 fpi = fopen(".git/object/pack/temp.idx", "rb");
100 printf("read header\n");
101 fread(i->header, sizeof(i->header), 1, fpi);
102 printf("Header: %s..Read fot\n", i->header);
103 fread(i->fot, 4, 256, fpi);
104 printf("Elements %d..Read sha1\n", i->fot[255]);
105 fread(i->sha1, sizeof(i->fot), i->fot[255], fpi);
106 printf("read crc\n");
107 fread(i->crc, sizeof(i->fot), i->fot[255], fpi);
108 printf("read offset\n");
109 fread(i->offset, sizeof(i->fot), i->fot[255], fpi);
110 //TODO: Offsets for file size 2G missing here
111 printf("read packsha\n");
112 fread(i->packsha1, 20, 1, fpi);
113 printf("read idxsha\n");
114 fread(i->idxsha1, 20, 1, fpi);
115 fclose(fpi);
116 }
117 }
118
bsearchpos(const char * k,const char * a,size_t h,size_t w)119 long bsearchpos(const char *k, const char *a, size_t h, size_t w)
120 {
121 long l = 0, m = 0, r = 0;
122
123 if (!h) return 0;
124 while (h>0) {
125 m = l+(h/2);
126 r = strncmp(k, a+(m*w), 20);
127 if (!r||h==1) break; //match on search or position for insert
128 if (r<0) { h /= 2; } else { l = m; h -= h/2; }
129 }
130
131 //For inserts check if insert is bigger obj at identified position
132 return m += (r>0) ? 1 : 0;
133 }
134
135 //find offset position in packfile for given SHA1 hash
get_index(struct IndexV2 * i,char * h)136 long get_index(struct IndexV2 *i, char *h)
137 {
138 long pos = bsearchpos(h, i->sha1[0], i->fot[255], 20);
139 return i->offset[pos];
140 }
141
142 //https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L35
143 //https://yqintl.alicdn.com/eef7fe4f22cc97912cee011c99d3fe5821ae9e88.png
144 //read type and length of an packed object at a given offset
unpack(FILE * fpp,int * type,long * offset)145 unsigned long long unpack(FILE *fpp, int *type, long *offset)
146 {
147 int bitshift= 4;
148 unsigned long long length = 0;
149 char data;
150
151 printf("Start unpack\n");
152 fseek(fpp, *offset, SEEK_SET);
153 printf("Offset set to: %ld\n", *offset);
154 fread(&data, 1, 1, fpp);
155 printf("Data: %d\n", data);
156 *type = ((data & 0x70)>>4);
157 printf("Type: %d\n", *type);
158 length |= data & 0x0F;
159 while ((data & 0x80) && fread(&data, 1, 1, fpp)!=-1)
160 {
161 length |= (unsigned long long)(data & 0x7F) << bitshift;
162 bitshift += 7; // (*offset)++;
163 }
164 printf("Length: %llu\n", length);
165
166 return length;
167 }
168
169 // ToDo: borrowed from int inf(FILE *source, FILE *dest) in
170 // zpipe.c: example of proper use of zlib's inflate() and deflate()
171 // Not copyrighted -- provided to the public domain
172 // Version 1.4 11 December 2005 Mark Adler */
173 #define CHUNK 4096
inf(FILE * source,char * dest)174 int inf(FILE *source, char *dest) //modified signature to ease use
175 {
176 int ret;
177 char *position = dest;
178 unsigned have;
179 z_stream strm;
180 unsigned char in[CHUNK];
181 unsigned char out[CHUNK];
182 strm.zalloc = Z_NULL;
183 strm.zfree = Z_NULL;
184 strm.opaque = Z_NULL;
185 strm.avail_in = 0;
186 strm.next_in = Z_NULL;
187 ret = inflateInit(&strm);
188 if (ret != Z_OK) return ret;
189
190 // decompress until deflate stream ends or end of file
191 do {
192
193 strm.avail_in = fread(in, 1, CHUNK, source);
194 if (ferror(source)) {
195 (void)inflateEnd(&strm);
196
197 return Z_ERRNO;
198 }
199 if (strm.avail_in == 0)
200 break;
201 strm.next_in = in;
202
203
204 // run inflate() on input until output buffer not full
205 do {
206
207 strm.avail_out = CHUNK;
208 strm.next_out = out;
209
210 ret = inflate(&strm, Z_NO_FLUSH);
211 //assert(ret != Z_STREAM_ERROR); // state not clobbered
212 switch (ret) {
213 case Z_NEED_DICT:
214 ret = Z_DATA_ERROR; // and fall through
215 case Z_DATA_ERROR:
216 case Z_MEM_ERROR:
217 (void)inflateEnd(&strm);
218
219 return ret;
220 }
221
222 have = CHUNK - strm.avail_out;
223 memcpy(position, out, have); //added to original
224 position += have; //added to original
225 //if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
226 // (void)inflateEnd(&strm);
227 // return Z_ERRNO;
228 //}
229 } while (strm.avail_out == 0);
230 // done when inflate() says it's done
231 } while (ret != Z_STREAM_END);
232 // modified from zpipe.c to set FP to end of zlib object
233 fseek(source, ftell(source)-strm.avail_in, SEEK_SET);
234 // clean up and return
235 (void)inflateEnd(&strm);
236
237 return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
238 }
239
240 //https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L72
241 //Set object to the index after adding prefix, calculating the hash and finding the position
set_object(struct IndexV2 * idx,int type,char * o,unsigned count,unsigned ofs)242 long set_object(struct IndexV2 *idx, int type, char *o, unsigned count,
243 unsigned ofs)
244 {
245 // TODO: Too many allocs in here 1) to concat the search string for hashing
246 // 2) to insert into the array (can be reduce to a single malloc in fetch as
247 // the pack header contains the number of objects in pack
248 char *c, *p = "", *h = xmalloc(20); //composition,prefix,hash
249 long pos = 0;
250
251 printf("Alloc... ");
252 //append a object prefix based on its type (not included in packfile)
253 switch(type) {
254 case 1: p = xmprintf("commit %d", count); break; //count is used as o can contain \0 in the string
255 case 2: p = xmprintf("tree %d", count); break;
256 case 3: p = xmprintf("blob %d", count); break;
257 case 4: p = xmprintf("tag %d", count); break;
258 case 6: printf("REF_DELTA"); break; //not expected in fetch packs as fetch packs are self-containing
259 case 7: printf("OBJ_DELTA\n"); break;
260 }
261 c = xmalloc(strlen(p)+count+2);
262 memcpy(c, p, strlen(p)+1);
263 memcpy(c+strlen(p)+1, o, count+1);
264 h = SHA1(c, strlen(p)+count+1, h); //ToDo: borrowed from OpenSSL to not to pipe or refactor SHA1SUM in toybox
265 printf("..Binary search\n");
266 for (int j = 0; j<20; j++) printf("%02x", h[j]); //find insert position
267 pos = bsearchpos(h, idx->sha1[0], idx->fot[255], 20);
268 printf("\n..Insert pos %ld\n", pos);
269 printf("..Preloop\n");
270
271 //adjust of fanout table https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L204
272 for (int i = h[0]; i<=255; i++) idx->fot[i] += 1;
273 printf("Post loop\n");
274 printf("Resize sha1 array..idx->fot[255]%d\n", idx->fot[255]); //Memory management for insert
275 //TODO:Could be also a single malloc at gitfetch based on the nbr of objects in pack
276
277 //Did not fix the TODO yet, because set_object could be reused for other commands adding single objects to the index
278 idx->sha1 = realloc(idx->sha1, (idx->fot[255]+1)*20*sizeof(char));
279 printf("Mem copy sha1 array..sizeof(idx->sha1)%zu\n", sizeof(idx->sha1));
280 memmove(&idx->sha1[pos+1], &idx->sha1[pos], (idx->fot[255]-pos)*20*sizeof(char));
281 printf("Resize offset\n");
282 idx->offset = realloc(idx->offset, (idx->fot[255]+1)*sizeof(unsigned));
283 printf("Mem copy offset\n");
284 memmove(&idx->offset[pos+1], &idx->offset[pos], sizeof(unsigned)*(idx->fot[255]-pos));
285 printf("Set offset value\n");
286 memcpy(&idx->sha1[pos], h, 20); //insert SHA1
287 idx->offset[pos] = ofs; //insert offset of SHA1
288 //ToDo: id->crc[idx->fot[h[0]]]=;
289 printf("Write object\n");
290 free(h);
291 free(c);
292
293 return ofs;
294 }
295
296 //init a git repository in a given directory name
gitinit(char * name)297 static void gitinit(char *name)
298 {
299 //For git clone actually only refs and object/pack are needed
300 if (mkdir(name, 0755)!=0){
301 //I create the others for a git compliant folder structure
302 mkdir(xmprintf("%s%s", name, "/.git"), 0755);
303 mkdir(xmprintf("%s%s", name, "/.git/objects"), 0755);
304 mkdir(xmprintf("%s%s", name, "/.git/objects/pack"), 0755);
305 mkdir(xmprintf("%s%s", name, "/.git/branches"), 0755);
306 mkdir(xmprintf("%s%s", name, "/.git/hooks"), 0755); //hook files skipped as implementations does not support hooks
307 mkdir(xmprintf("%s%s", name, "/.git/info"), 0755);
308 mkdir(xmprintf("%s%s", name, "/.git/objects/info"), 0755);
309 mkdir(xmprintf("%s%s", name, "/.git/refs"), 0755);
310 mkdir(xmprintf("%s%s", name, "/.git/heads"), 0755);
311 mkdir(xmprintf("%s%s", name, "/.git/tags"), 0755);
312 xcreate(xmprintf("%s%s", name, "/.git/config"), O_CREAT, 0644);
313 xcreate(xmprintf("%s%s", name, "/.git/description"), O_CREAT, 0644);
314 xcreate(xmprintf("%s%s", name, "/.git/HEAD"), O_CREAT, 0644);
315 xcreate(xmprintf("%s%s", name, "/.git/info/exclude"), O_CREAT, 0644);
316 }
317 }
318
319 //set basic configuration and add remote URL
gitremote(char * url)320 static void gitremote(char *url)
321 {
322 if (access(".git/config", F_OK)!=0) {
323 FILE *fp = fopen(".git/config", "wb");
324
325 fwrite("[core]\n", 1, 7, fp);
326 fwrite("\trepositoryformatversion = 0\n", 1, 29, fp);
327 fwrite("\tfilemode = false\n", 1, 18, fp);
328 fwrite("\tbare = false\n", 1, 14, fp);
329 fwrite("\tlogallrefupdates = true\n", 1, 25, fp);
330 fwrite("\tsymlinks = false\n", 1, 18, fp);
331 fwrite("\tignorecase = true\n", 1, 19, fp);
332 fwrite("[remote \"origin\"]\n", 1, 18, fp);
333 fwrite(xmprintf("\turl = %s/refs\n", TT.url), 1, strlen(TT.url)+13, fp);
334 fwrite("\tfetch = +ref/heads/*:refs/remotes/origin/*\n", 1, 44, fp);
335 fclose(fp);
336 }
337 }
338
339 // this is most likely still buggy and create a late observable heap overflow larger deltafied repos
340 // https://stackoverflow.com/a/14303988
341 // resolve deltafied objects in the pack file, see URL in comments for further explainations
resolve_delta(char * s,char * d,long dsize,unsigned * count)342 char *resolve_delta(char *s, char *d, long dsize, unsigned *count)
343 {
344 long pos = 0, bitshift = 0;
345 //https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L113
346 // Skipping source size; did not find out why it is on the delta header as the source object header contains it too; maybe misunderstood and this makes things buggy, but I dont need it here
347 while ((d[pos] & 0x80)) pos++;
348 pos++; //fixes https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L114
349 *count = 0;
350 bitshift = 0;
351 while ((d[pos] & 0x80)) { //reading target_size from header
352 *count |= (unsigned long long)(d[pos++]& 0x7F) << bitshift;
353 bitshift += 7; // (*offset)++;
354 }
355
356 *count |= (unsigned long long)(d[pos++]& 0x7F) << bitshift;
357 printf("Target Count %d:\n", *count);
358 char *t = xmalloc(*count+1);
359 *count = 0;
360 while (pos<dsize) {
361 int i = 0, j = 1;
362 unsigned offset = 0, size = 0;
363
364 if ((d[pos]&0x80)) {//https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L128
365 //https://stackoverflow.com/a/14303988
366 printf("Case 1\n");
367 while (i<4) {
368 if (d[pos]&(1<<i)) {
369 offset |= d[pos+j]<<(i*8);
370 j++;
371 }
372 i++;
373 }
374 while (i<7) {
375 if (d[pos]&(1<<i)) {
376 size |= d[pos+j]<<(i*8);
377 j++;
378 }
379 i++;
380 }
381
382 // packfomat: size zero is automatically converted to 0x10000.
383 // https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L156
384 if (size==0) size = 0x10000;
385 memcpy(t+*count, s+offset, size);
386 //t[size] = '\0';
387 pos += j;
388 printf("Pos\n");
389 } else {
390 //https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L158
391 printf("Case 0\n");
392 size = d[pos++]; //incrememt
393 memcpy(t+*count, d+pos, size);
394 pos += size;
395 }
396 *count += size;
397
398 printf("Target: \n");
399 }
400 free(s);
401 free(d);
402
403 return t;
404 }
405
406 //unpack object (,resolve deltafied objects recursively) and return the unpacked object
unpack_object(FILE * fpp,struct IndexV2 * i,long offset,unsigned * count,int * type)407 char *unpack_object(FILE *fpp, struct IndexV2 *i, long offset, unsigned *count,
408 int *type)
409 {
410 unsigned dcount = unpack(fpp, type, &offset);
411 char *object = xmalloc(dcount);
412
413 object[*count] = '\0';
414 printf("Count: %d \n", *count);
415 // see OBJ_REF_DELTA here https://yqintl.alicdn.com/eef7fe4f22cc97912cee011c99d3fe5821ae9e88.png
416 if (*type==7) {
417 char *h = xmalloc(20);
418
419 fread(h, 20, 1, fpp); //fseek(fpp, 20, SEEK_CUR);
420 printf("Read base object\n");
421 for (int j = 0; j<20; j++) printf("%02x", h[j]);
422 printf("\n");
423 inf(fpp, object); //TO CHECK IF INF OR PLAIN TEXT:
424 long toffset = ftell(fpp); //save original file offset
425 char *source = unpack_object(fpp, i, get_index(i, h), count, type);
426 printf("Inflate delta data\n");
427 fseek(fpp, toffset, SEEK_SET); //return to original file offset
428 printf("Resolve delta data\n");
429 free(h);//recursion due to https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L58
430 return resolve_delta(source, object, dcount, count);
431 } else {
432 printf("Type Else:\n");
433 inf(fpp, object);
434 *count = dcount;
435 printf("Type Else end:\n");
436 //printf("Unpacked Object: %s\n", object);
437
438 return object;
439 }
440 }
441
442 //make 20byte SHA1 hash from 40 byte SHA1 string
txtoh(char * p)443 char *txtoh(char *p)
444 {
445 //TODO: Dont like the malloc here, but did not find a solution to sscanf into p again
446 char *h = xmalloc(41);
447
448 for (int c = 0; c<20; c++) {
449 sscanf(&p[2*c], "%2hhx", &(h[c]));
450 }
451 h[20] = 0;
452 printf("return");
453
454 return h;
455 }
456
457 //traveres the commit tree for checkout
write_children(char * hash,char * path,FILE * fpp)458 void write_children(char *hash, char *path, FILE *fpp)
459 {
460 FILE *fc;
461 char *object;
462 int type;
463 long offset;
464 unsigned count=0;
465
466 printf("seek index\n");
467
468 offset = get_index(TT.i, hash);
469 printf("Found index: %ld\n", offset);
470 printf("read object\n");
471 object = unpack_object(fpp, TT.i, offset, &count, &type);
472 printf("%s\n", object);
473 printf("Type %d\n", type);
474 if (type==1) { //at commit object
475 memcpy(hash, object+5, 40);
476 write_children(txtoh(hash), path, fpp);
477 } else if (type==2) { //at tree object https://stackoverflow.com/a/21599232
478 char *hs, *name;
479 int pos = 0;
480
481 printf("process folder %s\n", path);
482 while (pos<count){
483 //find position where the next hash starts
484 hs = object+pos;
485 printf("Object+pos: %s\n", hs);
486 // memcpy(mode, hs+2, 3)//TODO:String to umask
487 if (*hs=='1') { //tree object reference is a file
488 // concat file name
489 name = *path ? xmprintf("%s/%s", path, hs+7) : hs+7;
490 printf("prepare file %s\n", name);
491 } else { //tree object reference is a folder
492 // concat folder name
493 name = *path ? xmprintf("%s/%s", path, hs+6) : hs+6;
494 printf("create folder %s\n", name);
495 mkdir(name, 0755); //TODO: umask
496 }
497 hs += strlen(hs)+1;
498 memcpy(hash, hs, 20);
499 write_children(hash, name, fpp);
500 pos = hs-object+20;
501 printf("Position/count for %s: %d/%u\n", path, pos, count);
502 }
503 printf("**EXIT WHILE**\n");
504 } else { //at blob/file object
505 printf("process file %s\n", path);
506 fc = fopen(path, "w");
507 printf("process opened \n");
508 fputs(object, fc); //TODO:Not sure if length might be an issue here
509 printf("process file written\n");
510 fclose(fc);
511 }
512 free(object);
513 printf("Child: %s done\n", path);
514 }
515
516 //fetches the meta data from the remote repository,requests a pack file for the remote master head,
517 //unpacks all objects and set objects to the index
gitfetch(void)518 static void gitfetch(void)
519 {
520 printf("refs\n");
521
522 // TODO:I use herein after two temp files for fetch which git does not offer
523 // to 1) avoid a rewrite and 2) messing up the repo files while testing
524
525 // TODO: Refactor wget into lib
526 xrun((char *[]){"wget", "-O", ".git/refs/temp.refs",
527 "https://github.com/landley/toybox/info/refs?service=git-upload-pack",
528 0});
529 //char h[] = "8cf1722f0fde510ea81d13b31bde1e48917a0306";
530 //TODO: Replace static testing hash and uncomment the following line if rare delta resolve /?heap overflow? bug was found
531 FILE *fpr = fopen(".git/refs/temp.refs", "r");
532 char *h;
533 size_t l = 0;
534
535 getline(&h,&l,fpr);
536 getline(&h,&l,fpr);
537 getline(&h,&l,fpr);
538 getline(&h,&l,fpr);
539 fclose(fpr);
540 strcpy(h,&h[4]);
541 h[40] = 0;
542 printf("Master HEAD hash: %s\n",h);
543 //TODO: Persist hash to /refs/master/HEAD
544 printf("pack\n");
545 xrun((char *[]){"toybox", "wget", "-O", ".git/objects/pack/temp.pack", "-p", xmprintf("$'0032want %s\n00000009done\n'", h), "https://github.com/landley/toybox/git-upload-pack", 0});
546 //TODO: does not skip 0008NAK printf("init\n");
547 FILE *fpp;
548 printf("openpack\n");
549 fpp = fopen(".git/objects/pack/temp.pack", "r");
550 printf("read index\n");
551 read_index(TT.i); //init index with out reading
552 printf("init\n");
553 unsigned ocount = 0, count;
554 int type;
555 char *object;
556
557 printf("skip header\n");
558 long offset = 12+8; //8byte from the wget post response are skipped too
559 fseek(fpp, 8+8, SEEK_SET); //header check skipped //header check skipped https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L37
560 printf("read count\n");
561 fread(&ocount, 4, 1, fpp);
562 ocount = ntohl(ocount); //https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L46
563 printf("Count: %d ..Loop pack\n", ocount);
564 for (int j = 0; j<ocount; j++){
565 printf("Read object %d\n", j);
566 count = 0;
567 object = unpack_object(fpp, TT.i, offset, &count, &type);
568 printf("Count: %d Offset: %ld ..Set object\n", count, offset);
569 set_object(TT.i, type, object, count, offset);
570
571 free(object);
572
573 printf("Adjust offset\n");
574 offset = ftell(fpp); //adjust offset to new file position
575 printf("Adjusted offset to: %ld\n", offset);
576 }
577
578 //TODO: Final pack checksum not calculated and checked
579 fclose(fpp);
580 }
581
582 //Checkout HEAD to the working directory by recursing write_children
583 //TODO: Replase static hashes with hash read from refs/<branch>/head
gitcheckout(char * name)584 static void gitcheckout(char *name)
585 {
586
587 FILE *fpp;
588 //FILE *fh;
589
590 printf("Find branch for checkout\n");
591 //fh = fopen(xmprintf(".git/ref/heads/%s", name ? "master" : name), "r"); //TODO: Checkout master as in ref/heads
592 printf("Read head\n");
593 //hf[fread(&hf, 40, 1, fh)] = '\0';
594 //fclose(fh);
595 printf("Close heads and read pack\n");
596 fpp = fopen(".git/objects/pack/temp.pack", "r");
597 printf("set signature\n");
598 char *p = "52fb04274b3491fdfe91b2e5acc23dc3f3064a86"; //static hashes for testing toybox 0.0.1";
599 //char *p = "c555a0ca46e75097596274bf5e634127015aa144"; //static hashes for testing 0.0.2";
600 //char *p = "4307a7b07cec4ad8cbab47a29ba941f8cb041812"; //static hashes for testing 0.0.3";
601 //char *p = "3632d5d8fe05d14da983e37c7cd34db0769e6238"; //static hashes for testing 0.0.4";
602 //char *p = "8cf1722f0fde510ea81d13b31bde1e48917a0306"; //3604ba4f42c3d83e2b14f6d0f423a33a3a8706c3";
603 printf("enter tree root\n");
604 write_children(txtoh(p), "", fpp);
605 fclose(fpp);
606 }
607
gitclone_main(void)608 void gitclone_main(void)
609 {
610 TT.url = xstrdup(toys.optargs[0]);
611 if (strend(TT.url, ".git")) TT.url[strlen(TT.url)-4] = '\0';
612 TT.name = strrchr(TT.url, '/')+1;
613 gitinit(TT.name);
614 chdir(TT.name);
615 TT.i = malloc(sizeof(struct IndexV2));
616 gitremote(TT.url);
617 gitfetch();
618 gitcheckout("master");
619 chdir("..");
620 }
621
622 #define FOR_gitinit
623 #include "generated/flags.h"
624
gitinit_main(void)625 void gitinit_main(void)
626 {
627 gitinit(xstrdup(toys.optargs[0]));
628 }
629
630 #define FOR_gitremote
631
gitremote_main(void)632 void gitremote_main(void)
633 {
634 TT.url = xstrdup(toys.optargs[0]);
635 if (strend(TT.url, ".git")) TT.url[strlen(TT.url)-4] = '\0';
636 gitremote(TT.url);
637 }
638
639 #define FOR_gitinit
640
gitfetch_main(void)641 void gitfetch_main(void)
642 {
643 gitfetch();
644 }
645
646 #define FOR_gitcheckout
647
gitcheckout_main(void)648 void gitcheckout_main(void)
649 {
650 gitcheckout(xstrdup(toys.optargs[0]));
651 }
652
653 // Command to wget refs and pack file using toybox wget to place them manually into the repository
654 // ./toybox wget -O - -d https://github.com/landley/toybox/info/refs?service=git-upload-pack | less
655
656 // ./toybox wget -O pack.dat -d -p $'0032want 8cf1722f0fde510ea81d13b31bde1e48917a0306\n00000009done\n' https://github.com/landley/toybox/git-upload-pack
657