From 72ba278a188950bf0d09f4459e396269d6ec8505 Mon Sep 17 00:00:00 2001 From: Ori Bernstein Date: Mon, 28 Nov 2022 04:49:57 +0000 Subject: [PATCH] git: sync with 9front --- add | 6 +- branch | 22 +-- clone | 16 +- commit | 22 +-- common.rc | 72 ++++++--- compat | 80 +++++++++- delta.c | 18 +-- diff | 22 ++- export | 5 +- fs.c | 127 +++++++++------ git.h | 27 +++- import | 55 ++++--- init | 2 +- log.c | 114 +++++--------- merge | 5 +- mkfile | 3 +- pack.c | 121 ++++++++------ proto.c | 35 ++++- pull | 28 ++-- query.c | 1 + rebase | 4 +- ref.c | 460 +++++++++++++++++++----------------------------------- revert | 8 +- save.c | 81 ++++++---- send.c | 14 +- serve.c | 39 ++--- util.c | 121 ++++++++++++++ walk.c | 10 +- 28 files changed, 845 insertions(+), 673 deletions(-) diff --git a/add b/add index 53e24d9..b980d7d 100644 --- a/add +++ b/add @@ -16,11 +16,11 @@ if(~ $remove 1){ if(~ $#* 0) exec aux/usage -paths=`$nl{cleanname -d $gitrel $*} +paths=`$nl{cleanname -d $gitrel $* | drop $gitroot} if(~ $add tracked) - files=`$nl{walk -f $paths} + files=`$nl{walk -f ./$paths} if not - files=`$nl{cd .git/index9/tracked/ && walk -f $paths} + files=`$nl{cd .git/index9/tracked/ && walk -f ./$paths} for(f in $files){ if(! ~ `$nl{cleanname $f} .git/*){ diff --git a/branch b/branch index 6693d7c..01c4117 100755 --- a/branch +++ b/branch @@ -32,7 +32,7 @@ if not orig=`{git/query HEAD} if (~ $#baseref 1) base=`{git/query $baseref} || exit 'bad base' -if not if(test -e .git/$new) +if not if(~ $#newbr 0) base=`{git/query $new} if not base=`{git/query HEAD} @@ -41,7 +41,6 @@ if(~ $#newbr 0){ if(! ~ $#baseref 0) die update would clobber $branch with $baseref baseref=`$nl{echo -n $new | sed s@refs/heads/@refs/remotes/origin/@} - echo $baseref if(! test -e .git/$new) if(! base=`{git/query $baseref}) die could not find branch $branch @@ -49,9 +48,12 @@ if(~ $#newbr 0){ modified=`$nl{git/query -c HEAD $base | grep '^[^-]' | subst '^..'} deleted=`$nl{git/query -c HEAD $base | grep '^-' | subst '^..'} -if(! ~ $#modified 0 || ! ~ $#deleted 0 && ~ $#merge 0){ - git/walk -fRMA $modified $deleted || - die 'uncommited changes would be clobbered' +# if we're not merging, don't clobber existing changes. +if(~ $#merge 0){ + if(! ~ $#modified 0 || ! ~ $#deleted 0){ + git/walk -fRMA $modified $deleted || + die 'uncommitted changes would be clobbered' + } } if(~ $delete 1){ rm -f .git/$new @@ -67,8 +69,6 @@ if(! ~ $#stay 0){ } basedir=`{git/query -p $base} dirtypaths=() -if(! ~ $#modified 0 || ! ~ $#deleted 0) - dirtypaths=`$nl{git/walk -cfRMA $modified $deleted} if(! ~ $#modified 0 || ! ~ $#deleted 0) dirtypaths=`$nl{git/walk -cfRMA $modified $deleted} if(~ $#dirtypaths 0) @@ -100,10 +100,9 @@ for(m in $cleanpaths){ rm -rf .git/index9/tracked/$m } if(~ $b file){ - if(cp -x -- $basedir/tree/$m $m) - walk -eq $m > .git/index9/tracked/$m - if not - echo -n > .git/index9/tracked/$m + cp -x -- $basedir/tree/$m $m + walk -eq $m > .git/index9/tracked/$m + touch $m } } @@ -119,4 +118,5 @@ if(! ~ $#deleted 0){ } echo ref: $new > .git/HEAD +echo $new: `{git/query $new} exit '' diff --git a/clone b/clone index 47043d5..93e7f0b 100755 --- a/clone +++ b/clone @@ -7,7 +7,7 @@ eval `''{aux/getflags $*} || exec aux/usage if(~ $debug 1) debug=(-d) -remote=`{echo $1 | subst -g '/*$'} +remote=`{echo $1 | sed 's@/*$@@'} local=$2 if(~ $#remote 0) @@ -33,7 +33,7 @@ fn clone{ echo '[remote "origin"]' echo ' url='$remote } - {git/fetch $debug $branchflag $remote >[2=3] | awk ' + {git/get $debug $branchflag $remote >[2=3] | awk ' BEGIN{ headref="" if(ENVIRON["branch"] != "") @@ -79,19 +79,17 @@ fn clone{ tree=.git/fs/HEAD/tree lbranch=`{git/branch} - rbranch=`{echo $lbranch | subst '^heads' 'remotes/origin'} + rbranch=`{echo $lbranch | subst 'heads' 'remotes/origin'} echo checking out repository... if(test -f .git/refs/$rbranch){ cp .git/refs/$rbranch .git/refs/$lbranch git/fs @ {builtin cd $tree && tar cif /fd/1 .} | @ {tar xf /fd/0} \ || die 'checkout failed:' $status - for(f in `$nl{walk -f $tree | subst '^'$tree'/*'}){ - if(! ~ $#f 0){ - idx=.git/index9/tracked/$f - mkdir -p `$nl{basename -d $idx} - walk -eq $f > $idx - } + for(f in `$nl{walk -f $tree | drop $tree}){ + idx=.git/index9/tracked/$f + mkdir -p `$nl{basename -d $idx} + walk -eq ./$f > $idx } } if not{ diff --git a/commit b/commit index 5b23f77..15f85a5 100644 --- a/commit +++ b/commit @@ -2,21 +2,6 @@ rfork ne . /sys/lib/git/common.rc -fn whoami{ - name=`{git/conf user.name} - email=`{git/conf user.email} - if(test -f /adm/keys.who){ - if(~ $name '') - name=`{awk -F'|' '$1=="'$user'" {x=$3} END{print x}' /dev/null) status=gone if not if (~ $3 /dev/null && cmp $1 $2>/dev/null) status=gone - if not + if not { + mergedperms='-x' + if(test -x $2){ + if(test -x $1 -a -x $3) + mergedperms='+x' + } + if not{ + if(test -x $1 -o -x $3) + mergedperms='+x' + } status=() + } +} + +fn whoami{ + name=`$nl{git/conf user.name} + email=`$nl{git/conf user.email} + if(test -f /adm/keys.who){ + if(~ $name '') + name=`$nl{awk -F'|' '$1=="'$user'" {x=$3} END{print x}' $tmp) echo merge needed: $out >[1=2] - if(present $ours $base $theirs){ + if(mergeperm $ours $base $theirs){ mv $tmp $out git/add $out + chmod $mergedperms $out } if not { rm -f $tmp $out @@ -77,13 +103,13 @@ fn gitup{ if(~ $#gitroot 0) die 'not a git repository' gitfs=$gitroot/.git/fs - gitrel=`{pwd | subst '^'$"gitroot'/?'} + gitrel=`{pwd | drop $gitroot | sed 's@^/@@'} if(~ $#gitrel 0) gitrel='.' - cd $gitroot + if(! cd $gitroot) + die cd $gitroot: no repo there startfs=() - if(! test -d $gitfs) - mkdir -p $gitfs + mkdir -p $gitfs if(! test -e $gitfs/ctl) startfs=true if(! grep -s '^repo '$gitroot'$' $gitfs/ctl >[2]/dev/null) diff --git a/compat b/compat index 15eb261..9e08e76 100755 --- a/compat +++ b/compat @@ -4,12 +4,14 @@ rfork e opts=() args=() +nl=' +' fn cmd_init{ - while(~ $#* 0){ + while(! ~ $#* 0){ switch($1){ case --bare - opts=(-b) + # ignore case -- # go likes to use these case -* @@ -19,7 +21,6 @@ fn cmd_init{ } shift } - ls >[1=2] git/init $opts $args } @@ -94,7 +95,7 @@ fn cmd_rev-parse{ echo `{dcmd git9/branch | sed s@^heads/@@g} shift case * - dprint option $opt + die unknown option $opt } shift } @@ -112,6 +113,15 @@ fn cmd_show-ref{ echo `{cat $gitroot/.git/refs/$b} refs/$b } +fn cmd_rev-parse{ + switch($1){ + case --git-dir + echo `{git/conf -r}^/.git + case * + die 'unknown rev-parse '$* + } +} + fn cmd_remote{ if({! ~ $#* 3 && ! ~ $#* 4} || ! ~ $1 add) die unimplemented remote cmd $* @@ -125,10 +135,60 @@ fn cmd_remote{ } } +fn cmd_log{ + count=() + format='' + while(~ $1 -*){ + switch($1){ + case --format + format=$2 + shift + case '--format='* + format=`{echo $1 | sed 's/--format=//g'} + case -n + count=-n$2 + shift + case -n* + count=$1 + case * + dprint option $opt + } + shift + } + @{cd $gitroot && git/fs} + switch($format){ + case '' + git/log $count + case '%H:%ct' + for(c in `{git/log -s $count| awk '{print $1}'}) + echo $c:`{mtime $gitroot/.git/fs/object/$c/msg} + case '%h %cd' + for(c in `{git/log -s $count| awk '{print $1}'}) + echo $c `{date `{mtime $gitroot/.git/fs/object/$c/msg}} + } + +} + +fn cmd_show{ + cmd_log -n1 $* +} + +fn cmd_ls-remote{ + if(~ $1 -q) + shift + remote=`$nl{git/conf 'remote "'$1'".url'} + if(~ $#remote 0) + remote=$1 + git/get -l $remote | awk '/^remote/{print $3"\t"$2}' +} + fn cmd_version{ echo git version 2.2.0 } +fn cmd_status{ + echo +} fn usage{ echo 'git ' >[1=2] @@ -149,10 +209,20 @@ if(~ $0 *compat){ exec rc } +if(~ $#gitcompatdebug 1) + echo running $* >>/tmp/gitlog + +if(~ $1 -c) + shift 2 +if(~ $1 -c*) + shift 1 if(! test -f '/env/fn#cmd_'$1) die git $1: commmand not implemented if(! ~ $1 init && ! ~ $1 clone) gitroot=`{git/conf -r} || die repo -cmd_$1 $*(2-) +if(~ $#gitcompatdebug 1) + cmd_$1 $*(2-) | tee >>/tmp/gitlog +if not + cmd_$1 $*(2-) exit '' diff --git a/delta.c b/delta.c index 32af58b..1841393 100644 --- a/delta.c +++ b/delta.c @@ -7,7 +7,6 @@ enum { Minchunk = 128, Maxchunk = 8192, Splitmask = (1<<8)-1, - }; static u32int geartab[] = { @@ -48,9 +47,7 @@ static u32int geartab[] = { static u64int hash(void *p, int n) { - uchar buf[SHA1dlen]; - sha1((uchar*)p, n, buf, nil); - return GETBE64(buf); + return murmurhash2(p, n); } static void @@ -172,23 +169,26 @@ emitdelta(Delta **pd, int *nd, int cpy, int off, int len) static int stretch(Dtab *dt, Dblock *b, uchar *s, uchar *e, int n) { - uchar *p, *q, *eb; + uchar *p0, *p, *q, *eb; if(b == nil) return n; p = s + n; q = dt->base + b->off + n; - eb = dt->base + dt->nbase; - while(n < (1<<24)-1){ + p0 = p; + if(dt->nbase < (1<<24)-1) + eb = dt->base + dt->nbase; + else + eb = dt->base + (1<<24)-1; + while(1){ if(p == e || q == eb) break; if(*p != *q) break; p++; q++; - n++; } - return n; + return n + (p - p0); } Delta* diff --git a/diff b/diff index 075ea3e..28e69d6 100644 --- a/diff +++ b/diff @@ -26,12 +26,22 @@ fn lsdirty { git/query -c $commit HEAD | subst '^..' } +showed=() +mntgen /mnt/scratch +bind $branch/tree/ /mnt/scratch/a +bind . /mnt/scratch/b for(f in `$nl{lsdirty | sort | uniq}){ - orig=$branch/tree/$f - if(! test -f $orig) - orig=/dev/null - if(! test -f $f) - f=/dev/null - diff -u $orig $f + if(~ $#showed 0){ + echo diff `{git/query $commit} uncommitted + showed=1 + } + cd /mnt/scratch + a=a/$f + b=b/$f + if(! test -f a/$f) + a=/dev/null + if(! test -f b/$f) + b=/dev/null + diff -u $a $b } exit '' diff --git a/export b/export index 145e625..f47c7d4 100755 --- a/export +++ b/export @@ -41,9 +41,8 @@ for(c in $commits){ if(test -d $cp/tree) bind $cp/tree b - echo From $c echo From: `{cat $cp/author} - echo Date: `{date -um `{mtime $cp/author | awk '{print $1}'}} + echo Date: `{date -uf'WW, DD MMM YYYY hh:mm:ss Z' `{walk -em $cp/author}} <$cp/msg awk ' NR == 1 { n = ENVIRON["n"] @@ -72,7 +71,7 @@ for(c in $commits){ b=b/$f if(! test -e $b) b=/dev/null - ape/diff -urN $a $b + diff -ur $a $b } } >$patchfile if(~ $#patchdir 0){ diff --git a/fs.c b/fs.c index 945a720..7e6b4bf 100644 --- a/fs.c +++ b/fs.c @@ -12,12 +12,13 @@ enum { Qhead, Qbranch, Qcommit, - Qcommitmsg, - Qcommitparent, - Qcommittree, - Qcommitdata, - Qcommithash, - Qcommitauthor, + Qmsg, + Qparent, + Qtree, + Qcdata, + Qhash, + Qauthor, + Qcommitter, Qobject, Qctl, Qmax, @@ -69,13 +70,14 @@ char *qroot[] = { "ctl", }; -#define Eperm "permission denied"; -#define Eexist "does not exist"; -#define E2long "path too long"; -#define Enodir "not a directory"; -#define Erepo "unable to read repo"; -#define Egreg "wat"; -#define Ebadobj "invalid object"; +#define Eperm "permission denied" +#define Eexist "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" char gitdir[512]; char *username; @@ -284,23 +286,23 @@ gcommitgen(int i, Dir *d, void *p) d->mode = 0755 | DMDIR; d->name = estrdup9p("tree"); d->qid.type = QTDIR; - d->qid.path = qpath(c, i, o->id, Qcommittree); + d->qid.path = qpath(c, i, o->id, Qtree); break; case 1: d->name = estrdup9p("parent"); - d->qid.path = qpath(c, i, o->id, Qcommitparent); + d->qid.path = qpath(c, i, o->id, Qparent); break; case 2: d->name = estrdup9p("msg"); - d->qid.path = qpath(c, i, o->id, Qcommitmsg); + d->qid.path = qpath(c, i, o->id, Qmsg); break; case 3: d->name = estrdup9p("hash"); - d->qid.path = qpath(c, i, o->id, Qcommithash); + d->qid.path = qpath(c, i, o->id, Qhash); break; case 4: d->name = estrdup9p("author"); - d->qid.path = qpath(c, i, o->id, Qcommitauthor); + d->qid.path = qpath(c, i, o->id, Qauthor); break; default: return -1; @@ -376,19 +378,20 @@ objread(Req *r, Gitaux *aux) static void readcommitparent(Req *r, Object *o) { - char *buf, *p; + char *buf, *p, *e; int i, n; - n = o->commit->nparent * (40 + 2); + /* 40 bytes per hash, 1 per nl, 1 for terminator */ + n = o->commit->nparent * (40 + 1) + 1; buf = emalloc(n); p = buf; + e = buf + n; for (i = 0; i < o->commit->nparent; i++) - p += sprint(p, "%H\n", o->commit->parent[i]); - readbuf(r, buf, n); + p = seprint(p, e, "%H\n", o->commit->parent[i]); + readbuf(r, buf, p - buf); free(buf); } - static void gitattach(Req *r) { @@ -491,18 +494,20 @@ objwalk1(Qid *q, Object *o, Crumb *p, Crumb *c, char *name, vlong qdir, Gitaux * q->type = 0; c->mtime = o->commit->mtime; c->mode = 0644; - assert(qdir == Qcommit || qdir == Qobject || qdir == Qcommittree || qdir == Qhead); + assert(qdir == Qcommit || qdir == Qobject || qdir == Qtree || qdir == Qhead || qdir == Qcommitter); if(strcmp(name, "msg") == 0) - q->path = qpath(p, 0, o->id, Qcommitmsg); + q->path = qpath(p, 0, o->id, Qmsg); else if(strcmp(name, "parent") == 0) - q->path = qpath(p, 1, o->id, Qcommitparent); + q->path = qpath(p, 1, o->id, Qparent); else if(strcmp(name, "hash") == 0) - q->path = qpath(p, 2, o->id, Qcommithash); + q->path = qpath(p, 2, o->id, Qhash); else if(strcmp(name, "author") == 0) - q->path = qpath(p, 3, o->id, Qcommitauthor); + q->path = qpath(p, 3, o->id, Qauthor); + else if(strcmp(name, "committer") == 0) + q->path = qpath(p, 3, o->id, Qcommitter); else if(strcmp(name, "tree") == 0){ q->type = QTDIR; - q->path = qpath(p, 4, o->id, Qcommittree); + q->path = qpath(p, 4, o->id, Qtree); unref(c->obj); c->mode = DMDIR | 0755; c->obj = readobject(o->commit->tree); @@ -620,9 +625,9 @@ gitwalk1(Fid *fid, char *name, Qid *q) e = objwalk1(q, o->obj, o, c, name, Qobject, aux); }else{ if(hparse(&h, name) == -1) - return "invalid object name"; + return Eobject; if((c->obj = readobject(h)) == nil) - return "could not read object"; + return Eobject; if(c->obj->type == GBlob || c->obj->type == GTag){ c->mode = 0644; q->type = 0; @@ -640,14 +645,15 @@ gitwalk1(Fid *fid, char *name, Qid *q) case Qcommit: e = objwalk1(q, o->obj, o, c, name, Qcommit, aux); break; - case Qcommittree: - e = objwalk1(q, o->obj, o, c, name, Qcommittree, aux); + case Qtree: + e = objwalk1(q, o->obj, o, c, name, Qtree, aux); break; - case Qcommitparent: - case Qcommitmsg: - case Qcommitdata: - case Qcommithash: - case Qcommitauthor: + case Qparent: + case Qmsg: + case Qcdata: + case Qhash: + case Qauthor: + case Qcommitter: case Qctl: return Enodir; default: @@ -760,20 +766,24 @@ gitread(Req *r) else dirread9p(r, objgen, aux); break; - case Qcommitmsg: + case Qmsg: readbuf(r, o->commit->msg, o->commit->nmsg); break; - case Qcommitparent: + case Qparent: readcommitparent(r, o); break; - case Qcommithash: + case Qhash: snprint(buf, sizeof(buf), "%H\n", o->hash); readstr(r, buf); break; - case Qcommitauthor: + case Qauthor: snprint(buf, sizeof(buf), "%s\n", o->commit->author); readstr(r, buf); break; + case Qcommitter: + snprint(buf, sizeof(buf), "%s\n", o->commit->committer); + readstr(r, buf); + break; case Qctl: e = readctl(r); break; @@ -785,8 +795,8 @@ gitread(Req *r) objread(r, aux); break; case Qcommit: - case Qcommittree: - case Qcommitdata: + case Qtree: + case Qcdata: objread(r, aux); break; default: @@ -795,6 +805,34 @@ gitread(Req *r) respond(r, e); } +static void +gitopen(Req *r) +{ + Gitaux *aux; + Crumb *c; + + aux = r->fid->aux; + c = crumb(aux, 0); + switch(r->ifcall.mode&3){ + default: + respond(r, "botched mode"); + break; + case OWRITE: + respond(r, Eperm); + break; + case OREAD: + case ORDWR: + respond(r, nil); + break; + case OEXEC: + if((c->mode & 0111) == 0) + respond(r, Eperm); + else + respond(r, nil); + break; + } +} + static void gitstat(Req *r) { @@ -821,6 +859,7 @@ Srv gitsrv = { .attach=gitattach, .walk1=gitwalk1, .clone=gitclone, + .open=gitopen, .read=gitread, .stat=gitstat, .destroyfid=gitdestroyfid, diff --git a/git.h b/git.h index 76c215b..c3ba7b7 100644 --- a/git.h +++ b/git.h @@ -4,6 +4,7 @@ #include #include +typedef struct Capset Capset; typedef struct Conn Conn; typedef struct Hash Hash; typedef struct Delta Delta; @@ -18,12 +19,16 @@ typedef struct Idxent Idxent; typedef struct Objlist Objlist; typedef struct Dtab Dtab; typedef struct Dblock Dblock; +typedef struct Objq Objq; +typedef struct Qelt Qelt; enum { Pathmax = 512, Npackcache = 32, Hashsz = 20, Pktmax = 65536, + KiB = 1024, + MiB = 1024*KiB, }; enum { @@ -151,6 +156,18 @@ struct Objset { int sz; }; +struct Qelt { + Object *o; + vlong ctime; + int color; +}; + +struct Objq { + Qelt *heap; + int nheap; + int heapsz; +}; + struct Dtab { Object *o; uchar *base; @@ -225,9 +242,9 @@ struct Delta { extern Reprog *authorpat; extern Objset objcache; +extern vlong cachemax; extern Hash Zhash; extern int chattygit; -extern int cachemax; extern int interactive; #pragma varargck type "H" Hash @@ -286,6 +303,7 @@ int swapsuffix(char *, int, char *, char *, char *); char *strip(char *); int findrepo(char *, int); int showprogress(int, int); +u64int murmurhash2(void*, usize); /* packing */ void dtinit(Dtab *, Object*); @@ -295,9 +313,16 @@ Delta* deltify(Object*, Dtab*, int*); /* proto handling */ int readpkt(Conn*, char*, int); int writepkt(Conn*, char*, int); +int fmtpkt(Conn*, char*, ...); int flushpkt(Conn*); void initconn(Conn*, int, int); int gitconnect(Conn *, char *, char *); int readphase(Conn *); int writephase(Conn *); void closeconn(Conn *); + +/* queues */ +void qinit(Objq*); +void qclear(Objq*); +void qput(Objq*, Object*, int); +int qpop(Objq*, Qelt*); diff --git a/import b/import index c89d52d..ebb5dbc 100755 --- a/import +++ b/import @@ -7,11 +7,13 @@ fn sigexit { rm -f $diffpath } + fn apply @{ git/fs - email='' - name='' + amail='' + aname='' msg='' + whoami parents='-p'^`{git/query HEAD} branch=`{git/branch} if(test -e $gitfs/branch/$branch/tree) @@ -26,11 +28,11 @@ fn apply @{ } state=="headers" && /^From:/ { sub(/^From:[ \t]*/, "", $0); - name=$0; - email=$0; - sub(/[ \t]*<.*$/, "", name); - sub(/.*/, "", email); + aname=$0; + amail=$0; + sub(/[ \t]*<.*$/, "", aname); + sub(/^[^<]*[^>]*$/, "", amail); } state=="headers" && /^Date:/{ sub(/^Date:[ \t]*/, "", $0) @@ -43,12 +45,19 @@ fn apply @{ } state=="headers" && /^$/ { state="body" - next } - (state=="headers" || state=="body") && (/^diff/ || /^---[ ]*$/){ + (state=="headers" || state=="body") && (/^diff / || /^---( |$)/){ state="diff" } + state=="body" && /^[ ]*$/ { + empty=1 + next + } state=="body" { + if(empty) + printf "\n" > "/env/msg" + empty=0 + sub(/[ ]+$/, "") print > "/env/msg" } state=="diff" { @@ -57,19 +66,27 @@ fn apply @{ END{ if(state != "diff") exit("malformed patch: " state); - if(name == "" || email == "" || date == "" || gotmsg == "") + if(aname == "" || amail == "" || date == "" || gotmsg == "") exit("missing headers"); - printf "%s", name > "/env/name" - printf "%s", email > "/env/email" + printf "%s", aname > "/env/aname" + printf "%s", amail > "/env/amail" printf "%s", date > "/env/date" } ' || die 'could not import:' $status # force re-reading env rc -c ' - echo applying $msg | sed 1q date=`{seconds $date} - if(! files=`$nl{ape/patch -Ep1 < $diffpath | grep ''^patching file'' | sed ''s/^patching file `(.*)''''/\1/''}) + files=`$nl{patch -np1 < $diffpath} + if(! git/walk -q $files){ + >[1=2] { + echo patch would clobber files: + git/walk $files + exit clobber + } + } + echo applying $msg | sed 1q + if(! files=`$nl{patch -p1 < $diffpath}) die ''patch failed'' for(f in $files){ if(test -e $f) @@ -79,8 +96,10 @@ fn apply @{ } git/walk -fRMA $files if(~ $#nocommit 0){ - hash=`{git/save -n $name -e $email -m $msg -d $date $parents $files} - echo $hash > $refpath + if(hash=`{git/save -n $aname -e $amail -N $name -E $email -m $msg -d $date $parents $files}){ + echo $hash > $refpath + rm -f .git/index9/removed/$files + } } status='''' ' @@ -93,7 +112,7 @@ eval `''{aux/getflags $*} || exec aux/usage patches=(/fd/0) if(! ~ $#* 0) - patches=$* + patches=`{cleanname -d $gitrel $*} for(p in $patches){ # upas serves the decoded header and body separately, # so we cat them together when applying a upas message. @@ -102,6 +121,6 @@ for(p in $patches){ if(test -d $p && test -f $p/header && test -f $p/body) {{cat $p/header; echo; cat $p/body} | apply} || die $status if not - apply < $p || die $status + apply < $p } exit '' diff --git a/init b/init index 7eca4ce..50fa027 100755 --- a/init +++ b/init @@ -20,7 +20,7 @@ if(~ $#upstream 0){ } mkdir -p $dir/.git/refs/^(heads remotes) -mkdir -p $dir/.git/fs +mkdir -p $dir/.git/^(fs objects) >$dir/.git/config { echo '[core]' echo ' repositoryformatversion = p9.0' diff --git a/log.c b/log.c index 2a0692a..3336503 100644 --- a/log.c +++ b/log.c @@ -14,11 +14,10 @@ Biobuf *out; char *queryexpr; char *commitid; int shortlog; +int msgcount = -1; -Object **heap; -int nheap; -int heapsz; Objset done; +Objq objq; Pfilt *pathfilt; void @@ -65,7 +64,7 @@ lookup(Pfilt *pf, Object *o) } int -filtermatch1(Pfilt *pf, Object *t, Object *pt) +matchesfilter1(Pfilt *pf, Object *t, Object *pt) { Object *a, *b; Hash ha, hb; @@ -89,7 +88,7 @@ filtermatch1(Pfilt *pf, Object *t, Object *pt) sysfatal("read %H: %r", ha); if((b = readobject(hb)) == nil) sysfatal("read %H: %r", hb); - r = filtermatch1(&pf->sub[i], a, b); + r = matchesfilter1(&pf->sub[i], a, b); unref(a); unref(b); if(r) @@ -99,11 +98,12 @@ filtermatch1(Pfilt *pf, Object *t, Object *pt) } int -filtermatch(Object *o) +matchesfilter(Object *o) { Object *t, *p, *pt; int i, r; + assert(o->type == GCommit); if(pathfilt == nil) return 1; if((t = readobject(o->commit->tree)) == nil) @@ -113,7 +113,7 @@ filtermatch(Object *o) sysfatal("read %H: %r", o->commit->parent[i]); if((pt = readobject(p->commit->tree)) == nil) sysfatal("read %H: %r", o->commit->tree); - r = filtermatch1(pathfilt, t, pt); + r = matchesfilter1(pathfilt, t, pt); unref(p); unref(pt); if(r) @@ -132,16 +132,13 @@ nextline(char *p, char *e) return p; } -static void +static int show(Object *o) { Tm tm; char *p, *q, *e; assert(o->type == GCommit); - if(!filtermatch(o)) - return; - if(shortlog){ p = o->commit->msg; e = p + o->commit->nmsg; @@ -153,6 +150,9 @@ show(Object *o) tmtime(&tm, o->commit->mtime, tzload("local")); Bprint(out, "Hash:\t%H\n", o->hash); Bprint(out, "Author:\t%s\n", o->commit->author); + if(o->commit->committer != nil + && strcmp(o->commit->author, o->commit->committer) != 0) + Bprint(out, "Committer:\t%s\n", o->commit->committer); Bprint(out, "Date:\t%τ\n", tmfmt(&tm, "WW MMM D hh:mm:ss z YYYY")); Bprint(out, "\n"); p = o->commit->msg; @@ -168,6 +168,7 @@ show(Object *o) Bprint(out, "\n"); } Bflush(out); + return 1; } static void @@ -179,74 +180,24 @@ showquery(char *q) if((n = resolverefs(&h, q)) == -1) sysfatal("resolve: %r"); - for(i = 0; i < n; i++){ + for(i = 0; i < n && (msgcount == -1 || msgcount > 0); i++){ if((o = readobject(h[i])) == nil) sysfatal("read %H: %r", h[i]); - show(o); + if(matchesfilter(o)){ + show(o); + if(msgcount != -1) + msgcount--; + } unref(o); } exits(nil); } -static void -qput(Object *o) -{ - Object *p; - int i; - - if(oshas(&done, o->hash)) - return; - osadd(&done, o); - if(nheap == heapsz){ - heapsz *= 2; - heap = earealloc(heap, heapsz, sizeof(Object*)); - } - heap[nheap++] = o; - for(i = nheap - 1; i > 0; i = (i-1)/2){ - o = heap[i]; - p = heap[(i-1)/2]; - if(o->commit->mtime < p->commit->mtime) - break; - heap[i] = p; - heap[(i-1)/2] = o; - } -} - -static Object* -qpop(void) -{ - Object *o, *t; - int i, l, r, m; - - if(nheap == 0) - return nil; - - i = 0; - o = heap[0]; - t = heap[--nheap]; - heap[0] = t; - while(1){ - m = i; - l = 2*i+1; - r = 2*i+2; - if(l < nheap && heap[m]->commit->mtime < heap[l]->commit->mtime) - m = l; - if(r < nheap && heap[m]->commit->mtime < heap[r]->commit->mtime) - m = r; - else - break; - t = heap[m]; - heap[m] = heap[i]; - heap[i] = t; - i = m; - } - return o; -} - static void showcommits(char *c) { Object *o, *p; + Qelt e; int i; Hash h; @@ -256,18 +207,24 @@ showcommits(char *c) sysfatal("resolve %s: %r", c); if((o = readobject(h)) == nil) sysfatal("load %H: %r", h); - heapsz = 8; - heap = eamalloc(heapsz, sizeof(Object*)); + qinit(&objq); osinit(&done); - qput(o); - while((o = qpop()) != nil){ - show(o); - for(i = 0; i < o->commit->nparent; i++){ - if((p = readobject(o->commit->parent[i])) == nil) + qput(&objq, o, 0); + while(qpop(&objq, &e) && (msgcount == -1 || msgcount > 0)){ + if(matchesfilter(e.o)){ + show(e.o); + if(msgcount != -1) + msgcount--; + } + for(i = 0; i < e.o->commit->nparent; i++){ + if(oshas(&done, e.o->commit->parent[i])) + continue; + if((p = readobject(e.o->commit->parent[i])) == nil) sysfatal("load %H: %r", o->commit->parent[i]); - qput(p); + osadd(&done, p); + qput(&objq, p, 0); } - unref(o); + unref(e.o); } } @@ -294,6 +251,9 @@ main(int argc, char **argv) case 's': shortlog++; break; + case 'n': + msgcount = atoi(EARGF(usage())); + break; default: usage(); break; diff --git a/merge b/merge index 6e97919..051e224 100755 --- a/merge +++ b/merge @@ -7,13 +7,12 @@ fn merge{ basebr=$gitfs/object/$2/tree theirbr=$gitfs/object/$3/tree - all=`$nl{{git/query -c $1 $2; git/query -c $2 $3} | sed 's/^..//' | \ - subst -g '^('$ourbr'|'$basebr'|'$theirbr')/*' | sort | uniq} + all=`$nl{{git/query -c $1 $2; git/query -c $2 $3} | sed 's/^..//' | sort | uniq} for(f in $all){ ours=$ourbr/$f base=$basebr/$f theirs=$theirbr/$f - merge1 $f $theirs $base $ours + merge1 ./$f $ours $base $theirs } } diff --git a/mkfile b/mkfile index 610d3af..d69be86 100644 --- a/mkfile +++ b/mkfile @@ -3,7 +3,7 @@ BIN=/$objtype/bin/git TARG=\ conf\ - fetch\ + get\ fs\ log\ query\ @@ -21,6 +21,7 @@ RC=\ compat\ diff\ export\ + hist\ import\ init\ merge\ diff --git a/pack.c b/pack.c index 2ffcdec..d361161 100644 --- a/pack.c +++ b/pack.c @@ -20,7 +20,7 @@ struct Metavec { struct Meta { Object *obj; - char *path; + vlong path; vlong mtime; /* The best delta we picked */ @@ -65,8 +65,8 @@ static Object *readidxobject(Biobuf *, Hash, int); Objset objcache; Object *lruhead; Object *lrutail; -int ncache; -int cachemax = 4096; +vlong ncache; +vlong cachemax = 512*MiB; Packf *packf; int npackf; int openpacks; @@ -158,7 +158,7 @@ cache(Object *o) if(!(o->flag & Ccache)){ o->flag |= Ccache; ref(o); - ncache++; + ncache += o->size; } while(ncache > cachemax && lrutail != nil){ p = lrutail; @@ -168,8 +168,8 @@ cache(Object *o) p->flag &= ~Ccache; p->prev = nil; p->next = nil; + ncache -= p->size; unref(p); - ncache--; } } @@ -194,26 +194,26 @@ loadpack(Packf *pf, char *name) pf->nidx = packf[i].nidx; packf[i].idx = nil; packf[i].pack = nil; + return 0; } } if((ifd = open(buf, OREAD)) == -1) - goto error; - if((d = dirfstat(ifd)) == nil) - goto error; + return -1; + if((d = dirfstat(ifd)) == nil){ + close(ifd); + return -1; + } pf->nidx = d->length; pf->idx = emalloc(pf->nidx); if(readn(ifd, pf->idx, pf->nidx) != pf->nidx){ + close(ifd); free(pf->idx); free(d); - goto error; + return -1; } + close(ifd); free(d); return 0; - -error: - if(ifd != -1) - close(ifd); - return -1; } static void @@ -253,38 +253,51 @@ openpack(Packf *pf) vlong t; int i, best; - if(pf->pack == nil){ - if((pf->pack = Bopen(pf->path, OREAD)) == nil) - return nil; - openpacks++; + if(pf->pack != nil){ + pf->refs++; + return pf->pack; } - if(openpacks == Npackcache){ - t = pf->opentm; + /* + * If we've got more packs open + * than we want cached, try to + * free up the oldest ones. + * + * If we can't find a slot, this + * isn't fatal; we can just use + * another fd. + */ + while(openpacks >= Npackcache){ + t = (1ull<<62)-1; best = -1; for(i = 0; i < npackf; i++){ - if(packf[i].opentm < t && packf[i].refs > 0){ + if(&packf[i] != pf + && packf[i].pack != nil + && packf[i].opentm < t + && packf[i].refs == 0){ t = packf[i].opentm; best = i; } } - if(best != -1){ - Bterm(packf[best].pack); - packf[best].pack = nil; - openpacks--; + if(best == -1){ + fprint(2, "no available pack slots\n"); + break; } + Bterm(packf[best].pack); + packf[best].pack = nil; + openpacks--; } + openpacks++; pf->opentm = nsec(); pf->refs++; + if((pf->pack = Bopen(pf->path, OREAD)) == nil) + return nil; return pf->pack; } static void closepack(Packf *pf) { - if(--pf->refs == 0){ - Bterm(pf->pack); - pf->pack = nil; - } + pf->refs--; } static u32int @@ -871,6 +884,8 @@ parsecommit(Object *o) }else if(strcmp(buf, "gpgsig") == 0){ /* just drop it */ if((t = strstr(p, "-----END PGP SIGNATURE-----")) == nil) + if((t = strstr(p, "-----END SSH SIGNATURE-----")) == nil) + if((t = strstr(p, "-----END SIGNED MESSAGE-----")) == nil) sysfatal("malformed gpg signature"); np -= t - p; p = t; @@ -1023,7 +1038,6 @@ readidxobject(Biobuf *idx, Hash h, int flag) return obj; } } - snprint(hbuf, sizeof(hbuf), "%H", h); snprint(path, sizeof(path), ".git/objects/%c%c/%s", hbuf[0], hbuf[1], hbuf + 2); @@ -1272,17 +1286,18 @@ static int deltaordercmp(void *pa, void *pb) { Meta *a, *b; - int cmp; + vlong cmp; a = *(Meta**)pa; b = *(Meta**)pb; if(a->obj->type != b->obj->type) return a->obj->type - b->obj->type; - cmp = strcmp(a->path, b->path); + cmp = (b->path - a->path); + if(cmp != 0) + return (cmp < 0) ? -1 : 1; + cmp = a->mtime - b->mtime; if(cmp != 0) - return cmp; - if(a->mtime != b->mtime) - return a->mtime - b->mtime; + return (cmp < 0) ? -1 : 1; return memcmp(a->obj->hash.h, b->obj->hash.h, sizeof(a->obj->hash.h)); } @@ -1305,7 +1320,7 @@ writeordercmp(void *pa, void *pb) } static void -addmeta(Metavec *v, Objset *has, Object *o, char *path, vlong mtime) +addmeta(Metavec *v, Objset *has, Object *o, vlong pathid, vlong mtime) { Meta *m; @@ -1316,7 +1331,7 @@ addmeta(Metavec *v, Objset *has, Object *o, char *path, vlong mtime) return; m = emalloc(sizeof(Meta)); m->obj = o; - m->path = estrdup(path); + m->path = pathid; m->mtime = mtime; if(v->nmeta == v->metasz){ @@ -1330,7 +1345,6 @@ static void freemeta(Meta *m) { free(m->delta); - free(m->path); free(m); } @@ -1339,8 +1353,9 @@ loadtree(Metavec *v, Objset *has, Hash tree, char *dpath, vlong mtime) { Object *t, *o; Dirent *e; + vlong dh, eh; + int i, k, r; char *p; - int i, k; if(oshas(has, tree)) return 0; @@ -1351,7 +1366,8 @@ loadtree(Metavec *v, Objset *has, Hash tree, char *dpath, vlong mtime) unref(t); return 0; } - addmeta(v, has, t, dpath, mtime); + dh = murmurhash2(dpath, strlen(dpath)); + addmeta(v, has, t, dh, mtime); for(i = 0; i < t->tree->nent; i++){ e = &t->tree->ent[i]; if(oshas(has, e->h)) @@ -1360,14 +1376,16 @@ loadtree(Metavec *v, Objset *has, Hash tree, char *dpath, vlong mtime) continue; k = (e->mode & DMDIR) ? GTree : GBlob; o = clearedobject(e->h, k); - p = smprint("%s/%s", dpath, e->name); - if(k == GBlob) - addmeta(v, has, o, p, mtime); - else if(loadtree(v, has, e->h, p, mtime) == -1){ + if(k == GTree){ + p = smprint("%s/%s", dpath, e->name); + r = loadtree(v, has, e->h, p, mtime); free(p); - return -1; + if(r == -1) + return -1; + }else{ + eh = murmurhash2(e->name, strlen(e->name)); + addmeta(v, has, o, dh^eh, mtime); } - free(p); } unref(t); return 0; @@ -1388,7 +1406,7 @@ loadcommit(Metavec *v, Objset *has, Hash h) unref(c); return 0; } - addmeta(v, has, c, "", c->commit->ctime); + addmeta(v, has, c, 0, c->commit->ctime); r = loadtree(v, has, c->commit->tree, "", c->commit->ctime); unref(c); return r; @@ -1448,7 +1466,8 @@ pickdeltas(Meta **meta, int nmeta) pct = 0; dprint(1, "picking deltas\n"); - fprint(2, "deltifying %d objects: 0%%", nmeta); + if(interactive) + fprint(2, "deltifying %d objects: 0%%", nmeta); qsort(meta, nmeta, sizeof(Meta*), deltaordercmp); for(i = 0; i < nmeta; i++){ m = meta[i]; @@ -1491,7 +1510,8 @@ pickdeltas(Meta **meta, int nmeta) } for(i = max(0, nmeta - 10); i < nmeta; i++) dtclear(&meta[i]->dtab); - fprint(2, "\b\b\b\b100%%\n"); + if(interactive) + fprint(2, "\b\b\b\b100%%\n"); } static int @@ -1677,7 +1697,8 @@ genpack(int fd, Meta **meta, int nmeta, Hash *h, int odelta) for(i = 0; i < nmeta; i++){ pct = showprogress((i*100)/nmeta, pct); m = meta[i]; - if((m->off = Boffset(bfd)) == -1) + m->off = Boffset(bfd); + if(m->off == -1) goto error; if((o = readobject(m->obj->hash)) == nil) return -1; diff --git a/proto.c b/proto.c index 1611a2a..9af09ec 100644 --- a/proto.c +++ b/proto.c @@ -58,8 +58,8 @@ readpkt(Conn *c, char *buf, int nbuf) char *e; int n; - if(readn(c->rfd, len, 4) == -1) - return -1; + if(readn(c->rfd, len, 4) != 4) + sysfatal("pktline: short read from transport"); len[4] = 0; n = strtol(len, &e, 16); if(n == 0){ @@ -93,6 +93,20 @@ writepkt(Conn *c, char *buf, int nbuf) return 0; } +int +fmtpkt(Conn *c, char *fmt, ...) +{ + char pkt[Pktmax]; + va_list ap; + int n; + + va_start(ap, fmt); + n = vsnprint(pkt, sizeof(pkt), fmt, ap); + n = writepkt(c, pkt, n); + va_end(ap); + return n; +} + int flushpkt(Conn *c) { @@ -220,14 +234,27 @@ static int issmarthttp(Conn *c, char *direction) { char buf[Pktmax+1], svc[128]; - int n; + int fd, n; + + if((fd = webopen(c, "contenttype", OREAD)) == -1) + return -1; + n = readn(fd, buf, sizeof(buf) - 1); + close(fd); + if(n == -1) + return -1; + buf[n] = '\0'; + snprint(svc, sizeof(svc), "application/x-git-%s-pack-advertisement", direction); + if(strcmp(svc, buf) != 0){ + werrstr("dumb http protocol not supported"); + return -1; + } if((n = readpkt(c, buf, sizeof(buf))) == -1) sysfatal("http read: %r"); buf[n] = 0; snprint(svc, sizeof(svc), "# service=git-%s-pack\n", direction); if(strncmp(svc, buf, n) != 0){ - werrstr("dumb http protocol not supported"); + werrstr("invalid initial packet line"); return -1; } if(readpkt(c, buf, sizeof(buf)) != 0){ diff --git a/pull b/pull index 4dd7fe9..10fbf62 100755 --- a/pull +++ b/pull @@ -3,17 +3,13 @@ rfork en . /sys/lib/git/common.rc fn update{ - branch=$1 - upstream=$2 - url=$3 - dir=$4 - bflag=() + upstream=$1 + url=$2 + dir=$3 dflag=() - if(! ~ $#branch 0) - bflag=(-b $branch) if(! ~ $#debug 0) dflag='-d' - {git/fetch $dflag $bflag -u $upstream $url >[2=3] || die $status} | awk ' + {git/get $dflag -u $upstream $url >[2=3] || die $status} | awk ' /^remote/{ if($2=="HEAD") next @@ -30,16 +26,11 @@ fn update{ gitup -flagfmt='a:allbranch, b:branch branch, d:debug, - f:fetchonly, u:upstream upstream, q:quiet' +flagfmt='d:debug, q:quiet, f:fetchonly, + u:upstream upstream' args='' eval `''{aux/getflags $*} || exec aux/usage -if(~ $#branch 0) - branch=refs/`{git/branch} -if(~ $allbranch 1) - branch='' - if(~ $#upstream 0) upstream=origin remote=`$nl{git/conf 'remote "'$upstream'".url'} @@ -48,7 +39,7 @@ if(~ $#remote 0){ upstream=THEM } -update $branch $upstream $remote +update $upstream $remote if (~ $fetchonly 1) exit @@ -75,8 +66,7 @@ if(! ~ `{git/query HEAD $remote @} `{git/query HEAD}){ # The remote is directly ahead of the local, and we have # no local commits that need merging. if(~ $#quiet 0) - git/log -s -e $local'..'$remote >[1=2] -echo -echo $remote':' `{git/query $local} '=>' `{git/query $remote} >[1=2] + git/log -s -e $local'..'$remote +echo $remote':' `{git/query $local} '=>' `{git/query $remote} git/branch -mnb $remote $local exit '' diff --git a/query.c b/query.c index cb0f243..40020ea 100644 --- a/query.c +++ b/query.c @@ -158,6 +158,7 @@ main(int argc, char **argv) char query[2048], repo[512]; ARGBEGIN{ + case 'd': chattygit++; break; case 'p': fullpath++; break; case 'c': changes++; break; case 'r': reverse ^= 1; break; diff --git a/rebase b/rebase index 395f3e6..1eb03fb 100755 --- a/rebase +++ b/rebase @@ -7,8 +7,6 @@ flagfmt='a:abort, r:resume, i:interactive'; args='onto' eval `''{aux/getflags $*} || exec aux/usage tmp=_rebase.working -if(! git/walk -q) - die dirty working tree if(~ $#abort 1){ if(! test -f .git/rebase.todo) die no rebase to abort @@ -31,7 +29,7 @@ if not{ src=`{git/branch} dst=`{git/query $1} echo $src > .git/rebase.src - git/log -se $dst' '$src' @ .. '$src | sed 's/^/pick /' >.git/rebase.todo + git/log -se $dst'..'$src | sed 's/^/pick /' >.git/rebase.todo if(! ~ $#interactive 0){ giteditor=`{git/conf core.editor} if(~ $#editor 0) diff --git a/ref.c b/ref.c index 5f67dac..bf798e2 100644 --- a/ref.c +++ b/ref.c @@ -5,13 +5,18 @@ #include "git.h" typedef struct Eval Eval; -typedef struct XObject XObject; -typedef struct Objq Objq; enum { Blank, Keep, Drop, + Skip, +}; + +enum { + Lca, + Twixt, + Range, }; struct Eval { @@ -22,17 +27,11 @@ struct Eval { int stksz; }; -struct XObject { - Object *obj; - Object *mark; - XObject *queue; - XObject *next; -}; - -struct Objq { - Objq *next; - Object *o; - int color; +static char *colors[] = { +[Keep] "keep", +[Drop] "drop", +[Blank] "blank", +[Skip] "skip", }; static Object zcommit = { @@ -46,26 +45,6 @@ eatspace(Eval *ev) ev->p++; } -int -objdatecmp(void *pa, void *pb) -{ - Object *a, *b; - int r; - - a = readobject((*(Object**)pa)->hash); - b = readobject((*(Object**)pb)->hash); - assert(a->type == GCommit && b->type == GCommit); - if(a->commit->mtime == b->commit->mtime) - r = 0; - else if(a->commit->mtime < b->commit->mtime) - r = -1; - else - r = 1; - unref(a); - unref(b); - return r; -} - void push(Eval *ev, Object *o) { @@ -128,150 +107,24 @@ take(Eval *ev, char *m) return 1; } -XObject* -hnode(XObject *ht[], Object *o) -{ - XObject *h; - int hh; - - hh = o->hash.h[0] & 0xff; - for(h = ht[hh]; h; h = h->next) - if(hasheq(&o->hash, &h->obj->hash)) - return h; - - h = emalloc(sizeof(*h)); - h->obj = o; - h->mark = nil; - h->queue = nil; - h->next = ht[hh]; - ht[hh] = h; - return h; -} - -Object* -ancestor(Object *a, Object *b) -{ - Object *o, *p, *r; - XObject *ht[256]; - XObject *h, *q, *q1, *q2; - int i; - - if(a == b) - return a; - if(a == nil || b == nil) - return nil; - r = nil; - memset(ht, 0, sizeof(ht)); - q1 = nil; - - h = hnode(ht, a); - h->mark = a; - h->queue = q1; - q1 = h; - - h = hnode(ht, b); - h->mark = b; - h->queue = q1; - q1 = h; - - while(1){ - q2 = nil; - while(q = q1){ - q1 = q->queue; - q->queue = nil; - o = q->obj; - for(i = 0; i < o->commit->nparent; i++){ - p = readobject(o->commit->parent[i]); - if(p == nil) - goto err; - h = hnode(ht, p); - if(h->mark != nil){ - if(h->mark != q->mark){ - r = h->obj; - goto done; - } - } else { - h->mark = q->mark; - h->queue = q2; - q2 = h; - } - } - } - if(q2 == nil){ -err: - werrstr("no common ancestor"); - break; - } - q1 = q2; - } -done: - for(i=0; inext; - free(h); - } - } - return r; -} - -int -lca(Eval *ev) -{ - Object *a, *b, *o; - - if(ev->nstk < 2){ - werrstr("ancestor needs 2 objects"); - return -1; - } - a = pop(ev); - b = pop(ev); - o = ancestor(a, b); - if(o == nil) - return -1; - push(ev, o); - return 0; -} - static int -repaint(Objset *keep, Objset *drop, Object *o) +paint(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres, int mode) { - Object *p; - int i; - - if(!oshas(keep, o->hash) && !oshas(drop, o->hash)){ - dprint(2, "repaint: blank => drop %H\n", o->hash); - osadd(drop, o); - return 0; - } - if(oshas(keep, o->hash)) - dprint(2, "repaint: keep => drop %H\n", o->hash); - osadd(drop, o); - for(i = 0; i < o->commit->nparent; i++){ - if((p = readobject(o->commit->parent[i])) == nil) - return -1; - if(repaint(keep, drop, p) == -1) - return -1; - unref(p); - } - return 0; -} + Qelt e; + Objq objq; + Objset keep, drop, skip; + Object *o, *c, **range; + int i, nskip, nrange; -int -findtwixt(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres) -{ - Objq *q, *e, *n, **p; - Objset keep, drop; - Object *o, *c; - int i, ncolor; - - e = nil; - q = nil; - p = &q; osinit(&keep); osinit(&drop); + osinit(&skip); + qinit(&objq); + range = nil; + nrange = 0; + 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); werrstr("read head %H: %r", head[i]); @@ -282,17 +135,11 @@ findtwixt(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres unref(o); continue; } - dprint(1, "twixt init: keep %H\n", o->hash); - e = emalloc(sizeof(Objq)); - e->o = o; - e->color = Keep; - *p = e; - p = &e->next; + dprint(1, "init: keep %H\n", o->hash); + qput(&objq, o, Keep); 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; @@ -303,77 +150,151 @@ findtwixt(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres continue; } dprint(1, "init: drop %H\n", o->hash); - e = emalloc(sizeof(Objq)); - e->o = o; - e->color = Drop; - *p = e; - p = &e->next; + qput(&objq, o, Drop); unref(o); } dprint(1, "finding twixt commits\n"); - while(q != nil){ - if(oshas(&drop, q->o->hash)) - ncolor = Drop; - else if(oshas(&keep, q->o->hash)) - ncolor = Keep; - else - ncolor = Blank; - if(ncolor == Drop || ncolor == Keep && q->color == Keep) - goto next; - if(ncolor == Keep && q->color == Drop){ - if(repaint(&keep, &drop, q->o) == -1) + while(nskip != objq.nheap && qpop(&objq, &e)){ + if(e.color == Skip) + nskip--; + if(oshas(&skip, e.o->hash)) + continue; + switch(e.color){ + case Keep: + if(oshas(&keep, e.o->hash)) + continue; + if(oshas(&drop, e.o->hash)) + e.color = Skip; + else if(mode == Range){ + range = earealloc(range, nrange+1, sizeof(Object*)); + range[nrange++] = e.o; + } + osadd(&keep, e.o); + break; + case Drop: + if(oshas(&drop, e.o->hash)) + continue; + if(oshas(&keep, e.o->hash)) + e.color = Skip; + osadd(&drop, e.o); + break; + case Skip: + osadd(&skip, e.o); + break; + } + o = readobject(e.o->hash); + for(i = 0; i < o->commit->nparent; i++){ + if((c = readobject(e.o->commit->parent[i])) == nil) goto error; - }else if (ncolor == Blank) { - dprint(2, "visit: %s %H\n", q->color == Keep ? "keep" : "drop", q->o->hash); - if(q->color == Keep) - osadd(&keep, q->o); - else - osadd(&drop, q->o); - for(i = 0; i < q->o->commit->nparent; i++){ - if((c = readobject(q->o->commit->parent[i])) == nil) - goto error; - if(c->type != GCommit){ - fprint(2, "warning: %H does not point at commit\n", c->hash); - unref(c); - continue; - } - dprint(2, "enqueue: %s %H\n", q->color == Keep ? "keep" : "drop", c->hash); - n = emalloc(sizeof(Objq)); - n->color = q->color; - n->next = nil; - n->o = c; - e->next = n; - e = n; + if(c->type != GCommit){ + fprint(2, "warning: %H does not point at commit\n", c->hash); unref(c); + continue; } - }else{ - sysfatal("oops"); + dprint(2, "\tenqueue: %s %H\n", colors[e.color], c->hash); + qput(&objq, c, e.color); + unref(c); + if(e.color == Skip) + nskip++; } -next: - n = q->next; - free(q); - q = n; + unref(o); } - *res = eamalloc(keep.nobj, sizeof(Object*)); - *nres = 0; - for(i = 0; i < keep.sz; i++){ - if(keep.obj[i] != nil && !oshas(&drop, keep.obj[i]->hash)){ - (*res)[*nres] = keep.obj[i]; - (*nres)++; + switch(mode){ + case Lca: + dprint(1, "found ancestor\n"); + o = nil; + for(i = 0; i < keep.sz; i++){ + o = keep.obj[i]; + if(o != nil && oshas(&drop, o->hash) && !oshas(&skip, o->hash)) + break; + } + if(i == keep.sz){ + *nres = 0; + *res = nil; + }else{ + *nres = 1; + *res = eamalloc(1, sizeof(Object*)); + (*res)[0] = o; } + break; + case Twixt: + dprint(1, "found twixt\n"); + *res = eamalloc(keep.nobj, sizeof(Object*)); + *nres = 0; + for(i = 0; i < keep.sz; i++){ + o = keep.obj[i]; + if(o != nil && !oshas(&drop, o->hash) && !oshas(&skip, o->hash)){ + (*res)[*nres] = o; + (*nres)++; + } + } + break; + case Range: + dprint(1, "found range\n"); + *res = eamalloc(nrange, sizeof(Object*)); + *nres = 0; + for(i = nrange - 1; i >= 0; i--){ + o = range[i]; + if(!oshas(&drop, o->hash) && !oshas(&skip, o->hash)){ + (*res)[*nres] = o; + (*nres)++; + } + } + free(range); + break; } osclear(&keep); osclear(&drop); + osclear(&skip); return 0; error: - for(; q != nil; q = n) { - n = q->next; - free(q); - } + dprint(1, "paint error: %r\n"); + free(objq.heap); + free(range); return -1; } +int +findtwixt(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres) +{ + return paint(head, nhead, tail, ntail, res, nres, Twixt); +} + +Object* +ancestor(Object *a, Object *b) +{ + Object **o, *r; + int n; + + if(paint(&a->hash, 1, &b->hash, 1, &o, &n, Lca) == -1 || n == 0) + return nil; + r = ref(o[0]); + free(o); + return r; +} + +int +lca(Eval *ev) +{ + Object *a, *b, **o; + int n; + + if(ev->nstk < 2){ + werrstr("ancestor needs 2 objects"); + return -1; + } + n = 0; + b = pop(ev); + a = pop(ev); + paint(&a->hash, 1, &b->hash, 1, &o, &n, Lca); + if(n == 0) + return -1; + push(ev, *o); + free(o); + return 0; +} + static int parent(Eval *ev) { @@ -392,34 +313,11 @@ parent(Eval *ev) return 0; } -static int -unwind(Eval *ev, Object **obj, int *idx, int nobj, Object **p, Objset *set, int keep) -{ - int i; - - for(i = nobj; i >= 0; i--){ - idx[i]++; - if(keep && !oshas(set, obj[i]->hash)){ - push(ev, obj[i]); - osadd(set, obj[i]); - }else{ - osadd(set, obj[i]); - } - if(idx[i] < obj[i]->commit->nparent){ - *p = obj[i]; - return i; - } - unref(obj[i]); - } - return -1; -} - static int range(Eval *ev) { - Object *a, *b, *p, *q, **all; - int nall, *idx, mark; - Objset keep, skip; + Object *a, *b, **o; + int i, n; b = pop(ev); a = pop(ev); @@ -432,50 +330,12 @@ range(Eval *ev) return -1; } - p = b; - all = nil; - idx = nil; - nall = 0; - mark = ev->nstk; - osinit(&keep); - osinit(&skip); - osadd(&keep, a); - while(1){ - all = earealloc(all, (nall + 1), sizeof(Object*)); - idx = earealloc(idx, (nall + 1), sizeof(int)); - all[nall] = p; - idx[nall] = 0; - if(p == a || p->commit->nparent == 0 && a == &zcommit){ - if((nall = unwind(ev, all, idx, nall, &p, &keep, 1)) == -1) - break; - }else if(p->commit->nparent == 0){ - if((nall = unwind(ev, all, idx, nall, &p, &skip, 0)) == -1) - break; - }else if(oshas(&keep, p->hash)){ - if((nall = unwind(ev, all, idx, nall, &p, &keep, 1)) == -1) - break; - }else if(oshas(&skip, p->hash)) - if((nall = unwind(ev, all, idx, nall, &p, &skip, 0)) == -1) - break; - if(p->commit->nparent == 0) - break; - if((q = readobject(p->commit->parent[idx[nall]])) == nil){ - werrstr("bad commit %H", p->commit->parent[idx[nall]]); - goto error; - } - if(q->type != GCommit){ - werrstr("not commit: %H", q->hash); - goto error; - } - p = q; - nall++; - } - free(all); - qsort(ev->stk + mark, ev->nstk - mark, sizeof(Object*), objdatecmp); + if(paint(&b->hash, 1, &a->hash, 1, &o, &n, Range) == -1) + return -1; + for(i = 0; i < n; i++) + push(ev, o[i]); + free(o); return 0; -error: - free(all); - return -1; } int diff --git a/revert b/revert index a516e3a..1adb0d2 100644 --- a/revert +++ b/revert @@ -5,16 +5,18 @@ rfork en gitup flagfmt='c:query query' args='file ...' -eval `''{aux/getflags $*} || exec aux/usage +if (! eval `''{aux/getflags $*} || ~ $#* 0) + exec aux/usage commit=$gitfs/HEAD if(~ $#query 1) commit=`{git/query -p $query} -files=`$nl{cleanname -d $gitrel $*} -for(f in `$nl{cd $commit/tree/ && walk -f $files}){ +files=`$nl{cleanname -d $gitrel $* | drop $gitroot} +for(f in `$nl{cd $commit/tree/ && walk -f ./$files}){ mkdir -p `{basename -d $f} cp -x -- $commit/tree/$f $f + touch $f git/add $f } exit '' diff --git a/save.c b/save.c index a9f0f51..08a1a50 100644 --- a/save.c +++ b/save.c @@ -14,17 +14,27 @@ enum { Maxparents = 16, }; +char *authorname; +char *authoremail; +char *committername; +char *committeremail; +char *commitmsg; +Hash parents[Maxparents]; +int nparents; + int -gitmode(int m) +gitmode(Dirent *e) { - if(m & DMDIR) /* directory */ + if(e->islink) + return 0120000; + else if(e->ismod) + return 0160000; + else if(e->mode & DMDIR) return 0040000; - else if(m & 0111) /* executable */ + else if(e->mode & 0111) return 0100755; - else if(m != 0) /* regular */ + else return 0100644; - else /* symlink */ - return 0120000; } int @@ -141,7 +151,7 @@ writetree(Dirent *ent, int nent, Hash *h) for(d = ent; d != ent + nent; d++){ if(strlen(d->name) >= 255) sysfatal("overly long filename: %s", d->name); - t = seprint(t, etxt, "%o %s", gitmode(d->mode), d->name) + 1; + t = seprint(t, etxt, "%o %s", gitmode(d), d->name) + 1; memcpy(t, d->h.h, sizeof(d->h.h)); t += sizeof(d->h.h); } @@ -297,7 +307,7 @@ treeify(Object *t, char **path, char **epath, int off, Hash *h) void -mkcommit(Hash *c, char *msg, char *name, char *email, vlong date, Hash *parents, int nparents, Hash tree) +mkcommit(Hash *c, vlong date, Hash tree) { char *s, h[64]; int ns, nh, i; @@ -307,10 +317,10 @@ mkcommit(Hash *c, char *msg, char *name, char *email, vlong date, Hash *parents, fmtprint(&f, "tree %H\n", tree); for(i = 0; i < nparents; i++) fmtprint(&f, "parent %H\n", parents[i]); - fmtprint(&f, "author %s <%s> %lld +0000\n", name, email, date); - fmtprint(&f, "committer %s <%s> %lld +0000\n", name, email, date); + fmtprint(&f, "author %s <%s> %lld +0000\n", authorname, authoremail, date); + fmtprint(&f, "committer %s <%s> %lld +0000\n", committername, committeremail, date); fmtprint(&f, "\n"); - fmtprint(&f, "%s", msg); + fmtprint(&f, "%s", commitmsg); s = fmtstrflush(&f); ns = strlen(s); @@ -344,9 +354,9 @@ usage(void) void main(int argc, char **argv) { - Hash th, ch, parents[Maxparents]; - char *msg, *name, *email, *dstr, cwd[1024]; - int i, r, ncwd, nparents; + Hash th, ch; + char *dstr, cwd[1024]; + int i, r, ncwd; vlong date; Object *t; @@ -355,19 +365,29 @@ main(int argc, char **argv) sysfatal("could not find git repo: %r"); if(getwd(cwd, sizeof(cwd)) == nil) sysfatal("getcwd: %r"); - msg = nil; - name = nil; - email = nil; dstr = nil; date = time(nil); - nparents = 0; ncwd = strlen(cwd); ARGBEGIN{ - case 'm': msg = EARGF(usage()); break; - case 'n': name = EARGF(usage()); break; - case 'e': email = EARGF(usage()); break; - case 'd': dstr = EARGF(usage()); break; + case 'm': + commitmsg = EARGF(usage()); + break; + case 'n': + authorname = EARGF(usage()); + break; + case 'e': + authoremail = EARGF(usage()); + break; + case 'N': + committername = EARGF(usage()); + break; + case 'E': + committeremail = EARGF(usage()); + break; + case 'd': + dstr = EARGF(usage()); + break; case 'p': if(nparents >= Maxparents) sysfatal("too many parents"); @@ -376,21 +396,26 @@ main(int argc, char **argv) break; default: usage(); + break; }ARGEND; - if(!msg) + if(commitmsg == nil) sysfatal("missing message"); - if(!name) + if(authorname == nil) sysfatal("missing name"); - if(!email) + if(authoremail == nil) sysfatal("missing email"); + if((committername == nil) != (committeremail == nil)) + sysfatal("partially specified committer"); + if(committername == nil && committeremail == nil){ + committername = authorname; + committeremail = authoremail; + } if(dstr){ date=strtoll(dstr, &dstr, 10); if(strlen(dstr) != 0) sysfatal("could not parse date %s", dstr); } - if(msg == nil || name == nil) - usage(); for(i = 0; i < argc; i++){ cleanname(argv[i]); if(*argv[i] == '/' && strncmp(argv[i], cwd, ncwd) == 0) @@ -403,7 +428,7 @@ main(int argc, char **argv) r = treeify(t, argv, argv + argc, 0, &th); if(r == -1) sysfatal("could not commit: %r\n"); - mkcommit(&ch, msg, name, email, date, parents, nparents, th); + mkcommit(&ch, date, th); print("%H\n", ch); exits(nil); } diff --git a/send.c b/send.c index 877c369..1188a71 100644 --- a/send.c +++ b/send.c @@ -134,8 +134,8 @@ sendpack(Conn *c) nmap = nours; map = eamalloc(nmap, sizeof(Map)); for(i = 0; i < nmap; i++){ - map[i].ours = ours[i]; map[i].theirs = Zhash; + map[i].ours = ours[i]; map[i].ref = refs[i]; } while(1){ @@ -155,9 +155,15 @@ sendpack(Conn *c) theirs = earealloc(theirs, ntheirs+1, sizeof(Hash)); if(hparse(&theirs[ntheirs], sp[0]) == -1) sysfatal("invalid hash %s", sp[0]); + if((idx = findkey(map, nmap, sp[1])) != -1) + map[idx].theirs = theirs[ntheirs]; + /* + * we only keep their ref if we can read the object to add it + * to our reachability; otherwise, discard it; we only care + * that we don't have it, so we can tell whether we need to + * bail out of pushing. + */ if((o = readobject(theirs[ntheirs])) != nil){ - if((idx = findkey(map, nmap, sp[1])) != -1) - map[idx].theirs = theirs[ntheirs]; ntheirs++; unref(o); } @@ -180,7 +186,7 @@ sendpack(Conn *c) p = ancestor(a, b); if(!force && !hasheq(&m->theirs, &Zhash) && (a == nil || p != a)){ fprint(2, "remote has diverged\n"); - werrstr("force needed"); + werrstr("remote diverged"); flushpkt(c); return -1; } diff --git a/serve.c b/serve.c index 16eb341..da7cd56 100644 --- a/serve.c +++ b/serve.c @@ -8,20 +8,6 @@ char *pathpfx = nil; int allowwrite; -int -fmtpkt(Conn *c, char *fmt, ...) -{ - char pkt[Pktmax]; - va_list ap; - int n; - - va_start(ap, fmt); - n = vsnprint(pkt, sizeof(pkt), fmt, ap); - n = writepkt(c, pkt, n); - va_end(ap); - return n; -} - int showrefs(Conn *c) { @@ -34,7 +20,7 @@ showrefs(Conn *c) refs = nil; names = nil; if(resolveref(&head, "HEAD") != -1) - if(fmtpkt(c, "%H HEAD", head) == -1) + if(fmtpkt(c, "%H HEAD\n", head) == -1) goto error; if((nrefs = listrefs(&refs, &names)) == -1) @@ -362,7 +348,7 @@ lockrepo(void) int updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) { - char refpath[512]; + char refpath[512], buf[128]; int i, newidx, hadref, fd, ret, lockfd; vlong newtm; Object *o; @@ -378,19 +364,19 @@ updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) */ newtm = -23811206400; if((lockfd = lockrepo()) == -1){ - werrstr("repo locked\n"); + 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]); + 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]); + snprint(buf, sizeof(buf), "ref path too long: %s", ref[i]); goto error; } if(hasheq(&upd[i], &Zhash)){ @@ -398,11 +384,11 @@ updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) continue; } if((o = readobject(upd[i])) == nil){ - werrstr("update to nonexistent hash %H", upd[i]); + snprint(buf, sizeof(buf), "update to nonexistent hash %H", upd[i]); goto error; } if(o->type != GCommit){ - werrstr("not commit: %H", upd[i]); + snprint(buf, sizeof(buf), "not commit: %H", upd[i]); goto error; } if(o->commit->mtime > newtm){ @@ -411,11 +397,11 @@ updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) } unref(o); if((fd = create(refpath, OWRITE|OTRUNC, 0644)) == -1){ - werrstr("open ref: %r"); + 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 error; } @@ -436,19 +422,20 @@ updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) */ if(resolveref(&h, "HEAD") == -1 && hadref == 0 && newidx != -1){ if((fd = create(".git/HEAD", OWRITE|OTRUNC, 0644)) == -1){ - werrstr("open HEAD: %r"); + snprint(buf, sizeof(buf), "open HEAD: %r"); goto error; } if(fprint(fd, "ref: %s", ref[0]) == -1){ - werrstr("write HEAD ref: %r"); + snprint(buf, sizeof(buf), "write HEAD ref: %r"); goto error; } close(fd); } ret = 0; error: - fmtpkt(c, "ERR %r"); + fmtpkt(c, "ERR %s", buf); close(lockfd); + werrstr(buf); return ret; } diff --git a/util.c b/util.c index 5ae897d..99b1f8e 100644 --- a/util.c +++ b/util.c @@ -10,6 +10,10 @@ Hash Zhash; int chattygit; int interactive = 1; +enum { + Seed = 2928213749ULL +}; + Object* emptydir(void) { @@ -321,3 +325,120 @@ showprogress(int x, int pct) } return pct; } + +void +qinit(Objq *q) +{ + memset(q, 0, sizeof(Objq)); + q->nheap = 0; + q->heapsz = 8; + q->heap = eamalloc(q->heapsz, sizeof(Qelt)); +} + +void +qclear(Objq *q) +{ + free(q->heap); +} + +void +qput(Objq *q, Object *o, int color) +{ + Qelt t; + int i; + + if(q->nheap == q->heapsz){ + q->heapsz *= 2; + q->heap = earealloc(q->heap, q->heapsz, sizeof(Qelt)); + } + q->heap[q->nheap].o = o; + q->heap[q->nheap].color = color; + q->heap[q->nheap].ctime = o->commit->ctime; + for(i = q->nheap; i > 0; i = (i-1)/2){ + if(q->heap[i].ctime < q->heap[(i-1)/2].ctime) + break; + t = q->heap[i]; + q->heap[i] = q->heap[(i-1)/2]; + q->heap[(i-1)/2] = t; + } + q->nheap++; +} + +int +qpop(Objq *q, Qelt *e) +{ + int i, l, r, m; + Qelt t; + + if(q->nheap == 0) + return 0; + *e = q->heap[0]; + if(--q->nheap == 0) + return 1; + + i = 0; + q->heap[0] = q->heap[q->nheap]; + while(1){ + m = i; + l = 2*i+1; + r = 2*i+2; + if(l < q->nheap && q->heap[m].ctime < q->heap[l].ctime) + m = l; + if(r < q->nheap && q->heap[m].ctime < q->heap[r].ctime) + m = r; + if(m == i) + break; + t = q->heap[m]; + q->heap[m] = q->heap[i]; + q->heap[i] = t; + i = m; + } + return 1; +} + +u64int +murmurhash2(void *pp, usize n) +{ + u32int m = 0x5bd1e995; + u32int r = 24; + u32int h, k; + u32int *w, *e; + uchar *p; + + h = Seed ^ n; + e = pp; + e += (n / 4); + for (w = pp; w != e; w++) { + /* + * NB: this is endian dependent. + * This is fine for use in git, since the + * hashes computed here are only ever used + * for in memory data structures. + * + * Pack files will differ when packed on + * machines with different endianness, + * but the results will still be correct. + */ + k = *w; + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + } + + p = (uchar*)w; + switch (n & 0x3) { + case 3: h ^= p[2] << 16; + case 2: h ^= p[1] << 8; + case 1: h ^= p[0] << 0; + h *= m; + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} diff --git a/walk.c b/walk.c index 50a227e..b6a2d63 100644 --- a/walk.c +++ b/walk.c @@ -295,10 +295,12 @@ main(int argc, char **argv) if(!quiet && (printflg & Tflg)) print("%s%s\n", tstr, p); }else{ - if(d == nil || access(rpath, AEXIST) == 0){ - dirty |= Rflg; - if(!quiet && (printflg & Rflg)) - print("%s%s\n", rstr, p); + if(d == nil || access(rpath, AEXIST) == 0 ){ + if(access(bpath, AEXIST) == 0){ + dirty |= Rflg; + if(!quiet && (printflg & Rflg)) + print("%s%s\n", rstr, p); + } }else if(access(bpath, AEXIST) == -1) { dirty |= Aflg; if(!quiet && (printflg & Aflg))