patch.c 4.41 KB
Newer Older
1 2 3 4 5 6 7
/*
* 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.
*/

8
#include "patch.h"
9

10 11
#include "git2/patch.h"
#include "diff.h"
12

13 14 15 16 17 18 19
int git_patch__invoke_callbacks(
	git_patch *patch,
	git_diff_file_cb file_cb,
	git_diff_binary_cb binary_cb,
	git_diff_hunk_cb hunk_cb,
	git_diff_line_cb line_cb,
	void *payload)
20
{
21 22
	int error = 0;
	uint32_t i, j;
23

24 25
	if (file_cb)
		error = file_cb(patch->delta, 0, payload);
26

27 28 29
	if (error)
		return error;

30 31 32
	if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
		if (binary_cb)
			error = binary_cb(patch->delta, &patch->binary, payload);
33

34
		return error;
35 36
	}

37 38
	if (!hunk_cb && !line_cb)
		return error;
39

40 41
	for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
		git_patch_hunk *h = git_array_get(patch->hunks, i);
42

43 44
		if (hunk_cb)
			error = hunk_cb(patch->delta, &h->hunk, payload);
45

46 47
		if (!line_cb)
			continue;
48

49 50 51
		for (j = 0; !error && j < h->line_count; ++j) {
			git_diff_line *l =
				git_array_get(patch->lines, h->line_start + j);
52

53 54
			error = line_cb(patch->delta, &h->hunk, l, payload);
		}
55 56 57 58 59
	}

	return error;
}

60 61 62 63 64
size_t git_patch_size(
	git_patch *patch,
	int include_context,
	int include_hunk_headers,
	int include_file_headers)
65
{
66
	size_t out;
67

68
	assert(patch);
69

70
	out = patch->content_size;
71

72 73
	if (!include_context)
		out -= patch->context_size;
74

75 76
	if (include_hunk_headers)
		out += patch->header_size;
77

78 79
	if (include_file_headers) {
		git_buf file_header = GIT_BUF_INIT;
80

81 82
		if (git_diff_delta__format_file_header(
			&file_header, patch->delta, NULL, NULL, 0) < 0)
83
			git_error_clear();
84 85
		else
			out += git_buf_len(&file_header);
86

87
		git_buf_dispose(&file_header);
88 89
	}

90
	return out;
91 92
}

93 94 95 96 97
int git_patch_line_stats(
	size_t *total_ctxt,
	size_t *total_adds,
	size_t *total_dels,
	const git_patch *patch)
98
{
99
	size_t totals[3], idx;
100

101
	memset(totals, 0, sizeof(totals));
102

103 104 105 106
	for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
		git_diff_line *line = git_array_get(patch->lines, idx);
		if (!line)
			continue;
107

108 109 110 111 112 113 114 115 116 117
		switch (line->origin) {
		case GIT_DIFF_LINE_CONTEXT:  totals[0]++; break;
		case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
		case GIT_DIFF_LINE_DELETION: totals[2]++; break;
		default:
			/* diff --stat and --numstat don't count EOFNL marks because
			* they will always be paired with a ADDITION or DELETION line.
			*/
			break;
		}
118 119
	}

120 121 122 123 124 125
	if (total_ctxt)
		*total_ctxt = totals[0];
	if (total_adds)
		*total_adds = totals[1];
	if (total_dels)
		*total_dels = totals[2];
126 127 128 129

	return 0;
}

130
const git_diff_delta *git_patch_get_delta(const git_patch *patch)
131
{
132 133
	assert(patch);
	return patch->delta;
134 135
}

136
size_t git_patch_num_hunks(const git_patch *patch)
137
{
138 139
	assert(patch);
	return git_array_size(patch->hunks);
140 141
}

142
static int patch_error_outofrange(const char *thing)
143
{
144
	git_error_set(GIT_ERROR_INVALID, "patch %s index out of range", thing);
145
	return GIT_ENOTFOUND;
146 147
}

148 149 150
int git_patch_get_hunk(
	const git_diff_hunk **out,
	size_t *lines_in_hunk,
151
	git_patch *patch,
152
	size_t hunk_idx)
153
{
154 155
	git_patch_hunk *hunk;
	assert(patch);
156

157
	hunk = git_array_get(patch->hunks, hunk_idx);
158

159 160 161 162
	if (!hunk) {
		if (out) *out = NULL;
		if (lines_in_hunk) *lines_in_hunk = 0;
		return patch_error_outofrange("hunk");
163 164
	}

165 166
	if (out) *out = &hunk->hunk;
	if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
167 168 169
	return 0;
}

170
int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
171
{
172 173
	git_patch_hunk *hunk;
	assert(patch);
174

175 176 177
	if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
		return patch_error_outofrange("hunk");
	return (int)hunk->line_count;
178 179
}

180 181
int git_patch_get_line_in_hunk(
	const git_diff_line **out,
182
	git_patch *patch,
183 184
	size_t hunk_idx,
	size_t line_of_hunk)
185
{
186
	git_patch_hunk *hunk;
187 188
	git_diff_line *line;

189
	assert(patch);
190

191 192 193
	if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
		if (out) *out = NULL;
		return patch_error_outofrange("hunk");
194 195
	}

196 197 198 199 200
	if (line_of_hunk >= hunk->line_count ||
		!(line = git_array_get(
			patch->lines, hunk->line_start + line_of_hunk))) {
		if (out) *out = NULL;
		return patch_error_outofrange("line");
201 202
	}

203
	if (out) *out = line;
204 205 206
	return 0;
}

207 208 209 210 211 212
int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx)
{
	assert(out && diff && diff->patch_fn);
	return diff->patch_fn(out, diff, idx);
}

213
static void git_patch__free(git_patch *patch)
214
{
215 216
	if (patch->free_fn)
		patch->free_fn(patch);
217 218
}

219
void git_patch_free(git_patch *patch)
220
{
221 222
	if (patch)
		GIT_REFCOUNT_DEC(patch, git_patch__free);
223
}