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