"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Omar Polo <op@omarpolo.com>
Subject:
draft: keeping authorship of diffs in commits
To:
gameoftrees@openbsd.org
Date:
Thu, 03 Feb 2022 13:45:49 +0100

Download raw body.

Thread
Hello,

Although it's not really used on OpenBSD (because one lists OKs and such
in the commit message), when working on personal projects I'd like to
keep the authorship of diffs I receive.

With git the usual workflow is to receive a git-formatted patch
(e.g. via git send-email) and apply it locally with git-am.  git-am is
able to extract the author of the patch from the email and use it in the
commit.  That's the "from" and "via" fields showed by tog.

Got only allows to set the author of the commit (the "from" field in
tog) but not the committer of the diff.  In the past I've set GOT_AUTHOR
before committing to the username and email of the author of the diff.
While viable, that doesn't set the committer ("via" field in tog).  OK,
it's a bit of an aesthetic reason, but I'd like to being able to keep
the authorship and still be listed as the committer of a diff.

Diff below is a draft for this.

It lacks several things (manpage bits, testing, probably more) and it
also produces the wrong error message if -A has a wrong format, but it's
just to gauge the interest and see if it's worth going forward.  It adds
an -A flag for got commit which is then used to set the author of the
commit.  The committer is still chosen the same way as always (got.conf,
GOT_AUTHOR, ...)

Cheers

Omar Polo


diff 4cd2c1745cda66c359c86bea412d77af0b643a7e /home/op/w/got
blob - 16b939dba60d2210e6c75b2ffbb51a4b1f8d608a
file + got/got.c
--- got/got.c
+++ got/got.c
@@ -597,6 +597,28 @@ import_progress(void *arg, const char *path)
 }
 
 static const struct got_error *
+validate_author(const char *author)
+{
+	/*
+	 * Really dumb email address check; we're only doing this to
+	 * avoid git's object parser breaking on commits we create.
+	 */
+	while (*author && *author != '<')
+		author++;
+	if (*author != '<')
+		return got_error(GOT_ERR_COMMIT_NO_EMAIL);
+	while (*author && *author != '@')
+		author++;
+	if (*author != '@')
+		return got_error(GOT_ERR_COMMIT_NO_EMAIL);
+	while (*author && *author != '>')
+		author++;
+	if (*author != '>')
+		return got_error(GOT_ERR_COMMIT_NO_EMAIL);
+	return NULL;
+}
+
+static const struct got_error *
 get_author(char **author, struct got_repository *repo,
     struct got_worktree *worktree)
 {
@@ -653,27 +675,7 @@ get_author(char **author, struct got_repository *repo,
 	if (*author == NULL)
 		return got_error_from_errno("strdup");
 
-	/*
-	 * Really dumb email address check; we're only doing this to
-	 * avoid git's object parser breaking on commits we create.
-	 */
-	while (*got_author && *got_author != '<')
-		got_author++;
-	if (*got_author != '<') {
-		err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
-		goto done;
-	}
-	while (*got_author && *got_author != '@')
-		got_author++;
-	if (*got_author != '@') {
-		err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
-		goto done;
-	}
-	while (*got_author && *got_author != '>')
-		got_author++;
-	if (*got_author != '>')
-		err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
-done:
+	err = validate_author(got_author);
 	if (err) {
 		free(*author);
 		*author = NULL;
@@ -7528,8 +7530,9 @@ cmd_commit(int argc, char *argv[])
 	struct got_object_id *id = NULL;
 	const char *logmsg = NULL;
 	char *prepared_logmsg = NULL;
+	const char *author = NULL;
 	struct collect_commit_logmsg_arg cl_arg;
-	char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
+	char *gitconfig_path = NULL, *editor = NULL, *committer = NULL;
 	int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0;
 	int allow_bad_symlinks = 0, non_interactive = 0, merge_in_progress = 0;
 	struct got_pathlist_head paths;
@@ -7537,8 +7540,13 @@ cmd_commit(int argc, char *argv[])
 	TAILQ_INIT(&paths);
 	cl_arg.logmsg_path = NULL;
 
-	while ((ch = getopt(argc, argv, "F:m:NS")) != -1) {
+	while ((ch = getopt(argc, argv, "A:F:m:NS")) != -1) {
 		switch (ch) {
+		case 'A':
+			author = optarg;
+			if ((error = validate_author(author)) != NULL)
+				return error;
+			break;
 		case 'F':
 			if (logmsg != NULL)
 				option_conflict('F', 'm');
@@ -7613,9 +7621,11 @@ cmd_commit(int argc, char *argv[])
 		goto done;
 	}
 
-	error = get_author(&author, repo, worktree);
+	error = get_author(&committer, repo, worktree);
 	if (error)
 		return error;
+	if (author == NULL)
+		author = committer;
 
 	/*
 	 * unveil(2) traverses exec(2); if an editor is used we have
@@ -7647,7 +7657,7 @@ cmd_commit(int argc, char *argv[])
 		cl_arg.branch_name += 11;
 	}
 	cl_arg.repo_path = got_repo_get_path(repo);
-	error = got_worktree_commit(&id, worktree, &paths, author, NULL,
+	error = got_worktree_commit(&id, worktree, &paths, author, committer,
 	    allow_bad_symlinks, collect_commit_logmsg, &cl_arg,
 	    print_status, NULL, repo);
 	if (error) {
@@ -7680,7 +7690,7 @@ done:
 	free(id_str);
 	free(gitconfig_path);
 	free(editor);
-	free(author);
+	free(committer);
 	free(prepared_logmsg);
 	return error;
 }