/* hg debug stuff, will become update/merge program */ #include #include #include #include #include "dat.h" #include "fns.h" typedef struct Workdir Workdir; typedef struct Dstate Dstate; struct Dstate { Dstate *next; int mode; ulong size; long mtime; char status; char path[]; }; struct Workdir { char path[MAXPATH]; uchar p1hash[HASHSZ]; uchar p2hash[HASHSZ]; Dstate *ht[256]; }; int clean = 0; static Dstate** dslookup(Workdir *wd, char *path) { Dstate **hp, *h; hp = &wd->ht[hashstr(path) % nelem(wd->ht)]; for(h = *hp; h != nil; h = *hp){ if(strcmp(path, h->path) == 0) break; hp = &h->next; } return hp; } static void clearworkdir(Workdir *wd) { Dstate *h; int i; for(i=0; iht); i++) while(h = wd->ht[i]){ wd->ht[i] = h->next; free(h); } memset(wd, 0, sizeof(*wd)); } static int loadworkdir(Workdir *wd, char *path) { uchar hdr[1+4+4+4+4]; char buf[MAXPATH], *err; Dstate **hp, *h; int fd, n; memset(wd, 0, sizeof(*wd)); if(getworkdir(wd->path, path) < 0) return -1; snprint(buf, sizeof(buf), "%s/.hg/dirstate", wd->path); if((fd = open(buf, OREAD)) < 0) return -1; err = "dirstate truncated"; if(read(fd, wd->p1hash, HASHSZ) != HASHSZ) goto Error; if(read(fd, wd->p2hash, HASHSZ) != HASHSZ) goto Error; for(;;){ if((n = read(fd, hdr, sizeof(hdr))) == 0) break; if(n < 0){ err = "reading dirstate: %r"; goto Error; } if(n != sizeof(hdr)) goto Error; n = hdr[16] | hdr[15]<<8 | hdr[14]<<16 | hdr[13]<<24; if(n < 0 || n >= sizeof(buf)){ err = "bad path length in dirstate"; goto Error; } if(read(fd, buf, n) != n) goto Error; buf[n++] = 0; hp = dslookup(wd, buf); if(*hp != nil){ err = "duplicate entry in dirstate"; goto Error; } h = malloc(sizeof(*h) + n); if(h == nil){ err = "out of memory"; goto Error; } memmove(h->path, buf, n); h->status = hdr[0]; h->mode = hdr[4] | hdr[3]<<8 | hdr[2]<<16 | hdr[1]<<24; h->size = hdr[8] | hdr[7]<<8 | hdr[6]<<16 | hdr[5]<<24; h->mtime = hdr[12] | hdr[11]<<8 | hdr[10]<<16 | hdr[9]<<24; h->next = *hp; *hp = h; } close(fd); return 0; Error: clearworkdir(wd); close(fd); werrstr(err); return -1; } char* pjoin(char *path, char *name) { if(path[0] == '\0') path = "/"; if(name[0] == '\0') return strdup(path); if(path[strlen(path)-1] == '/' || name[0] == '/') return smprint("%s%s", path, name); return smprint("%s/%s", path, name); } void changes1(int fd, char *lpath, char *rpath, char *apath, void (*apply)(char *, char *, char *, char *, char *, void *), void *aux) { char *state, *name; Biobuf bin; Binit(&bin, fd, OREAD); while((state = Brdstr(&bin, '\n', 1)) != nil){ if((name = strchr(state, '\t')) == nil) continue; while(*name == '\t' || *name == ' ') *name++ = '\0'; if(name[0] == '.' && name[1] == '/') name += 2; apply(state, name, lpath, rpath, apath, aux); } Bterm(&bin); } int changes(char *opt, char *lp, char *rp, char *ap, void (*apply)(char *, char *, char *, char *, char *, void *), void *aux) { int pfd[2], pid; Waitmsg *w; if(pipe(pfd) < 0) return -1; pid = rfork(RFPROC|RFMEM|RFFDG); switch(pid){ case -1: close(pfd[0]); close(pfd[1]); return -1; case 0: close(pfd[0]); dup(pfd[1], 1); close(pfd[1]); execl("/bin/derp", "derp", opt, "-p", "0111", lp, ap, rp, nil); exits("exec"); } close(pfd[1]); changes1(pfd[0], lp, rp, ap, apply, aux); close(pfd[0]); while(w = wait()){ if(w->pid == pid){ if(w->msg[0] != '\0'){ werrstr("%s", w->msg); free(w); return 1; } free(w); return 0; } free(w); } return -1; } void apply(char *state, char *name, char *lp, char *rp, char *ap, Workdir *) { Dir *rd, *ld; ld = rd = nil; // fprint(2, "### %s %s ->\t", state, name); if(strcmp(state, "na") == 0){ rd = dirstat(rp); if(rd != nil){ if(rd->qid.type & QTDIR) print("mkdir %s\n", lp); else print("cp %s %s\n", rp, lp); } } else if(strcmp(state, "an") == 0){ } else if(strcmp(state, "nm") == 0){ print("cp %s %s\n", rp, lp); } else if(strcmp(state, "mn") == 0){ } else if(strcmp(state, "nd") == 0){ print("rm %s\n", lp); } else if(strcmp(state, "dn") == 0){ } else if(strcmp(state, "aa!") == 0 || strcmp(state, "mm!") == 0){ ld = dirstat(lp); rd = dirstat(rp); if(ld != nil && rd != nil){ if(rd->qid.type & QTDIR) print("# conflict # mkdir %s\n", lp); else if(ld->qid.type & QTDIR) print("# conflict # rm -r %s\n", lp); else print("# conflict # ape/diff3 %s %s %s >%s\n", lp, ap, rp, lp); } } else if(strcmp(state, "md!") == 0){ print("# delete conflict # rm %s\n", lp); } else if(strcmp(state, "dm!") == 0){ print("# delete conflict # cp %s %s\n", rp, lp); } else { print("# unknown status %s %s\n", state, name); } free(rd); free(ld); } void apply1(char *state, char *name, char *lp, char *rp, char *ap, void *aux) { Workdir *wd = aux; lp = pjoin(lp, name); rp = pjoin(rp, name); ap = pjoin(ap, name); apply(state, lp + strlen(wd->path)+1, lp, rp, ap, wd); free(ap); free(rp); free(lp); } void apply0(char *state, char *name, char *lp, char *rp, char *ap, void *aux) { Workdir *wd = aux; Dir *ld; if(clean){ /* working dir clean */ apply1(state, name, wd->path, rp, ap, wd); return; } lp = pjoin(wd->path, name); ld = dirstat(lp); if(clean == 0 && ld != nil && (ld->qid.type & QTDIR) == 0){ /* check for changes in working directory */ rp = pjoin(rp, name); ap = pjoin(ap, name); changes("-Lcq", lp, rp, ap, apply1, wd); free(ap); free(rp); } else { /* working dir clean */ apply1(state, name, wd->path, rp, ap, wd); } free(lp); free(ld); } void usage(void) { fprint(2, "usage: %s [-m mtpt] [-r rev] [root]\n", argv0); exits("usage"); } void main(int argc, char *argv[]) { char lp[MAXPATH], rp[MAXPATH], ap[MAXPATH]; uchar rh[HASHSZ], ah[HASHSZ]; char *mtpt, *rev; Workdir wd; fmtinstall('H', Hfmt); rev = "tip"; mtpt = "/mnt/hg"; ARGBEGIN { case 'm': mtpt = EARGF(usage()); break; case 'r': rev = EARGF(usage()); break; case 'c': clean = 1; break; } ARGEND; memset(&wd, 0, sizeof(wd)); if(loadworkdir(&wd, *argv) < 0) sysfatal("loadworkdir: %r"); if(memcmp(wd.p2hash, nullid, HASHSZ)) sysfatal("outstanding merge"); snprint(rp, sizeof(rp), "%s/%s", mtpt, rev); if(readhash(rp, "rev", rh) != 0) sysfatal("unable to get hash for %s", rev); if(memcmp(rh, wd.p1hash, HASHSZ) == 0){ fprint(2, "up to date\n"); exits(0); } ancestor(mtpt, wd.p1hash, rh, ah); if(memcmp(ah, nullid, HASHSZ) == 0) sysfatal("no common ancestor between %H and %H", wd.p1hash, rh); if(memcmp(ah, rh, HASHSZ) == 0) memmove(ah, wd.p1hash, HASHSZ); snprint(lp, sizeof(lp), "%s/%H/files", mtpt, wd.p1hash); snprint(rp, sizeof(rp), "%s/%H/files", mtpt, rh); snprint(ap, sizeof(ap), "%s/%H/files", mtpt, ah); changes("-L", lp, rp, ap, apply0, &wd); exits(0); }