@@ -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,61 @@ 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+ static void determine_identity (struct strbuf * ident_str , int is_author )
634635{
635636 char * name , * email , * date ;
636- struct ident_split author ;
637-
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" ));
641-
642- if (author_message ) {
643- struct ident_split ident ;
637+ struct ident_split ident ;
638+ const char * env_name = is_author ? "GIT_AUTHOR_NAME" : "GIT_COMMITTER_NAME" ;
639+ const char * env_email = is_author ? "GIT_AUTHOR_EMAIL" : "GIT_COMMITTER_EMAIL" ;
640+ const char * env_date = is_author ? "GIT_AUTHOR_DATE" : "GIT_COMMITTER_DATE" ;
641+ const char * force_ident = is_author ? force_author : force_committer ;
642+ const char * param_name = is_author ? "--author" : "--committer" ;
643+ int ident_flag = is_author ? WANT_AUTHOR_IDENT : WANT_COMMITTER_IDENT ;
644+
645+ name = xstrdup_or_null (getenv (env_name ));
646+ email = xstrdup_or_null (getenv (env_email ));
647+ date = xstrdup_or_null (getenv (env_date ));
648+
649+ if (is_author && author_message ) {
650+ struct ident_split msg_ident ;
644651 size_t len ;
645652 const char * a ;
646653
647654 a = find_commit_header (author_message_buffer , "author" , & len );
648655 if (!a )
649656 die (_ ("commit '%s' lacks author header" ), author_message );
650- if (split_ident_line (& ident , a , len ) < 0 )
657+ if (split_ident_line (& msg_ident , a , len ) < 0 )
651658 die (_ ("commit '%s' has malformed author line" ), author_message );
652659
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 ));
660+ set_ident_var (& name , xmemdupz (msg_ident .name_begin , msg_ident .name_end - msg_ident .name_begin ));
661+ set_ident_var (& email , xmemdupz (msg_ident .mail_begin , msg_ident .mail_end - msg_ident .mail_begin ));
655662
656- if (ident .date_begin ) {
663+ if (msg_ident .date_begin ) {
657664 struct strbuf date_buf = STRBUF_INIT ;
658665 strbuf_addch (& date_buf , '@' );
659- strbuf_add (& date_buf , ident .date_begin , ident .date_end - ident .date_begin );
666+ strbuf_add (& date_buf , msg_ident .date_begin , msg_ident .date_end - msg_ident .date_begin );
660667 strbuf_addch (& date_buf , ' ' );
661- strbuf_add (& date_buf , ident .tz_begin , ident .tz_end - ident .tz_begin );
668+ strbuf_add (& date_buf , msg_ident .tz_begin , msg_ident .tz_end - msg_ident .tz_begin );
662669 set_ident_var (& date , strbuf_detach (& date_buf , NULL ));
663670 }
664671 }
665672
666- if (force_author ) {
667- struct ident_split ident ;
673+ if (force_ident ) {
674+ struct ident_split force_ident_split ;
675+
676+ if (split_ident_line (& force_ident_split , force_ident , strlen (force_ident )) < 0 )
677+ die (_ ("malformed %s parameter" ), param_name );
678+ set_ident_var (& name , xmemdupz (force_ident_split .name_begin , force_ident_split .name_end - force_ident_split .name_begin ));
679+ set_ident_var (& email , xmemdupz (force_ident_split .mail_begin , force_ident_split .mail_end - force_ident_split .mail_begin ));
668680
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 ));
681+ if (!is_author && force_ident_split .date_begin ) {
682+ struct strbuf date_buf = STRBUF_INIT ;
683+ strbuf_addch (& date_buf , '@' );
684+ strbuf_add (& date_buf , force_ident_split .date_begin , force_ident_split .date_end - force_ident_split .date_begin );
685+ strbuf_addch (& date_buf , ' ' );
686+ strbuf_add (& date_buf , force_ident_split .tz_begin , force_ident_split .tz_end - force_ident_split .tz_begin );
687+ set_ident_var (& date , strbuf_detach (& date_buf , NULL ));
688+ }
673689 }
674690
675691 if (force_date ) {
@@ -679,17 +695,35 @@ static void determine_author_info(struct strbuf *author_ident)
679695 set_ident_var (& date , strbuf_detach (& date_buf , NULL ));
680696 }
681697
682- strbuf_addstr (author_ident , fmt_ident (name , email , WANT_AUTHOR_IDENT , date ,
698+ strbuf_addstr (ident_str , fmt_ident (name , email , ident_flag , date ,
683699 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 , '@' );
700+ assert_split_ident (& ident , ident_str );
701+
702+ if (is_author ) {
703+ export_one ("GIT_AUTHOR_NAME" , ident .name_begin , ident .name_end , 0 );
704+ export_one ("GIT_AUTHOR_EMAIL" , ident .mail_begin , ident .mail_end , 0 );
705+ export_one ("GIT_AUTHOR_DATE" , ident .date_begin , ident .tz_end , '@' );
706+ } else {
707+ export_one ("GIT_COMMITTER_NAME" , ident .name_begin , ident .name_end , 0 );
708+ export_one ("GIT_COMMITTER_EMAIL" , ident .mail_begin , ident .mail_end , 0 );
709+ export_one ("GIT_COMMITTER_DATE" , ident .date_begin , ident .tz_end , '@' );
710+ }
711+
688712 free (name );
689713 free (email );
690714 free (date );
691715}
692716
717+ static void determine_author_info (struct strbuf * author_ident )
718+ {
719+ determine_identity (author_ident , 1 );
720+ }
721+
722+ static void determine_committer_info (struct strbuf * committer_ident )
723+ {
724+ determine_identity (committer_ident , 0 );
725+ }
726+
693727static int author_date_is_interesting (void )
694728{
695729 return author_message || force_date ;
@@ -1137,16 +1171,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
11371171 return 1 ;
11381172}
11391173
1140- static const char * find_author_by_nickname (const char * name )
1174+ static const char * find_identity_by_nickname (const char * name , int is_author )
11411175{
11421176 struct rev_info revs ;
11431177 struct commit * commit ;
11441178 struct strbuf buf = STRBUF_INIT ;
11451179 const char * av [20 ];
11461180 int ac = 0 ;
1181+ const char * field = is_author ? "author" : "committer" ;
1182+ const char * format = is_author ? "%aN <%aE>" : "%cN <%cE>" ;
11471183
11481184 repo_init_revisions (the_repository , & revs , NULL );
1149- strbuf_addf (& buf , "--author =%s" , name );
1185+ strbuf_addf (& buf , "--%s =%s" , field , name );
11501186 av [++ ac ] = "--all" ;
11511187 av [++ ac ] = "-i" ;
11521188 av [++ ac ] = buf .buf ;
@@ -1164,11 +1200,22 @@ static const char *find_author_by_nickname(const char *name)
11641200 ctx .date_mode .type = DATE_NORMAL ;
11651201 strbuf_release (& buf );
11661202 repo_format_commit_message (the_repository , commit ,
1167- "%aN <%aE>" , & buf , & ctx );
1203+ format , & buf , & ctx );
11681204 release_revisions (& revs );
11691205 return strbuf_detach (& buf , NULL );
11701206 }
1171- die (_ ("--author '%s' is not 'Name <email>' and matches no existing author" ), name );
1207+ die (_ ("--%s '%s' is not 'Name <email>' and matches no existing %s" ),
1208+ field , name , field );
1209+ }
1210+
1211+ static const char * find_author_by_nickname (const char * name )
1212+ {
1213+ return find_identity_by_nickname (name , 1 );
1214+ }
1215+
1216+ static const char * find_committer_by_nickname (const char * name )
1217+ {
1218+ return find_identity_by_nickname (name , 0 );
11721219}
11731220
11741221static void handle_ignored_arg (struct wt_status * s )
@@ -1321,6 +1368,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
13211368 if (force_author && renew_authorship )
13221369 die (_ ("options '%s' and '%s' cannot be used together" ), "--reset-author" , "--author" );
13231370
1371+ if (force_committer && !strchr (force_committer , '>' ))
1372+ force_committer = find_committer_by_nickname (force_committer );
1373+
13241374 if (logfile || have_option_m || use_message )
13251375 use_editor = 0 ;
13261376
@@ -1709,6 +1759,7 @@ int cmd_commit(int argc,
17091759 OPT_FILENAME ('F' , "file" , & logfile , N_ ("read message from file" )),
17101760 OPT_STRING (0 , "author" , & force_author , N_ ("author" ), N_ ("override author for commit" )),
17111761 OPT_STRING (0 , "date" , & force_date , N_ ("date" ), N_ ("override date for commit" )),
1762+ OPT_STRING (0 , "committer" , & force_committer , N_ ("committer" ), N_ ("override committer for commit" )),
17121763 OPT_CALLBACK ('m' , "message" , & message , N_ ("message" ), N_ ("commit message" ), opt_parse_m ),
17131764 OPT_STRING ('c' , "reedit-message" , & edit_message , N_ ("commit" ), N_ ("reuse and edit message from specified commit" )),
17141765 OPT_STRING ('C' , "reuse-message" , & use_message , N_ ("commit" ), N_ ("reuse message from specified commit" )),
@@ -1785,6 +1836,7 @@ int cmd_commit(int argc,
17851836
17861837 struct strbuf sb = STRBUF_INIT ;
17871838 struct strbuf author_ident = STRBUF_INIT ;
1839+ struct strbuf committer_ident = STRBUF_INIT ;
17881840 const char * index_file , * reflog_msg ;
17891841 struct object_id oid ;
17901842 struct commit_list * parents = NULL ;
@@ -1930,8 +1982,12 @@ int cmd_commit(int argc,
19301982 append_merge_tag_headers (parents , & tail );
19311983 }
19321984
1985+ if (force_committer )
1986+ determine_committer_info (& committer_ident );
1987+
19331988 if (commit_tree_extended (sb .buf , sb .len , & the_repository -> index -> cache_tree -> oid ,
1934- parents , & oid , author_ident .buf , NULL ,
1989+ parents , & oid , author_ident .buf ,
1990+ force_committer ? committer_ident .buf : NULL ,
19351991 sign_commit , extra )) {
19361992 rollback_index_files ();
19371993 die (_ ("failed to write commit object" ));
@@ -1980,6 +2036,7 @@ int cmd_commit(int argc,
19802036 free_commit_extra_headers (extra );
19812037 free_commit_list (parents );
19822038 strbuf_release (& author_ident );
2039+ strbuf_release (& committer_ident );
19832040 strbuf_release (& err );
19842041 strbuf_release (& sb );
19852042 free (logfile );
0 commit comments