]>
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; |
7000f3fa RB |
25 | } diff_print_info; |
26 | ||
27 | static int diff_print_info_init( | |
28 | diff_print_info *pi, | |
10672e3e RB |
29 | git_buf *out, |
30 | git_diff *diff, | |
31 | git_diff_format_t format, | |
32 | git_diff_line_cb cb, | |
33 | void *payload) | |
7000f3fa | 34 | { |
7000f3fa | 35 | pi->diff = diff; |
10672e3e | 36 | pi->format = format; |
7000f3fa RB |
37 | pi->print_cb = cb; |
38 | pi->payload = payload; | |
39 | pi->buf = out; | |
40 | ||
10672e3e RB |
41 | if (diff) |
42 | pi->flags = diff->opts.flags; | |
e349ed50 ET |
43 | else |
44 | pi->flags = 0; | |
10672e3e | 45 | |
86bfc3e1 CMN |
46 | if (diff && diff->opts.id_abbrev != 0) |
47 | pi->oid_strlen = diff->opts.id_abbrev; | |
10672e3e | 48 | else if (!diff || !diff->repo) |
74ded024 RB |
49 | pi->oid_strlen = GIT_ABBREV_DEFAULT; |
50 | else if (git_repository__cvar( | |
51 | &pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0) | |
7000f3fa RB |
52 | return -1; |
53 | ||
54 | pi->oid_strlen += 1; /* for NUL byte */ | |
55 | ||
56 | if (pi->oid_strlen < 2) | |
57 | pi->oid_strlen = 2; | |
58 | else if (pi->oid_strlen > GIT_OID_HEXSZ + 1) | |
59 | pi->oid_strlen = GIT_OID_HEXSZ + 1; | |
60 | ||
3b5f7954 RB |
61 | memset(&pi->line, 0, sizeof(pi->line)); |
62 | pi->line.old_lineno = -1; | |
63 | pi->line.new_lineno = -1; | |
64 | pi->line.num_lines = 1; | |
65 | ||
7000f3fa RB |
66 | return 0; |
67 | } | |
68 | ||
a1683f28 | 69 | static char diff_pick_suffix(int mode) |
7000f3fa RB |
70 | { |
71 | if (S_ISDIR(mode)) | |
72 | return '/'; | |
a7fcc44d | 73 | else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */ |
7000f3fa RB |
74 | /* in git, modes are very regular, so we must have 0100755 mode */ |
75 | return '*'; | |
76 | else | |
77 | return ' '; | |
78 | } | |
79 | ||
80 | char git_diff_status_char(git_delta_t status) | |
81 | { | |
82 | char code; | |
83 | ||
84 | switch (status) { | |
85b7268e AR |
85 | case GIT_DELTA_ADDED: code = 'A'; break; |
86 | case GIT_DELTA_DELETED: code = 'D'; break; | |
87 | case GIT_DELTA_MODIFIED: code = 'M'; break; | |
88 | case GIT_DELTA_RENAMED: code = 'R'; break; | |
89 | case GIT_DELTA_COPIED: code = 'C'; break; | |
90 | case GIT_DELTA_IGNORED: code = 'I'; break; | |
91 | case GIT_DELTA_UNTRACKED: code = '?'; break; | |
92 | case GIT_DELTA_UNREADABLE: code = 'X'; break; | |
93 | default: code = ' '; break; | |
7000f3fa RB |
94 | } |
95 | ||
96 | return code; | |
97 | } | |
98 | ||
10672e3e RB |
99 | static int diff_print_one_name_only( |
100 | const git_diff_delta *delta, float progress, void *data) | |
101 | { | |
102 | diff_print_info *pi = data; | |
103 | git_buf *out = pi->buf; | |
104 | ||
105 | GIT_UNUSED(progress); | |
106 | ||
107 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && | |
108 | delta->status == GIT_DELTA_UNMODIFIED) | |
109 | return 0; | |
110 | ||
111 | git_buf_clear(out); | |
25e0b157 RB |
112 | git_buf_puts(out, delta->new_file.path); |
113 | git_buf_putc(out, '\n'); | |
114 | if (git_buf_oom(out)) | |
115 | return -1; | |
10672e3e | 116 | |
3b5f7954 RB |
117 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
118 | pi->line.content = git_buf_cstr(out); | |
119 | pi->line.content_len = git_buf_len(out); | |
120 | ||
25e0b157 | 121 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
10672e3e RB |
122 | } |
123 | ||
124 | static int diff_print_one_name_status( | |
7000f3fa RB |
125 | const git_diff_delta *delta, float progress, void *data) |
126 | { | |
127 | diff_print_info *pi = data; | |
a1683f28 | 128 | git_buf *out = pi->buf; |
7000f3fa | 129 | char old_suffix, new_suffix, code = git_diff_status_char(delta->status); |
74ded024 RB |
130 | int (*strcomp)(const char *, const char *) = |
131 | pi->diff ? pi->diff->strcomp : git__strcmp; | |
7000f3fa RB |
132 | |
133 | GIT_UNUSED(progress); | |
134 | ||
10672e3e | 135 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') |
7000f3fa RB |
136 | return 0; |
137 | ||
a1683f28 RB |
138 | old_suffix = diff_pick_suffix(delta->old_file.mode); |
139 | new_suffix = diff_pick_suffix(delta->new_file.mode); | |
7000f3fa | 140 | |
a1683f28 | 141 | git_buf_clear(out); |
7000f3fa RB |
142 | |
143 | if (delta->old_file.path != delta->new_file.path && | |
74ded024 | 144 | strcomp(delta->old_file.path,delta->new_file.path) != 0) |
df40f398 | 145 | git_buf_printf(out, "%c\t%s%c %s%c\n", code, |
7000f3fa RB |
146 | delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); |
147 | else if (delta->old_file.mode != delta->new_file.mode && | |
148 | delta->old_file.mode != 0 && delta->new_file.mode != 0) | |
df40f398 RB |
149 | git_buf_printf(out, "%c\t%s%c %s%c\n", code, |
150 | delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); | |
7000f3fa | 151 | else if (old_suffix != ' ') |
a1683f28 | 152 | git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); |
7000f3fa | 153 | else |
a1683f28 | 154 | git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); |
a1683f28 | 155 | if (git_buf_oom(out)) |
25e0b157 | 156 | return -1; |
7000f3fa | 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); |
7000f3fa RB |
163 | } |
164 | ||
a1683f28 | 165 | static int diff_print_one_raw( |
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 RB |
170 | char code = git_diff_status_char(delta->status); |
171 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
172 | ||
173 | GIT_UNUSED(progress); | |
174 | ||
10672e3e | 175 | if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ') |
7000f3fa RB |
176 | return 0; |
177 | ||
a1683f28 | 178 | git_buf_clear(out); |
7000f3fa | 179 | |
9950bb4e CMN |
180 | git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id); |
181 | git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id); | |
7000f3fa RB |
182 | |
183 | git_buf_printf( | |
12e422a0 RB |
184 | out, (pi->oid_strlen <= GIT_OID_HEXSZ) ? |
185 | ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c", | |
7000f3fa RB |
186 | delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); |
187 | ||
188 | if (delta->similarity > 0) | |
a1683f28 | 189 | git_buf_printf(out, "%03u", delta->similarity); |
7000f3fa | 190 | |
e4acc3ba | 191 | if (delta->old_file.path != delta->new_file.path) |
7000f3fa | 192 | git_buf_printf( |
a1683f28 | 193 | out, "\t%s %s\n", delta->old_file.path, delta->new_file.path); |
7000f3fa RB |
194 | else |
195 | git_buf_printf( | |
a1683f28 | 196 | out, "\t%s\n", delta->old_file.path ? |
7000f3fa RB |
197 | delta->old_file.path : delta->new_file.path); |
198 | ||
a1683f28 | 199 | if (git_buf_oom(out)) |
25e0b157 | 200 | return -1; |
7000f3fa | 201 | |
3b5f7954 RB |
202 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
203 | pi->line.content = git_buf_cstr(out); | |
204 | pi->line.content_len = git_buf_len(out); | |
205 | ||
25e0b157 | 206 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
207 | } |
208 | ||
197b8966 RB |
209 | static int diff_print_oid_range( |
210 | git_buf *out, const git_diff_delta *delta, int oid_strlen) | |
7000f3fa RB |
211 | { |
212 | char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; | |
213 | ||
9950bb4e CMN |
214 | git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id); |
215 | git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id); | |
7000f3fa RB |
216 | |
217 | /* TODO: Match git diff more closely */ | |
218 | if (delta->old_file.mode == delta->new_file.mode) { | |
a1683f28 | 219 | git_buf_printf(out, "index %s..%s %o\n", |
7000f3fa RB |
220 | start_oid, end_oid, delta->old_file.mode); |
221 | } else { | |
222 | if (delta->old_file.mode == 0) { | |
a1683f28 | 223 | git_buf_printf(out, "new file mode %o\n", delta->new_file.mode); |
7000f3fa | 224 | } else if (delta->new_file.mode == 0) { |
a1683f28 | 225 | git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode); |
7000f3fa | 226 | } else { |
a1683f28 RB |
227 | git_buf_printf(out, "old mode %o\n", delta->old_file.mode); |
228 | git_buf_printf(out, "new mode %o\n", delta->new_file.mode); | |
7000f3fa | 229 | } |
a1683f28 | 230 | git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); |
7000f3fa RB |
231 | } |
232 | ||
25e0b157 | 233 | return git_buf_oom(out) ? -1 : 0; |
7000f3fa RB |
234 | } |
235 | ||
eb1c1707 | 236 | static int diff_delta_format_with_paths( |
197b8966 RB |
237 | git_buf *out, |
238 | const git_diff_delta *delta, | |
239 | const char *oldpfx, | |
240 | const char *newpfx, | |
eb1c1707 | 241 | const char *template) |
7000f3fa | 242 | { |
7000f3fa | 243 | const char *oldpath = delta->old_file.path; |
7000f3fa | 244 | const char *newpath = delta->new_file.path; |
7000f3fa | 245 | |
9950bb4e | 246 | if (git_oid_iszero(&delta->old_file.id)) { |
eb1c1707 RB |
247 | oldpfx = ""; |
248 | oldpath = "/dev/null"; | |
249 | } | |
9950bb4e | 250 | if (git_oid_iszero(&delta->new_file.id)) { |
eb1c1707 RB |
251 | newpfx = ""; |
252 | newpath = "/dev/null"; | |
253 | } | |
254 | ||
255 | return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath); | |
256 | } | |
257 | ||
258 | int git_diff_delta__format_file_header( | |
259 | git_buf *out, | |
260 | const git_diff_delta *delta, | |
261 | const char *oldpfx, | |
262 | const char *newpfx, | |
263 | int oid_strlen) | |
264 | { | |
7000f3fa RB |
265 | if (!oldpfx) |
266 | oldpfx = DIFF_OLD_PREFIX_DEFAULT; | |
7000f3fa RB |
267 | if (!newpfx) |
268 | newpfx = DIFF_NEW_PREFIX_DEFAULT; | |
197b8966 RB |
269 | if (!oid_strlen) |
270 | oid_strlen = GIT_ABBREV_DEFAULT + 1; | |
7000f3fa | 271 | |
197b8966 RB |
272 | git_buf_clear(out); |
273 | ||
274 | git_buf_printf(out, "diff --git %s%s %s%s\n", | |
eb1c1707 | 275 | oldpfx, delta->old_file.path, newpfx, delta->new_file.path); |
7000f3fa | 276 | |
25e0b157 | 277 | GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen)); |
7000f3fa | 278 | |
eb1c1707 RB |
279 | if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) |
280 | diff_delta_format_with_paths( | |
281 | out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n"); | |
7000f3fa | 282 | |
197b8966 RB |
283 | return git_buf_oom(out) ? -1 : 0; |
284 | } | |
7000f3fa | 285 | |
e349ed50 ET |
286 | static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) |
287 | { | |
288 | git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL; | |
289 | const void *old_data, *new_data; | |
947a58c1 RB |
290 | git_off_t old_data_len, new_data_len; |
291 | unsigned long delta_data_len, inflated_len; | |
e349ed50 | 292 | const char *out_type = "literal"; |
947a58c1 | 293 | char *scan, *end; |
e349ed50 ET |
294 | int error; |
295 | ||
296 | old_data = old ? git_blob_rawcontent(old) : NULL; | |
297 | new_data = new ? git_blob_rawcontent(new) : NULL; | |
298 | ||
947a58c1 RB |
299 | old_data_len = old ? git_blob_rawsize(old) : 0; |
300 | new_data_len = new ? git_blob_rawsize(new) : 0; | |
c6320bec | 301 | |
bc81220d RB |
302 | /* The git_delta function accepts unsigned long only */ |
303 | if (!git__is_ulong(old_data_len) || !git__is_ulong(new_data_len)) | |
304 | return GIT_EBUFS; | |
e349ed50 ET |
305 | |
306 | out = &deflate; | |
947a58c1 | 307 | inflated_len = (unsigned long)new_data_len; |
e349ed50 ET |
308 | |
309 | if ((error = git_zstream_deflatebuf( | |
bc81220d | 310 | out, new_data, (size_t)new_data_len)) < 0) |
e349ed50 ET |
311 | goto done; |
312 | ||
c6320bec | 313 | /* The git_delta function accepts unsigned long only */ |
947a58c1 RB |
314 | if (!git__is_ulong((git_off_t)deflate.size)) { |
315 | error = GIT_EBUFS; | |
c6320bec PK |
316 | goto done; |
317 | } | |
e349ed50 | 318 | |
e349ed50 | 319 | if (old && new) { |
947a58c1 RB |
320 | void *delta_data = git_delta( |
321 | old_data, (unsigned long)old_data_len, | |
322 | new_data, (unsigned long)new_data_len, | |
323 | &delta_data_len, (unsigned long)deflate.size); | |
e349ed50 ET |
324 | |
325 | if (delta_data) { | |
947a58c1 RB |
326 | error = git_zstream_deflatebuf( |
327 | &delta, delta_data, (size_t)delta_data_len); | |
328 | ||
329 | git__free(delta_data); | |
e349ed50 ET |
330 | |
331 | if (error < 0) | |
332 | goto done; | |
333 | ||
334 | if (delta.size < deflate.size) { | |
335 | out = δ | |
336 | out_type = "delta"; | |
337 | inflated_len = delta_data_len; | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
4c9ffdff | 342 | git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len); |
e349ed50 ET |
343 | pi->line.num_lines++; |
344 | ||
947a58c1 RB |
345 | for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) { |
346 | size_t chunk_len = end - scan; | |
347 | if (chunk_len > 52) | |
348 | chunk_len = 52; | |
e349ed50 ET |
349 | |
350 | if (chunk_len <= 26) | |
c6320bec | 351 | git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); |
e349ed50 | 352 | else |
c6320bec | 353 | git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); |
e349ed50 | 354 | |
e003f83a | 355 | git_buf_encode_base85(pi->buf, scan, chunk_len); |
e349ed50 ET |
356 | git_buf_putc(pi->buf, '\n'); |
357 | ||
358 | if (git_buf_oom(pi->buf)) { | |
359 | error = -1; | |
360 | goto done; | |
361 | } | |
362 | ||
947a58c1 | 363 | scan += chunk_len; |
e349ed50 ET |
364 | pi->line.num_lines++; |
365 | } | |
366 | ||
367 | done: | |
368 | git_buf_free(&deflate); | |
369 | git_buf_free(&delta); | |
370 | ||
371 | return error; | |
372 | } | |
373 | ||
374 | /* git diff --binary 8d7523f~2 8d7523f~1 */ | |
375 | static int diff_print_patch_file_binary( | |
376 | diff_print_info *pi, const git_diff_delta *delta, | |
377 | const char *oldpfx, const char *newpfx) | |
378 | { | |
379 | git_blob *old = NULL, *new = NULL; | |
380 | const git_oid *old_id, *new_id; | |
381 | int error; | |
947a58c1 | 382 | size_t pre_binary_size; |
e349ed50 | 383 | |
947a58c1 RB |
384 | if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) |
385 | goto noshow; | |
e349ed50 | 386 | |
947a58c1 | 387 | pre_binary_size = pi->buf->size; |
e349ed50 ET |
388 | git_buf_printf(pi->buf, "GIT binary patch\n"); |
389 | pi->line.num_lines++; | |
390 | ||
391 | old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL; | |
392 | new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL; | |
393 | ||
947a58c1 RB |
394 | if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) |
395 | goto done; | |
396 | if (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) | |
397 | goto done; | |
398 | ||
399 | if ((error = print_binary_hunk(pi, old, new)) < 0 || | |
e349ed50 ET |
400 | (error = git_buf_putc(pi->buf, '\n')) < 0 || |
401 | (error = print_binary_hunk(pi, new, old)) < 0) | |
947a58c1 RB |
402 | { |
403 | if (error == GIT_EBUFS) { | |
404 | giterr_clear(); | |
405 | git_buf_truncate(pi->buf, pre_binary_size); | |
406 | goto noshow; | |
407 | } | |
408 | } | |
e349ed50 ET |
409 | |
410 | pi->line.num_lines++; | |
411 | ||
412 | done: | |
413 | git_blob_free(old); | |
414 | git_blob_free(new); | |
415 | ||
416 | return error; | |
947a58c1 RB |
417 | |
418 | noshow: | |
419 | pi->line.num_lines = 1; | |
420 | return diff_delta_format_with_paths( | |
421 | pi->buf, delta, oldpfx, newpfx, | |
422 | "Binary files %s%s and %s%s differ\n"); | |
e349ed50 ET |
423 | } |
424 | ||
197b8966 RB |
425 | static int diff_print_patch_file( |
426 | const git_diff_delta *delta, float progress, void *data) | |
427 | { | |
25e0b157 | 428 | int error; |
197b8966 | 429 | diff_print_info *pi = data; |
eb1c1707 RB |
430 | const char *oldpfx = |
431 | pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT; | |
432 | const char *newpfx = | |
433 | pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT; | |
197b8966 | 434 | |
e349ed50 ET |
435 | bool binary = !!(delta->flags & GIT_DIFF_FLAG_BINARY); |
436 | bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); | |
437 | int oid_strlen = binary && show_binary ? | |
438 | GIT_OID_HEXSZ + 1 : pi->oid_strlen; | |
439 | ||
197b8966 | 440 | GIT_UNUSED(progress); |
7000f3fa | 441 | |
197b8966 RB |
442 | if (S_ISDIR(delta->new_file.mode) || |
443 | delta->status == GIT_DELTA_UNMODIFIED || | |
444 | delta->status == GIT_DELTA_IGNORED || | |
61bef72d | 445 | delta->status == GIT_DELTA_UNREADABLE || |
197b8966 | 446 | (delta->status == GIT_DELTA_UNTRACKED && |
10672e3e | 447 | (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) |
7000f3fa RB |
448 | return 0; |
449 | ||
25e0b157 | 450 | if ((error = git_diff_delta__format_file_header( |
e349ed50 | 451 | pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0) |
25e0b157 | 452 | return error; |
7000f3fa | 453 | |
3b5f7954 RB |
454 | pi->line.origin = GIT_DIFF_LINE_FILE_HDR; |
455 | pi->line.content = git_buf_cstr(pi->buf); | |
456 | pi->line.content_len = git_buf_len(pi->buf); | |
457 | ||
25e0b157 RB |
458 | if ((error = pi->print_cb(delta, NULL, &pi->line, pi->payload)) != 0) |
459 | return error; | |
eb1c1707 | 460 | |
e349ed50 | 461 | if (!binary) |
eb1c1707 RB |
462 | return 0; |
463 | ||
464 | git_buf_clear(pi->buf); | |
465 | ||
e349ed50 | 466 | if ((error = diff_print_patch_file_binary(pi, delta, oldpfx, newpfx)) < 0) |
25e0b157 | 467 | return error; |
eb1c1707 | 468 | |
3b5f7954 RB |
469 | pi->line.origin = GIT_DIFF_LINE_BINARY; |
470 | pi->line.content = git_buf_cstr(pi->buf); | |
471 | pi->line.content_len = git_buf_len(pi->buf); | |
3b5f7954 | 472 | |
25e0b157 | 473 | return pi->print_cb(delta, NULL, &pi->line, pi->payload); |
7000f3fa RB |
474 | } |
475 | ||
a1683f28 | 476 | static int diff_print_patch_hunk( |
7000f3fa | 477 | const git_diff_delta *d, |
3b5f7954 | 478 | const git_diff_hunk *h, |
7000f3fa RB |
479 | void *data) |
480 | { | |
481 | diff_print_info *pi = data; | |
482 | ||
483 | if (S_ISDIR(d->new_file.mode)) | |
484 | return 0; | |
485 | ||
3b5f7954 RB |
486 | pi->line.origin = GIT_DIFF_LINE_HUNK_HDR; |
487 | pi->line.content = h->header; | |
488 | pi->line.content_len = h->header_len; | |
7000f3fa | 489 | |
25e0b157 | 490 | return pi->print_cb(d, h, &pi->line, pi->payload); |
7000f3fa RB |
491 | } |
492 | ||
a1683f28 | 493 | static int diff_print_patch_line( |
7000f3fa | 494 | const git_diff_delta *delta, |
3b5f7954 RB |
495 | const git_diff_hunk *hunk, |
496 | const git_diff_line *line, | |
7000f3fa RB |
497 | void *data) |
498 | { | |
499 | diff_print_info *pi = data; | |
500 | ||
501 | if (S_ISDIR(delta->new_file.mode)) | |
502 | return 0; | |
503 | ||
25e0b157 | 504 | return pi->print_cb(delta, hunk, line, pi->payload); |
7000f3fa RB |
505 | } |
506 | ||
10672e3e RB |
507 | /* print a git_diff to an output callback */ |
508 | int git_diff_print( | |
3ff1d123 | 509 | git_diff *diff, |
10672e3e | 510 | git_diff_format_t format, |
3ff1d123 | 511 | git_diff_line_cb print_cb, |
7000f3fa RB |
512 | void *payload) |
513 | { | |
514 | int error; | |
515 | git_buf buf = GIT_BUF_INIT; | |
516 | diff_print_info pi; | |
10672e3e RB |
517 | git_diff_file_cb print_file = NULL; |
518 | git_diff_hunk_cb print_hunk = NULL; | |
519 | git_diff_line_cb print_line = NULL; | |
520 | ||
521 | switch (format) { | |
522 | case GIT_DIFF_FORMAT_PATCH: | |
523 | print_file = diff_print_patch_file; | |
524 | print_hunk = diff_print_patch_hunk; | |
525 | print_line = diff_print_patch_line; | |
526 | break; | |
527 | case GIT_DIFF_FORMAT_PATCH_HEADER: | |
528 | print_file = diff_print_patch_file; | |
529 | break; | |
530 | case GIT_DIFF_FORMAT_RAW: | |
531 | print_file = diff_print_one_raw; | |
532 | break; | |
533 | case GIT_DIFF_FORMAT_NAME_ONLY: | |
534 | print_file = diff_print_one_name_only; | |
535 | break; | |
536 | case GIT_DIFF_FORMAT_NAME_STATUS: | |
537 | print_file = diff_print_one_name_status; | |
538 | break; | |
539 | default: | |
540 | giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format); | |
541 | return -1; | |
542 | } | |
7000f3fa | 543 | |
10672e3e RB |
544 | if (!(error = diff_print_info_init( |
545 | &pi, &buf, diff, format, print_cb, payload))) | |
96869a4e | 546 | { |
7000f3fa | 547 | error = git_diff_foreach( |
10672e3e | 548 | diff, print_file, print_hunk, print_line, &pi); |
7000f3fa | 549 | |
25e0b157 | 550 | if (error) /* make sure error message is set */ |
26c1cb91 | 551 | giterr_set_after_callback_function(error, "git_diff_print"); |
96869a4e RB |
552 | } |
553 | ||
7000f3fa RB |
554 | git_buf_free(&buf); |
555 | ||
556 | return error; | |
557 | } | |
558 | ||
3ff1d123 RB |
559 | /* print a git_patch to an output callback */ |
560 | int git_patch_print( | |
561 | git_patch *patch, | |
562 | git_diff_line_cb print_cb, | |
7000f3fa RB |
563 | void *payload) |
564 | { | |
565 | int error; | |
566 | git_buf temp = GIT_BUF_INIT; | |
567 | diff_print_info pi; | |
7000f3fa RB |
568 | |
569 | assert(patch && print_cb); | |
570 | ||
571 | if (!(error = diff_print_info_init( | |
10672e3e RB |
572 | &pi, &temp, git_patch__diff(patch), |
573 | GIT_DIFF_FORMAT_PATCH, print_cb, payload))) | |
96869a4e | 574 | { |
3ff1d123 | 575 | error = git_patch__invoke_callbacks( |
a1683f28 RB |
576 | patch, diff_print_patch_file, diff_print_patch_hunk, |
577 | diff_print_patch_line, &pi); | |
7000f3fa | 578 | |
25e0b157 | 579 | if (error) /* make sure error message is set */ |
26c1cb91 | 580 | giterr_set_after_callback_function(error, "git_patch_print"); |
96869a4e RB |
581 | } |
582 | ||
7000f3fa RB |
583 | git_buf_free(&temp); |
584 | ||
585 | return error; | |
586 | } | |
587 | ||
27e54bcf | 588 | int git_diff_print_callback__to_buf( |
a1683f28 | 589 | const git_diff_delta *delta, |
3b5f7954 RB |
590 | const git_diff_hunk *hunk, |
591 | const git_diff_line *line, | |
a1683f28 RB |
592 | void *payload) |
593 | { | |
594 | git_buf *output = payload; | |
3b5f7954 RB |
595 | GIT_UNUSED(delta); GIT_UNUSED(hunk); |
596 | ||
27e54bcf RB |
597 | if (!output) { |
598 | giterr_set(GITERR_INVALID, "Buffer pointer must be provided"); | |
599 | return -1; | |
600 | } | |
601 | ||
3b5f7954 RB |
602 | if (line->origin == GIT_DIFF_LINE_ADDITION || |
603 | line->origin == GIT_DIFF_LINE_DELETION || | |
604 | line->origin == GIT_DIFF_LINE_CONTEXT) | |
605 | git_buf_putc(output, line->origin); | |
606 | ||
607 | return git_buf_put(output, line->content, line->content_len); | |
a1683f28 RB |
608 | } |
609 | ||
27e54bcf RB |
610 | int git_diff_print_callback__to_file_handle( |
611 | const git_diff_delta *delta, | |
612 | const git_diff_hunk *hunk, | |
613 | const git_diff_line *line, | |
614 | void *payload) | |
615 | { | |
616 | FILE *fp = payload ? payload : stdout; | |
617 | ||
618 | GIT_UNUSED(delta); GIT_UNUSED(hunk); | |
619 | ||
620 | if (line->origin == GIT_DIFF_LINE_CONTEXT || | |
621 | line->origin == GIT_DIFF_LINE_ADDITION || | |
622 | line->origin == GIT_DIFF_LINE_DELETION) | |
623 | fputc(line->origin, fp); | |
624 | fwrite(line->content, 1, line->content_len, fp); | |
625 | return 0; | |
626 | } | |
627 | ||
450e8e9e | 628 | /* print a git_patch to a git_buf */ |
1e4976cb | 629 | int git_patch_to_buf(git_buf *out, git_patch *patch) |
450e8e9e | 630 | { |
1e4976cb RB |
631 | assert(out && patch); |
632 | git_buf_sanitize(out); | |
27e54bcf | 633 | return git_patch_print(patch, git_diff_print_callback__to_buf, out); |
450e8e9e | 634 | } |