]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/git/fetch.c
git/fetch: be more robust
[plan9front.git] / sys / src / cmd / git / fetch.c
1 #include <u.h>
2 #include <libc.h>
3
4 #include "git.h"
5
6 char *fetchbranch;
7 char *upstream = "origin";
8 int listonly;
9
10 int
11 resolveremote(Hash *h, char *ref)
12 {
13         char buf[128], *s;
14         int r, f;
15
16         ref = strip(ref);
17         if((r = hparse(h, ref)) != -1)
18                 return r;
19         /* Slightly special handling: translate remote refs to local ones. */
20         if(strcmp(ref, "HEAD") == 0){
21                 snprint(buf, sizeof(buf), ".git/HEAD");
22         }else if(strstr(ref, "refs/heads") == ref){
23                 ref += strlen("refs/heads");
24                 snprint(buf, sizeof(buf), ".git/refs/remotes/%s/%s", upstream, ref);
25         }else if(strstr(ref, "refs/tags") == ref){
26                 ref += strlen("refs/tags");
27                 snprint(buf, sizeof(buf), ".git/refs/tags/%s/%s", upstream, ref);
28         }else{
29                 return -1;
30         }
31
32         r = -1;
33         s = strip(buf);
34         if((f = open(s, OREAD)) == -1)
35                 return -1;
36         if(readn(f, buf, sizeof(buf)) >= 40)
37                 r = hparse(h, buf);
38         close(f);
39
40         if(r == -1 && strstr(buf, "ref:") == buf)
41                 return resolveremote(h, buf + strlen("ref:"));
42         return r;
43 }
44
45 int
46 rename(char *pack, char *idx, Hash h)
47 {
48         char name[128];
49         Dir st;
50
51         nulldir(&st);
52         st.name = name;
53         snprint(name, sizeof(name), "%H.pack", h);
54         if(access(name, AEXIST) == 0)
55                 fprint(2, "warning, pack %s already fetched\n", name);
56         else if(dirwstat(pack, &st) == -1)
57                 return -1;
58         snprint(name, sizeof(name), "%H.idx", h);
59         if(access(name, AEXIST) == 0)
60                 fprint(2, "warning, pack %s already indexed\n", name);
61         else if(dirwstat(idx, &st) == -1)
62                 return -1;
63         return 0;
64 }
65
66 int
67 checkhash(int fd, vlong sz, Hash *hcomp)
68 {
69         DigestState *st;
70         Hash hexpect;
71         char buf[Pktmax];
72         vlong n, r;
73         int nr;
74         
75         if(sz < 28){
76                 werrstr("undersize packfile");
77                 return -1;
78         }
79
80         st = nil;
81         n = 0;
82         while(n != sz - 20){
83                 nr = sizeof(buf);
84                 if(sz - n - 20 < sizeof(buf))
85                         nr = sz - n - 20;
86                 r = readn(fd, buf, nr);
87                 if(r != nr)
88                         return -1;
89                 st = sha1((uchar*)buf, nr, nil, st);
90                 n += r;
91         }
92         sha1(nil, 0, hcomp->h, st);
93         if(readn(fd, hexpect.h, sizeof(hexpect.h)) != sizeof(hexpect.h))
94                 sysfatal("truncated packfile");
95         if(!hasheq(hcomp, &hexpect)){
96                 werrstr("bad hash: %H != %H", *hcomp, hexpect);
97                 return -1;
98         }
99         return 0;
100 }
101
102 int
103 mkoutpath(char *path)
104 {
105         char s[128];
106         char *p;
107         int fd;
108
109         snprint(s, sizeof(s), "%s", path);
110         for(p=strchr(s+1, '/'); p; p=strchr(p+1, '/')){
111                 *p = 0;
112                 if(access(s, AEXIST) != 0){
113                         fd = create(s, OREAD, DMDIR | 0775);
114                         if(fd == -1)
115                                 return -1;
116                         close(fd);
117                 }               
118                 *p = '/';
119         }
120         return 0;
121 }
122
123 int
124 branchmatch(char *br, char *pat)
125 {
126         char name[128];
127
128         if(strstr(pat, "refs/heads") == pat)
129                 snprint(name, sizeof(name), "%s", pat);
130         else if(strstr(pat, "heads"))
131                 snprint(name, sizeof(name), "refs/%s", pat);
132         else
133                 snprint(name, sizeof(name), "refs/heads/%s", pat);
134         return strcmp(br, name) == 0;
135 }
136
137 char *
138 matchcap(char *s, char *cap, int full)
139 {
140         if(strncmp(s, cap, strlen(cap)) == 0)
141                 if(!full || strlen(s) == strlen(cap))
142                         return s + strlen(cap);
143         return nil;
144 }
145
146 void
147 handlecaps(char *caps)
148 {
149         char *p, *n, *c, *r;
150
151         for(p = caps; p != nil; p = n){
152                 n = strchr(p, ' ');
153                 if(n != nil)
154                         *n++ = 0;
155                 if((c = matchcap(p, "symref=", 0)) != nil){
156                         if((r = strchr(c, ':')) != nil){
157                                 *r++ = '\0';
158                                 print("symref %s %s\n", c, r);
159                         }
160                 }
161         }
162 }
163
164 void
165 fail(char *pack, char *idx, char *msg, ...)
166 {
167         char buf[ERRMAX];
168         va_list ap;
169
170         va_start(ap, msg);
171         snprint(buf, sizeof(buf), msg, ap);
172         va_end(ap);
173
174         remove(pack);
175         remove(idx);
176         fprint(2, "%s", buf);
177         exits(buf);
178 }
179
180 int
181 fetchpack(Conn *c)
182 {
183         char buf[Pktmax], *sp[3];
184         char *packtmp, *idxtmp, **ref;
185         Hash h, *have, *want;
186         int nref, refsz, first;
187         int i, n, req, pfd;
188         vlong packsz;
189         Object *o;
190
191         nref = 0;
192         refsz = 16;
193         first = 1;
194         have = eamalloc(refsz, sizeof(have[0]));
195         want = eamalloc(refsz, sizeof(want[0]));
196         ref = eamalloc(refsz, sizeof(ref[0]));
197         while(1){
198                 n = readpkt(c, buf, sizeof(buf));
199                 if(n == -1)
200                         return -1;
201                 if(n == 0)
202                         break;
203                 if(strncmp(buf, "ERR ", 4) == 0)
204                         sysfatal("%s", buf + 4);
205
206                 if(first && n > strlen(buf))
207                         handlecaps(buf + strlen(buf) + 1);
208                 first = 0;
209
210                 getfields(buf, sp, nelem(sp), 1, " \t\n\r");
211                 if(strstr(sp[1], "^{}"))
212                         continue;
213                 if(fetchbranch && !branchmatch(sp[1], fetchbranch))
214                         continue;
215                 if(refsz == nref + 1){
216                         refsz *= 2;
217                         have = earealloc(have, refsz, sizeof(have[0]));
218                         want = earealloc(want, refsz, sizeof(want[0]));
219                         ref = earealloc(ref, refsz, sizeof(ref[0]));
220                 }
221                 if(hparse(&want[nref], sp[0]) == -1)
222                         sysfatal("invalid hash %s", sp[0]);
223                 if (resolveremote(&have[nref], sp[1]) == -1)
224                         memset(&have[nref], 0, sizeof(have[nref]));
225                 ref[nref] = estrdup(sp[1]);
226                 nref++;
227         }
228         if(listonly){
229                 flushpkt(c);
230                 goto showrefs;
231         }
232
233         if(writephase(c) == -1)
234                 sysfatal("write: %r");
235         req = 0;
236         for(i = 0; i < nref; i++){
237                 if(hasheq(&have[i], &want[i]))
238                         continue;
239                 if((o = readobject(want[i])) != nil){
240                         unref(o);
241                         continue;
242                 }
243                 n = snprint(buf, sizeof(buf), "want %H\n", want[i]);
244                 if(writepkt(c, buf, n) == -1)
245                         sysfatal("could not send want for %H", want[i]);
246                 req = 1;
247         }
248         flushpkt(c);
249         for(i = 0; i < nref; i++){
250                 if(hasheq(&have[i], &Zhash))
251                         continue;
252                 n = snprint(buf, sizeof(buf), "have %H\n", have[i]);
253                 if(writepkt(c, buf, n + 1) == -1)
254                         sysfatal("could not send have for %H", have[i]);
255         }
256         if(!req)
257                 flushpkt(c);
258
259         n = snprint(buf, sizeof(buf), "done\n");
260         if(writepkt(c, buf, n) == -1)
261                 sysfatal("write: %r");
262         if(!req)
263                 return 0;
264         if(readphase(c) == -1)
265                 sysfatal("read: %r");
266         if((n = readpkt(c, buf, sizeof(buf))) == -1)
267                 sysfatal("read: %r");
268         buf[n] = 0;
269
270         if((packtmp = smprint(".git/objects/pack/fetch.%d.pack", getpid())) == nil)
271                 sysfatal("smprint: %r");
272         if((idxtmp = smprint(".git/objects/pack/fetch.%d.idx", getpid())) == nil)
273                 sysfatal("smprint: %r");
274         if(mkoutpath(packtmp) == -1)
275                 sysfatal("could not create %s: %r", packtmp);
276         if((pfd = create(packtmp, ORDWR, 0664)) == -1)
277                 sysfatal("could not create %s: %r", packtmp);
278
279         fprint(2, "fetching...\n");
280         packsz = 0;
281         while(1){
282                 n = readn(c->rfd, buf, sizeof buf);
283                 if(n == 0)
284                         break;
285                 if(n == -1 || write(pfd, buf, n) != n)
286                         sysfatal("fetch packfile: %r");
287                 packsz += n;
288         }
289
290         closeconn(c);
291         if(seek(pfd, 0, 0) == -1)
292                 fail(packtmp, idxtmp, "packfile seek: %r");
293         if(checkhash(pfd, packsz, &h) == -1)
294                 fail(packtmp, idxtmp, "corrupt packfile: %r");
295         close(pfd);
296         if(indexpack(packtmp, idxtmp, h) == -1)
297                 fail(packtmp, idxtmp, "could not index fetched pack: %r");
298         if(rename(packtmp, idxtmp, h) == -1)
299                 fail(packtmp, idxtmp, "could not rename indexed pack: %r");
300
301 showrefs:
302         for(i = 0; i < nref; i++){
303                 print("remote %s %H local %H\n", ref[i], want[i], have[i]);
304                 free(ref[i]);
305         }
306         free(ref);
307         free(want);
308         free(have);
309         return 0;
310 }
311
312 void
313 usage(void)
314 {
315         fprint(2, "usage: %s [-dl] [-b br] [-u upstream] remote\n", argv0);
316         fprint(2, "\t-b br:     only fetch matching branch 'br'\n");
317         fprint(2, "remote:      fetch from this repository\n");
318         exits("usage");
319 }
320
321 void
322 main(int argc, char **argv)
323 {
324         Conn c;
325
326         ARGBEGIN{
327         case 'b':       fetchbranch=EARGF(usage());     break;
328         case 'u':       upstream=EARGF(usage());        break;
329         case 'd':       chattygit++;                    break;
330         case 'l':       listonly++;                     break;
331         default:        usage();                        break;
332         }ARGEND;
333
334         gitinit();
335         if(argc != 1)
336                 usage();
337
338         if(gitconnect(&c, argv[0], "upload") == -1)
339                 sysfatal("could not dial %s: %r", argv[0]);
340         if(fetchpack(&c) == -1)
341                 sysfatal("fetch failed: %r");
342         closeconn(&c);
343         exits(nil);
344 }