]> git.proxmox.com Git - libgit2.git/blob - src/diff_print.c
Merge pull request #4078 from pks-t/pks/example-cleanup
[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_generate.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_similarity_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 const char *type;
340 int error = 0;
341
342 if (delta->similarity > 100) {
343 giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity);
344 error = -1;
345 goto done;
346 }
347
348 if (delta->status == GIT_DELTA_RENAMED)
349 type = "rename";
350 else if (delta->status == GIT_DELTA_COPIED)
351 type = "copy";
352 else
353 abort();
354
355 if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 ||
356 (error = git_buf_puts(&new_path, delta->new_file.path)) < 0 ||
357 (error = git_buf_quote(&old_path)) < 0 ||
358 (error = git_buf_quote(&new_path)) < 0)
359 goto done;
360
361 git_buf_printf(out,
362 "similarity index %d%%\n"
363 "%s from %s\n"
364 "%s to %s\n",
365 delta->similarity,
366 type, old_path.ptr,
367 type, new_path.ptr);
368
369 if (git_buf_oom(out))
370 error = -1;
371
372 done:
373 git_buf_free(&old_path);
374 git_buf_free(&new_path);
375
376 return error;
377 }
378
379 static bool delta_is_unchanged(const git_diff_delta *delta)
380 {
381 if (git_oid_iszero(&delta->old_file.id) &&
382 git_oid_iszero(&delta->new_file.id))
383 return true;
384
385 if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
386 delta->new_file.mode == GIT_FILEMODE_COMMIT)
387 return false;
388
389 if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
390 return true;
391
392 return false;
393 }
394
395 int git_diff_delta__format_file_header(
396 git_buf *out,
397 const git_diff_delta *delta,
398 const char *oldpfx,
399 const char *newpfx,
400 int id_strlen)
401 {
402 git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
403 bool unchanged = delta_is_unchanged(delta);
404 int error = 0;
405
406 if (!oldpfx)
407 oldpfx = DIFF_OLD_PREFIX_DEFAULT;
408 if (!newpfx)
409 newpfx = DIFF_NEW_PREFIX_DEFAULT;
410 if (!id_strlen)
411 id_strlen = GIT_ABBREV_DEFAULT;
412
413 if ((error = diff_delta_format_path(
414 &old_path, oldpfx, delta->old_file.path)) < 0 ||
415 (error = diff_delta_format_path(
416 &new_path, newpfx, delta->new_file.path)) < 0)
417 goto done;
418
419 git_buf_clear(out);
420
421 git_buf_printf(out, "diff --git %s %s\n",
422 old_path.ptr, new_path.ptr);
423
424 if (delta->status == GIT_DELTA_RENAMED ||
425 (delta->status == GIT_DELTA_COPIED && unchanged)) {
426 if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
427 goto done;
428 }
429
430 if (!unchanged) {
431 if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0)
432 goto done;
433
434 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
435 diff_delta_format_with_paths(out, delta,
436 "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
437 }
438
439 if (unchanged && delta->old_file.mode != delta->new_file.mode)
440 diff_print_modes(out, delta);
441
442 if (git_buf_oom(out))
443 error = -1;
444
445 done:
446 git_buf_free(&old_path);
447 git_buf_free(&new_path);
448
449 return error;
450 }
451
452 static int format_binary(
453 diff_print_info *pi,
454 git_diff_binary_t type,
455 const char *data,
456 size_t datalen,
457 size_t inflatedlen)
458 {
459 const char *typename = type == GIT_DIFF_BINARY_DELTA ?
460 "delta" : "literal";
461 const char *scan, *end;
462
463 git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
464 pi->line.num_lines++;
465
466 for (scan = data, end = data + datalen; scan < end; ) {
467 size_t chunk_len = end - scan;
468 if (chunk_len > 52)
469 chunk_len = 52;
470
471 if (chunk_len <= 26)
472 git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
473 else
474 git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
475
476 git_buf_encode_base85(pi->buf, scan, chunk_len);
477 git_buf_putc(pi->buf, '\n');
478
479 if (git_buf_oom(pi->buf))
480 return -1;
481
482 scan += chunk_len;
483 pi->line.num_lines++;
484 }
485 git_buf_putc(pi->buf, '\n');
486
487 return 0;
488 }
489
490 static int diff_print_patch_file_binary_noshow(
491 diff_print_info *pi, git_diff_delta *delta,
492 const char *old_pfx, const char *new_pfx)
493 {
494 git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
495 int error;
496
497 if ((error = diff_delta_format_path(
498 &old_path, old_pfx, delta->old_file.path)) < 0 ||
499 (error = diff_delta_format_path(
500 &new_path, new_pfx, delta->new_file.path)) < 0)
501 goto done;
502
503 pi->line.num_lines = 1;
504 error = diff_delta_format_with_paths(
505 pi->buf, delta, "Binary files %s and %s differ\n",
506 old_path.ptr, new_path.ptr);
507
508 done:
509 git_buf_free(&old_path);
510 git_buf_free(&new_path);
511
512 return error;
513 }
514
515 static int diff_print_patch_file_binary(
516 diff_print_info *pi, git_diff_delta *delta,
517 const char *old_pfx, const char *new_pfx,
518 const git_diff_binary *binary)
519 {
520 size_t pre_binary_size;
521 int error;
522
523 if (delta->status == GIT_DELTA_UNMODIFIED)
524 return 0;
525
526 if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
527 return diff_print_patch_file_binary_noshow(
528 pi, delta, old_pfx, new_pfx);
529
530 pre_binary_size = pi->buf->size;
531 git_buf_printf(pi->buf, "GIT binary patch\n");
532 pi->line.num_lines++;
533
534 if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
535 binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
536 (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
537 binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
538
539 if (error == GIT_EBUFS) {
540 giterr_clear();
541 git_buf_truncate(pi->buf, pre_binary_size);
542
543 return diff_print_patch_file_binary_noshow(
544 pi, delta, old_pfx, new_pfx);
545 }
546 }
547
548 pi->line.num_lines++;
549 return error;
550 }
551
552 static int diff_print_patch_file(
553 const git_diff_delta *delta, float progress, void *data)
554 {
555 int error;
556 diff_print_info *pi = data;
557 const char *oldpfx =
558 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
559 const char *newpfx =
560 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
561
562 bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
563 (pi->flags & GIT_DIFF_FORCE_BINARY);
564 bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
565 int id_strlen = pi->id_strlen;
566
567 if (binary && show_binary)
568 id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev :
569 delta->new_file.id_abbrev;
570
571 GIT_UNUSED(progress);
572
573 if (S_ISDIR(delta->new_file.mode) ||
574 delta->status == GIT_DELTA_UNMODIFIED ||
575 delta->status == GIT_DELTA_IGNORED ||
576 delta->status == GIT_DELTA_UNREADABLE ||
577 (delta->status == GIT_DELTA_UNTRACKED &&
578 (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
579 return 0;
580
581 if ((error = git_diff_delta__format_file_header(
582 pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0)
583 return error;
584
585 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
586 pi->line.content = git_buf_cstr(pi->buf);
587 pi->line.content_len = git_buf_len(pi->buf);
588
589 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
590 }
591
592 static int diff_print_patch_binary(
593 const git_diff_delta *delta,
594 const git_diff_binary *binary,
595 void *data)
596 {
597 diff_print_info *pi = data;
598 const char *old_pfx =
599 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
600 const char *new_pfx =
601 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
602 int error;
603
604 git_buf_clear(pi->buf);
605
606 if ((error = diff_print_patch_file_binary(
607 pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
608 return error;
609
610 pi->line.origin = GIT_DIFF_LINE_BINARY;
611 pi->line.content = git_buf_cstr(pi->buf);
612 pi->line.content_len = git_buf_len(pi->buf);
613
614 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
615 }
616
617 static int diff_print_patch_hunk(
618 const git_diff_delta *d,
619 const git_diff_hunk *h,
620 void *data)
621 {
622 diff_print_info *pi = data;
623
624 if (S_ISDIR(d->new_file.mode))
625 return 0;
626
627 pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
628 pi->line.content = h->header;
629 pi->line.content_len = h->header_len;
630
631 return pi->print_cb(d, h, &pi->line, pi->payload);
632 }
633
634 static int diff_print_patch_line(
635 const git_diff_delta *delta,
636 const git_diff_hunk *hunk,
637 const git_diff_line *line,
638 void *data)
639 {
640 diff_print_info *pi = data;
641
642 if (S_ISDIR(delta->new_file.mode))
643 return 0;
644
645 return pi->print_cb(delta, hunk, line, pi->payload);
646 }
647
648 /* print a git_diff to an output callback */
649 int git_diff_print(
650 git_diff *diff,
651 git_diff_format_t format,
652 git_diff_line_cb print_cb,
653 void *payload)
654 {
655 int error;
656 git_buf buf = GIT_BUF_INIT;
657 diff_print_info pi;
658 git_diff_file_cb print_file = NULL;
659 git_diff_binary_cb print_binary = NULL;
660 git_diff_hunk_cb print_hunk = NULL;
661 git_diff_line_cb print_line = NULL;
662
663 switch (format) {
664 case GIT_DIFF_FORMAT_PATCH:
665 print_file = diff_print_patch_file;
666 print_binary = diff_print_patch_binary;
667 print_hunk = diff_print_patch_hunk;
668 print_line = diff_print_patch_line;
669 break;
670 case GIT_DIFF_FORMAT_PATCH_HEADER:
671 print_file = diff_print_patch_file;
672 break;
673 case GIT_DIFF_FORMAT_RAW:
674 print_file = diff_print_one_raw;
675 break;
676 case GIT_DIFF_FORMAT_NAME_ONLY:
677 print_file = diff_print_one_name_only;
678 break;
679 case GIT_DIFF_FORMAT_NAME_STATUS:
680 print_file = diff_print_one_name_status;
681 break;
682 default:
683 giterr_set(GITERR_INVALID, "unknown diff output format (%d)", format);
684 return -1;
685 }
686
687 if (!(error = diff_print_info_init_fromdiff(
688 &pi, &buf, diff, format, print_cb, payload))) {
689 error = git_diff_foreach(
690 diff, print_file, print_binary, print_hunk, print_line, &pi);
691
692 if (error) /* make sure error message is set */
693 giterr_set_after_callback_function(error, "git_diff_print");
694 }
695
696 git_buf_free(&buf);
697
698 return error;
699 }
700
701 int git_diff_print_callback__to_buf(
702 const git_diff_delta *delta,
703 const git_diff_hunk *hunk,
704 const git_diff_line *line,
705 void *payload)
706 {
707 git_buf *output = payload;
708 GIT_UNUSED(delta); GIT_UNUSED(hunk);
709
710 if (!output) {
711 giterr_set(GITERR_INVALID, "buffer pointer must be provided");
712 return -1;
713 }
714
715 if (line->origin == GIT_DIFF_LINE_ADDITION ||
716 line->origin == GIT_DIFF_LINE_DELETION ||
717 line->origin == GIT_DIFF_LINE_CONTEXT)
718 git_buf_putc(output, line->origin);
719
720 return git_buf_put(output, line->content, line->content_len);
721 }
722
723 int git_diff_print_callback__to_file_handle(
724 const git_diff_delta *delta,
725 const git_diff_hunk *hunk,
726 const git_diff_line *line,
727 void *payload)
728 {
729 FILE *fp = payload ? payload : stdout;
730
731 GIT_UNUSED(delta); GIT_UNUSED(hunk);
732
733 if (line->origin == GIT_DIFF_LINE_CONTEXT ||
734 line->origin == GIT_DIFF_LINE_ADDITION ||
735 line->origin == GIT_DIFF_LINE_DELETION)
736 fputc(line->origin, fp);
737 fwrite(line->content, 1, line->content_len, fp);
738 return 0;
739 }
740
741 /* print a git_diff to a git_buf */
742 int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
743 {
744 assert(out && diff);
745 git_buf_sanitize(out);
746 return git_diff_print(
747 diff, format, git_diff_print_callback__to_buf, out);
748 }
749
750 /* print a git_patch to an output callback */
751 int git_patch_print(
752 git_patch *patch,
753 git_diff_line_cb print_cb,
754 void *payload)
755 {
756 int error;
757 git_buf temp = GIT_BUF_INIT;
758 diff_print_info pi;
759
760 assert(patch && print_cb);
761
762 if (!(error = diff_print_info_init_frompatch(
763 &pi, &temp, patch,
764 GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
765 {
766 error = git_patch__invoke_callbacks(
767 patch,
768 diff_print_patch_file, diff_print_patch_binary,
769 diff_print_patch_hunk, diff_print_patch_line,
770 &pi);
771
772 if (error) /* make sure error message is set */
773 giterr_set_after_callback_function(error, "git_patch_print");
774 }
775
776 git_buf_free(&temp);
777
778 return error;
779 }
780
781 /* print a git_patch to a git_buf */
782 int git_patch_to_buf(git_buf *out, git_patch *patch)
783 {
784 assert(out && patch);
785 git_buf_sanitize(out);
786 return git_patch_print(patch, git_diff_print_callback__to_buf, out);
787 }