]>
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 | */ | |
7 | #include "common.h" | |
8 | #include "diff.h" | |
804d5fe9 ET |
9 | #include "diff_file.h" |
10 | #include "patch_diff.h" | |
f240acce | 11 | #include "fileops.h" |
e349ed50 ET |
12 | #include "zstream.h" |
13 | #include "blob.h" | |
14 | #include "delta.h" | |
27e54bcf | 15 | #include "git2/sys/diff.h" |
7000f3fa RB |
16 | |
17 | typedef struct { | |
10672e3e | 18 | git_diff_format_t format; |
3ff1d123 | 19 | git_diff_line_cb print_cb; |
7000f3fa | 20 | void *payload; |
804d5fe9 | 21 | |
7000f3fa | 22 | git_buf *buf; |
804d5fe9 ET |
23 | git_diff_line line; |
24 | ||
25 | const char *old_prefix; | |
26 | const char *new_prefix; | |
10672e3e | 27 | uint32_t flags; |
7000f3fa | 28 | int oid_strlen; |
804d5fe9 ET |
29 | |
30 | int (*strcomp)(const char *, const char *); | |
7000f3fa RB |
31 | } diff_print_info; |
32 | ||
8147b1af | 33 | static int diff_print_info_init__common( |
7000f3fa | 34 | diff_print_info *pi, |
10672e3e | 35 | git_buf *out, |
8147b1af | 36 | git_repository *repo, |
10672e3e RB |
37 | git_diff_format_t format, |
38 | git_diff_line_cb cb, | |
39 | void *payload) | |
7000f3fa | 40 | { |
8147b1af | 41 | pi->format = format; |
7000f3fa | 42 | pi->print_cb = cb; |
8147b1af ET |
43 | pi->payload = payload; |
44 | pi->buf = out; | |
45 | ||
46 | if (!pi->oid_strlen) { | |
47 | if (!repo) | |
48 | pi->oid_strlen = GIT_ABBREV_DEFAULT; | |
49 | else if (git_repository__cvar(&pi->oid_strlen, repo, GIT_CVAR_ABBREV) < 0) | |
50 | return -1; | |
51 | } | |
7000f3fa RB |
52 | |
53 | pi->oid_strlen += 1; /* for NUL byte */ | |
54 | ||
8147b1af | 55 | if (pi->oid_strlen > GIT_OID_HEXSZ + 1) |
7000f3fa RB |
56 | pi->oid_strlen = GIT_OID_HEXSZ + 1; |
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; | |
80 | pi->oid_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 ET |
102 | pi->flags = patch->diff_opts.flags; |
103 | pi->oid_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; | |
133 | case GIT_DELTA_UNREADABLE: code = 'X'; break; | |
134 | default: code = ' '; break; | |
7000f3fa RB |
135 | } |
136 | ||
137 | return code; | |
138 | } | |
139 | ||
10672e3e RB |
140 | static int diff_print_one_name_only( |
141 | const git_diff_delta *delta, float progress, void *data) | |
142 | { | |
143 | diff_print_info *pi = data; | |
144 | git_buf *out = pi->buf; | |
145 | ||
146 | GIT_UNUSED(progress); | |
147 | ||
148 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && | |
149 | delta->status == GIT_DELTA_UNMODIFIED) | |
150 | return 0; | |
151 | ||
152 | git_buf_clear(out); | |
25e0b157 RB |
153 | git_buf_puts(out, delta->new_file.path); |
154 | git_buf_putc(out, '\n'); | |
155 | if (git_buf_oom(out)) | |
156 | return -1; | |
10672e3e | 157 | |
3b5f7954 RB |
158 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
159 | pi->line.content = git_buf_cstr(out); | |
160 | pi->line.content_len = git_buf_len(out); | |
161 | ||
25e0b157 | 162 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
10672e3e RB |
163 | } |
164 | ||
165 | static int diff_print_one_name_status( | |
7000f3fa RB |
166 | const git_diff_delta *delta, float progress, void *data) |
167 | { | |
168 | diff_print_info *pi = data; | |
a1683f28 | 169 | git_buf *out = pi->buf; |
7000f3fa | 170 | char old_suffix, new_suffix, code = git_diff_status_char(delta->status); |
804d5fe9 ET |
171 | int(*strcomp)(const char *, const char *) = pi->strcomp ? |
172 | pi->strcomp : git__strcmp; | |
7000f3fa RB |
173 | |
174 | GIT_UNUSED(progress); | |
175 | ||
10672e3e | 176 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') |
7000f3fa RB |
177 | return 0; |
178 | ||
a1683f28 RB |
179 | old_suffix = diff_pick_suffix(delta->old_file.mode); |
180 | new_suffix = diff_pick_suffix(delta->new_file.mode); | |
7000f3fa | 181 | |
a1683f28 | 182 | git_buf_clear(out); |
7000f3fa RB |
183 | |
184 | if (delta->old_file.path != delta->new_file.path && | |
74ded024 | 185 | strcomp(delta->old_file.path,delta->new_file.path) != 0) |
df40f398 | 186 | git_buf_printf(out, "%c\t%s%c %s%c\n", code, |
7000f3fa RB |
187 | delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); |
188 | else if (delta->old_file.mode != delta->new_file.mode && | |
189 | delta->old_file.mode != 0 && delta->new_file.mode != 0) | |
df40f398 RB |
190 | git_buf_printf(out, "%c\t%s%c %s%c\n", code, |
191 | delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); | |
7000f3fa | 192 | else if (old_suffix != ' ') |
a1683f28 | 193 | git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); |
7000f3fa | 194 | else |
a1683f28 | 195 | git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); |
a1683f28 | 196 | if (git_buf_oom(out)) |
25e0b157 | 197 | return -1; |
7000f3fa | 198 | |
3b5f7954 RB |
199 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
200 | pi->line.content = git_buf_cstr(out); | |
201 | pi->line.content_len = git_buf_len(out); | |
202 | ||
25e0b157 | 203 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
204 | } |
205 | ||
a1683f28 | 206 | static int diff_print_one_raw( |
7000f3fa RB |
207 | const git_diff_delta *delta, float progress, void *data) |
208 | { | |
209 | diff_print_info *pi = data; | |
a1683f28 | 210 | git_buf *out = pi->buf; |
d68cb736 | 211 | int id_abbrev; |
7000f3fa RB |
212 | char code = git_diff_status_char(delta->status); |
213 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
214 | ||
215 | GIT_UNUSED(progress); | |
216 | ||
10672e3e | 217 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') |
7000f3fa RB |
218 | return 0; |
219 | ||
a1683f28 | 220 | git_buf_clear(out); |
7000f3fa | 221 | |
d68cb736 ET |
222 | id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev : |
223 | delta->new_file.id_abbrev; | |
224 | ||
225 | if (pi->oid_strlen - 1 > id_abbrev) { | |
226 | giterr_set(GITERR_PATCH, | |
227 | "The patch input contains %d id characters (cannot print %d)", | |
228 | id_abbrev, pi->oid_strlen); | |
229 | return -1; | |
230 | } | |
231 | ||
9950bb4e CMN |
232 | git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id); |
233 | git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id); | |
7000f3fa RB |
234 | |
235 | git_buf_printf( | |
12e422a0 RB |
236 | out, (pi->oid_strlen <= GIT_OID_HEXSZ) ? |
237 | ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c", | |
7000f3fa RB |
238 | delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); |
239 | ||
240 | if (delta->similarity > 0) | |
a1683f28 | 241 | git_buf_printf(out, "%03u", delta->similarity); |
7000f3fa | 242 | |
e4acc3ba | 243 | if (delta->old_file.path != delta->new_file.path) |
7000f3fa | 244 | git_buf_printf( |
a1683f28 | 245 | out, "\t%s %s\n", delta->old_file.path, delta->new_file.path); |
7000f3fa RB |
246 | else |
247 | git_buf_printf( | |
a1683f28 | 248 | out, "\t%s\n", delta->old_file.path ? |
7000f3fa RB |
249 | delta->old_file.path : delta->new_file.path); |
250 | ||
a1683f28 | 251 | if (git_buf_oom(out)) |
25e0b157 | 252 | return -1; |
7000f3fa | 253 | |
3b5f7954 RB |
254 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
255 | pi->line.content = git_buf_cstr(out); | |
256 | pi->line.content_len = git_buf_len(out); | |
257 | ||
25e0b157 | 258 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
259 | } |
260 | ||
197b8966 RB |
261 | static int diff_print_oid_range( |
262 | git_buf *out, const git_diff_delta *delta, int oid_strlen) | |
7000f3fa RB |
263 | { |
264 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
265 | ||
d68cb736 ET |
266 | if (delta->old_file.mode && |
267 | oid_strlen - 1 > delta->old_file.id_abbrev) { | |
268 | giterr_set(GITERR_PATCH, | |
269 | "The patch input contains %d id characters (cannot print %d)", | |
270 | delta->old_file.id_abbrev, oid_strlen); | |
271 | return -1; | |
272 | } | |
273 | ||
274 | if ((delta->new_file.mode && | |
275 | oid_strlen - 1 > delta->new_file.id_abbrev)) { | |
276 | giterr_set(GITERR_PATCH, | |
277 | "The patch input contains %d id characters (cannot print %d)", | |
278 | delta->new_file.id_abbrev, oid_strlen); | |
279 | return -1; | |
280 | } | |
281 | ||
9950bb4e CMN |
282 | git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id); |
283 | git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id); | |
7000f3fa | 284 | |
7000f3fa | 285 | if (delta->old_file.mode == delta->new_file.mode) { |
a1683f28 | 286 | git_buf_printf(out, "index %s..%s %o\n", |
7000f3fa RB |
287 | start_oid, end_oid, delta->old_file.mode); |
288 | } else { | |
289 | if (delta->old_file.mode == 0) { | |
a1683f28 | 290 | git_buf_printf(out, "new file mode %o\n", delta->new_file.mode); |
7000f3fa | 291 | } else if (delta->new_file.mode == 0) { |
a1683f28 | 292 | git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode); |
7000f3fa | 293 | } else { |
a1683f28 RB |
294 | git_buf_printf(out, "old mode %o\n", delta->old_file.mode); |
295 | git_buf_printf(out, "new mode %o\n", delta->new_file.mode); | |
7000f3fa | 296 | } |
a1683f28 | 297 | git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); |
7000f3fa RB |
298 | } |
299 | ||
25e0b157 | 300 | return git_buf_oom(out) ? -1 : 0; |
7000f3fa RB |
301 | } |
302 | ||
eb1c1707 | 303 | static int diff_delta_format_with_paths( |
197b8966 RB |
304 | git_buf *out, |
305 | const git_diff_delta *delta, | |
306 | const char *oldpfx, | |
307 | const char *newpfx, | |
eb1c1707 | 308 | const char *template) |
7000f3fa | 309 | { |
7000f3fa | 310 | const char *oldpath = delta->old_file.path; |
7000f3fa | 311 | const char *newpath = delta->new_file.path; |
7000f3fa | 312 | |
9950bb4e | 313 | if (git_oid_iszero(&delta->old_file.id)) { |
eb1c1707 RB |
314 | oldpfx = ""; |
315 | oldpath = "/dev/null"; | |
316 | } | |
9950bb4e | 317 | if (git_oid_iszero(&delta->new_file.id)) { |
eb1c1707 RB |
318 | newpfx = ""; |
319 | newpath = "/dev/null"; | |
320 | } | |
321 | ||
322 | return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath); | |
323 | } | |
324 | ||
19e46645 ET |
325 | int diff_delta_format_rename_header( |
326 | git_buf *out, | |
327 | const git_diff_delta *delta) | |
328 | { | |
329 | if (delta->similarity > 100) { | |
330 | giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity); | |
331 | return -1; | |
332 | } | |
333 | ||
334 | git_buf_printf(out, | |
335 | "similarity index %d%%\n" | |
336 | "rename from %s\n" | |
337 | "rename to %s\n", | |
338 | delta->similarity, | |
339 | delta->old_file.path, | |
340 | delta->new_file.path); | |
341 | ||
342 | return git_buf_oom(out) ? -1 : 0; | |
343 | } | |
344 | ||
eb1c1707 RB |
345 | int git_diff_delta__format_file_header( |
346 | git_buf *out, | |
347 | const git_diff_delta *delta, | |
348 | const char *oldpfx, | |
349 | const char *newpfx, | |
350 | int oid_strlen) | |
351 | { | |
72806f4c ET |
352 | bool skip_index; |
353 | ||
7000f3fa RB |
354 | if (!oldpfx) |
355 | oldpfx = DIFF_OLD_PREFIX_DEFAULT; | |
7000f3fa RB |
356 | if (!newpfx) |
357 | newpfx = DIFF_NEW_PREFIX_DEFAULT; | |
197b8966 RB |
358 | if (!oid_strlen) |
359 | oid_strlen = GIT_ABBREV_DEFAULT + 1; | |
7000f3fa | 360 | |
197b8966 RB |
361 | git_buf_clear(out); |
362 | ||
363 | git_buf_printf(out, "diff --git %s%s %s%s\n", | |
eb1c1707 | 364 | oldpfx, delta->old_file.path, newpfx, delta->new_file.path); |
7000f3fa | 365 | |
19e46645 ET |
366 | if (delta->status == GIT_DELTA_RENAMED) |
367 | GITERR_CHECK_ERROR(diff_delta_format_rename_header(out, delta)); | |
368 | ||
72806f4c ET |
369 | skip_index = (delta->status == GIT_DELTA_RENAMED && |
370 | delta->similarity == 100 && | |
371 | delta->old_file.mode == 0 && | |
372 | delta->new_file.mode == 0); | |
373 | ||
374 | if (!skip_index) { | |
375 | GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen)); | |
7000f3fa | 376 | |
72806f4c ET |
377 | if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) |
378 | diff_delta_format_with_paths( | |
379 | out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n"); | |
380 | } | |
7000f3fa | 381 | |
197b8966 RB |
382 | return git_buf_oom(out) ? -1 : 0; |
383 | } | |
7000f3fa | 384 | |
8147b1af ET |
385 | static int format_binary( |
386 | diff_print_info *pi, | |
387 | git_diff_binary_t type, | |
388 | const char *data, | |
389 | size_t datalen, | |
390 | size_t inflatedlen) | |
e349ed50 | 391 | { |
8147b1af ET |
392 | const char *typename = type == GIT_DIFF_BINARY_DELTA ? |
393 | "delta" : "literal"; | |
394 | const char *scan, *end; | |
e349ed50 | 395 | |
49840056 | 396 | git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen); |
e349ed50 ET |
397 | pi->line.num_lines++; |
398 | ||
8147b1af | 399 | for (scan = data, end = data + datalen; scan < end; ) { |
947a58c1 RB |
400 | size_t chunk_len = end - scan; |
401 | if (chunk_len > 52) | |
402 | chunk_len = 52; | |
e349ed50 ET |
403 | |
404 | if (chunk_len <= 26) | |
c6320bec | 405 | git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); |
e349ed50 | 406 | else |
c6320bec | 407 | git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); |
e349ed50 | 408 | |
e003f83a | 409 | git_buf_encode_base85(pi->buf, scan, chunk_len); |
e349ed50 ET |
410 | git_buf_putc(pi->buf, '\n'); |
411 | ||
8147b1af ET |
412 | if (git_buf_oom(pi->buf)) |
413 | return -1; | |
e349ed50 | 414 | |
947a58c1 | 415 | scan += chunk_len; |
e349ed50 ET |
416 | pi->line.num_lines++; |
417 | } | |
e4b2b919 | 418 | git_buf_putc(pi->buf, '\n'); |
e349ed50 | 419 | |
8147b1af ET |
420 | return 0; |
421 | } | |
e349ed50 | 422 | |
e349ed50 | 423 | static int diff_print_patch_file_binary( |
8147b1af ET |
424 | diff_print_info *pi, git_diff_delta *delta, |
425 | const char *old_pfx, const char *new_pfx, | |
426 | const git_diff_binary *binary) | |
e349ed50 | 427 | { |
947a58c1 | 428 | size_t pre_binary_size; |
8147b1af | 429 | int error; |
e349ed50 | 430 | |
947a58c1 RB |
431 | if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) |
432 | goto noshow; | |
e349ed50 | 433 | |
3149ff6f ET |
434 | if (binary->new_file.datalen == 0 && binary->old_file.datalen == 0) |
435 | return 0; | |
436 | ||
947a58c1 | 437 | pre_binary_size = pi->buf->size; |
e349ed50 ET |
438 | git_buf_printf(pi->buf, "GIT binary patch\n"); |
439 | pi->line.num_lines++; | |
440 | ||
8147b1af ET |
441 | if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, |
442 | binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || | |
8147b1af ET |
443 | (error = format_binary(pi, binary->old_file.type, binary->old_file.data, |
444 | binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { | |
445 | ||
947a58c1 RB |
446 | if (error == GIT_EBUFS) { |
447 | giterr_clear(); | |
448 | git_buf_truncate(pi->buf, pre_binary_size); | |
449 | goto noshow; | |
450 | } | |
451 | } | |
e349ed50 ET |
452 | |
453 | pi->line.num_lines++; | |
e349ed50 | 454 | return error; |
947a58c1 RB |
455 | |
456 | noshow: | |
457 | pi->line.num_lines = 1; | |
458 | return diff_delta_format_with_paths( | |
8147b1af | 459 | pi->buf, delta, old_pfx, new_pfx, |
947a58c1 | 460 | "Binary files %s%s and %s%s differ\n"); |
e349ed50 ET |
461 | } |
462 | ||
197b8966 RB |
463 | static int diff_print_patch_file( |
464 | const git_diff_delta *delta, float progress, void *data) | |
465 | { | |
25e0b157 | 466 | int error; |
197b8966 | 467 | diff_print_info *pi = data; |
eb1c1707 | 468 | const char *oldpfx = |
804d5fe9 | 469 | pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; |
eb1c1707 | 470 | const char *newpfx = |
804d5fe9 | 471 | pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; |
197b8966 | 472 | |
8147b1af ET |
473 | bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) || |
474 | (pi->flags & GIT_DIFF_FORCE_BINARY); | |
e349ed50 ET |
475 | bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); |
476 | int oid_strlen = binary && show_binary ? | |
477 | GIT_OID_HEXSZ + 1 : pi->oid_strlen; | |
478 | ||
197b8966 | 479 | GIT_UNUSED(progress); |
7000f3fa | 480 | |
197b8966 RB |
481 | if (S_ISDIR(delta->new_file.mode) || |
482 | delta->status == GIT_DELTA_UNMODIFIED || | |
483 | delta->status == GIT_DELTA_IGNORED || | |
61bef72d | 484 | delta->status == GIT_DELTA_UNREADABLE || |
197b8966 | 485 | (delta->status == GIT_DELTA_UNTRACKED && |
10672e3e | 486 | (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) |
7000f3fa RB |
487 | return 0; |
488 | ||
25e0b157 | 489 | if ((error = git_diff_delta__format_file_header( |
e349ed50 | 490 | pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0) |
25e0b157 | 491 | return error; |
7000f3fa | 492 | |
3b5f7954 RB |
493 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
494 | pi->line.content = git_buf_cstr(pi->buf); | |
495 | pi->line.content_len = git_buf_len(pi->buf); | |
496 | ||
8147b1af ET |
497 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
498 | } | |
eb1c1707 | 499 | |
8147b1af ET |
500 | static int diff_print_patch_binary( |
501 | const git_diff_delta *delta, | |
502 | const git_diff_binary *binary, | |
503 | void *data) | |
504 | { | |
505 | diff_print_info *pi = data; | |
506 | const char *old_pfx = | |
804d5fe9 | 507 | pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT; |
8147b1af | 508 | const char *new_pfx = |
804d5fe9 | 509 | pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT; |
8147b1af | 510 | int error; |
eb1c1707 RB |
511 | |
512 | git_buf_clear(pi->buf); | |
513 | ||
8147b1af ET |
514 | if ((error = diff_print_patch_file_binary( |
515 | pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0) | |
25e0b157 | 516 | return error; |
eb1c1707 | 517 | |
8147b1af ET |
518 | pi->line.origin = GIT_DIFF_LINE_BINARY; |
519 | pi->line.content = git_buf_cstr(pi->buf); | |
3b5f7954 | 520 | pi->line.content_len = git_buf_len(pi->buf); |
3b5f7954 | 521 | |
25e0b157 | 522 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
523 | } |
524 | ||
a1683f28 | 525 | static int diff_print_patch_hunk( |
7000f3fa | 526 | const git_diff_delta *d, |
3b5f7954 | 527 | const git_diff_hunk *h, |
7000f3fa RB |
528 | void *data) |
529 | { | |
530 | diff_print_info *pi = data; | |
531 | ||
532 | if (S_ISDIR(d->new_file.mode)) | |
533 | return 0; | |
534 | ||
3b5f7954 RB |
535 | pi->line.origin = GIT_DIFF_LINE_HUNK_HDR; |
536 | pi->line.content = h->header; | |
537 | pi->line.content_len = h->header_len; | |
7000f3fa | 538 | |
25e0b157 | 539 | return pi->print_cb(d, h, &pi->line, pi->payload); |
7000f3fa RB |
540 | } |
541 | ||
a1683f28 | 542 | static int diff_print_patch_line( |
7000f3fa | 543 | const git_diff_delta *delta, |
3b5f7954 RB |
544 | const git_diff_hunk *hunk, |
545 | const git_diff_line *line, | |
7000f3fa RB |
546 | void *data) |
547 | { | |
548 | diff_print_info *pi = data; | |
549 | ||
550 | if (S_ISDIR(delta->new_file.mode)) | |
551 | return 0; | |
552 | ||
25e0b157 | 553 | return pi->print_cb(delta, hunk, line, pi->payload); |
7000f3fa RB |
554 | } |
555 | ||
10672e3e RB |
556 | /* print a git_diff to an output callback */ |
557 | int git_diff_print( | |
3ff1d123 | 558 | git_diff *diff, |
10672e3e | 559 | git_diff_format_t format, |
3ff1d123 | 560 | git_diff_line_cb print_cb, |
7000f3fa RB |
561 | void *payload) |
562 | { | |
563 | int error; | |
564 | git_buf buf = GIT_BUF_INIT; | |
565 | diff_print_info pi; | |
10672e3e | 566 | git_diff_file_cb print_file = NULL; |
8147b1af | 567 | git_diff_binary_cb print_binary = NULL; |
10672e3e RB |
568 | git_diff_hunk_cb print_hunk = NULL; |
569 | git_diff_line_cb print_line = NULL; | |
570 | ||
571 | switch (format) { | |
572 | case GIT_DIFF_FORMAT_PATCH: | |
573 | print_file = diff_print_patch_file; | |
8147b1af | 574 | print_binary = diff_print_patch_binary; |
10672e3e RB |
575 | print_hunk = diff_print_patch_hunk; |
576 | print_line = diff_print_patch_line; | |
577 | break; | |
578 | case GIT_DIFF_FORMAT_PATCH_HEADER: | |
579 | print_file = diff_print_patch_file; | |
580 | break; | |
581 | case GIT_DIFF_FORMAT_RAW: | |
582 | print_file = diff_print_one_raw; | |
583 | break; | |
584 | case GIT_DIFF_FORMAT_NAME_ONLY: | |
585 | print_file = diff_print_one_name_only; | |
586 | break; | |
587 | case GIT_DIFF_FORMAT_NAME_STATUS: | |
588 | print_file = diff_print_one_name_status; | |
589 | break; | |
590 | default: | |
591 | giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format); | |
592 | return -1; | |
593 | } | |
7000f3fa | 594 | |
8147b1af ET |
595 | if (!(error = diff_print_info_init_fromdiff( |
596 | &pi, &buf, diff, format, print_cb, payload))) { | |
7000f3fa | 597 | error = git_diff_foreach( |
8147b1af | 598 | diff, print_file, print_binary, print_hunk, print_line, &pi); |
7000f3fa | 599 | |
25e0b157 | 600 | if (error) /* make sure error message is set */ |
26c1cb91 | 601 | giterr_set_after_callback_function(error, "git_diff_print"); |
96869a4e RB |
602 | } |
603 | ||
7000f3fa RB |
604 | git_buf_free(&buf); |
605 | ||
606 | return error; | |
607 | } | |
608 | ||
27e54bcf | 609 | int git_diff_print_callback__to_buf( |
a1683f28 | 610 | const git_diff_delta *delta, |
3b5f7954 RB |
611 | const git_diff_hunk *hunk, |
612 | const git_diff_line *line, | |
a1683f28 RB |
613 | void *payload) |
614 | { | |
615 | git_buf *output = payload; | |
3b5f7954 RB |
616 | GIT_UNUSED(delta); GIT_UNUSED(hunk); |
617 | ||
27e54bcf RB |
618 | if (!output) { |
619 | giterr_set(GITERR_INVALID, "Buffer pointer must be provided"); | |
620 | return -1; | |
621 | } | |
622 | ||
3b5f7954 RB |
623 | if (line->origin == GIT_DIFF_LINE_ADDITION || |
624 | line->origin == GIT_DIFF_LINE_DELETION || | |
625 | line->origin == GIT_DIFF_LINE_CONTEXT) | |
626 | git_buf_putc(output, line->origin); | |
627 | ||
628 | return git_buf_put(output, line->content, line->content_len); | |
a1683f28 RB |
629 | } |
630 | ||
27e54bcf RB |
631 | int git_diff_print_callback__to_file_handle( |
632 | const git_diff_delta *delta, | |
633 | const git_diff_hunk *hunk, | |
634 | const git_diff_line *line, | |
635 | void *payload) | |
636 | { | |
637 | FILE *fp = payload ? payload : stdout; | |
638 | ||
639 | GIT_UNUSED(delta); GIT_UNUSED(hunk); | |
640 | ||
641 | if (line->origin == GIT_DIFF_LINE_CONTEXT || | |
642 | line->origin == GIT_DIFF_LINE_ADDITION || | |
643 | line->origin == GIT_DIFF_LINE_DELETION) | |
644 | fputc(line->origin, fp); | |
645 | fwrite(line->content, 1, line->content_len, fp); | |
646 | return 0; | |
647 | } | |
648 | ||
804d5fe9 ET |
649 | /* print a git_patch to an output callback */ |
650 | int git_patch_print( | |
651 | git_patch *patch, | |
652 | git_diff_line_cb print_cb, | |
653 | void *payload) | |
654 | { | |
655 | int error; | |
656 | git_buf temp = GIT_BUF_INIT; | |
657 | diff_print_info pi; | |
658 | ||
659 | assert(patch && print_cb); | |
660 | ||
661 | if (!(error = diff_print_info_init_frompatch( | |
662 | &pi, &temp, patch, | |
663 | GIT_DIFF_FORMAT_PATCH, print_cb, payload))) | |
664 | { | |
665 | error = git_patch__invoke_callbacks( | |
666 | patch, | |
667 | diff_print_patch_file, diff_print_patch_binary, | |
668 | diff_print_patch_hunk, diff_print_patch_line, | |
669 | &pi); | |
670 | ||
671 | if (error) /* make sure error message is set */ | |
672 | giterr_set_after_callback_function(error, "git_patch_print"); | |
673 | } | |
674 | ||
675 | git_buf_free(&temp); | |
676 | ||
677 | return error; | |
678 | } | |
679 | ||
450e8e9e | 680 | /* print a git_patch to a git_buf */ |
1e4976cb | 681 | int git_patch_to_buf(git_buf *out, git_patch *patch) |
450e8e9e | 682 | { |
1e4976cb RB |
683 | assert(out && patch); |
684 | git_buf_sanitize(out); | |
27e54bcf | 685 | return git_patch_print(patch, git_diff_print_callback__to_buf, out); |
450e8e9e | 686 | } |