xref: /aosp_15_r20/external/toybox/toys/pending/git.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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