]>
Commit | Line | Data |
---|---|---|
7000f3fa RB |
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 | */ | |
eae0bfdc | 7 | |
7000f3fa | 8 | #include "common.h" |
eae0bfdc | 9 | |
7000f3fa | 10 | #include "diff.h" |
804d5fe9 | 11 | #include "diff_file.h" |
8d44f8b7 | 12 | #include "patch_generate.h" |
22a2d3d5 | 13 | #include "futils.h" |
e349ed50 ET |
14 | #include "zstream.h" |
15 | #include "blob.h" | |
16 | #include "delta.h" | |
27e54bcf | 17 | #include "git2/sys/diff.h" |
7000f3fa RB |
18 | |
19 | typedef struct { | |
10672e3e | 20 | git_diff_format_t format; |
3ff1d123 | 21 | git_diff_line_cb print_cb; |
7000f3fa | 22 | void *payload; |
804d5fe9 | 23 | |
7000f3fa | 24 | git_buf *buf; |
804d5fe9 ET |
25 | git_diff_line line; |
26 | ||
27 | const char *old_prefix; | |
28 | const char *new_prefix; | |
10672e3e | 29 | uint32_t flags; |
040ec883 | 30 | int id_strlen; |
804d5fe9 ET |
31 | |
32 | int (*strcomp)(const char *, const char *); | |
7000f3fa RB |
33 | } diff_print_info; |
34 | ||
8147b1af | 35 | static int diff_print_info_init__common( |
7000f3fa | 36 | diff_print_info *pi, |
10672e3e | 37 | git_buf *out, |
8147b1af | 38 | git_repository *repo, |
10672e3e RB |
39 | git_diff_format_t format, |
40 | git_diff_line_cb cb, | |
41 | void *payload) | |
7000f3fa | 42 | { |
8147b1af | 43 | pi->format = format; |
7000f3fa | 44 | pi->print_cb = cb; |
8147b1af ET |
45 | pi->payload = payload; |
46 | pi->buf = out; | |
47 | ||
040ec883 | 48 | if (!pi->id_strlen) { |
8147b1af | 49 | if (!repo) |
040ec883 | 50 | pi->id_strlen = GIT_ABBREV_DEFAULT; |
22a2d3d5 | 51 | else if (git_repository__configmap_lookup(&pi->id_strlen, repo, GIT_CONFIGMAP_ABBREV) < 0) |
8147b1af ET |
52 | return -1; |
53 | } | |
7000f3fa | 54 | |
040ec883 ET |
55 | if (pi->id_strlen > GIT_OID_HEXSZ) |
56 | pi->id_strlen = GIT_OID_HEXSZ; | |
7000f3fa | 57 | |
3b5f7954 RB |
58 | memset(&pi->line, 0, sizeof(pi->line)); |
59 | pi->line.old_lineno = -1; | |
60 | pi->line.new_lineno = -1; | |
8147b1af | 61 | pi->line.num_lines = 1; |
3b5f7954 | 62 | |
7000f3fa RB |
63 | return 0; |
64 | } | |
65 | ||
8147b1af ET |
66 | static int diff_print_info_init_fromdiff( |
67 | diff_print_info *pi, | |
68 | git_buf *out, | |
69 | git_diff *diff, | |
70 | git_diff_format_t format, | |
71 | git_diff_line_cb cb, | |
72 | void *payload) | |
73 | { | |
74 | git_repository *repo = diff ? diff->repo : NULL; | |
75 | ||
76 | memset(pi, 0, sizeof(diff_print_info)); | |
77 | ||
8147b1af ET |
78 | if (diff) { |
79 | pi->flags = diff->opts.flags; | |
040ec883 | 80 | pi->id_strlen = diff->opts.id_abbrev; |
804d5fe9 ET |
81 | pi->old_prefix = diff->opts.old_prefix; |
82 | pi->new_prefix = diff->opts.new_prefix; | |
83 | ||
84 | pi->strcomp = diff->strcomp; | |
8147b1af ET |
85 | } |
86 | ||
87 | return diff_print_info_init__common(pi, out, repo, format, cb, payload); | |
88 | } | |
89 | ||
90 | static int diff_print_info_init_frompatch( | |
91 | diff_print_info *pi, | |
92 | git_buf *out, | |
93 | git_patch *patch, | |
94 | git_diff_format_t format, | |
95 | git_diff_line_cb cb, | |
96 | void *payload) | |
97 | { | |
c25aa7cd | 98 | GIT_ASSERT_ARG(patch); |
be8479c9 | 99 | |
8147b1af ET |
100 | memset(pi, 0, sizeof(diff_print_info)); |
101 | ||
8147b1af | 102 | pi->flags = patch->diff_opts.flags; |
040ec883 | 103 | pi->id_strlen = patch->diff_opts.id_abbrev; |
804d5fe9 ET |
104 | pi->old_prefix = patch->diff_opts.old_prefix; |
105 | pi->new_prefix = patch->diff_opts.new_prefix; | |
8147b1af | 106 | |
804d5fe9 | 107 | return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload); |
8147b1af ET |
108 | } |
109 | ||
a1683f28 | 110 | static char diff_pick_suffix(int mode) |
7000f3fa RB |
111 | { |
112 | if (S_ISDIR(mode)) | |
113 | return '/'; | |
a7fcc44d | 114 | else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */ |
7000f3fa RB |
115 | /* in git, modes are very regular, so we must have 0100755 mode */ |
116 | return '*'; | |
117 | else | |
118 | return ' '; | |
119 | } | |
120 | ||
121 | char git_diff_status_char(git_delta_t status) | |
122 | { | |
123 | char code; | |
124 | ||
125 | switch (status) { | |
85b7268e AR |
126 | case GIT_DELTA_ADDED: code = 'A'; break; |
127 | case GIT_DELTA_DELETED: code = 'D'; break; | |
128 | case GIT_DELTA_MODIFIED: code = 'M'; break; | |
129 | case GIT_DELTA_RENAMED: code = 'R'; break; | |
130 | case GIT_DELTA_COPIED: code = 'C'; break; | |
131 | case GIT_DELTA_IGNORED: code = 'I'; break; | |
132 | case GIT_DELTA_UNTRACKED: code = '?'; break; | |
4b3ec53c | 133 | case GIT_DELTA_TYPECHANGE: code = 'T'; break; |
85b7268e AR |
134 | case GIT_DELTA_UNREADABLE: code = 'X'; break; |
135 | default: code = ' '; break; | |
7000f3fa RB |
136 | } |
137 | ||
138 | return code; | |
139 | } | |
140 | ||
10672e3e RB |
141 | static int diff_print_one_name_only( |
142 | const git_diff_delta *delta, float progress, void *data) | |
143 | { | |
144 | diff_print_info *pi = data; | |
145 | git_buf *out = pi->buf; | |
146 | ||
147 | GIT_UNUSED(progress); | |
148 | ||
149 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && | |
150 | delta->status == GIT_DELTA_UNMODIFIED) | |
151 | return 0; | |
152 | ||
153 | git_buf_clear(out); | |
25e0b157 RB |
154 | git_buf_puts(out, delta->new_file.path); |
155 | git_buf_putc(out, '\n'); | |
156 | if (git_buf_oom(out)) | |
157 | return -1; | |
10672e3e | 158 | |
3b5f7954 RB |
159 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
160 | pi->line.content = git_buf_cstr(out); | |
161 | pi->line.content_len = git_buf_len(out); | |
162 | ||
25e0b157 | 163 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
10672e3e RB |
164 | } |
165 | ||
166 | static int diff_print_one_name_status( | |
7000f3fa RB |
167 | const git_diff_delta *delta, float progress, void *data) |
168 | { | |
169 | diff_print_info *pi = data; | |
a1683f28 | 170 | git_buf *out = pi->buf; |
7000f3fa | 171 | char old_suffix, new_suffix, code = git_diff_status_char(delta->status); |
804d5fe9 ET |
172 | int(*strcomp)(const char *, const char *) = pi->strcomp ? |
173 | pi->strcomp : git__strcmp; | |
7000f3fa RB |
174 | |
175 | GIT_UNUSED(progress); | |
176 | ||
10672e3e | 177 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') |
7000f3fa RB |
178 | return 0; |
179 | ||
a1683f28 RB |
180 | old_suffix = diff_pick_suffix(delta->old_file.mode); |
181 | new_suffix = diff_pick_suffix(delta->new_file.mode); | |
7000f3fa | 182 | |
a1683f28 | 183 | git_buf_clear(out); |
7000f3fa RB |
184 | |
185 | if (delta->old_file.path != delta->new_file.path && | |
74ded024 | 186 | strcomp(delta->old_file.path,delta->new_file.path) != 0) |
df40f398 | 187 | git_buf_printf(out, "%c\t%s%c %s%c\n", code, |
7000f3fa RB |
188 | delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); |
189 | else if (delta->old_file.mode != delta->new_file.mode && | |
190 | delta->old_file.mode != 0 && delta->new_file.mode != 0) | |
df40f398 RB |
191 | git_buf_printf(out, "%c\t%s%c %s%c\n", code, |
192 | delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); | |
7000f3fa | 193 | else if (old_suffix != ' ') |
a1683f28 | 194 | git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); |
7000f3fa | 195 | else |
a1683f28 | 196 | git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); |
a1683f28 | 197 | if (git_buf_oom(out)) |
25e0b157 | 198 | return -1; |
7000f3fa | 199 | |
3b5f7954 RB |
200 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
201 | pi->line.content = git_buf_cstr(out); | |
202 | pi->line.content_len = git_buf_len(out); | |
203 | ||
25e0b157 | 204 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
205 | } |
206 | ||
a1683f28 | 207 | static int diff_print_one_raw( |
7000f3fa RB |
208 | const git_diff_delta *delta, float progress, void *data) |
209 | { | |
210 | diff_print_info *pi = data; | |
a1683f28 | 211 | git_buf *out = pi->buf; |
d68cb736 | 212 | int id_abbrev; |
7000f3fa RB |
213 | char code = git_diff_status_char(delta->status); |
214 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
215 | ||
216 | GIT_UNUSED(progress); | |
217 | ||
10672e3e | 218 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') |
7000f3fa RB |
219 | return 0; |
220 | ||
a1683f28 | 221 | git_buf_clear(out); |
7000f3fa | 222 | |
d68cb736 ET |
223 | id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev : |
224 | delta->new_file.id_abbrev; | |
225 | ||
040ec883 | 226 | if (pi->id_strlen > id_abbrev) { |
ac3d33df | 227 | git_error_set(GIT_ERROR_PATCH, |
909d5494 | 228 | "the patch input contains %d id characters (cannot print %d)", |
040ec883 | 229 | id_abbrev, pi->id_strlen); |
d68cb736 ET |
230 | return -1; |
231 | } | |
232 | ||
040ec883 ET |
233 | git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id); |
234 | git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id); | |
7000f3fa RB |
235 | |
236 | git_buf_printf( | |
040ec883 | 237 | out, (pi->id_strlen <= GIT_OID_HEXSZ) ? |
12e422a0 | 238 | ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c", |
7000f3fa RB |
239 | delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); |
240 | ||
241 | if (delta->similarity > 0) | |
a1683f28 | 242 | git_buf_printf(out, "%03u", delta->similarity); |
7000f3fa | 243 | |
e4acc3ba | 244 | if (delta->old_file.path != delta->new_file.path) |
7000f3fa | 245 | git_buf_printf( |
a1683f28 | 246 | out, "\t%s %s\n", delta->old_file.path, delta->new_file.path); |
7000f3fa RB |
247 | else |
248 | git_buf_printf( | |
a1683f28 | 249 | out, "\t%s\n", delta->old_file.path ? |
7000f3fa RB |
250 | delta->old_file.path : delta->new_file.path); |
251 | ||
a1683f28 | 252 | if (git_buf_oom(out)) |
25e0b157 | 253 | return -1; |
7000f3fa | 254 | |
3b5f7954 RB |
255 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
256 | pi->line.content = git_buf_cstr(out); | |
257 | pi->line.content_len = git_buf_len(out); | |
258 | ||
25e0b157 | 259 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
260 | } |
261 | ||
e2cdc145 ET |
262 | static int diff_print_modes( |
263 | git_buf *out, const git_diff_delta *delta) | |
264 | { | |
265 | git_buf_printf(out, "old mode %o\n", delta->old_file.mode); | |
266 | git_buf_printf(out, "new mode %o\n", delta->new_file.mode); | |
267 | ||
268 | return git_buf_oom(out) ? -1 : 0; | |
269 | } | |
270 | ||
197b8966 | 271 | static int diff_print_oid_range( |
22a2d3d5 UG |
272 | git_buf *out, const git_diff_delta *delta, int id_strlen, |
273 | bool print_index) | |
7000f3fa RB |
274 | { |
275 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
276 | ||
d68cb736 | 277 | if (delta->old_file.mode && |
040ec883 | 278 | id_strlen > delta->old_file.id_abbrev) { |
ac3d33df | 279 | git_error_set(GIT_ERROR_PATCH, |
909d5494 | 280 | "the patch input contains %d id characters (cannot print %d)", |
040ec883 | 281 | delta->old_file.id_abbrev, id_strlen); |
d68cb736 ET |
282 | return -1; |
283 | } | |
284 | ||
285 | if ((delta->new_file.mode && | |
040ec883 | 286 | id_strlen > delta->new_file.id_abbrev)) { |
ac3d33df | 287 | git_error_set(GIT_ERROR_PATCH, |
909d5494 | 288 | "the patch input contains %d id characters (cannot print %d)", |
040ec883 | 289 | delta->new_file.id_abbrev, id_strlen); |
d68cb736 ET |
290 | return -1; |
291 | } | |
292 | ||
040ec883 ET |
293 | git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id); |
294 | git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id); | |
7000f3fa | 295 | |
7000f3fa | 296 | if (delta->old_file.mode == delta->new_file.mode) { |
22a2d3d5 UG |
297 | if (print_index) |
298 | git_buf_printf(out, "index %s..%s %o\n", | |
299 | start_oid, end_oid, delta->old_file.mode); | |
7000f3fa | 300 | } else { |
e2cdc145 | 301 | if (delta->old_file.mode == 0) |
a1683f28 | 302 | git_buf_printf(out, "new file mode %o\n", delta->new_file.mode); |
e2cdc145 | 303 | else if (delta->new_file.mode == 0) |
a1683f28 | 304 | git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode); |
e2cdc145 ET |
305 | else |
306 | diff_print_modes(out, delta); | |
307 | ||
22a2d3d5 UG |
308 | if (print_index) |
309 | git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); | |
7000f3fa RB |
310 | } |
311 | ||
25e0b157 | 312 | return git_buf_oom(out) ? -1 : 0; |
7000f3fa RB |
313 | } |
314 | ||
4ac2d8ac ET |
315 | static int diff_delta_format_path( |
316 | git_buf *out, const char *prefix, const char *filename) | |
317 | { | |
318 | if (git_buf_joinpath(out, prefix, filename) < 0) | |
319 | return -1; | |
320 | ||
321 | return git_buf_quote(out); | |
322 | } | |
323 | ||
eb1c1707 | 324 | static int diff_delta_format_with_paths( |
197b8966 RB |
325 | git_buf *out, |
326 | const git_diff_delta *delta, | |
4ac2d8ac ET |
327 | const char *template, |
328 | const char *oldpath, | |
329 | const char *newpath) | |
7000f3fa | 330 | { |
22a2d3d5 | 331 | if (git_oid_is_zero(&delta->old_file.id)) |
eb1c1707 | 332 | oldpath = "/dev/null"; |
4ac2d8ac | 333 | |
22a2d3d5 | 334 | if (git_oid_is_zero(&delta->new_file.id)) |
eb1c1707 | 335 | newpath = "/dev/null"; |
eb1c1707 | 336 | |
4ac2d8ac | 337 | return git_buf_printf(out, template, oldpath, newpath); |
eb1c1707 RB |
338 | } |
339 | ||
22a2d3d5 | 340 | static int diff_delta_format_similarity_header( |
19e46645 ET |
341 | git_buf *out, |
342 | const git_diff_delta *delta) | |
343 | { | |
4ac2d8ac | 344 | git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; |
1a79cd95 | 345 | const char *type; |
4ac2d8ac ET |
346 | int error = 0; |
347 | ||
19e46645 | 348 | if (delta->similarity > 100) { |
ac3d33df | 349 | git_error_set(GIT_ERROR_PATCH, "invalid similarity %d", delta->similarity); |
4ac2d8ac ET |
350 | error = -1; |
351 | goto done; | |
19e46645 ET |
352 | } |
353 | ||
22a2d3d5 | 354 | GIT_ASSERT(delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED); |
1a79cd95 ET |
355 | if (delta->status == GIT_DELTA_RENAMED) |
356 | type = "rename"; | |
1a79cd95 | 357 | else |
22a2d3d5 | 358 | type = "copy"; |
1a79cd95 | 359 | |
4ac2d8ac | 360 | if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 || |
22a2d3d5 UG |
361 | (error = git_buf_puts(&new_path, delta->new_file.path)) < 0 || |
362 | (error = git_buf_quote(&old_path)) < 0 || | |
363 | (error = git_buf_quote(&new_path)) < 0) | |
4ac2d8ac ET |
364 | goto done; |
365 | ||
19e46645 ET |
366 | git_buf_printf(out, |
367 | "similarity index %d%%\n" | |
1a79cd95 ET |
368 | "%s from %s\n" |
369 | "%s to %s\n", | |
19e46645 | 370 | delta->similarity, |
1a79cd95 ET |
371 | type, old_path.ptr, |
372 | type, new_path.ptr); | |
19e46645 | 373 | |
4ac2d8ac ET |
374 | if (git_buf_oom(out)) |
375 | error = -1; | |
376 | ||
377 | done: | |
ac3d33df JK |
378 | git_buf_dispose(&old_path); |
379 | git_buf_dispose(&new_path); | |
4ac2d8ac ET |
380 | |
381 | return error; | |
19e46645 ET |
382 | } |
383 | ||
1a79cd95 ET |
384 | static bool delta_is_unchanged(const git_diff_delta *delta) |
385 | { | |
22a2d3d5 UG |
386 | if (git_oid_is_zero(&delta->old_file.id) && |
387 | git_oid_is_zero(&delta->new_file.id)) | |
1a79cd95 ET |
388 | return true; |
389 | ||
390 | if (delta->old_file.mode == GIT_FILEMODE_COMMIT || | |
391 | delta->new_file.mode == GIT_FILEMODE_COMMIT) | |
392 | return false; | |
393 | ||
394 | if (git_oid_equal(&delta->old_file.id, &delta->new_file.id)) | |
395 | return true; | |
396 | ||
397 | return false; | |
398 | } | |
399 | ||
eb1c1707 RB |
400 | int git_diff_delta__format_file_header( |
401 | git_buf *out, | |
402 | const git_diff_delta *delta, | |
403 | const char *oldpfx, | |
404 | const char *newpfx, | |
22a2d3d5 UG |
405 | int id_strlen, |
406 | bool print_index) | |
eb1c1707 | 407 | { |
4ac2d8ac | 408 | git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; |
1a79cd95 | 409 | bool unchanged = delta_is_unchanged(delta); |
4ac2d8ac | 410 | int error = 0; |
72806f4c | 411 | |
7000f3fa RB |
412 | if (!oldpfx) |
413 | oldpfx = DIFF_OLD_PREFIX_DEFAULT; | |
7000f3fa RB |
414 | if (!newpfx) |
415 | newpfx = DIFF_NEW_PREFIX_DEFAULT; | |
040ec883 ET |
416 | if (!id_strlen) |
417 | id_strlen = GIT_ABBREV_DEFAULT; | |
7000f3fa | 418 | |
4ac2d8ac ET |
419 | if ((error = diff_delta_format_path( |
420 | &old_path, oldpfx, delta->old_file.path)) < 0 || | |
421 | (error = diff_delta_format_path( | |
422 | &new_path, newpfx, delta->new_file.path)) < 0) | |
423 | goto done; | |
424 | ||
197b8966 RB |
425 | git_buf_clear(out); |
426 | ||
4ac2d8ac ET |
427 | git_buf_printf(out, "diff --git %s %s\n", |
428 | old_path.ptr, new_path.ptr); | |
7000f3fa | 429 | |
22a2d3d5 UG |
430 | if (unchanged && delta->old_file.mode != delta->new_file.mode) |
431 | diff_print_modes(out, delta); | |
432 | ||
1a79cd95 | 433 | if (delta->status == GIT_DELTA_RENAMED || |
22a2d3d5 | 434 | (delta->status == GIT_DELTA_COPIED && unchanged)) { |
1a79cd95 | 435 | if ((error = diff_delta_format_similarity_header(out, delta)) < 0) |
4ac2d8ac ET |
436 | goto done; |
437 | } | |
19e46645 | 438 | |
e2cdc145 | 439 | if (!unchanged) { |
22a2d3d5 UG |
440 | if ((error = diff_print_oid_range(out, delta, |
441 | id_strlen, print_index)) < 0) | |
4ac2d8ac | 442 | goto done; |
7000f3fa | 443 | |
72806f4c | 444 | if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) |
4ac2d8ac ET |
445 | diff_delta_format_with_paths(out, delta, |
446 | "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr); | |
72806f4c | 447 | } |
7000f3fa | 448 | |
4ac2d8ac ET |
449 | if (git_buf_oom(out)) |
450 | error = -1; | |
451 | ||
452 | done: | |
ac3d33df JK |
453 | git_buf_dispose(&old_path); |
454 | git_buf_dispose(&new_path); | |
4ac2d8ac ET |
455 | |
456 | return error; | |
197b8966 | 457 | } |
7000f3fa | 458 | |
8147b1af ET |
459 | static int format_binary( |
460 | diff_print_info *pi, | |
461 | git_diff_binary_t type, | |
462 | const char *data, | |
463 | size_t datalen, | |
464 | size_t inflatedlen) | |
e349ed50 | 465 | { |
8147b1af ET |
466 | const char *typename = type == GIT_DIFF_BINARY_DELTA ? |
467 | "delta" : "literal"; | |
468 | const char *scan, *end; | |
e349ed50 | 469 | |
49840056 | 470 | git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen); |
e349ed50 ET |
471 | pi->line.num_lines++; |
472 | ||
8147b1af | 473 | for (scan = data, end = data + datalen; scan < end; ) { |
947a58c1 RB |
474 | size_t chunk_len = end - scan; |
475 | if (chunk_len > 52) | |
476 | chunk_len = 52; | |
e349ed50 ET |
477 | |
478 | if (chunk_len <= 26) | |
c6320bec | 479 | git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); |
e349ed50 | 480 | else |
c6320bec | 481 | git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); |
e349ed50 | 482 | |
e003f83a | 483 | git_buf_encode_base85(pi->buf, scan, chunk_len); |
e349ed50 ET |
484 | git_buf_putc(pi->buf, '\n'); |
485 | ||
8147b1af ET |
486 | if (git_buf_oom(pi->buf)) |
487 | return -1; | |
e349ed50 | 488 | |
947a58c1 | 489 | scan += chunk_len; |
e349ed50 ET |
490 | pi->line.num_lines++; |
491 | } | |
e4b2b919 | 492 | git_buf_putc(pi->buf, '\n'); |
e349ed50 | 493 | |
22a2d3d5 UG |
494 | if (git_buf_oom(pi->buf)) |
495 | return -1; | |
496 | ||
8147b1af ET |
497 | return 0; |
498 | } | |
e349ed50 | 499 | |
4ac2d8ac ET |
500 | static int diff_print_patch_file_binary_noshow( |
501 | diff_print_info *pi, git_diff_delta *delta, | |
f941f035 | 502 | const char *old_pfx, const char *new_pfx) |
4ac2d8ac ET |
503 | { |
504 | git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; | |
505 | int error; | |
506 | ||
22a2d3d5 UG |
507 | if ((error = diff_delta_format_path(&old_path, old_pfx, delta->old_file.path)) < 0 || |
508 | (error = diff_delta_format_path(&new_path, new_pfx, delta->new_file.path)) < 0 || | |
509 | (error = diff_delta_format_with_paths(pi->buf, delta, "Binary files %s and %s differ\n", | |
510 | old_path.ptr, new_path.ptr)) < 0) | |
4ac2d8ac ET |
511 | goto done; |
512 | ||
4ac2d8ac | 513 | pi->line.num_lines = 1; |
4ac2d8ac ET |
514 | |
515 | done: | |
ac3d33df JK |
516 | git_buf_dispose(&old_path); |
517 | git_buf_dispose(&new_path); | |
4ac2d8ac ET |
518 | return error; |
519 | } | |
520 | ||
e349ed50 | 521 | static int diff_print_patch_file_binary( |
8147b1af ET |
522 | diff_print_info *pi, git_diff_delta *delta, |
523 | const char *old_pfx, const char *new_pfx, | |
524 | const git_diff_binary *binary) | |
e349ed50 | 525 | { |
947a58c1 | 526 | size_t pre_binary_size; |
8147b1af | 527 | int error; |
e349ed50 | 528 | |
f4e3dae7 ET |
529 | if (delta->status == GIT_DELTA_UNMODIFIED) |
530 | return 0; | |
531 | ||
adedac5a | 532 | if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data) |
4ac2d8ac | 533 | return diff_print_patch_file_binary_noshow( |
f941f035 | 534 | pi, delta, old_pfx, new_pfx); |
e349ed50 | 535 | |
947a58c1 | 536 | pre_binary_size = pi->buf->size; |
e349ed50 ET |
537 | git_buf_printf(pi->buf, "GIT binary patch\n"); |
538 | pi->line.num_lines++; | |
539 | ||
8147b1af | 540 | if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, |
22a2d3d5 UG |
541 | binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || |
542 | (error = format_binary(pi, binary->old_file.type, binary->old_file.data, | |
543 | binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { | |
947a58c1 | 544 | if (error == GIT_EBUFS) { |
ac3d33df | 545 | git_error_clear(); |
947a58c1 | 546 | git_buf_truncate(pi->buf, pre_binary_size); |
4ac2d8ac ET |
547 | |
548 | return diff_print_patch_file_binary_noshow( | |
f941f035 | 549 | pi, delta, old_pfx, new_pfx); |
947a58c1 RB |
550 | } |
551 | } | |
e349ed50 ET |
552 | |
553 | pi->line.num_lines++; | |
e349ed50 ET |
554 | return error; |
555 | } | |
556 | ||
197b8966 RB |
557 | static int diff_print_patch_file( |
558 | const git_diff_delta *delta, float progress, void *data) | |
559 | { | |
25e0b157 | 560 | int error; |
197b8966 | 561 | diff_print_info *pi = data; |
eb1c1707 | 562 | const char *oldpfx = |
804d5fe9 | 563 | pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; |
eb1c1707 | 564 | const char *newpfx = |
804d5fe9 | 565 | pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; |
197b8966 | 566 | |
8147b1af ET |
567 | bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) || |
568 | (pi->flags & GIT_DIFF_FORCE_BINARY); | |
e349ed50 | 569 | bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); |
adedac5a | 570 | int id_strlen = pi->id_strlen; |
22a2d3d5 | 571 | bool print_index = (pi->format != GIT_DIFF_FORMAT_PATCH_ID); |
adedac5a ET |
572 | |
573 | if (binary && show_binary) | |
574 | id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev : | |
575 | delta->new_file.id_abbrev; | |
e349ed50 | 576 | |
197b8966 | 577 | GIT_UNUSED(progress); |
7000f3fa | 578 | |
197b8966 | 579 | if (S_ISDIR(delta->new_file.mode) || |
22a2d3d5 UG |
580 | delta->status == GIT_DELTA_UNMODIFIED || |
581 | delta->status == GIT_DELTA_IGNORED || | |
582 | delta->status == GIT_DELTA_UNREADABLE || | |
583 | (delta->status == GIT_DELTA_UNTRACKED && | |
10672e3e | 584 | (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) |
7000f3fa RB |
585 | return 0; |
586 | ||
22a2d3d5 UG |
587 | if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx, |
588 | id_strlen, print_index)) < 0) | |
25e0b157 | 589 | return error; |
7000f3fa | 590 | |
3b5f7954 RB |
591 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
592 | pi->line.content = git_buf_cstr(pi->buf); | |
593 | pi->line.content_len = git_buf_len(pi->buf); | |
594 | ||
8147b1af ET |
595 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
596 | } | |
eb1c1707 | 597 | |
8147b1af ET |
598 | static int diff_print_patch_binary( |
599 | const git_diff_delta *delta, | |
600 | const git_diff_binary *binary, | |
601 | void *data) | |
602 | { | |
603 | diff_print_info *pi = data; | |
604 | const char *old_pfx = | |
804d5fe9 | 605 | pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; |
8147b1af | 606 | const char *new_pfx = |
804d5fe9 | 607 | pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; |
8147b1af | 608 | int error; |
eb1c1707 RB |
609 | |
610 | git_buf_clear(pi->buf); | |
611 | ||
8147b1af ET |
612 | if ((error = diff_print_patch_file_binary( |
613 | pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0) | |
25e0b157 | 614 | return error; |
eb1c1707 | 615 | |
8147b1af ET |
616 | pi->line.origin = GIT_DIFF_LINE_BINARY; |
617 | pi->line.content = git_buf_cstr(pi->buf); | |
3b5f7954 | 618 | pi->line.content_len = git_buf_len(pi->buf); |
3b5f7954 | 619 | |
25e0b157 | 620 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
621 | } |
622 | ||
a1683f28 | 623 | static int diff_print_patch_hunk( |
7000f3fa | 624 | const git_diff_delta *d, |
3b5f7954 | 625 | const git_diff_hunk *h, |
7000f3fa RB |
626 | void *data) |
627 | { | |
628 | diff_print_info *pi = data; | |
629 | ||
630 | if (S_ISDIR(d->new_file.mode)) | |
631 | return 0; | |
632 | ||
3b5f7954 RB |
633 | pi->line.origin = GIT_DIFF_LINE_HUNK_HDR; |
634 | pi->line.content = h->header; | |
635 | pi->line.content_len = h->header_len; | |
7000f3fa | 636 | |
25e0b157 | 637 | return pi->print_cb(d, h, &pi->line, pi->payload); |
7000f3fa RB |
638 | } |
639 | ||
a1683f28 | 640 | static int diff_print_patch_line( |
7000f3fa | 641 | const git_diff_delta *delta, |
3b5f7954 RB |
642 | const git_diff_hunk *hunk, |
643 | const git_diff_line *line, | |
7000f3fa RB |
644 | void *data) |
645 | { | |
646 | diff_print_info *pi = data; | |
647 | ||
648 | if (S_ISDIR(delta->new_file.mode)) | |
649 | return 0; | |
650 | ||
25e0b157 | 651 | return pi->print_cb(delta, hunk, line, pi->payload); |
7000f3fa RB |
652 | } |
653 | ||
10672e3e RB |
654 | /* print a git_diff to an output callback */ |
655 | int git_diff_print( | |
3ff1d123 | 656 | git_diff *diff, |
10672e3e | 657 | git_diff_format_t format, |
3ff1d123 | 658 | git_diff_line_cb print_cb, |
7000f3fa RB |
659 | void *payload) |
660 | { | |
661 | int error; | |
662 | git_buf buf = GIT_BUF_INIT; | |
663 | diff_print_info pi; | |
10672e3e | 664 | git_diff_file_cb print_file = NULL; |
8147b1af | 665 | git_diff_binary_cb print_binary = NULL; |
10672e3e RB |
666 | git_diff_hunk_cb print_hunk = NULL; |
667 | git_diff_line_cb print_line = NULL; | |
668 | ||
669 | switch (format) { | |
670 | case GIT_DIFF_FORMAT_PATCH: | |
671 | print_file = diff_print_patch_file; | |
8147b1af | 672 | print_binary = diff_print_patch_binary; |
10672e3e RB |
673 | print_hunk = diff_print_patch_hunk; |
674 | print_line = diff_print_patch_line; | |
675 | break; | |
22a2d3d5 UG |
676 | case GIT_DIFF_FORMAT_PATCH_ID: |
677 | print_file = diff_print_patch_file; | |
678 | print_binary = diff_print_patch_binary; | |
679 | print_line = diff_print_patch_line; | |
680 | break; | |
10672e3e RB |
681 | case GIT_DIFF_FORMAT_PATCH_HEADER: |
682 | print_file = diff_print_patch_file; | |
683 | break; | |
684 | case GIT_DIFF_FORMAT_RAW: | |
685 | print_file = diff_print_one_raw; | |
686 | break; | |
687 | case GIT_DIFF_FORMAT_NAME_ONLY: | |
688 | print_file = diff_print_one_name_only; | |
689 | break; | |
690 | case GIT_DIFF_FORMAT_NAME_STATUS: | |
691 | print_file = diff_print_one_name_status; | |
692 | break; | |
693 | default: | |
ac3d33df | 694 | git_error_set(GIT_ERROR_INVALID, "unknown diff output format (%d)", format); |
10672e3e RB |
695 | return -1; |
696 | } | |
7000f3fa | 697 | |
22a2d3d5 UG |
698 | if ((error = diff_print_info_init_fromdiff(&pi, &buf, diff, format, print_cb, payload)) < 0) |
699 | goto out; | |
7000f3fa | 700 | |
22a2d3d5 UG |
701 | if ((error = git_diff_foreach(diff, print_file, print_binary, print_hunk, print_line, &pi)) != 0) { |
702 | git_error_set_after_callback_function(error, "git_diff_print"); | |
703 | goto out; | |
96869a4e RB |
704 | } |
705 | ||
22a2d3d5 | 706 | out: |
ac3d33df | 707 | git_buf_dispose(&buf); |
7000f3fa RB |
708 | return error; |
709 | } | |
710 | ||
27e54bcf | 711 | int git_diff_print_callback__to_buf( |
a1683f28 | 712 | const git_diff_delta *delta, |
3b5f7954 RB |
713 | const git_diff_hunk *hunk, |
714 | const git_diff_line *line, | |
a1683f28 RB |
715 | void *payload) |
716 | { | |
717 | git_buf *output = payload; | |
3b5f7954 RB |
718 | GIT_UNUSED(delta); GIT_UNUSED(hunk); |
719 | ||
27e54bcf | 720 | if (!output) { |
ac3d33df | 721 | git_error_set(GIT_ERROR_INVALID, "buffer pointer must be provided"); |
27e54bcf RB |
722 | return -1; |
723 | } | |
724 | ||
3b5f7954 | 725 | if (line->origin == GIT_DIFF_LINE_ADDITION || |
22a2d3d5 UG |
726 | line->origin == GIT_DIFF_LINE_DELETION || |
727 | line->origin == GIT_DIFF_LINE_CONTEXT) | |
3b5f7954 RB |
728 | git_buf_putc(output, line->origin); |
729 | ||
730 | return git_buf_put(output, line->content, line->content_len); | |
a1683f28 RB |
731 | } |
732 | ||
27e54bcf RB |
733 | int git_diff_print_callback__to_file_handle( |
734 | const git_diff_delta *delta, | |
735 | const git_diff_hunk *hunk, | |
736 | const git_diff_line *line, | |
737 | void *payload) | |
738 | { | |
739 | FILE *fp = payload ? payload : stdout; | |
22a2d3d5 | 740 | int error; |
27e54bcf | 741 | |
22a2d3d5 UG |
742 | GIT_UNUSED(delta); |
743 | GIT_UNUSED(hunk); | |
27e54bcf RB |
744 | |
745 | if (line->origin == GIT_DIFF_LINE_CONTEXT || | |
22a2d3d5 UG |
746 | line->origin == GIT_DIFF_LINE_ADDITION || |
747 | line->origin == GIT_DIFF_LINE_DELETION) { | |
748 | while ((error = fputc(line->origin, fp)) == EINTR) | |
749 | continue; | |
750 | if (error) { | |
751 | git_error_set(GIT_ERROR_OS, "could not write status"); | |
752 | return -1; | |
753 | } | |
754 | } | |
755 | ||
756 | if (fwrite(line->content, line->content_len, 1, fp) != 1) { | |
757 | git_error_set(GIT_ERROR_OS, "could not write line"); | |
758 | return -1; | |
759 | } | |
760 | ||
27e54bcf RB |
761 | return 0; |
762 | } | |
763 | ||
72827490 ET |
764 | /* print a git_diff to a git_buf */ |
765 | int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format) | |
766 | { | |
c25aa7cd PP |
767 | int error; |
768 | ||
769 | GIT_ASSERT_ARG(out); | |
770 | GIT_ASSERT_ARG(diff); | |
771 | ||
772 | if ((error = git_buf_sanitize(out)) < 0) | |
773 | return error; | |
774 | ||
22a2d3d5 | 775 | return git_diff_print(diff, format, git_diff_print_callback__to_buf, out); |
72827490 ET |
776 | } |
777 | ||
804d5fe9 ET |
778 | /* print a git_patch to an output callback */ |
779 | int git_patch_print( | |
780 | git_patch *patch, | |
781 | git_diff_line_cb print_cb, | |
782 | void *payload) | |
783 | { | |
804d5fe9 ET |
784 | git_buf temp = GIT_BUF_INIT; |
785 | diff_print_info pi; | |
22a2d3d5 | 786 | int error; |
804d5fe9 | 787 | |
c25aa7cd PP |
788 | GIT_ASSERT_ARG(patch); |
789 | GIT_ASSERT_ARG(print_cb); | |
804d5fe9 | 790 | |
22a2d3d5 UG |
791 | if ((error = diff_print_info_init_frompatch(&pi, &temp, patch, |
792 | GIT_DIFF_FORMAT_PATCH, print_cb, payload)) < 0) | |
793 | goto out; | |
794 | ||
795 | if ((error = git_patch__invoke_callbacks(patch, diff_print_patch_file, diff_print_patch_binary, | |
796 | diff_print_patch_hunk, diff_print_patch_line, &pi)) < 0) { | |
797 | git_error_set_after_callback_function(error, "git_patch_print"); | |
798 | goto out; | |
804d5fe9 ET |
799 | } |
800 | ||
22a2d3d5 | 801 | out: |
ac3d33df | 802 | git_buf_dispose(&temp); |
804d5fe9 ET |
803 | return error; |
804 | } | |
805 | ||
450e8e9e | 806 | /* print a git_patch to a git_buf */ |
1e4976cb | 807 | int git_patch_to_buf(git_buf *out, git_patch *patch) |
450e8e9e | 808 | { |
c25aa7cd PP |
809 | int error; |
810 | ||
811 | GIT_ASSERT_ARG(out); | |
812 | GIT_ASSERT_ARG(patch); | |
813 | ||
814 | if ((error = git_buf_sanitize(out)) < 0) | |
815 | return error; | |
816 | ||
27e54bcf | 817 | return git_patch_print(patch, git_diff_print_callback__to_buf, out); |
450e8e9e | 818 | } |