@@ -49,7 +49,7 @@ static const char * const builtin_commit_usage[] = {
4949 " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>]\n"
5050 " [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
5151 " [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
52- " [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
52+ " [--committer=<committer>] [-- date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
5353 " [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
5454 " [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
5555 " [--] [<pathspec>...]" ),
@@ -112,6 +112,7 @@ static enum {
112112} commit_style ;
113113
114114static const char * force_author ;
115+ static const char * force_committer ;
115116static char * logfile ;
116117static char * template_file ;
117118/*
@@ -630,46 +631,48 @@ static void set_ident_var(char **buf, char *val)
630631 * buf = val ;
631632}
632633
633- static void determine_author_info ( struct strbuf * author_ident )
634- {
635- char * name , * email , * date ;
636- struct ident_split author ;
634+ static void set_author_from_message ( char * * name , char * * email , char * * date ) {
635+ struct ident_split ident ;
636+ size_t len ;
637+ const char * a ;
637638
638- name = xstrdup_or_null (getenv ("GIT_AUTHOR_NAME" ));
639- email = xstrdup_or_null (getenv ("GIT_AUTHOR_EMAIL" ));
640- date = xstrdup_or_null (getenv ("GIT_AUTHOR_DATE" ));
639+ if (!author_message )
640+ return ;
641641
642- if (author_message ) {
643- struct ident_split ident ;
644- size_t len ;
645- const char * a ;
646-
647- a = find_commit_header (author_message_buffer , "author" , & len );
648- if (!a )
649- die (_ ("commit '%s' lacks author header" ), author_message );
650- if (split_ident_line (& ident , a , len ) < 0 )
651- die (_ ("commit '%s' has malformed author line" ), author_message );
652-
653- set_ident_var (& name , xmemdupz (ident .name_begin , ident .name_end - ident .name_begin ));
654- set_ident_var (& email , xmemdupz (ident .mail_begin , ident .mail_end - ident .mail_begin ));
655-
656- if (ident .date_begin ) {
657- struct strbuf date_buf = STRBUF_INIT ;
658- strbuf_addch (& date_buf , '@' );
659- strbuf_add (& date_buf , ident .date_begin , ident .date_end - ident .date_begin );
660- strbuf_addch (& date_buf , ' ' );
661- strbuf_add (& date_buf , ident .tz_begin , ident .tz_end - ident .tz_begin );
662- set_ident_var (& date , strbuf_detach (& date_buf , NULL ));
663- }
642+ a = find_commit_header (author_message_buffer , "author" , & len );
643+ if (!a )
644+ die (_ ("commit '%s' lacks author header" ), author_message );
645+ if (split_ident_line (& ident , a , len ) < 0 )
646+ die (_ ("commit '%s' has malformed author line" ), author_message );
647+
648+ set_ident_var (name , xmemdupz (ident .name_begin , ident .name_end - ident .name_begin ));
649+ set_ident_var (email , xmemdupz (ident .mail_begin , ident .mail_end - ident .mail_begin ));
650+
651+ if (ident .date_begin ) {
652+ struct strbuf date_buf = STRBUF_INIT ;
653+ strbuf_addch (& date_buf , '@' );
654+ strbuf_add (& date_buf , ident .date_begin , ident .date_end - ident .date_begin );
655+ strbuf_addch (& date_buf , ' ' );
656+ strbuf_add (& date_buf , ident .tz_begin , ident .tz_end - ident .tz_begin );
657+ set_ident_var (date , strbuf_detach (& date_buf , NULL ));
664658 }
659+ }
665660
666- if (force_author ) {
667- struct ident_split ident ;
661+ static void determine_identity (struct strbuf * ident_str , enum want_ident whose_ident ,
662+ const char * env_name , const char * env_email , const char * env_date ,
663+ const char * force_ident , const char * param_name ,
664+ char * name , char * email , char * date )
665+ {
666+ struct ident_split ident ;
667+
668+
669+ if (force_ident ) {
670+ struct ident_split force_ident_split ;
668671
669- if (split_ident_line (& ident , force_author , strlen (force_author )) < 0 )
670- die (_ ("malformed --author parameter" ));
671- set_ident_var (& name , xmemdupz (ident .name_begin , ident .name_end - ident .name_begin ));
672- set_ident_var (& email , xmemdupz (ident .mail_begin , ident .mail_end - ident .mail_begin ));
672+ if (split_ident_line (& force_ident_split , force_ident , strlen (force_ident )) < 0 )
673+ die (_ ("malformed %s parameter" ), param_name );
674+ set_ident_var (& name , xmemdupz (force_ident_split .name_begin , force_ident_split .name_end - force_ident_split .name_begin ));
675+ set_ident_var (& email , xmemdupz (force_ident_split .mail_begin , force_ident_split .mail_end - force_ident_split .mail_begin ));
673676 }
674677
675678 if (force_date ) {
@@ -679,17 +682,49 @@ static void determine_author_info(struct strbuf *author_ident)
679682 set_ident_var (& date , strbuf_detach (& date_buf , NULL ));
680683 }
681684
682- strbuf_addstr (author_ident , fmt_ident (name , email , WANT_AUTHOR_IDENT , date ,
685+ strbuf_addstr (ident_str , fmt_ident (name , email , whose_ident , date ,
683686 IDENT_STRICT ));
684- assert_split_ident (& author , author_ident );
685- export_one ("GIT_AUTHOR_NAME" , author .name_begin , author .name_end , 0 );
686- export_one ("GIT_AUTHOR_EMAIL" , author .mail_begin , author .mail_end , 0 );
687- export_one ("GIT_AUTHOR_DATE" , author .date_begin , author .tz_end , '@' );
687+ assert_split_ident (& ident , ident_str );
688+
689+ export_one (env_name , ident .name_begin , ident .name_end , 0 );
690+ export_one (env_email , ident .mail_begin , ident .mail_end , 0 );
691+ export_one (env_date , ident .date_begin , ident .tz_end , '@' );
692+
688693 free (name );
689694 free (email );
690695 free (date );
691696}
692697
698+ static void determine_author_info (struct strbuf * author_ident )
699+ {
700+ char * name , * email , * date ;
701+
702+ name = xstrdup_or_null (getenv ("GIT_AUTHOR_NAME" ));
703+ email = xstrdup_or_null (getenv ("GIT_AUTHOR_EMAIL" ));
704+ date = xstrdup_or_null (getenv ("GIT_AUTHOR_DATE" ));
705+
706+ set_author_from_message (& name , & email , & date );
707+
708+ determine_identity (author_ident , WANT_AUTHOR_IDENT ,
709+ "GIT_AUTHOR_NAME" , "GIT_AUTHOR_EMAIL" , "GIT_AUTHOR_DATE" ,
710+ force_author , "--author" ,
711+ name , email , date );
712+ }
713+
714+ static void determine_committer_info (struct strbuf * committer_ident )
715+ {
716+ char * name , * email , * date ;
717+
718+ name = xstrdup_or_null (getenv ("GIT_COMMITTER_NAME" ));
719+ email = xstrdup_or_null (getenv ("GIT_COMMITTER_EMAIL" ));
720+ date = xstrdup_or_null (getenv ("GIT_COMMITTER_DATE" ));
721+
722+ determine_identity (committer_ident , WANT_COMMITTER_IDENT ,
723+ "GIT_COMMITTER_NAME" , "GIT_COMMITTER_EMAIL" , "GIT_COMMITTER_DATE" ,
724+ force_committer , "--committer" ,
725+ name , email , date );
726+ }
727+
693728static int author_date_is_interesting (void )
694729{
695730 return author_message || force_date ;
@@ -1137,16 +1172,23 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
11371172 return 1 ;
11381173}
11391174
1140- static const char * find_author_by_nickname (const char * name )
1175+ static const char * find_identity_by_nickname (const char * name , enum want_ident whose_ident )
11411176{
11421177 struct rev_info revs ;
11431178 struct commit * commit ;
11441179 struct strbuf buf = STRBUF_INIT ;
11451180 const char * av [20 ];
11461181 int ac = 0 ;
1182+ const char * field , * format ;
1183+
1184+ if (whose_ident != WANT_AUTHOR_IDENT && whose_ident != WANT_COMMITTER_IDENT )
1185+ BUG ("find_identity_by_nickname requires WANT_AUTHOR_IDENT or WANT_COMMITTER_IDENT" );
1186+
1187+ field = whose_ident == WANT_AUTHOR_IDENT ? "author" : "committer" ;
1188+ format = whose_ident == WANT_AUTHOR_IDENT ? "%aN <%aE>" : "%cN <%cE>" ;
11471189
11481190 repo_init_revisions (the_repository , & revs , NULL );
1149- strbuf_addf (& buf , "--author =%s" , name );
1191+ strbuf_addf (& buf , "--%s =%s" , field , name );
11501192 av [++ ac ] = "--all" ;
11511193 av [++ ac ] = "-i" ;
11521194 av [++ ac ] = buf .buf ;
@@ -1164,11 +1206,22 @@ static const char *find_author_by_nickname(const char *name)
11641206 ctx .date_mode .type = DATE_NORMAL ;
11651207 strbuf_release (& buf );
11661208 repo_format_commit_message (the_repository , commit ,
1167- "%aN <%aE>" , & buf , & ctx );
1209+ format , & buf , & ctx );
11681210 release_revisions (& revs );
11691211 return strbuf_detach (& buf , NULL );
11701212 }
1171- die (_ ("--author '%s' is not 'Name <email>' and matches no existing author" ), name );
1213+ die (_ ("--%s '%s' is not 'Name <email>' and matches no existing %s" ),
1214+ field , name , field );
1215+ }
1216+
1217+ static const char * find_author_by_nickname (const char * name )
1218+ {
1219+ return find_identity_by_nickname (name , WANT_AUTHOR_IDENT );
1220+ }
1221+
1222+ static const char * find_committer_by_nickname (const char * name )
1223+ {
1224+ return find_identity_by_nickname (name , WANT_COMMITTER_IDENT );
11721225}
11731226
11741227static void handle_ignored_arg (struct wt_status * s )
@@ -1321,6 +1374,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
13211374 if (force_author && renew_authorship )
13221375 die (_ ("options '%s' and '%s' cannot be used together" ), "--reset-author" , "--author" );
13231376
1377+ if (force_committer && !strchr (force_committer , '>' ))
1378+ force_committer = find_committer_by_nickname (force_committer );
1379+
13241380 if (logfile || have_option_m || use_message )
13251381 use_editor = 0 ;
13261382
@@ -1709,6 +1765,7 @@ int cmd_commit(int argc,
17091765 OPT_FILENAME ('F' , "file" , & logfile , N_ ("read message from file" )),
17101766 OPT_STRING (0 , "author" , & force_author , N_ ("author" ), N_ ("override author for commit" )),
17111767 OPT_STRING (0 , "date" , & force_date , N_ ("date" ), N_ ("override date for commit" )),
1768+ OPT_STRING (0 , "committer" , & force_committer , N_ ("committer" ), N_ ("override committer for commit" )),
17121769 OPT_CALLBACK ('m' , "message" , & message , N_ ("message" ), N_ ("commit message" ), opt_parse_m ),
17131770 OPT_STRING ('c' , "reedit-message" , & edit_message , N_ ("commit" ), N_ ("reuse and edit message from specified commit" )),
17141771 OPT_STRING ('C' , "reuse-message" , & use_message , N_ ("commit" ), N_ ("reuse message from specified commit" )),
@@ -1785,6 +1842,7 @@ int cmd_commit(int argc,
17851842
17861843 struct strbuf sb = STRBUF_INIT ;
17871844 struct strbuf author_ident = STRBUF_INIT ;
1845+ struct strbuf committer_ident = STRBUF_INIT ;
17881846 const char * index_file , * reflog_msg ;
17891847 struct object_id oid ;
17901848 struct commit_list * parents = NULL ;
@@ -1930,8 +1988,12 @@ int cmd_commit(int argc,
19301988 append_merge_tag_headers (parents , & tail );
19311989 }
19321990
1991+ if (force_committer )
1992+ determine_committer_info (& committer_ident );
1993+
19331994 if (commit_tree_extended (sb .buf , sb .len , & the_repository -> index -> cache_tree -> oid ,
1934- parents , & oid , author_ident .buf , NULL ,
1995+ parents , & oid , author_ident .buf ,
1996+ force_committer ? committer_ident .buf : NULL ,
19351997 sign_commit , extra )) {
19361998 rollback_index_files ();
19371999 die (_ ("failed to write commit object" ));
@@ -1980,6 +2042,7 @@ int cmd_commit(int argc,
19802042 free_commit_extra_headers (extra );
19812043 free_commit_list (parents );
19822044 strbuf_release (& author_ident );
2045+ strbuf_release (& committer_ident );
19832046 strbuf_release (& err );
19842047 strbuf_release (& sb );
19852048 free (logfile );
0 commit comments