Coverage Report

Created: 2024-09-16 06:10

/src/git/fmt-merge-msg.c
Line
Count
Source (jump to first uncovered line)
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "config.h"
5
#include "environment.h"
6
#include "refs.h"
7
#include "object-name.h"
8
#include "object-store-ll.h"
9
#include "diff.h"
10
#include "diff-merges.h"
11
#include "hex.h"
12
#include "revision.h"
13
#include "tag.h"
14
#include "string-list.h"
15
#include "branch.h"
16
#include "fmt-merge-msg.h"
17
#include "commit-reach.h"
18
#include "gpg-interface.h"
19
#include "wildmatch.h"
20
21
static int use_branch_desc;
22
static int suppress_dest_pattern_seen;
23
static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
24
25
int fmt_merge_msg_config(const char *key, const char *value,
26
       const struct config_context *ctx, void *cb)
27
0
{
28
0
  if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
29
0
    int is_bool;
30
0
    merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool);
31
0
    if (!is_bool && merge_log_config < 0)
32
0
      return error("%s: negative length %s", key, value);
33
0
    if (is_bool && merge_log_config)
34
0
      merge_log_config = DEFAULT_MERGE_LOG_LEN;
35
0
  } else if (!strcmp(key, "merge.branchdesc")) {
36
0
    use_branch_desc = git_config_bool(key, value);
37
0
  } else if (!strcmp(key, "merge.suppressdest")) {
38
0
    if (!value)
39
0
      return config_error_nonbool(key);
40
0
    if (!*value)
41
0
      string_list_clear(&suppress_dest_patterns, 0);
42
0
    else
43
0
      string_list_append(&suppress_dest_patterns, value);
44
0
    suppress_dest_pattern_seen = 1;
45
0
  } else {
46
0
    return git_default_config(key, value, ctx, cb);
47
0
  }
48
0
  return 0;
49
0
}
50
51
/* merge data per repository where the merged tips came from */
52
struct src_data {
53
  struct string_list branch, tag, r_branch, generic;
54
  int head_status;
55
};
56
57
struct origin_data {
58
  struct object_id oid;
59
  unsigned is_local_branch:1;
60
};
61
62
static void init_src_data(struct src_data *data)
63
0
{
64
0
  data->branch.strdup_strings = 1;
65
0
  data->tag.strdup_strings = 1;
66
0
  data->r_branch.strdup_strings = 1;
67
0
  data->generic.strdup_strings = 1;
68
0
}
69
70
static struct string_list srcs = STRING_LIST_INIT_DUP;
71
static struct string_list origins = STRING_LIST_INIT_DUP;
72
73
struct merge_parents {
74
  int alloc, nr;
75
  struct merge_parent {
76
    struct object_id given;
77
    struct object_id commit;
78
    unsigned char used;
79
  } *item;
80
};
81
82
/*
83
 * I know, I know, this is inefficient, but you won't be pulling and merging
84
 * hundreds of heads at a time anyway.
85
 */
86
static struct merge_parent *find_merge_parent(struct merge_parents *table,
87
                struct object_id *given,
88
                struct object_id *commit)
89
0
{
90
0
  int i;
91
0
  for (i = 0; i < table->nr; i++) {
92
0
    if (given && !oideq(&table->item[i].given, given))
93
0
      continue;
94
0
    if (commit && !oideq(&table->item[i].commit, commit))
95
0
      continue;
96
0
    return &table->item[i];
97
0
  }
98
0
  return NULL;
99
0
}
100
101
static void add_merge_parent(struct merge_parents *table,
102
           struct object_id *given,
103
           struct object_id *commit)
