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 
git_commit__free(void * _commit)26 void git_commit__free(void *_commit)
27 {
28 	git_commit *commit = _commit;
29 
30 	git_array_clear(commit->parent_ids);
31 
32 	git_signature_free(commit->author);
33 	git_signature_free(commit->committer);
34 
35 	git__free(commit->raw_header);
36 	git__free(commit->raw_message);
37 	git__free(commit->message_encoding);
38 	git__free(commit->summary);
39 	git__free(commit->body);
40 
41 	git__free(commit);
42 }
43 
git_commit__create_buffer_internal(git_buf * out,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_oid * tree,git_array_oid_t * parents)44 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 	size_t i = 0;
54 	const git_oid *parent;
55 
56 	GIT_ASSERT_ARG(out);
57 	GIT_ASSERT_ARG(tree);
58 
59 	git_oid__writebuf(out, "tree ", tree);
60 
61 	for (i = 0; i < git_array_size(*parents); i++) {
62 		parent = git_array_get(*parents, i);
63 		git_oid__writebuf(out, "parent ", parent);
64 	}
65 
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');
73 
74 	if (git_buf_puts(out, message) < 0)
75 		goto on_error;
76 
77 	return 0;
78 
79 on_error:
80 	git_buf_dispose(out);
81 	return -1;
82 }
83 
validate_tree_and_parents(git_array_oid_t * parents,git_repository * repo,const git_oid * tree,git_commit_parent_callback parent_cb,void * parent_payload,const git_oid * current_id,bool validate)84 static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
85 	git_commit_parent_callback parent_cb, void *parent_payload,
86 	const git_oid *current_id, bool validate)
87 {
88 	size_t i;
89 	int error;
90 	git_oid *parent_cpy;
91 	const git_oid *parent;
92 
93 	if (validate && !git_object__is_valid(repo, tree, GIT_OBJECT_TREE))
94 		return -1;
95 
96 	i = 0;
97 	while ((parent = parent_cb(i, parent_payload)) != NULL) {
98 		if (validate && !git_object__is_valid(repo, parent, GIT_OBJECT_COMMIT)) {
99 			error = -1;
100 			goto on_error;
101 		}
102 
103 		parent_cpy = git_array_alloc(*parents);
104 		GIT_ERROR_CHECK_ALLOC(parent_cpy);
105 
106 		git_oid_cpy(parent_cpy, parent);
107 		i++;
108 	}
109 
110 	if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
111 		git_error_set(GIT_ERROR_OBJECT, "failed to create commit: current tip is not the first parent");
112 		error = GIT_EMODIFIED;
113 		goto on_error;
114 	}
115 
116 	return 0;
117 
118 on_error:
119 	git_array_clear(*parents);
120 	return error;
121 }
122 
git_commit__create_internal(git_oid * id,git_repository * repo,const char * update_ref,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_oid * tree,git_commit_parent_callback parent_cb,void * parent_payload,bool validate)123 static int git_commit__create_internal(
124 	git_oid *id,
125 	git_repository *repo,
126 	const char *update_ref,
127 	const git_signature *author,
128 	const git_signature *committer,
129 	const char *message_encoding,
130 	const char *message,
131 	const git_oid *tree,
132 	git_commit_parent_callback parent_cb,
133 	void *parent_payload,
134 	bool validate)
135 {
136 	int error;
137 	git_odb *odb;
138 	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;
142 
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 	}
148 	git_error_clear();
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 
156 	error = git_commit__create_buffer_internal(&buf, author, committer,
157 		message_encoding, message, tree,
158 		&parents);
159 
160 	if (error < 0)
161 		goto cleanup;
162 
163 	if (git_repository_odb__weakptr(&odb, repo) < 0)
164 		goto cleanup;
165 
166 	if (git_odb__freshen(odb, tree) < 0)
167 		goto cleanup;
168 
169 	if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT) < 0)
170 		goto cleanup;
171 
172 
173 	if (update_ref != NULL) {
174 		error = git_reference__update_for_commit(
175 			repo, ref, update_ref, id, "commit");
176 		goto cleanup;
177 	}
178 
179 cleanup:
180 	git_array_clear(parents);
181 	git_reference_free(ref);
182 	git_buf_dispose(&buf);
183 	return error;
184 }
185 
git_commit_create_from_callback(git_oid * id,git_repository * repo,const char * update_ref,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_oid * tree,git_commit_parent_callback parent_cb,void * parent_payload)186 int git_commit_create_from_callback(
187 	git_oid *id,
188 	git_repository *repo,
189 	const char *update_ref,
190 	const git_signature *author,
191 	const git_signature *committer,
192 	const char *message_encoding,
193 	const char *message,
194 	const git_oid *tree,
195 	git_commit_parent_callback parent_cb,
196 	void *parent_payload)
197 {
198 	return git_commit__create_internal(
199 		id, repo, update_ref, author, committer, message_encoding, message,
200 		tree, parent_cb, parent_payload, true);
201 }
202 
203 typedef struct {
204 	size_t total;
205 	va_list args;
206 } commit_parent_varargs;
207 
commit_parent_from_varargs(size_t curr,void * payload)208 static 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 
git_commit_create_v(git_oid * id,git_repository * repo,const char * update_ref,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_tree * tree,size_t parent_count,...)218 int git_commit_create_v(
219 	git_oid *id,
220 	git_repository *repo,
221 	const char *update_ref,
222 	const git_signature *author,
223 	const git_signature *committer,
224 	const char *message_encoding,
225 	const char *message,
226 	const git_tree *tree,
227 	size_t parent_count,
228 	...)
229 {
230 	int error = 0;
231 	commit_parent_varargs data;
232 
233 	GIT_ASSERT_ARG(tree);
234 	GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
235 
236 	data.total = parent_count;
237 	va_start(data.args, parent_count);
238 
239 	error = git_commit__create_internal(
240 		id, repo, update_ref, author, committer,
241 		message_encoding, message, git_tree_id(tree),
242 		commit_parent_from_varargs, &data, false);
243 
244 	va_end(data.args);
245 	return error;
246 }
247 
248 typedef struct {
249 	size_t total;
250 	const git_oid **parents;
251 } commit_parent_oids;
252 
commit_parent_from_ids(size_t curr,void * payload)253 static 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 
git_commit_create_from_ids(git_oid * id,git_repository * repo,const char * update_ref,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_oid * tree,size_t parent_count,const git_oid * parents[])259 int git_commit_create_from_ids(
260 	git_oid *id,
261 	git_repository *repo,
262 	const char *update_ref,
263 	const git_signature *author,
264 	const git_signature *committer,
265 	const char *message_encoding,
266 	const char *message,
267 	const git_oid *tree,
268 	size_t parent_count,
269 	const git_oid *parents[])
270 {
271 	commit_parent_oids data = { parent_count, parents };
272 
273 	return git_commit__create_internal(
274 		id, repo, update_ref, author, committer,
275 		message_encoding, message, tree,
276 		commit_parent_from_ids, &data, true);
277 }
278 
279 typedef struct {
280 	size_t total;
281 	const git_commit **parents;
282 	git_repository *repo;
283 } commit_parent_data;
284 
commit_parent_from_array(size_t curr,void * payload)285 static 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 
git_commit_create(git_oid * id,git_repository * repo,const char * update_ref,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_tree * tree,size_t parent_count,const git_commit * parents[])297 int git_commit_create(
298 	git_oid *id,
299 	git_repository *repo,
300 	const char *update_ref,
301 	const git_signature *author,
302 	const git_signature *committer,
303 	const char *message_encoding,
304 	const char *message,
305 	const git_tree *tree,
306 	size_t parent_count,
307 	const git_commit *parents[])
308 {
309 	commit_parent_data data = { parent_count, parents, repo };
310 
311 	GIT_ASSERT_ARG(tree);
312 	GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
313 
314 	return git_commit__create_internal(
315 		id, repo, update_ref, author, committer,
316 		message_encoding, message, git_tree_id(tree),
317 		commit_parent_from_array, &data, false);
318 }
319 
commit_parent_for_amend(size_t curr,void * payload)320 static 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 }
327 
git_commit_amend(git_oid * id,const git_commit * commit_to_amend,const char * update_ref,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_tree * tree)328 int git_commit_amend(
329 	git_oid *id,
330 	const git_commit *commit_to_amend,
331 	const char *update_ref,
332 	const git_signature *author,
333 	const git_signature *committer,
334 	const char *message_encoding,
335 	const char *message,
336 	const git_tree *tree)
337 {
338 	git_repository *repo;
339 	git_oid tree_id;
340 	git_reference *ref;
341 	int error;
342 
343 	GIT_ASSERT_ARG(id);
344 	GIT_ASSERT_ARG(commit_to_amend);
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;
359 		GIT_ERROR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
360 		git_oid_cpy(&tree_id, git_tree_id(old_tree));
361 		git_tree_free(old_tree);
362 	} else {
363 		GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
364 		git_oid_cpy(&tree_id, git_tree_id(tree));
365 	}
366 
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);
373 			git_error_set(GIT_ERROR_REFERENCE, "commit to amend is not the tip of the given branch");
374 			return -1;
375 		}
376 	}
377 
378 	error = git_commit__create_internal(
379 		id, repo, NULL, author, committer, message_encoding, message,
380 		&tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
381 
382 	if (!error && update_ref) {
383 		error = git_reference__update_for_commit(
384 			repo, ref, NULL, id, "commit");
385 		git_reference_free(ref);
386 	}
387 
388 	return error;
389 }
390 
commit_parse(git_commit * commit,const char * data,size_t size,unsigned int flags)391 static int commit_parse(git_commit *commit, const char *data, size_t size, unsigned int flags)
392 {
393 	const char *buffer_start = data, *buffer;
394 	const char *buffer_end = buffer_start + size;
395 	git_oid parent_id;
396 	size_t header_len;
397 	git_signature dummy_sig;
398 
399 	GIT_ASSERT_ARG(commit);
400 	GIT_ASSERT_ARG(data);
401 
402 	buffer = buffer_start;
403 
404 	/* Allocate for one, which will allow not to realloc 90% of the time  */
405 	git_array_init_to_size(commit->parent_ids, 1);
406 	GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
407 
408 	/* The tree is always the first field */
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 	}
418 
419 	/*
420 	 * TODO: commit grafts!
421 	 */
422 
423 	while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
424 		git_oid *new_id = git_array_alloc(commit->parent_ids);
425 		GIT_ERROR_CHECK_ALLOC(new_id);
426 
427 		git_oid_cpy(new_id, &parent_id);
428 	}
429 
430 	if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
431 		commit->author = git__malloc(sizeof(git_signature));
432 		GIT_ERROR_CHECK_ALLOC(commit->author);
433 
434 		if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
435 			return -1;
436 	}
437 
438 	/* Some tools create multiple author fields, ignore the extra ones */
439 	while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) {
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 
447 	/* Always parse the committer; we need the commit time */
448 	commit->committer = git__malloc(sizeof(git_signature));
449 	GIT_ERROR_CHECK_ALLOC(commit->committer);
450 
451 	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
452 		return -1;
453 
454 	if (flags & GIT_COMMIT_PARSE_QUICK)
455 		return 0;
456 
457 	/* Parse add'l header entries */
458 	while (buffer < buffer_end) {
459 		const char *eoln = buffer;
460 		if (buffer[-1] == '\n' && buffer[0] == '\n')
461 			break;
462 
463 		while (eoln < buffer_end && *eoln != '\n')
464 			++eoln;
465 
466 		if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
467 			buffer += strlen("encoding ");
468 
469 			commit->message_encoding = git__strndup(buffer, eoln - buffer);
470 			GIT_ERROR_CHECK_ALLOC(commit->message_encoding);
471 		}
472 
473 		if (eoln < buffer_end && *eoln == '\n')
474 			++eoln;
475 		buffer = eoln;
476 	}
477 
478 	header_len = buffer - buffer_start;
479 	commit->raw_header = git__strndup(buffer_start, header_len);
480 	GIT_ERROR_CHECK_ALLOC(commit->raw_header);
481 
482 	/* point "buffer" to data after header, +1 for the final LF */
483 	buffer = buffer_start + header_len + 1;
484 
485 	/* extract commit message */
486 	if (buffer <= buffer_end)
487 		commit->raw_message = git__strndup(buffer, buffer_end - buffer);
488 	else
489 		commit->raw_message = git__strdup("");
490 	GIT_ERROR_CHECK_ALLOC(commit->raw_message);
491 
492 	return 0;
493 
494 bad_buffer:
495 	git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object");
496 	return -1;
497 }
498 
git_commit__parse_raw(void * commit,const char * data,size_t size)499 int git_commit__parse_raw(void *commit, const char *data, size_t size)
500 {
501 	return commit_parse(commit, data, size, 0);
502 }
503 
git_commit__parse_ext(git_commit * commit,git_odb_object * odb_obj,unsigned int flags)504 int 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 
git_commit__parse(void * _commit,git_odb_object * odb_obj)509 int git_commit__parse(void *_commit, git_odb_object *odb_obj)
510 {
511 	return git_commit__parse_ext(_commit, odb_obj, 0);
512 }
513 
514 #define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
515 	_rvalue git_commit_##_name(const git_commit *commit) \
516 	{\
517 		GIT_ASSERT_ARG_WITH_RETVAL(commit, _invalid); \
518 		return _return; \
519 	}
520 
521 GIT_COMMIT_GETTER(const git_signature *, author, commit->author, NULL)
522 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer, NULL)
523 GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message, NULL)
524 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding, NULL)
525 GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header, NULL)
526 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time, INT64_MIN)
527 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset, -1)
528 GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids), 0)
529 GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id, NULL)
530 
git_commit_message(const git_commit * commit)531 const char *git_commit_message(const git_commit *commit)
532 {
533 	const char *message;
534 
535 	GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
536 
537 	message = commit->raw_message;
538 
539 	/* trim leading newlines from raw message */
540 	while (*message && *message == '\n')
541 		++message;
542 
543 	return message;
544 }
545 
git_commit_summary(git_commit * commit)546 const char *git_commit_summary(git_commit *commit)
547 {
548 	git_buf summary = GIT_BUF_INIT;
549 	const char *msg, *space;
550 	bool space_contains_newline = false;
551 
552 	GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
553 
554 	if (!commit->summary) {
555 		for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
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'))
559 				break;
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 			}
581 		}
582 
583 		commit->summary = git_buf_detach(&summary);
584 		if (!commit->summary)
585 			commit->summary = git__strdup("");
586 	}
587 
588 	return commit->summary;
589 }
590 
git_commit_body(git_commit * commit)591 const char *git_commit_body(git_commit *commit)
592 {
593 	const char *msg, *end;
594 
595 	GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
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)
612 			commit->body = git__strndup(msg, end - msg + 1);
613 	}
614 
615 	return commit->body;
616 }
617 
git_commit_tree(git_tree ** tree_out,const git_commit * commit)618 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
619 {
620 	GIT_ASSERT_ARG(commit);
621 	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
622 }
623 
git_commit_parent_id(const git_commit * commit,unsigned int n)624 const git_oid *git_commit_parent_id(
625 	const git_commit *commit, unsigned int n)
626 {
627 	GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
628 
629 	return git_array_get(commit->parent_ids, n);
630 }
631 
git_commit_parent(git_commit ** parent,const git_commit * commit,unsigned int n)632 int git_commit_parent(
633 	git_commit **parent, const git_commit *commit, unsigned int n)
634 {
635 	const git_oid *parent_id;
636 	GIT_ASSERT_ARG(commit);
637 
638 	parent_id = git_commit_parent_id(commit, n);
639 	if (parent_id == NULL) {
640 		git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n);
641 		return GIT_ENOTFOUND;
642 	}
643 
644 	return git_commit_lookup(parent, commit->object.repo, parent_id);
645 }
646 
git_commit_nth_gen_ancestor(git_commit ** ancestor,const git_commit * commit,unsigned int n)647 int git_commit_nth_gen_ancestor(
648 	git_commit **ancestor,
649 	const git_commit *commit,
650 	unsigned int n)
651 {
652 	git_commit *current, *parent = NULL;
653 	int error;
654 
655 	GIT_ASSERT_ARG(ancestor);
656 	GIT_ASSERT_ARG(commit);
657 
658 	if (git_commit_dup(&current, (git_commit *)commit) < 0)
659 		return -1;
660 
661 	if (n == 0) {
662 		*ancestor = current;
663 		return 0;
664 	}
665 
666 	while (n--) {
667 		error = git_commit_parent(&parent, current, 0);
668 
669 		git_commit_free(current);
670 
671 		if (error < 0)
672 			return error;
673 
674 		current = parent;
675 	}
676 
677 	*ancestor = parent;
678 	return 0;
679 }
680 
git_commit_header_field(git_buf * out,const git_commit * commit,const char * field)681 int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
682 {
683 	const char *eol, *buf = commit->raw_header;
684 
685 	git_buf_clear(out);
686 
687 	while ((eol = strchr(buf, '\n'))) {
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;
697 			continue;
698 		}
699 
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;
704 			continue;
705 		}
706 
707 		buf++; /* skip the SP */
708 
709 		git_buf_put(out, buf, eol - buf);
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');
716 			buf = eol + 2;
717 			eol = strchr(buf, '\n');
718 			if (!eol)
719 				goto malformed;
720 
721 			git_buf_put(out, buf, eol - buf);
722 		}
723 
724 		if (git_buf_oom(out))
725 			goto oom;
726 
727 		return 0;
728 	}
729 
730 	git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field);
731 	return GIT_ENOTFOUND;
732 
733 malformed:
734 	git_error_set(GIT_ERROR_OBJECT, "malformed header");
735 	return -1;
736 oom:
737 	git_error_set_oom();
738 	return -1;
739 }
740 
git_commit_extract_signature(git_buf * signature,git_buf * signed_data,git_repository * repo,git_oid * commit_id,const char * field)741 int 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 
749 	git_buf_clear(signature);
750 	git_buf_clear(signed_data);
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 
761 	if (obj->cached.type != GIT_OBJECT_COMMIT) {
762 		git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB");
763 		error = GIT_ENOTFOUND;
764 		goto cleanup;
765 	}
766 
767 	buf = git_odb_object_data(obj);
768 
769 	while ((h = strchr(buf, '\n')) && h[1] != '\0') {
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 
809 		error = git_buf_puts(signed_data, eol+1);
810 		git_odb_object_free(obj);
811 		return error;
812 	}
813 
814 	git_error_set(GIT_ERROR_OBJECT, "this commit is not signed");
815 	error = GIT_ENOTFOUND;
816 	goto cleanup;
817 
818 malformed:
819 	git_error_set(GIT_ERROR_OBJECT, "malformed header");
820 	error = -1;
821 	goto cleanup;
822 oom:
823 	git_error_set_oom();
824 	error = -1;
825 	goto cleanup;
826 
827 cleanup:
828 	git_odb_object_free(obj);
829 	git_buf_clear(signature);
830 	git_buf_clear(signed_data);
831 	return error;
832 }
833 
git_commit_create_buffer(git_buf * out,git_repository * repo,const git_signature * author,const git_signature * committer,const char * message_encoding,const char * message,const git_tree * tree,size_t parent_count,const git_commit * parents[])834 int 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 
849 	GIT_ASSERT_ARG(tree);
850 	GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
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(
858 		out, author, committer,
859 		message_encoding, message, tree_id,
860 		&parents_arr);
861 
862 	git_array_clear(parents_arr);
863 	return error;
864 }
865 
866 /**
867  * Append to 'out' properly marking continuations when there's a newline in 'content'
868  */
format_header_field(git_buf * out,const char * field,const char * content)869 static int format_header_field(git_buf *out, const char *field, const char *content)
870 {
871 	const char *lf;
872 
873 	GIT_ASSERT_ARG(out);
874 	GIT_ASSERT_ARG(field);
875 	GIT_ASSERT_ARG(content);
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');
888 
889 	return git_buf_oom(out) ? -1 : 0;
890 }
891 
commit_parent_from_commit(size_t n,void * payload)892 static 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 
git_commit_create_with_signature(git_oid * out,git_repository * repo,const char * commit_content,const char * signature,const char * signature_field)900 int 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;
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;
923 
924 	git_array_clear(parents);
925 
926 	/* Then we start appending by identifying the end of the commit header */
927 	header_end = strstr(commit_content, "\n\n");
928 	if (!header_end) {
929 		git_error_set(GIT_ERROR_INVALID, "malformed commit contents");
930 		error = -1;
931 		goto cleanup;
932 	}
933 
934 	/* The header ends after the first LF */
935 	header_end++;
936 	git_buf_put(&commit, commit_content, header_end - commit_content);
937 
938 	if (signature != NULL) {
939 		field = signature_field ? signature_field : "gpgsig";
940 
941 		if ((error = format_header_field(&commit, field, signature)) < 0)
942 			goto cleanup;
943 	}
944 
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 
953 	if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0)
954 		goto cleanup;
955 
956 cleanup:
957 	git_commit__free(parsed);
958 	git_buf_dispose(&commit);
959 	return error;
960 }
961 
git_commit_committer_with_mailmap(git_signature ** out,const git_commit * commit,const git_mailmap * mailmap)962 int 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 
git_commit_author_with_mailmap(git_signature ** out,const git_commit * commit,const git_mailmap * mailmap)968 int 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 }
973