public inbox for git@vger.kernel.org 
 help / color / mirror / Atom feed
From: Phillip Wood <phillip.wood123@gmail•com>
To: ZheNing Hu via GitGitGadget <gitgitgadget@gmail•com>,
	git@vger•kernel.org
Cc: Junio C Hamano <gitster@pobox•com>, Jeff King <peff@peff•net>,
	ZheNing Hu <adlternative@gmail•com>
Subject: Re: [PATCH] commit: add --committer option
Date: Mon, 10 Nov 2025 16:50:04 +0000	[thread overview]
Message-ID: <6be20c41-15a0-4732-bd12-4927a59a9f59@gmail.com> (raw)
In-Reply-To: <pull.1997.git.1762683774166.gitgitgadget@gmail.com>

Hi ZheNing

On 09/11/2025 10:22, ZheNing Hu via GitGitGadget wrote:
> From: ZheNing Hu <adlternative@gmail•com>
> 
>      
>      This patch introduces the --committer option to git-commit, providing:
>      
>       1. Consistency with the existing --author option
>       2. A more convenient alternative to environment variables
>       3. Better support for automated workflows and scripts
>       4. Improved user experience when managing multiple identities

What's the use case for the same person committing under different 
identities? We already have a config mechanism to set different 
identities for different repositories but I'm struggling to see why 
someone would want to create commits under multiple identities in a 
single repository. For scripts it easy enough to set the relevant 
environment variables if a tool wants to create commits under its own 
identity.

Thanks

Phillip