104
0
{
105
0
  if (table->nr && find_merge_parent(table, given, commit))
106
0
    return;
107
0
  ALLOC_GROW(table->item, table->nr + 1, table->alloc);
108
0
  oidcpy(&table->item[table->nr].given, given);
109
0
  oidcpy(&table->item[table->nr].commit, commit);
110
0
  table->item[table->nr].used = 0;
111
0
  table->nr++;
112
0
}
113
114
static int handle_line(char *line, struct merge_parents *merge_parents)
115
0
{
116
0
  int i, len = strlen(line);
117
0
  struct origin_data *origin_data;
118
0
  char *src;
119
0
  const char *origin, *tag_name;
120
0
  char *to_free = NULL;
121
0
  struct src_data *src_data;
122
0
  struct string_list_item *item;
123
0
  int pulling_head = 0;
124
0
  struct object_id oid;
125
0
  const unsigned hexsz = the_hash_algo->hexsz;
126
127
0
  if (len < hexsz + 3 || line[hexsz] != '\t')
128
0
    return 1;
129
130
0
  if (starts_with(line + hexsz + 1, "not-for-merge"))
131
0
    return 0;
132
133
0
  if (line[hexsz + 1] != '\t')
134
0
    return 2;
135
136
0
  i = get_oid_hex(line, &oid);
137
0
  if (i)
138
0
    return 3;
139
140
0
  if (!find_merge_parent(merge_parents, &oid, NULL))
141
0
    return 0; /* subsumed by other parents */
142
143
0
  CALLOC_ARRAY(origin_data, 1);
144
0
  oidcpy(&origin_data->oid, &oid);
145
146
0
  if (line[len - 1] == '\n')
147
0
    line[len - 1] = 0;
148
0
  line += hexsz + 2;
149
150
  /*
151
   * At this point, line points at the beginning of comment e.g.
152
   * "branch 'frotz' of git://that/repository.git".
153
   * Find the repository name and point it with src.
154
   */
155
0
  src = strstr(line, " of ");
156
0
  if (src) {
157
0
    *src = 0;
158
0
    src += 4;
159
0
    pulling_head = 0;
160
0
  } else {
161
0
    src = line;
162
0
    pulling_head = 1;
163
0
  }
164
165
0
  item = unsorted_string_list_lookup(&srcs, src);
166
0
  if (!item) {
167
0
    item = string_list_append(&srcs, src);
168
0
    item->util = xcalloc(1, sizeof(struct src_data));
169
0
    init_src_data(item->util);
170
0
  }
171
0
  src_data = item->util;
172
173
0
  if (pulling_head) {
174
0
    origin = src;
175
0
    src_data->head_status |= 1;
176
0
  } else if (skip_prefix(line, "branch ", &origin)) {
177
0
    origin_data->is_local_branch = 1;
178
0
    string_list_append(&src_data->branch, origin);
179
0
    src_data->head_status |= 2;
180
0
  } else if (skip_prefix(line, "tag ", &tag_name)) {
181
0
    origin = line;
182
0
    string_list_append(&src_data->tag, tag_name);
183
0
    src_data->head_status |= 2;
184
0
  } else if (skip_prefix(line, "remote-tracking branch ", &origin)) {
185
0
    string_list_append(&src_data->r_branch, origin);
186
0
    src_data->head_status |= 2;
187
0
  } else {
188
0
    origin = src;
189
0
    string_list_append(&src_data->generic, line);
190
0
    src_data->head_status |= 2;
191
0
  }
192
193
0
  if (!strcmp(".", src) || !strcmp(src, origin)) {
194
0
    int len = strlen(origin);
195
0
    if (origin[0] == '\'' && origin[len - 1] == '\'')
196
0
      origin = to_free = xmemdupz(origin + 1, len - 2);
197
0
  } else
198
0
    origin = to_free = xstrfmt("%s of %s", origin, src);
199
0
  if (strcmp(".", src))
200
0
    origin_data->is_local_branch = 0;
201
0
  string_list_append(&origins, origin)->util = origin_data;
202
0
  free(to_free);
203
0
  return 0;
204
0
}
205
206
static void print_joined(const char *singular, const char *plural,
207
    struct string_list *list, struct strbuf *out)
