ref: 72ba278a188950bf0d09f4459e396269d6ec8505
parent: c65ded47aa82d89e1998ed0e8bbc8b1009d25642
author: Ori Bernstein <[email protected]>
date: Sun Nov 27 23:49:57 EST 2022
git: sync with 9front
--- a/add
+++ b/add
@@ -16,11 +16,11 @@
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/*){
--- a/branch
+++ b/branch
@@ -32,7 +32,7 @@
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(! ~ $#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 @@
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
@@ -69,8 +71,6 @@
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)
cleanpaths=($modified $deleted)
if not {
@@ -100,10 +100,9 @@
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 @@
}
echo ref: $new > .git/HEAD
+echo $new: `{git/query $new}
exit ''
--- a/clone
+++ b/clone
@@ -7,7 +7,7 @@
if(~ $debug 1)
debug=(-d)
-remote=`{echo $1 | subst -g '/*$'}
+remote=`{echo $1 | sed 's@/*$@@'}
local=$2
if(~ $#remote 0)
@@ -33,7 +33,7 @@
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,7 +79,7 @@
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
@@ -86,12 +86,10 @@
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{
--- 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}' </adm/keys.who}
- if(~ $email '')
- email=`{awk -F'|' '$1=="'$user'" {x=$5} END{print x}' </adm/keys.who}
- }
- if(~ $name '')
- name=glenda
- if(~ $email '')
- [email protected]
-}
-
fn findbranch{
branch=`{git/branch}
if(test -e $gitfs/branch/$branch/tree){
@@ -58,7 +43,7 @@
echo '#'
for(p in $parents)
echo '# parent:' $p
- git/walk -fAMR $files | subst -g '^' '# '
+ git/walk -fAMR $files | subst '^' '# '
echo '#'
echo '# Commit message:'
}
@@ -92,7 +77,7 @@
msg=`''{cat $msgfile}
if(! ~ $#parents 0)
pflags='-p'^$parents
- hash=`{git/save -n $"name -e $"email -m $"msg $pflags $files || die $status}
+ hash=`{git/save -n $"name -e $"email -m $"msg $pflags $files || die $status}
rm -f .git/index9/merge-parents
}
@@ -116,7 +101,8 @@
}
fn sigexit{
- rm -f $msgfile $msgfile.tmp
+ if(! ~ $#msgfile 0)
+ rm -f $msgfile $msgfile.tmp
}
gitup
--- a/common.rc
+++ b/common.rc
@@ -11,35 +11,60 @@
exit 'usage'
}
-# subst [-g] this [that]
-fn subst{
- awk 'BEGIN{
- global = 0
- for(i = 1; ARGV[i] ~ /^-/; i++){
- if(ARGV[i] == "-g")
- global = 1
- ARGC--
- }
- this = ARGV[i++]; ARGC--
- that = ARGV[i++]; ARGC--
- }
+fn subst {
+ awk '
+ BEGIN{ARGC=0}
+ {sub(ARGV[1], ARGV[2]); print}
+ ' $*
+}
+
+fn drop {
+ awk '
+ BEGIN{ARGC=0}
{
- if(global) gsub(this, that)
- else sub(this, that)
+ if(index($0, ARGV[1]) == 1)
+ $0=substr($0, length(ARGV[1])+1)
- }' $*
+ }
+ ' $*
}
-fn present {
+fn mergeperm {
if(~ $1 /dev/null && cmp $2 $3>/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=()
+ }
}
-# merge1 out theirs base ours
+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}' </adm/keys.who}
+ if(~ $email '')
+ email=`$nl{awk -F'|' '$1=="'$user'" {x=$5} END{print x}' </adm/keys.who}
+ }
+ if(~ $name '')
+ name=$user
+ if(~ $email '')
+ email=$user@$sysname
+ status=''
+}
+
+# merge1 out ours base theirs
fn merge1 {@{
rfork e
n=$pid
@@ -62,9 +87,10 @@
if(! ape/diff3 -3 -m $ours $base $theirs > $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 @@
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)
--- a/compat
+++ b/compat
@@ -4,12 +4,14 @@
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 @@
}
shift
}
- ls >[1=2]
git/init $opts $args
}
@@ -94,7 +95,7 @@
echo `{dcmd git9/branch | sed s@^heads/@@g}
shift
case *
- dprint option $opt
+ die unknown option $opt
}
shift
}
@@ -112,6 +113,15 @@
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_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 <command> <args>' >[1=2]
@@ -149,10 +209,20 @@
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 ''
--- a/delta.c
+++ b/delta.c
@@ -7,7 +7,6 @@
Minchunk = 128,
Maxchunk = 8192,
Splitmask = (1<<8)-1,
-
};
static u32int geartab[] = {
@@ -48,9 +47,7 @@
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,14 +169,18 @@
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)
@@ -186,9 +187,8 @@
break;
p++;
q++;
- n++;
}
- return n;
+ return n + (p - p0);
}
Delta*
--- a/diff
+++ b/diff
@@ -26,12 +26,22 @@
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 ''
--- a/export
+++ b/export
@@ -41,9 +41,8 @@
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 @@
b=b/$f
if(! test -e $b)
b=/dev/null
- ape/diff -urN $a $b
+ diff -ur $a $b
}
} >$patchfile
if(~ $#patchdir 0){
--- a/fs.c
+++ b/fs.c
@@ -12,12 +12,13 @@
Qhead,
Qbranch,
Qcommit,
- Qcommitmsg,
- Qcommitparent,
- Qcommittree,
- Qcommitdata,
- Qcommithash,
- Qcommitauthor,
+ Qmsg,
+ Qparent,
+ Qtree,
+ Qcdata,
+ Qhash,
+ Qauthor,
+ Qcommitter,
Qobject,
Qctl,
Qmax,
@@ -69,13 +70,14 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
objread(r, aux);
break;
case Qcommit:
- case Qcommittree:
- case Qcommitdata:
+ case Qtree:
+ case Qcdata:
objread(r, aux);
break;
default:
@@ -796,6 +806,34 @@
}
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)
{
Gitaux *aux;
@@ -821,6 +859,7 @@
.attach=gitattach,
.walk1=gitwalk1,
.clone=gitclone,
+ .open=gitopen,
.read=gitread,
.stat=gitstat,
.destroyfid=gitdestroyfid,
--- a/git.h
+++ b/git.h
@@ -4,6 +4,7 @@
#include <flate.h>
#include <regexp.h>
+typedef struct Capset Capset;
typedef struct Conn Conn;
typedef struct Hash Hash;
typedef struct Delta Delta;
@@ -18,6 +19,8 @@
typedef struct Objlist Objlist;
typedef struct Dtab Dtab;
typedef struct Dblock Dblock;
+typedef struct Objq Objq;
+typedef struct Qelt Qelt;
enum {
Pathmax = 512,
@@ -24,6 +27,8 @@
Npackcache = 32,
Hashsz = 20,
Pktmax = 65536,
+ KiB = 1024,
+ MiB = 1024*KiB,
};
enum {
@@ -151,6 +156,18 @@
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 @@
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 @@
char *strip(char *);
int findrepo(char *, int);
int showprogress(int, int);
+u64int murmurhash2(void*, usize);
/* packing */
void dtinit(Dtab *, Object*);
@@ -295,6 +313,7 @@
/* 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 *);
@@ -301,3 +320,9 @@
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*);
--- a/import
+++ b/import
@@ -7,11 +7,13 @@
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 @@
}
state=="headers" && /^From:/ {
sub(/^From:[ \t]*/, "", $0);
- name=$0;
- email=$0;
- sub(/[ \t]*<.*$/, "", name);
- sub(/.*</, "", email);
- sub(/>/, "", email);
+ aname=$0;
+ amail=$0;
+ sub(/[ \t]*<.*$/, "", aname);
+ sub(/^[^<]*</, "", amail);
+ sub(/>[^>]*$/, "", amail);
}
state=="headers" && /^Date:/{
sub(/^Date:[ \t]*/, "", $0)
@@ -43,12 +45,19 @@
}
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,10 +66,10 @@
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
@@ -67,9 +76,17 @@
# 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 @@
}
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 @@
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 @@
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 ''
--- a/init
+++ b/init
@@ -20,7 +20,7 @@
}
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'
--- a/log.c
+++ b/log.c
@@ -14,11 +14,10 @@
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 @@
}
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 @@
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 @@
}
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 @@
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,7 +132,7 @@
return p;
}
-static void
+static int
show(Object *o)
{
Tm tm;
@@ -139,9 +139,6 @@
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 @@
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 @@
Bprint(out, "\n");
}
Bflush(out);
+ return 1;
}
static void
@@ -179,10 +180,14 @@
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);
@@ -189,64 +194,10 @@
}
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 @@
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);
}
}
@@ -293,6 +250,9 @@
break;
case 's':
shortlog++;
+ break;
+ case 'n':
+ msgcount = atoi(EARGF(usage()));
break;
default:
usage();
--- a/merge
+++ b/merge
@@ -7,13 +7,12 @@
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
}
}
--- a/mkfile
+++ b/mkfile
@@ -3,7 +3,7 @@
BIN=/$objtype/bin/git
TARG=\
conf\
- fetch\
+ get\
fs\
log\
query\
@@ -21,6 +21,7 @@
compat\
diff\
export\
+ hist\
import\
init\
merge\
--- a/pack.c
+++ b/pack.c
@@ -20,7 +20,7 @@
struct Meta {
Object *obj;
- char *path;
+ vlong path;
vlong mtime;
/* The best delta we picked */
@@ -65,8 +65,8 @@
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 @@
if(!(o->flag & Ccache)){
o->flag |= Ccache;
ref(o);
- ncache++;
+ ncache += o->size;
}
while(ncache > cachemax && lrutail != nil){
p = lrutail;
@@ -168,8 +168,8 @@
p->flag &= ~Ccache;
p->prev = nil;
p->next = nil;
+ ncache -= p->size;
unref(p);
- ncache--;
}
}
@@ -194,26 +194,26 @@
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,28 +253,44 @@
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;
}
@@ -281,10 +297,7 @@
static void
closepack(Packf *pf)
{
- if(--pf->refs == 0){
- Bterm(pf->pack);
- pf->pack = nil;
- }
+ pf->refs--;
}
static u32int
@@ -871,6 +884,8 @@
}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 @@
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 @@
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;
- if(a->mtime != b->mtime)
- return a->mtime - b->mtime;
+ return (cmp < 0) ? -1 : 1;
+ cmp = a->mtime - b->mtime;
+ if(cmp != 0)
+ return (cmp < 0) ? -1 : 1;
return memcmp(a->obj->hash.h, b->obj->hash.h, sizeof(a->obj->hash.h));
}
@@ -1305,7 +1320,7 @@
}
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 @@
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 @@
freemeta(Meta *m)
{
free(m->delta);
- free(m->path);
free(m);
}
@@ -1339,8 +1353,9 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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;
--- a/proto.c
+++ b/proto.c
@@ -58,8 +58,8 @@
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){
@@ -94,6 +94,20 @@
}
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)
{
dprint(1, "<=w= 0000\n");
@@ -220,14 +234,27 @@
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){
--- a/pull
+++ b/pull
@@ -3,17 +3,13 @@
. /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 @@
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 @@
upstream=THEM
}
-update $branch $upstream $remote
+update $upstream $remote
if (~ $fetchonly 1)
exit
@@ -75,8 +66,7 @@
# 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 ''
--- a/query.c
+++ b/query.c
@@ -158,6 +158,7 @@
char query[2048], repo[512];
ARGBEGIN{
+ case 'd': chattygit++; break;
case 'p': fullpath++; break;
case 'c': changes++; break;
case 'r': reverse ^= 1; break;
--- a/rebase
+++ b/rebase
@@ -7,8 +7,6 @@
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 @@
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)
--- a/ref.c
+++ b/ref.c
@@ -5,15 +5,20 @@
#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 {
char *str;
char *p;
@@ -22,19 +27,13 @@
int stksz;
};
-struct XObject {
- Object *obj;
- Object *mark;
- XObject *queue;
- XObject *next;
+static char *colors[] = {
+[Keep] "keep",
+[Drop] "drop",
+[Blank] "blank",
+[Skip] "skip",
};
-struct Objq {
- Objq *next;
- Object *o;
- int color;
-};
-
static Object zcommit = {
.type=GCommit
};
@@ -46,26 +45,6 @@
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 @@
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; i<nelem(ht); i++){
- while(h = ht[i]){
- ht[i] = h->next;
- 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;
+ Qelt e;
+ Objq objq;
+ Objset keep, drop, skip;
+ Object *o, *c, **range;
+ int i, nskip, nrange;
- 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;
-}
-
-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 @@
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 @@
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)
{
@@ -393,33 +314,10 @@
}
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 @@
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
--- a/revert
+++ b/revert
@@ -5,16 +5,18 @@
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 ''
--- a/save.c
+++ b/save.c
@@ -14,17 +14,27 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
}
--- a/send.c
+++ b/send.c
@@ -134,8 +134,8 @@
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 @@
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 @@
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;
}
--- a/serve.c
+++ b/serve.c
@@ -9,20 +9,6 @@
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)
{
int i, ret, nrefs;
@@ -34,7 +20,7 @@
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 @@
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,7 +364,7 @@
*/
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++){
@@ -385,12 +371,12 @@
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 @@
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 @@
}
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,11 +422,11 @@
*/
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);
@@ -447,8 +433,9 @@
}
ret = 0;
error:
- fmtpkt(c, "ERR %r");
+ fmtpkt(c, "ERR %s", buf);
close(lockfd);
+ werrstr(buf);
return ret;
}
--- a/util.c
+++ b/util.c
@@ -10,6 +10,10 @@
int chattygit;
int interactive = 1;
+enum {
+ Seed = 2928213749ULL
+};
+
Object*
emptydir(void)
{
@@ -320,4 +324,121 @@
fprint(2, "\b\b\b\b%3d%%", 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;
}
--- a/walk.c
+++ b/walk.c
@@ -295,10 +295,12 @@
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))