-
Notifications
You must be signed in to change notification settings - Fork 157
commit: add --committer option #1997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
commit: add --committer option #1997
Conversation
d52fc56 to
05e97b4
Compare
|
/submit |
|
Submitted as pull.1997.git.1762683774166.gitgitgadget@gmail.com To fetch this version into To fetch this version to local tag |
|
On the Git mailing list, Patrick Steinhardt wrote (reply to this): On Sun, Nov 09, 2025 at 10:22:54AM +0000, ZheNing Hu via GitGitGadget wrote:
> From: ZheNing Hu <adlternative@gmail.com>
>
> Add --committer option to git-commit, allowing users to override the
> committer identity similar to how --author works. This provides a more
> convenient alternative to setting GIT_COMMITTER_* environment variables.
Yeah, I can see how that's useful.
> 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>...]
Nit: I'd move `--committer` before `--date` so that it comes directly
after `--author`.
> @@ -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.
This matches the description of `--author`.
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 0243f17d53..88e77cbaab 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -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;
A lot of the infra in this new function is shared with
`determine_author_info()`. It would be great if we could refactor it so
that the common parts are shared given that this all is quite
non-trivial.
Maybe we could have something like `determine_identity()` that contains
the common bits between both functions? It might ultimately not really
be worth it, but at least the functionality in the `force_committer`
condition feels like it should be pulled out.
> @@ -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;
>
Is it the right thing to search by author here? Shouldn't we rather be
searching by committer?
> @@ -1930,8 +1978,13 @@ int cmd_commit(int argc,
> append_merge_tag_headers(parents, &tail);
> }
>
> + if (force_committer) {
> + determine_committer_info(&committer_ident);
> + }
> +
Nit: we tend to not use braces around single-line bodies.
Thanks!
Patrick |
|
User |
05e97b4 to
4d72ff0
Compare
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Patrick Steinhardt <ps@pks.im> 于2025年11月10日周一 17:24写道:
>
> On Sun, Nov 09, 2025 at 10:22:54AM +0000, ZheNing Hu via GitGitGadget wrote:
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Add --committer option to git-commit, allowing users to override the
> > committer identity similar to how --author works. This provides a more
> > convenient alternative to setting GIT_COMMITTER_* environment variables.
>
> Yeah, I can see how that's useful.
>
I'm glad we're aligned on this.
> > 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>...]
>
> Nit: I'd move `--committer` before `--date` so that it comes directly
> after `--author`.
>
Agreed, will fix.
> > @@ -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.
>
> This matches the description of `--author`.
>
Agreed, I will fix it to use committer description.
> > diff --git a/builtin/commit.c b/builtin/commit.c
> > index 0243f17d53..88e77cbaab 100644
> > --- a/builtin/commit.c
> > +++ b/builtin/commit.c
> > @@ -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;
>
> A lot of the infra in this new function is shared with
> `determine_author_info()`. It would be great if we could refactor it so
> that the common parts are shared given that this all is quite
> non-trivial.
>
> Maybe we could have something like `determine_identity()` that contains
> the common bits between both functions? It might ultimately not really
> be worth it, but at least the functionality in the `force_committer`
> condition feels like it should be pulled out.
>
Good suggestion, I will refactor them to use `determine_identity()`,
which should be more generic, and it even caught that I missed
updating `GIT_COMMITTER_*`.
> > @@ -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;
> >
>
> Is it the right thing to search by author here? Shouldn't we rather be
> searching by committer?
>
Yes, there should also be a `find_identity_by_nickname()` or
`find_committer_by_nickname()` here.
> > @@ -1930,8 +1978,13 @@ int cmd_commit(int argc,
> > append_merge_tag_headers(parents, &tail);
> > }
> >
> > + if (force_committer) {
> > + determine_committer_info(&committer_ident);
> > + }
> > +
>
> Nit: we tend to not use braces around single-line bodies.
>
Agree.
> Thanks!
>
> Patrick |
4d72ff0 to
58e9e5c
Compare
|
On the Git mailing list, Phillip Wood wrote (reply to this): 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 |
|
/submit |
|
User |
|
Submitted as pull.1997.v2.git.1762793782815.gitgitgadget@gmail.com To fetch this version into To fetch this version to local tag |
|
On the Git mailing list, Junio C Hamano wrote (reply to this): Patrick Steinhardt <ps@pks.im> writes:
> On Sun, Nov 09, 2025 at 10:22:54AM +0000, ZheNing Hu via GitGitGadget wrote:
>> From: ZheNing Hu <adlternative@gmail.com>
>>
>> Add --committer option to git-commit, allowing users to override the
>> committer identity similar to how --author works. This provides a more
>> convenient alternative to setting GIT_COMMITTER_* environment variables.
>
> Yeah, I can see how that's useful.
Well, I don't. Naming somebody other than yourself as the author
may be something that is needed from time to time by human users,
but lying about the committer who made commits? Our tradition is to
give long rope to let users hang themselves, but we already have the
environment variable override specifically designed for scripted uses,
where there may be very legit uses of recording arbitrary committer
identity that has nothing to do with the identity the current user
who is running the Git processes usually uses. I do not think it is
a "useful" change to make it more it ergonomic to perform certain
operations that we may not want to encourage.
So I dunno.
|
|
On the Git mailing list, "brian m. carlson" wrote (reply to this): On 2025-11-10 at 16:50:04, Phillip Wood wrote:
> 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.
Someone who works on the same project under both their personal and
corporate identities. For instance, me working on the Git project.
Some open source projects also require a CLA and you have to use a
particular address to match the one that's listed on the CLA. For
example, Google requires an address with a Google account, so in the
hypothetical state where I was going to contribute to one of their
projects, I'd need to use a different committer identity with my Gmail
address.
I've also kept business logs in Git when I had a small business and I
might well need to log approving a profit distribution (with my
corporate address) and log accepting a profit distribution (with my
personal address). Those would need separate digital signatures from my
two different email addresses.
--
brian m. carlson (they/them)
Toronto, Ontario, CA |
|
User |
|
On the Git mailing list, Junio C Hamano wrote (reply to this): "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> Co-authored-by: Aone-Agent <aone-agent@alibaba-inc.com>
What is this second author and how would its presence in the author
list interact with your DCO obligation?
How did you make sure that whatever is in this patch were not copied
by the "agent" from somewhere that we cannot copy the code from
before deciding to send this patch? The "cannot copy from" may come
in different shapes, from "their code is proprietary" to "their
licensing terms are not compatible with GPLv2" to "they welcome us
borrowing but we must give credit to them", any of which we should
be careful to avoid.
|
|
On the Git mailing list, Junio C Hamano wrote (reply to this): Junio C Hamano <gitster@pobox.com> writes:
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
>> Co-authored-by: Aone-Agent <aone-agent@alibaba-inc.com>
>
> What is this second author and how would its presence in the author
> list interact with your DCO obligation?
>
> How did you make sure that whatever is in this patch were not copied
> by the "agent" from somewhere that we cannot copy the code from
> before deciding to send this patch? The "cannot copy from" may come
> in different shapes, from "their code is proprietary" to "their
> licensing terms are not compatible with GPLv2" to "they welcome us
> borrowing but we must give credit to them", any of which we should
> be careful to avoid.
Well, the last one is not something we should *avoid*. If their
licensing terms are compatible with ours but they want to be
credited, then we comply that request and credit them.
But I think readers got the idea.
Thanks. |
|
On the Git mailing list, Jeff King wrote (reply to this): On Mon, Nov 10, 2025 at 06:01:57PM +0000, brian m. carlson wrote:
> On 2025-11-10 at 16:50:04, Phillip Wood wrote:
> > 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.
>
> Someone who works on the same project under both their personal and
> corporate identities. For instance, me working on the Git project.
>
> Some open source projects also require a CLA and you have to use a
> particular address to match the one that's listed on the CLA. For
> example, Google requires an address with a Google account, so in the
> hypothetical state where I was going to contribute to one of their
> projects, I'd need to use a different committer identity with my Gmail
> address.
>
> I've also kept business logs in Git when I had a small business and I
> might well need to log approving a profit distribution (with my
> corporate address) and log accepting a profit distribution (with my
> personal address). Those would need separate digital signatures from my
> two different email addresses.
Is a "--committer" option the best solution there, though? I'd think
you'd want to set user.* in the repo-level .git/config (or using a
dir-specific include) would be less error-prone.
That doesn't help for using two identities for the same repo, but in my
experience it is easier to use two separate repositories for that to
match the organization of the work (even if you may sometimes fetch
between them).
I'm not totally opposed to the new flag, and in general I'd defer to
people who say they find a new feature useful. I'm just having a hard
time imagining a scenario where it's the best option.
-Peff |
|
On the Git mailing list, Junio C Hamano wrote (reply to this): Jeff King <peff@peff.net> writes:
> Is a "--committer" option the best solution there, though? I'd think
> you'd want to set user.* in the repo-level .git/config (or using a
> dir-specific include) would be less error-prone.
>
> That doesn't help for using two identities for the same repo, but in my
> experience it is easier to use two separate repositories for that to
> match the organization of the work (even if you may sometimes fetch
> between them).
This happens to match my experience, but my use case may be rather
skewed. The two sets of contents managed by my two clones that push
into the same repository (of course to two different branches) are
rather disjoint and they never merge into each other (in fact they
do not even share the root commit).
If you are working under two identities on the same codebase, I
would imagine that two repo arrangement may be more cumbersome than
working in a single repository and switching between the identities,
and may be preferrable as long as you are confident that you won't
commit a change under the "other" (wrong) identity. Of course, your
reflogs and notes would also follow the "then-current" committer
identity, so if I were to flip between two identities while working
on the same codebase in a single repository, I am very likely to
export GIT_{AUTHOR,COMMITTER}_* environment variables, dedicate that
shell/window to the work done under that identity, and switch the
environment variables if/when I want to switch (or have another
shell/window with the other identities exported---perhaps I'd do
that in a secondary worktree). I cannot imagine myself keep giving
--author and --committer between my two identities without mistakes.
> I'm not totally opposed to the new flag, and in general I'd defer to
> people who say they find a new feature useful. I'm just having a hard
> time imagining a scenario where it's the best option.
Same here. The "give them long enough rope" principle tells me that
this may be worth having if even only to have symmetry with existing
"--author" option, but these two are not inherently symmetric to
begin with, and I am not sure if there is a scenario in which this
new option is the best thing to use. |
|
On the Git mailing list, Patrick Steinhardt wrote (reply to this): On Mon, Nov 10, 2025 at 03:11:36PM -0500, Jeff King wrote:
> On Mon, Nov 10, 2025 at 06:01:57PM +0000, brian m. carlson wrote:
>
> > On 2025-11-10 at 16:50:04, Phillip Wood wrote:
> > > 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.
> >
> > Someone who works on the same project under both their personal and
> > corporate identities. For instance, me working on the Git project.
> >
> > Some open source projects also require a CLA and you have to use a
> > particular address to match the one that's listed on the CLA. For
> > example, Google requires an address with a Google account, so in the
> > hypothetical state where I was going to contribute to one of their
> > projects, I'd need to use a different committer identity with my Gmail
> > address.
> >
> > I've also kept business logs in Git when I had a small business and I
> > might well need to log approving a profit distribution (with my
> > corporate address) and log accepting a profit distribution (with my
> > personal address). Those would need separate digital signatures from my
> > two different email addresses.
>
> Is a "--committer" option the best solution there, though? I'd think
> you'd want to set user.* in the repo-level .git/config (or using a
> dir-specific include) would be less error-prone.
>
> That doesn't help for using two identities for the same repo, but in my
> experience it is easier to use two separate repositories for that to
> match the organization of the work (even if you may sometimes fetch
> between them).
>
> I'm not totally opposed to the new flag, and in general I'd defer to
> people who say they find a new feature useful. I'm just having a hard
> time imagining a scenario where it's the best option.
The reason why I find it useful is mostly scripted uses. Sure, you can
already set environment variables there. But from my experience,
environment variables tend to be a significantly worse API compared to
command line options:
- They are harder to discover in the manual page.
- You don't have any "guarantees" that Git actually interprets them,
as there won't be an error if you mistype the name.
- Cause and effect may be detached with environment variables, but
with command line options that's never the case.
So I myself would prefer using "--committer" over its accompanying
environment variable any point in time when I have a scripted use case
for it.
Patrick |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Phillip Wood <phillip.wood123@gmail.com> 于2025年11月11日周二 00:50写道:
>
> 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.
>
I frequently need to distinguish between different user.name and user.email
configurations on our company's internal GitHub.
The current problems are:
When I misconfigure (which happens occasionally), git commit --author only fixes
the author part, I still need to additionally set GIT_COMMITTER_NAME and
GIT_COMMITTER_EMAIL environment variables to fix the committer information
These environment variables are painful to use, requiring manual setup
every time
If a --committer option could be provided to align with --author, users wouldn't
need to remember and use these additional environment variables.
This would greatly simplify the workflow and reduce cognitive overhead.
> 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
> |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Junio C Hamano <gitster@pobox.com> 于2025年11月11日周二 01:38写道:
>
> Patrick Steinhardt <ps@pks.im> writes:
>
> > On Sun, Nov 09, 2025 at 10:22:54AM +0000, ZheNing Hu via GitGitGadget wrote:
> >> From: ZheNing Hu <adlternative@gmail.com>
> >>
> >> Add --committer option to git-commit, allowing users to override the
> >> committer identity similar to how --author works. This provides a more
> >> convenient alternative to setting GIT_COMMITTER_* environment variables.
> >
> > Yeah, I can see how that's useful.
>
> Well, I don't. Naming somebody other than yourself as the author
> may be something that is needed from time to time by human users,
> but lying about the committer who made commits? Our tradition is to
> give long rope to let users hang themselves, but we already have the
> environment variable override specifically designed for scripted uses,
> where there may be very legit uses of recording arbitrary committer
> identity that has nothing to do with the identity the current user
> who is running the Git processes usually uses. I do not think it is
> a "useful" change to make it more it ergonomic to perform certain
> operations that we may not want to encourage.
>
> So I dunno.
>
I understand your concern about not wanting to encourage users
to misrepresent their committer identity. However, I'd like to point out
that if someone truly wants to "lie" about the committer identity, there's
nothing stopping them today. They can already do this through multiple
methods.
My primary motivation for this patch is not to make it easier to
falsify identity,
but rather to help legitimate users who work across different repositories
with different identities. These users occasionally need to fix accidentally
misconfigured committer information, and the current methods
(setting environment variables or editing config files) can be cumbersome
for this use case.
You can see from [1] how many people have struggled with modifying
commits committer.
>
[1]: https://stackoverflow.com/questions/750172/how-do-i-change-the-author-and-committer-name-email-for-multiple-commits?page=1&tab=scoredesc#tab-top |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Junio C Hamano <gitster@pobox.com> 于2025年11月11日周二 03:22写道:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > Co-authored-by: Aone-Agent <aone-agent@alibaba-inc.com>
>
> What is this second author and how would its presence in the author
> list interact with your DCO obligation?
>
> How did you make sure that whatever is in this patch were not copied
> by the "agent" from somewhere that we cannot copy the code from
> before deciding to send this patch? The "cannot copy from" may come
> in different shapes, from "their code is proprietary" to "their
> licensing terms are not compatible with GPLv2" to "they welcome us
> borrowing but we must give credit to them", any of which we should
> be careful to avoid.
>
>
This was automatically added by some code assistance tools.
I indeed forgot to consider its impact on the open source license.
I'll remove it right away. |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Jeff King <peff@peff.net> 于2025年11月11日周二 04:11写道:
>
> On Mon, Nov 10, 2025 at 06:01:57PM +0000, brian m. carlson wrote:
>
> > On 2025-11-10 at 16:50:04, Phillip Wood wrote:
> > > 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.
> >
> > Someone who works on the same project under both their personal and
> > corporate identities. For instance, me working on the Git project.
> >
> > Some open source projects also require a CLA and you have to use a
> > particular address to match the one that's listed on the CLA. For
> > example, Google requires an address with a Google account, so in the
> > hypothetical state where I was going to contribute to one of their
> > projects, I'd need to use a different committer identity with my Gmail
> > address.
> >
> > I've also kept business logs in Git when I had a small business and I
> > might well need to log approving a profit distribution (with my
> > corporate address) and log accepting a profit distribution (with my
> > personal address). Those would need separate digital signatures from my
> > two different email addresses.
>
> Is a "--committer" option the best solution there, though? I'd think
> you'd want to set user.* in the repo-level .git/config (or using a
> dir-specific include) would be less error-prone.
>
> That doesn't help for using two identities for the same repo, but in my
> experience it is easier to use two separate repositories for that to
> match the organization of the work (even if you may sometimes fetch
> between them).
>
> I'm not totally opposed to the new flag, and in general I'd defer to
> people who say they find a new feature useful. I'm just having a hard
> time imagining a scenario where it's the best option.
>
Sometimes it's because I forgot to configure the repository-level git user
config and started development first. Only when I tried to correct the
committer did I feel the pain.
> -Peff |
Add --committer option to git-commit, allowing users to override the committer identity similar to how --author works. This provides a more convenient alternative to setting GIT_COMMITTER_* environment variables. Like --author, the --committer option supports two formats: - Explicit identity: --committer="Name <email@example.com>" - Pattern search: --committer="pattern" searches commit history for a matching committer and reuses that identity To share code with the existing --author option, this patch refactors: 1. find_author_by_nickname() into find_identity_by_nickname() which handles both author and committer searches through an is_author parameter. 2. determine_author_info() into determine_identity() which handles identity parsing and setting for both author and committer through an is_author parameter. Signed-off-by: ZheNing Hu <adlternative@gmail.com>
58e9e5c to
acf724f
Compare
|
On the Git mailing list, Phillip Wood wrote (reply to this): On 11/11/2025 13:01, ZheNing Hu wrote:
> Phillip Wood <phillip.wood123@gmail.com> 于2025年11月11日周二 00:50写道:
>> 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.
>>
> > I frequently need to distinguish between different user.name and user.email
> configurations on our company's internal GitHub.
> > The current problems are:
> > When I misconfigure (which happens occasionally), git commit --author only fixes
> the author part, I still need to additionally set GIT_COMMITTER_NAME and
> GIT_COMMITTER_EMAIL environment variables to fix the committer information
> These environment variables are painful to use, requiring manual setup
> every time
I'm afraid I don't quite follow. If you are amending existing commits to fix them up after you have corrected your configuration then they will have the correct committer automatically when you run "git commit --amend --author=..." to correct the author. If you are committing before you have realized that user.{name,email} are misconfigured then I don't see how "--committer" helps because you have not yet realized anything is wrong.
Thanks
Phillip
> If a --committer option could be provided to align with --author, users wouldn't
> need to remember and use these additional environment variables.
> This would greatly simplify the workflow and reduce cognitive overhead.
> > >> 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
>>
> |
|
On the Git mailing list, Phillip Wood wrote (reply to this): On 11/11/2025 06:54, Patrick Steinhardt wrote:
> On Mon, Nov 10, 2025 at 03:11:36PM -0500, Jeff King wrote:
>> On Mon, Nov 10, 2025 at 06:01:57PM +0000, brian m. carlson wrote:
>>
>>> On 2025-11-10 at 16:50:04, Phillip Wood wrote:
>>>> 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.
>>>
>>> Someone who works on the same project under both their personal and
>>> corporate identities. For instance, me working on the Git project.
>>>
>>> Some open source projects also require a CLA and you have to use a
>>> particular address to match the one that's listed on the CLA. For
>>> example, Google requires an address with a Google account, so in the
>>> hypothetical state where I was going to contribute to one of their
>>> projects, I'd need to use a different committer identity with my Gmail
>>> address.
>>>
>>> I've also kept business logs in Git when I had a small business and I
>>> might well need to log approving a profit distribution (with my
>>> corporate address) and log accepting a profit distribution (with my
>>> personal address). Those would need separate digital signatures from my
>>> two different email addresses.
>>
>> Is a "--committer" option the best solution there, though? I'd think
>> you'd want to set user.* in the repo-level .git/config (or using a
>> dir-specific include) would be less error-prone.
>>
>> That doesn't help for using two identities for the same repo, but in my
>> experience it is easier to use two separate repositories for that to
>> match the organization of the work (even if you may sometimes fetch
>> between them).
>>
>> I'm not totally opposed to the new flag, and in general I'd defer to
>> people who say they find a new feature useful. I'm just having a hard
>> time imagining a scenario where it's the best option.
Yes, it strikes me as very inconvenient to have to specify "--committer" each time. I'd have though you'd either want to (i) set up an alias in which case you can start your alias with "-c user.name=..." or "!GIT_COMMITTER_NAME=...", or (ii) set GIT_COMMITTER_NAME in your shell.
> The reason why I find it useful is mostly scripted uses. Sure, you can
> already set environment variables there. But from my experience,
> environment variables tend to be a significantly worse API compared to
> command line options:
> > - They are harder to discover in the manual page.
They're documented in the COMMIT INFORMATION section of the "git commit" man page, admittedly that comes after the options and examples but overriding the committer is a fairly niche requirement.
> - You don't have any "guarantees" that Git actually interprets them,
> as there won't be an error if you mistype the name.
Playing devil's advocate even if you use "--committer" you still need to check the result to make sure there were no typo's in the committer info just as you would if you were setting GIT_COMMITTER_NAME.
> - Cause and effect may be detached with environment variables, but
> with command line options that's never the case.
> > So I myself would prefer using "--committer" over its accompanying
> environment variable any point in time when I have a scripted use case
> for it.
I'm wary of cluttering the UI of one of our core porcelain command with options for use with scripting.
Thanks
Phillip |
|
On the Git mailing list, Junio C Hamano wrote (reply to this): ZheNing Hu <adlternative@gmail.com> writes:
> Junio C Hamano <gitster@pobox.com> 于2025年11月11日周二 03:22写道:
>>
>> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>>
>> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
>> > Co-authored-by: Aone-Agent <aone-agent@alibaba-inc.com>
>>
>> What is this second author and how would its presence in the author
>> list interact with your DCO obligation?
>>
>> How did you make sure that whatever is in this patch were not copied
>> by the "agent" from somewhere that we cannot copy the code from
>> before deciding to send this patch? The "cannot copy from" may come
>> in different shapes, from "their code is proprietary" to "their
>> licensing terms are not compatible with GPLv2" to "they welcome us
>> borrowing but we must give credit to them", any of which we should
>> be careful to avoid.
>>
>>
>
> This was automatically added by some code assistance tools.
> I indeed forgot to consider its impact on the open source license.
> I'll remove it right away.
Please don't silently remove it without answering the question you
were asked. "The tool adds it but I disabled the agentic features
of the tool and everything readers see in the submitted patch was
what I typed, with no agent input" would be a perfect answer. "I
did not consider the ramifications of use of the agentic tool, and I
do not know where the code the tool added for me came from, so I
cannot be sure I can contribute this patch to the project" would be
a sad but may be an honest answer. "I cannot tell the origin but I
can remove the line and claim I wrote everything myself" is not an
answer that we want to see. I am sure that your answer would fall
within the spectrum, and am hoping it would be the earlier, perfect
one, or one close to it, but we need to hear it.
Thanks. |
|
On the Git mailing list, Jeff King wrote (reply to this): On Tue, Nov 11, 2025 at 09:42:38PM +0800, ZheNing Hu wrote:
> > Is a "--committer" option the best solution there, though? I'd think
> > you'd want to set user.* in the repo-level .git/config (or using a
> > dir-specific include) would be less error-prone.
> >
> > That doesn't help for using two identities for the same repo, but in my
> > experience it is easier to use two separate repositories for that to
> > match the organization of the work (even if you may sometimes fetch
> > between them).
> >
> > I'm not totally opposed to the new flag, and in general I'd defer to
> > people who say they find a new feature useful. I'm just having a hard
> > time imagining a scenario where it's the best option.
>
> Sometimes it's because I forgot to configure the repository-level git user
> config and started development first. Only when I tried to correct the
> committer did I feel the pain.
OK, this workflow does make sense to me. Fixing up an earlier mistake is
inherently a one-off thing, and a command-line option is more ergonomic
than using the environment variables.
Two small thoughts:
- I suspect what you'd usually want there is for the committer and the
author to match. We have --committer-date-is-author-date for rebase,
and conceptually I think something like --committer-is-author would
do what you want here. But obviously it's less flexible, and I don't
know if it's that much easier to use.
- Because it's easy to make such mistakes, when you override the
author (so that it doesn't match the committer), git-commit prints
an extra "Author:" line in the output to make that more obvious.
Should we do the same with committer when you've overridden it? We
already do print "Committer:" when the ident was guessed from system
info, but I wonder if it would make sense to print when it was
forced. I dunno. I guess the time you most need the hint is when you
meant to use --committer and --author together, but only used
--author. But I don't know how Git would infer that case (versus the
normal case of you applying someone else's work and crediting them
with --author).
I'm not sure if either is a useful direction, but they seemed
sufficiently not-dumb for me to at least type them out. ;)
-Peff |
|
On the Git mailing list, Junio C Hamano wrote (reply to this): Jeff King <peff@peff.net> writes:
>> Sometimes it's because I forgot to configure the repository-level git user
>> config and started development first. Only when I tried to correct the
>> committer did I feel the pain.
>
> OK, this workflow does make sense to me. Fixing up an earlier mistake is
> inherently a one-off thing, and a command-line option is more ergonomic
> than using the environment variables.
Not very much, at least to me. Fixing up an earlier mistake may be
one-shot thing but it is to correct multiple commits in one go,
which would be error prone if you do so with "git commit --option".
Either "fast-export | fast-import" pipe, or "git rebase" (which this
patch does not give --committer option, but it already knows how to
honor existing environment variables) would be used for that, no?
> Two small thoughts:
>
> - I suspect what you'd usually want there is for the committer and the
> author to match. We have --committer-date-is-author-date for rebase,
> and conceptually I think something like --committer-is-author would
> do what you want here. But obviously it's less flexible, and I don't
> know if it's that much easier to use.
I am not sure how the user experience of this would look like.
> - Because it's easy to make such mistakes, when you override the
> author (so that it doesn't match the committer), git-commit prints
> an extra "Author:" line in the output to make that more obvious.
> Should we do the same with committer when you've overridden it? We
> already do print "Committer:" when the ident was guessed from system
> info, but I wonder if it would make sense to print when it was
> forced. I dunno. I guess the time you most need the hint is when you
> meant to use --committer and --author together, but only used
> --author. But I don't know how Git would infer that case (versus the
> normal case of you applying someone else's work and crediting them
> with --author).
Yup, guessing based on what you did _not_ give is always a hard task
for any tool ;-).
> I'm not sure if either is a useful direction, but they seemed
> sufficiently not-dumb for me to at least type them out. ;)
>
> -Peff |
|
On the Git mailing list, Jeff King wrote (reply to this): On Tue, Nov 11, 2025 at 12:16:40PM -0800, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
> >> Sometimes it's because I forgot to configure the repository-level git user
> >> config and started development first. Only when I tried to correct the
> >> committer did I feel the pain.
> >
> > OK, this workflow does make sense to me. Fixing up an earlier mistake is
> > inherently a one-off thing, and a command-line option is more ergonomic
> > than using the environment variables.
>
> Not very much, at least to me. Fixing up an earlier mistake may be
> one-shot thing but it is to correct multiple commits in one go,
> which would be error prone if you do so with "git commit --option".
> Either "fast-export | fast-import" pipe, or "git rebase" (which this
> patch does not give --committer option, but it already knows how to
> honor existing environment variables) would be used for that, no?
I usually lean on "commit --amend" for this, coupled with rebase if
there are multiple commits. So I've used:
git rebase -x "git commit --no-edit --amend --author=..."
or similar when fixing up incorrect application of somebody else's
patches (e.g., if I ended up using "git apply" and tweaking the commit
message myself, rather than using "git am").
> > Two small thoughts:
> >
> > - I suspect what you'd usually want there is for the committer and the
> > author to match. We have --committer-date-is-author-date for rebase,
> > and conceptually I think something like --committer-is-author would
> > do what you want here. But obviously it's less flexible, and I don't
> > know if it's that much easier to use.
>
> I am not sure how the user experience of this would look like.
I just mean being able to do:
git commit --amend --author='Foo Bar <foo@example.com>' --committer-is-author
instead of:
git commit --amend --author='Foo Bar <foo@example.com>' --committer='Foo Bar <foo@example.com>'
-Peff |
|
On the Git mailing list, Junio C Hamano wrote (reply to this): Jeff King <peff@peff.net> writes:
> I just mean being able to do:
>
> git commit --amend --author='Foo Bar <foo@example.com>' --committer-is-author
>
> instead of:
>
> git commit --amend --author='Foo Bar <foo@example.com>' --committer='Foo Bar <foo@example.com>'
Ah, I see. Like
git -c user.name='Foo Bar' -c user.email=foo@example.com commit --amend
Makes me wonder if we want user.ident that covers them both ;-) |
|
On the Git mailing list, Jeff King wrote (reply to this): On Tue, Nov 11, 2025 at 01:58:21PM -0800, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
> > I just mean being able to do:
> >
> > git commit --amend --author='Foo Bar <foo@example.com>' --committer-is-author
> >
> > instead of:
> >
> > git commit --amend --author='Foo Bar <foo@example.com>' --committer='Foo Bar <foo@example.com>'
>
> Ah, I see. Like
>
> git -c user.name='Foo Bar' -c user.email=foo@example.com commit --amend
>
> Makes me wonder if we want user.ident that covers them both ;-)
Hmm, I hadn't thought to use "-c" config for this. That makes me
question the utility of --committer a little bit. ;) I guess it is
slightly more convenient than "-c" in that it will trigger the
find_author_by_nickname() magic.
-Peff |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Phillip Wood <phillip.wood123@gmail.com> 于2025年11月11日周二 22:38写道:
>
> On 11/11/2025 13:01, ZheNing Hu wrote:
> > Phillip Wood <phillip.wood123@gmail.com> 于2025年11月11日周二 00:50写道:
> >> 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.
> >>
> >
> > I frequently need to distinguish between different user.name and user.email
> > configurations on our company's internal GitHub.
> >
> > The current problems are:
> >
> > When I misconfigure (which happens occasionally), git commit --author only fixes
> > the author part, I still need to additionally set GIT_COMMITTER_NAME and
> > GIT_COMMITTER_EMAIL environment variables to fix the committer information
> > These environment variables are painful to use, requiring manual setup
> > every time
>
> I'm afraid I don't quite follow. If you are amending existing commits to
> fix them up after you have corrected your configuration then they will
> have the correct committer automatically when you run "git commit
> --amend --author=..." to correct the author. If you are committing
> before you have realized that user.{name,email} are misconfigured then I
> don't see how "--committer" helps because you have not yet realized
> anything is wrong.
>
You're right that after realizing the misconfiguration and correcting the
repository's user.name and user.email, running `git commit --amend` will
fix the committer information, but the author remains unchanged. Users
then need an additional `git commit --amend --author=...` to fix the author,
which does work but requires an extra step.
I see your point that this becomes more cumbersome when dealing with
multiple commits. In such cases, users currently need to use something like:
```
GIT_AUTHOR_NAME="..." GIT_AUTHOR_EMAIL="..." \
GIT_COMMITTER_NAME="..." GIT_COMMITTER_EMAIL="..." \
git rebase -f <target>
```
This is indeed tedious and error-prone, especially when you want
to quickly fix and push commits to the platform.
`git commit --amend --author --committer` or a new `git rebase
--author --committer`
would provide a more user-friendly workflow for correcting identity
information after misconfiguration, eliminating the need to manually
set multiple
environment variables or run multiple commands.
> Thanks
>
> Phillip
>
Thanks
ZheNing Hu
> > If a --committer option could be provided to align with --author, users wouldn't
> > need to remember and use these additional environment variables.
> > This would greatly simplify the workflow and reduce cognitive overhead.
> >
> >
> >> 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
> >>
> >
> |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Phillip Wood <phillip.wood123@gmail.com> 于2025年11月11日周二 22:53写道:
>
> On 11/11/2025 06:54, Patrick Steinhardt wrote:
> > On Mon, Nov 10, 2025 at 03:11:36PM -0500, Jeff King wrote:
> >> On Mon, Nov 10, 2025 at 06:01:57PM +0000, brian m. carlson wrote:
> >>
> >>> On 2025-11-10 at 16:50:04, Phillip Wood wrote:
> >>>> 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.
> >>>
> >>> Someone who works on the same project under both their personal and
> >>> corporate identities. For instance, me working on the Git project.
> >>>
> >>> Some open source projects also require a CLA and you have to use a
> >>> particular address to match the one that's listed on the CLA. For
> >>> example, Google requires an address with a Google account, so in the
> >>> hypothetical state where I was going to contribute to one of their
> >>> projects, I'd need to use a different committer identity with my Gmail
> >>> address.
> >>>
> >>> I've also kept business logs in Git when I had a small business and I
> >>> might well need to log approving a profit distribution (with my
> >>> corporate address) and log accepting a profit distribution (with my
> >>> personal address). Those would need separate digital signatures from my
> >>> two different email addresses.
> >>
> >> Is a "--committer" option the best solution there, though? I'd think
> >> you'd want to set user.* in the repo-level .git/config (or using a
> >> dir-specific include) would be less error-prone.
> >>
> >> That doesn't help for using two identities for the same repo, but in my
> >> experience it is easier to use two separate repositories for that to
> >> match the organization of the work (even if you may sometimes fetch
> >> between them).
> >>
> >> I'm not totally opposed to the new flag, and in general I'd defer to
> >> people who say they find a new feature useful. I'm just having a hard
> >> time imagining a scenario where it's the best option.
>
> Yes, it strikes me as very inconvenient to have to specify "--committer"
> each time. I'd have though you'd either want to (i) set up an alias in
> which case you can start your alias with "-c user.name=..." or
> "!GIT_COMMITTER_NAME=...", or (ii) set GIT_COMMITTER_NAME in your shell.
Since modifying the committer is a low-frequency operation, there's
no real need to specifically configure an alias for it.
> > The reason why I find it useful is mostly scripted uses. Sure, you can
> > already set environment variables there. But from my experience,
> > environment variables tend to be a significantly worse API compared to
> > command line options:
> >
> > - They are harder to discover in the manual page.
>
> They're documented in the COMMIT INFORMATION section of the "git commit"
> man page, admittedly that comes after the options and examples but
> overriding the committer is a fairly niche requirement.
>
Although it's niche or infrequent, when configuration errors do occur,
the current fix process is quite painful for users. Previously, countless
users have gone through great trouble seeking solutions because they
didn't know how to resolve incorrect user.name and email configurations,
which prevented them from pushing code to the company's servers.
This may also suggest that Git lacks simpler ways to fix their identity.
In fact, many users end up needing to use more complex tools like git
filter-repo to attempt fixing their commits, whereas git commit --committer
and --author could become a relatively simple and memorable fix method.
> > - You don't have any "guarantees" that Git actually interprets them,
> > as there won't be an error if you mistype the name.
>
> Playing devil's advocate even if you use "--committer" you still need to
> check the result to make sure there were no typo's in the committer info
> just as you would if you were setting GIT_COMMITTER_NAME.
>
GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL are split
into two parts. Sometimes I even forget whether GIT_COMMITTER_EMAIL
should be "<email.example.com>" or "email.example.com", whereas
--author or --committer provides a more compact and memorable
"user <email.example.com>" pair.
> > - Cause and effect may be detached with environment variables, but
> > with command line options that's never the case.
> >
> > So I myself would prefer using "--committer" over its accompanying
> > environment variable any point in time when I have a scripted use case
> > for it.
>
> I'm wary of cluttering the UI of one of our core porcelain command with
> options for use with scripting.
>
> Thanks
>
> Phillip
Thanks
ZheNing Hu |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Junio C Hamano <gitster@pobox.com> 于2025年11月11日周二 23:40写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> > Junio C Hamano <gitster@pobox.com> 于2025年11月11日周二 03:22写道:
> >>
> >> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
> >>
> >> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> >> > Co-authored-by: Aone-Agent <aone-agent@alibaba-inc.com>
> >>
> >> What is this second author and how would its presence in the author
> >> list interact with your DCO obligation?
> >>
> >> How did you make sure that whatever is in this patch were not copied
> >> by the "agent" from somewhere that we cannot copy the code from
> >> before deciding to send this patch? The "cannot copy from" may come
> >> in different shapes, from "their code is proprietary" to "their
> >> licensing terms are not compatible with GPLv2" to "they welcome us
> >> borrowing but we must give credit to them", any of which we should
> >> be careful to avoid.
> >>
> >>
> >
> > This was automatically added by some code assistance tools.
> > I indeed forgot to consider its impact on the open source license.
> > I'll remove it right away.
>
> Please don't silently remove it without answering the question you
> were asked. "The tool adds it but I disabled the agentic features
> of the tool and everything readers see in the submitted patch was
> what I typed, with no agent input" would be a perfect answer. "I
> did not consider the ramifications of use of the agentic tool, and I
> do not know where the code the tool added for me came from, so I
> cannot be sure I can contribute this patch to the project" would be
> a sad but may be an honest answer. "I cannot tell the origin but I
> can remove the line and claim I wrote everything myself" is not an
> answer that we want to see. I am sure that your answer would fall
> within the spectrum, and am hoping it would be the earlier, perfect
> one, or one close to it, but we need to hear it.
>
Ha, I think my situation is closer to the second case, but I'm certain
that identical code cannot be found on GitHub or any other platform.
Moreover, this code is entirely modeled after Git's own code
(e.g., determine_identity is based on determine_author_info). There's
no possibility of copying code from other projects with licenses
incompatible with Git. However, your reminder is valid, especially
in this era where AI Agents are heavily infiltrating development.
Any similar open-source contributions in the future will need to
pay attention to this point.
> Thanks. |
|
/submit |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Jeff King <peff@peff.net> 于2025年11月12日周三 03:15写道:
>
> On Tue, Nov 11, 2025 at 09:42:38PM +0800, ZheNing Hu wrote:
>
> > > Is a "--committer" option the best solution there, though? I'd think
> > > you'd want to set user.* in the repo-level .git/config (or using a
> > > dir-specific include) would be less error-prone.
> > >
> > > That doesn't help for using two identities for the same repo, but in my
> > > experience it is easier to use two separate repositories for that to
> > > match the organization of the work (even if you may sometimes fetch
> > > between them).
> > >
> > > I'm not totally opposed to the new flag, and in general I'd defer to
> > > people who say they find a new feature useful. I'm just having a hard
> > > time imagining a scenario where it's the best option.
> >
> > Sometimes it's because I forgot to configure the repository-level git user
> > config and started development first. Only when I tried to correct the
> > committer did I feel the pain.
>
> OK, this workflow does make sense to me. Fixing up an earlier mistake is
> inherently a one-off thing, and a command-line option is more ergonomic
> than using the environment variables.
>
> Two small thoughts:
>
> - I suspect what you'd usually want there is for the committer and the
> author to match. We have --committer-date-is-author-date for rebase,
> and conceptually I think something like --committer-is-author would
> do what you want here. But obviously it's less flexible, and I don't
> know if it's that much easier to use.
>
Well, to be honest, I don't really like this kind of -A --BaseA option style.
On the contrary, -A -B is actually simple and easy enough for me
(or more symmetrical). However, it's true that git rebase probably
doesn't have --author and --committer options. Perhaps this is also
a pain point, or maybe something to look into for potential future
contributions.
> - Because it's easy to make such mistakes, when you override the
> author (so that it doesn't match the committer), git-commit prints
> an extra "Author:" line in the output to make that more obvious.
> Should we do the same with committer when you've overridden it? We
> already do print "Committer:" when the ident was guessed from system
> info, but I wonder if it would make sense to print when it was
> forced. I dunno. I guess the time you most need the hint is when you
> meant to use --committer and --author together, but only used
> --author. But I don't know how Git would infer that case (versus the
> normal case of you applying someone else's work and crediting them
> with --author).
>
Ah, you have a point. Perhaps the Commit should also be output,
though it might be trivial (if you hadn't reminded me, I wouldn't have even
noticed that git commit outputs Author when using --author).
> I'm not sure if either is a useful direction, but they seemed
> sufficiently not-dumb for me to at least type them out. ;)
>
> -Peff |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Junio C Hamano <gitster@pobox.com> 于2025年11月12日周三 04:16写道:
>
> Jeff King <peff@peff.net> writes:
>
> >> Sometimes it's because I forgot to configure the repository-level git user
> >> config and started development first. Only when I tried to correct the
> >> committer did I feel the pain.
> >
> > OK, this workflow does make sense to me. Fixing up an earlier mistake is
> > inherently a one-off thing, and a command-line option is more ergonomic
> > than using the environment variables.
>
> Not very much, at least to me. Fixing up an earlier mistake may be
> one-shot thing but it is to correct multiple commits in one go,
> which would be error prone if you do so with "git commit --option".
> Either "fast-export | fast-import" pipe, or "git rebase" (which this
> patch does not give --committer option, but it already knows how to
> honor existing environment variables) would be used for that, no?
>
Ha, perhaps this should be left to git rebase --committer --author
in the future. GIT_AUTHOR_* and GIT_COMMITTER_* are indeed
a bit cumbersome to use.
> > Two small thoughts:
> >
> > - I suspect what you'd usually want there is for the committer and the
> > author to match. We have --committer-date-is-author-date for rebase,
> > and conceptually I think something like --committer-is-author would
> > do what you want here. But obviously it's less flexible, and I don't
> > know if it's that much easier to use.
>
> I am not sure how the user experience of this would look like.
>
> > - Because it's easy to make such mistakes, when you override the
> > author (so that it doesn't match the committer), git-commit prints
> > an extra "Author:" line in the output to make that more obvious.
> > Should we do the same with committer when you've overridden it? We
> > already do print "Committer:" when the ident was guessed from system
> > info, but I wonder if it would make sense to print when it was
> > forced. I dunno. I guess the time you most need the hint is when you
> > meant to use --committer and --author together, but only used
> > --author. But I don't know how Git would infer that case (versus the
> > normal case of you applying someone else's work and crediting them
> > with --author).
>
> Yup, guessing based on what you did _not_ give is always a hard task
> for any tool ;-).
>
> > I'm not sure if either is a useful direction, but they seemed
> > sufficiently not-dumb for me to at least type them out. ;)
> >
> > -Peff |
|
Submitted as pull.1997.v3.git.1762966535495.gitgitgadget@gmail.com To fetch this version into To fetch this version to local tag |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Jeff King <peff@peff.net> 于2025年11月12日周三 05:33写道:
>
> On Tue, Nov 11, 2025 at 12:16:40PM -0800, Junio C Hamano wrote:
>
> > Jeff King <peff@peff.net> writes:
> >
> > >> Sometimes it's because I forgot to configure the repository-level git user
> > >> config and started development first. Only when I tried to correct the
> > >> committer did I feel the pain.
> > >
> > > OK, this workflow does make sense to me. Fixing up an earlier mistake is
> > > inherently a one-off thing, and a command-line option is more ergonomic
> > > than using the environment variables.
> >
> > Not very much, at least to me. Fixing up an earlier mistake may be
> > one-shot thing but it is to correct multiple commits in one go,
> > which would be error prone if you do so with "git commit --option".
> > Either "fast-export | fast-import" pipe, or "git rebase" (which this
> > patch does not give --committer option, but it already knows how to
> > honor existing environment variables) would be used for that, no?
>
> I usually lean on "commit --amend" for this, coupled with rebase if
> there are multiple commits. So I've used:
>
> git rebase -x "git commit --no-edit --amend --author=..."
>
> or similar when fixing up incorrect application of somebody else's
> patches (e.g., if I ended up using "git apply" and tweaking the commit
> message myself, rather than using "git am").
>
> > > Two small thoughts:
> > >
> > > - I suspect what you'd usually want there is for the committer and the
> > > author to match. We have --committer-date-is-author-date for rebase,
> > > and conceptually I think something like --committer-is-author would
> > > do what you want here. But obviously it's less flexible, and I don't
> > > know if it's that much easier to use.
> >
> > I am not sure how the user experience of this would look like.
>
> I just mean being able to do:
>
> git commit --amend --author='Foo Bar <foo@example.com>' --committer-is-author
>
> instead of:
>
> git commit --amend --author='Foo Bar <foo@example.com>' --committer='Foo Bar <foo@example.com>'
>
If I wanted an elegant parameter myself, it would definitely be
`git commit --amend --user='Foo Bar <foo@example.com>'`, instead of `-A -B-is-A`
> -Peff |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Junio C Hamano <gitster@pobox.com> 于2025年11月12日周三 05:58写道:
>
> Jeff King <peff@peff.net> writes:
>
> > I just mean being able to do:
> >
> > git commit --amend --author='Foo Bar <foo@example.com>' --committer-is-author
> >
> > instead of:
> >
> > git commit --amend --author='Foo Bar <foo@example.com>' --committer='Foo Bar <foo@example.com>'
>
> Ah, I see. Like
>
> git -c user.name='Foo Bar' -c user.email=foo@example.com commit --amend
>
You still need:
git -c user.name ='Foo Bar' -c user.email=foo@example.com commit
--amend --author
It's hard to remember and not symmetric enough.
> Makes me wonder if we want user.ident that covers them both ;-) |
|
On the Git mailing list, ZheNing Hu wrote (reply to this): Jeff King <peff@peff.net> 于2025年11月12日周三 06:23写道:
>
> On Tue, Nov 11, 2025 at 01:58:21PM -0800, Junio C Hamano wrote:
>
> > Jeff King <peff@peff.net> writes:
> >
> > > I just mean being able to do:
> > >
> > > git commit --amend --author='Foo Bar <foo@example.com>' --committer-is-author
> > >
> > > instead of:
> > >
> > > git commit --amend --author='Foo Bar <foo@example.com>' --committer='Foo Bar <foo@example.com>'
> >
> > Ah, I see. Like
> >
> > git -c user.name='Foo Bar' -c user.email=foo@example.com commit --amend
> >
> > Makes me wonder if we want user.ident that covers them both ;-)
>
> Hmm, I hadn't thought to use "-c" config for this. That makes me
> question the utility of --committer a little bit. ;) I guess it is
> slightly more convenient than "-c" in that it will trigger the
> find_author_by_nickname() magic.
>
The advantages of --committer over -c user.name -c user.email:
1. It's symmetric with --author
2. A single option is sufficient
3. As you mentioned, it can search for the committer in commit
history (although that wasn't my original intention)
> -Peff |
|
On the Git mailing list, Junio C Hamano wrote (reply to this): ZheNing Hu <adlternative@gmail.com> writes:
>> I'm afraid I don't quite follow. If you are amending existing commits to
>> fix them up after you have corrected your configuration then they will
>> have the correct committer automatically when you run "git commit
>> --amend --author=..." to correct the author. If you are committing
>> before you have realized that user.{name,email} are misconfigured then I
>> don't see how "--committer" helps because you have not yet realized
>> anything is wrong.
>>
>
> You're right that after realizing the misconfiguration and correcting the
> repository's user.name and user.email, running `git commit --amend` will
> fix the committer information, but the author remains unchanged. Users
> then need an additional `git commit --amend --author=...` to fix the author,
> which does work but requires an extra step.
Isn't it more like "You need to run with --author to correct the
authorship by amending the commit *anyway*, but while doing so, the
committer information will automatically be corrected"? As I said
earlier in a separate message, the author and the committer are not
symmetric, so having "--author" does make sense in the above picture,
while "--committer", as Phillip points out, much less.
|
|
On the Git mailing list, Junio C Hamano wrote (reply to this): "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
> +`--committer=<committer>`::
> + Override the committer for the commit. Specify an explicit committer using the
Isn't "set" or "use" more appropirate verb to use here?
We already take the committer identity from multiple plases, like
user.{name,email}, or GIT_COMMITTER_{NAME,EMAIL} configuration, and
with the patch we also take it from a command line option, with the
usual precedence order (i.e., command line trumps environment which
trumps configuration).
> -static void determine_author_info(struct strbuf *author_ident)
> +static void determine_identity(struct strbuf *ident_str, int is_author)
"is_author" does not sound grammatical for this case; if you are
giving an ident of an unknown kind to this function and supplying
another parameter to let it know which kind, "is_author" may make
sense, but not here.
As you will convert it into WANT_{AUTHOR,COMMITTER}_IDENT before
using anyway, why not let the caller use the "enum want_ident" to
tell this function what to do?
> {
> char *name, *email, *date;
> - struct ident_split author;
> -
> - name = xstrdup_or_null(getenv("GIT_AUTHOR_NAME"));
> - email = xstrdup_or_null(getenv("GIT_AUTHOR_EMAIL"));
> - date = xstrdup_or_null(getenv("GIT_AUTHOR_DATE"));
> -
> - if (author_message) {
> - struct ident_split ident;
> + struct ident_split ident;
> + const char *env_name = is_author ? "GIT_AUTHOR_NAME" : "GIT_COMMITTER_NAME";
> + const char *env_email = is_author ? "GIT_AUTHOR_EMAIL" : "GIT_COMMITTER_EMAIL";
> + const char *env_date = is_author ? "GIT_AUTHOR_DATE" : "GIT_COMMITTER_DATE";
> + const char *force_ident = is_author ? force_author : force_committer;
> + const char *param_name = is_author ? "--author" : "--committer";
> + int ident_flag = is_author ? WANT_AUTHOR_IDENT : WANT_COMMITTER_IDENT;
> +
> + name = xstrdup_or_null(getenv(env_name));
> + email = xstrdup_or_null(getenv(env_email));
> + date = xstrdup_or_null(getenv(env_date));
> +
> + if (is_author && author_message) {
> + struct ident_split msg_ident;
> size_t len;
> const char *a;
>
> a = find_commit_header(author_message_buffer, "author", &len);
> if (!a)
> die(_("commit '%s' lacks author header"), author_message);
> - if (split_ident_line(&ident, a, len) < 0)
> + if (split_ident_line(&msg_ident, a, len) < 0)
> die(_("commit '%s' has malformed author line"), author_message);
>
> - 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));
> + set_ident_var(&name, xmemdupz(msg_ident.name_begin, msg_ident.name_end - msg_ident.name_begin));
> + set_ident_var(&email, xmemdupz(msg_ident.mail_begin, msg_ident.mail_end - msg_ident.mail_begin));
>
> - if (ident.date_begin) {
> + if (msg_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_add(&date_buf, msg_ident.date_begin, msg_ident.date_end - msg_ident.date_begin);
> strbuf_addch(&date_buf, ' ');
> - strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin);
> + strbuf_add(&date_buf, msg_ident.tz_begin, msg_ident.tz_end - msg_ident.tz_begin);
> set_ident_var(&date, strbuf_detach(&date_buf, NULL));
> }
> }
The helper tries to be generic between both kinds of ident, but we
still need conditional that says "this part of the function is only
when we are looking for author", which is rather unsatisfactory.
Also why do we need this much patch noise, only because you renamed
one variable? I wonder if it would make it cleaner to move the body
of this if() {} statement into a separate helper function, leaving
only
if (whose_ident == WANT_AUTHOR_IDENT)
set_author_from_message(&name, &email, &date);
or something simple here? |
Currently, when users need to override the committer identity in git-commit,
they have to set GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL environment
variables, which can be cumbersome in scripting scenarios or when frequently
switching committer identities.
While git-commit already provides the --author option to conveniently override
the author identity, there's no equivalent --committer option for the committer
identity. This asymmetry creates an inconsistent user experience.
This patch introduces the --committer option to git-commit, providing:
The implementation follows the same pattern as the --author option, accepting
the format "Name " and properly validating the input.
cc: Junio C Hamano gitster@pobox.com
cc: Jeff King peff@peff.net
cc: Patrick Steinhardt ps@pks.im
cc: Phillip Wood phillip.wood123@gmail.com
cc: "brian m. carlson" sandals@crustytoothpaste.net