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 "oidarray.h"
24 
git_commit__free(void * _commit)25 void git_commit__free(void *_commit)
26 {
27 	git_commit *commit = _commit;
28 
29 	git_array_clear(commit->parent_ids);
30 
31 	git_signature_free(commit->author);
32 	git_signature_free(commit->committer);
33 
34 	git__free(commit->raw_header);
35 	git__free(commit->raw_message);
36 	git__free(commit->message_encoding);
37 	git__free(commit->summary);
38 	git__free(commit->body);
39 
40 	git__free(commit);
41 }
42 
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)43 static int git_commit__create_buffer_internal(
44 	git_buf *out,
45 	const git_signature *author,
46 	const git_signature *committer,
47 	const char *message_encoding,
48 	const char *message,
49 	const git_oid *tree,
50 	git_array_oid_t *parents)
51 {
52 	size_t i = 0;
53 	const git_oid *parent;
54 
55 	assert(out && tree);
56 
57 	git_oid__writebuf(out, "tree ", tree);
58 
59 	for (i = 0; i < git_array_size(*parents); i++) {
60 		parent = git_array_get(*parents, i);
61 		git_oid__writebuf(out, "parent ", parent);
62 	}
63 
64 	git_signature__writebuf(out, "author ", author);
65 	git_signature__writebuf(out, "committer ", committer);
66 
67 	if (message_encoding != NULL)
68 		git_buf_printf(out, "encoding %s\n", message_encoding);
69 
70 	git_buf_putc(out, '\n');
71 
72 	if (git_buf_puts(out, message) < 0)
73 		goto on_error;
74 
75 	return 0;
76 
77 on_error:
78 	git_buf_dispose(out);
79 	return -1;
80 }
81 
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)82 static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
83 				     git_commit_parent_callback parent_cb, void *parent_payload,
84 				     const git_oid *current_id, bool validate)
85 {
86 	size_t i;
87 	int error;
88 	git_oid *parent_cpy;
89 	const git_oid *parent;
90 
91 	if (validate && !git_object__is_valid(repo, tree, GIT_OBJECT_TREE))
92 		return -1;
93 
94 	i = 0;
95 	while ((parent = parent_cb(i, parent_payload)) != NULL) {
96 		if (validate && !git_object__is_valid(repo, parent, GIT_OBJECT_COMMIT)) {
97 			error = -1;
98 			goto on_error;
99 		}
100 
101 		parent_cpy = git_array_alloc(*parents);
102 		GIT_ERROR_CHECK_ALLOC(parent_cpy);
103 
104 		git_oid_cpy(parent_cpy, parent);
105 		i++;
106 	}
107 
108 	if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
109 		git_error_set(GIT_ERROR_OBJECT, "failed to create commit: current tip is not the first parent");
110 		error = GIT_EMODIFIED;
111 		goto on_error;
112 	}
113 
114 	return 0;
115 
116 on_error:
117 	git_array_clear(*parents);
118 	return error;
119 }
120 
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)121 static int git_commit__create_internal(
122 	git_oid *id,
123 	git_repository *repo,
124 	const char *update_ref,
125 	const git_signature *author,
126 	const git_signature *committer,
127 	const char *message_encoding,
128 	const char *message,
129 	const git_oid *tree,
130 	git_commit_parent_callback parent_cb,
131 	void *parent_payload,
132 	bool validate)
133 {
134 	int error;
135 	git_odb *odb;
136 	git_reference *ref = NULL;
137 	git_buf buf = GIT_BUF_INIT;
138 	const git_oid *current_id = NULL;
139 	git_array_oid_t parents = GIT_ARRAY_INIT;
140 
141 	if (update_ref) {
142 		error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
143 		if (error < 0 && error != GIT_ENOTFOUND)
144 			return error;
145 	}
146 	git_error_clear();
147 
148 	if (ref)
149 		current_id = git_reference_target(ref);
150 
151 	if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
152 		goto cleanup;
153 
154 	error = git_commit__create_buffer_internal(&buf, author, committer,
155 						   message_encoding, message, tree,
156 						   &parents);
157 
158 	if (error < 0)
159 		goto cleanup;
160 
161 	if (git_repository_odb__weakptr(&odb, repo) < 0)
162 		goto cleanup;
163 
164 	if (git_odb__freshen(odb, tree) < 0)
165 		goto cleanup;
166 
167 	if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT) < 0)
168 		goto cleanup;
169 
170 
171 	if (update_ref != NULL) {
172 		error = git_reference__update_for_commit(
173 			repo, ref, update_ref, id, "commit");
174 		goto cleanup;
175 	}
176 
177 cleanup:
178 	git_array_clear(parents);
179 	git_reference_free(ref);
180 	git_buf_dispose(&buf);
181 	return error;
182 }
183 
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)184 int git_commit_create_from_callback(
185 	git_oid *id,
186 	git_repository *repo,
187 	const char *update_ref,
188 	const git_signature *author,
189 	const git_signature *committer,
190 	const char *message_encoding,
191 	const char *message,
192 	const git_oid *tree,
193 	git_commit_parent_callback parent_cb,
194 	void *parent_payload)
195 {
196 	return git_commit__create_internal(
197 		id, repo, update_ref, author, committer, message_encoding, message,
198 		tree, parent_cb, parent_payload, true);
199 }
200 
201 typedef struct {
202 	size_t total;
203 	va_list args;
204 } commit_parent_varargs;
205 
commit_parent_from_varargs(size_t curr,void * payload)206 static const git_oid *commit_parent_from_varargs(size_t curr, void *payload)
207 {
208 	commit_parent_varargs *data = payload;
209 	const git_commit *commit;
210 	if (curr >= data->total)
211 		return NULL;
212 	commit = va_arg(data->args, const git_commit *);
213 	return commit ? git_commit_id(commit) : NULL;
214 }
215 
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,...)216 int git_commit_create_v(
217 	git_oid *id,
218 	git_repository *repo,
219 	const char *update_ref,
220 	const git_signature *author,
221 	const git_signature *committer,
222 	const char *message_encoding,
223 	const char *message,
224 	const git_tree *tree,
225 	size_t parent_count,
226 	...)
227 {
228 	int error = 0;
229 	commit_parent_varargs data;
230 
231 	assert(tree && git_tree_owner(tree) == repo);
232 
233 	data.total = parent_count;
234 	va_start(data.args, parent_count);
235 
236 	error = git_commit__create_internal(
237 		id, repo, update_ref, author, committer,
238 		message_encoding, message, git_tree_id(tree),
239 		commit_parent_from_varargs, &data, false);
240 
241 	va_end(data.args);
242 	return error;
243 }
244 
245 typedef struct {
246 	size_t total;
247 	const git_oid **parents;
248 } commit_parent_oids;
249 
commit_parent_from_ids(size_t curr,void * payload)250 static const git_oid *commit_parent_from_ids(size_t curr, void *payload)
251 {
252 	commit_parent_oids *data = payload;
253 	return (curr < data->total) ? data->parents[curr] : NULL;
254 }
255 
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[])256 int git_commit_create_from_ids(
257 	git_oid *id,
258 	git_repository *repo,
259 	const char *update_ref,
260 	const git_signature *author,
261 	const git_signature *committer,
262 	const char *message_encoding,
263 	const char *message,
264 	const git_oid *tree,
265 	size_t parent_count,
266 	const git_oid *parents[])
267 {
268 	commit_parent_oids data = { parent_count, parents };
269 
270 	return git_commit__create_internal(
271 		id, repo, update_ref, author, committer,
272 		message_encoding, message, tree,
273 		commit_parent_from_ids, &data, true);
274 }
275 
276 typedef struct {
277 	size_t total;
278 	const git_commit **parents;
279 	git_repository *repo;
280 } commit_parent_data;
281 
commit_parent_from_array(size_t curr,void * payload)282 static const git_oid *commit_parent_from_array(size_t curr, void *payload)
283 {
284 	commit_parent_data *data = payload;
285 	const git_commit *commit;
286 	if (curr >= data->total)
287 		return NULL;
288 	commit = data->parents[curr];
289 	if (git_commit_owner(commit) != data->repo)
290 		return NULL;
291 	return git_commit_id(commit);
292 }
293 
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[])294 int git_commit_create(
295 	git_oid *id,
296 	git_repository *repo,
297 	const char *update_ref,
298 	const git_signature *author,
299 	const git_signature *committer,
300 	const char *message_encoding,
301 	const char *message,
302 	const git_tree *tree,
303 	size_t parent_count,
304 	const git_commit *parents[])
305 {
306 	commit_parent_data data = { parent_count, parents, repo };
307 
308 	assert(tree && git_tree_owner(tree) == repo);
309 
310 	return git_commit__create_internal(
311 		id, repo, update_ref, author, committer,
312 		message_encoding, message, git_tree_id(tree),
313 		commit_parent_from_array, &data, false);
314 }
315 
commit_parent_for_amend(size_t curr,void * payload)316 static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
317 {
318 	const git_commit *commit_to_amend = payload;
319 	if (curr >= git_array_size(commit_to_amend->parent_ids))
320 		return NULL;
321 	return git_array_get(commit_to_amend->parent_ids, curr);
322 }
323 
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)324 int git_commit_amend(
325 	git_oid *id,
326 	const git_commit *commit_to_amend,
327 	const char *update_ref,
328 	const git_signature *author,
329 	const git_signature *committer,
330 	const char *message_encoding,
331 	const char *message,
332 	const git_tree *tree)
333 {
334 	git_repository *repo;
335 	git_oid tree_id;
336 	git_reference *ref;
337 	int error;
338 
339 	assert(id && commit_to_amend);
340 
341 	repo = git_commit_owner(commit_to_amend);
342 
343 	if (!author)
344 		author = git_commit_author(commit_to_amend);
345 	if (!committer)
346 		committer = git_commit_committer(commit_to_amend);
347 	if (!message_encoding)
348 		message_encoding = git_commit_message_encoding(commit_to_amend);
349 	if (!message)
350 		message = git_commit_message(commit_to_amend);
351 
352 	if (!tree) {
353 		git_tree *old_tree;
354 		GIT_ERROR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
355 		git_oid_cpy(&tree_id, git_tree_id(old_tree));
356 		git_tree_free(old_tree);
357 	} else {
358 		assert(git_tree_owner(tree) == repo);
359 		git_oid_cpy(&tree_id, git_tree_id(tree));
360 	}
361 
362 	if (update_ref) {
363 		if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0)
364 			return error;
365 
366 		if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) {
367 			git_reference_free(ref);
368 			git_error_set(GIT_ERROR_REFERENCE, "commit to amend is not the tip of the given branch");
369 			return -1;
370 		}
371 	}
372 
373 	error = git_commit__create_internal(
374 		id, repo, NULL, author, committer, message_encoding, message,
375 		&tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
376 
377 	if (!error && update_ref) {
378 		error = git_reference__update_for_commit(
379 			repo, ref, NULL, id, "commit");
380 		git_reference_free(ref);
381 	}
382 
383 	return error;
384 }
385 
git_commit__parse_raw(void * _commit,const char * data,size_t size)386 int git_commit__parse_raw(void *_commit, const char *data, size_t size)
387 {
388 	git_commit *commit = _commit;
389 	const char *buffer_start = data, *buffer;
390 	const char *buffer_end = buffer_start + size;
391 	git_oid parent_id;
392 	size_t header_len;
393 	git_signature dummy_sig;
394 
395 	buffer = buffer_start;
396 
397 	/* Allocate for one, which will allow not to realloc 90% of the time  */
398 	git_array_init_to_size(commit->parent_ids, 1);
399 	GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
400 
401 	/* The tree is always the first field */
402 	if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
403 		goto bad_buffer;
404 
405 	/*
406 	 * TODO: commit grafts!
407 	 */
408 
409 	while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
410 		git_oid *new_id = git_array_alloc(commit->parent_ids);
411 		GIT_ERROR_CHECK_ALLOC(new_id);
412 
413 		git_oid_cpy(new_id, &parent_id);
414 	}
415 
416 	commit->author = git__malloc(sizeof(git_signature));
417 	GIT_ERROR_CHECK_ALLOC(commit->author);
418 
419 	if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
420 		return -1;
421 
422 	/* Some tools create multiple author fields, ignore the extra ones */
423 	while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) {
424 		if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0)
425 			return -1;
426 
427 		git__free(dummy_sig.name);
428 		git__free(dummy_sig.email);
429 	}
430 
431 	/* Always parse the committer; we need the commit time */
432 	commit->committer = git__malloc(sizeof(git_signature));
433 	GIT_ERROR_CHECK_ALLOC(commit->committer);
434 
435 	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
436 		return -1;
437 
438 	/* Parse add'l header entries */
439 	while (buffer < buffer_end) {
440 		const char *eoln = buffer;
441 		if (buffer[-1] == '\n' && buffer[0] == '\n')
442 			break;
443 
444 		while (eoln < buffer_end && *eoln != '\n')
445 			++eoln;
446 
447 		if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
448 			buffer += strlen("encoding ");
449 
450 			commit->message_encoding = git__strndup(buffer, eoln - buffer);
451 			GIT_ERROR_CHECK_ALLOC(commit->message_encoding);
452 		}
453 
454 		if (eoln < buffer_end && *eoln == '\n')
455 			++eoln;
456 		buffer = eoln;
457 	}
458 
459 	header_len = buffer - buffer_start;
460 	commit->raw_header = git__strndup(buffer_start, header_len);
461 	GIT_ERROR_CHECK_ALLOC(commit->raw_header);
462 
463 	/* point "buffer" to data after header, +1 for the final LF */
464 	buffer = buffer_start + header_len + 1;
465 
466 	/* extract commit message */
467 	if (buffer <= buffer_end)
468 		commit->raw_message = git__strndup(buffer, buffer_end - buffer);
469 	else
470 		commit->raw_message = git__strdup("");
471 	GIT_ERROR_CHECK_ALLOC(commit->raw_message);
472 
473 	return 0;
474 
475 bad_buffer:
476 	git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object");
477 	return -1;
478 }
479 
git_commit__parse(void * _commit,git_odb_object * odb_obj)480 int git_commit__parse(void *_commit, git_odb_object *odb_obj)
481 {
482 	return git_commit__parse_raw(_commit,
483 		git_odb_object_data(odb_obj),
484 		git_odb_object_size(odb_obj));
485 }
486 
487 #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
488 	_rvalue git_commit_##_name(const git_commit *commit) \
489 	{\
490 		assert(commit); \
491 		return _return; \
492 	}
493 
494 GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
495 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
496 GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
497 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
498 GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
499 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
500 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
501 GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
502 GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id)
503 
git_commit_message(const git_commit * commit)504 const char *git_commit_message(const git_commit *commit)
505 {
506 	const char *message;
507 
508 	assert(commit);
509 
510 	message = commit->raw_message;
511 
512 	/* trim leading newlines from raw message */
513 	while (*message && *message == '\n')
514 		++message;
515 
516 	return message;
517 }
518 
git_commit_summary(git_commit * commit)519 const char *git_commit_summary(git_commit *commit)
520 {
521 	git_buf summary = GIT_BUF_INIT;
522 	const char *msg, *space;
523 	bool space_contains_newline = false;
524 
525 	assert(commit);
526 
527 	if (!commit->summary) {
528 		for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
529 			char next_character = msg[0];
530 			/* stop processing at the end of the first paragraph */
531 			if (next_character == '\n' && (!msg[1] || msg[1] == '\n'))
532 				break;
533 			/* record the beginning of contiguous whitespace runs */
534 			else if (git__isspace(next_character)) {
535 				if(space == NULL) {
536 					space = msg;
537 					space_contains_newline = false;
538 				}
539 				space_contains_newline |= next_character == '\n';
540 			}
541 			/* the next character is non-space */
542 			else {
543 				/* process any recorded whitespace */
544 				if (space) {
545 					if(space_contains_newline)
546 						git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
547 					else
548 						git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */
549 					space = NULL;
550 				}
551 				/* copy the next character */
552 				git_buf_putc(&summary, next_character);
553 			}
554 		}
555 
556 		commit->summary = git_buf_detach(&summary);
557 		if (!commit->summary)
558 			commit->summary = git__strdup("");
559 	}
560 
561 	return commit->summary;
562 }
563 
git_commit_body(git_commit * commit)564 const char *git_commit_body(git_commit *commit)
565 {
566 	const char *msg, *end;
567 
568 	assert(commit);
569 
570 	if (!commit->body) {
571 		/* search for end of summary */
572 		for (msg = git_commit_message(commit); *msg; ++msg)
573 			if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
574 				break;
575 
576 		/* trim leading and trailing whitespace */
577 		for (; *msg; ++msg)
578 			if (!git__isspace(*msg))
579 				break;
580 		for (end = msg + strlen(msg) - 1; msg <= end; --end)
581 			if (!git__isspace(*end))
582 				break;
583 
584 		if (*msg)
585 			    commit->body = git__strndup(msg, end - msg + 1);
586 	}
587 
588 	return commit->body;
589 }
590 
git_commit_tree(git_tree ** tree_out,const git_commit * commit)591 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
592 {
593 	assert(commit);
594 	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
595 }
596 
git_commit_parent_id(const git_commit * commit,unsigned int n)597 const git_oid *git_commit_parent_id(
598 	const git_commit *commit, unsigned int n)
599 {
600 	assert(commit);
601 
602 	return git_array_get(commit->parent_ids, n);
603 }
604 
git_commit_parent(git_commit ** parent,const git_commit * commit,unsigned int n)605 int git_commit_parent(
606 	git_commit **parent, const git_commit *commit, unsigned int n)
607 {
608 	const git_oid *parent_id;
609 	assert(commit);
610 
611 	parent_id = git_commit_parent_id(commit, n);
612 	if (parent_id == NULL) {
613 		git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n);
614 		return GIT_ENOTFOUND;
615 	}
616 
617 	return git_commit_lookup(parent, commit->object.repo, parent_id);
618 }
619 
git_commit_nth_gen_ancestor(git_commit ** ancestor,const git_commit * commit,unsigned int n)620 int git_commit_nth_gen_ancestor(
621 	git_commit **ancestor,
622 	const git_commit *commit,
623 	unsigned int n)
624 {
625 	git_commit *current, *parent = NULL;
626 	int error;
627 
628 	assert(ancestor && commit);
629 
630 	if (git_commit_dup(&current, (git_commit *)commit) < 0)
631 		return -1;
632 
633 	if (n == 0) {
634 		*ancestor = current;
635 		return 0;
636 	}
637 
638 	while (n--) {
639 		error = git_commit_parent(&parent, current, 0);
640 
641 		git_commit_free(current);
642 
643 		if (error < 0)
644 			return error;
645 
646 		current = parent;
647 	}
648 
649 	*ancestor = parent;
650 	return 0;
651 }
652 
git_commit_header_field(git_buf * out,const git_commit * commit,const char * field)653 int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
654 {
655 	const char *eol, *buf = commit->raw_header;
656 
657 	git_buf_clear(out);
658 
659 	while ((eol = strchr(buf, '\n'))) {
660 		/* We can skip continuations here */
661 		if (buf[0] == ' ') {
662 			buf = eol + 1;
663 			continue;
664 		}
665 
666 		/* Skip until we find the field we're after */
667 		if (git__prefixcmp(buf, field)) {
668 			buf = eol + 1;
669 			continue;
670 		}
671 
672 		buf += strlen(field);
673 		/* Check that we're not matching a prefix but the field itself */
674 		if (buf[0] != ' ') {
675 			buf = eol + 1;
676 			continue;
677 		}
678 
679 		buf++; /* skip the SP */
680 
681 		git_buf_put(out, buf, eol - buf);
682 		if (git_buf_oom(out))
683 			goto oom;
684 
685 		/* If the next line starts with SP, it's multi-line, we must continue */
686 		while (eol[1] == ' ') {
687 			git_buf_putc(out, '\n');
688 			buf = eol + 2;
689 			eol = strchr(buf, '\n');
690 			if (!eol)
691 				goto malformed;
692 
693 			git_buf_put(out, buf, eol - buf);
694 		}
695 
696 		if (git_buf_oom(out))
697 			goto oom;
698 
699 		return 0;
700 	}
701 
702 	git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field);
703 	return GIT_ENOTFOUND;
704 
705 malformed:
706 	git_error_set(GIT_ERROR_OBJECT, "malformed header");
707 	return -1;
708 oom:
709 	git_error_set_oom();
710 	return -1;
711 }
712 
git_commit_extract_signature(git_buf * signature,git_buf * signed_data,git_repository * repo,git_oid * commit_id,const char * field)713 int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
714 {
715 	git_odb_object *obj;
716 	git_odb *odb;
717 	const char *buf;
718 	const char *h, *eol;
719 	int error;
720 
721 	git_buf_clear(signature);
722 	git_buf_clear(signed_data);
723 
724 	if (!field)
725 		field = "gpgsig";
726 
727 	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
728 		return error;
729 
730 	if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
731 		return error;
732 
733 	if (obj->cached.type != GIT_OBJECT_COMMIT) {
734 		git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in ODB");
735 		error = GIT_ENOTFOUND;
736 		goto cleanup;
737 	}
738 
739 	buf = git_odb_object_data(obj);
740 
741 	while ((h = strchr(buf, '\n')) && h[1] != '\0') {
742 		h++;
743 		if (git__prefixcmp(buf, field)) {
744 			if (git_buf_put(signed_data, buf, h - buf) < 0)
745 				return -1;
746 
747 			buf = h;
748 			continue;
749 		}
750 
751 		h = buf;
752 		h += strlen(field);
753 		eol = strchr(h, '\n');
754 		if (h[0] != ' ') {
755 			buf = h;
756 			continue;
757 		}
758 		if (!eol)
759 			goto malformed;
760 
761 		h++; /* skip the SP */
762 
763 		git_buf_put(signature, h, eol - h);
764 		if (git_buf_oom(signature))
765 			goto oom;
766 
767 		/* If the next line starts with SP, it's multi-line, we must continue */
768 		while (eol[1] == ' ') {
769 			git_buf_putc(signature, '\n');
770 			h = eol + 2;
771 			eol = strchr(h, '\n');
772 			if (!eol)
773 				goto malformed;
774 
775 			git_buf_put(signature, h, eol - h);
776 		}
777 
778 		if (git_buf_oom(signature))
779 			goto oom;
780 
781 		error = git_buf_puts(signed_data, eol+1);
782 		git_odb_object_free(obj);
783 		return error;
784 	}
785 
786 	git_error_set(GIT_ERROR_OBJECT, "this commit is not signed");
787 	error = GIT_ENOTFOUND;
788 	goto cleanup;
789 
790 malformed:
791 	git_error_set(GIT_ERROR_OBJECT, "malformed header");
792 	error = -1;
793 	goto cleanup;
794 oom:
795 	git_error_set_oom();
796 	error = -1;
797 	goto cleanup;
798 
799 cleanup:
800 	git_odb_object_free(obj);
801 	git_buf_clear(signature);
802 	git_buf_clear(signed_data);
803 	return error;
804 }
805 
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[])806 int git_commit_create_buffer(git_buf *out,
807 	git_repository *repo,
808 	const git_signature *author,
809 	const git_signature *committer,
810 	const char *message_encoding,
811 	const char *message,
812 	const git_tree *tree,
813 	size_t parent_count,
814 	const git_commit *parents[])
815 {
816 	int error;
817 	commit_parent_data data = { parent_count, parents, repo };
818 	git_array_oid_t parents_arr = GIT_ARRAY_INIT;
819 	const git_oid *tree_id;
820 
821 	assert(tree && git_tree_owner(tree) == repo);
822 
823 	tree_id = git_tree_id(tree);
824 
825 	if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
826 		return error;
827 
828 	error = git_commit__create_buffer_internal(
829 		out, author, committer,
830 		message_encoding, message, tree_id,
831 		&parents_arr);
832 
833 	git_array_clear(parents_arr);
834 	return error;
835 }
836 
837 /**
838  * Append to 'out' properly marking continuations when there's a newline in 'content'
839  */
format_header_field(git_buf * out,const char * field,const char * content)840 static void format_header_field(git_buf *out, const char *field, const char *content)
841 {
842 	const char *lf;
843 
844 	assert(out && field && content);
845 
846 	git_buf_puts(out, field);
847 	git_buf_putc(out, ' ');
848 
849 	while ((lf = strchr(content, '\n')) != NULL) {
850 		git_buf_put(out, content, lf - content);
851 		git_buf_puts(out, "\n ");
852 		content = lf + 1;
853 	}
854 
855 	git_buf_puts(out, content);
856 	git_buf_putc(out, '\n');
857 }
858 
git_commit_create_with_signature(git_oid * out,git_repository * repo,const char * commit_content,const char * signature,const char * signature_field)859 int git_commit_create_with_signature(
860 	git_oid *out,
861 	git_repository *repo,
862 	const char *commit_content,
863 	const char *signature,
864 	const char *signature_field)
865 {
866 	git_odb *odb;
867 	int error = 0;
868 	const char *field;
869 	const char *header_end;
870 	git_buf commit = GIT_BUF_INIT;
871 
872 	/* We start by identifying the end of the commit header */
873 	header_end = strstr(commit_content, "\n\n");
874 	if (!header_end) {
875 		git_error_set(GIT_ERROR_INVALID, "malformed commit contents");
876 		return -1;
877 	}
878 
879 	field = signature_field ? signature_field : "gpgsig";
880 
881 	/* The header ends after the first LF */
882 	header_end++;
883 	git_buf_put(&commit, commit_content, header_end - commit_content);
884 	format_header_field(&commit, field, signature);
885 	git_buf_puts(&commit, header_end);
886 
887 	if (git_buf_oom(&commit))
888 		return -1;
889 
890 	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
891 		goto cleanup;
892 
893 	if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0)
894 		goto cleanup;
895 
896 cleanup:
897 	git_buf_dispose(&commit);
898 	return error;
899 }
900 
git_commit_committer_with_mailmap(git_signature ** out,const git_commit * commit,const git_mailmap * mailmap)901 int git_commit_committer_with_mailmap(
902 	git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
903 {
904 	return git_mailmap_resolve_signature(out, mailmap, commit->committer);
905 }
906 
git_commit_author_with_mailmap(git_signature ** out,const git_commit * commit,const git_mailmap * mailmap)907 int git_commit_author_with_mailmap(
908 	git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
909 {
910 	return git_mailmap_resolve_signature(out, mailmap, commit->author);
911 }
912