]> git.proxmox.com Git - libgit2.git/blob - src/commit.c
Bump debhelper compatibility level to 13
[libgit2.git] / src / commit.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "commit.h"
9
10 #include "git2/common.h"
11 #include "git2/object.h"
12 #include "git2/repository.h"
13 #include "git2/signature.h"
14 #include "git2/mailmap.h"
15 #include "git2/sys/commit.h"
16
17 #include "odb.h"
18 #include "commit.h"
19 #include "signature.h"
20 #include "message.h"
21 #include "refs.h"
22 #include "object.h"
23 #include "array.h"
24 #include "oidarray.h"
25
26 void git_commit__free(void *_commit)
27 {
28 git_commit *commit = _commit;
29
30 git_array_clear(commit->parent_ids);
31
32 git_signature_free(commit->author);
33 git_signature_free(commit->committer);
34
35 git__free(commit->raw_header);
36 git__free(commit->raw_message);
37 git__free(commit->message_encoding);
38 git__free(commit->summary);
39 git__free(commit->body);
40
41 git__free(commit);
42 }
43
44 static int git_commit__create_buffer_internal(
45 git_buf *out,
46 const git_signature *author,
47 const git_signature *committer,
48 const char *message_encoding,
49 const char *message,
50 const git_oid *tree,
51 git_array_oid_t *parents)
52 {
53 size_t i = 0;
54 const git_oid *parent;
55
56 GIT_ASSERT_ARG(out);
57 GIT_ASSERT_ARG(tree);
58
59 git_oid__writebuf(out, "tree ", tree);
60
61 for (i = 0; i < git_array_size(*parents); i++) {
62 parent = git_array_get(*parents, i);
63 git_oid__writebuf(out, "parent ", parent);
64 }
65
66 git_signature__writebuf(out, "author ", author);
67 git_signature__writebuf(out, "committer ", committer);
68
69 if (message_encoding != NULL)
70 git_buf_printf(out, "encoding %s\n", message_encoding);
71
72 git_buf_putc(out, '\n');
73
74 if (git_buf_puts(out, message) < 0)
75 goto on_error;
76
77 return 0;
78
79 on_error:
80 git_buf_dispose(out);
81 return -1;
82 }
83
84 static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
85 git_commit_parent_callback parent_cb, void *parent_payload,
86 const git_oid *current_id, bool validate)
87 {
88 size_t i;
89 int error;
90 git_oid *parent_cpy;
91 const git_oid *parent;
92
93 if (validate && !git_object__is_valid(repo, tree, GIT_OBJECT_TREE))
94 return -1;
95
96 i = 0;
97 while ((parent = parent_cb(i, parent_payload)) != NULL) {
98 if (validate && !git_object__is_valid(repo, parent, GIT_OBJECT_COMMIT)) {
99 error = -1;
100 goto on_error;
101 }
102
103 parent_cpy = git_array_alloc(*parents);
104 GIT_ERROR_CHECK_ALLOC(parent_cpy);
105
106 git_oid_cpy(parent_cpy, parent);
107 i++;
108 }
109
110 if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
111 git_error_set(GIT_ERROR_OBJECT, "failed to create commit: current tip is not the first parent");
112 error = GIT_EMODIFIED;
113 goto on_error;
114 }
115
116 return 0;
117
118 on_error:
119 git_array_clear(*parents);
120 return error;
121 }
122
123 static int git_commit__create_internal(
124 git_oid *id,
125 git_repository *repo,
126 const char *update_ref,
127 const git_signature *author,
128 const git_signature *committer,
129 const char *message_encoding,
130 const char *message,
131 const git_oid *tree,
132 git_commit_parent_callback parent_cb,
133 void *parent_payload,
134 bool validate)
135 {
136 int error;
137 git_odb *odb;
138 git_reference *ref = NULL;
139 git_buf buf = GIT_BUF_INIT;
140 const git_oid *current_id = NULL;
141 git_array_oid_t parents = GIT_ARRAY_INIT;
142
143 if (update_ref) {
144 error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
145 if (error < 0 && error != GIT_ENOTFOUND)
146 return error;
147 }
148 git_error_clear();
149
150 if (ref)
151 current_id = git_reference_target(ref);
152
153 if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
154 goto cleanup;
155
156 error = git_commit__create_buffer_internal(&buf, author, committer,
157 message_encoding, message, tree,
158 &parents);
159
160 if (error < 0)
161 goto cleanup;
162
163 if (git_repository_odb__weakptr(&odb, repo) < 0)
164 goto cleanup;
165
166 if (git_odb__freshen(odb, tree) < 0)
167 goto cleanup;
168
169 if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT) < 0)
170 goto cleanup;
171
172
173 if (update_ref != NULL) {
174 error = git_reference__update_for_commit(
175 repo, ref, update_ref, id, "commit");
176 goto cleanup;
177 }
178
179 cleanup:
180 git_array_clear(parents);
181 git_reference_free(ref);
182 git_buf_dispose(&buf);
183 return error;
184 }
185
186 int git_commit_create_from_callback(
187 git_oid *id,
188 git_repository *repo,
189 const char *update_ref,
190 const git_signature *author,
191 const git_signature *committer,
192 const char *message_encoding,
193 const char *message,
194 const git_oid *tree,
195 git_commit_parent_callback parent_cb,
196 void *parent_payload)
197 {
198 return git_commit__create_internal(
199 id, repo, update_ref, author, committer, message_encoding, message,
200 tree, parent_cb, parent_payload, true);
201 }
202
203 typedef struct {
204 size_t total;
205 va_list args;
206 } commit_parent_varargs;
207
208 static const git_oid *commit_parent_from_varargs(size_t curr, void *payload)
209 {
210 commit_parent_varargs *data = payload;
211 const git_commit *commit;
212 if (curr >= data->total)
213 return NULL;
214 commit = va_arg(data->args, const git_commit *);
215 return commit ? git_commit_id(commit) : NULL;
216 }
217
218 int git_commit_create_v(
219 git_oid *id,
220 git_repository *repo,
221 const char *update_ref,
222 const git_signature *author,
223 const git_signature *committer,
224 const char *message_encoding,
225 const char *message,
226 const git_tree *tree,
227 size_t parent_count,
228 ...)
229 {
230 int error = 0;
231 commit_parent_varargs data;
232
233 GIT_ASSERT_ARG(tree);
234 GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
235
236 data.total = parent_count;
237 va_start(data.args, parent_count);
238
239 error = git_commit__create_internal(
240 id, repo, update_ref, author, committer,
241 message_encoding, message, git_tree_id(tree),
242 commit_parent_from_varargs, &data, false);
243
244 va_end(data.args);
245 return error;
246 }
247
248 typedef struct {
249 size_t total;
250 const git_oid **parents;
251 } commit_parent_oids;
252
253 static const git_oid *commit_parent_from_ids(size_t curr, void *payload)
254 {
255 commit_parent_oids *data = payload;
256 return (curr < data->total) ? data->parents[curr] : NULL;
257 }
258
259 int git_commit_create_from_ids(
260 git_oid *id,
261 git_repository *repo,
262 const char *update_ref,
263 const git_signature *author,
264 const git_signature *committer,
265 const char *message_encoding,
266 const char *message,
267 const git_oid *tree,
268 size_t parent_count,
269 const git_oid *parents[])
270 {
271 commit_parent_oids data = { parent_count, parents };
272
273 return git_commit__create_internal(
274 id, repo, update_ref, author, committer,
275 message_encoding, message, tree,
276 commit_parent_from_ids, &data, true);
277 }
278
279 typedef struct {
280 size_t total;
281 const git_commit **parents;
282 git_repository *repo;
283 } commit_parent_data;
284
285 static const git_oid *commit_parent_from_array(size_t curr, void *payload)
286 {
287 commit_parent_data *data = payload;
288 const git_commit *commit;
289 if (curr >= data->total)
290 return NULL;
291 commit = data->parents[curr];
292 if (git_commit_owner(commit) != data->repo)
293 return NULL;
294 return git_commit_id(commit);
295 }
296
297 int git_commit_create(
298 git_oid *id,
299 git_repository *repo,
300 const char *update_ref,
301 const git_signature *author,
302 const git_signature *committer,
303 const char *message_encoding,
304 const char *message,
305 const git_tree *tree,
306 size_t parent_count,
307 const git_commit *parents[])
308 {
309 commit_parent_data data = { parent_count, parents, repo };
310
311 GIT_ASSERT_ARG(tree);
312 GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
313
314 return git_commit__create_internal(
315 id, repo, update_ref, author, committer,
316 message_encoding, message, git_tree_id(tree),
317 commit_parent_from_array, &data, false);
318 }
319
320 static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
321 {
322 const git_commit *commit_to_amend = payload;
323 if (curr >= git_array_size(commit_to_amend->parent_ids))
324 return NULL;
325 return git_array_get(commit_to_amend->parent_ids, curr);
326 }
327
328 int git_commit_amend(
329 git_oid *id,
330 const git_commit *commit_to_amend,
331 const char *update_ref,
332 const git_signature *author,
333 const git_signature *committer,
334 const char *message_encoding,
335 const char *message,
336 const git_tree *tree)
337 {
338 git_repository *repo;
339 git_oid tree_id;
340 git_reference *ref;
341 int error;
342
343 GIT_ASSERT_ARG(id);
344 GIT_ASSERT_ARG(commit_to_amend);
345
346 repo = git_commit_owner(commit_to_amend);
347
348 if (!author)
349 author = git_commit_author(commit_to_amend);
350 if (!committer)
351 committer = git_commit_committer(commit_to_amend);
352 if (!message_encoding)
353 message_encoding = git_commit_message_encoding(commit_to_amend);
354 if (!message)
355 message = git_commit_message(commit_to_amend);
356
357 if (!tree) {
358 git_tree *old_tree;
359 GIT_ERROR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
360 git_oid_cpy(&tree_id, git_tree_id(old_tree));
361 git_tree_free(old_tree);
362 } else {
363 GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
364 git_oid_cpy(&tree_id, git_tree_id(tree));
365 }
366
367 if (update_ref) {
368 if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0)
369 return error;
370
371 if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) {
372 git_reference_free(ref);
373 git_error_set(GIT_ERROR_REFERENCE, "commit to amend is not the tip of the given branch");
374 return -1;
375 }
376 }
377
378 error = git_commit__create_internal(
379 id, repo, NULL, author, committer, message_encoding, message,
380 &tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
381
382 if (!error && update_ref) {
383 error = git_reference__update_for_commit(
384 repo, ref, NULL, id, "commit");
385 git_reference_free(ref);
386 }
387
388 return error;
389 }
390
391 static int commit_parse(git_commit *commit, const char *data, size_t size, unsigned int flags)
392 {
393 const char *buffer_start = data, *buffer;
394 const char *buffer_end = buffer_start + size;
395 git_oid parent_id;
396 size_t header_len;
397 git_signature dummy_sig;
398
399 GIT_ASSERT_ARG(commit);
400 GIT_ASSERT_ARG(data);
401
402 buffer = buffer_start;
403
404 /* Allocate for one, which will allow not to realloc 90% of the time */
405 git_array_init_to_size(commit->parent_ids, 1);
406 GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
407
408 /* The tree is always the first field */
409 if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
410 if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
411 goto bad_buffer;
412 } else {
413 size_t tree_len = strlen("tree ") + GIT_OID_HEXSZ + 1;
414 if (buffer + tree_len > buffer_end)
415 goto bad_buffer;
416 buffer += tree_len;
417 }
418
419 /*
420 * TODO: commit grafts!
421 */
422
423 while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
424 git_oid *new_id = git_array_alloc(commit->parent_ids);
425 GIT_ERROR_CHECK_ALLOC(new_id);
426
427 git_oid_cpy(new_id, &parent_id);
428 }
429
430 if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
431 commit->author = git__malloc(sizeof(git_signature));
432 GIT_ERROR_CHECK_ALLOC(commit->author);
433
434 if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
435 return -1;
436 }
437
438 /* Some tools create multiple author fields, ignore the extra ones */
439 while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) {
440 if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0)
441 return -1;
442
443 git__free(dummy_sig.name);
444 git__free(dummy_sig.email);
445 }
446
447 /* Always parse the committer; we need the commit time */
448 commit->committer = git__malloc(sizeof(git_signature));
449 GIT_ERROR_CHECK_ALLOC(commit->committer);
450
451 if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
452 return -1;
453
454 if (flags & GIT_COMMIT_PARSE_QUICK)
455 return 0;
456
457 /* Parse add'l header entries */
458 while (buffer < buffer_end) {
459 const char *eoln = buffer;
460 if (buffer[-1] == '\n' && buffer[0] == '\n')
461 break;
462
463 while (eoln < buffer_end && *eoln != '\n')
464 ++eoln;
465
466 if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
467 buffer += strlen("encoding ");
468
469 commit->message_encoding = git__strndup(buffer, eoln - buffer);
470 GIT_ERROR_CHECK_ALLOC(commit->message_encoding);
471 }
472
473 if (eoln < buffer_end && *eoln == '\n')
474 ++eoln;
475 buffer = eoln;
476 }
477
478 header_len = buffer - buffer_start;
479 commit->raw_header = git__strndup(buffer_start, header_len);
480 GIT_ERROR_CHECK_ALLOC(commit->raw_header);
481
482 /* point "buffer" to data after header, +1 for the final LF */
483 buffer = buffer_start + header_len + 1;
484
485 /* extract commit message */
486 if (buffer <= buffer_end)
487 commit->raw_message = git__strndup(buffer, buffer_end - buffer);
488 else
489 commit->raw_message = git__strdup("");
490 GIT_ERROR_CHECK_ALLOC(commit->raw_message);
491
492 return 0;
493
494 bad_buffer:
495 git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object");
496 return -1;
497 }
498
499 int git_commit__parse_raw(void *commit, const char *data, size_t size)
500 {
501 return commit_parse(commit, data, size, 0);
502 }
503
504 int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags)
505 {
506 return commit_parse(commit, git_odb_object_data(odb_obj), git_odb_object_size(odb_obj), flags);
507 }
508
509 int git_commit__parse(void *_commit, git_odb_object *odb_obj)
510 {
511 return git_commit__parse_ext(_commit, odb_obj, 0);
512 }
513
514 #define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
515 _rvalue git_commit_##_name(const git_commit *commit) \
516 {\
517 GIT_ASSERT_ARG_WITH_RETVAL(commit, _invalid); \
518 return _return; \
519 }
520
521 GIT_COMMIT_GETTER(const git_signature *, author, commit->author, NULL)
522 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer, NULL)
523 GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message, NULL)
524 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding, NULL)
525 GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header, NULL)
526 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time, INT64_MIN)
527 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset, -1)
528 GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids), 0)
529 GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id, NULL)
530
531 const char *git_commit_message(const git_commit *commit)
532 {
533 const char *message;
534
535 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
536
537 message = commit->raw_message;
538
539 /* trim leading newlines from raw message */
540 while (*message && *message == '\n')
541 ++message;
542
543 return message;
544 }
545
546 const char *git_commit_summary(git_commit *commit)
547 {
548 git_buf summary = GIT_BUF_INIT;
549 const char *msg, *space;
550 bool space_contains_newline = false;
551
552 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
553
554 if (!commit->summary) {
555 for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
556 char next_character = msg[0];
557 /* stop processing at the end of the first paragraph */
558 if (next_character == '\n' && (!msg[1] || msg[1] == '\n'))
559 break;
560 /* record the beginning of contiguous whitespace runs */
561 else if (git__isspace(next_character)) {
562 if(space == NULL) {
563 space = msg;
564 space_contains_newline = false;
565 }
566 space_contains_newline |= next_character == '\n';
567 }
568 /* the next character is non-space */
569 else {
570 /* process any recorded whitespace */
571 if (space) {
572 if(space_contains_newline)
573 git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
574 else
575 git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */
576 space = NULL;
577 }
578 /* copy the next character */
579 git_buf_putc(&summary, next_character);
580 }
581 }
582
583 commit->summary = git_buf_detach(&summary);
584 if (!commit->summary)
585 commit->summary = git__strdup("");
586 }
587
588 return commit->summary;
589 }
590
591 const char *git_commit_body(git_commit *commit)
592 {
593 const char *msg, *end;
594
595 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
596
597 if (!commit->body) {
598 /* search for end of summary */
599 for (msg = git_commit_message(commit); *msg; ++msg)
600 if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
601 break;
602
603 /* trim leading and trailing whitespace */
604 for (; *msg; ++msg)
605 if (!git__isspace(*msg))
606 break;
607 for (end = msg + strlen(msg) - 1; msg <= end; --end)
608 if (!git__isspace(*end))
609 break;
610
611 if (*msg)
612 commit->body = git__strndup(msg, end - msg + 1);
613 }
614
615 return commit->body;
616 }
617
618 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
619 {
620 GIT_ASSERT_ARG(commit);
621 return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
622 }
623
624 const git_oid *git_commit_parent_id(
625 const git_commit *commit, unsigned int n)
626 {
627 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
628
629 return git_array_get(commit->parent_ids, n);
630 }
631
632 int git_commit_parent(
633 git_commit **parent, const git_commit *commit, unsigned int n)
634 {
635 const git_oid *parent_id;
636 GIT_ASSERT_ARG(commit);
637
638 parent_id = git_commit_parent_id(commit, n);
639 if (parent_id == NULL) {
640 git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n);
641 return GIT_ENOTFOUND;
642 }
643
644 return git_commit_lookup(parent, commit->object.repo, parent_id);
645 }
646
647 int git_commit_nth_gen_ancestor(
648 git_commit **ancestor,
649 const git_commit *commit,
650 unsigned int n)
651 {
652 git_commit *current, *parent = NULL;
653 int error;
654
655 GIT_ASSERT_ARG(ancestor);
656 GIT_ASSERT_ARG(commit);
657
658 if (git_commit_dup(&current, (git_commit *)commit) < 0)
659 return -1;
660
661 if (n == 0) {
662 *ancestor = current;
663 return 0;
664 }
665
666 while (n--) {
667 error = git_commit_parent(&parent, current, 0);
668
669 git_commit_free(current);
670
671 if (error < 0)
672 return error;
673
674 current = parent;
675 }
676
677 *ancestor = parent;
678 return 0;
679 }
680
681 int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
682 {
683 const char *eol, *buf = commit->raw_header;
684
685 git_buf_clear(out);
686
687 while ((eol = strchr(buf, '\n'))) {
688 /* We can skip continuations here */
689 if (buf[0] == ' ') {
690 buf = eol + 1;
691 continue;
692 }
693
694 /* Skip until we find the field we're after */
695 if (git__prefixcmp(buf, field)) {
696 buf = eol + 1;
697 continue;
698 }
699
700 buf += strlen(field);
701 /* Check that we're not matching a prefix but the field itself */
702 if (buf[0] != ' ') {
703 buf = eol + 1;
704 continue;
705 }
706
707 buf++; /* skip the SP */
708
709 git_buf_put(out, buf, eol - buf);
710 if (git_buf_oom(out))
711 goto oom;
712
713 /* If the next line starts with SP, it's multi-line, we must continue */
714 while (eol[1] == ' ') {
715 git_buf_putc(out, '\n');
716 buf = eol + 2;
717 eol = strchr(buf, '\n');
718 if (!eol)
719 goto malformed;
720
721 git_buf_put(out, buf, eol - buf);
722 }
723
724 if (git_buf_oom(out))
725 goto oom;
726
727 return 0;
728 }
729
730 git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field);
731 return GIT_ENOTFOUND;
732
733 malformed:
734 git_error_set(GIT_ERROR_OBJECT, "malformed header");
735 return -1;
736 oom:
737 git_error_set_oom();
738 return -1;
739 }
740
741 int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
742 {
743 git_odb_object *obj;
744 git_odb *odb;
745 const char *buf;
746 const char *h, *eol;
747 int error;
748
749 git_buf_clear(signature);
750 git_buf_clear(signed_data);
751
752 if (!field)
753 field = "gpgsig";
754
755 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
756 return error;
757
758 if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
759 return error;
760
761 if (obj->cached.type != GIT_OBJECT_COMMIT) {
762 git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB");
763 error = GIT_ENOTFOUND;
764 goto cleanup;
765 }
766
767 buf = git_odb_object_data(obj);
768
769 while ((h = strchr(buf, '\n')) && h[1] != '\0') {
770 h++;
771 if (git__prefixcmp(buf, field)) {
772 if (git_buf_put(signed_data, buf, h - buf) < 0)
773 return -1;
774
775 buf = h;
776 continue;
777 }
778
779 h = buf;
780 h += strlen(field);
781 eol = strchr(h, '\n');
782 if (h[0] != ' ') {
783 buf = h;
784 continue;
785 }
786 if (!eol)
787 goto malformed;
788
789 h++; /* skip the SP */
790
791 git_buf_put(signature, h, eol - h);
792 if (git_buf_oom(signature))
793 goto oom;
794
795 /* If the next line starts with SP, it's multi-line, we must continue */
796 while (eol[1] == ' ') {
797 git_buf_putc(signature, '\n');
798 h = eol + 2;
799 eol = strchr(h, '\n');
800 if (!eol)
801 goto malformed;
802
803 git_buf_put(signature, h, eol - h);
804 }
805
806 if (git_buf_oom(signature))
807 goto oom;
808
809 error = git_buf_puts(signed_data, eol+1);
810 git_odb_object_free(obj);
811 return error;
812 }
813
814 git_error_set(GIT_ERROR_OBJECT, "this commit is not signed");
815 error = GIT_ENOTFOUND;
816 goto cleanup;
817
818 malformed:
819 git_error_set(GIT_ERROR_OBJECT, "malformed header");
820 error = -1;
821 goto cleanup;
822 oom:
823 git_error_set_oom();
824 error = -1;
825 goto cleanup;
826
827 cleanup:
828 git_odb_object_free(obj);
829 git_buf_clear(signature);
830 git_buf_clear(signed_data);
831 return error;
832 }
833
834 int git_commit_create_buffer(git_buf *out,
835 git_repository *repo,
836 const git_signature *author,
837 const git_signature *committer,
838 const char *message_encoding,
839 const char *message,
840 const git_tree *tree,
841 size_t parent_count,
842 const git_commit *parents[])
843 {
844 int error;
845 commit_parent_data data = { parent_count, parents, repo };
846 git_array_oid_t parents_arr = GIT_ARRAY_INIT;
847 const git_oid *tree_id;
848
849 GIT_ASSERT_ARG(tree);
850 GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
851
852 tree_id = git_tree_id(tree);
853
854 if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
855 return error;
856
857 error = git_commit__create_buffer_internal(
858 out, author, committer,
859 message_encoding, message, tree_id,
860 &parents_arr);
861
862 git_array_clear(parents_arr);
863 return error;
864 }
865
866 /**
867 * Append to 'out' properly marking continuations when there's a newline in 'content'
868 */
869 static int format_header_field(git_buf *out, const char *field, const char *content)
870 {
871 const char *lf;
872
873 GIT_ASSERT_ARG(out);
874 GIT_ASSERT_ARG(field);
875 GIT_ASSERT_ARG(content);
876
877 git_buf_puts(out, field);
878 git_buf_putc(out, ' ');
879
880 while ((lf = strchr(content, '\n')) != NULL) {
881 git_buf_put(out, content, lf - content);
882 git_buf_puts(out, "\n ");
883 content = lf + 1;
884 }
885
886 git_buf_puts(out, content);
887 git_buf_putc(out, '\n');
888
889 return git_buf_oom(out) ? -1 : 0;
890 }
891
892 static const git_oid *commit_parent_from_commit(size_t n, void *payload)
893 {
894 const git_commit *commit = (const git_commit *) payload;
895
896 return git_array_get(commit->parent_ids, n);
897
898 }
899
900 int git_commit_create_with_signature(
901 git_oid *out,
902 git_repository *repo,
903 const char *commit_content,
904 const char *signature,
905 const char *signature_field)
906 {
907 git_odb *odb;
908 int error = 0;
909 const char *field;
910 const char *header_end;
911 git_buf commit = GIT_BUF_INIT;
912 git_commit *parsed;
913 git_array_oid_t parents = GIT_ARRAY_INIT;
914
915 /* The first step is to verify that all the tree and parents exist */
916 parsed = git__calloc(1, sizeof(git_commit));
917 GIT_ERROR_CHECK_ALLOC(parsed);
918 if ((error = commit_parse(parsed, commit_content, strlen(commit_content), 0)) < 0)
919 goto cleanup;
920
921 if ((error = validate_tree_and_parents(&parents, repo, &parsed->tree_id, commit_parent_from_commit, parsed, NULL, true)) < 0)
922 goto cleanup;
923
924 git_array_clear(parents);
925
926 /* Then we start appending by identifying the end of the commit header */
927 header_end = strstr(commit_content, "\n\n");
928 if (!header_end) {
929 git_error_set(GIT_ERROR_INVALID, "malformed commit contents");
930 error = -1;
931 goto cleanup;
932 }
933
934 /* The header ends after the first LF */
935 header_end++;
936 git_buf_put(&commit, commit_content, header_end - commit_content);
937
938 if (signature != NULL) {
939 field = signature_field ? signature_field : "gpgsig";
940
941 if ((error = format_header_field(&commit, field, signature)) < 0)
942 goto cleanup;
943 }
944
945 git_buf_puts(&commit, header_end);
946
947 if (git_buf_oom(&commit))
948 return -1;
949
950 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
951 goto cleanup;
952
953 if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0)
954 goto cleanup;
955
956 cleanup:
957 git_commit__free(parsed);
958 git_buf_dispose(&commit);
959 return error;
960 }
961
962 int git_commit_committer_with_mailmap(
963 git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
964 {
965 return git_mailmap_resolve_signature(out, mailmap, commit->committer);
966 }
967
968 int git_commit_author_with_mailmap(
969 git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
970 {
971 return git_mailmap_resolve_signature(out, mailmap, commit->author);
972 }