summaryrefslogtreecommitdiff
path: root/src/libgit2/diff_parse.c
blob: 04603969e4080c9f9fff852e4dd04958b7532c4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
 * Copyright (C) the libgit2 contributors. All rights reserved.
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */

#include "diff_parse.h"

#include "diff.h"
#include "patch.h"
#include "patch_parse.h"

static void diff_parsed_free(git_diff *d)
{
	git_diff_parsed *diff = (git_diff_parsed *)d;
	git_patch *patch;
	size_t i;

	git_vector_foreach(&diff->patches, i, patch)
		git_patch_free(patch);

	git_vector_free(&diff->patches);

	git_vector_free(&diff->base.deltas);
	git_pool_clear(&diff->base.pool);

	git__memzero(diff, sizeof(*diff));
	git__free(diff);
}

static git_diff_parsed *diff_parsed_alloc(git_oid_t oid_type)
{
	git_diff_parsed *diff;

	if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
		return NULL;

	GIT_REFCOUNT_INC(&diff->base);
	diff->base.type = GIT_DIFF_TYPE_PARSED;
	diff->base.strcomp = git__strcmp;
	diff->base.strncomp = git__strncmp;
	diff->base.pfxcomp = git__prefixcmp;
	diff->base.entrycomp = git_diff__entry_cmp;
	diff->base.patch_fn = git_patch_parsed_from_diff;
	diff->base.free_fn = diff_parsed_free;

	if (git_diff_options_init(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) {
		git__free(diff);
		return NULL;
	}

	diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
	diff->base.opts.oid_type = oid_type;

	if (git_pool_init(&diff->base.pool, 1) < 0 ||
	    git_vector_init(&diff->patches, 0, NULL) < 0 ||
		git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
		git_diff_free(&diff->base);
		return NULL;
	}

	git_vector_set_cmp(&diff->base.deltas, git_diff_delta__cmp);

	return diff;
}

int git_diff_from_buffer(
	git_diff **out,
	const char *content,
	size_t content_len
#ifdef GIT_EXPERIMENTAL_SHA256
	, git_diff_parse_options *opts
#endif
	)
{
	git_diff_parsed *diff;
	git_patch *patch;
	git_patch_parse_ctx *ctx = NULL;
	git_patch_options patch_opts = GIT_PATCH_OPTIONS_INIT;
	git_oid_t oid_type;
	int error = 0;

	*out = NULL;

#ifdef GIT_EXPERIMENTAL_SHA256
	oid_type = (opts && opts->oid_type) ? opts->oid_type :
		GIT_OID_DEFAULT;
#else
	oid_type = GIT_OID_DEFAULT;
#endif

	patch_opts.oid_type = oid_type;

	diff = diff_parsed_alloc(oid_type);
	GIT_ERROR_CHECK_ALLOC(diff);

	ctx = git_patch_parse_ctx_init(content, content_len, &patch_opts);
	GIT_ERROR_CHECK_ALLOC(ctx);

	while (ctx->parse_ctx.remain_len) {
		if ((error = git_patch_parse(&patch, ctx)) < 0)
			break;

		git_vector_insert(&diff->patches, patch);
		git_vector_insert(&diff->base.deltas, patch->delta);
	}

	if (error == GIT_ENOTFOUND && git_vector_length(&diff->patches) > 0) {
		git_error_clear();
		error = 0;
	}

	git_patch_parse_ctx_free(ctx);

	if (error < 0)
		git_diff_free(&diff->base);
	else
		*out = &diff->base;

	return error;
}