208
0
{
209
0
  if (list->nr == 0)
210
0
    return;
211
0
  if (list->nr == 1) {
212
0
    strbuf_addf(out, "%s%s", singular, list->items[0].string);
213
0
  } else {
214
0
    int i;
215
0
    strbuf_addstr(out, plural);
216
0
    for (i = 0; i < list->nr - 1; i++)
217
0
      strbuf_addf(out, "%s%s", i > 0 ? ", " : "",
218
0
            list->items[i].string);
219
0
    strbuf_addf(out, " and %s", list->items[list->nr - 1].string);
220
0
  }
221
0
}
222
223
static void add_branch_desc(struct strbuf *out, const char *name)
224
0
{
225
0
  struct strbuf desc = STRBUF_INIT;
226
227
0
  if (!read_branch_desc(&desc, name)) {
228
0
    const char *bp = desc.buf;
229
0
    while (*bp) {
230
0
      const char *ep = strchrnul(bp, '\n');
231
0
      if (*ep)
232
0
        ep++;
233
0
      strbuf_addf(out, "  : %.*s", (int)(ep - bp), bp);
234
0
      bp = ep;
235
0
    }
236
0
    strbuf_complete_line(out);
237
0
  }
238
0
  strbuf_release(&desc);
239
0
}
240
241
0
#define util_as_integral(elem) ((intptr_t)((elem)->util))
242
243
static void record_person_from_buf(int which, struct string_list *people,
244
           const char *buffer)
245
0
{
246
0
  char *name_buf, *name, *name_end;
247
0
  struct string_list_item *elem;
248
0
  const char *field;
249
250
0
  field = (which == 'a') ? "\nauthor " : "\ncommitter ";
251
0
  name = strstr(buffer, field);
252
0
  if (!name)
253
0
    return;
254
0
  name += strlen(field);
255
0
  name_end = strchrnul(name, '<');
256
0
  if (*name_end)
257
0
    name_end--;
258
0
  while (isspace(*name_end) && name <= name_end)
259
0
    name_end--;
260
0
  if (name_end < name)
261
0
    return;
262
0
  name_buf = xmemdupz(name, name_end - name + 1);
263
264
0
  elem = string_list_lookup(people, name_buf);
265
0
  if (!elem) {
266
0
    elem = string_list_insert(people, name_buf);
267
0
    elem->util = (void *)0;
268
0
  }
269
0
  elem->util = (void*)(util_as_integral(elem) + 1);
270
0
  free(name_buf);
271
0
}
272
273
274
static void record_person(int which, struct string_list *people,
275
        struct commit *commit)
276
0
{
277
0
  const char *buffer = repo_get_commit_buffer(the_repository, commit,
278
0
                NULL);
279
0
  record_person_from_buf(which, people, buffer);
280
0
  repo_unuse_commit_buffer(the_repository, commit, buffer);
281
0
}
282
283
static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
284
0
{
285
0
  const struct string_list_item *a = a_, *b = b_;
286
0
  return util_as_integral(b) - util_as_integral(a);
287
0
}
288
289
static void add_people_count(struct strbuf *out, struct string_list *people)
290
0
{
291
0
  if (people->nr == 1)
292
0
    strbuf_addstr(out, people->items[0].string);
293
0
  else if (people->nr == 2)
294
0
    strbuf_addf(out, "%s (%d) and %s (%d)",
295
0
          people->items[0].string,
296
0
          (int)util_as_integral(&people->items[0]),
297
0
          people->items[1].string,
298
0
          (int)util_as_integral(&people->items[1]));
299
0
  else if (people->nr)
300
0
    strbuf_addf(out, "%s (%d) and others",
301
0
          people->items[0].string,
302
0
          (int)util_as_integral(&people->items[0]));
303
0
}
304
305
static void credit_people(struct strbuf *out,
306
        struct string_list *them,
307
        int kind)
