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