File Coverage

deps/libgit2/src/commit.c
Criterion Covered Total %
statement 227 442 51.3
branch 147 336 43.7
condition n/a
subroutine n/a
pod n/a
total 374 778 48.0


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