308
0
{
309
0
  const char *label;
310
0
  const char *me;
311
312
0
  if (kind == 'a') {
313
0
    label = "By";
314
0
    me = git_author_info(IDENT_NO_DATE);
315
0
  } else {
316
0
    label = "Via";
317
0
    me = git_committer_info(IDENT_NO_DATE);
318
0
  }
319
320
0
  if (!them->nr ||
321
0
      (them->nr == 1 &&
322
0
       me &&
323
0
       skip_prefix(me, them->items->string, &me) &&
324
0
       starts_with(me, " <")))
325
0
    return;
326
0
  strbuf_addf(out, "\n%s %s ", comment_line_str, label);
327
0
  add_people_count(out, them);
328
0
}
329
330
static void add_people_info(struct strbuf *out,
331
          struct string_list *authors,
332
          struct string_list *committers)
333
0
{
334
0
  QSORT(authors->items, authors->nr,
335
0
        cmp_string_list_util_as_integral);
336
0
  QSORT(committers->items, committers->nr,
337
0
        cmp_string_list_util_as_integral);
338
339
0
  credit_people(out, authors, 'a');
340
0
  credit_people(out, committers, 'c');
341
0
}
342
343
static void shortlog(const char *name,
344
         struct origin_data *origin_data,
345
         struct commit *head,
346
         struct rev_info *rev,
347
         struct fmt_merge_msg_opts *opts,
348
         struct strbuf *out)
349
0
{
350
0
  int i, count = 0;
351
0
  struct commit *commit;
352
0
  struct object *branch;
353
0
  struct string_list subjects = STRING_LIST_INIT_DUP;
354
0
  struct string_list authors = STRING_LIST_INIT_DUP;
355
0
  struct string_list committers = STRING_LIST_INIT_DUP;
356
0
  int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
357
0
  struct strbuf sb = STRBUF_INIT;
358
0
  const struct object_id *oid = &origin_data->oid;
359
0
  int limit = opts->shortlog_len;
360
361
0
  branch = deref_tag(the_repository, parse_object(the_repository, oid),
362
0
         oid_to_hex(oid),
363
0
         the_hash_algo->hexsz);
364
0
  if (!branch || branch->type != OBJ_COMMIT)
365
0
    return;
366
367
0
  setup_revisions(0, NULL, rev, NULL);
368
0
  add_pending_object(rev, branch, name);
369
0
  add_pending_object(rev, &head->object, "^HEAD");
370
0
  head->object.flags |= UNINTERESTING;
371
0
  if (prepare_revision_walk(rev))
372
0
    die("revision walk setup failed");
373
0
  while ((commit = get_revision(rev)) != NULL) {
374
0
    struct pretty_print_context ctx = {0};
375
376
0
    if (commit->parents && commit->parents->next) {
377
      /* do not list a merge but count committer */
378
0
      if (opts->credit_people)
379
0
        record_person('c', &committers, commit);
380
0
      continue;
381
0
    }
382
0
    if (!count && opts->credit_people)
383
      /* the 'tip' committer */
384
0
      record_person('c', &committers, commit);
385
0
    if (opts->credit_people)
386
0
      record_person('a', &authors, commit);
387
0
    count++;
388
0
    if (subjects.nr > limit)
389
0
      continue;
390
391
0
    repo_format_commit_message(the_repository, commit, "%s", &sb,
392
0
             &ctx);
393
0
    strbuf_ltrim(&sb);
394
395
0
    if (!sb.len)
396
0
      string_list_append(&subjects,
397
0
             oid_to_hex(&commit->object.oid));
398
0
    else
399
0
      string_list_append_nodup(&subjects,
400
0
             strbuf_detach(&sb, NULL));
401
0
  }
402
403
0
  if (opts->credit_people)
404
0
    add_people_info(out, &authors, &committers);
405
0
  if (count > limit)
406
0
    strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
407
0
  else
408
0
    strbuf_addf(out, "\n* %s:\n", name);
409
410
0
  if (origin_data->is_local_branch && use_branch_desc)
411
0
    add_branch_desc(out, name);
412
413
0
  for (i = 0; i < subjects.nr; i++)
414
0
    if (i >= limit)
415
0
      strbuf_addstr(out, "  ...\n");
416
0
    else
417
0
      strbuf_addf(out, "  %s\n", subjects.items[i].string);
418
419
0
  clear_commit_marks((struct commit *)branch, flags);
420
0
  clear_commit_marks(head, flags);
421
0
  free_commit_list(rev->commits);
422
0
  rev->commits = NULL;
423
0
  rev->pending.nr = 0;
424
425
0
  string_list_clear(&authors, 0);
426
0
  string_list_clear(&committers, 0);
427
0
  string_list_clear(&subjects, 0);
428
0
}
429
430
/*
431
 * See if dest_branch matches with any glob pattern on the
432
 * suppress_dest_patterns list.
433
 *
434
 * We may want to also allow negative matches e.g. ":!glob" like we do
435
 * for pathspec, but for now, let's keep it simple and stupid.
436
 */
