]> git.proxmox.com Git - libgit2.git/blob - src/commit.c
b137463f3956db890cf1cf63fa29b094710bf767
[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 "buf.h"
18 #include "odb.h"
19 #include "commit.h"
20 #include "signature.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_str *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_str_printf(out, "encoding %s\n", message_encoding);
71
72 git_str_putc(out, '\n');
73
74 if (git_str_puts(out, message) < 0)
75 goto on_error;
76
77 return 0;
78
79 on_error:
80 git_str_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_str buf = GIT_STR_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_str_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 int error;
399
400 GIT_ASSERT_ARG(commit);
401 GIT_ASSERT_ARG(data);
402
403 buffer = buffer_start;
404
405 /* Allocate for one, which will allow not to realloc 90% of the time */
406 git_array_init_to_size(commit->parent_ids, 1);
407 GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
408
409 /* The tree is always the first field */
410 if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
411 if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
412 goto bad_buffer;
413 } else {
414 size_t tree_len = strlen("tree ") + GIT_OID_HEXSZ + 1;
415 if (buffer + tree_len > buffer_end)
416 goto bad_buffer;
417 buffer += tree_len;
418 }
419
420 /*
421 * TODO: commit grafts!
422 */
423
424 while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
425 git_oid *new_id = git_array_alloc(commit->parent_ids);
426 GIT_ERROR_CHECK_ALLOC(new_id);
427
428 git_oid_cpy(new_id, &parent_id);
429 }
430
431 if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
432 commit->author = git__malloc(sizeof(git_signature));
433 GIT_ERROR_CHECK_ALLOC(commit->author);
434
435 if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < 0)
436 return error;
437 }
438
439 /* Some tools create multiple author fields, ignore the extra ones */
440 while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) {
441 if ((error = git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n')) < 0)
442 return error;
443
444 git__free(dummy_sig.name);
445 git__free(dummy_sig.email);
446 }
447
448 /* Always parse the committer; we need the commit time */
449 commit->committer = git__malloc(sizeof(git_signature));
450 GIT_ERROR_CHECK_ALLOC(commit->committer);
451
452 if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < 0)
453 return error;
454
455 if (flags & GIT_COMMIT_PARSE_QUICK)
456 return 0;
457
458 /* Parse add'l header entries */
459 while (buffer < buffer_end) {
460 const char *eoln = buffer;
461 if (buffer[-1] == '\n' && buffer[0] == '\n')
462 break;
463
464 while (eoln < buffer_end && *eoln != '\n')
465 ++eoln;
466
467 if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
468 buffer += strlen("encoding ");
469
470 commit->message_encoding = git__strndup(buffer, eoln - buffer);
471 GIT_ERROR_CHECK_ALLOC(commit->message_encoding);
472 }
473
474 if (eoln < buffer_end && *eoln == '\n')
475 ++eoln;
476 buffer = eoln;
477 }
478
479 header_len = buffer - buffer_start;
480 commit->raw_header = git__strndup(buffer_start, header_len);
481 GIT_ERROR_CHECK_ALLOC(commit->raw_header);
482
483 /* point "buffer" to data after header, +1 for the final LF */
484 buffer = buffer_start + header_len + 1;
485
486 /* extract commit message */
487 if (buffer <= buffer_end)
488 commit->raw_message = git__strndup(buffer, buffer_end - buffer);
489 else
490 commit->raw_message = git__strdup("");
491 GIT_ERROR_CHECK_ALLOC(commit->raw_message);
492
493 return 0;
494
495 bad_buffer:
496 git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object");
497 return GIT_EINVALID;
498 }
499
500 int git_commit__parse_raw(void *commit, const char *data, size_t size)
501 {
502 return commit_parse(commit, data, size, 0);
503 }
504
505 int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags)
506 {
507 return commit_parse(commit, git_odb_object_data(odb_obj), git_odb_object_size(odb_obj), flags);
508 }
509
510 int git_commit__parse(void *_commit, git_odb_object *odb_obj)
511 {
512 return git_commit__parse_ext(_commit, odb_obj, 0);
513 }
514
515 #define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
516 _rvalue git_commit_##_name(const git_commit *commit) \
517 {\
518 GIT_ASSERT_ARG_WITH_RETVAL(commit, _invalid); \
519 return _return; \
520 }
521
522 GIT_COMMIT_GETTER(const git_signature *, author, commit->author, NULL)
523 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer, NULL)
524 GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message, NULL)
525 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding, NULL)
526 GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header, NULL)
527 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time, INT64_MIN)
528 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset, -1)
529 GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids), 0)
530 GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id, NULL)
531
532 const char *git_commit_message(const git_commit *commit)
533 {
534 const char *message;
535
536 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
537
538 message = commit->raw_message;
539
540 /* trim leading newlines from raw message */
541 while (*message && *message == '\n')
542 ++message;
543
544 return message;
545 }
546
547 const char *git_commit_summary(git_commit *commit)
548 {
549 git_str summary = GIT_STR_INIT;
550 const char *msg, *space, *next;
551 bool space_contains_newline = false;
552
553 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
554
555 if (!commit->summary) {
556 for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
557 char next_character = msg[0];
558 /* stop processing at the end of the first paragraph */
559 if (next_character == '\n') {
560 if (!msg[1])
561 break;
562 if (msg[1] == '\n')
563 break;
564 /* stop processing if next line contains only whitespace */
565 next = msg + 1;
566 while (*next && git__isspace_nonlf(*next)) {
567 ++next;
568 }
569 if (!*next || *next == '\n')
570 break;
571 }
572 /* record the beginning of contiguous whitespace runs */
573 if (git__isspace(next_character)) {
574 if(space == NULL) {
575 space = msg;
576 space_contains_newline = false;
577 }
578 space_contains_newline |= next_character == '\n';
579 }
580 /* the next character is non-space */
581 else {
582 /* process any recorded whitespace */
583 if (space) {
584 if(space_contains_newline)
585 git_str_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
586 else
587 git_str_put(&summary, space, (msg - space)); /* otherwise copy it */
588 space = NULL;
589 }
590 /* copy the next character */
591 git_str_putc(&summary, next_character);
592 }
593 }
594
595 commit->summary = git_str_detach(&summary);
596 if (!commit->summary)
597 commit->summary = git__strdup("");
598 }
599
600 return commit->summary;
601 }
602
603 const char *git_commit_body(git_commit *commit)
604 {
605 const char *msg, *end;
606
607 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
608
609 if (!commit->body) {
610 /* search for end of summary */
611 for (msg = git_commit_message(commit); *msg; ++msg)
612 if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
613 break;
614
615 /* trim leading and trailing whitespace */
616 for (; *msg; ++msg)
617 if (!git__isspace(*msg))
618 break;
619 for (end = msg + strlen(msg) - 1; msg <= end; --end)
620 if (!git__isspace(*end))
621 break;
622
623 if (*msg)
624 commit->body = git__strndup(msg, end - msg + 1);
625 }
626
627 return commit->body;
628 }
629
630 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
631 {
632 GIT_ASSERT_ARG(commit);
633 return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
634 }
635
636 const git_oid *git_commit_parent_id(
637 const git_commit *commit, unsigned int n)
638 {
639 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
640
641 return git_array_get(commit->parent_ids, n);
642 }
643
644 int git_commit_parent(
645 git_commit **parent, const git_commit *commit, unsigned int n)
646 {
647 const git_oid *parent_id;
648 GIT_ASSERT_ARG(commit);
649
650 parent_id = git_commit_parent_id(commit, n);
651 if (parent_id == NULL) {
652 git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n);
653 return GIT_ENOTFOUND;
654 }
655
656 return git_commit_lookup(parent, commit->object.repo, parent_id);
657 }
658
659 int git_commit_nth_gen_ancestor(
660 git_commit **ancestor,
661 const git_commit *commit,
662 unsigned int n)
663 {
664 git_commit *current, *parent = NULL;
665 int error;
666
667 GIT_ASSERT_ARG(ancestor);
668 GIT_ASSERT_ARG(commit);
669
670 if (git_commit_dup(&current, (git_commit *)commit) < 0)
671 return -1;
672
673 if (n == 0) {
674 *ancestor = current;
675 return 0;
676 }
677
678 while (n--) {
679 error = git_commit_parent(&parent, current, 0);
680
681 git_commit_free(current);
682
683 if (error < 0)
684 return error;
685
686 current = parent;
687 }
688
689 *ancestor = parent;
690 return 0;
691 }
692
693 int git_commit_header_field(
694 git_buf *out,
695 const git_commit *commit,
696 const char *field)
697 {
698 GIT_BUF_WRAP_PRIVATE(out, git_commit__header_field, commit, field);
699 }
700
701 int git_commit__header_field(
702 git_str *out,
703 const git_commit *commit,
704 const char *field)
705 {
706 const char *eol, *buf = commit->raw_header;
707
708 git_str_clear(out);
709
710 while ((eol = strchr(buf, '\n'))) {
711 /* We can skip continuations here */
712 if (buf[0] == ' ') {
713 buf = eol + 1;
714 continue;
715 }
716
717 /* Skip until we find the field we're after */
718 if (git__prefixcmp(buf, field)) {
719 buf = eol + 1;
720 continue;
721 }
722
723 buf += strlen(field);
724 /* Check that we're not matching a prefix but the field itself */
725 if (buf[0] != ' ') {
726 buf = eol + 1;
727 continue;
728 }
729
730 buf++; /* skip the SP */
731
732 git_str_put(out, buf, eol - buf);
733 if (git_str_oom(out))
734 goto oom;
735
736 /* If the next line starts with SP, it's multi-line, we must continue */
737 while (eol[1] == ' ') {
738 git_str_putc(out, '\n');
739 buf = eol + 2;
740 eol = strchr(buf, '\n');
741 if (!eol)
742 goto malformed;
743
744 git_str_put(out, buf, eol - buf);
745 }
746
747 if (git_str_oom(out))
748 goto oom;
749
750 return 0;
751 }
752
753 git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field);
754 return GIT_ENOTFOUND;
755
756 malformed:
757 git_error_set(GIT_ERROR_OBJECT, "malformed header");
758 return -1;
759 oom:
760 git_error_set_oom();
761 return -1;
762 }
763
764 int git_commit_extract_signature(
765 git_buf *signature_out,
766 git_buf *signed_data_out,
767 git_repository *repo,
768 git_oid *commit_id,
769 const char *field)
770 {
771 git_str signature = GIT_STR_INIT, signed_data = GIT_STR_INIT;
772 int error;
773
774 if ((error = git_buf_tostr(&signature, signature_out)) < 0 ||
775 (error = git_buf_tostr(&signed_data, signed_data_out)) < 0 ||
776 (error = git_commit__extract_signature(&signature, &signed_data, repo, commit_id, field)) < 0 ||
777 (error = git_buf_fromstr(signature_out, &signature)) < 0 ||
778 (error = git_buf_fromstr(signed_data_out, &signed_data)) < 0)
779 goto done;
780
781 done:
782 git_str_dispose(&signature);
783 git_str_dispose(&signed_data);
784 return error;
785 }
786
787 int git_commit__extract_signature(
788 git_str *signature,
789 git_str *signed_data,
790 git_repository *repo,
791 git_oid *commit_id,
792 const char *field)
793 {
794 git_odb_object *obj;
795 git_odb *odb;
796 const char *buf;
797 const char *h, *eol;
798 int error;
799
800 git_str_clear(signature);
801 git_str_clear(signed_data);
802
803 if (!field)
804 field = "gpgsig";
805
806 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
807 return error;
808
809 if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
810 return error;
811
812 if (obj->cached.type != GIT_OBJECT_COMMIT) {
813 git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB");
814 error = GIT_ENOTFOUND;
815 goto cleanup;
816 }
817
818 buf = git_odb_object_data(obj);
819
820 while ((h = strchr(buf, '\n')) && h[1] != '\0') {
821 h++;
822 if (git__prefixcmp(buf, field)) {
823 if (git_str_put(signed_data, buf, h - buf) < 0)
824 return -1;
825
826 buf = h;
827 continue;
828 }
829
830 h = buf;
831 h += strlen(field);
832 eol = strchr(h, '\n');
833 if (h[0] != ' ') {
834 buf = h;
835 continue;
836 }
837 if (!eol)
838 goto malformed;
839
840 h++; /* skip the SP */
841
842 git_str_put(signature, h, eol - h);
843 if (git_str_oom(signature))
844 goto oom;
845
846 /* If the next line starts with SP, it's multi-line, we must continue */
847 while (eol[1] == ' ') {
848 git_str_putc(signature, '\n');
849 h = eol + 2;
850 eol = strchr(h, '\n');
851 if (!eol)
852 goto malformed;
853
854 git_str_put(signature, h, eol - h);
855 }
856
857 if (git_str_oom(signature))
858 goto oom;
859
860 error = git_str_puts(signed_data, eol+1);
861 git_odb_object_free(obj);
862 return error;
863 }
864
865 git_error_set(GIT_ERROR_OBJECT, "this commit is not signed");
866 error = GIT_ENOTFOUND;
867 goto cleanup;
868
869 malformed:
870 git_error_set(GIT_ERROR_OBJECT, "malformed header");
871 error = -1;
872 goto cleanup;
873 oom:
874 git_error_set_oom();
875 error = -1;
876 goto cleanup;
877
878 cleanup:
879 git_odb_object_free(obj);
880 git_str_clear(signature);
881 git_str_clear(signed_data);
882 return error;
883 }
884
885 int git_commit_create_buffer(
886 git_buf *out,
887 git_repository *repo,
888 const git_signature *author,
889 const git_signature *committer,
890 const char *message_encoding,
891 const char *message,
892 const git_tree *tree,
893 size_t parent_count,
894 const git_commit *parents[])
895 {
896 GIT_BUF_WRAP_PRIVATE(out, git_commit__create_buffer, repo,
897 author, committer, message_encoding, message,
898 tree, parent_count, parents);
899 }
900
901 int git_commit__create_buffer(
902 git_str *out,
903 git_repository *repo,
904 const git_signature *author,
905 const git_signature *committer,
906 const char *message_encoding,
907 const char *message,
908 const git_tree *tree,
909 size_t parent_count,
910 const git_commit *parents[])
911 {
912 int error;
913 commit_parent_data data = { parent_count, parents, repo };
914 git_array_oid_t parents_arr = GIT_ARRAY_INIT;
915 const git_oid *tree_id;
916
917 GIT_ASSERT_ARG(tree);
918 GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
919
920 tree_id = git_tree_id(tree);
921
922 if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
923 return error;
924
925 error = git_commit__create_buffer_internal(
926 out, author, committer,
927 message_encoding, message, tree_id,
928 &parents_arr);
929
930 git_array_clear(parents_arr);
931 return error;
932 }
933
934 /**
935 * Append to 'out' properly marking continuations when there's a newline in 'content'
936 */
937 static int format_header_field(git_str *out, const char *field, const char *content)
938 {
939 const char *lf;
940
941 GIT_ASSERT_ARG(out);
942 GIT_ASSERT_ARG(field);
943 GIT_ASSERT_ARG(content);
944
945 git_str_puts(out, field);
946 git_str_putc(out, ' ');
947
948 while ((lf = strchr(content, '\n')) != NULL) {
949 git_str_put(out, content, lf - content);
950 git_str_puts(out, "\n ");
951 content = lf + 1;
952 }
953
954 git_str_puts(out, content);
955 git_str_putc(out, '\n');
956
957 return git_str_oom(out) ? -1 : 0;
958 }
959
960 static const git_oid *commit_parent_from_commit(size_t n, void *payload)
961 {
962 const git_commit *commit = (const git_commit *) payload;
963
964 return git_array_get(commit->parent_ids, n);
965
966 }
967
968 int git_commit_create_with_signature(
969 git_oid *out,
970 git_repository *repo,
971 const char *commit_content,
972 const char *signature,
973 const char *signature_field)
974 {
975 git_odb *odb;
976 int error = 0;
977 const char *field;
978 const char *header_end;
979 git_str commit = GIT_STR_INIT;
980 git_commit *parsed;
981 git_array_oid_t parents = GIT_ARRAY_INIT;
982
983 /* The first step is to verify that all the tree and parents exist */
984 parsed = git__calloc(1, sizeof(git_commit));
985 GIT_ERROR_CHECK_ALLOC(parsed);
986 if (commit_parse(parsed, commit_content, strlen(commit_content), 0) < 0) {
987 error = -1;
988 goto cleanup;
989 }
990
991 if ((error = validate_tree_and_parents(&parents, repo, &parsed->tree_id, commit_parent_from_commit, parsed, NULL, true)) < 0)
992 goto cleanup;
993
994 git_array_clear(parents);
995
996 /* Then we start appending by identifying the end of the commit header */
997 header_end = strstr(commit_content, "\n\n");
998 if (!header_end) {
999 git_error_set(GIT_ERROR_INVALID, "malformed commit contents");
1000 error = -1;
1001 goto cleanup;
1002 }
1003
1004 /* The header ends after the first LF */
1005 header_end++;
1006 git_str_put(&commit, commit_content, header_end - commit_content);
1007
1008 if (signature != NULL) {
1009 field = signature_field ? signature_field : "gpgsig";
1010
1011 if ((error = format_header_field(&commit, field, signature)) < 0)
1012 goto cleanup;
1013 }
1014
1015 git_str_puts(&commit, header_end);
1016
1017 if (git_str_oom(&commit))
1018 return -1;
1019
1020 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
1021 goto cleanup;
1022
1023 if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0)
1024 goto cleanup;
1025
1026 cleanup:
1027 git_commit__free(parsed);
1028 git_str_dispose(&commit);
1029 return error;
1030 }
1031
1032 int git_commit_committer_with_mailmap(
1033 git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
1034 {
1035 return git_mailmap_resolve_signature(out, mailmap, commit->committer);
1036 }
1037
1038 int git_commit_author_with_mailmap(
1039 git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
1040 {
1041 return git_mailmap_resolve_signature(out, mailmap, commit->author);
1042 }