@@ -27,6 +27,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
27
27
merge_log_config = DEFAULT_MERGE_LOG_LEN ;
28
28
} else if (!strcmp (key , "merge.branchdesc" )) {
29
29
use_branch_desc = git_config_bool (key , value );
30
+ } else {
31
+ return git_default_config (key , value , cb );
30
32
}
31
33
return 0 ;
32
34
}
@@ -180,6 +182,101 @@ static void add_branch_desc(struct strbuf *out, const char *name)
180
182
strbuf_release (& desc );
181
183
}
182
184
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
+
183
280
static void shortlog (const char * name ,
184
281
struct origin_data * origin_data ,
185
282
struct commit * head ,
@@ -190,6 +287,8 @@ static void shortlog(const char *name,
190
287
struct commit * commit ;
191
288
struct object * branch ;
192
289
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 ;
193
292
int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED ;
194
293
struct strbuf sb = STRBUF_INIT ;
195
294
const unsigned char * sha1 = origin_data -> sha1 ;
@@ -199,7 +298,6 @@ static void shortlog(const char *name,
199
298
return ;
200
299
201
300
setup_revisions (0 , NULL , rev , NULL );
202
- rev -> ignore_merges = 1 ;
203
301
add_pending_object (rev , branch , name );
204
302
add_pending_object (rev , & head -> object , "^HEAD" );
205
303
head -> object .flags |= UNINTERESTING ;
@@ -208,10 +306,15 @@ static void shortlog(const char *name,
208
306
while ((commit = get_revision (rev )) != NULL ) {
209
307
struct pretty_print_context ctx = {0 };
210
308
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 );
213
312
continue ;
214
-
313
+ }
314
+ if (!count )
315
+ /* the 'tip' committer */
316
+ record_person ('c' , & committers , commit );
317
+ record_person ('a' , & authors , commit );
215
318
count ++ ;
216
319
if (subjects .nr > limit )
217
320
continue ;
@@ -226,6 +329,7 @@ static void shortlog(const char *name,
226
329
string_list_append (& subjects , strbuf_detach (& sb , NULL ));
227
330
}
228
331
332
+ add_people_info (out , & authors , & committers );
229
333
if (count > limit )
230
334
strbuf_addf (out , "\n* %s: (%d commits)\n" , name , count );
231
335
else
@@ -246,6 +350,8 @@ static void shortlog(const char *name,
246
350
rev -> commits = NULL ;
247
351
rev -> pending .nr = 0 ;
248
352
353
+ string_list_clear (& authors , 0 );
354
+ string_list_clear (& committers , 0 );
249
355
string_list_clear (& subjects , 0 );
250
356
}
251
357
0 commit comments