]> git.proxmox.com Git - libgit2.git/blame - src/commit.c
New upstream version 1.3.0+dfsg.1
[libgit2.git] / src / 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
72a3fe42 17#include "odb.h"
4f0adcd0 18#include "commit.h"
638c2ca4 19#include "signature.h"
458b9450 20#include "message.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
CMN
44static int git_commit__create_buffer_internal(
45 git_buf *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)
70 git_buf_printf(out, "encoding %s\n", message_encoding);
71
72 git_buf_putc(out, '\n');
217c029b 73
47cb42da
CMN
74 if (git_buf_puts(out, message) < 0)
75 goto on_error;
76
77 return 0;
78
79on_error:
ac3d33df 80 git_buf_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;
139 git_buf buf = GIT_BUF_INIT;
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);
ac3d33df 182 git_buf_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;
417f0abc 398
c25aa7cd
PP
399 GIT_ASSERT_ARG(commit);
400 GIT_ASSERT_ARG(data);
22a2d3d5 401
a6563619 402 buffer = buffer_start;
f094f905 403
a6563619
CMN
404 /* Allocate for one, which will allow not to realloc 90% of the time */
405 git_array_init_to_size(commit->parent_ids, 1);
ac3d33df 406 GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
eec95235 407
a6563619 408 /* The tree is always the first field */
22a2d3d5
UG
409 if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
410 if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
411 goto bad_buffer;
412 } else {
413 size_t tree_len = strlen("tree ") + GIT_OID_HEXSZ + 1;
414 if (buffer + tree_len > buffer_end)
415 goto bad_buffer;
416 buffer += tree_len;
417 }
225fe215 418
9b3577ed 419 /*
9b3577ed
VM
420 * TODO: commit grafts!
421 */
417f0abc 422
cfbe4be3 423 while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
9abc78ae 424 git_oid *new_id = git_array_alloc(commit->parent_ids);
ac3d33df 425 GIT_ERROR_CHECK_ALLOC(new_id);
73fe6a8e 426
cfbe4be3 427 git_oid_cpy(new_id, &parent_id);
9b3577ed 428 }
417f0abc 429
22a2d3d5
UG
430 if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
431 commit->author = git__malloc(sizeof(git_signature));
432 GIT_ERROR_CHECK_ALLOC(commit->author);
73fe6a8e 433
22a2d3d5
UG
434 if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
435 return -1;
436 }
52f2390b 437
65d69fe8 438 /* Some tools create multiple author fields, ignore the extra ones */
ac3d33df 439 while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) {
65d69fe8
CMN
440 if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0)
441 return -1;
442
443 git__free(dummy_sig.name);
444 git__free(dummy_sig.email);
445 }
446
58519018 447 /* Always parse the committer; we need the commit time */
638c2ca4 448 commit->committer = git__malloc(sizeof(git_signature));
ac3d33df 449 GIT_ERROR_CHECK_ALLOC(commit->committer);
73fe6a8e
VM
450
451 if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
452 return -1;
5ae2f0c0 453
22a2d3d5
UG
454 if (flags & GIT_COMMIT_PARSE_QUICK)
455 return 0;
456
f094f905
RB
457 /* Parse add'l header entries */
458 while (buffer < buffer_end) {
291090a0 459 const char *eoln = buffer;
a6563619
CMN
460 if (buffer[-1] == '\n' && buffer[0] == '\n')
461 break;
462
291090a0
RB
463 while (eoln < buffer_end && *eoln != '\n')
464 ++eoln;
5ae2f0c0 465
6c7cee42 466 if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
291090a0 467 buffer += strlen("encoding ");
5ae2f0c0 468
291090a0 469 commit->message_encoding = git__strndup(buffer, eoln - buffer);
ac3d33df 470 GIT_ERROR_CHECK_ALLOC(commit->message_encoding);
291090a0 471 }
5ae2f0c0 472
d47c6aab
CMN
473 if (eoln < buffer_end && *eoln == '\n')
474 ++eoln;
291090a0 475 buffer = eoln;
5ae2f0c0 476 }
58519018 477
a6563619
CMN
478 header_len = buffer - buffer_start;
479 commit->raw_header = git__strndup(buffer_start, header_len);
ac3d33df 480 GIT_ERROR_CHECK_ALLOC(commit->raw_header);
f094f905 481
a6563619
CMN
482 /* point "buffer" to data after header, +1 for the final LF */
483 buffer = buffer_start + header_len + 1;
52f2390b 484
f094f905 485 /* extract commit message */
a719ef5e 486 if (buffer <= buffer_end)
598f069b 487 commit->raw_message = git__strndup(buffer, buffer_end - buffer);
a719ef5e
PS
488 else
489 commit->raw_message = git__strdup("");
ac3d33df 490 GIT_ERROR_CHECK_ALLOC(commit->raw_message);
417f0abc 491
73fe6a8e
VM
492 return 0;
493
494bad_buffer:
ac3d33df 495 git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object");
73fe6a8e 496 return -1;
417f0abc 497}
4caa8962 498
22a2d3d5
UG
499int git_commit__parse_raw(void *commit, const char *data, size_t size)
500{
501 return commit_parse(commit, data, size, 0);
502}
503
504int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags)
505{
506 return commit_parse(commit, git_odb_object_data(odb_obj), git_odb_object_size(odb_obj), flags);
507}
508
ac3d33df
JK
509int git_commit__parse(void *_commit, git_odb_object *odb_obj)
510{
22a2d3d5 511 return git_commit__parse_ext(_commit, odb_obj, 0);
ac3d33df
JK
512}
513
c25aa7cd 514#define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
cfbe4be3 515 _rvalue git_commit_##_name(const git_commit *commit) \
0c3596f1 516 {\
c25aa7cd 517 GIT_ASSERT_ARG_WITH_RETVAL(commit, _invalid); \
6b2a1941 518 return _return; \
0c3596f1
VM
519 }
520
c25aa7cd
PP
521GIT_COMMIT_GETTER(const git_signature *, author, commit->author, NULL)
522GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer, NULL)
523GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message, NULL)
524GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding, NULL)
525GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header, NULL)
526GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time, INT64_MIN)
527GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset, -1)
528GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids), 0)
529GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id, NULL)
6b2a1941 530
598f069b 531const char *git_commit_message(const git_commit *commit)
532{
be0a1a79 533 const char *message;
598f069b 534
c25aa7cd 535 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
598f069b 536
be0a1a79
PH
537 message = commit->raw_message;
538
598f069b 539 /* trim leading newlines from raw message */
540 while (*message && *message == '\n')
541 ++message;
542
543 return message;
544}
545
300d192f
ET
546const char *git_commit_summary(git_commit *commit)
547{
548 git_buf summary = GIT_BUF_INIT;
549 const char *msg, *space;
f5f96a23 550 bool space_contains_newline = false;
300d192f 551
c25aa7cd 552 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
300d192f
ET
553
554 if (!commit->summary) {
555 for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
f5f96a23
SR
556 char next_character = msg[0];
557 /* stop processing at the end of the first paragraph */
558 if (next_character == '\n' && (!msg[1] || msg[1] == '\n'))
300d192f 559 break;
f5f96a23
SR
560 /* record the beginning of contiguous whitespace runs */
561 else if (git__isspace(next_character)) {
562 if(space == NULL) {
563 space = msg;
564 space_contains_newline = false;
565 }
566 space_contains_newline |= next_character == '\n';
567 }
568 /* the next character is non-space */
569 else {
570 /* process any recorded whitespace */
571 if (space) {
572 if(space_contains_newline)
573 git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
574 else
575 git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */
576 space = NULL;
577 }
578 /* copy the next character */
579 git_buf_putc(&summary, next_character);
580 }
300d192f
ET
581 }
582
80c29fe9
RB
583 commit->summary = git_buf_detach(&summary);
584 if (!commit->summary)
238e8149 585 commit->summary = git__strdup("");
300d192f
ET
586 }
587
588 return commit->summary;
589}
590
7f8fe1d4
PS
591const char *git_commit_body(git_commit *commit)
592{
593 const char *msg, *end;
594
c25aa7cd 595 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
7f8fe1d4
PS
596
597 if (!commit->body) {
598 /* search for end of summary */
599 for (msg = git_commit_message(commit); *msg; ++msg)
600 if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
601 break;
602
603 /* trim leading and trailing whitespace */
604 for (; *msg; ++msg)
605 if (!git__isspace(*msg))
606 break;
607 for (end = msg + strlen(msg) - 1; msg <= end; --end)
608 if (!git__isspace(*end))
609 break;
610
611 if (*msg)
22a2d3d5 612 commit->body = git__strndup(msg, end - msg + 1);
7f8fe1d4
PS
613 }
614
615 return commit->body;
616}
617
cfbe4be3 618int git_commit_tree(git_tree **tree_out, const git_commit *commit)
6b2a1941 619{
c25aa7cd 620 GIT_ASSERT_ARG(commit);
cfbe4be3 621 return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
6b2a1941 622}
57450775 623
58206c9a
RB
624const git_oid *git_commit_parent_id(
625 const git_commit *commit, unsigned int n)
2b92a154 626{
c25aa7cd 627 GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
2b92a154 628
9abc78ae 629 return git_array_get(commit->parent_ids, n);
2b92a154 630}
631
58206c9a
RB
632int git_commit_parent(
633 git_commit **parent, const git_commit *commit, unsigned int n)
48c27f86 634{
cfbe4be3 635 const git_oid *parent_id;
c25aa7cd 636 GIT_ASSERT_ARG(commit);
48c27f86 637
cfbe4be3
VM
638 parent_id = git_commit_parent_id(commit, n);
639 if (parent_id == NULL) {
ac3d33df 640 git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n);
3aa351ea
CMN
641 return GIT_ENOTFOUND;
642 }
48c27f86 643
cfbe4be3 644 return git_commit_lookup(parent, commit->object.repo, parent_id);
48c27f86 645}
b1aca6ea 646
647int git_commit_nth_gen_ancestor(
648 git_commit **ancestor,
649 const git_commit *commit,
650 unsigned int n)
651{
e583334c 652 git_commit *current, *parent = NULL;
b1aca6ea 653 int error;
654
c25aa7cd
PP
655 GIT_ASSERT_ARG(ancestor);
656 GIT_ASSERT_ARG(commit);
b1aca6ea 657
f0224772 658 if (git_commit_dup(&current, (git_commit *)commit) < 0)
7c1ee212 659 return -1;
b1aca6ea 660
7c1ee212
CMN
661 if (n == 0) {
662 *ancestor = current;
663 return 0;
664 }
b1aca6ea 665
666 while (n--) {
7c1ee212 667 error = git_commit_parent(&parent, current, 0);
b1aca6ea 668
7c1ee212 669 git_commit_free(current);
b1aca6ea 670
671 if (error < 0)
672 return error;
673
674 current = parent;
675 }
676
677 *ancestor = parent;
678 return 0;
679}
a3f42fe8
CMN
680
681int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
682{
f55eca16 683 const char *eol, *buf = commit->raw_header;
a3f42fe8 684
dc851d9e 685 git_buf_clear(out);
f55eca16 686
460ae11f 687 while ((eol = strchr(buf, '\n'))) {
f55eca16
CMN
688 /* We can skip continuations here */
689 if (buf[0] == ' ') {
690 buf = eol + 1;
691 continue;
692 }
693
694 /* Skip until we find the field we're after */
695 if (git__prefixcmp(buf, field)) {
696 buf = eol + 1;
a3f42fe8
CMN
697 continue;
698 }
699
f55eca16
CMN
700 buf += strlen(field);
701 /* Check that we're not matching a prefix but the field itself */
702 if (buf[0] != ' ') {
703 buf = eol + 1;
a3f42fe8
CMN
704 continue;
705 }
a3f42fe8 706
f55eca16 707 buf++; /* skip the SP */
a3f42fe8 708
f55eca16 709 git_buf_put(out, buf, eol - buf);
a3f42fe8
CMN
710 if (git_buf_oom(out))
711 goto oom;
712
713 /* If the next line starts with SP, it's multi-line, we must continue */
714 while (eol[1] == ' ') {
715 git_buf_putc(out, '\n');
f55eca16
CMN
716 buf = eol + 2;
717 eol = strchr(buf, '\n');
a3f42fe8
CMN
718 if (!eol)
719 goto malformed;
720
f55eca16 721 git_buf_put(out, buf, eol - buf);
a3f42fe8
CMN
722 }
723
724 if (git_buf_oom(out))
725 goto oom;
726
727 return 0;
728 }
729
ac3d33df 730 git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field);
a3f42fe8
CMN
731 return GIT_ENOTFOUND;
732
733malformed:
ac3d33df 734 git_error_set(GIT_ERROR_OBJECT, "malformed header");
a3f42fe8
CMN
735 return -1;
736oom:
ac3d33df 737 git_error_set_oom();
a3f42fe8
CMN
738 return -1;
739}
a65afb75
CMN
740
741int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
742{
743 git_odb_object *obj;
744 git_odb *odb;
745 const char *buf;
746 const char *h, *eol;
747 int error;
748
dc851d9e
PS
749 git_buf_clear(signature);
750 git_buf_clear(signed_data);
a65afb75
CMN
751
752 if (!field)
753 field = "gpgsig";
754
755 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
756 return error;
757
758 if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
759 return error;
760
ac3d33df 761 if (obj->cached.type != GIT_OBJECT_COMMIT) {
22a2d3d5 762 git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB");
eadd0f05
CMN
763 error = GIT_ENOTFOUND;
764 goto cleanup;
765 }
766
a65afb75
CMN
767 buf = git_odb_object_data(obj);
768
bf804d40 769 while ((h = strchr(buf, '\n')) && h[1] != '\0') {
a65afb75
CMN
770 h++;
771 if (git__prefixcmp(buf, field)) {
772 if (git_buf_put(signed_data, buf, h - buf) < 0)
773 return -1;
774
775 buf = h;
776 continue;
777 }
778
779 h = buf;
780 h += strlen(field);
781 eol = strchr(h, '\n');
782 if (h[0] != ' ') {
783 buf = h;
784 continue;
785 }
786 if (!eol)
787 goto malformed;
788
789 h++; /* skip the SP */
790
791 git_buf_put(signature, h, eol - h);
792 if (git_buf_oom(signature))
793 goto oom;
794
795 /* If the next line starts with SP, it's multi-line, we must continue */
796 while (eol[1] == ' ') {
797 git_buf_putc(signature, '\n');
798 h = eol + 2;
799 eol = strchr(h, '\n');
800 if (!eol)
801 goto malformed;
802
803 git_buf_put(signature, h, eol - h);
804 }
805
806 if (git_buf_oom(signature))
807 goto oom;
808
ade0d9c6 809 error = git_buf_puts(signed_data, eol+1);
a65afb75 810 git_odb_object_free(obj);
ade0d9c6 811 return error;
a65afb75
CMN
812 }
813
ac3d33df 814 git_error_set(GIT_ERROR_OBJECT, "this commit is not signed");
a65afb75
CMN
815 error = GIT_ENOTFOUND;
816 goto cleanup;
817
818malformed:
ac3d33df 819 git_error_set(GIT_ERROR_OBJECT, "malformed header");
a65afb75
CMN
820 error = -1;
821 goto cleanup;
822oom:
ac3d33df 823 git_error_set_oom();
a65afb75
CMN
824 error = -1;
825 goto cleanup;
826
827cleanup:
828 git_odb_object_free(obj);
829 git_buf_clear(signature);
830 git_buf_clear(signed_data);
831 return error;
832}
47cb42da
CMN
833
834int git_commit_create_buffer(git_buf *out,
835 git_repository *repo,
836 const git_signature *author,
837 const git_signature *committer,
838 const char *message_encoding,
839 const char *message,
840 const git_tree *tree,
841 size_t parent_count,
842 const git_commit *parents[])
843{
844 int error;
845 commit_parent_data data = { parent_count, parents, repo };
846 git_array_oid_t parents_arr = GIT_ARRAY_INIT;
847 const git_oid *tree_id;
848
c25aa7cd
PP
849 GIT_ASSERT_ARG(tree);
850 GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
47cb42da
CMN
851
852 tree_id = git_tree_id(tree);
853
854 if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
855 return error;
856
857 error = git_commit__create_buffer_internal(
0d77a56f 858 out, author, committer,
47cb42da
CMN
859 message_encoding, message, tree_id,
860 &parents_arr);
861
862 git_array_clear(parents_arr);
863 return error;
864}
02d61a3b
CMN
865
866/**
867 * Append to 'out' properly marking continuations when there's a newline in 'content'
868 */
c25aa7cd 869static int format_header_field(git_buf *out, const char *field, const char *content)
02d61a3b
CMN
870{
871 const char *lf;
872
c25aa7cd
PP
873 GIT_ASSERT_ARG(out);
874 GIT_ASSERT_ARG(field);
875 GIT_ASSERT_ARG(content);
02d61a3b
CMN
876
877 git_buf_puts(out, field);
878 git_buf_putc(out, ' ');
879
880 while ((lf = strchr(content, '\n')) != NULL) {
881 git_buf_put(out, content, lf - content);
882 git_buf_puts(out, "\n ");
883 content = lf + 1;
884 }
885
886 git_buf_puts(out, content);
887 git_buf_putc(out, '\n');
c25aa7cd
PP
888
889 return git_buf_oom(out) ? -1 : 0;
02d61a3b
CMN
890}
891
22a2d3d5
UG
892static const git_oid *commit_parent_from_commit(size_t n, void *payload)
893{
894 const git_commit *commit = (const git_commit *) payload;
895
896 return git_array_get(commit->parent_ids, n);
897
898}
899
02d61a3b
CMN
900int git_commit_create_with_signature(
901 git_oid *out,
902 git_repository *repo,
903 const char *commit_content,
904 const char *signature,
905 const char *signature_field)
906{
907 git_odb *odb;
908 int error = 0;
909 const char *field;
910 const char *header_end;
911 git_buf commit = GIT_BUF_INIT;
22a2d3d5
UG
912 git_commit *parsed;
913 git_array_oid_t parents = GIT_ARRAY_INIT;
914
915 /* The first step is to verify that all the tree and parents exist */
916 parsed = git__calloc(1, sizeof(git_commit));
917 GIT_ERROR_CHECK_ALLOC(parsed);
918 if ((error = commit_parse(parsed, commit_content, strlen(commit_content), 0)) < 0)
919 goto cleanup;
920
921 if ((error = validate_tree_and_parents(&parents, repo, &parsed->tree_id, commit_parent_from_commit, parsed, NULL, true)) < 0)
922 goto cleanup;
02d61a3b 923
22a2d3d5
UG
924 git_array_clear(parents);
925
926 /* Then we start appending by identifying the end of the commit header */
02d61a3b
CMN
927 header_end = strstr(commit_content, "\n\n");
928 if (!header_end) {
ac3d33df 929 git_error_set(GIT_ERROR_INVALID, "malformed commit contents");
22a2d3d5
UG
930 error = -1;
931 goto cleanup;
02d61a3b
CMN
932 }
933
02d61a3b
CMN
934 /* The header ends after the first LF */
935 header_end++;
936 git_buf_put(&commit, commit_content, header_end - commit_content);
22a2d3d5
UG
937
938 if (signature != NULL) {
939 field = signature_field ? signature_field : "gpgsig";
c25aa7cd
PP
940
941 if ((error = format_header_field(&commit, field, signature)) < 0)
942 goto cleanup;
22a2d3d5
UG
943 }
944
02d61a3b
CMN
945 git_buf_puts(&commit, header_end);
946
947 if (git_buf_oom(&commit))
948 return -1;
949
950 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
951 goto cleanup;
952
ac3d33df 953 if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0)
02d61a3b
CMN
954 goto cleanup;
955
956cleanup:
22a2d3d5 957 git_commit__free(parsed);
ac3d33df 958 git_buf_dispose(&commit);
02d61a3b
CMN
959 return error;
960}
ac3d33df
JK
961
962int git_commit_committer_with_mailmap(
963 git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
964{
965 return git_mailmap_resolve_signature(out, mailmap, commit->committer);
966}
967
968int git_commit_author_with_mailmap(
969 git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
970{
971 return git_mailmap_resolve_signature(out, mailmap, commit->author);
972}