File Coverage

deps/libgit2/src/libgit2/commit.c
Criterion Covered Total %
statement 202 474 42.6
branch 130 364 35.7
condition n/a
subroutine n/a
pod n/a
total 332 838 39.6


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