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