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