code: plan9front

Download patch

ref: d9564c0642b9a8280f8e4dfe9ff15a2c65b6d93d
parent: 485b334608910bbf6930a4f52ca946ba092fad04
author: Ori Bernstein <ori@eigenstate.org>
date: Thu Sep 2 22:47:18 EDT 2021

git: separate author and committer
Git has the ability to track the person who
creates a commit separately from the person
who wrote the commit. For git9, we ignored
this feature.

However, as we start using git/import more,
it will be useful to figure out who imported
a commit, as well as who wrote it.

This change adds support for seeing this
information in git, as well as setting the
author and committer separately in git/import.

--- a/sys/lib/git/common.rc
+++ b/sys/lib/git/common.rc
@@ -38,7 +38,22 @@
 		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=glenda
+	if(~ $email '')
+		email=glenda@9front.local
+}
+
+# merge1 out ours base theirs
 fn merge1 {@{
 	rfork e
 	n=$pid
--- a/sys/src/cmd/git/commit
+++ b/sys/src/cmd/git/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=glenda@9front.local
-}
-
 fn findbranch{
 	branch=`{git/branch}
 	if(test -e $gitfs/branch/$branch/tree){
@@ -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
 }
 
--- a/sys/src/cmd/git/fs.c
+++ b/sys/src/cmd/git/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,
@@ -284,23 +285,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;
@@ -491,18 +492,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);
@@ -640,14 +643,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 +764,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 +793,8 @@
 			objread(r, aux);
 		break;
 	case Qcommit:
-	case Qcommittree:
-	case Qcommitdata:
+	case Qtree:
+	case Qcdata:
 		objread(r, aux);
 		break;
 	default:
--- a/sys/src/cmd/git/import
+++ b/sys/src/cmd/git/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)
@@ -45,7 +47,7 @@
 		state="body"
 		next
 	}
-	(state=="headers" || state=="body") && (/^diff / || /^--- /){
+	(state=="headers" || state=="body") && (/^diff / || /^---( |$)/){
 		state="diff"
 	}
 	state=="body" {
@@ -57,10 +59,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
@@ -79,8 +81,8 @@
 		}
 		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
 		}
 		status=''''
 	'
--- a/sys/src/cmd/git/log.c
+++ b/sys/src/cmd/git/log.c
@@ -153,6 +153,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, "Commiter:\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;
--- a/sys/src/cmd/git/merge
+++ b/sys/src/cmd/git/merge
@@ -12,7 +12,7 @@
 		ours=$ourbr/$f
 		base=$basebr/$f
 		theirs=$theirbr/$f
-		merge1 ./$f $theirs $base $ours
+		merge1 ./$f $ours $base $theirs
 	}
 }
 
--- a/sys/src/cmd/git/save.c
+++ b/sys/src/cmd/git/save.c
@@ -14,6 +14,14 @@
 	Maxparents = 16,
 };
 
+char	*authorname;
+char	*authoremail;
+char	*committername;
+char	*committeremail;
+char	*commitmsg;
+Hash	parents[Maxparents];
+int	nparents;
+
 int
 gitmode(Dirent *e)
 {
@@ -299,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;
@@ -309,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);
@@ -346,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;
 
@@ -357,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");
@@ -378,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)
@@ -405,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);
 }