@@ -27,6 +27,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
2727 merge_log_config = DEFAULT_MERGE_LOG_LEN ;
2828 } else if (!strcmp (key , "merge.branchdesc" )) {
2929 use_branch_desc = git_config_bool (key , value );
30+ } else {
31+ return git_default_config (key , value , cb );
3032 }
3133 return 0 ;
3234}
@@ -180,6 +182,101 @@ static void add_branch_desc(struct strbuf *out, const char *name)
180182 strbuf_release (& desc );
181183}
182184
185+ #define util_as_integral (elem ) ((intptr_t)((elem)->util))
186+
187+ static void record_person (int which , struct string_list * people ,
188+ struct commit * commit )
189+ {
190+ char name_buf [MAX_GITNAME ], * name , * name_end ;
191+ struct string_list_item * elem ;
192+ const char * field = (which == 'a' ) ? "\nauthor " : "\ncommitter " ;
193+
194+ name = strstr (commit -> buffer , field );
195+ if (!name )
196+ return ;
197+ name += strlen (field );
198+ name_end = strchrnul (name , '<' );
199+ if (* name_end )
200+ name_end -- ;
201+ while (isspace (* name_end ) && name <= name_end )
202+ name_end -- ;
203+ if (name_end < name || name + MAX_GITNAME <= name_end )
204+ return ;
205+ memcpy (name_buf , name , name_end - name + 1 );
206+ name_buf [name_end - name + 1 ] = '\0' ;
207+
208+ elem = string_list_lookup (people , name_buf );
209+ if (!elem ) {
210+ elem = string_list_insert (people , name_buf );
211+ elem -> util = (void * )0 ;
212+ }
213+ elem -> util = (void * )(util_as_integral (elem ) + 1 );
214+ }
215+
216+ static int cmp_string_list_util_as_integral (const void * a_ , const void * b_ )
217+ {
218+ const struct string_list_item * a = a_ , * b = b_ ;
219+ return util_as_integral (b ) - util_as_integral (a );
220+ }
221+
222+ static void add_people_count (struct strbuf * out , struct string_list * people )
223+ {
224+ if (people -> nr == 1 )
225+ strbuf_addf (out , "%s" , people -> items [0 ].string );
226+ else if (people -> nr == 2 )
227+ strbuf_addf (out , "%s (%d) and %s (%d)" ,
228+ people -> items [0 ].string ,
229+ (int )util_as_integral (& people -> items [0 ]),
230+ people -> items [1 ].string ,
231+ (int )util_as_integral (& people -> items [1 ]));
232+ else if (people -> nr )
233+ strbuf_addf (out , "%s (%d) and others" ,
234+ people -> items [0 ].string ,
235+ (int )util_as_integral (& people -> items [0 ]));
236+ }
237+
238+ static void credit_people (struct strbuf * out ,
239+ struct string_list * them ,
240+ int kind )
241+ {
242+ const char * label ;
243+ const char * me ;
244+
245+ if (kind == 'a' ) {
246+ label = "\nBy " ;
247+ me = git_author_info (IDENT_NO_DATE );
248+ } else {
249+ label = "\nvia " ;
250+ me = git_committer_info (IDENT_NO_DATE );
251+ }
252+
253+ if (!them -> nr ||
254+ (them -> nr == 1 &&
255+ me &&
256+ (me = skip_prefix (me , them -> items -> string )) != NULL &&
257+ skip_prefix (me , " <" )))
258+ return ;
259+ strbuf_addstr (out , label );
260+ add_people_count (out , them );
261+ }
262+
263+ static void add_people_info (struct strbuf * out ,
264+ struct string_list * authors ,
265+ struct string_list * committers )
266+ {
267+ if (authors -> nr )
268+ qsort (authors -> items ,
269+ authors -> nr , sizeof (authors -> items [0 ]),
270+ cmp_string_list_util_as_integral );
271+ if (committers -> nr )
272+ qsort (committers -> items ,
273+ committers -> nr , sizeof (committers -> items [0 ]),
274+ cmp_string_list_util_as_integral );
275+
276+ credit_people (out , authors , 'a' );
277+ credit_people (out , committers , 'c' );
278+ }
279+
183280static void shortlog (const char * name ,
184281 struct origin_data * origin_data ,
185282 struct commit * head ,
@@ -190,6 +287,8 @@ static void shortlog(const char *name,
190287 struct commit * commit ;
191288 struct object * branch ;
192289 struct string_list subjects = STRING_LIST_INIT_DUP ;
290+ struct string_list authors = STRING_LIST_INIT_DUP ;
291+ struct string_list committers = STRING_LIST_INIT_DUP ;
193292 int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED ;
194293 struct strbuf sb = STRBUF_INIT ;
195294 const unsigned char * sha1 = origin_data -> sha1 ;
@@ -199,7 +298,6 @@ static void shortlog(const char *name,
199298 return ;
200299
201300 setup_revisions (0 , NULL , rev , NULL );
202- rev -> ignore_merges = 1 ;
203301 add_pending_object (rev , branch , name );
204302 add_pending_object (rev , & head -> object , "^HEAD" );
205303 head -> object .flags |= UNINTERESTING ;
@@ -208,10 +306,15 @@ static void shortlog(const char *name,
208306 while ((commit = get_revision (rev )) != NULL ) {
209307 struct pretty_print_context ctx = {0 };
210308
211- /* ignore merges */
212- if (commit -> parents && commit -> parents -> next )
309+ if (commit -> parents && commit -> parents -> next ) {
310+ /* do not list a merge but count committer */
311+ record_person ('c' , & committers , commit );
213312 continue ;
214-
313+ }
314+ if (!count )
315+ /* the 'tip' committer */
316+ record_person ('c' , & committers , commit );
317+ record_person ('a' , & authors , commit );
215318 count ++ ;
216319 if (subjects .nr > limit )
217320 continue ;
@@ -226,6 +329,7 @@ static void shortlog(const char *name,
226329 string_list_append (& subjects , strbuf_detach (& sb , NULL ));
227330 }
228331
332+ add_people_info (out , & authors , & committers );
229333 if (count > limit )
230334 strbuf_addf (out , "\n* %s: (%d commits)\n" , name , count );
231335 else
@@ -246,6 +350,8 @@ static void shortlog(const char *name,
246350 rev -> commits = NULL ;
247351 rev -> pending .nr = 0 ;
248352
353+ string_list_clear (& authors , 0 );
354+ string_list_clear (& committers , 0 );
249355 string_list_clear (& subjects , 0 );
250356}
251357
0 commit comments