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