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