437
static int dest_suppressed(const char *dest_branch)
438
0
{
439
0
  struct string_list_item *item;
440
441
0
  for_each_string_list_item(item, &suppress_dest_patterns) {
442
0
    if (!wildmatch(item->string, dest_branch, WM_PATHNAME))
443
0
      return 1;
444
0
  }
445
0
  return 0;
446
0
}
447
448
static void fmt_merge_msg_title(struct strbuf *out,
449
        const char *current_branch)
450
0
{
451
0
  int i = 0;
452
0
  const char *sep = "";
453
454
0
  strbuf_addstr(out, "Merge ");
455
0
  for (i = 0; i < srcs.nr; i++) {
456
0
    struct src_data *src_data = srcs.items[i].util;
457
0
    const char *subsep = "";
458
459
0
    strbuf_addstr(out, sep);
460
0
    sep = "; ";
461
462
0
    if (src_data->head_status == 1) {
463
0
      strbuf_addstr(out, srcs.items[i].string);
464
0
      continue;
465
0
    }
466
0
    if (src_data->head_status == 3) {
467
0
      subsep = ", ";
468
0
      strbuf_addstr(out, "HEAD");
469
0
    }
470
0
    if (src_data->branch.nr) {
471
0
      strbuf_addstr(out, subsep);
472
0
      subsep = ", ";
473
0
      print_joined("branch ", "branches ", &src_data->branch,
474
0
          out);
475
0
    }
476
0
    if (src_data->r_branch.nr) {
477
0
      strbuf_addstr(out, subsep);
478
0
      subsep = ", ";
479
0
      print_joined("remote-tracking branch ", "remote-tracking branches ",
480
0
          &src_data->r_branch, out);
481
0
    }
482
0
    if (src_data->tag.nr) {
483
0
      strbuf_addstr(out, subsep);
484
0
      subsep = ", ";
485
0
      print_joined("tag ", "tags ", &src_data->tag, out);
486
0
    }
487
0
    if (src_data->generic.nr) {
488
0
      strbuf_addstr(out, subsep);
489
0
      print_joined("commit ", "commits ", &src_data->generic,
490
0
          out);
491
0
    }
492
0
    if (strcmp(".", srcs.items[i].string))
493
0
      strbuf_addf(out, " of %s", srcs.items[i].string);
494
0
  }
495
496
0
  if (!dest_suppressed(current_branch))
497
0
    strbuf_addf(out, " into %s", current_branch);
498
0
  strbuf_addch(out, '\n');
499
0
}
500
501
static void fmt_tag_signature(struct strbuf *tagbuf,
502
            struct strbuf *sig,
503
            const char *buf,
504
            unsigned long len)
