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