]>
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" |
f240acce | 13 | #include "fileops.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 ET |
50 | pi->id_strlen = GIT_ABBREV_DEFAULT; |
51 | else if (git_repository__cvar(&pi->id_strlen, repo, GIT_CVAR_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 | { | |
be8479c9 PS |
98 | assert(patch); |
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) { |
d68cb736 | 227 | giterr_set(GITERR_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( |
040ec883 | 272 | git_buf *out, const git_diff_delta *delta, int id_strlen) |
7000f3fa RB |
273 | { |
274 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
275 | ||
d68cb736 | 276 | if (delta->old_file.mode && |
040ec883 | 277 | id_strlen > delta->old_file.id_abbrev) { |
d68cb736 | 278 | giterr_set(GITERR_PATCH, |
909d5494 | 279 | "the patch input contains %d id characters (cannot print %d)", |
040ec883 | 280 | delta->old_file.id_abbrev, id_strlen); |
d68cb736 ET |
281 | return -1; |
282 | } | |
283 | ||
284 | if ((delta->new_file.mode && | |
040ec883 | 285 | id_strlen > delta->new_file.id_abbrev)) { |
d68cb736 | 286 | giterr_set(GITERR_PATCH, |
909d5494 | 287 | "the patch input contains %d id characters (cannot print %d)", |
040ec883 | 288 | delta->new_file.id_abbrev, id_strlen); |
d68cb736 ET |
289 | return -1; |
290 | } | |
291 | ||
040ec883 ET |
292 | git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id); |
293 | git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id); | |
7000f3fa | 294 | |
7000f3fa | 295 | if (delta->old_file.mode == delta->new_file.mode) { |
a1683f28 | 296 | git_buf_printf(out, "index %s..%s %o\n", |
7000f3fa RB |
297 | start_oid, end_oid, delta->old_file.mode); |
298 | } else { | |
e2cdc145 | 299 | if (delta->old_file.mode == 0) |
a1683f28 | 300 | git_buf_printf(out, "new file mode %o\n", delta->new_file.mode); |
e2cdc145 | 301 | else if (delta->new_file.mode == 0) |
a1683f28 | 302 | git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode); |
e2cdc145 ET |
303 | else |
304 | diff_print_modes(out, delta); | |
305 | ||
a1683f28 | 306 | git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); |
7000f3fa RB |
307 | } |
308 | ||
25e0b157 | 309 | return git_buf_oom(out) ? -1 : 0; |
7000f3fa RB |
310 | } |
311 | ||
4ac2d8ac ET |
312 | static int diff_delta_format_path( |
313 | git_buf *out, const char *prefix, const char *filename) | |
314 | { | |
315 | if (git_buf_joinpath(out, prefix, filename) < 0) | |
316 | return -1; | |
317 | ||
318 | return git_buf_quote(out); | |
319 | } | |
320 | ||
eb1c1707 | 321 | static int diff_delta_format_with_paths( |
197b8966 RB |
322 | git_buf *out, |
323 | const git_diff_delta *delta, | |
4ac2d8ac ET |
324 | const char *template, |
325 | const char *oldpath, | |
326 | const char *newpath) | |
7000f3fa | 327 | { |
4ac2d8ac | 328 | if (git_oid_iszero(&delta->old_file.id)) |
eb1c1707 | 329 | oldpath = "/dev/null"; |
4ac2d8ac ET |
330 | |
331 | if (git_oid_iszero(&delta->new_file.id)) | |
eb1c1707 | 332 | newpath = "/dev/null"; |
eb1c1707 | 333 | |
4ac2d8ac | 334 | return git_buf_printf(out, template, oldpath, newpath); |
eb1c1707 RB |
335 | } |
336 | ||
1a79cd95 | 337 | int diff_delta_format_similarity_header( |
19e46645 ET |
338 | git_buf *out, |
339 | const git_diff_delta *delta) | |
340 | { | |
4ac2d8ac | 341 | git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; |
1a79cd95 | 342 | const char *type; |
4ac2d8ac ET |
343 | int error = 0; |
344 | ||
19e46645 ET |
345 | if (delta->similarity > 100) { |
346 | giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity); | |
4ac2d8ac ET |
347 | error = -1; |
348 | goto done; | |
19e46645 ET |
349 | } |
350 | ||
1a79cd95 ET |
351 | if (delta->status == GIT_DELTA_RENAMED) |
352 | type = "rename"; | |
353 | else if (delta->status == GIT_DELTA_COPIED) | |
354 | type = "copy"; | |
355 | else | |
356 | abort(); | |
357 | ||
4ac2d8ac ET |
358 | if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 || |
359 | (error = git_buf_puts(&new_path, delta->new_file.path)) < 0 || | |
360 | (error = git_buf_quote(&old_path)) < 0 || | |
361 | (error = git_buf_quote(&new_path)) < 0) | |
362 | goto done; | |
363 | ||
19e46645 ET |
364 | git_buf_printf(out, |
365 | "similarity index %d%%\n" | |
1a79cd95 ET |
366 | "%s from %s\n" |
367 | "%s to %s\n", | |
19e46645 | 368 | delta->similarity, |
1a79cd95 ET |
369 | type, old_path.ptr, |
370 | type, new_path.ptr); | |
19e46645 | 371 | |
4ac2d8ac ET |
372 | if (git_buf_oom(out)) |
373 | error = -1; | |
374 | ||
375 | done: | |
376 | git_buf_free(&old_path); | |
377 | git_buf_free(&new_path); | |
378 | ||
379 | return error; | |
19e46645 ET |
380 | } |
381 | ||
1a79cd95 ET |
382 | static bool delta_is_unchanged(const git_diff_delta *delta) |
383 | { | |
384 | if (git_oid_iszero(&delta->old_file.id) && | |
385 | git_oid_iszero(&delta->new_file.id)) | |
386 | return true; | |
387 | ||
388 | if (delta->old_file.mode == GIT_FILEMODE_COMMIT || | |
389 | delta->new_file.mode == GIT_FILEMODE_COMMIT) | |
390 | return false; | |
391 | ||
392 | if (git_oid_equal(&delta->old_file.id, &delta->new_file.id)) | |
393 | return true; | |
394 | ||
395 | return false; | |
396 | } | |
397 | ||
eb1c1707 RB |
398 | int git_diff_delta__format_file_header( |
399 | git_buf *out, | |
400 | const git_diff_delta *delta, | |
401 | const char *oldpfx, | |
402 | const char *newpfx, | |
040ec883 | 403 | int id_strlen) |
eb1c1707 | 404 | { |
4ac2d8ac | 405 | git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; |
1a79cd95 | 406 | bool unchanged = delta_is_unchanged(delta); |
4ac2d8ac | 407 | int error = 0; |
72806f4c | 408 | |
7000f3fa RB |
409 | if (!oldpfx) |
410 | oldpfx = DIFF_OLD_PREFIX_DEFAULT; | |
7000f3fa RB |
411 | if (!newpfx) |
412 | newpfx = DIFF_NEW_PREFIX_DEFAULT; | |
040ec883 ET |
413 | if (!id_strlen) |
414 | id_strlen = GIT_ABBREV_DEFAULT; | |
7000f3fa | 415 | |
4ac2d8ac ET |
416 | if ((error = diff_delta_format_path( |
417 | &old_path, oldpfx, delta->old_file.path)) < 0 || | |
418 | (error = diff_delta_format_path( | |
419 | &new_path, newpfx, delta->new_file.path)) < 0) | |
420 | goto done; | |
421 | ||
197b8966 RB |
422 | git_buf_clear(out); |
423 | ||
4ac2d8ac ET |
424 | git_buf_printf(out, "diff --git %s %s\n", |
425 | old_path.ptr, new_path.ptr); | |
7000f3fa | 426 | |
1a79cd95 ET |
427 | if (delta->status == GIT_DELTA_RENAMED || |
428 | (delta->status == GIT_DELTA_COPIED && unchanged)) { | |
429 | if ((error = diff_delta_format_similarity_header(out, delta)) < 0) | |
4ac2d8ac ET |
430 | goto done; |
431 | } | |
19e46645 | 432 | |
e2cdc145 | 433 | if (!unchanged) { |
040ec883 | 434 | if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0) |
4ac2d8ac | 435 | goto done; |
7000f3fa | 436 | |
72806f4c | 437 | if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) |
4ac2d8ac ET |
438 | diff_delta_format_with_paths(out, delta, |
439 | "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr); | |
72806f4c | 440 | } |
7000f3fa | 441 | |
e2cdc145 ET |
442 | if (unchanged && delta->old_file.mode != delta->new_file.mode) |
443 | diff_print_modes(out, delta); | |
444 | ||
4ac2d8ac ET |
445 | if (git_buf_oom(out)) |
446 | error = -1; | |
447 | ||
448 | done: | |
449 | git_buf_free(&old_path); | |
450 | git_buf_free(&new_path); | |
451 | ||
452 | return error; | |
197b8966 | 453 | } |
7000f3fa | 454 | |
8147b1af ET |
455 | static int format_binary( |
456 | diff_print_info *pi, | |
457 | git_diff_binary_t type, | |
458 | const char *data, | |
459 | size_t datalen, | |
460 | size_t inflatedlen) | |
e349ed50 | 461 | { |
8147b1af ET |
462 | const char *typename = type == GIT_DIFF_BINARY_DELTA ? |
463 | "delta" : "literal"; | |
464 | const char *scan, *end; | |
e349ed50 | 465 | |
49840056 | 466 | git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen); |
e349ed50 ET |
467 | pi->line.num_lines++; |
468 | ||
8147b1af | 469 | for (scan = data, end = data + datalen; scan < end; ) { |
947a58c1 RB |
470 | size_t chunk_len = end - scan; |
471 | if (chunk_len > 52) | |
472 | chunk_len = 52; | |
e349ed50 ET |
473 | |
474 | if (chunk_len <= 26) | |
c6320bec | 475 | git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); |
e349ed50 | 476 | else |
c6320bec | 477 | git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); |
e349ed50 | 478 | |
e003f83a | 479 | git_buf_encode_base85(pi->buf, scan, chunk_len); |
e349ed50 ET |
480 | git_buf_putc(pi->buf, '\n'); |
481 | ||
8147b1af ET |
482 | if (git_buf_oom(pi->buf)) |
483 | return -1; | |
e349ed50 | 484 | |
947a58c1 | 485 | scan += chunk_len; |
e349ed50 ET |
486 | pi->line.num_lines++; |
487 | } | |
e4b2b919 | 488 | git_buf_putc(pi->buf, '\n'); |
e349ed50 | 489 | |
8147b1af ET |
490 | return 0; |
491 | } | |
e349ed50 | 492 | |
4ac2d8ac ET |
493 | static int diff_print_patch_file_binary_noshow( |
494 | diff_print_info *pi, git_diff_delta *delta, | |
f941f035 | 495 | const char *old_pfx, const char *new_pfx) |
4ac2d8ac ET |
496 | { |
497 | git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT; | |
498 | int error; | |
499 | ||
500 | if ((error = diff_delta_format_path( | |
501 | &old_path, old_pfx, delta->old_file.path)) < 0 || | |
502 | (error = diff_delta_format_path( | |
503 | &new_path, new_pfx, delta->new_file.path)) < 0) | |
504 | goto done; | |
505 | ||
4ac2d8ac ET |
506 | pi->line.num_lines = 1; |
507 | error = diff_delta_format_with_paths( | |
508 | pi->buf, delta, "Binary files %s and %s differ\n", | |
509 | old_path.ptr, new_path.ptr); | |
510 | ||
511 | done: | |
512 | git_buf_free(&old_path); | |
513 | git_buf_free(&new_path); | |
514 | ||
515 | return error; | |
516 | } | |
517 | ||
e349ed50 | 518 | static int diff_print_patch_file_binary( |
8147b1af ET |
519 | diff_print_info *pi, git_diff_delta *delta, |
520 | const char *old_pfx, const char *new_pfx, | |
521 | const git_diff_binary *binary) | |
e349ed50 | 522 | { |
947a58c1 | 523 | size_t pre_binary_size; |
8147b1af | 524 | int error; |
e349ed50 | 525 | |
f4e3dae7 ET |
526 | if (delta->status == GIT_DELTA_UNMODIFIED) |
527 | return 0; | |
528 | ||
adedac5a | 529 | if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data) |
4ac2d8ac | 530 | return diff_print_patch_file_binary_noshow( |
f941f035 | 531 | pi, delta, old_pfx, new_pfx); |
e349ed50 | 532 | |
947a58c1 | 533 | pre_binary_size = pi->buf->size; |
e349ed50 ET |
534 | git_buf_printf(pi->buf, "GIT binary patch\n"); |
535 | pi->line.num_lines++; | |
536 | ||
8147b1af ET |
537 | if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, |
538 | binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || | |
8147b1af ET |
539 | (error = format_binary(pi, binary->old_file.type, binary->old_file.data, |
540 | binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { | |
541 | ||
947a58c1 RB |
542 | if (error == GIT_EBUFS) { |
543 | giterr_clear(); | |
544 | git_buf_truncate(pi->buf, pre_binary_size); | |
4ac2d8ac ET |
545 | |
546 | return diff_print_patch_file_binary_noshow( | |
f941f035 | 547 | pi, delta, old_pfx, new_pfx); |
947a58c1 RB |
548 | } |
549 | } | |
e349ed50 ET |
550 | |
551 | pi->line.num_lines++; | |
e349ed50 ET |
552 | return error; |
553 | } | |
554 | ||
197b8966 RB |
555 | static int diff_print_patch_file( |
556 | const git_diff_delta *delta, float progress, void *data) | |
557 | { | |
25e0b157 | 558 | int error; |
197b8966 | 559 | diff_print_info *pi = data; |
eb1c1707 | 560 | const char *oldpfx = |
804d5fe9 | 561 | pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; |
eb1c1707 | 562 | const char *newpfx = |
804d5fe9 | 563 | pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; |
197b8966 | 564 | |
8147b1af ET |
565 | bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) || |
566 | (pi->flags & GIT_DIFF_FORCE_BINARY); | |
e349ed50 | 567 | bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); |
adedac5a ET |
568 | int id_strlen = pi->id_strlen; |
569 | ||
570 | if (binary && show_binary) | |
571 | id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev : | |
572 | delta->new_file.id_abbrev; | |
e349ed50 | 573 | |
197b8966 | 574 | GIT_UNUSED(progress); |
7000f3fa | 575 | |
197b8966 RB |
576 | if (S_ISDIR(delta->new_file.mode) || |
577 | delta->status == GIT_DELTA_UNMODIFIED || | |
578 | delta->status == GIT_DELTA_IGNORED || | |
61bef72d | 579 | delta->status == GIT_DELTA_UNREADABLE || |
197b8966 | 580 | (delta->status == GIT_DELTA_UNTRACKED && |
10672e3e | 581 | (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) |
7000f3fa RB |
582 | return 0; |
583 | ||
25e0b157 | 584 | if ((error = git_diff_delta__format_file_header( |
040ec883 | 585 | pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0) |
25e0b157 | 586 | return error; |
7000f3fa | 587 | |
3b5f7954 RB |
588 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
589 | pi->line.content = git_buf_cstr(pi->buf); | |
590 | pi->line.content_len = git_buf_len(pi->buf); | |
591 | ||
8147b1af ET |
592 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
593 | } | |
eb1c1707 | 594 | |
8147b1af ET |
595 | static int diff_print_patch_binary( |
596 | const git_diff_delta *delta, | |
597 | const git_diff_binary *binary, | |
598 | void *data) | |
599 | { | |
600 | diff_print_info *pi = data; | |
601 | const char *old_pfx = | |
804d5fe9 | 602 | pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; |
8147b1af | 603 | const char *new_pfx = |
804d5fe9 | 604 | pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; |
8147b1af | 605 | int error; |
eb1c1707 RB |
606 | |
607 | git_buf_clear(pi->buf); | |
608 | ||
8147b1af ET |
609 | if ((error = diff_print_patch_file_binary( |
610 | pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0) | |
25e0b157 | 611 | return error; |
eb1c1707 | 612 | |
8147b1af ET |
613 | pi->line.origin = GIT_DIFF_LINE_BINARY; |
614 | pi->line.content = git_buf_cstr(pi->buf); | |
3b5f7954 | 615 | pi->line.content_len = git_buf_len(pi->buf); |
3b5f7954 | 616 | |
25e0b157 | 617 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
618 | } |
619 | ||
a1683f28 | 620 | static int diff_print_patch_hunk( |
7000f3fa | 621 | const git_diff_delta *d, |
3b5f7954 | 622 | const git_diff_hunk *h, |
7000f3fa RB |
623 | void *data) |
624 | { | |
625 | diff_print_info *pi = data; | |
626 | ||
627 | if (S_ISDIR(d->new_file.mode)) | |
628 | return 0; | |
629 | ||
3b5f7954 RB |
630 | pi->line.origin = GIT_DIFF_LINE_HUNK_HDR; |
631 | pi->line.content = h->header; | |
632 | pi->line.content_len = h->header_len; | |
7000f3fa | 633 | |
25e0b157 | 634 | return pi->print_cb(d, h, &pi->line, pi->payload); |
7000f3fa RB |
635 | } |
636 | ||
a1683f28 | 637 | static int diff_print_patch_line( |
7000f3fa | 638 | const git_diff_delta *delta, |
3b5f7954 RB |
639 | const git_diff_hunk *hunk, |
640 | const git_diff_line *line, | |
7000f3fa RB |
641 | void *data) |
642 | { | |
643 | diff_print_info *pi = data; | |
644 | ||
645 | if (S_ISDIR(delta->new_file.mode)) | |
646 | return 0; | |
647 | ||
25e0b157 | 648 | return pi->print_cb(delta, hunk, line, pi->payload); |
7000f3fa RB |
649 | } |
650 | ||
10672e3e RB |
651 | /* print a git_diff to an output callback */ |
652 | int git_diff_print( | |
3ff1d123 | 653 | git_diff *diff, |
10672e3e | 654 | git_diff_format_t format, |
3ff1d123 | 655 | git_diff_line_cb print_cb, |
7000f3fa RB |
656 | void *payload) |
657 | { | |
658 | int error; | |
659 | git_buf buf = GIT_BUF_INIT; | |
660 | diff_print_info pi; | |
10672e3e | 661 | git_diff_file_cb print_file = NULL; |
8147b1af | 662 | git_diff_binary_cb print_binary = NULL; |
10672e3e RB |
663 | git_diff_hunk_cb print_hunk = NULL; |
664 | git_diff_line_cb print_line = NULL; | |
665 | ||
666 | switch (format) { | |
667 | case GIT_DIFF_FORMAT_PATCH: | |
668 | print_file = diff_print_patch_file; | |
8147b1af | 669 | print_binary = diff_print_patch_binary; |
10672e3e RB |
670 | print_hunk = diff_print_patch_hunk; |
671 | print_line = diff_print_patch_line; | |
672 | break; | |
673 | case GIT_DIFF_FORMAT_PATCH_HEADER: | |
674 | print_file = diff_print_patch_file; | |
675 | break; | |
676 | case GIT_DIFF_FORMAT_RAW: | |
677 | print_file = diff_print_one_raw; | |
678 | break; | |
679 | case GIT_DIFF_FORMAT_NAME_ONLY: | |
680 | print_file = diff_print_one_name_only; | |
681 | break; | |
682 | case GIT_DIFF_FORMAT_NAME_STATUS: | |
683 | print_file = diff_print_one_name_status; | |
684 | break; | |
685 | default: | |
909d5494 | 686 | giterr_set(GITERR_INVALID, "unknown diff output format (%d)", format); |
10672e3e RB |
687 | return -1; |
688 | } | |
7000f3fa | 689 | |
8147b1af ET |
690 | if (!(error = diff_print_info_init_fromdiff( |
691 | &pi, &buf, diff, format, print_cb, payload))) { | |
7000f3fa | 692 | error = git_diff_foreach( |
8147b1af | 693 | diff, print_file, print_binary, print_hunk, print_line, &pi); |
7000f3fa | 694 | |
25e0b157 | 695 | if (error) /* make sure error message is set */ |
26c1cb91 | 696 | giterr_set_after_callback_function(error, "git_diff_print"); |
96869a4e RB |
697 | } |
698 | ||
7000f3fa RB |
699 | git_buf_free(&buf); |
700 | ||
701 | return error; | |
702 | } | |
703 | ||
27e54bcf | 704 | int git_diff_print_callback__to_buf( |
a1683f28 | 705 | const git_diff_delta *delta, |
3b5f7954 RB |
706 | const git_diff_hunk *hunk, |
707 | const git_diff_line *line, | |
a1683f28 RB |
708 | void *payload) |
709 | { | |
710 | git_buf *output = payload; | |
3b5f7954 RB |
711 | GIT_UNUSED(delta); GIT_UNUSED(hunk); |
712 | ||
27e54bcf | 713 | if (!output) { |
909d5494 | 714 | giterr_set(GITERR_INVALID, "buffer pointer must be provided"); |
27e54bcf RB |
715 | return -1; |
716 | } | |
717 | ||
3b5f7954 RB |
718 | if (line->origin == GIT_DIFF_LINE_ADDITION || |
719 | line->origin == GIT_DIFF_LINE_DELETION || | |
720 | line->origin == GIT_DIFF_LINE_CONTEXT) | |
721 | git_buf_putc(output, line->origin); | |
722 | ||
723 | return git_buf_put(output, line->content, line->content_len); | |
a1683f28 RB |
724 | } |
725 | ||
27e54bcf RB |
726 | int git_diff_print_callback__to_file_handle( |
727 | const git_diff_delta *delta, | |
728 | const git_diff_hunk *hunk, | |
729 | const git_diff_line *line, | |
730 | void *payload) | |
731 | { | |
732 | FILE *fp = payload ? payload : stdout; | |
733 | ||
734 | GIT_UNUSED(delta); GIT_UNUSED(hunk); | |
735 | ||
736 | if (line->origin == GIT_DIFF_LINE_CONTEXT || | |
737 | line->origin == GIT_DIFF_LINE_ADDITION || | |
738 | line->origin == GIT_DIFF_LINE_DELETION) | |
739 | fputc(line->origin, fp); | |
740 | fwrite(line->content, 1, line->content_len, fp); | |
741 | return 0; | |
742 | } | |
743 | ||
72827490 ET |
744 | /* print a git_diff to a git_buf */ |
745 | int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format) | |
746 | { | |
747 | assert(out && diff); | |
748 | git_buf_sanitize(out); | |
749 | return git_diff_print( | |
750 | diff, format, git_diff_print_callback__to_buf, out); | |
751 | } | |
752 | ||
804d5fe9 ET |
753 | /* print a git_patch to an output callback */ |
754 | int git_patch_print( | |
755 | git_patch *patch, | |
756 | git_diff_line_cb print_cb, | |
757 | void *payload) | |
758 | { | |
759 | int error; | |
760 | git_buf temp = GIT_BUF_INIT; | |
761 | diff_print_info pi; | |
762 | ||
763 | assert(patch && print_cb); | |
764 | ||
765 | if (!(error = diff_print_info_init_frompatch( | |
766 | &pi, &temp, patch, | |
767 | GIT_DIFF_FORMAT_PATCH, print_cb, payload))) | |
768 | { | |
769 | error = git_patch__invoke_callbacks( | |
770 | patch, | |
771 | diff_print_patch_file, diff_print_patch_binary, | |
772 | diff_print_patch_hunk, diff_print_patch_line, | |
773 | &pi); | |
774 | ||
775 | if (error) /* make sure error message is set */ | |
776 | giterr_set_after_callback_function(error, "git_patch_print"); | |
777 | } | |
778 | ||
779 | git_buf_free(&temp); | |
780 | ||
781 | return error; | |
782 | } | |
783 | ||
450e8e9e | 784 | /* print a git_patch to a git_buf */ |
1e4976cb | 785 | int git_patch_to_buf(git_buf *out, git_patch *patch) |
450e8e9e | 786 | { |
1e4976cb RB |
787 | assert(out && patch); |
788 | git_buf_sanitize(out); | |
27e54bcf | 789 | return git_patch_print(patch, git_diff_print_callback__to_buf, out); |
450e8e9e | 790 | } |