505
0
{
506
0
  const char *tag_body = strstr(buf, "\n\n");
507
0
  if (tag_body) {
508
0
    tag_body += 2;
509
0
    strbuf_add(tagbuf, tag_body, buf + len - tag_body);
510
0
  }
511
0
  strbuf_complete_line(tagbuf);
512
0
  if (sig->len) {
513
0
    strbuf_addch(tagbuf, '\n');
514
0
    strbuf_add_commented_lines(tagbuf, sig->buf, sig->len,
515
0
             comment_line_str);
516
0
  }
517
0
}
518
519
static void fmt_merge_msg_sigs(struct strbuf *out)
520
0
{
521
0
  int i, tag_number = 0, first_tag = 0;
522
0
  struct strbuf tagbuf = STRBUF_INIT;
523
524
0
  for (i = 0; i < origins.nr; i++) {
525
0
    struct object_id *oid = origins.items[i].util;
526
0
    enum object_type type;
527
0
    unsigned long size;
528
0
    char *buf = repo_read_object_file(the_repository, oid, &type,
529
0
              &size);
530
0
    char *origbuf = buf;
531
0
    unsigned long len = size;
532
0
    struct signature_check sigc = { NULL };
533
0
    struct strbuf payload = STRBUF_INIT, sig = STRBUF_INIT;
534
535
0
    if (!buf || type != OBJ_TAG)
536
0
      goto next;
537
538
0
    if (!parse_signature(buf, size, &payload, &sig))
539
0
      ;/* merely annotated */
540
0
    else {
541
0
      buf = payload.buf;
542
0
      len = payload.len;
543
0
      sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
544
0
      sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
545
0
      if (check_signature(&sigc, sig.buf, sig.len) &&
546
0
          !sigc.output)
547
0
        strbuf_addstr(&sig, "gpg verification failed.\n");
548
0
      else
549
0
        strbuf_addstr(&sig, sigc.output);
550
0
    }
551
552
0
    if (!tag_number++) {
553
0
      fmt_tag_signature(&tagbuf, &sig, buf, len);
554
0
      first_tag = i;
555
0
    } else {
556
0
      if (tag_number == 2) {
557
0
        struct strbuf tagline = STRBUF_INIT;
558
0
        strbuf_addch(&tagline, '\n');
559
0
        strbuf_add_commented_lines(&tagline,
560
0
            origins.items[first_tag].string,
561
0
            strlen(origins.items[first_tag].string),
562
0
            comment_line_str);
563
0
        strbuf_insert(&tagbuf, 0, tagline.buf,
564
0
                tagline.len);
565
0
        strbuf_release(&tagline);
566
0
      }
567
0
      strbuf_addch(&tagbuf, '\n');
568
0
      strbuf_add_commented_lines(&tagbuf,
569
0
          origins.items[i].string,
570
0
          strlen(origins.items[i].string),
571
0
          comment_line_str);
572
0
      fmt_tag_signature(&tagbuf, &sig, buf, len);
573
0
    }
574
0
    strbuf_release(&payload);
575
0
    strbuf_release(&sig);
576
0
    signature_check_clear(&sigc);
577
0
  next:
578
0
    free(origbuf);
579
0
  }
580
0
  if (tagbuf.len) {
581
0
    strbuf_addch(out, '\n');
582
0
    strbuf_addbuf(out, &tagbuf);
583
0
  }
584
0
  strbuf_release(&tagbuf);
585
0
}
586
587
static void find_merge_parents(struct merge_parents *result,
588
             struct strbuf *in, struct object_id *head)
