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