Blame src/commit.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#include "git2/common.h"
Packit Service 20376f
#include "git2/object.h"
Packit Service 20376f
#include "git2/repository.h"
Packit Service 20376f
#include "git2/signature.h"
Packit Service 20376f
#include "git2/sys/commit.h"
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include "odb.h"
Packit Service 20376f
#include "commit.h"
Packit Service 20376f
#include "signature.h"
Packit Service 20376f
#include "message.h"
Packit Service 20376f
#include "refs.h"
Packit Service 20376f
#include "object.h"
Packit Service 20376f
#include "oidarray.h"
Packit Service 20376f
Packit Service 20376f
void git_commit__free(void *_commit)
Packit Service 20376f
{
Packit Service 20376f
	git_commit *commit = _commit;
Packit Service 20376f
Packit Service 20376f
	git_array_clear(commit->parent_ids);
Packit Service 20376f
Packit Service 20376f
	git_signature_free(commit->author);
Packit Service 20376f
	git_signature_free(commit->committer);
Packit Service 20376f
Packit Service 20376f
	git__free(commit->raw_header);
Packit Service 20376f
	git__free(commit->raw_message);
Packit Service 20376f
	git__free(commit->message_encoding);
Packit Service 20376f
	git__free(commit->summary);
Packit Service 20376f
	git__free(commit->body);
Packit Service 20376f
Packit Service 20376f
	git__free(commit);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int git_commit__create_buffer_internal(
Packit Service 20376f
	git_buf *out,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_oid *tree,
Packit Service 20376f
	git_array_oid_t *parents)
Packit Service 20376f
{
Packit Service 20376f
	size_t i = 0;
Packit Service 20376f
	const git_oid *parent;
Packit Service 20376f
Packit Service 20376f
	assert(out && tree);
Packit Service 20376f
Packit Service 20376f
	git_oid__writebuf(out, "tree ", tree);
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < git_array_size(*parents); i++) {
Packit Service 20376f
		parent = git_array_get(*parents, i);
Packit Service 20376f
		git_oid__writebuf(out, "parent ", parent);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_signature__writebuf(out, "author ", author);
Packit Service 20376f
	git_signature__writebuf(out, "committer ", committer);
Packit Service 20376f
Packit Service 20376f
	if (message_encoding != NULL)
Packit Service 20376f
		git_buf_printf(out, "encoding %s\n", message_encoding);
Packit Service 20376f
Packit Service 20376f
	git_buf_putc(out, '\n');
Packit Service 20376f
Packit Service 20376f
	if (git_buf_puts(out, message) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_buf_free(out);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
Packit Service 20376f
				     git_commit_parent_callback parent_cb, void *parent_payload,
Packit Service 20376f
				     const git_oid *current_id, bool validate)
Packit Service 20376f
{
Packit Service 20376f
	size_t i;
Packit Service 20376f
	int error;
Packit Service 20376f
	git_oid *parent_cpy;
Packit Service 20376f
	const git_oid *parent;
Packit Service 20376f
Packit Service 20376f
	if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	i = 0;
Packit Service 20376f
	while ((parent = parent_cb(i, parent_payload)) != NULL) {
Packit Service 20376f
		if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
Packit Service 20376f
			error = -1;
Packit Service 20376f
			goto on_error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		parent_cpy = git_array_alloc(*parents);
Packit Service 20376f
		GITERR_CHECK_ALLOC(parent_cpy);
Packit Service 20376f
Packit Service 20376f
		git_oid_cpy(parent_cpy, parent);
Packit Service 20376f
		i++;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
Packit Service 20376f
		giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
Packit Service 20376f
		error = GIT_EMODIFIED;
Packit Service 20376f
		goto on_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git_array_clear(*parents);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int git_commit__create_internal(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *update_ref,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_oid *tree,
Packit Service 20376f
	git_commit_parent_callback parent_cb,
Packit Service 20376f
	void *parent_payload,
Packit Service 20376f
	bool validate)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_odb *odb;
Packit Service 20376f
	git_reference *ref = NULL;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	const git_oid *current_id = NULL;
Packit Service 20376f
	git_array_oid_t parents = GIT_ARRAY_INIT;
Packit Service 20376f
Packit Service 20376f
	if (update_ref) {
Packit Service 20376f
		error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
Packit Service 20376f
		if (error < 0 && error != GIT_ENOTFOUND)
Packit Service 20376f
			return error;
Packit Service 20376f
	}
Packit Service 20376f
	giterr_clear();
Packit Service 20376f
Packit Service 20376f
	if (ref)
Packit Service 20376f
		current_id = git_reference_target(ref);
Packit Service 20376f
Packit Service 20376f
	if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	error = git_commit__create_buffer_internal(&buf, author, committer,
Packit Service 20376f
						   message_encoding, message, tree,
Packit Service 20376f
						   &parents);
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (git_repository_odb__weakptr(&odb, repo) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (git_odb__freshen(odb, tree) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
	if (update_ref != NULL) {
Packit Service 20376f
		error = git_reference__update_for_commit(
Packit Service 20376f
			repo, ref, update_ref, id, "commit");
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_array_clear(parents);
Packit Service 20376f
	git_reference_free(ref);
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_create_from_callback(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *update_ref,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_oid *tree,
Packit Service 20376f
	git_commit_parent_callback parent_cb,
Packit Service 20376f
	void *parent_payload)
Packit Service 20376f
{
Packit Service 20376f
	return git_commit__create_internal(
Packit Service 20376f
		id, repo, update_ref, author, committer, message_encoding, message,
Packit Service 20376f
		tree, parent_cb, parent_payload, true);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	size_t total;
Packit Service 20376f
	va_list args;
Packit Service 20376f
} commit_parent_varargs;
Packit Service 20376f
Packit Service 20376f
static const git_oid *commit_parent_from_varargs(size_t curr, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	commit_parent_varargs *data = payload;
Packit Service 20376f
	const git_commit *commit;
Packit Service 20376f
	if (curr >= data->total)
Packit Service 20376f
		return NULL;
Packit Service 20376f
	commit = va_arg(data->args, const git_commit *);
Packit Service 20376f
	return commit ? git_commit_id(commit) : NULL;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_create_v(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *update_ref,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_tree *tree,
Packit Service 20376f
	size_t parent_count,
Packit Service 20376f
	...)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	commit_parent_varargs data;
Packit Service 20376f
Packit Service 20376f
	assert(tree && git_tree_owner(tree) == repo);
Packit Service 20376f
Packit Service 20376f
	data.total = parent_count;
Packit Service 20376f
	va_start(data.args, parent_count);
Packit Service 20376f
Packit Service 20376f
	error = git_commit__create_internal(
Packit Service 20376f
		id, repo, update_ref, author, committer,
Packit Service 20376f
		message_encoding, message, git_tree_id(tree),
Packit Service 20376f
		commit_parent_from_varargs, &data, false);
Packit Service 20376f
Packit Service 20376f
	va_end(data.args);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	size_t total;
Packit Service 20376f
	const git_oid **parents;
Packit Service 20376f
} commit_parent_oids;
Packit Service 20376f
Packit Service 20376f
static const git_oid *commit_parent_from_ids(size_t curr, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	commit_parent_oids *data = payload;
Packit Service 20376f
	return (curr < data->total) ? data->parents[curr] : NULL;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_create_from_ids(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *update_ref,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_oid *tree,
Packit Service 20376f
	size_t parent_count,
Packit Service 20376f
	const git_oid *parents[])
Packit Service 20376f
{
Packit Service 20376f
	commit_parent_oids data = { parent_count, parents };
Packit Service 20376f
Packit Service 20376f
	return git_commit__create_internal(
Packit Service 20376f
		id, repo, update_ref, author, committer,
Packit Service 20376f
		message_encoding, message, tree,
Packit Service 20376f
		commit_parent_from_ids, &data, true);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	size_t total;
Packit Service 20376f
	const git_commit **parents;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
} commit_parent_data;
Packit Service 20376f
Packit Service 20376f
static const git_oid *commit_parent_from_array(size_t curr, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	commit_parent_data *data = payload;
Packit Service 20376f
	const git_commit *commit;
Packit Service 20376f
	if (curr >= data->total)
Packit Service 20376f
		return NULL;
Packit Service 20376f
	commit = data->parents[curr];
Packit Service 20376f
	if (git_commit_owner(commit) != data->repo)
Packit Service 20376f
		return NULL;
Packit Service 20376f
	return git_commit_id(commit);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_create(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *update_ref,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_tree *tree,
Packit Service 20376f
	size_t parent_count,
Packit Service 20376f
	const git_commit *parents[])
Packit Service 20376f
{
Packit Service 20376f
	commit_parent_data data = { parent_count, parents, repo };
Packit Service 20376f
Packit Service 20376f
	assert(tree && git_tree_owner(tree) == repo);
Packit Service 20376f
Packit Service 20376f
	return git_commit__create_internal(
Packit Service 20376f
		id, repo, update_ref, author, committer,
Packit Service 20376f
		message_encoding, message, git_tree_id(tree),
Packit Service 20376f
		commit_parent_from_array, &data, false);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	const git_commit *commit_to_amend = payload;
Packit Service 20376f
	if (curr >= git_array_size(commit_to_amend->parent_ids))
Packit Service 20376f
		return NULL;
Packit Service 20376f
	return git_array_get(commit_to_amend->parent_ids, curr);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_amend(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	const git_commit *commit_to_amend,
Packit Service 20376f
	const char *update_ref,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_tree *tree)
Packit Service 20376f
{
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	git_oid tree_id;
Packit Service 20376f
	git_reference *ref;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(id && commit_to_amend);
Packit Service 20376f
Packit Service 20376f
	repo = git_commit_owner(commit_to_amend);
Packit Service 20376f
Packit Service 20376f
	if (!author)
Packit Service 20376f
		author = git_commit_author(commit_to_amend);
Packit Service 20376f
	if (!committer)
Packit Service 20376f
		committer = git_commit_committer(commit_to_amend);
Packit Service 20376f
	if (!message_encoding)
Packit Service 20376f
		message_encoding = git_commit_message_encoding(commit_to_amend);
Packit Service 20376f
	if (!message)
Packit Service 20376f
		message = git_commit_message(commit_to_amend);
Packit Service 20376f
Packit Service 20376f
	if (!tree) {
Packit Service 20376f
		git_tree *old_tree;
Packit Service 20376f
		GITERR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
Packit Service 20376f
		git_oid_cpy(&tree_id, git_tree_id(old_tree));
Packit Service 20376f
		git_tree_free(old_tree);
Packit Service 20376f
	} else {
Packit Service 20376f
		assert(git_tree_owner(tree) == repo);
Packit Service 20376f
		git_oid_cpy(&tree_id, git_tree_id(tree));
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (update_ref) {
Packit Service 20376f
		if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) {
Packit Service 20376f
			git_reference_free(ref);
Packit Service 20376f
			giterr_set(GITERR_REFERENCE, "commit to amend is not the tip of the given branch");
Packit Service 20376f
			return -1;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = git_commit__create_internal(
Packit Service 20376f
		id, repo, NULL, author, committer, message_encoding, message,
Packit Service 20376f
		&tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
Packit Service 20376f
Packit Service 20376f
	if (!error && update_ref) {
Packit Service 20376f
		error = git_reference__update_for_commit(
Packit Service 20376f
			repo, ref, NULL, id, "commit");
Packit Service 20376f
		git_reference_free(ref);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit__parse(void *_commit, git_odb_object *odb_obj)
Packit Service 20376f
{
Packit Service 20376f
	git_commit *commit = _commit;
Packit Service 20376f
	const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
Packit Service 20376f
	const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
Packit Service 20376f
	git_oid parent_id;
Packit Service 20376f
	size_t header_len;
Packit Service 20376f
	git_signature dummy_sig;
Packit Service 20376f
Packit Service 20376f
	buffer = buffer_start;
Packit Service 20376f
Packit Service 20376f
	/* Allocate for one, which will allow not to realloc 90% of the time  */
Packit Service 20376f
	git_array_init_to_size(commit->parent_ids, 1);
Packit Service 20376f
	GITERR_CHECK_ARRAY(commit->parent_ids);
Packit Service 20376f
Packit Service 20376f
	/* The tree is always the first field */
Packit Service 20376f
	if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
Packit Service 20376f
		goto bad_buffer;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * TODO: commit grafts!
Packit Service 20376f
	 */
Packit Service 20376f
Packit Service 20376f
	while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
Packit Service 20376f
		git_oid *new_id = git_array_alloc(commit->parent_ids);
Packit Service 20376f
		GITERR_CHECK_ALLOC(new_id);
Packit Service 20376f
Packit Service 20376f
		git_oid_cpy(new_id, &parent_id);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	commit->author = git__malloc(sizeof(git_signature));
Packit Service 20376f
	GITERR_CHECK_ALLOC(commit->author);
Packit Service 20376f
Packit Service 20376f
	if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	/* Some tools create multiple author fields, ignore the extra ones */
Packit Service 20376f
	while ((size_t)(buffer_end - buffer) >= strlen("author ") && !git__prefixcmp(buffer, "author ")) {
Packit Service 20376f
		if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		git__free(dummy_sig.name);
Packit Service 20376f
		git__free(dummy_sig.email);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Always parse the committer; we need the commit time */
Packit Service 20376f
	commit->committer = git__malloc(sizeof(git_signature));
Packit Service 20376f
	GITERR_CHECK_ALLOC(commit->committer);
Packit Service 20376f
Packit Service 20376f
	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	/* Parse add'l header entries */
Packit Service 20376f
	while (buffer < buffer_end) {
Packit Service 20376f
		const char *eoln = buffer;
Packit Service 20376f
		if (buffer[-1] == '\n' && buffer[0] == '\n')
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		while (eoln < buffer_end && *eoln != '\n')
Packit Service 20376f
			++eoln;
Packit Service 20376f
Packit Service 20376f
		if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
Packit Service 20376f
			buffer += strlen("encoding ");
Packit Service 20376f
Packit Service 20376f
			commit->message_encoding = git__strndup(buffer, eoln - buffer);
Packit Service 20376f
			GITERR_CHECK_ALLOC(commit->message_encoding);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (eoln < buffer_end && *eoln == '\n')
Packit Service 20376f
			++eoln;
Packit Service 20376f
		buffer = eoln;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	header_len = buffer - buffer_start;
Packit Service 20376f
	commit->raw_header = git__strndup(buffer_start, header_len);
Packit Service 20376f
	GITERR_CHECK_ALLOC(commit->raw_header);
Packit Service 20376f
Packit Service 20376f
	/* point "buffer" to data after header, +1 for the final LF */
Packit Service 20376f
	buffer = buffer_start + header_len + 1;
Packit Service 20376f
Packit Service 20376f
	/* extract commit message */
Packit Service 20376f
	if (buffer <= buffer_end)
Packit Service 20376f
		commit->raw_message = git__strndup(buffer, buffer_end - buffer);
Packit Service 20376f
	else
Packit Service 20376f
		commit->raw_message = git__strdup("");
Packit Service 20376f
	GITERR_CHECK_ALLOC(commit->raw_message);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
bad_buffer:
Packit Service 20376f
	giterr_set(GITERR_OBJECT, "failed to parse bad commit object");
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
Packit Service 20376f
	_rvalue git_commit_##_name(const git_commit *commit) \
Packit Service 20376f
	{\
Packit Service 20376f
		assert(commit); \
Packit Service 20376f
		return _return; \
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
Packit Service 20376f
GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
Packit Service 20376f
GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
Packit Service 20376f
GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
Packit Service 20376f
GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
Packit Service 20376f
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
Packit Service 20376f
GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
Packit Service 20376f
GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
Packit Service 20376f
GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id)
Packit Service 20376f
Packit Service 20376f
const char *git_commit_message(const git_commit *commit)
Packit Service 20376f
{
Packit Service 20376f
	const char *message;
Packit Service 20376f
Packit Service 20376f
	assert(commit);
Packit Service 20376f
Packit Service 20376f
	message = commit->raw_message;
Packit Service 20376f
Packit Service 20376f
	/* trim leading newlines from raw message */
Packit Service 20376f
	while (*message && *message == '\n')
Packit Service 20376f
		++message;
Packit Service 20376f
Packit Service 20376f
	return message;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *git_commit_summary(git_commit *commit)
Packit Service 20376f
{
Packit Service 20376f
	git_buf summary = GIT_BUF_INIT;
Packit Service 20376f
	const char *msg, *space;
Packit Service 20376f
	bool space_contains_newline = false;
Packit Service 20376f
Packit Service 20376f
	assert(commit);
Packit Service 20376f
Packit Service 20376f
	if (!commit->summary) {
Packit Service 20376f
		for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
Packit Service 20376f
			char next_character = msg[0];
Packit Service 20376f
			/* stop processing at the end of the first paragraph */
Packit Service 20376f
			if (next_character == '\n' && (!msg[1] || msg[1] == '\n'))
Packit Service 20376f
				break;
Packit Service 20376f
			/* record the beginning of contiguous whitespace runs */
Packit Service 20376f
			else if (git__isspace(next_character)) {
Packit Service 20376f
				if(space == NULL) {
Packit Service 20376f
					space = msg;
Packit Service 20376f
					space_contains_newline = false;
Packit Service 20376f
				}
Packit Service 20376f
				space_contains_newline |= next_character == '\n';
Packit Service 20376f
			}
Packit Service 20376f
			/* the next character is non-space */
Packit Service 20376f
			else {
Packit Service 20376f
				/* process any recorded whitespace */
Packit Service 20376f
				if (space) {
Packit Service 20376f
					if(space_contains_newline)
Packit Service 20376f
						git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
Packit Service 20376f
					else
Packit Service 20376f
						git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */
Packit Service 20376f
					space = NULL;
Packit Service 20376f
				}
Packit Service 20376f
				/* copy the next character */
Packit Service 20376f
				git_buf_putc(&summary, next_character);
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		commit->summary = git_buf_detach(&summary);
Packit Service 20376f
		if (!commit->summary)
Packit Service 20376f
			commit->summary = git__strdup("");
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return commit->summary;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const char *git_commit_body(git_commit *commit)
Packit Service 20376f
{
Packit Service 20376f
	const char *msg, *end;
Packit Service 20376f
Packit Service 20376f
	assert(commit);
Packit Service 20376f
Packit Service 20376f
	if (!commit->body) {
Packit Service 20376f
		/* search for end of summary */
Packit Service 20376f
		for (msg = git_commit_message(commit); *msg; ++msg)
Packit Service 20376f
			if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
Packit Service 20376f
				break;
Packit Service 20376f
Packit Service 20376f
		/* trim leading and trailing whitespace */
Packit Service 20376f
		for (; *msg; ++msg)
Packit Service 20376f
			if (!git__isspace(*msg))
Packit Service 20376f
				break;
Packit Service 20376f
		for (end = msg + strlen(msg) - 1; msg <= end; --end)
Packit Service 20376f
			if (!git__isspace(*end))
Packit Service 20376f
				break;
Packit Service 20376f
Packit Service 20376f
		if (*msg)
Packit Service 20376f
			    commit->body = git__strndup(msg, end - msg + 1);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return commit->body;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
Packit Service 20376f
{
Packit Service 20376f
	assert(commit);
Packit Service 20376f
	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
const git_oid *git_commit_parent_id(
Packit Service 20376f
	const git_commit *commit, unsigned int n)
Packit Service 20376f
{
Packit Service 20376f
	assert(commit);
Packit Service 20376f
Packit Service 20376f
	return git_array_get(commit->parent_ids, n);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_parent(
Packit Service 20376f
	git_commit **parent, const git_commit *commit, unsigned int n)
Packit Service 20376f
{
Packit Service 20376f
	const git_oid *parent_id;
Packit Service 20376f
	assert(commit);
Packit Service 20376f
Packit Service 20376f
	parent_id = git_commit_parent_id(commit, n);
Packit Service 20376f
	if (parent_id == NULL) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "parent %u does not exist", n);
Packit Service 20376f
		return GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return git_commit_lookup(parent, commit->object.repo, parent_id);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_nth_gen_ancestor(
Packit Service 20376f
	git_commit **ancestor,
Packit Service 20376f
	const git_commit *commit,
Packit Service 20376f
	unsigned int n)
Packit Service 20376f
{
Packit Service 20376f
	git_commit *current, *parent = NULL;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	assert(ancestor && commit);
Packit Service 20376f
Packit Service 20376f
	if (git_commit_dup(&current, (git_commit *)commit) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if (n == 0) {
Packit Service 20376f
		*ancestor = current;
Packit Service 20376f
		return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	while (n--) {
Packit Service 20376f
		error = git_commit_parent(&parent, current, 0);
Packit Service 20376f
Packit Service 20376f
		git_commit_free(current);
Packit Service 20376f
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		current = parent;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*ancestor = parent;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
Packit Service 20376f
{
Packit Service 20376f
	const char *eol, *buf = commit->raw_header;
Packit Service 20376f
Packit Service 20376f
	git_buf_clear(out);
Packit Service 20376f
Packit Service 20376f
	while ((eol = strchr(buf, '\n'))) {
Packit Service 20376f
		/* We can skip continuations here */
Packit Service 20376f
		if (buf[0] == ' ') {
Packit Service 20376f
			buf = eol + 1;
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* Skip until we find the field we're after */
Packit Service 20376f
		if (git__prefixcmp(buf, field)) {
Packit Service 20376f
			buf = eol + 1;
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		buf += strlen(field);
Packit Service 20376f
		/* Check that we're not matching a prefix but the field itself */
Packit Service 20376f
		if (buf[0] != ' ') {
Packit Service 20376f
			buf = eol + 1;
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		buf++; /* skip the SP */
Packit Service 20376f
Packit Service 20376f
		git_buf_put(out, buf, eol - buf);
Packit Service 20376f
		if (git_buf_oom(out))
Packit Service 20376f
			goto oom;
Packit Service 20376f
Packit Service 20376f
		/* If the next line starts with SP, it's multi-line, we must continue */
Packit Service 20376f
		while (eol[1] == ' ') {
Packit Service 20376f
			git_buf_putc(out, '\n');
Packit Service 20376f
			buf = eol + 2;
Packit Service 20376f
			eol = strchr(buf, '\n');
Packit Service 20376f
			if (!eol)
Packit Service 20376f
				goto malformed;
Packit Service 20376f
Packit Service 20376f
			git_buf_put(out, buf, eol - buf);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (git_buf_oom(out))
Packit Service 20376f
			goto oom;
Packit Service 20376f
Packit Service 20376f
		return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	giterr_set(GITERR_OBJECT, "no such field '%s'", field);
Packit Service 20376f
	return GIT_ENOTFOUND;
Packit Service 20376f
Packit Service 20376f
malformed:
Packit Service 20376f
	giterr_set(GITERR_OBJECT, "malformed header");
Packit Service 20376f
	return -1;
Packit Service 20376f
oom:
Packit Service 20376f
	giterr_set_oom();
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
Packit Service 20376f
{
Packit Service 20376f
	git_odb_object *obj;
Packit Service 20376f
	git_odb *odb;
Packit Service 20376f
	const char *buf;
Packit Service 20376f
	const char *h, *eol;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	git_buf_clear(signature);
Packit Service 20376f
	git_buf_clear(signed_data);
Packit Service 20376f
Packit Service 20376f
	if (!field)
Packit Service 20376f
		field = "gpgsig";
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if (obj->cached.type != GIT_OBJ_COMMIT) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "the requested type does not match the type in ODB");
Packit Service 20376f
		error = GIT_ENOTFOUND;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	buf = git_odb_object_data(obj);
Packit Service 20376f
Packit Service 20376f
	while ((h = strchr(buf, '\n')) && h[1] != '\0') {
Packit Service 20376f
		h++;
Packit Service 20376f
		if (git__prefixcmp(buf, field)) {
Packit Service 20376f
			if (git_buf_put(signed_data, buf, h - buf) < 0)
Packit Service 20376f
				return -1;
Packit Service 20376f
Packit Service 20376f
			buf = h;
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		h = buf;
Packit Service 20376f
		h += strlen(field);
Packit Service 20376f
		eol = strchr(h, '\n');
Packit Service 20376f
		if (h[0] != ' ') {
Packit Service 20376f
			buf = h;
Packit Service 20376f
			continue;
Packit Service 20376f
		}
Packit Service 20376f
		if (!eol)
Packit Service 20376f
			goto malformed;
Packit Service 20376f
Packit Service 20376f
		h++; /* skip the SP */
Packit Service 20376f
Packit Service 20376f
		git_buf_put(signature, h, eol - h);
Packit Service 20376f
		if (git_buf_oom(signature))
Packit Service 20376f
			goto oom;
Packit Service 20376f
Packit Service 20376f
		/* If the next line starts with SP, it's multi-line, we must continue */
Packit Service 20376f
		while (eol[1] == ' ') {
Packit Service 20376f
			git_buf_putc(signature, '\n');
Packit Service 20376f
			h = eol + 2;
Packit Service 20376f
			eol = strchr(h, '\n');
Packit Service 20376f
			if (!eol)
Packit Service 20376f
				goto malformed;
Packit Service 20376f
Packit Service 20376f
			git_buf_put(signature, h, eol - h);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (git_buf_oom(signature))
Packit Service 20376f
			goto oom;
Packit Service 20376f
Packit Service 20376f
		error = git_buf_puts(signed_data, eol+1);
Packit Service 20376f
		git_odb_object_free(obj);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	giterr_set(GITERR_OBJECT, "this commit is not signed");
Packit Service 20376f
	error = GIT_ENOTFOUND;
Packit Service 20376f
	goto cleanup;
Packit Service 20376f
Packit Service 20376f
malformed:
Packit Service 20376f
	giterr_set(GITERR_OBJECT, "malformed header");
Packit Service 20376f
	error = -1;
Packit Service 20376f
	goto cleanup;
Packit Service 20376f
oom:
Packit Service 20376f
	giterr_set_oom();
Packit Service 20376f
	error = -1;
Packit Service 20376f
	goto cleanup;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_odb_object_free(obj);
Packit Service 20376f
	git_buf_clear(signature);
Packit Service 20376f
	git_buf_clear(signed_data);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_create_buffer(git_buf *out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_signature *author,
Packit Service 20376f
	const git_signature *committer,
Packit Service 20376f
	const char *message_encoding,
Packit Service 20376f
	const char *message,
Packit Service 20376f
	const git_tree *tree,
Packit Service 20376f
	size_t parent_count,
Packit Service 20376f
	const git_commit *parents[])
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	commit_parent_data data = { parent_count, parents, repo };
Packit Service 20376f
	git_array_oid_t parents_arr = GIT_ARRAY_INIT;
Packit Service 20376f
	const git_oid *tree_id;
Packit Service 20376f
Packit Service 20376f
	assert(tree && git_tree_owner(tree) == repo);
Packit Service 20376f
Packit Service 20376f
	tree_id = git_tree_id(tree);
Packit Service 20376f
Packit Service 20376f
	if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	error = git_commit__create_buffer_internal(
Packit Service 20376f
		out, author, committer,
Packit Service 20376f
		message_encoding, message, tree_id,
Packit Service 20376f
		&parents_arr);
Packit Service 20376f
Packit Service 20376f
	git_array_clear(parents_arr);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * Append to 'out' properly marking continuations when there's a newline in 'content'
Packit Service 20376f
 */
Packit Service 20376f
static void format_header_field(git_buf *out, const char *field, const char *content)
Packit Service 20376f
{
Packit Service 20376f
	const char *lf;
Packit Service 20376f
Packit Service 20376f
	assert(out && field && content);
Packit Service 20376f
Packit Service 20376f
	git_buf_puts(out, field);
Packit Service 20376f
	git_buf_putc(out, ' ');
Packit Service 20376f
Packit Service 20376f
	while ((lf = strchr(content, '\n')) != NULL) {
Packit Service 20376f
		git_buf_put(out, content, lf - content);
Packit Service 20376f
		git_buf_puts(out, "\n ");
Packit Service 20376f
		content = lf + 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_puts(out, content);
Packit Service 20376f
	git_buf_putc(out, '\n');
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_commit_create_with_signature(
Packit Service 20376f
	git_oid *out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *commit_content,
Packit Service 20376f
	const char *signature,
Packit Service 20376f
	const char *signature_field)
Packit Service 20376f
{
Packit Service 20376f
	git_odb *odb;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	const char *field;
Packit Service 20376f
	const char *header_end;
Packit Service 20376f
	git_buf commit = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	/* We start by identifying the end of the commit header */
Packit Service 20376f
	header_end = strstr(commit_content, "\n\n");
Packit Service 20376f
	if (!header_end) {
Packit Service 20376f
		giterr_set(GITERR_INVALID, "malformed commit contents");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	field = signature_field ? signature_field : "gpgsig";
Packit Service 20376f
Packit Service 20376f
	/* The header ends after the first LF */
Packit Service 20376f
	header_end++;
Packit Service 20376f
	git_buf_put(&commit, commit_content, header_end - commit_content);
Packit Service 20376f
	format_header_field(&commit, field, signature);
Packit Service 20376f
	git_buf_puts(&commit, header_end);
Packit Service 20376f
Packit Service 20376f
	if (git_buf_oom(&commit))
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_buf_free(&commit);
Packit Service 20376f
	return error;
Packit Service 20376f
}