ref: 45001b2700f817cc4a3609574031437040de393e
parent: 8da1ef146aa867ec8021aa4acae54b030701ee6e
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Feb 17 23:54:36 EST 2024
sync with 9front
--- a/conf.c
+++ b/conf.c
@@ -59,7 +59,7 @@
main(int argc, char **argv)
{
char repo[512], *p, *s;
- int i, j;
+ int i, j, nrel;
ARGBEGIN{
case 'f': file[nfile++]=EARGF(usage()); break;
@@ -69,7 +69,7 @@
}ARGEND;
if(findroot){
- if(findrepo(repo, sizeof(repo)) == -1)
+ if(findrepo(repo, sizeof(repo), &nrel) == -1)
sysfatal("%r");
print("%s\n", repo);
exits(nil);
--- a/fs.c
+++ b/fs.c
@@ -71,11 +71,10 @@
};
#define Eperm "permission denied"
-#define Eexist "does not exist"
+#define Eexist "file does not exist"
#define E2long "path too long"
#define Enodir "not a directory"
#define Erepo "unable to read repo"
-#define Eobject "invalid object"
#define Egreg "wat"
#define Ebadobj "invalid object"
@@ -305,6 +304,9 @@
d->qid.path = qpath(c, i, o->id, Qauthor);
break;
default:
+ free(d->uid);
+ free(d->gid);
+ free(d->muid);
return -1;
}
return 0;
@@ -453,6 +455,9 @@
break;
}
free(path);
+ for(i = 0; o != nil && i < aux->ncrumb; i++)
+ if(crumb(aux, i)->obj == o)
+ return nil;
return o;
}
@@ -482,7 +487,7 @@
if(!w)
return Ebadobj;
q->type = (w->type == GTree) ? QTDIR : 0;
- q->path = qpath(c, i, w->id, qdir);
+ q->path = qpath(p, i, w->id, qdir);
c->mode = m;
c->mode |= (w->type == GTree) ? DMDIR|0755 : 0644;
c->obj = w;
@@ -618,6 +623,8 @@
q->path = qpath(o, Qbranch, c->obj->id, Qcommit);
else
e = Eexist;
+ if(d != nil)
+ c->mode = d->mode & ~0222;
free(d);
break;
case Qobject:
@@ -625,9 +632,9 @@
e = objwalk1(q, o->obj, o, c, name, Qobject, aux);
}else{
if(hparse(&h, name) == -1)
- return Eobject;
+ return Ebadobj;
if((c->obj = readobject(h)) == nil)
- return Eobject;
+ return Ebadobj;
if(c->obj->type == GBlob || c->obj->type == GTag){
c->mode = 0644;
q->type = 0;
--- a/get.c
+++ b/get.c
@@ -280,7 +280,7 @@
if(hasheq(&have[i], &Zhash) || oshas(&hadobj, have[i]))
continue;
if((o = readobject(have[i])) == nil)
- sysfatal("missing object we should have: %H", have[i]);
+ sysfatal("missing exected object: %H", have[i]);
if(fmtpkt(c, "have %H", o->hash) == -1)
sysfatal("write: %r");
enqueueparent(&haveq, o);
--- a/git.h
+++ b/git.h
@@ -10,6 +10,7 @@
typedef struct Delta Delta;
typedef struct Cinfo Cinfo;
typedef struct Tinfo Tinfo;
+typedef struct Ginfo Ginfo;
typedef struct Object Object;
typedef struct Objset Objset;
typedef struct Pack Pack;
@@ -21,6 +22,7 @@
typedef struct Dblock Dblock;
typedef struct Objq Objq;
typedef struct Qelt Qelt;
+typedef struct Idxent Idxent;
enum {
Pathmax = 512,
@@ -128,6 +130,7 @@
union {
Cinfo *commit;
Tinfo *tree;
+ Ginfo *tag;
};
};
@@ -150,6 +153,18 @@
vlong mtime;
};
+struct Ginfo {
+ /* Tag */
+ Hash object;
+ char *tagger;
+ char *type;
+ char *tag;
+ char *msg;
+ int nmsg;
+ vlong ctime;
+ vlong mtime;
+};
+
struct Objset {
Object **obj;
int nobj;
@@ -190,6 +205,13 @@
int len;
};
+struct Idxent {
+ char *path;
+ Qid qid;
+ int mode;
+ int order;
+ char state;
+};
#define GETBE16(b)\
((((b)[0] & 0xFFul) << 8) | \
@@ -301,9 +323,10 @@
int hassuffix(char *, char *);
int swapsuffix(char *, int, char *, char *, char *);
char *strip(char *);
-int findrepo(char *, int);
+int findrepo(char *, int, int*);
int showprogress(int, int);
u64int murmurhash2(void*, usize);
+Qid parseqid(char*);
/* packing */
void dtinit(Dtab *, Object*);
--- a/log.c
+++ b/log.c
@@ -207,6 +207,8 @@
sysfatal("resolve %s: %r", c);
if((o = readobject(h)) == nil)
sysfatal("load %H: %r", h);
+ if(o->type != GCommit)
+ sysfatal("%s: not a commit", c);
qinit(&objq);
osinit(&done);
qput(&objq, o, 0);
@@ -239,7 +241,7 @@
main(int argc, char **argv)
{
char path[1024], repo[1024], *p, *r;
- int i, nrepo;
+ int i, nrel, nrepo;
ARGBEGIN{
case 'e':
@@ -259,7 +261,7 @@
break;
}ARGEND;
- if(findrepo(repo, sizeof(repo)) == -1)
+ if(findrepo(repo, sizeof(repo), &nrel) == -1)
sysfatal("find root: %r");
nrepo = strlen(repo);
if(argc != 0){
@@ -291,5 +293,6 @@
showquery(queryexpr);
else
showcommits(commitid);
+ Bterm(out);
exits(nil);
}
--- a/pack.c
+++ b/pack.c
@@ -66,7 +66,7 @@
Object *lruhead;
Object *lrutail;
vlong ncache;
-vlong cachemax = 512*MiB;
+vlong cachemax = 128*MiB;
Packf *packf;
int npackf;
int openpacks;
@@ -160,7 +160,7 @@
ref(o);
ncache += o->size;
}
- while(ncache > cachemax && lrutail != nil){
+ while(ncache > cachemax && lrutail != lruhead){
p = lrutail;
lrutail = p->prev;
if(lrutail != nil)
@@ -825,6 +825,7 @@
{
char buf[128];
Resub m[4];
+ vlong tz;
char *p;
int n, nm;
@@ -845,10 +846,16 @@
memcpy(*name, m[1].sp, nm);
buf[nm] = 0;
+ nm = m[3].ep - m[3].sp;
+ memcpy(buf, m[3].sp, nm);
+ buf[nm] = 0;
+ tz = atoll(buf);
+
nm = m[2].ep - m[2].sp;
memcpy(buf, m[2].sp, nm);
buf[nm] = 0;
- *time = atoll(buf);
+ *time = atoll(buf) + 3600*(tz/100) + 60*(tz%100);
+
return 0;
}
@@ -901,6 +908,46 @@
}
static void
+parsetag(Object *o)
+{
+ char *p, buf[128];
+ int np;
+
+ p = o->data;
+ np = o->size;
+ o->tag = emalloc(sizeof(Ginfo));
+ while(1){
+ if(scanword(&p, &np, buf, sizeof(buf)) == -1)
+ break;
+ if(strcmp(buf, "object") == 0){
+ if(scanword(&p, &np, buf, sizeof(buf)) == -1)
+ sysfatal("invalid commit: tree missing");
+ if(hparse(&o->tag->object, buf) == -1)
+ sysfatal("invalid commit: garbled tree");
+ }else if(strcmp(buf, "tagger") == 0){
+ parseauthor(&p, &np, &o->commit->author, &o->tag->mtime);
+ }else if(strcmp(buf, "type") == 0){
+ if(scanword(&p, &np, buf, sizeof(buf)) == -1)
+ sysfatal("bad tag type");
+ if((o->tag->type = strdup(buf)) == nil)
+ sysfatal("strdup: %r");
+ }else if(strcmp(buf, "tag") == 0){
+ if(scanword(&p, &np, buf, sizeof(buf)) == -1)
+ sysfatal("bad tag type");
+ if((o->tag->type = strdup(buf)) == nil)
+ sysfatal("strdup: %r");
+ }
+ nextline(&p, &np);
+ }
+ while (np && isspace(*p)) {
+ p++;
+ np--;
+ }
+ o->commit->msg = p;
+ o->commit->nmsg = np;
+}
+
+static void
parsetree(Object *o)
{
int m, a, entsz, nent;
@@ -954,12 +1001,6 @@
o->tree->ent = ent;
o->tree->nent = nent;
}
-
-static void
-parsetag(Object *)
-{
-}
-
void
parseobject(Object *o)
{
@@ -1210,10 +1251,8 @@
if(objectcrc(f, o) == -1)
return -1;
}
- if(n == nvalid){
+ if(n == nvalid)
sysfatal("fix point reached too early: %d/%d: %r", nvalid, nobj);
- goto error;
- }
nvalid = n;
}
if(interactive)
--- a/proto.c
+++ b/proto.c
@@ -58,8 +58,10 @@
char *e;
int n;
- if(readn(c->rfd, len, 4) != 4)
- sysfatal("pktline: short read from transport");
+ if(readn(c->rfd, len, 4) != 4){
+ werrstr("pktline: short read from transport");
+ return -1;
+ }
len[4] = 0;
n = strtol(len, &e, 16);
if(n == 0){
--- a/query.c
+++ b/query.c
@@ -152,10 +152,10 @@
void
main(int argc, char **argv)
{
- int i, j, n;
+ char *query, repo[512];
+ char *p, *e, *objpfx;
+ int i, j, n, nrel;
Hash *h;
- char *p, *e, *s, *objpfx;
- char query[2048], repo[512];
ARGBEGIN{
case 'd': chattygit++; break;
@@ -170,25 +170,26 @@
if(argc == 0)
usage();
- if(findrepo(repo, sizeof(repo)) == -1)
+ if(findrepo(repo, sizeof(repo), &nrel) == -1)
sysfatal("find root: %r");
if(chdir(repo) == -1)
sysfatal("chdir: %r");
if((objpfx = smprint("%s/.git/fs/object/", repo)) == nil)
sysfatal("smprint: %r");
- s = "";
+ for(i = 0, n = 0; i < argc; i++)
+ n += strlen(argv[i]) + 1;
+ query = emalloc(n+1);
p = query;
- e = query + nelem(query);
- for(i = 0; i < argc; i++){
- p = seprint(p, e, "%s%s", s, argv[i]);
- s = " ";
- }
- if((n = resolverefs(&h, query)) == -1)
+ e = query + n;
+ for(i = 0; i < argc; i++)
+ p = seprint(p, e, "%s ", argv[i]);
+ n = resolverefs(&h, query);
+ free(query);
+ if(n == -1)
sysfatal("resolve: %r");
if(changes){
- if(n != 2)
- sysfatal("diff: need 2 commits, got %d", n);
- diffcommits(h[0], h[1]);
+ for(i = 1; i < n; i++)
+ diffcommits(h[0], h[i]);
}else{
p = (fullpath ? objpfx : "");
for(j = 0; j < n; j++)
--- a/ref.c
+++ b/ref.c
@@ -125,8 +125,10 @@
nskip = 0;
for(i = 0; i < nhead; i++){
+ if(hasheq(&head[i], &Zhash))
+ continue;
if((o = readobject(head[i])) == nil){
- fprint(2, "warning: %H does not point at commit\n", o->hash);
+ fprint(2, "warning: %H does not point at commit\n", head[i]);
werrstr("read head %H: %r", head[i]);
return -1;
}
@@ -140,6 +142,8 @@
unref(o);
}
for(i = 0; i < ntail; i++){
+ if(hasheq(&tail[i], &Zhash))
+ continue;
if((o = readobject(tail[i])) == nil){
werrstr("read tail %H: %r", tail[i]);
return -1;
@@ -184,6 +188,10 @@
break;
}
o = readobject(e.o->hash);
+ if(o->type != GCommit){
+ werrstr("not a commit: %H", o->hash);
+ goto error;
+ }
for(i = 0; i < o->commit->nparent; i++){
if((c = readobject(e.o->commit->parent[i])) == nil)
goto error;
@@ -301,6 +309,10 @@
Object *o, *p;
o = pop(ev);
+ if(o->type != GCommit){
+ werrstr("not a commit: %H", o->hash);
+ return -1;
+ }
/* Special case: first commit has no parent. */
if(o->commit->nparent == 0)
p = emptydir();
--- a/save.c
+++ b/save.c
@@ -10,6 +10,7 @@
char *dat;
int ndat;
};
+
enum {
Maxparents = 16,
};
@@ -21,7 +22,9 @@
char *commitmsg;
Hash parents[Maxparents];
int nparents;
-
+Idxent *idx;
+int idxsz;
+int nidx;
int
gitmode(Dirent *e)
{
@@ -31,7 +34,7 @@
return 0160000;
else if(e->mode & DMDIR)
return 0040000;
- else if(e->mode & 0111)
+ else if(e->mode & 0100)
return 0100755;
else
return 0100644;
@@ -38,6 +41,20 @@
}
int
+idxcmp(void *pa, void *pb)
+{
+ Idxent *a, *b;
+ int c;
+
+ a = (Idxent*)pa;
+ b = (Idxent*)pb;
+ if((c = strcmp(a->path, b->path)) != 0)
+ return c;
+ assert(a->order != b->order);
+ return a-> order < b->order ? -1 : 1;
+}
+
+int
entcmp(void *pa, void *pb)
{
char abuf[256], bbuf[256], *ae, *be;
@@ -184,27 +201,21 @@
int
tracked(char *path)
{
- char ipath[256];
- Dir *d;
+ int r, lo, hi, mid;
- /* Explicitly removed. */
- snprint(ipath, sizeof(ipath), ".git/index9/removed/%s", path);
- if(strstr(cleanname(ipath), ".git/index9/removed") != ipath)
- sysfatal("path %s leaves index", ipath);
- d = dirstat(ipath);
- if(d != nil && d->qid.type != QTDIR){
- free(d);
- return 0;
+ lo = 0;
+ hi = nidx-1;
+ while(lo <= hi){
+ mid = (hi + lo) / 2;
+ r = strcmp(path, idx[mid].path);
+ if(r < 0)
+ hi = mid-1;
+ else if(r > 0)
+ lo = mid+1;
+ else
+ return idx[mid].state != 'R';
}
-
- /* Explicitly added. */
- snprint(ipath, sizeof(ipath), ".git/index9/tracked/%s", path);
- if(strstr(cleanname(ipath), ".git/index9/tracked") != ipath)
- sysfatal("path %s leaves index", ipath);
- if(access(ipath, AEXIST) == 0)
- return 1;
-
- return 0;
+ return 0;
}
int
@@ -354,10 +365,11 @@
void
main(int argc, char **argv)
{
+ char *ln, *dstr, *parts[4], cwd[1024];
+ int i, r, line, ncwd;
Hash th, ch;
- char *dstr, cwd[1024];
- int i, r, ncwd;
vlong date;
+ Biobuf *f;
Object *t;
gitinit();
@@ -425,6 +437,33 @@
}
t = findroot();
+ nidx = 0;
+ idxsz = 32;
+ idx = emalloc(idxsz*sizeof(Idxent));
+ if((f = Bopen(".git/INDEX9", OREAD)) == nil)
+ sysfatal("open index: %r");
+ line = 0;
+ while((ln = Brdstr(f, '\n', 1)) != nil){
+ line++;
+ if(ln[0] == 0 || ln[0] == '\n')
+ continue;
+ if(getfields(ln, parts, nelem(parts), 0, " \t") != nelem(parts))
+ sysfatal(".git/INDEX9:%d: corrupt index", line);
+ if(nidx == idxsz){
+ idxsz += idxsz/2;
+ idx = realloc(idx, idxsz*sizeof(Idxent));
+ }
+ cleanname(parts[3]);
+ idx[nidx].state = *parts[0];
+ idx[nidx].qid = parseqid(parts[1]);
+ idx[nidx].mode = strtol(parts[2], nil, 8);
+ idx[nidx].path = strdup(parts[3]);
+ idx[nidx].order = nidx;
+ nidx++;
+ free(ln);
+ }
+ Bterm(f);
+ qsort(idx, nidx, sizeof(Idxent), idxcmp);
r = treeify(t, argv, argv + argc, 0, &th);
if(r == -1)
sysfatal("could not commit: %r\n");
--- a/send.c
+++ b/send.c
@@ -75,9 +75,11 @@
sysfatal("smprint: %r");
if((idx = findref(ref, nu, r)) == -1)
idx = nu++;
+ else
+ free(ref[idx]);
assert(idx < nremoved + nbranch);
memcpy(&tail[idx], &Zhash, sizeof(Hash));
- free(r);
+ ref[idx] = r;
}
dprint(1, "nu: %d\n", nu);
for(i = 0; i < nu; i++)
@@ -184,7 +186,10 @@
p = nil;
if(a != nil && b != nil)
p = ancestor(a, b);
- if(!force && !hasheq(&m->theirs, &Zhash) && (a == nil || p != a)){
+ if(!force
+ && !hasheq(&m->theirs, &Zhash)
+ && !hasheq(&m->ours, &Zhash)
+ && (a == nil || p != a)){
fprint(2, "remote has diverged\n");
werrstr("remote diverged");
flushpkt(c);
--- a/serve.c
+++ b/serve.c
@@ -7,28 +7,12 @@
char *pathpfx = nil;
int allowwrite;
-int report;
int
-parsecaps(char *caps)
-{
- char *p, *n;
-
- for(p = caps; p != nil; p = n){
- if((n = strchr(p, ' ')) != nil)
- *n++ = 0;
- if(strcmp(p, "report-status") == 0)
- report = 1;
- }
- return 0;
-}
-
-int
showrefs(Conn *c)
{
int i, ret, nrefs;
Hash head, *refs;
- char *p, *e, pkt[Pktmax];
char **names;
ret = -1;
@@ -44,14 +28,7 @@
for(i = 0; i < nrefs; i++){
if(strncmp(names[i], "heads/", strlen("heads/")) != 0)
continue;
- p = pkt;
- e = pkt+sizeof(pkt);
- p = seprint(p, e, "%H refs/%s\n", refs[i], names[i]);
- if(i == 0){
- *p++ = 0;
- p = seprint(p, e, "report-status");
- }
- if(writepkt(c, pkt, p-pkt) == -1)
+ if(fmtpkt(c, "%H refs/%s\n", refs[i], names[i]) == -1)
goto error;
}
if(flushpkt(c) == -1)
@@ -68,8 +45,8 @@
int
servnegotiate(Conn *c, Hash **head, int *nhead, Hash **tail, int *ntail)
{
- char *sp[3], pkt[Pktmax];
- int n, nsp, acked;
+ char pkt[Pktmax];
+ int n, acked;
Object *o;
Hash h;
@@ -85,22 +62,14 @@
goto error;
if(n == 0)
break;
- if((nsp = getfields(pkt, sp, nelem(sp), 1, " \t")) < 2){
- werrstr("protocol garble %s", pkt);
- goto error;
- }
- if(strcmp(sp[0], "want") != 0){
+ if(strncmp(pkt, "want ", 5) != 0){
werrstr(" protocol garble %s", pkt);
goto error;
}
- if(hparse(&h, sp[1]) == -1){
+ if(hparse(&h, &pkt[5]) == -1){
werrstr(" garbled want");
goto error;
}
- if(nsp > 2 && parsecaps(sp[2]) == -1){
- werrstr("garbled caps %s", sp[2]);
- goto error;
- }
if((o = readobject(h)) == nil){
werrstr("requested nonexistent object");
goto error;
@@ -182,7 +151,7 @@
{
char pkt[Pktmax], *sp[4];
Hash old, new;
- int l, n, i;
+ int n, i;
if(showrefs(c) == -1)
return -1;
@@ -195,11 +164,6 @@
goto error;
if(n == 0)
break;
- l = strlen(pkt);
- if(n > l+1 && parsecaps(pkt+l+1) == -1){
- fmtpkt(c, "ERR protocol garble %s\n", pkt);
- goto error;
- }
if(getfields(pkt, sp, nelem(sp), 1, " \t\n\r") != 3){
fmtpkt(c, "ERR protocol garble %s\n", pkt);
goto error;
@@ -350,26 +314,21 @@
packsz += n;
}
if(checkhash(pfd, packsz, &h) == -1){
- werrstr("hash mismatch\n");
+ dprint(1, "hash mismatch\n");
goto error1;
}
if(indexpack(packtmp, idxtmp, h) == -1){
- werrstr("indexing failed: %r\n");
+ dprint(1, "indexing failed: %r\n");
goto error1;
}
if(rename(packtmp, idxtmp, h) == -1){
- werrstr("rename failed: %r\n");
+ dprint(1, "rename failed: %r\n");
goto error2;
}
- if(report)
- fmtpkt(c, "unpack ok");
return 0;
error2: remove(idxtmp);
error1: remove(packtmp);
- dprint(1, "update pack: %r");
- if(report)
- fmtpkt(c, "unpack %r");
return -1;
}
@@ -389,13 +348,12 @@
int
updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd)
{
- char refpath[512];
- int i, j, newidx, hadref, fd, ret, lockfd;
+ char refpath[512], buf[128];
+ int i, newidx, hadref, fd, ret, lockfd;
vlong newtm;
Object *o;
Hash h;
- i = 0;
ret = -1;
hadref = 0;
newidx = -1;
@@ -406,20 +364,20 @@
*/
newtm = -23811206400;
if((lockfd = lockrepo()) == -1){
- werrstr("repo locked\n");
- goto out;
+ snprint(buf, sizeof(buf), "repo locked\n");
+ return -1;
}
for(i = 0; i < nupd; i++){
if(resolveref(&h, ref[i]) == 0){
hadref = 1;
if(!hasheq(&h, &cur[i])){
- werrstr("old ref changed: %s", ref[i]);
- goto out;
+ snprint(buf, sizeof(buf), "old ref changed: %s", ref[i]);
+ goto error;
}
}
if(snprint(refpath, sizeof(refpath), ".git/%s", ref[i]) == sizeof(refpath)){
- werrstr("ref path too long: %s", ref[i]);
- goto out;
+ snprint(buf, sizeof(buf), "ref path too long: %s", ref[i]);
+ goto error;
}
if(hasheq(&upd[i], &Zhash)){
remove(refpath);
@@ -426,12 +384,12 @@
continue;
}
if((o = readobject(upd[i])) == nil){
- werrstr("update to nonexistent hash %H", upd[i]);
- goto out;
+ snprint(buf, sizeof(buf), "update to nonexistent hash %H", upd[i]);
+ goto error;
}
if(o->type != GCommit){
- werrstr("not commit: %H", upd[i]);
- goto out;
+ snprint(buf, sizeof(buf), "not commit: %H", upd[i]);
+ goto error;
}
if(o->commit->mtime > newtm){
newtm = o->commit->mtime;
@@ -439,13 +397,13 @@
}
unref(o);
if((fd = create(refpath, OWRITE|OTRUNC, 0644)) == -1){
- werrstr("open ref: %r");
- goto out;
+ snprint(buf, sizeof(buf), "open ref: %r");
+ goto error;
}
if(fprint(fd, "%H", upd[i]) == -1){
- werrstr("upate ref: %r");
+ snprint(buf, sizeof(buf), "upate ref: %r");
close(fd);
- goto out;
+ goto error;
}
close(fd);
}
@@ -462,30 +420,22 @@
* use. This should make us pick a useful default in
* those cases, instead of silently failing.
*/
- i = 0;
if(resolveref(&h, "HEAD") == -1 && hadref == 0 && newidx != -1){
if((fd = create(".git/HEAD", OWRITE|OTRUNC, 0644)) == -1){
- werrstr("open HEAD: %r");
- goto out;
+ snprint(buf, sizeof(buf), "open HEAD: %r");
+ goto error;
}
- if(fprint(fd, "ref: %s", ref[i]) == -1){
- werrstr("write HEAD ref: %r");
- goto out;
+ if(fprint(fd, "ref: %s", ref[0]) == -1){
+ snprint(buf, sizeof(buf), "write HEAD ref: %r");
+ goto error;
}
close(fd);
}
ret = 0;
-out:
- if(report){
- for(j = 0; j < nupd; j++){
- if(i != j || ret == 0)
- fmtpkt(c, "ok %s", ref[i]);
- else
- fmtpkt(c, "ng %s %r\n", ref[i]);
- }
- }
- if(lockfd != -1)
- close(lockfd);
+error:
+ fmtpkt(c, "ERR %s", buf);
+ close(lockfd);
+ werrstr(buf);
return ret;
}
--- a/util.c
+++ b/util.c
@@ -192,8 +192,10 @@
Qid q;
q = va_arg(fmt->args, Qid);
- return fmtprint(fmt, "Qid{path=0x%llx(dir:%d,obj:%lld), vers=%ld, type=%d}",
- q.path, QDIR(&q), (q.path >> 8), q.vers, q.type);
+ if(q.path == ~0ULL && q.vers == ~0UL && q.type == 0xff)
+ return fmtprint(fmt, "NOQID");
+ else
+ return fmtprint(fmt, "%llux.%lud.%hhx", q.path, q.vers, q.type);
}
void
@@ -205,7 +207,7 @@
fmtinstall('Q', Qfmt);
inflateinit();
deflateinit();
- authorpat = regcomp("[\t ]*(.*)[\t ]+([0-9]+)[\t ]+([\\-+]?[0-9]+)");
+ authorpat = regcomp("[\t ]*(.*)[\t ]+([0-9]+)[\t ]*([\\-+]?[0-9]+)?");
osinit(&objcache);
}
@@ -294,7 +296,7 @@
/* Finds the directory containing the git repo. */
int
-findrepo(char *buf, int nbuf)
+findrepo(char *buf, int nbuf, int *nrel)
{
char *p, *suff;
@@ -302,6 +304,7 @@
if(getwd(buf, nbuf - strlen(suff) - 1) == nil)
return -1;
+ *nrel = 0;
for(p = buf + strlen(buf); p != nil; p = strrchr(buf, '/')){
strcpy(p, suff);
if(access(buf, AEXIST) == 0){
@@ -308,6 +311,7 @@
p[p == buf] = '\0';
return 0;
}
+ *nrel += 1;
*p = '\0';
}
werrstr("not a git repository");
@@ -347,6 +351,7 @@
Qelt t;
int i;
+ assert(o->type == GCommit);
if(q->nheap == q->heapsz){
q->heapsz *= 2;
q->heap = earealloc(q->heap, q->heapsz, sizeof(Qelt));
@@ -441,4 +446,25 @@
h ^= h >> 15;
return h;
+}
+
+Qid
+parseqid(char *s)
+{
+ char *e;
+ Qid q;
+
+ if(strcmp(s, "NOQID") == 0)
+ return (Qid){-1, -1, -1};
+ e = s;
+ q.path = strtoull(e, &e, 16);
+ if(*e != '.')
+ sysfatal("corrupt qid: %s (%s)\n", s, e);
+ q.vers = strtoul(e+1, &e, 10);
+ if(*e != '.')
+ sysfatal("corrupt qid: %s (%s)\n", s, e);
+ q.type = strtoul(e+1, &e, 16);
+ if(*e != '\0')
+ sysfatal("corrupt qid: %s (%x)\n", s, *e);
+ return q;
}
--- a/walk.c
+++ b/walk.c
@@ -2,52 +2,72 @@
#include <libc.h>
#include "git.h"
+typedef struct Seen Seen;
+typedef struct Idxed Idxed;
+typedef struct Idxent Idxent;
+
#define NCACHE 4096
-#define TDIR ".git/index9/tracked"
-#define RDIR ".git/index9/removed"
-#define HDIR ".git/fs/HEAD/tree"
-typedef struct Cache Cache;
-typedef struct Wres Wres;
-struct Cache {
+
+enum {
+ Rflg = 1 << 0,
+ Mflg = 1 << 1,
+ Aflg = 1 << 2,
+ Uflg = 1 << 3,
+ /* everything after this is not an error */
+ Tflg = 1 << 4,
+};
+
+struct Seen {
Dir* cache;
int n;
int max;
};
-struct Wres {
- char **path;
- int npath;
- int pathsz;
+struct Idxed {
+ char** cache;
+ int n;
+ int max;
};
-enum {
- Rflg = 1 << 0,
- Mflg = 1 << 1,
- Aflg = 1 << 2,
- Tflg = 1 << 3,
-};
+Seen seentab[NCACHE];
+Idxed idxtab[NCACHE];
+char repopath[1024];
+char wdirpath[1024];
+char *rstr = "R ";
+char *mstr = "M ";
+char *astr = "A ";
+char *ustr = "U ";
+char *tstr = "T ";
+char *bdir = ".git/fs/HEAD/tree";
+int useidx = 1;
+int nrel;
+int quiet;
+int dirty;
+int printflg;
-Cache seencache[NCACHE];
-int quiet;
-int printflg;
-char *rstr = "R ";
-char *tstr = "T ";
-char *mstr = "M ";
-char *astr = "A ";
+Idxent *idx;
+int idxsz;
+int nidx;
+int staleidx;
+Idxent *wdir;
+int wdirsz;
+int nwdir;
+int loadwdir(char*);
+
int
seen(Dir *dir)
{
+ Seen *c;
Dir *dp;
int i;
- Cache *c;
- c = &seencache[dir->qid.path&(NCACHE-1)];
+ c = &seentab[dir->qid.path&(NCACHE-1)];
dp = c->cache;
for(i=0; i<c->n; i++, dp++)
- if(dir->qid.path == dp->qid.path &&
- dir->type == dp->type &&
- dir->dev == dp->dev)
+ if(dir->qid.path == dp->qid.path
+ && dir->qid.type == dp->qid.type
+ && dir->dev == dp->dev)
return 1;
if(c->n == c->max){
if (c->max == 0)
@@ -62,129 +82,115 @@
return 0;
}
-void
-grow(Wres *r)
-{
- if(r->npath == r->pathsz){
- r->pathsz = 2*r->pathsz + 1;
- r->path = erealloc(r->path, r->pathsz * sizeof(char*));
- }
-}
-
int
-readpaths(Wres *r, char *pfx, char *dir)
+checkedin(Idxent *e, int change)
{
- char *f, *sub, *full, *sep;
- Dir *d;
- int fd, ret, i, n;
+ char *p;
+ int r;
- d = nil;
- ret = -1;
- sep = "";
- if(dir[0] != 0)
- sep = "/";
- if((full = smprint("%s/%s", pfx, dir)) == nil)
- sysfatal("smprint: %r");
- if((fd = open(full, OREAD)) < 0)
- goto error;
- while((n = dirread(fd, &d)) > 0){
- for(i = 0; i < n; i++){
- if(seen(&d[i]))
- continue;
- if(d[i].qid.type & QTDIR){
- if((sub = smprint("%s%s%s", dir, sep, d[i].name)) == nil)
- sysfatal("smprint: %r");
- if(readpaths(r, pfx, sub) == -1){
- free(sub);
- goto error;
- }
- free(sub);
- }else{
- grow(r);
- if((f = smprint("%s%s%s", dir, sep, d[i].name)) == nil)
- sysfatal("smprint: %r");
- r->path[r->npath++] = f;
- }
- }
- free(d);
+ p = smprint("%s/%s", bdir, e->path);
+ r = access(p, AEXIST);
+ if(r == 0 && change){
+ if(e->state != 'R')
+ e->state = 'T';
+ staleidx = 1;
}
- ret = r->npath;
-error:
- close(fd);
- free(full);
- return ret;
+ free(p);
+ return r == 0;
}
int
-cmp(void *pa, void *pb)
+indexed(char *path, int isdir)
{
- return strcmp(*(char **)pa, *(char **)pb);
-}
+ int lo, hi, mid, n, r;
+ char *s;
-void
-dedup(Wres *r)
-{
- int i, o;
-
- if(r->npath <= 1)
- return;
- o = 0;
- qsort(r->path, r->npath, sizeof(r->path[0]), cmp);
- for(i = 1; i < r->npath; i++)
- if(strcmp(r->path[o], r->path[i]) != 0)
- r->path[++o] = r->path[i];
- r->npath = o + 1;
+ if(!useidx){
+ s = smprint("%s/%s", bdir, path);
+ r = access(s, AEXIST);
+ free(s);
+ return r == 0;
+ }
+ s = path;
+ if(isdir)
+ s = smprint("%s/", path);
+ r = -1;
+ lo = 0;
+ hi = nidx-1;
+ n = strlen(s);
+ while(lo <= hi){
+ mid = (hi + lo) / 2;
+ if(isdir)
+ r = strncmp(s, idx[mid].path, n);
+ else
+ r = strcmp(s, idx[mid].path);
+ if(r < 0)
+ hi = mid-1;
+ else if(r > 0)
+ lo = mid+1;
+ else
+ break;
+ }
+ if(isdir)
+ free(s);
+ return r == 0;
}
int
-sameqid(Dir *d, char *qf)
+idxcmp(void *pa, void *pb)
{
- char indexqid[64], fileqid[64], *p;
- int fd, n;
+ Idxent *a, *b;
+ int c;
- if(!d)
- return 0;
- if((fd = open(qf, OREAD)) == -1)
- return 0;
- if((n = readn(fd, indexqid, sizeof(indexqid) - 1)) == -1)
- return 0;
- indexqid[n] = 0;
- close(fd);
- if((p = strpbrk(indexqid, " \t\n\r")) != nil)
- *p = 0;
-
- snprint(fileqid, sizeof(fileqid), "%ullx.%uld.%.2uhhx",
- d->qid.path, d->qid.vers, d->qid.type);
-
- if(strcmp(indexqid, fileqid) == 0)
- return 1;
- return 0;
+ a = (Idxent*)pa;
+ b = (Idxent*)pb;
+ if((c = strcmp(a->path, b->path)) != 0)
+ return c;
+ /* order is unique */
+ return a-> order < b->order ? -1 : 1;
}
-void
-writeqid(Dir *d, char *qf)
-{
- int fd;
-
- if((fd = create(qf, OWRITE, 0666)) == -1)
- return;
- fprint(fd, "%ullx.%uld.%.2uhhx\n",
- d->qid.path, d->qid.vers, d->qid.type);
- close(fd);
-}
-
+/*
+ * compares whether the indexed entry 'a'
+ * has the same contents and mode as
+ * the entry on disk 'b'; if the indexed
+ * entry is nil, does a deep comparison
+ * of the checked out file and the file
+ * checked in.
+ */
int
-samedata(char *pa, char *pb)
+samedata(Idxent *a, Idxent *b)
{
- char ba[32*1024], bb[32*1024];
+ char *gitpath, ba[IOUNIT], bb[IOUNIT];
int fa, fb, na, nb, same;
+ Dir *da, *db;
+ if(a != nil){
+ if(a->qid.path == b->qid.path
+ && a->qid.vers == b->qid.vers
+ && a->qid.type == b->qid.type
+ && a->mode == b->mode
+ && a->mode != 0)
+ return 1;
+ }
+
same = 0;
- fa = open(pa, OREAD);
- fb = open(pb, OREAD);
- if(fa == -1 || fb == -1){
+ da = nil;
+ db = nil;
+ if((gitpath = smprint("%s/%s", bdir, b->path)) == nil)
+ sysfatal("smprint: %r");
+ fa = open(gitpath, OREAD);
+ fb = open(b->path, OREAD);
+ if(fa == -1 || fb == -1)
goto mismatch;
- }
+ da = dirfstat(fa);
+ db = dirfstat(fb);
+ if(da == nil || db == nil)
+ goto mismatch;
+ if((da->mode&0100) != (db->mode&0100))
+ goto mismatch;
+ if(da->length != db->length)
+ goto mismatch;
while(1){
if((na = readn(fa, ba, sizeof(ba))) == -1)
goto mismatch;
@@ -197,8 +203,16 @@
if(memcmp(ba, bb, na) != 0)
goto mismatch;
}
+ if(a != nil){
+ a->qid = db->qid;
+ a->mode = db->mode;
+ staleidx = 1;
+ }
same = 1;
+
mismatch:
+ free(da);
+ free(db);
if(fa != -1)
close(fa);
if(fb != -1)
@@ -206,10 +220,139 @@
return same;
}
+int
+loadent(char *dir, Dir *d, int fullpath)
+{
+ char *path;
+ int ret, isdir;
+ Idxent *e;
+
+ if(fullpath)
+ path = strdup(dir);
+ else
+ path = smprint("%s/%s", dir, d->name);
+ if(path == nil)
+ sysfatal("smprint: %r");
+
+ cleanname(path);
+ if(strncmp(path, ".git/", 5) == 0){
+ free(path);
+ return 0;
+ }
+ ret = 0;
+ isdir = d->qid.type & QTDIR;
+ if((printflg & Uflg) == 0 && !indexed(path, isdir)){
+ free(path);
+ return 0;
+ }
+ if(isdir){
+ ret = loadwdir(path);
+ free(path);
+ }else{
+ if(nwdir == wdirsz){
+ wdirsz += wdirsz/2;
+ wdir = erealloc(wdir, wdirsz*sizeof(Idxent));
+ }
+ e = wdir + nwdir;
+ e->path = path;
+ e->qid = d->qid;
+ e->mode = d->mode;
+ e->order = nwdir;
+ e->state = 'T';
+ nwdir++;
+ }
+ return ret;
+}
+
+int
+loadwdir(char *path)
+{
+ int fd, ret, i, n;
+ Dir *d, *e;
+
+ d = nil;
+ e = nil;
+ ret = -1;
+ cleanname(path);
+ if(strncmp(path, ".git/", 5) == 0)
+ return 0;
+ if((fd = open(path, OREAD)) < 0)
+ goto error;
+ if((e = dirfstat(fd)) == nil)
+ sysfatal("fstat: %r");
+ if(e->qid.type & QTDIR)
+ while((n = dirread(fd, &d)) > 0){
+ for(i = 0; i < n; i++)
+ if(loadent(path, &d[i], 0) == -1)
+ goto error;
+ free(d);
+ }
+ else{
+ if(loadent(path, e, 1) == -1)
+ goto error;
+ }
+ ret = 0;
+error:
+ free(e);
+ if(fd != -1)
+ close(fd);
+ return ret;
+}
+
+int
+pfxmatch(char *p, char **pfx, int *pfxlen, int npfx)
+{
+ int i;
+
+ if(p == nil)
+ return 0;
+ if(npfx == 0)
+ return 1;
+ for(i = 0; i < npfx; i++){
+ if(strncmp(p, pfx[i], pfxlen[i]) != 0)
+ continue;
+ if(p[pfxlen[i]] == '/' || p[pfxlen[i]] == 0)
+ return 1;
+ if(strcmp(pfx[i], ".") == 0 || *pfx[i] == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+char*
+reporel(char *s)
+{
+ char *p;
+ int n;
+
+ if(*s == '/')
+ s = strdup(s);
+ else
+ s = smprint("%s/%s", wdirpath, s);
+ p = cleanname(s);
+ n = strlen(repopath);
+ if(strncmp(s, repopath, n) != 0)
+ sysfatal("path outside repo: %s", s);
+ p += n;
+ if(*p == '/')
+ p++;
+ memmove(s, p, strlen(p)+1);
+ return s;
+}
+
void
+show(Biobuf *o, int flg, char *str, char *path)
+{
+ dirty |= flg;
+ if(!quiet && (printflg & flg))
+ Bprint(o, "%s%s\n", str, path);
+}
+
+void
usage(void)
{
- fprint(2, "usage: %s [-qbc] [-f filt] [paths...]\n", argv0);
+ fprint(2, "usage: %s [-qbc] [-f filt] [-b base] [paths...]\n", argv0);
exits("usage");
}
@@ -216,12 +359,22 @@
void
main(int argc, char **argv)
{
- char *rpath, *tpath, *bpath, buf[8], repo[512];
- char *p, *e;
- int i, dirty;
- Wres r;
- Dir *d;
+ char *p, *e, *ln, *base, **argrel, *parts[4], xbuf[8];
+ int i, j, c, line, wfd, *argn;
+ Biobuf *f, *o, *w;
+ Hash h, hd;
+ Dir rn;
+ gitinit();
+ if(access(".git/fs/ctl", AEXIST) != 0)
+ sysfatal("no running git/fs");
+ if(getwd(wdirpath, sizeof(wdirpath)) == nil)
+ sysfatal("getwd: %r");
+ if(findrepo(repopath, sizeof(repopath), &nrel) == -1)
+ sysfatal("find root: %r");
+ if(chdir(repopath) == -1)
+ sysfatal("chdir: %r");
+
ARGBEGIN{
case 'q':
quiet++;
@@ -231,6 +384,7 @@
tstr = "";
mstr = "";
astr = "";
+ ustr = "";
break;
case 'f':
for(p = EARGF(usage()); *p; p++)
@@ -239,95 +393,184 @@
case 'A': printflg |= Aflg; break;
case 'M': printflg |= Mflg; break;
case 'R': printflg |= Rflg; break;
+ case 'U': printflg |= Uflg; break;
default: usage(); break;
}
break;
+ case 'b':
+ useidx = 0;
+ base = EARGF(usage());
+ if(resolveref(&h, base) == -1)
+ sysfatal("no such ref '%s'", base);
+ /* optimization: we're a lot faster when using the index */
+ if(resolveref(&hd, "HEAD") == 0 && hasheq(&h, &hd))
+ useidx = 1;
+ bdir = smprint(".git/fs/object/%H/tree", h);
+ break;
default:
usage();
- }ARGEND
+ }ARGEND;
- if(findrepo(repo, sizeof(repo)) == -1)
- sysfatal("find root: %r");
- if(chdir(repo) == -1)
- sysfatal("chdir: %r");
- if(access(".git/fs/ctl", AEXIST) != 0)
- sysfatal("no running git/fs");
- dirty = 0;
- memset(&r, 0, sizeof(r));
if(printflg == 0)
printflg = Tflg | Aflg | Mflg | Rflg;
- if(argc == 0){
- if(access(TDIR, AEXIST) == 0 && readpaths(&r, TDIR, "") == -1)
- sysfatal("read tracked: %r");
- if(access(RDIR, AEXIST) == 0 && readpaths(&r, RDIR, "") == -1)
- sysfatal("read removed: %r");
- }else{
- for(i = 0; i < argc; i++){
- tpath = smprint(TDIR"/%s", argv[i]);
- rpath = smprint(RDIR"/%s", argv[i]);
- if((d = dirstat(tpath)) == nil && (d = dirstat(rpath)) == nil)
- goto nextarg;
- if(d->mode & DMDIR){
- readpaths(&r, TDIR, argv[i]);
- readpaths(&r, RDIR, argv[i]);
- }else{
- grow(&r);
- r.path[r.npath++] = estrdup(argv[i]);
+
+ nidx = 0;
+ idxsz = 32;
+ idx = emalloc(idxsz*sizeof(Idxent));
+ nwdir = 0;
+ wdirsz = 32;
+ wdir = emalloc(wdirsz*sizeof(Idxent));
+ argrel = emalloc(argc*sizeof(char*));
+ argn = emalloc(argc*sizeof(int));
+ for(i = 0; i < argc; i++){
+ argrel[i] = reporel(argv[i]);
+ argn[i] = strlen(argrel[i]);
+ }
+ if((o = Bfdopen(1, OWRITE)) == nil)
+ sysfatal("open out: %r");
+ if(useidx){
+ if((f = Bopen(".git/INDEX9", OREAD)) == nil){
+ fprint(2, "open index: %r\n");
+ if(access(".git/index9", AEXIST) == 0){
+ fprint(2, "index format conversion needed:\n");
+ fprint(2, "\tcd %s && git/fs\n", repopath);
+ fprint(2, "\t@{cd .git/index9/removed >[2]/dev/null && walk -f | sed 's/^/R NOQID 0 /'} >> .git/INDEX9\n");
+ fprint(2, "\t@{cd .git/fs/HEAD/tree && walk -f | sed 's/^/T NOQID 0 /'} >> .git/INDEX9\n");
}
-nextarg:
- free(tpath);
- free(rpath);
- free(d);
+ exits("noindex");
}
+ line = 0;
+ while((ln = Brdstr(f, '\n', 1)) != nil){
+ line++;
+ /* allow blank lines */
+ if(ln[0] == 0 || ln[0] == '\n')
+ continue;
+ if(getfields(ln, parts, nelem(parts), 0, " \t") != nelem(parts))
+ sysfatal(".git/INDEX9:%d: corrupt index", line);
+ if(nidx == idxsz){
+ idxsz += idxsz/2;
+ idx = realloc(idx, idxsz*sizeof(Idxent));
+ }
+ cleanname(parts[3]);
+ if(strncmp(parts[3], ".git/", 5) == 0){
+ staleidx = 1;
+ free(ln);
+ continue;
+ }
+ idx[nidx].state = *parts[0];
+ idx[nidx].qid = parseqid(parts[1]);
+ idx[nidx].mode = strtol(parts[2], nil, 8);
+ idx[nidx].path = strdup(parts[3]);
+ idx[nidx].order = nidx;
+ nidx++;
+ free(ln);
+ }
+ qsort(idx, nidx, sizeof(Idxent), idxcmp);
}
- dedup(&r);
- for(i = 0; i < r.npath; i++){
- p = r.path[i];
- d = dirstat(p);
- if(d && d->mode & DMDIR)
- goto next;
- rpath = smprint(RDIR"/%s", p);
- tpath = smprint(TDIR"/%s", p);
- bpath = smprint(HDIR"/%s", p);
- /* Fast path: we don't want to force access to the rpath. */
- if(d && sameqid(d, tpath)) {
- if(!quiet && (printflg & Tflg))
- print("%s%s\n", tstr, p);
- }else{
- if(d == nil || access(rpath, AEXIST) == 0 ){
- if(access(bpath, AEXIST) == 0){
- dirty |= Rflg;
- if(!quiet && (printflg & Rflg))
- print("%s%s\n", rstr, p);
+ for(i = 0; i < argc; i++){
+ argrel[i] = reporel(argv[i]);
+ argn[i] = strlen(argrel[i]);
+ }
+ if(argc == 0)
+ loadwdir(".");
+ else for(i = 0; i < argc; i++)
+ loadwdir(argrel[i]);
+ qsort(wdir, nwdir, sizeof(Idxent), idxcmp);
+ for(i = 0; i < argc; i++){
+ argrel[i] = reporel(argv[i]);
+ argn[i] = strlen(argrel[i]);
+ }
+ i = 0;
+ j = 0;
+ while(i < nidx || j < nwdir){
+ /* find the last entry we tracked for a path */
+ while(i+1 < nidx && strcmp(idx[i].path, idx[i+1].path) == 0){
+ staleidx = 1;
+ i++;
+ }
+ while(j+1 < nwdir && strcmp(wdir[j].path, wdir[j+1].path) == 0)
+ j++;
+ if(i < nidx && !pfxmatch(idx[i].path, argrel, argn, argc)){
+ i++;
+ continue;
+ }
+ if(i >= nidx)
+ c = 1;
+ else if(j >= nwdir)
+ c = -1;
+ else
+ c = strcmp(idx[i].path, wdir[j].path);
+ /* exists in both index and on disk */
+ if(c == 0){
+ if(idx[i].state == 'R'){
+ if(checkedin(&idx[i], 0))
+ show(o, Rflg, rstr, idx[i].path);
+ else{
+ idx[i].state = 'U';
+ staleidx = 1;
}
- }else if(access(bpath, AEXIST) == -1) {
- dirty |= Aflg;
- if(!quiet && (printflg & Aflg))
- print("%s%s\n", astr, p);
- }else if(samedata(p, bpath)){
- if(!quiet && (printflg & Tflg))
- print("%s%s\n", tstr, p);
- writeqid(d, tpath);
- }else{
- dirty |= Mflg;
- if(!quiet && (printflg & Mflg))
- print("%s%s\n", mstr, p);
- }
+ }else if(idx[i].state == 'A' && !checkedin(&idx[i], 1))
+ show(o, Aflg, astr, idx[i].path);
+ else if(!samedata(&idx[i], &wdir[j]))
+ show(o, Mflg, mstr, idx[i].path);
+ else
+ show(o, Tflg, tstr, idx[i].path);
+ i++;
+ j++;
+ /* only exists in index */
+ }else if(c < 0){
+ if(checkedin(&idx[i], 0))
+ show(o, Rflg, rstr, idx[i].path);
+ i++;
+ /* only exists on disk */
+ }else{
+ if(!useidx && checkedin(&wdir[j], 0)){
+ if(samedata(nil, &wdir[j]))
+ show(o, Tflg, tstr, wdir[j].path);
+ else
+ show(o, Mflg, mstr, wdir[j].path);
+ }else if(printflg & Uflg && pfxmatch(idx[i].path, argrel, argn, argc))
+ show(o, Uflg, ustr, wdir[j].path);
+ j++;
}
- free(rpath);
- free(tpath);
- free(bpath);
-next:
- free(d);
}
+ Bterm(o);
+
+ if(useidx && staleidx)
+ if((wfd = create(".git/INDEX9.new", OWRITE, 0644)) != -1){
+ if((w = Bfdopen(wfd, OWRITE)) == nil){
+ close(wfd);
+ goto Nope;
+ }
+ for(i = 0; i < nidx; i++){
+ while(i+1 < nidx && strcmp(idx[i].path, idx[i+1].path) == 0)
+ i++;
+ if(idx[i].state == 'U')
+ continue;
+ Bprint(w, "%c %Q %o %s\n",
+ idx[i].state,
+ idx[i].qid,
+ idx[i].mode,
+ idx[i].path);
+ }
+ Bterm(w);
+ nulldir(&rn);
+ rn.name = "INDEX9";
+ if(remove(".git/INDEX9") == -1)
+ goto Nope;
+ if(dirwstat(".git/INDEX9.new", &rn) == -1)
+ sysfatal("rename: %r");
+ }
+
+Nope:
if(!dirty)
exits(nil);
- p = buf;
- e = buf + sizeof(buf);
+ p = xbuf;
+ e = p + sizeof(xbuf);
for(i = 0; (1 << i) != Tflg; i++)
if(dirty & (1 << i))
- p = seprint(p, e, "%c", "DMAT"[i]);
- exits(buf);
+ p = seprint(p, e, "%c", "RMAUT"[i]);
+ exits(xbuf);
}