patch.c 4.19 KB
Newer Older
1
#include "git2/patch.h"
2 3
#include "diff.h"
#include "patch.h"
4 5


6 7 8 9 10 11 12
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)
13
{
14 15
	int error = 0;
	uint32_t i, j;
16

17 18
	if (file_cb)
		error = file_cb(patch->delta, 0, payload);
19

20 21 22
	if (error)
		return error;

23 24 25
	if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
		if (binary_cb)
			error = binary_cb(patch->delta, &patch->binary, payload);
26

27
		return error;
28 29
	}

30 31
	if (!hunk_cb && !line_cb)
		return error;
32

33 34
	for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
		git_patch_hunk *h = git_array_get(patch->hunks, i);
35

36 37
		if (hunk_cb)
			error = hunk_cb(patch->delta, &h->hunk, payload);
38

39 40
		if (!line_cb)
			continue;
41

42 43 44
		for (j = 0; !error && j < h->line_count; ++j) {
			git_diff_line *l =
				git_array_get(patch->lines, h->line_start + j);
45

46 47
			error = line_cb(patch->delta, &h->hunk, l, payload);
		}
48 49 50 51 52
	}

	return error;
}

53 54 55 56 57
size_t git_patch_size(
	git_patch *patch,
	int include_context,
	int include_hunk_headers,
	int include_file_headers)
58
{
59
	size_t out;
60

61
	assert(patch);
62

63
	out = patch->content_size;
64

65 66
	if (!include_context)
		out -= patch->context_size;
67

68 69
	if (include_hunk_headers)
		out += patch->header_size;
70

71 72
	if (include_file_headers) {
		git_buf file_header = GIT_BUF_INIT;
73

74 75 76 77 78
		if (git_diff_delta__format_file_header(
			&file_header, patch->delta, NULL, NULL, 0) < 0)
			giterr_clear();
		else
			out += git_buf_len(&file_header);
79

80
		git_buf_free(&file_header);
81 82
	}

83
	return out;
84 85
}

86 87 88 89 90
int git_patch_line_stats(
	size_t *total_ctxt,
	size_t *total_adds,
	size_t *total_dels,
	const git_patch *patch)
91
{
92
	size_t totals[3], idx;
93

94
	memset(totals, 0, sizeof(totals));
95

96 97 98 99
	for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
		git_diff_line *line = git_array_get(patch->lines, idx);
		if (!line)
			continue;
100

101 102 103 104 105 106 107 108 109 110
		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;
		}
111 112
	}

113 114 115 116 117 118
	if (total_ctxt)
		*total_ctxt = totals[0];
	if (total_adds)
		*total_adds = totals[1];
	if (total_dels)
		*total_dels = totals[2];
119 120 121 122

	return 0;
}

123
const git_diff_delta *git_patch_get_delta(const git_patch *patch)
124
{
125 126
	assert(patch);
	return patch->delta;
127 128
}

129
size_t git_patch_num_hunks(const git_patch *patch)
130
{
131 132
	assert(patch);
	return git_array_size(patch->hunks);
133 134
}

135
static int patch_error_outofrange(const char *thing)
136
{
137 138
	giterr_set(GITERR_INVALID, "patch %s index out of range", thing);
	return GIT_ENOTFOUND;
139 140
}

141 142 143
int git_patch_get_hunk(
	const git_diff_hunk **out,
	size_t *lines_in_hunk,
144
	git_patch *patch,
145
	size_t hunk_idx)
146
{
147 148
	git_patch_hunk *hunk;
	assert(patch);
149

150
	hunk = git_array_get(patch->hunks, hunk_idx);
151

152 153 154 155
	if (!hunk) {
		if (out) *out = NULL;
		if (lines_in_hunk) *lines_in_hunk = 0;
		return patch_error_outofrange("hunk");
156 157
	}

158 159
	if (out) *out = &hunk->hunk;
	if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
160 161 162
	return 0;
}

163
int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
164
{
165 166
	git_patch_hunk *hunk;
	assert(patch);
167

168 169 170
	if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
		return patch_error_outofrange("hunk");
	return (int)hunk->line_count;
171 172
}

173 174
int git_patch_get_line_in_hunk(
	const git_diff_line **out,
175
	git_patch *patch,
176 177
	size_t hunk_idx,
	size_t line_of_hunk)
178
{
179
	git_patch_hunk *hunk;
180 181
	git_diff_line *line;

182
	assert(patch);
183

184 185 186
	if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
		if (out) *out = NULL;
		return patch_error_outofrange("hunk");
187 188
	}

189 190 191 192 193
	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");
194 195
	}

196
	if (out) *out = line;
197 198 199
	return 0;
}

200 201 202 203 204 205
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);
}

206
static void git_patch__free(git_patch *patch)
207
{
208 209
	if (patch->free_fn)
		patch->free_fn(patch);
210 211
}

212
void git_patch_free(git_patch *patch)
213
{
214 215
	if (patch)
		GIT_REFCOUNT_DEC(patch, git_patch__free);
216
}