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 | } |