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