589
0
{
590
0
  struct commit_list *parents;
591
0
  struct commit *head_commit;
592
0
  int pos = 0, i, j;
593
594
0
  parents = NULL;
595
0
  while (pos < in->len) {
596
0
    int len;
597
0
    char *p = in->buf + pos;
598
0
    char *newline = strchr(p, '\n');
599
0
    const char *q;
600
0
    struct object_id oid;
601
0
    struct commit *parent;
602
0
    struct object *obj;
603
604
0
    len = newline ? newline - p : strlen(p);
605
0
    pos += len + !!newline;
606
607
0
    if (parse_oid_hex(p, &oid, &q) ||
608
0
        q[0] != '\t' ||
609
0
        q[1] != '\t')
610
0
      continue; /* skip not-for-merge */
611
    /*
612
     * Do not use get_merge_parent() here; we do not have
613
     * "name" here and we do not want to contaminate its
614
     * util field yet.
615
     */
616
0
    obj = parse_object(the_repository, &oid);
617
0
    parent = (struct commit *)repo_peel_to_type(the_repository,
618
0
                  NULL, 0, obj,
619
0
                  OBJ_COMMIT);
620
0
    if (!parent)
621
0
      continue;
622
0
    commit_list_insert(parent, &parents);
623
0
    add_merge_parent(result, &obj->oid, &parent->object.oid);
624
0
  }
625
0
  head_commit = lookup_commit(the_repository, head);
626
0
  if (head_commit)
627
0
    commit_list_insert(head_commit, &parents);
628
0
  reduce_heads_replace(&parents);
629
630
0
  while (parents) {
631
0
    struct commit *cmit = pop_commit(&parents);
632
0
    for (i = 0; i < result->nr; i++)
633
0
      if (oideq(&result->item[i].commit, &cmit->object.oid))
634
0
        result->item[i].used = 1;
635
0
  }
636
637
0
  for (i = j = 0; i < result->nr; i++) {
638
0
    if (result->item[i].used) {
639
0
      if (i != j)
640
0
        result->item[j] = result->item[i];
641
0
      j++;
642
0
    }
643
0
  }
644
0
  result->nr = j;
645
0
}
646
647
648
int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
649
      struct fmt_merge_msg_opts *opts)
650
0
{
651
0
  int i = 0, pos = 0;
652
0
  struct object_id head_oid;
653
0
  const char *current_branch;
654
0
  void *current_branch_to_free;
655
0
  struct merge_parents merge_parents;
656
657
0
  if (!suppress_dest_pattern_seen) {
658
0
    string_list_append(&suppress_dest_patterns, "main");
659
0
    string_list_append(&suppress_dest_patterns, "master");
660
0
  }
661
662
0
  memset(&merge_parents, 0, sizeof(merge_parents));
663
664
  /* learn the commit that we merge into and the current branch name */
665
0
  current_branch = current_branch_to_free =
666
0
    refs_resolve_refdup(get_main_ref_store(the_repository),
667
0
            "HEAD", RESOLVE_REF_READING, &head_oid,
668
0
            NULL);
669
0
  if (!current_branch)
670
0
    die("No current branch");
671
672
0
  if (opts->into_name)
673
0
    current_branch = opts->into_name;
674
0
  else if (starts_with(current_branch, "refs/heads/"))
675
0
    current_branch += 11;
676
677
0
  find_merge_parents(&merge_parents, in, &head_oid);
678
679
  /* get a line */
680
0
  while (pos < in->len) {
681
0
    int len;
682
0
    char *newline, *p = in->buf + pos;
683
684
0
    newline = strchr(p, '\n');
685
0
    len = newline ? newline - p : strlen(p);
686
0
    pos += len + !!newline;
687
0
    i++;
688
0
    p[len] = 0;
689
0
    if (handle_line(p, &merge_parents))
690
0
      die("error in line %d: %.*s", i, len, p);
691
0
  }
692
693
0
  if (opts->add_title && srcs.nr)
694
0
    fmt_merge_msg_title(out, current_branch);
695
696
0
  if (origins.nr)
697
0
    fmt_merge_msg_sigs(out);
698
699
0
  if (opts->shortlog_len) {
700
0
    struct commit *head;
701
0
    struct rev_info rev;
702
703
0
    head = lookup_commit_or_die(&head_oid, "HEAD");
704
0
    repo_init_revisions(the_repository, &rev, NULL);
705
0
    rev.commit_format = CMIT_FMT_ONELINE;
706
0
    diff_merges_suppress(&rev);
707
0
    rev.limited = 1;
708
709
0
    strbuf_complete_line(out);
710
711
0
    for (i = 0; i < origins.nr; i++)
712
0
      shortlog(origins.items[i].string,
713
0
         origins.items[i].util,
714
0
         head, &rev, opts, out);
715
0
    release_revisions(&rev);
716
0
  }
717
718
0
  strbuf_complete_line(out);
719
0
  free(current_branch_to_free);
720
0
  free(merge_parents.item);
721
0
  return 0;
722
0
}