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 	assert(out && tree);
57 
58 	git_oid__writebuf(out, "tree ", tree);
59 
60 	for (i = 0; i < git_array_size(*parents); i++) {
61 		parent = git_array_get(*parents, i);
62 		git_oid__writebuf(out, "parent ", parent);
63 	}
64 
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');
72 
73 	if (git_buf_puts(out, message) < 0)
74 		goto on_error;
75 
76 	return 0;
77 
78 on_error:
79 	git_buf_dispose(out);
80 	return -1;
81 }
82 
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)83 static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
84 	git_commit_parent_callback parent_cb, void *parent_payload,
85 	const git_oid *current_id, bool validate)
86 {
87 	size_t i;
88 	int error;
89 	git_oid *parent_cpy;
90 	const git_oid *parent;
91 
92 	if (validate && !git_object__is_valid(repo, tree, GIT_OBJECT_TREE))
93 		return -1;
94 
95 	i = 0;
96 	while ((parent = parent_cb(i, parent_payload)) != NULL) {
97 		if (validate && !git_object__is_valid(repo, parent, GIT_OBJECT_COMMIT)) {
98 			error = -1;
99 			goto on_error;
100 		}
101 
102 		parent_cpy = git_array_alloc(*parents);
103 		GIT_ERROR_CHECK_ALLOC(parent_cpy);
104 
105 		git_oid_cpy(parent_cpy, parent);
106 		i++;
107 	}
108 
109 	if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
110 		git_error_set(GIT_ERROR_OBJECT, "failed to create commit: current tip is not the first parent");
111 		error = GIT_EMODIFIED;
112 		goto on_error;
113 	}
114 
115 	return 0;
116 
117 on_error:
118 	git_array_clear(*parents);
119 	return error;
120 }
121 
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)122 static 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;
141 
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 	}
147 	git_error_clear();
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 
155 	error = git_commit__create_buffer_internal(&buf, author, committer,
156 		message_encoding, message, tree,
157 		&parents);
158 
159 	if (error < 0)
160 		goto cleanup;
161 
162 	if (git_repository_odb__weakptr(&odb, repo) < 0)
163 		goto cleanup;
164 
165 	if (git_odb__freshen(odb, tree) < 0)
166 		goto cleanup;
167 
168 	if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT) < 0)
169 		goto cleanup;
170 
171 
172 	if (update_ref != NULL) {
173 		error = git_reference__update_for_commit(
174 			repo, ref, update_ref, id, "commit");
175 		goto cleanup;
176 	}
177 
178 cleanup:
179 	git_array_clear(parents);
180 	git_reference_free(ref);
181 	git_buf_dispose(&buf);
182 	return error;
183 }
184 
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)185 int 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 
202 typedef struct {
203 	size_t total;
204 	va_list args;
205 } commit_parent_varargs;
206 
commit_parent_from_varargs(size_t curr,void * payload)207 static 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 
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,...)217 int 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 
237 	error = git_commit__create_internal(
238 		id, repo, update_ref, author, committer,
239 		message_encoding, message, git_tree_id(tree),
240 		commit_parent_from_varargs, &data, false);
241 
242 	va_end(data.args);
243 	return error;
244 }
245 
246 typedef struct {
247 	size_t total;
248 	const git_oid **parents;
249 } commit_parent_oids;
250 
commit_parent_from_ids(size_t curr,void * payload)251 static 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 
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[])257 int 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 
271 	return git_commit__create_internal(
272 		id, repo, update_ref, author, committer,
273 		message_encoding, message, tree,
274 		commit_parent_from_ids, &data, true);
275 }
276 
277 typedef struct {
278 	size_t total;
279 	const git_commit **parents;
280 	git_repository *repo;
281 } commit_parent_data;
282 
commit_parent_from_array(size_t curr,void * payload)283 static 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 
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[])295 int git_commit_create(
296 	git_oid *id,
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,
304 	size_t parent_count,
305 	const git_commit *parents[])
306 {
307 	commit_parent_data data = { parent_count, parents, repo };
308 
309 	assert(tree && git_tree_owner(tree) == repo);
310 
311 	return git_commit__create_internal(
312 		id, repo, update_ref, author, committer,
313 		message_encoding, message, git_tree_id(tree),
314 		commit_parent_from_array, &data, false);
315 }
316 
commit_parent_for_amend(size_t curr,void * payload)317 static 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 }
324 
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)325 int 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;
337 	git_reference *ref;
338 	int error;
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;
355 		GIT_ERROR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
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 	}
362 
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);
369 			git_error_set(GIT_ERROR_REFERENCE, "commit to amend is not the tip of the given branch");
370 			return -1;
371 		}
372 	}
373 
374 	error = git_commit__create_internal(
375 		id, repo, NULL, author, committer, message_encoding, message,
376 		&tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
377 
378 	if (!error && update_ref) {
379 		error = git_reference__update_for_commit(
380 			repo, ref, NULL, id, "commit");
381 		git_reference_free(ref);
382 	}
383 
384 	return error;
385 }
386 
commit_parse(git_commit * commit,const char * data,size_t size,unsigned int flags)387 static int commit_parse(git_commit *commit, const char *data, size_t size, unsigned int flags)
388 {
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 	assert(commit && data);
396 
397 	buffer = buffer_start;
398 
399 	/* Allocate for one, which will allow not to realloc 90% of the time  */
400 	git_array_init_to_size(commit->parent_ids, 1);
401 	GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
402 
403 	/* The tree is always the first field */
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 	}
413 
414 	/*
415 	 * TODO: commit grafts!
416 	 */
417 
418 	while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
419 		git_oid *new_id = git_array_alloc(commit->parent_ids);
420 		GIT_ERROR_CHECK_ALLOC(new_id);
421 
422 		git_oid_cpy(new_id, &parent_id);
423 	}
424 
425 	if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
426 		commit->author = git__malloc(sizeof(git_signature));
427 		GIT_ERROR_CHECK_ALLOC(commit->author);
428 
429 		if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
430 			return -1;
431 	}
432 
433 	/* Some tools create multiple author fields, ignore the extra ones */
434 	while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) {
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 
442 	/* Always parse the committer; we need the commit time */
443 	commit->committer = git__malloc(sizeof(git_signature));
444 	GIT_ERROR_CHECK_ALLOC(commit->committer);
445 
446 	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
447 		return -1;
448 
449 	if (flags & GIT_COMMIT_PARSE_QUICK)
450 		return 0;
451 
452 	/* Parse add'l header entries */
453 	while (buffer < buffer_end) {
454 		const char *eoln = buffer;
455 		if (buffer[-1] == '\n' && buffer[0] == '\n')
456 			break;
457 
458 		while (eoln < buffer_end && *eoln != '\n')
459 			++eoln;
460 
461 		if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
462 			buffer += strlen("encoding ");
463 
464 			commit->message_encoding = git__strndup(buffer, eoln - buffer);
465 			GIT_ERROR_CHECK_ALLOC(commit->message_encoding);
466 		}
467 
468 		if (eoln < buffer_end && *eoln == '\n')
469 			++eoln;
470 		buffer = eoln;
471 	}
472 
473 	header_len = buffer - buffer_start;
474 	commit->raw_header = git__strndup(buffer_start, header_len);
475 	GIT_ERROR_CHECK_ALLOC(commit->raw_header);
476 
477 	/* point "buffer" to data after header, +1 for the final LF */
478 	buffer = buffer_start + header_len + 1;
479 
480 	/* extract commit message */
481 	if (buffer <= buffer_end)
482 		commit->raw_message = git__strndup(buffer, buffer_end - buffer);
483 	else
484 		commit->raw_message = git__strdup("");
485 	GIT_ERROR_CHECK_ALLOC(commit->raw_message);
486 
487 	return 0;
488 
489 bad_buffer:
490 	git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object");
491 	return -1;
492 }
493 
git_commit__parse_raw(void * commit,const char * data,size_t size)494 int git_commit__parse_raw(void *commit, const char *data, size_t size)
495 {
496 	return commit_parse(commit, data, size, 0);
497 }
498 
git_commit__parse_ext(git_commit * commit,git_odb_object * odb_obj,unsigned int flags)499 int 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 
git_commit__parse(void * _commit,git_odb_object * odb_obj)504 int git_commit__parse(void *_commit, git_odb_object *odb_obj)
505 {
506 	return git_commit__parse_ext(_commit, odb_obj, 0);
507 }
508 
509 #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
510 	_rvalue git_commit_##_name(const git_commit *commit) \
511 	{\
512 		assert(commit); \
513 		return _return; \
514 	}
515 
516 GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
517 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
518 GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
519 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
520 GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
521 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
522 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
523 GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
524 GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id)
525 
git_commit_message(const git_commit * commit)526 const char *git_commit_message(const git_commit *commit)
527 {
528 	const char *message;
529 
530 	assert(commit);
531 
532 	message = commit->raw_message;
533 
534 	/* trim leading newlines from raw message */
535 	while (*message && *message == '\n')
536 		++message;
537 
538 	return message;
539 }
540 
git_commit_summary(git_commit * commit)541 const char *git_commit_summary(git_commit *commit)
542 {
543 	git_buf summary = GIT_BUF_INIT;
544 	const char *msg, *space;
545 	bool space_contains_newline = false;
546 
547 	assert(commit);
548 
549 	if (!commit->summary) {
550 		for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
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'))
554 				break;
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 			}
576 		}
577 
578 		commit->summary = git_buf_detach(&summary);
579 		if (!commit->summary)
580 			commit->summary = git__strdup("");
581 	}
582 
583 	return commit->summary;
584 }
585 
git_commit_body(git_commit * commit)586 const 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)
607 			commit->body = git__strndup(msg, end - msg + 1);
608 	}
609 
610 	return commit->body;
611 }
612 
git_commit_tree(git_tree ** tree_out,const git_commit * commit)613 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
614 {
615 	assert(commit);
616 	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
617 }
618 
git_commit_parent_id(const git_commit * commit,unsigned int n)619 const git_oid *git_commit_parent_id(
620 	const git_commit *commit, unsigned int n)
621 {
622 	assert(commit);
623 
624 	return git_array_get(commit->parent_ids, n);
625 }
626 
git_commit_parent(git_commit ** parent,const git_commit * commit,unsigned int n)627 int git_commit_parent(
628 	git_commit **parent, const git_commit *commit, unsigned int n)
629 {
630 	const git_oid *parent_id;
631 	assert(commit);
632 
633 	parent_id = git_commit_parent_id(commit, n);
634 	if (parent_id == NULL) {
635 		git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n);
636 		return GIT_ENOTFOUND;
637 	}
638 
639 	return git_commit_lookup(parent, commit->object.repo, parent_id);
640 }
641 
git_commit_nth_gen_ancestor(git_commit ** ancestor,const git_commit * commit,unsigned int n)642 int git_commit_nth_gen_ancestor(
643 	git_commit **ancestor,
644 	const git_commit *commit,
645 	unsigned int n)
646 {
647 	git_commit *current, *parent = NULL;
648 	int error;
649 
650 	assert(ancestor && commit);
651 
652 	if (git_commit_dup(&current, (git_commit *)commit) < 0)
653 		return -1;
654 
655 	if (n == 0) {
656 		*ancestor = current;
657 		return 0;
658 	}
659 
660 	while (n--) {
661 		error = git_commit_parent(&parent, current, 0);
662 
663 		git_commit_free(current);
664 
665 		if (error < 0)
666 			return error;
667 
668 		current = parent;
669 	}
670 
671 	*ancestor = parent;
672 	return 0;
673 }
674 
git_commit_header_field(git_buf * out,const git_commit * commit,const char * field)675 int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
676 {
677 	const char *eol, *buf = commit->raw_header;
678 
679 	git_buf_clear(out);
680 
681 	while ((eol = strchr(buf, '\n'))) {
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;
691 			continue;
692 		}
693 
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;
698 			continue;
699 		}
700 
701 		buf++; /* skip the SP */
702 
703 		git_buf_put(out, buf, eol - buf);
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');
710 			buf = eol + 2;
711 			eol = strchr(buf, '\n');
712 			if (!eol)
713 				goto malformed;
714 
715 			git_buf_put(out, buf, eol - buf);
716 		}
717 
718 		if (git_buf_oom(out))
719 			goto oom;
720 
721 		return 0;
722 	}
723 
724 	git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field);
725 	return GIT_ENOTFOUND;
726 
727 malformed:
728 	git_error_set(GIT_ERROR_OBJECT, "malformed header");
729 	return -1;
730 oom:
731 	git_error_set_oom();
732 	return -1;
733 }
734 
git_commit_extract_signature(git_buf * signature,git_buf * signed_data,git_repository * repo,git_oid * commit_id,const char * field)735 int 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 
743 	git_buf_clear(signature);
744 	git_buf_clear(signed_data);
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 
755 	if (obj->cached.type != GIT_OBJECT_COMMIT) {
756 		git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB");
757 		error = GIT_ENOTFOUND;
758 		goto cleanup;
759 	}
760 
761 	buf = git_odb_object_data(obj);
762 
763 	while ((h = strchr(buf, '\n')) && h[1] != '\0') {
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 
803 		error = git_buf_puts(signed_data, eol+1);
804 		git_odb_object_free(obj);
805 		return error;
806 	}
807 
808 	git_error_set(GIT_ERROR_OBJECT, "this commit is not signed");
809 	error = GIT_ENOTFOUND;
810 	goto cleanup;
811 
812 malformed:
813 	git_error_set(GIT_ERROR_OBJECT, "malformed header");
814 	error = -1;
815 	goto cleanup;
816 oom:
817 	git_error_set_oom();
818 	error = -1;
819 	goto cleanup;
820 
821 cleanup:
822 	git_odb_object_free(obj);
823 	git_buf_clear(signature);
824 	git_buf_clear(signed_data);
825 	return error;
826 }
827 
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[])828 int 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(
851 		out, author, committer,
852 		message_encoding, message, tree_id,
853 		&parents_arr);
854 
855 	git_array_clear(parents_arr);
856 	return error;
857 }
858 
859 /**
860  * Append to 'out' properly marking continuations when there's a newline in 'content'
861  */
format_header_field(git_buf * out,const char * field,const char * content)862 static 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 
commit_parent_from_commit(size_t n,void * payload)881 static 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 
git_commit_create_with_signature(git_oid * out,git_repository * repo,const char * commit_content,const char * signature,const char * signature_field)889 int 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;
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;
912 
913 	git_array_clear(parents);
914 
915 	/* Then we start appending by identifying the end of the commit header */
916 	header_end = strstr(commit_content, "\n\n");
917 	if (!header_end) {
918 		git_error_set(GIT_ERROR_INVALID, "malformed commit contents");
919 		error = -1;
920 		goto cleanup;
921 	}
922 
923 	/* The header ends after the first LF */
924 	header_end++;
925 	git_buf_put(&commit, commit_content, header_end - commit_content);
926 
927 	if (signature != NULL) {
928 		field = signature_field ? signature_field : "gpgsig";
929 		format_header_field(&commit, field, signature);
930 	}
931 
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 
940 	if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0)
941 		goto cleanup;
942 
943 cleanup:
944 	git_commit__free(parsed);
945 	git_buf_dispose(&commit);
946 	return error;
947 }
948 
git_commit_committer_with_mailmap(git_signature ** out,const git_commit * commit,const git_mailmap * mailmap)949 int 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 
git_commit_author_with_mailmap(git_signature ** out,const git_commit * commit,const git_mailmap * mailmap)955 int 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 }
960