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