shithub: git9

Download patch

ref: 4fecf55b94bb8a2c8648784fca2e00c85bfe2526
parent: 45001b2700f817cc4a3609574031437040de393e
author: Ori Bernstein <ori@eigenstate.org>
date: Mon May 27 15:09:53 EDT 2024

git: sync with 9front

--- a/add
+++ b/add
@@ -7,32 +7,13 @@
 flagfmt='r:remove'; args='file ...'
 eval `''{aux/getflags $*} || exec aux/usage
 
-add='tracked'
-del='removed'
-if(~ $remove 1){
-	add='removed'
-	del='tracked'
-}
+s=A
+if(~ $remove 1)
+	s=R
 if(~ $#* 0)
 	exec aux/usage
 
 paths=`$nl{cleanname -d $gitrel $* | drop $gitroot}
-if(~ $add tracked)
-	files=`$nl{walk -f ./$paths}
-if not
-	files=`$nl{cd .git/index9/tracked/ && walk -f ./$paths}
-
-for(f in $files){
-	if(! ~ `$nl{cleanname $f} .git/*){
-		addpath=.git/index9/$add/$f
-		delpath=.git/index9/$del/$f
-		mkdir -p `$nl{basename -d $addpath}
-		mkdir -p `$nl{basename -d $delpath}
-		# We don't want a matching qid, so that
-		# git/walk doesn't think this came from
-		# a checkout.
-		echo -n > $addpath
-		rm -f $delpath
-	}
-}
+walk -f ./$paths | grep -v '^(./)?.git/' | \
+	sed 's/^/'$s' NOQID 0 /' >> .git/INDEX9
 exit ''
--- a/branch
+++ b/branch
@@ -4,7 +4,7 @@
 
 gitup
 
-flagfmt='a:listall, b:baseref ref, d:delete, n:newbr, s:stay, m:merge'
+flagfmt='a:listall, b:baseref ref, d:delete, n:newbr, s:stay, m:merge, M:nomod'
 args='[branch]'
 eval `''{aux/getflags $*} || exec aux/usage
 
@@ -48,8 +48,11 @@
 modified=`$nl{git/query -c HEAD $base | grep '^[^-]' | subst '^..'}
 deleted=`$nl{git/query -c HEAD $base | grep '^-' | subst '^..'}
 
+# if we delete the current branch without switching, bad things happen
+if(~ $delete 1 && ~ `{git/query HEAD} `{git/query $branch})
+	die 'cannot delete current branch'
 # if we're not merging, don't clobber existing changes.
-if(~ $#merge 0){
+if(~ $#merge 0 && ~ $#delete 0){
 	if(! ~ $#modified 0 || ! ~ $#deleted 0){
 		git/walk -fRMA $modified $deleted || 
 			die 'uncommitted changes would be clobbered'
@@ -84,7 +87,6 @@
 for(m in $cleanpaths){
 	d=`$nl{basename -d $m}
 	mkdir -p $d
-	mkdir -p .git/index9/tracked/$d
 	# Modifications can turn a file into
 	# a directory, or vice versa, so we
 	# need to delete and copy the files
@@ -97,11 +99,11 @@
 		b=file
 	if(! ~ $a $b){
 		rm -rf $m
-		rm -rf .git/index9/tracked/$m
+		echo R NOQID 0 $m >> .git/INDEX9
 	}
 	if(~ $b file){
 		cp -x -- $basedir/tree/$m $m
-		walk -eq $m > .git/index9/tracked/$m
+		echo T NOQID 0 $m >> .git/INDEX9
 		touch $m
 	}
 }
@@ -112,9 +114,11 @@
 	merge1 $ours $ours $common $theirs
 }
 
-if(! ~ $#deleted 0){
-	rm -f $deleted
-	rm -f .git/index9/tracked/$deleted
+for(d in $deleted){
+	if(! test -d $d){
+		rm -f $d
+		echo R NOQID 0 $d >> .git/INDEX9
+	}
 }
 
 echo ref: $new > .git/HEAD
--- a/clone
+++ b/clone
@@ -17,8 +17,8 @@
 if(~ $#branch 1)
 	branchflag=(-b $branch)
 
-if(test -e $local)
-	die 'repository already exists:' $local
+if(test -e $local && ~ `{ls $local | sed 1q | wc -l} 1)
+	die 'destination already exists:' $local
 
 fn clone{
 	flag +e
@@ -67,7 +67,7 @@
 				refdir = headref;
 				gsub("/?[^/]*/?$", "", refdir)
 				gsub("^refs/remotes/origin", "refs/heads", headref)
-				system("mkdir -p .git/"refdir);
+				system("mkdir -p `{basename -d .git/"headref"}");
 				system("cp .git/" remote " .git/" headref)
 				print "ref: " headref > ".git/HEAD"
 			}else if(headhash != ""){
@@ -82,15 +82,13 @@
 	rbranch=`{echo $lbranch | subst 'heads' 'remotes/origin'}
 	echo checking out repository...
 	if(test -f .git/refs/$rbranch){
+		mkdir -p `{basename -d .git/refs/$lbranch}
 		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 | drop $tree}){
-			idx=.git/index9/tracked/$f
-			mkdir -p `$nl{basename -d $idx}
-			walk -eq ./$f > $idx
-		}
+		{for(f in `$nl{cd $tree && walk -f})
+			echo 'T NOQID 0 '$f} > .git/INDEX9
 	}
 	if not{
 		echo no default branch >[1=2]
@@ -100,6 +98,7 @@
 
 fn sigint {
 	echo cancelled clone $remote: cleaning $local >[1=2]
+	unmount $local/.git/fs >[2]/dev/null
 	rm -rf $local
 	exit interrupted
 }
@@ -108,6 +107,7 @@
 st=$status
 if(! ~ $st ''){
 	echo failed to clone $remote: cleaning $local >[1=2]
+	unmount $local/.git/fs >[2]/dev/null
 	rm -rf $local
 	exit $st
 }
--- a/commit
+++ b/commit
@@ -65,8 +65,8 @@
 fn parents{
 	if(! ~ $#revise 0)
 		parents=`{cat $gitfs/HEAD/parent}
-	if not if(test -f .git/index9/merge-parents)
-		parents=`{cat .git/index9/merge-parents | sort | uniq}
+	if not if(test -f .git/merge-parents)
+		parents=`{cat .git/merge-parents | sort | uniq}
 	if not if(~ $initial true)
 		parents=()
 	if not
@@ -78,7 +78,7 @@
 	if(! ~ $#parents 0)
 		pflags='-p'^$parents
 	hash=`{git/save -n $"name -e $"email -m $"msg $pflags $files || die $status}
-	rm -f .git/index9/merge-parents
+	rm -f .git/merge-parents
 }
 
 fn update{
@@ -89,14 +89,10 @@
 	echo $branch: $hash
 	echo $hash > $refpath
 	for(f in $files){
-		if(test -e .git/index9/removed/$f || ! test -e $f){
-			rm -f .git/index9/removed/$f
-			rm -f .git/index9/tracked/$f
-		}
-		if not{
-			mkdir -p `{basename -d $f}
-			walk -eq $f > .git/index9/tracked/$f
-		}
+		if(! test -e $f && ! test -e .git/object/$hash/tree/$f)
+			echo R NOQID 0 $f >> .git/INDEX9
+		if not
+			echo T NOQID 0 $f >> .git/INDEX9
 	}
 }
 
@@ -120,10 +116,12 @@
 }
 
 files=()
+if(test -f .git/merge-parents)
+	files=`$nl{git/query -c `{cat .git/merge-parents} | sed 's/^..//'}
 if(! ~ $#* 0)
-	files=`$nl{git/walk -c `$nl{cleanname -d $gitrel $*}}
-if(~ $status '' || ~ $#files 0 && ! test -f .git/index9/merge-parents && ~ $#revise 0)
-	die 'nothing to commit' $status
+	files=($files `$nl{git/walk -c `$nl{cleanname -d $gitrel $*}})
+if(~ $status '' || ~ $#files 0 && ! test -f .git/merge-parents && ~ $#revise 0)
+	die 'nothing to commit'
 @{
 	flag e +
 	whoami
--- a/common.rc
+++ b/common.rc
@@ -84,10 +84,11 @@
 		base=/dev/null
 	if(! test -f $theirs)
 		theirs=/dev/null
-	if(! ape/diff3 -3 -m $ours $base $theirs > $tmp)
-		echo merge needed: $out >[1=2]
 
 	if(mergeperm $ours $base $theirs){
+		mkdir -p `{basename -d $tmp}
+		if(! ape/diff3 -3 -m $ours $base $theirs > $tmp)
+			echo merge needed: $out >[1=2]
 		mv $tmp $out
 		git/add $out
 		chmod $mergedperms $out
@@ -99,9 +100,14 @@
 }}
 
 fn gitup{
-	gitroot=`{git/conf -r >[2]/dev/null}
+	gitroot=`$nl{git/conf -r >[2]/dev/null}
 	if(~ $#gitroot 0)
 		die 'not a git repository'
+	gitwork=`$nl{git/conf work.dir}
+	if(~ $#gitwork 1)
+		bind -c $gitwork/objects $gitroot/.git/objects
+	if(~ $#workdir 1)
+		bind $workdir .git/objects
 	gitfs=$gitroot/.git/fs
 	gitrel=`{pwd | drop $gitroot | sed 's@^/@@'}
 	if(~ $#gitrel 0)
--- a/diff
+++ b/diff
@@ -4,33 +4,32 @@
 
 gitup
 
-flagfmt='c:commit branch, s:summarize'; args='[file ...]'
+flagfmt='c:commit branch, s:summarize, u:uncommitted'; args='[file ...]'
 eval `''{aux/getflags $*} || exec aux/usage
 
-if(~ $#commit 0)
+if(~ $#commit 0){
 	commit=HEAD
+	cparam=()
+}
 
 files=()
+filt=MAR
+if(~ $#uncommitted 1)
+	filt=MARU
 if(! ~ $#* 0)
 	files=`{cleanname -d $gitrel $*}
 
 branch=`{git/query -p $commit}
-if(~ $summarize 1){
-	git/walk -fMAR $files
+if(~ $summarize 1 || ~ $uncommitted 1){
+	git/walk -f$filt $cparam $files
 	exit
 }
 
-fn lsdirty {
-	git/walk -c -fRMA $files
-	if(! ~ $commit HEAD)
-		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}){
+for(f in `$nl{git/walk -c -f$filt $cparam $files}){
 	if(~ $#showed 0){
 		echo diff `{git/query $commit} uncommitted
 		showed=1
--- a/get.c
+++ b/get.c
@@ -219,8 +219,6 @@
 			return -1;
 		if(n == 0)
 			break;
-		if(strncmp(buf, "ERR ", 4) == 0)
-			sysfatal("%s", buf + 4);
 
 		if(first && n > strlen(buf))
 			handlecaps(buf + strlen(buf) + 1);
--- a/git.h
+++ b/git.h
@@ -10,7 +10,6 @@
 typedef struct Delta	Delta;
 typedef struct Cinfo	Cinfo;
 typedef struct Tinfo	Tinfo;
-typedef struct Ginfo	Ginfo;
 typedef struct Object	Object;
 typedef struct Objset	Objset;
 typedef struct Pack	Pack;
@@ -130,7 +129,6 @@
 	union {
 		Cinfo	*commit;
 		Tinfo	*tree;
-		Ginfo	*tag;
 	};
 };
 
@@ -147,18 +145,6 @@
 	Hash	tree;
 	char	*author;
 	char	*committer;
-	char	*msg;
-	int	nmsg;
-	vlong	ctime;
-	vlong	mtime;
-};
-
-struct Ginfo {
-	/* Tag */
-	Hash	object;
-	char	*tagger;
-	char	*type;
-	char	*tag;
 	char	*msg;
 	int	nmsg;
 	vlong	ctime;
--- a/import
+++ b/import
@@ -98,7 +98,8 @@
 		if(~ $#nocommit 0){
 			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
+				for(f in $files)
+					echo T NOQID 0 $f >> .git/INDEX9
 			}
 		}
 		status=''''
--- a/init
+++ b/init
@@ -31,7 +31,7 @@
 	echo '[branch "'$branch'"]'
 	echo '	remote = origin'
 }
-
+>$dir/.git/INDEX9
 >$dir/.git/HEAD {
 	echo ref: refs/heads/$branch
 }
--- a/merge
+++ b/merge
@@ -38,8 +38,8 @@
 	git/revert .
 	exit ''
 }
-echo $ours >> .git/index9/merge-parents
-echo $theirs >> .git/index9/merge-parents
+echo $ours >> .git/merge-parents
+echo $theirs >> .git/merge-parents
 
 merge $ours $base $theirs
 >[1=2] echo 'merge complete: remember to commit'
--- a/pack.c
+++ b/pack.c
@@ -908,46 +908,6 @@
 }
 
 static void
-parsetag(Object *o)
-{
-	char *p, buf[128];
-	int np;
-
-	p = o->data;
-	np = o->size;
-	o->tag = emalloc(sizeof(Ginfo));
-	while(1){
-		if(scanword(&p, &np, buf, sizeof(buf)) == -1)
-			break;
-		if(strcmp(buf, "object") == 0){
-			if(scanword(&p, &np, buf, sizeof(buf)) == -1)
-				sysfatal("invalid commit: tree missing");
-			if(hparse(&o->tag->object, buf) == -1)
-				sysfatal("invalid commit: garbled tree");
-		}else if(strcmp(buf, "tagger") == 0){
-			parseauthor(&p, &np, &o->commit->author, &o->tag->mtime);
-		}else if(strcmp(buf, "type") == 0){
-			if(scanword(&p, &np, buf, sizeof(buf)) == -1)
-				sysfatal("bad tag type");
-			if((o->tag->type = strdup(buf)) == nil)
-				sysfatal("strdup: %r");
-		}else if(strcmp(buf, "tag") == 0){
-			if(scanword(&p, &np, buf, sizeof(buf)) == -1)
-				sysfatal("bad tag type");
-			if((o->tag->type = strdup(buf)) == nil)
-				sysfatal("strdup: %r");
-		}
-		nextline(&p, &np);
-	}
-	while (np && isspace(*p)) {
-		p++;
-		np--;
-	}
-	o->commit->msg = p;
-	o->commit->nmsg = np;
-}
-
-static void
 parsetree(Object *o)
 {
 	int m, a, entsz, nent;
@@ -1001,6 +961,12 @@
 	o->tree->ent = ent;
 	o->tree->nent = nent;
 }
+
+static void
+parsetag(Object *)
+{
+}
+
 void
 parseobject(Object *o)
 {
--- a/proto.c
+++ b/proto.c
@@ -75,6 +75,12 @@
 		sysfatal("pktline: undersize buffer");
 	if(readn(c->rfd, buf, n) != n)
 		return -1;
+	if(n > 4 && strncmp(buf, "ERR ", 4) == 0){
+		if((e = strrchr(buf, '\n')) != nil)
+			*e = '\0';
+		werrstr("%s", buf + 4);
+		return -1;
+	}
 	buf[n] = 0;
 	tracepkt(1, "=r=>", buf, n);
 	return n;
--- a/revert
+++ b/revert
@@ -8,12 +8,12 @@
 if (! eval `''{aux/getflags $*} || ~ $#* 0)
 	exec aux/usage
 
-commit=$gitfs/HEAD
-if(~ $#query 1)
-	commit=`{git/query -p $query}
+if(~ $#query 0)
+	query=HEAD
+commit=`{git/query -p $query}
 
 files=`$nl{cleanname -d $gitrel $* | drop $gitroot}
-for(f in `$nl{cd $commit/tree/ && walk -f ./$files}){
+for(f in `$nl{git/walk -c -fRM -b $query $files}){
 	mkdir -p `{basename -d $f}
 	cp -x -- $commit/tree/$f $f
 	touch $f
--- a/send.c
+++ b/send.c
@@ -149,8 +149,6 @@
 		if(first && n > strlen(buf))
 			parsecaps(buf + strlen(buf) + 1, &cs);
 		first = 0;
-		if(strncmp(buf, "ERR ", 4) == 0)
-			sysfatal("%s", buf + 4);
 
 		if(getfields(buf, sp, nelem(sp), 1, " \t\r\n") != 2)
 			sysfatal("invalid ref line %.*s", utfnlen(buf, n), buf);
--- a/serve.c
+++ b/serve.c
@@ -8,6 +8,19 @@
 char	*pathpfx = nil;
 int	allowwrite;
 
+_Noreturn static void
+fail(Conn *c, char *fmt, ...)
+{
+	char msg[ERRMAX];
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsnprint(msg, sizeof(msg), fmt, ap);
+	va_end(ap);
+	fmtpkt(c, "ERR %s\n", msg);
+	sysfatal("%s", msg);
+}
+
 int
 showrefs(Conn *c)
 {
@@ -24,7 +37,7 @@
 			goto error;
 
 	if((nrefs = listrefs(&refs, &names)) == -1)
-		sysfatal("listrefs: %r");
+		fail(c, "listrefs: %r");
 	for(i = 0; i < nrefs; i++){
 		if(strncmp(names[i], "heads/", strlen("heads/")) != 0)
 			continue;
@@ -128,10 +141,10 @@
 
 	dprint(1, "negotiating pack\n");
 	if(servnegotiate(c, &head, &nhead, &tail, &ntail) == -1)
-		sysfatal("negotiate: %r");
+		fail(c, "negotiate: %r");
 	dprint(1, "writing pack\n");
 	if(writepack(c->wfd, head, nhead, tail, ntail, &h) == -1)
-		sysfatal("send: %r");
+		fail(c, "send: %r");
 	return 0;
 }
 
@@ -138,6 +151,7 @@
 int
 validref(char *s)
 {
+	cleanname(s);
 	if(strncmp(s, "refs/", 5) != 0)
 		return 0;
 	for(; *s != '\0'; s++)
@@ -149,7 +163,7 @@
 int
 recvnegotiate(Conn *c, Hash **cur, Hash **upd, char ***ref, int *nupd)
 {
-	char pkt[Pktmax], *sp[4];
+	char pkt[Pktmax], refpath[512], *sp[4];
 	Hash old, new;
 	int n, i;
 
@@ -186,6 +200,16 @@
 		(*cur)[*nupd] = old;
 		(*upd)[*nupd] = new;
 		(*ref)[*nupd] = estrdup(sp[2]);
+		n = snprint(refpath, sizeof(refpath), ".git/%s", sp[2]);
+		if(n >= sizeof(refpath)-1){
+			fmtpkt(c, "ERR invalid ref %s\n", sp[2]);
+			goto error;
+		}
+		if(access(refpath, AWRITE) == -1
+		&& access(refpath, AEXIST) == 0){
+			fmtpkt(c, "ERR read-only ref %s\n", sp[2]);
+			goto error;
+		}
 		*nupd += 1;
 	}		
 	return 0;
@@ -446,8 +470,10 @@
 	char **ref;
 	int nupd;
 
+	if(!allowwrite)
+		fail(c, "read-only repo");
 	if(recvnegotiate(c, &cur, &upd, &ref, &nupd) == -1)
-		sysfatal("negotiate refs: %r");
+		fail(c, "negotiate refs: %r");
 	if(nupd != 0 && updatepack(c) == -1)
 		sysfatal("update pack: %r");
 	if(nupd != 0 && updaterefs(c, cur, upd, ref, nupd) == -1)
@@ -520,20 +546,18 @@
 	repo = parsecmd(buf, cmd, sizeof(cmd));
 	cleanname(repo);
 	if(strncmp(repo, "../", 3) == 0)
-		sysfatal("invalid path %s\n", repo);
-	if(bind(repo, "/", MREPL) == -1){
-		fmtpkt(&c, "ERR no repo %r\n");
-		sysfatal("enter %s: %r", repo);
-	}
+		fail(&c, "invalid path %s\n", repo);
+	if(bind(repo, "/", MREPL) == -1)
+		fail(&c, "no such repo", repo);
 	if(chdir("/") == -1)
-		sysfatal("chdir: %r");
+		fail(&c, "no such repo");
 	if(access(".git", AREAD) == -1)
-		sysfatal("no git repository");
-	if(strcmp(cmd, "git-receive-pack") == 0 && allowwrite)
+		fail(&c, "no such repo");
+	if(strcmp(cmd, "git-receive-pack") == 0)
 		recvpack(&c);
 	else if(strcmp(cmd, "git-upload-pack") == 0)
 		servpack(&c);
 	else
-		sysfatal("unsupported command '%s'", cmd);
+		fail(&c, "unsupported command '%s'", cmd);
 	exits(nil);
 }