]> git.proxmox.com Git - libgit2.git/blob - src/patch.c
a30546f3ce6aa74431c7070fcb256d02556259b7
[libgit2.git] / src / patch.c
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 "patch.h"
9
10 #include "git2/patch.h"
11 #include "diff.h"
12
13 int git_patch__invoke_callbacks(
14 git_patch *patch,
15 git_diff_file_cb file_cb,
16 git_diff_binary_cb binary_cb,
17 git_diff_hunk_cb hunk_cb,
18 git_diff_line_cb line_cb,
19 void *payload)
20 {
21 int error = 0;
22 uint32_t i, j;
23
24 if (file_cb)
25 error = file_cb(patch->delta, 0, payload);
26
27 if (error)
28 return error;
29
30 if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
31 if (binary_cb)
32 error = binary_cb(patch->delta, &patch->binary, payload);
33
34 return error;
35 }
36
37 if (!hunk_cb && !line_cb)
38 return error;
39
40 for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
41 git_patch_hunk *h = git_array_get(patch->hunks, i);
42
43 if (hunk_cb)
44 error = hunk_cb(patch->delta, &h->hunk, payload);
45
46 if (!line_cb)
47 continue;
48
49 for (j = 0; !error && j < h->line_count; ++j) {
50 git_diff_line *l =
51 git_array_get(patch->lines, h->line_start + j);
52
53 error = line_cb(patch->delta, &h->hunk, l, payload);
54 }
55 }
56
57 return error;
58 }
59
60 size_t git_patch_size(
61 git_patch *patch,
62 int include_context,
63 int include_hunk_headers,
64 int include_file_headers)
65 {
66 size_t out;
67
68 GIT_ASSERT_ARG(patch);
69
70 out = patch->content_size;
71
72 if (!include_context)
73 out -= patch->context_size;
74
75 if (include_hunk_headers)
76 out += patch->header_size;
77
78 if (include_file_headers) {
79 git_str file_header = GIT_STR_INIT;
80
81 if (git_diff_delta__format_file_header(
82 &file_header, patch->delta, NULL, NULL, 0, true) < 0)
83 git_error_clear();
84 else
85 out += git_str_len(&file_header);
86
87 git_str_dispose(&file_header);
88 }
89
90 return out;
91 }
92
93 int git_patch_line_stats(
94 size_t *total_ctxt,
95 size_t *total_adds,
96 size_t *total_dels,
97 const git_patch *patch)
98 {
99 size_t totals[3], idx;
100
101 memset(totals, 0, sizeof(totals));
102
103 for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
104 git_diff_line *line = git_array_get(patch->lines, idx);
105 if (!line)
106 continue;
107
108 switch (line->origin) {
109 case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
110 case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
111 case GIT_DIFF_LINE_DELETION: totals[2]++; break;
112 default:
113 /* diff --stat and --numstat don't count EOFNL marks because
114 * they will always be paired with a ADDITION or DELETION line.
115 */
116 break;
117 }
118 }
119
120 if (total_ctxt)
121 *total_ctxt = totals[0];
122 if (total_adds)
123 *total_adds = totals[1];
124 if (total_dels)
125 *total_dels = totals[2];
126
127 return 0;
128 }
129
130 const git_diff_delta *git_patch_get_delta(const git_patch *patch)
131 {
132 GIT_ASSERT_ARG_WITH_RETVAL(patch, NULL);
133 return patch->delta;
134 }
135
136 size_t git_patch_num_hunks(const git_patch *patch)
137 {
138 GIT_ASSERT_ARG(patch);
139 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 int git_patch_get_hunk(
149 const git_diff_hunk **out,
150 size_t *lines_in_hunk,
151 git_patch *patch,
152 size_t hunk_idx)
153 {
154 git_patch_hunk *hunk;
155 GIT_ASSERT_ARG(patch);
156
157 hunk = git_array_get(patch->hunks, hunk_idx);
158
159 if (!hunk) {
160 if (out) *out = NULL;
161 if (lines_in_hunk) *lines_in_hunk = 0;
162 return patch_error_outofrange("hunk");
163 }
164
165 if (out) *out = &hunk->hunk;
166 if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
167 return 0;
168 }
169
170 int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
171 {
172 git_patch_hunk *hunk;
173 GIT_ASSERT_ARG(patch);
174
175 if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
176 return patch_error_outofrange("hunk");
177 return (int)hunk->line_count;
178 }
179
180 int git_patch_get_line_in_hunk(
181 const git_diff_line **out,
182 git_patch *patch,
183 size_t hunk_idx,
184 size_t line_of_hunk)
185 {
186 git_patch_hunk *hunk;
187 git_diff_line *line;
188
189 GIT_ASSERT_ARG(patch);
190
191 if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
192 if (out) *out = NULL;
193 return patch_error_outofrange("hunk");
194 }
195
196 if (line_of_hunk >= hunk->line_count ||
197 !(line = git_array_get(
198 patch->lines, hunk->line_start + line_of_hunk))) {
199 if (out) *out = NULL;
200 return patch_error_outofrange("line");
201 }
202
203 if (out) *out = line;
204 return 0;
205 }
206
207 git_repository *git_patch_owner(const git_patch *patch)
208 {
209 return patch->repo;
210 }
211
212 int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx)
213 {
214 GIT_ASSERT_ARG(out);
215 GIT_ASSERT_ARG(diff);
216 GIT_ASSERT_ARG(diff->patch_fn);
217 return diff->patch_fn(out, diff, idx);
218 }
219
220 static void git_patch__free(git_patch *patch)
221 {
222 if (patch->free_fn)
223 patch->free_fn(patch);
224 }
225
226 void git_patch_free(git_patch *patch)
227 {
228 if (patch)
229 GIT_REFCOUNT_DEC(patch, git_patch__free);
230 }