]>
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" | |
114f5a6c | 9 | #include "diff_patch.h" |
f240acce | 10 | #include "fileops.h" |
e349ed50 ET |
11 | #include "zstream.h" |
12 | #include "blob.h" | |
13 | #include "delta.h" | |
27e54bcf | 14 | #include "git2/sys/diff.h" |
7000f3fa RB |
15 | |
16 | typedef struct { | |
3ff1d123 | 17 | git_diff *diff; |
10672e3e | 18 | git_diff_format_t format; |
3ff1d123 | 19 | git_diff_line_cb print_cb; |
7000f3fa RB |
20 | void *payload; |
21 | git_buf *buf; | |
10672e3e | 22 | uint32_t flags; |
7000f3fa | 23 | int oid_strlen; |
3b5f7954 | 24 | git_diff_line line; |
8147b1af ET |
25 | unsigned int |
26 | content_loaded : 1, | |
27 | content_allocated : 1; | |
28 | git_diff_file_content *ofile; | |
29 | git_diff_file_content *nfile; | |
7000f3fa RB |
30 | } diff_print_info; |
31 | ||
8147b1af | 32 | static int diff_print_info_init__common( |
7000f3fa | 33 | diff_print_info *pi, |
10672e3e | 34 | git_buf *out, |
8147b1af | 35 | git_repository *repo, |
10672e3e RB |
36 | git_diff_format_t format, |
37 | git_diff_line_cb cb, | |
38 | void *payload) | |
7000f3fa | 39 | { |
8147b1af | 40 | pi->format = format; |
7000f3fa | 41 | pi->print_cb = cb; |
8147b1af ET |
42 | pi->payload = payload; |
43 | pi->buf = out; | |
44 | ||
45 | if (!pi->oid_strlen) { | |
46 | if (!repo) | |
47 | pi->oid_strlen = GIT_ABBREV_DEFAULT; | |
48 | else if (git_repository__cvar(&pi->oid_strlen, repo, GIT_CVAR_ABBREV) < 0) | |
49 | return -1; | |
50 | } | |
7000f3fa RB |
51 | |
52 | pi->oid_strlen += 1; /* for NUL byte */ | |
53 | ||
8147b1af | 54 | if (pi->oid_strlen > GIT_OID_HEXSZ + 1) |
7000f3fa RB |
55 | pi->oid_strlen = GIT_OID_HEXSZ + 1; |
56 | ||
3b5f7954 RB |
57 | memset(&pi->line, 0, sizeof(pi->line)); |
58 | pi->line.old_lineno = -1; | |
59 | pi->line.new_lineno = -1; | |
8147b1af | 60 | pi->line.num_lines = 1; |
3b5f7954 | 61 | |
7000f3fa RB |
62 | return 0; |
63 | } | |
64 | ||
8147b1af ET |
65 | static int diff_print_info_init_fromdiff( |
66 | diff_print_info *pi, | |
67 | git_buf *out, | |
68 | git_diff *diff, | |
69 | git_diff_format_t format, | |
70 | git_diff_line_cb cb, | |
71 | void *payload) | |
72 | { | |
73 | git_repository *repo = diff ? diff->repo : NULL; | |
74 | ||
75 | memset(pi, 0, sizeof(diff_print_info)); | |
76 | ||
77 | pi->diff = diff; | |
78 | ||
79 | if (diff) { | |
80 | pi->flags = diff->opts.flags; | |
81 | pi->oid_strlen = diff->opts.id_abbrev; | |
82 | } | |
83 | ||
84 | return diff_print_info_init__common(pi, out, repo, format, cb, payload); | |
85 | } | |
86 | ||
87 | static int diff_print_info_init_frompatch( | |
88 | diff_print_info *pi, | |
89 | git_buf *out, | |
90 | git_patch *patch, | |
91 | git_diff_format_t format, | |
92 | git_diff_line_cb cb, | |
93 | void *payload) | |
94 | { | |
95 | git_repository *repo = patch && patch->diff ? patch->diff->repo : NULL; | |
96 | ||
97 | memset(pi, 0, sizeof(diff_print_info)); | |
98 | ||
99 | pi->diff = patch->diff; | |
100 | ||
101 | pi->flags = patch->diff_opts.flags; | |
102 | pi->oid_strlen = patch->diff_opts.id_abbrev; | |
103 | ||
104 | pi->content_loaded = 1; | |
105 | pi->ofile = &patch->ofile; | |
106 | pi->nfile = &patch->nfile; | |
107 | ||
108 | return diff_print_info_init__common(pi, out, repo, format, cb, payload); | |
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; | |
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); |
74ded024 RB |
172 | int (*strcomp)(const char *, const char *) = |
173 | pi->diff ? pi->diff->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; |
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 | |
9950bb4e CMN |
222 | git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id); |
223 | git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id); | |
7000f3fa RB |
224 | |
225 | git_buf_printf( | |
12e422a0 RB |
226 | out, (pi->oid_strlen <= GIT_OID_HEXSZ) ? |
227 | ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c", | |
7000f3fa RB |
228 | delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); |
229 | ||
230 | if (delta->similarity > 0) | |
a1683f28 | 231 | git_buf_printf(out, "%03u", delta->similarity); |
7000f3fa | 232 | |
e4acc3ba | 233 | if (delta->old_file.path != delta->new_file.path) |
7000f3fa | 234 | git_buf_printf( |
a1683f28 | 235 | out, "\t%s %s\n", delta->old_file.path, delta->new_file.path); |
7000f3fa RB |
236 | else |
237 | git_buf_printf( | |
a1683f28 | 238 | out, "\t%s\n", delta->old_file.path ? |
7000f3fa RB |
239 | delta->old_file.path : delta->new_file.path); |
240 | ||
a1683f28 | 241 | if (git_buf_oom(out)) |
25e0b157 | 242 | return -1; |
7000f3fa | 243 | |
3b5f7954 RB |
244 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
245 | pi->line.content = git_buf_cstr(out); | |
246 | pi->line.content_len = git_buf_len(out); | |
247 | ||
25e0b157 | 248 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
249 | } |
250 | ||
197b8966 RB |
251 | static int diff_print_oid_range( |
252 | git_buf *out, const git_diff_delta *delta, int oid_strlen) | |
7000f3fa RB |
253 | { |
254 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
255 | ||
9950bb4e CMN |
256 | git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id); |
257 | git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id); | |
7000f3fa RB |
258 | |
259 | /* TODO: Match git diff more closely */ | |
260 | if (delta->old_file.mode == delta->new_file.mode) { | |
a1683f28 | 261 | git_buf_printf(out, "index %s..%s %o\n", |
7000f3fa RB |
262 | start_oid, end_oid, delta->old_file.mode); |
263 | } else { | |
264 | if (delta->old_file.mode == 0) { | |
a1683f28 | 265 | git_buf_printf(out, "new file mode %o\n", delta->new_file.mode); |
7000f3fa | 266 | } else if (delta->new_file.mode == 0) { |
a1683f28 | 267 | git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode); |
7000f3fa | 268 | } else { |
a1683f28 RB |
269 | git_buf_printf(out, "old mode %o\n", delta->old_file.mode); |
270 | git_buf_printf(out, "new mode %o\n", delta->new_file.mode); | |
7000f3fa | 271 | } |
a1683f28 | 272 | git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); |
7000f3fa RB |
273 | } |
274 | ||
25e0b157 | 275 | return git_buf_oom(out) ? -1 : 0; |
7000f3fa RB |
276 | } |
277 | ||
eb1c1707 | 278 | static int diff_delta_format_with_paths( |
197b8966 RB |
279 | git_buf *out, |
280 | const git_diff_delta *delta, | |
281 | const char *oldpfx, | |
282 | const char *newpfx, | |
eb1c1707 | 283 | const char *template) |
7000f3fa | 284 | { |
7000f3fa | 285 | const char *oldpath = delta->old_file.path; |
7000f3fa | 286 | const char *newpath = delta->new_file.path; |
7000f3fa | 287 | |
9950bb4e | 288 | if (git_oid_iszero(&delta->old_file.id)) { |
eb1c1707 RB |
289 | oldpfx = ""; |
290 | oldpath = "/dev/null"; | |
291 | } | |
9950bb4e | 292 | if (git_oid_iszero(&delta->new_file.id)) { |
eb1c1707 RB |
293 | newpfx = ""; |
294 | newpath = "/dev/null"; | |
295 | } | |
296 | ||
297 | return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath); | |
298 | } | |
299 | ||
300 | int git_diff_delta__format_file_header( | |
301 | git_buf *out, | |
302 | const git_diff_delta *delta, | |
303 | const char *oldpfx, | |
304 | const char *newpfx, | |
305 | int oid_strlen) | |
306 | { | |
7000f3fa RB |
307 | if (!oldpfx) |
308 | oldpfx = DIFF_OLD_PREFIX_DEFAULT; | |
7000f3fa RB |
309 | if (!newpfx) |
310 | newpfx = DIFF_NEW_PREFIX_DEFAULT; | |
197b8966 RB |
311 | if (!oid_strlen) |
312 | oid_strlen = GIT_ABBREV_DEFAULT + 1; | |
7000f3fa | 313 | |
197b8966 RB |
314 | git_buf_clear(out); |
315 | ||
316 | git_buf_printf(out, "diff --git %s%s %s%s\n", | |
eb1c1707 | 317 | oldpfx, delta->old_file.path, newpfx, delta->new_file.path); |
7000f3fa | 318 | |
25e0b157 | 319 | GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen)); |
7000f3fa | 320 | |
eb1c1707 RB |
321 | if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) |
322 | diff_delta_format_with_paths( | |
323 | out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n"); | |
7000f3fa | 324 | |
197b8966 RB |
325 | return git_buf_oom(out) ? -1 : 0; |
326 | } | |
7000f3fa | 327 | |
8147b1af ET |
328 | static int format_binary( |
329 | diff_print_info *pi, | |
330 | git_diff_binary_t type, | |
331 | const char *data, | |
332 | size_t datalen, | |
333 | size_t inflatedlen) | |
e349ed50 | 334 | { |
8147b1af ET |
335 | const char *typename = type == GIT_DIFF_BINARY_DELTA ? |
336 | "delta" : "literal"; | |
337 | const char *scan, *end; | |
e349ed50 | 338 | |
8147b1af | 339 | git_buf_printf(pi->buf, "%s %lu\n", typename, inflatedlen); |
e349ed50 ET |
340 | pi->line.num_lines++; |
341 | ||
8147b1af | 342 | for (scan = data, end = data + datalen; scan < end; ) { |
947a58c1 RB |
343 | size_t chunk_len = end - scan; |
344 | if (chunk_len > 52) | |
345 | chunk_len = 52; | |
e349ed50 ET |
346 | |
347 | if (chunk_len <= 26) | |
c6320bec | 348 | git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); |
e349ed50 | 349 | else |
c6320bec | 350 | git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); |
e349ed50 | 351 | |
e003f83a | 352 | git_buf_encode_base85(pi->buf, scan, chunk_len); |
e349ed50 ET |
353 | git_buf_putc(pi->buf, '\n'); |
354 | ||
8147b1af ET |
355 | if (git_buf_oom(pi->buf)) |
356 | return -1; | |
e349ed50 | 357 | |
947a58c1 | 358 | scan += chunk_len; |
e349ed50 ET |
359 | pi->line.num_lines++; |
360 | } | |
361 | ||
8147b1af ET |
362 | return 0; |
363 | } | |
e349ed50 | 364 | |
8147b1af ET |
365 | static int diff_print_load_content( |
366 | diff_print_info *pi, | |
367 | git_diff_delta *delta) | |
368 | { | |
369 | git_diff_file_content *ofile, *nfile; | |
370 | int error; | |
371 | ||
372 | assert(pi->diff); | |
373 | ||
374 | ofile = git__calloc(1, sizeof(git_diff_file_content)); | |
375 | nfile = git__calloc(1, sizeof(git_diff_file_content)); | |
376 | ||
377 | GITERR_CHECK_ALLOC(ofile); | |
378 | GITERR_CHECK_ALLOC(nfile); | |
379 | ||
380 | if ((error = git_diff_file_content__init_from_diff( | |
381 | ofile, pi->diff, delta, true)) < 0 || | |
382 | (error = git_diff_file_content__init_from_diff( | |
383 | nfile, pi->diff, delta, true)) < 0) { | |
384 | ||
385 | git__free(ofile); | |
386 | git__free(nfile); | |
387 | return error; | |
388 | } | |
389 | ||
390 | pi->content_loaded = 1; | |
391 | pi->content_allocated = 1; | |
392 | pi->ofile = ofile; | |
393 | pi->nfile = nfile; | |
394 | ||
395 | return 0; | |
e349ed50 ET |
396 | } |
397 | ||
e349ed50 | 398 | static int diff_print_patch_file_binary( |
8147b1af ET |
399 | diff_print_info *pi, git_diff_delta *delta, |
400 | const char *old_pfx, const char *new_pfx, | |
401 | const git_diff_binary *binary) | |
e349ed50 | 402 | { |
947a58c1 | 403 | size_t pre_binary_size; |
8147b1af | 404 | int error; |
e349ed50 | 405 | |
947a58c1 RB |
406 | if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) |
407 | goto noshow; | |
e349ed50 | 408 | |
8147b1af ET |
409 | if (!pi->content_loaded && |
410 | (error = diff_print_load_content(pi, delta)) < 0) | |
411 | return error; | |
412 | ||
947a58c1 | 413 | pre_binary_size = pi->buf->size; |
e349ed50 ET |
414 | git_buf_printf(pi->buf, "GIT binary patch\n"); |
415 | pi->line.num_lines++; | |
416 | ||
8147b1af ET |
417 | if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, |
418 | binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || | |
e349ed50 | 419 | (error = git_buf_putc(pi->buf, '\n')) < 0 || |
8147b1af ET |
420 | (error = format_binary(pi, binary->old_file.type, binary->old_file.data, |
421 | binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { | |
422 | ||
947a58c1 RB |
423 | if (error == GIT_EBUFS) { |
424 | giterr_clear(); | |
425 | git_buf_truncate(pi->buf, pre_binary_size); | |
426 | goto noshow; | |
427 | } | |
428 | } | |
e349ed50 ET |
429 | |
430 | pi->line.num_lines++; | |
e349ed50 | 431 | return error; |
947a58c1 RB |
432 | |
433 | noshow: | |
434 | pi->line.num_lines = 1; | |
435 | return diff_delta_format_with_paths( | |
8147b1af | 436 | pi->buf, delta, old_pfx, new_pfx, |
947a58c1 | 437 | "Binary files %s%s and %s%s differ\n"); |
e349ed50 ET |
438 | } |
439 | ||
197b8966 RB |
440 | static int diff_print_patch_file( |
441 | const git_diff_delta *delta, float progress, void *data) | |
442 | { | |
25e0b157 | 443 | int error; |
197b8966 | 444 | diff_print_info *pi = data; |
eb1c1707 RB |
445 | const char *oldpfx = |
446 | pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT; | |
447 | const char *newpfx = | |
448 | pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT; | |
197b8966 | 449 | |
8147b1af ET |
450 | bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) || |
451 | (pi->flags & GIT_DIFF_FORCE_BINARY); | |
e349ed50 ET |
452 | bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); |
453 | int oid_strlen = binary && show_binary ? | |
454 | GIT_OID_HEXSZ + 1 : pi->oid_strlen; | |
455 | ||
197b8966 | 456 | GIT_UNUSED(progress); |
7000f3fa | 457 | |
197b8966 RB |
458 | if (S_ISDIR(delta->new_file.mode) || |
459 | delta->status == GIT_DELTA_UNMODIFIED || | |
460 | delta->status == GIT_DELTA_IGNORED || | |
61bef72d | 461 | delta->status == GIT_DELTA_UNREADABLE || |
197b8966 | 462 | (delta->status == GIT_DELTA_UNTRACKED && |
10672e3e | 463 | (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) |
7000f3fa RB |
464 | return 0; |
465 | ||
25e0b157 | 466 | if ((error = git_diff_delta__format_file_header( |
e349ed50 | 467 | pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0) |
25e0b157 | 468 | return error; |
7000f3fa | 469 | |
3b5f7954 RB |
470 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
471 | pi->line.content = git_buf_cstr(pi->buf); | |
472 | pi->line.content_len = git_buf_len(pi->buf); | |
473 | ||
8147b1af ET |
474 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
475 | } | |
eb1c1707 | 476 | |
8147b1af ET |
477 | static int diff_print_patch_binary( |
478 | const git_diff_delta *delta, | |
479 | const git_diff_binary *binary, | |
480 | void *data) | |
481 | { | |
482 | diff_print_info *pi = data; | |
483 | const char *old_pfx = | |
484 | pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT; | |
485 | const char *new_pfx = | |
486 | pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT; | |
487 | int error; | |
eb1c1707 RB |
488 | |
489 | git_buf_clear(pi->buf); | |
490 | ||
8147b1af ET |
491 | if ((error = diff_print_patch_file_binary( |
492 | pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0) | |
25e0b157 | 493 | return error; |
eb1c1707 | 494 | |
8147b1af ET |
495 | pi->line.origin = GIT_DIFF_LINE_BINARY; |
496 | pi->line.content = git_buf_cstr(pi->buf); | |
3b5f7954 | 497 | pi->line.content_len = git_buf_len(pi->buf); |
3b5f7954 | 498 | |
25e0b157 | 499 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
500 | } |
501 | ||
a1683f28 | 502 | static int diff_print_patch_hunk( |
7000f3fa | 503 | const git_diff_delta *d, |
3b5f7954 | 504 | const git_diff_hunk *h, |
7000f3fa RB |
505 | void *data) |
506 | { | |
507 | diff_print_info *pi = data; | |
508 | ||
509 | if (S_ISDIR(d->new_file.mode)) | |
510 | return 0; | |
511 | ||
3b5f7954 RB |
512 | pi->line.origin = GIT_DIFF_LINE_HUNK_HDR; |
513 | pi->line.content = h->header; | |
514 | pi->line.content_len = h->header_len; | |
7000f3fa | 515 | |
25e0b157 | 516 | return pi->print_cb(d, h, &pi->line, pi->payload); |
7000f3fa RB |
517 | } |
518 | ||
a1683f28 | 519 | static int diff_print_patch_line( |
7000f3fa | 520 | const git_diff_delta *delta, |
3b5f7954 RB |
521 | const git_diff_hunk *hunk, |
522 | const git_diff_line *line, | |
7000f3fa RB |
523 | void *data) |
524 | { | |
525 | diff_print_info *pi = data; | |
526 | ||
527 | if (S_ISDIR(delta->new_file.mode)) | |
528 | return 0; | |
529 | ||
25e0b157 | 530 | return pi->print_cb(delta, hunk, line, pi->payload); |
7000f3fa RB |
531 | } |
532 | ||
10672e3e RB |
533 | /* print a git_diff to an output callback */ |
534 | int git_diff_print( | |
3ff1d123 | 535 | git_diff *diff, |
10672e3e | 536 | git_diff_format_t format, |
3ff1d123 | 537 | git_diff_line_cb print_cb, |
7000f3fa RB |
538 | void *payload) |
539 | { | |
540 | int error; | |
541 | git_buf buf = GIT_BUF_INIT; | |
542 | diff_print_info pi; | |
10672e3e | 543 | git_diff_file_cb print_file = NULL; |
8147b1af | 544 | git_diff_binary_cb print_binary = NULL; |
10672e3e RB |
545 | git_diff_hunk_cb print_hunk = NULL; |
546 | git_diff_line_cb print_line = NULL; | |
547 | ||
548 | switch (format) { | |
549 | case GIT_DIFF_FORMAT_PATCH: | |
550 | print_file = diff_print_patch_file; | |
8147b1af | 551 | print_binary = diff_print_patch_binary; |
10672e3e RB |
552 | print_hunk = diff_print_patch_hunk; |
553 | print_line = diff_print_patch_line; | |
554 | break; | |
555 | case GIT_DIFF_FORMAT_PATCH_HEADER: | |
556 | print_file = diff_print_patch_file; | |
557 | break; | |
558 | case GIT_DIFF_FORMAT_RAW: | |
559 | print_file = diff_print_one_raw; | |
560 | break; | |
561 | case GIT_DIFF_FORMAT_NAME_ONLY: | |
562 | print_file = diff_print_one_name_only; | |
563 | break; | |
564 | case GIT_DIFF_FORMAT_NAME_STATUS: | |
565 | print_file = diff_print_one_name_status; | |
566 | break; | |
567 | default: | |
568 | giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format); | |
569 | return -1; | |
570 | } | |
7000f3fa | 571 | |
8147b1af ET |
572 | if (!(error = diff_print_info_init_fromdiff( |
573 | &pi, &buf, diff, format, print_cb, payload))) { | |
7000f3fa | 574 | error = git_diff_foreach( |
8147b1af | 575 | diff, print_file, print_binary, print_hunk, print_line, &pi); |
7000f3fa | 576 | |
25e0b157 | 577 | if (error) /* make sure error message is set */ |
26c1cb91 | 578 | giterr_set_after_callback_function(error, "git_diff_print"); |
96869a4e RB |
579 | } |
580 | ||
9568660f CMN |
581 | git__free(pi.nfile); |
582 | git__free(pi.ofile); | |
583 | ||
7000f3fa RB |
584 | git_buf_free(&buf); |
585 | ||
586 | return error; | |
587 | } | |
588 | ||
3ff1d123 RB |
589 | /* print a git_patch to an output callback */ |
590 | int git_patch_print( | |
591 | git_patch *patch, | |
592 | git_diff_line_cb print_cb, | |
7000f3fa RB |
593 | void *payload) |
594 | { | |
595 | int error; | |
596 | git_buf temp = GIT_BUF_INIT; | |
597 | diff_print_info pi; | |
7000f3fa RB |
598 | |
599 | assert(patch && print_cb); | |
600 | ||
8147b1af ET |
601 | if (!(error = diff_print_info_init_frompatch( |
602 | &pi, &temp, patch, | |
10672e3e | 603 | GIT_DIFF_FORMAT_PATCH, print_cb, payload))) |
96869a4e | 604 | { |
3ff1d123 | 605 | error = git_patch__invoke_callbacks( |
8147b1af ET |
606 | patch, diff_print_patch_file, diff_print_patch_binary, |
607 | diff_print_patch_hunk, diff_print_patch_line, &pi); | |
7000f3fa | 608 | |
25e0b157 | 609 | if (error) /* make sure error message is set */ |
26c1cb91 | 610 | giterr_set_after_callback_function(error, "git_patch_print"); |
96869a4e RB |
611 | } |
612 | ||
7000f3fa RB |
613 | git_buf_free(&temp); |
614 | ||
615 | return error; | |
616 | } | |
617 | ||
27e54bcf | 618 | int git_diff_print_callback__to_buf( |
a1683f28 | 619 | const git_diff_delta *delta, |
3b5f7954 RB |
620 | const git_diff_hunk *hunk, |
621 | const git_diff_line *line, | |
a1683f28 RB |
622 | void *payload) |
623 | { | |
624 | git_buf *output = payload; | |
3b5f7954 RB |
625 | GIT_UNUSED(delta); GIT_UNUSED(hunk); |
626 | ||
27e54bcf RB |
627 | if (!output) { |
628 | giterr_set(GITERR_INVALID, "Buffer pointer must be provided"); | |
629 | return -1; | |
630 | } | |
631 | ||
3b5f7954 RB |
632 | if (line->origin == GIT_DIFF_LINE_ADDITION || |
633 | line->origin == GIT_DIFF_LINE_DELETION || | |
634 | line->origin == GIT_DIFF_LINE_CONTEXT) | |
635 | git_buf_putc(output, line->origin); | |
636 | ||
637 | return git_buf_put(output, line->content, line->content_len); | |
a1683f28 RB |
638 | } |
639 | ||
27e54bcf RB |
640 | int git_diff_print_callback__to_file_handle( |
641 | const git_diff_delta *delta, | |
642 | const git_diff_hunk *hunk, | |
643 | const git_diff_line *line, | |
644 | void *payload) | |
645 | { | |
646 | FILE *fp = payload ? payload : stdout; | |
647 | ||
648 | GIT_UNUSED(delta); GIT_UNUSED(hunk); | |
649 | ||
650 | if (line->origin == GIT_DIFF_LINE_CONTEXT || | |
651 | line->origin == GIT_DIFF_LINE_ADDITION || | |
652 | line->origin == GIT_DIFF_LINE_DELETION) | |
653 | fputc(line->origin, fp); | |
654 | fwrite(line->content, 1, line->content_len, fp); | |
655 | return 0; | |
656 | } | |
657 | ||
450e8e9e | 658 | /* print a git_patch to a git_buf */ |
1e4976cb | 659 | int git_patch_to_buf(git_buf *out, git_patch *patch) |
450e8e9e | 660 | { |
1e4976cb RB |
661 | assert(out && patch); |
662 | git_buf_sanitize(out); | |
27e54bcf | 663 | return git_patch_print(patch, git_diff_print_callback__to_buf, out); |
450e8e9e | 664 | } |