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.
This commit is contained in:
Ori Bernstein 2021-09-03 02:47:18 +00:00
parent 485b334608
commit d9564c0642
7 changed files with 118 additions and 82 deletions

View file

@ -38,7 +38,22 @@ fn present {
status=() 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 {@{ fn merge1 {@{
rfork e rfork e
n=$pid n=$pid

View file

@ -2,21 +2,6 @@
rfork ne rfork ne
. /sys/lib/git/common.rc . /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{ fn findbranch{
branch=`{git/branch} branch=`{git/branch}
if(test -e $gitfs/branch/$branch/tree){ if(test -e $gitfs/branch/$branch/tree){

View file

@ -12,12 +12,13 @@ enum {
Qhead, Qhead,
Qbranch, Qbranch,
Qcommit, Qcommit,
Qcommitmsg, Qmsg,
Qcommitparent, Qparent,
Qcommittree, Qtree,
Qcommitdata, Qcdata,
Qcommithash, Qhash,
Qcommitauthor, Qauthor,
Qcommitter,
Qobject, Qobject,
Qctl, Qctl,
Qmax, Qmax,
@ -284,23 +285,23 @@ gcommitgen(int i, Dir *d, void *p)
d->mode = 0755 | DMDIR; d->mode = 0755 | DMDIR;
d->name = estrdup9p("tree"); d->name = estrdup9p("tree");
d->qid.type = QTDIR; d->qid.type = QTDIR;
d->qid.path = qpath(c, i, o->id, Qcommittree); d->qid.path = qpath(c, i, o->id, Qtree);
break; break;
case 1: case 1:
d->name = estrdup9p("parent"); d->name = estrdup9p("parent");
d->qid.path = qpath(c, i, o->id, Qcommitparent); d->qid.path = qpath(c, i, o->id, Qparent);
break; break;
case 2: case 2:
d->name = estrdup9p("msg"); d->name = estrdup9p("msg");
d->qid.path = qpath(c, i, o->id, Qcommitmsg); d->qid.path = qpath(c, i, o->id, Qmsg);
break; break;
case 3: case 3:
d->name = estrdup9p("hash"); d->name = estrdup9p("hash");
d->qid.path = qpath(c, i, o->id, Qcommithash); d->qid.path = qpath(c, i, o->id, Qhash);
break; break;
case 4: case 4:
d->name = estrdup9p("author"); d->name = estrdup9p("author");
d->qid.path = qpath(c, i, o->id, Qcommitauthor); d->qid.path = qpath(c, i, o->id, Qauthor);
break; break;
default: default:
return -1; return -1;
@ -491,18 +492,20 @@ objwalk1(Qid *q, Object *o, Crumb *p, Crumb *c, char *name, vlong qdir, Gitaux *
q->type = 0; q->type = 0;
c->mtime = o->commit->mtime; c->mtime = o->commit->mtime;
c->mode = 0644; 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) 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) 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) 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) 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){ else if(strcmp(name, "tree") == 0){
q->type = QTDIR; q->type = QTDIR;
q->path = qpath(p, 4, o->id, Qcommittree); q->path = qpath(p, 4, o->id, Qtree);
unref(c->obj); unref(c->obj);
c->mode = DMDIR | 0755; c->mode = DMDIR | 0755;
c->obj = readobject(o->commit->tree); c->obj = readobject(o->commit->tree);
@ -640,14 +643,15 @@ gitwalk1(Fid *fid, char *name, Qid *q)
case Qcommit: case Qcommit:
e = objwalk1(q, o->obj, o, c, name, Qcommit, aux); e = objwalk1(q, o->obj, o, c, name, Qcommit, aux);
break; break;
case Qcommittree: case Qtree:
e = objwalk1(q, o->obj, o, c, name, Qcommittree, aux); e = objwalk1(q, o->obj, o, c, name, Qtree, aux);
break; break;
case Qcommitparent: case Qparent:
case Qcommitmsg: case Qmsg:
case Qcommitdata: case Qcdata:
case Qcommithash: case Qhash:
case Qcommitauthor: case Qauthor:
case Qcommitter:
case Qctl: case Qctl:
return Enodir; return Enodir;
default: default:
@ -760,20 +764,24 @@ gitread(Req *r)
else else
dirread9p(r, objgen, aux); dirread9p(r, objgen, aux);
break; break;
case Qcommitmsg: case Qmsg:
readbuf(r, o->commit->msg, o->commit->nmsg); readbuf(r, o->commit->msg, o->commit->nmsg);
break; break;
case Qcommitparent: case Qparent:
readcommitparent(r, o); readcommitparent(r, o);
break; break;
case Qcommithash: case Qhash:
snprint(buf, sizeof(buf), "%H\n", o->hash); snprint(buf, sizeof(buf), "%H\n", o->hash);
readstr(r, buf); readstr(r, buf);
break; break;
case Qcommitauthor: case Qauthor:
snprint(buf, sizeof(buf), "%s\n", o->commit->author); snprint(buf, sizeof(buf), "%s\n", o->commit->author);
readstr(r, buf); readstr(r, buf);
break; break;
case Qcommitter:
snprint(buf, sizeof(buf), "%s\n", o->commit->committer);
readstr(r, buf);
break;
case Qctl: case Qctl:
e = readctl(r); e = readctl(r);
break; break;
@ -785,8 +793,8 @@ gitread(Req *r)
objread(r, aux); objread(r, aux);
break; break;
case Qcommit: case Qcommit:
case Qcommittree: case Qtree:
case Qcommitdata: case Qcdata:
objread(r, aux); objread(r, aux);
break; break;
default: default:

View file

@ -7,11 +7,13 @@ fn sigexit {
rm -f $diffpath rm -f $diffpath
} }
fn apply @{ fn apply @{
git/fs git/fs
email='' amail=''
name='' aname=''
msg='' msg=''
whoami
parents='-p'^`{git/query HEAD} parents='-p'^`{git/query HEAD}
branch=`{git/branch} branch=`{git/branch}
if(test -e $gitfs/branch/$branch/tree) if(test -e $gitfs/branch/$branch/tree)
@ -26,11 +28,11 @@ fn apply @{
} }
state=="headers" && /^From:/ { state=="headers" && /^From:/ {
sub(/^From:[ \t]*/, "", $0); sub(/^From:[ \t]*/, "", $0);
name=$0; aname=$0;
email=$0; amail=$0;
sub(/[ \t]*<.*$/, "", name); sub(/[ \t]*<.*$/, "", aname);
sub(/.*</, "", email); sub(/^[^<]*</, "", amail);
sub(/>/, "", email); sub(/>[^>]*$/, "", amail);
} }
state=="headers" && /^Date:/{ state=="headers" && /^Date:/{
sub(/^Date:[ \t]*/, "", $0) sub(/^Date:[ \t]*/, "", $0)
@ -45,7 +47,7 @@ fn apply @{
state="body" state="body"
next next
} }
(state=="headers" || state=="body") && (/^diff / || /^--- /){ (state=="headers" || state=="body") && (/^diff / || /^---( |$)/){
state="diff" state="diff"
} }
state=="body" { state=="body" {
@ -57,10 +59,10 @@ fn apply @{
END{ END{
if(state != "diff") if(state != "diff")
exit("malformed patch: " state); exit("malformed patch: " state);
if(name == "" || email == "" || date == "" || gotmsg == "") if(aname == "" || amail == "" || date == "" || gotmsg == "")
exit("missing headers"); exit("missing headers");
printf "%s", name > "/env/name" printf "%s", aname > "/env/aname"
printf "%s", email > "/env/email" printf "%s", amail > "/env/amail"
printf "%s", date > "/env/date" printf "%s", date > "/env/date"
} }
' || die 'could not import:' $status ' || die 'could not import:' $status
@ -79,7 +81,7 @@ fn apply @{
} }
git/walk -fRMA $files git/walk -fRMA $files
if(~ $#nocommit 0){ if(~ $#nocommit 0){
hash=`{git/save -n $name -e $email -m $msg -d $date $parents $files} if(hash=`{git/save -n $aname -e $amail -N $name -E $email -m $msg -d $date $parents $files})
echo $hash > $refpath echo $hash > $refpath
} }
status='''' status=''''

View file

@ -153,6 +153,9 @@ show(Object *o)
tmtime(&tm, o->commit->mtime, tzload("local")); tmtime(&tm, o->commit->mtime, tzload("local"));
Bprint(out, "Hash:\t%H\n", o->hash); Bprint(out, "Hash:\t%H\n", o->hash);
Bprint(out, "Author:\t%s\n", o->commit->author); 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, "Date:\t\n", tmfmt(&tm, "WW MMM D hh:mm:ss z YYYY"));
Bprint(out, "\n"); Bprint(out, "\n");
p = o->commit->msg; p = o->commit->msg;

View file

@ -12,7 +12,7 @@ fn merge{
ours=$ourbr/$f ours=$ourbr/$f
base=$basebr/$f base=$basebr/$f
theirs=$theirbr/$f theirs=$theirbr/$f
merge1 ./$f $theirs $base $ours merge1 ./$f $ours $base $theirs
} }
} }

View file

@ -14,6 +14,14 @@ enum {
Maxparents = 16, Maxparents = 16,
}; };
char *authorname;
char *authoremail;
char *committername;
char *committeremail;
char *commitmsg;
Hash parents[Maxparents];
int nparents;
int int
gitmode(Dirent *e) gitmode(Dirent *e)
{ {
@ -299,7 +307,7 @@ err:
void 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]; char *s, h[64];
int ns, nh, i; int ns, nh, i;
@ -309,10 +317,10 @@ mkcommit(Hash *c, char *msg, char *name, char *email, vlong date, Hash *parents,
fmtprint(&f, "tree %H\n", tree); fmtprint(&f, "tree %H\n", tree);
for(i = 0; i < nparents; i++) for(i = 0; i < nparents; i++)
fmtprint(&f, "parent %H\n", parents[i]); fmtprint(&f, "parent %H\n", parents[i]);
fmtprint(&f, "author %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", name, email, date); fmtprint(&f, "committer %s <%s> %lld +0000\n", committername, committeremail, date);
fmtprint(&f, "\n"); fmtprint(&f, "\n");
fmtprint(&f, "%s", msg); fmtprint(&f, "%s", commitmsg);
s = fmtstrflush(&f); s = fmtstrflush(&f);
ns = strlen(s); ns = strlen(s);
@ -346,9 +354,9 @@ usage(void)
void void
main(int argc, char **argv) main(int argc, char **argv)
{ {
Hash th, ch, parents[Maxparents]; Hash th, ch;
char *msg, *name, *email, *dstr, cwd[1024]; char *dstr, cwd[1024];
int i, r, ncwd, nparents; int i, r, ncwd;
vlong date; vlong date;
Object *t; Object *t;
@ -357,19 +365,29 @@ main(int argc, char **argv)
sysfatal("could not find git repo: %r"); sysfatal("could not find git repo: %r");
if(getwd(cwd, sizeof(cwd)) == nil) if(getwd(cwd, sizeof(cwd)) == nil)
sysfatal("getcwd: %r"); sysfatal("getcwd: %r");
msg = nil;
name = nil;
email = nil;
dstr = nil; dstr = nil;
date = time(nil); date = time(nil);
nparents = 0;
ncwd = strlen(cwd); ncwd = strlen(cwd);
ARGBEGIN{ ARGBEGIN{
case 'm': msg = EARGF(usage()); break; case 'm':
case 'n': name = EARGF(usage()); break; commitmsg = EARGF(usage());
case 'e': email = EARGF(usage()); break; break;
case 'd': dstr = 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': case 'p':
if(nparents >= Maxparents) if(nparents >= Maxparents)
sysfatal("too many parents"); sysfatal("too many parents");
@ -378,21 +396,26 @@ main(int argc, char **argv)
break; break;
default: default:
usage(); usage();
break;
}ARGEND; }ARGEND;
if(!msg) if(commitmsg == nil)
sysfatal("missing message"); sysfatal("missing message");
if(!name) if(authorname == nil)
sysfatal("missing name"); sysfatal("missing name");
if(!email) if(authoremail == nil)
sysfatal("missing email"); sysfatal("missing email");
if((committername == nil) != (committeremail == nil))
sysfatal("partially specified committer");
if(committername == nil && committeremail == nil){
committername = authorname;
committeremail = authoremail;
}
if(dstr){ if(dstr){
date=strtoll(dstr, &dstr, 10); date=strtoll(dstr, &dstr, 10);
if(strlen(dstr) != 0) if(strlen(dstr) != 0)
sysfatal("could not parse date %s", dstr); sysfatal("could not parse date %s", dstr);
} }
if(msg == nil || name == nil)
usage();
for(i = 0; i < argc; i++){ for(i = 0; i < argc; i++){
cleanname(argv[i]); cleanname(argv[i]);
if(*argv[i] == '/' && strncmp(argv[i], cwd, ncwd) == 0) if(*argv[i] == '/' && strncmp(argv[i], cwd, ncwd) == 0)
@ -405,7 +428,7 @@ main(int argc, char **argv)
r = treeify(t, argv, argv + argc, 0, &th); r = treeify(t, argv, argv + argc, 0, &th);
if(r == -1) if(r == -1)
sysfatal("could not commit: %r\n"); sysfatal("could not commit: %r\n");
mkcommit(&ch, msg, name, email, date, parents, nparents, th); mkcommit(&ch, date, th);
print("%H\n", ch); print("%H\n", ch);
exits(nil); exits(nil);
} }