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