>      The implementation follows the same pattern as the --author option,
>      accepting the format "Name " and properly validating the input.
> 
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1997%2Fadlternative%2Fzh%2Fimplement-committer-option-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1997/adlternative/zh/implement-committer-option-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1997
> 
>   Documentation/git-commit.adoc |  9 +++-
>   builtin/commit.c              | 58 ++++++++++++++++++++++++-
>   t/t7509-commit-authorship.sh  | 80 +++++++++++++++++++++++++++++++++++
>   3 files changed, 144 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc
> index 54c207ad45..a015c8328e 100644
> --- a/Documentation/git-commit.adoc
> +++ b/Documentation/git-commit.adoc
> @@ -12,7 +12,7 @@ git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]
>   	   [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>]
>   	   [-F <file> | -m <msg>] [--reset-author] [--allow-empty]
>   	   [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
> -	   [--date=<date>] [--cleanup=<mode>] [--[no-]status]
> +	   [--date=<date>] [--committer=<committer>] [--cleanup=<mode>] [--[no-]status]
>   	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
>   	   [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]
>   	   [--] [<pathspec>...]
> @@ -181,6 +181,13 @@ See linkgit:git-rebase[1] for details.
>   `--date=<date>`::
>   	Override the author date used in the commit.
>   
> +`--committer=<committer>`::
> +	Override the committer for the commit. Specify an explicit committer using the
> +	standard `A U Thor <committer@example•com>` format. Otherwise _<committer>_
> +	is assumed to be a pattern and is used to search for an existing
> +	commit by that author (i.e. `git rev-list --all -i --author=<committer>`);
> +	the commit author is then copied from the first such commit found.
> +
>   `-m <msg>`::
>   `--message=<msg>`::
>   	Use _<msg>_ as the commit message.
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 0243f17d53..88e77cbaab 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -49,7 +49,7 @@ static const char * const builtin_commit_usage[] = {
>   	   "           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>]\n"
>   	   "           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
>   	   "           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
> -	   "           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
> +	   "           [--date=<date>] [--committer=<committer>] [--cleanup=<mode>] [--[no-]status]\n"
>   	   "           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
>   	   "           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
>   	   "           [--] [<pathspec>...]"),
> @@ -112,6 +112,7 @@ static enum {
>   } commit_style;
>   
>   static const char *force_author;
> +static const char *force_committer;
>   static char *logfile;
>   static char *template_file;
>   /*
> @@ -690,6 +691,48 @@ static void determine_author_info(struct strbuf *author_ident)
>   	free(date);
>   }
>   
> +static void determine_committer_info(struct strbuf *committer_ident)
> +{
> +	char *name, *email, *date;
> +	struct ident_split committer;
> +
> +	name = xstrdup_or_null(getenv("GIT_COMMITTER_NAME"));
> +	email = xstrdup_or_null(getenv("GIT_COMMITTER_EMAIL"));
> +	date = xstrdup_or_null(getenv("GIT_COMMITTER_DATE"));
> +
> +	if (force_committer) {
> +		struct ident_split ident;
> +
> +		if (split_ident_line(&ident, force_committer, strlen(force_committer)) < 0)
> +			die(_("malformed --committer parameter"));
> +		set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
> +		set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
> +
> +		if (ident.date_begin) {
> +			struct strbuf date_buf = STRBUF_INIT;
> +			strbuf_addch(&date_buf, '@');
> +			strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin);
> +			strbuf_addch(&date_buf, ' ');
> +			strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin);
> +			set_ident_var(&date, strbuf_detach(&date_buf, NULL));
> +		}
> +	}
> +
> +	if (force_date) {
> +		struct strbuf date_buf = STRBUF_INIT;
> +		if (parse_force_date(force_date, &date_buf))
> +			die(_("invalid date format: %s"), force_date);
> +		set_ident_var(&date, strbuf_detach(&date_buf, NULL));
> +	}
> +
> +	strbuf_addstr(committer_ident, fmt_ident(name, email, WANT_COMMITTER_IDENT, date,
> +				IDENT_STRICT));
> +	assert_split_ident(&committer, committer_ident);
> +	free(name);
> +	free(email);
> +	free(date);
> +}
> +
>   static int author_date_is_interesting(void)
>   {
>   	return author_message || force_date;
> @@ -1321,6 +1364,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
>   	if (force_author && renew_authorship)
>   		die(_("options '%s' and '%s' cannot be used together"), "--reset-author", "--author");
>   
> +	if (force_committer && !strchr(force_committer, '>'))
> +		force_committer = find_author_by_nickname(force_committer);
> +
>   	if (logfile || have_option_m || use_message)
>   		use_editor = 0;
>   
> @@ -1709,6 +1755,7 @@ int cmd_commit(int argc,
>   		OPT_FILENAME('F', "file", &logfile, N_("read message from file")),
>   		OPT_STRING(0, "author", &force_author, N_("author"), N_("override author for commit")),
>   		OPT_STRING(0, "date", &force_date, N_("date"), N_("override date for commit")),
> +		OPT_STRING(0, "committer", &force_committer, N_("committer"), N_("override committer for commit")),
>   		OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m),
>   		OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")),
>   		OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
> @@ -1785,6 +1832,7 @@ int cmd_commit(int argc,
>   
>   	struct strbuf sb = STRBUF_INIT;
>   	struct strbuf author_ident = STRBUF_INIT;
> +	struct strbuf committer_ident = STRBUF_INIT;
>   	const char *index_file, *reflog_msg;
>   	struct object_id oid;
>   	struct commit_list *parents = NULL;
> @@ -1930,8 +1978,13 @@ int cmd_commit(int argc,
>   		append_merge_tag_headers(parents, &tail);
>   	}
>   
> +	if (force_committer) {
> +		determine_committer_info(&committer_ident);
> +	}
> +
>   	if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid,
> -				 parents, &oid, author_ident.buf, NULL,
> +				 parents, &oid, author_ident.buf,
> +				 force_committer ? committer_ident.buf : NULL,
>   				 sign_commit, extra)) {
>   		rollback_index_files();
>   		die(_("failed to write commit object"));
> @@ -1980,6 +2033,7 @@ cleanup:
>   	free_commit_extra_headers(extra);
>   	free_commit_list(parents);
>   	strbuf_release(&author_ident);
> +	strbuf_release(&committer_ident);
>   	strbuf_release(&err);
>   	strbuf_release(&sb);
>   	free(logfile);
> diff --git a/t/t7509-commit-authorship.sh b/t/t7509-commit-authorship.sh
> index 8e373b566b..45527f6a70 100755
> --- a/t/t7509-commit-authorship.sh
> +++ b/t/t7509-commit-authorship.sh
> @@ -12,6 +12,11 @@ author_header () {
>   	sed -n -e '/^$/q' -e '/^author /p'
>   }
>   
> +committer_header () {
> +	git cat-file commit "$1" |
> +	sed -n -e '/^$/q' -e '/^committer /p'
> +}
> +
>   message_body () {
>   	git cat-file commit "$1" |
>   	sed -e '1,/^$/d'
> @@ -171,4 +176,79 @@ test_expect_success '--reset-author with CHERRY_PICK_HEAD' '
>   	test_cmp expect actual
>   '
>   
> +test_expect_success '--committer option overrides committer' '
> +	git checkout Initial &&
> +	echo "Test --committer" >>foo &&
> +	test_tick &&
> +	git commit -a -m "test committer" --committer="Custom Committer <custom@committer•example>" &&
> +	committer_header HEAD >actual &&
> +	grep "Custom Committer <custom@committer•example>" actual
> +'
> +
> +test_expect_success '--committer with pattern search' '
> +	echo "Test committer pattern" >>foo &&
> +	test_tick &&
> +	git commit -a -m "test committer pattern" --committer="Frigate" &&
> +	committer_header HEAD >actual &&
> +	grep "Frigate <flying@over•world>" actual
> +'
> +
> +test_expect_success '--committer malformed parameter' '
> +	echo "Test malformed" >>foo &&
> +	test_tick &&
> +	test_must_fail git commit -a -m "test malformed" --committer="malformed committer"
> +'
> +
> +test_expect_success '--committer with --amend option' '
> +	git checkout -f Initial &&
> +	echo "Test committer with amend" >>foo &&
> +	test_tick &&
> +	git commit -a -m "initial commit for amend test" &&
> +	echo "Modified for amend" >>foo &&
> +	test_tick &&
> +	git commit -a --amend --no-edit \
> +		--author="Test Author <test@author•example>" \
> +		--committer="Test Committer <test@committer•example>" &&
> +	author_header HEAD >actual_author &&
> +	grep "Test Author <test@author•example>" actual_author &&
> +	committer_header HEAD >actual_committer &&
> +	grep "Test Committer <test@committer•example>" actual_committer
> +'
> +
> +test_expect_success 'GIT_COMMITTER_* environment variables' '
> +	git checkout -f Initial &&
> +	echo "Test env vars" >>foo &&
> +	test_tick &&
> +	GIT_COMMITTER_NAME="Env Committer" \
> +	GIT_COMMITTER_EMAIL="env@test•example" \
> +	git commit -a -m "test committer env vars" &&
> +	committer_header HEAD >actual &&
> +	grep "Env Committer <env@test•example>" actual
> +'
> +
> +test_expect_success '--committer overrides GIT_COMMITTER_* environment variables' '
> +	echo "Test override" >>foo &&
> +	test_tick &&
> +	GIT_COMMITTER_NAME="Env Committer" \
> +	GIT_COMMITTER_EMAIL="env@test•example" \
> +	git commit -a -m "test override" \
> +		--committer="Override Committer <override@test•example>" &&
> +	committer_header HEAD >actual &&
> +	grep "Override Committer <override@test•example>" actual
> +'
> +
> +test_expect_success '--date with --committer changes both author and committer dates' '
> +	git checkout -f Initial &&
> +	echo "Test date override" >>foo &&
> +	test_tick &&
> +	git commit -a -m "test date" \
> +		--author="Date Author <date@author•example>" \
> +		--committer="Date Committer <date@committer•example>" \
> +		--date="2024-06-15 10:30:00 +0800" &&
> +	git log -1 --format="%ai" >author_date &&
> +	git log -1 --format="%ci" >committer_date &&
> +	grep "2024-06-15 10:30:00 +0800" author_date &&
> +	grep "2024-06-15 10:30:00 +0800" committer_date
> +'
> +
>   test_done
> 
> base-commit: 4badef0c3503dc29059d678abba7fac0f042bc84


  parent reply	other threads:[~2025-11-10 16:50 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-09 10:22 [PATCH] commit: add --committer option ZheNing Hu via GitGitGadget
2025-11-10  9:24 ` Patrick Steinhardt
2025-11-10 14:17   ` ZheNing Hu
2025-11-10 17:38   ` Junio C Hamano
2025-11-11 13:19     ` ZheNing Hu
2025-11-10 16:50 ` Phillip Wood [this message]
2025-11-10 18:01   ` brian m. carlson
2025-11-10 20:11     ` Jeff King
2025-11-10 22:06       ` Junio C Hamano
2025-11-11  6:54       ` Patrick Steinhardt
2025-11-11 14:53         ` Phillip Wood
2025-11-12 16:11           ` ZheNing Hu
2025-11-11 13:42       ` ZheNing Hu
2025-11-11 19:15         ` Jeff King
2025-11-11 20:16           ` Junio C Hamano
2025-11-11 21:33             ` Jeff King
2025-11-11 21:58               ` Junio C Hamano
2025-11-11 22:23                 ` Jeff King
2025-11-12 16:51                   ` ZheNing Hu
2025-11-12 16:48                 ` ZheNing Hu
2025-11-12 16:46               ` ZheNing Hu
2025-11-12 16:41             ` ZheNing Hu
2025-11-12 16:37           ` ZheNing Hu
2025-11-11 13:01   ` ZheNing Hu
2025-11-11 14:38     ` Phillip Wood
2025-11-12 15:58       ` ZheNing Hu
2025-11-12 17:24         ` Junio C Hamano
2025-11-15  5:29           ` ZheNing Hu
2025-11-16  1:06             ` Junio C Hamano
2025-11-17 15:06               ` ZheNing Hu
2025-11-16 22:12         ` Matej Dujava
2025-11-17 14:27           ` Phillip Wood
2025-11-17 15:18             ` ZheNing Hu
2025-11-17 15:15           ` ZheNing Hu
2025-11-10 16:56 ` [PATCH v2] " ZheNing Hu via GitGitGadget
2025-11-10 19:22   ` Junio C Hamano
2025-11-10 19:29     ` Junio C Hamano
2025-11-11 13:36     ` ZheNing Hu
2025-11-11 15:40       ` Junio C Hamano
2025-11-12 16:23         ` ZheNing Hu
2025-11-12 16:55   ` [PATCH v3] " ZheNing Hu via GitGitGadget
2025-11-12 18:56     ` Junio C Hamano
2025-11-15  6:33       ` ZheNing Hu
2025-11-15 15:43     ` [PATCH v4] " ZheNing Hu via GitGitGadget

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6be20c41-15a0-4732-bd12-4927a59a9f59@gmail.com \
    --to=phillip.wood123@gmail$(echo .)com \
    --cc=adlternative@gmail$(echo .)com \
    --cc=git@vger$(echo .)kernel.org \
    --cc=gitgitgadget@gmail$(echo .)com \
    --cc=gitster@pobox$(echo .)com \
    --cc=peff@peff$(echo .)net \
    --cc=phillip.wood@dunelm$(echo .)org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox