]> git.proxmox.com Git - libgit2.git/blame - src/diff_print.c
binary diff: test index->workdir binary diffs
[libgit2.git] / src / diff_print.c
CommitLineData
7000f3fa
RB
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"
114f5a6c 9#include "diff_patch.h"
f240acce 10#include "fileops.h"
e349ed50
ET
11#include "zstream.h"
12#include "blob.h"
13#include "delta.h"
27e54bcf 14#include "git2/sys/diff.h"
7000f3fa
RB
15
16typedef struct {
3ff1d123 17 git_diff *diff;
10672e3e 18 git_diff_format_t format;
3ff1d123 19 git_diff_line_cb print_cb;
7000f3fa
RB
20 void *payload;
21 git_buf *buf;
10672e3e 22 uint32_t flags;
7000f3fa 23 int oid_strlen;
3b5f7954 24 git_diff_line line;
7000f3fa
RB
25} diff_print_info;
26
27static int diff_print_info_init(
28 diff_print_info *pi,
10672e3e
RB
29 git_buf *out,
30 git_diff *diff,
31 git_diff_format_t format,
32 git_diff_line_cb cb,
33 void *payload)
7000f3fa 34{
7000f3fa 35 pi->diff = diff;
10672e3e 36 pi->format = format;
7000f3fa
RB
37 pi->print_cb = cb;
38 pi->payload = payload;
39 pi->buf = out;
40
10672e3e
RB
41 if (diff)
42 pi->flags = diff->opts.flags;
e349ed50
ET
43 else
44 pi->flags = 0;
10672e3e 45
86bfc3e1
CMN
46 if (diff && diff->opts.id_abbrev != 0)
47 pi->oid_strlen = diff->opts.id_abbrev;
10672e3e 48 else if (!diff || !diff->repo)
74ded024
RB
49 pi->oid_strlen = GIT_ABBREV_DEFAULT;
50 else if (git_repository__cvar(
51 &pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
7000f3fa
RB
52 return -1;
53
54 pi->oid_strlen += 1; /* for NUL byte */
55
56 if (pi->oid_strlen < 2)
57 pi->oid_strlen = 2;
58 else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
59 pi->oid_strlen = GIT_OID_HEXSZ + 1;
60
3b5f7954
RB
61 memset(&pi->line, 0, sizeof(pi->line));
62 pi->line.old_lineno = -1;
63 pi->line.new_lineno = -1;
64 pi->line.num_lines = 1;
65
7000f3fa
RB
66 return 0;
67}
68
a1683f28 69static char diff_pick_suffix(int mode)
7000f3fa
RB
70{
71 if (S_ISDIR(mode))
72 return '/';
a7fcc44d 73 else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
7000f3fa
RB
74 /* in git, modes are very regular, so we must have 0100755 mode */
75 return '*';
76 else
77 return ' ';
78}
79
80char git_diff_status_char(git_delta_t status)
81{
82 char code;
83
84 switch (status) {
85b7268e
AR
85 case GIT_DELTA_ADDED: code = 'A'; break;
86 case GIT_DELTA_DELETED: code = 'D'; break;
87 case GIT_DELTA_MODIFIED: code = 'M'; break;
88 case GIT_DELTA_RENAMED: code = 'R'; break;
89 case GIT_DELTA_COPIED: code = 'C'; break;
90 case GIT_DELTA_IGNORED: code = 'I'; break;
91 case GIT_DELTA_UNTRACKED: code = '?'; break;
92 case GIT_DELTA_UNREADABLE: code = 'X'; break;
93 default: code = ' '; break;
7000f3fa
RB
94 }
95
96 return code;
97}
98
10672e3e
RB
99static int diff_print_one_name_only(
100 const git_diff_delta *delta, float progress, void *data)
101{
102 diff_print_info *pi = data;
103 git_buf *out = pi->buf;
104
105 GIT_UNUSED(progress);
106
107 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
108 delta->status == GIT_DELTA_UNMODIFIED)
109 return 0;
110
111 git_buf_clear(out);
25e0b157
RB
112 git_buf_puts(out, delta->new_file.path);
113 git_buf_putc(out, '\n');
114 if (git_buf_oom(out))
115 return -1;
10672e3e 116
3b5f7954
RB
117 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
118 pi->line.content = git_buf_cstr(out);
119 pi->line.content_len = git_buf_len(out);
120
25e0b157 121 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
10672e3e
RB
122}
123
124static int diff_print_one_name_status(
7000f3fa
RB
125 const git_diff_delta *delta, float progress, void *data)
126{
127 diff_print_info *pi = data;
a1683f28 128 git_buf *out = pi->buf;
7000f3fa 129 char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
74ded024
RB
130 int (*strcomp)(const char *, const char *) =
131 pi->diff ? pi->diff->strcomp : git__strcmp;
7000f3fa
RB
132
133 GIT_UNUSED(progress);
134
10672e3e 135 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
7000f3fa
RB
136 return 0;
137
a1683f28
RB
138 old_suffix = diff_pick_suffix(delta->old_file.mode);
139 new_suffix = diff_pick_suffix(delta->new_file.mode);
7000f3fa 140
a1683f28 141 git_buf_clear(out);
7000f3fa
RB
142
143 if (delta->old_file.path != delta->new_file.path &&
74ded024 144 strcomp(delta->old_file.path,delta->new_file.path) != 0)
df40f398 145 git_buf_printf(out, "%c\t%s%c %s%c\n", code,
7000f3fa
RB
146 delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
147 else if (delta->old_file.mode != delta->new_file.mode &&
148 delta->old_file.mode != 0 && delta->new_file.mode != 0)
df40f398
RB
149 git_buf_printf(out, "%c\t%s%c %s%c\n", code,
150 delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
7000f3fa 151 else if (old_suffix != ' ')
a1683f28 152 git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
7000f3fa 153 else
a1683f28 154 git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path);
a1683f28 155 if (git_buf_oom(out))
25e0b157 156 return -1;
7000f3fa 157
3b5f7954
RB
158 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
159 pi->line.content = git_buf_cstr(out);
160 pi->line.content_len = git_buf_len(out);
161
25e0b157 162 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
163}
164
a1683f28 165static int diff_print_one_raw(
7000f3fa
RB
166 const git_diff_delta *delta, float progress, void *data)
167{
168 diff_print_info *pi = data;
a1683f28 169 git_buf *out = pi->buf;
7000f3fa
RB
170 char code = git_diff_status_char(delta->status);
171 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
172
173 GIT_UNUSED(progress);
174
10672e3e 175 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
7000f3fa
RB
176 return 0;
177
a1683f28 178 git_buf_clear(out);
7000f3fa 179
9950bb4e
CMN
180 git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id);
181 git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id);
7000f3fa
RB
182
183 git_buf_printf(
12e422a0
RB
184 out, (pi->oid_strlen <= GIT_OID_HEXSZ) ?
185 ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
7000f3fa
RB
186 delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
187
188 if (delta->similarity > 0)
a1683f28 189 git_buf_printf(out, "%03u", delta->similarity);
7000f3fa 190
e4acc3ba 191 if (delta->old_file.path != delta->new_file.path)
7000f3fa 192 git_buf_printf(
a1683f28 193 out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
7000f3fa
RB
194 else
195 git_buf_printf(
a1683f28 196 out, "\t%s\n", delta->old_file.path ?
7000f3fa
RB
197 delta->old_file.path : delta->new_file.path);
198
a1683f28 199 if (git_buf_oom(out))
25e0b157 200 return -1;
7000f3fa 201
3b5f7954
RB
202 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
203 pi->line.content = git_buf_cstr(out);
204 pi->line.content_len = git_buf_len(out);
205
25e0b157 206 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
207}
208
197b8966
RB
209static int diff_print_oid_range(
210 git_buf *out, const git_diff_delta *delta, int oid_strlen)
7000f3fa
RB
211{
212 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
213
9950bb4e
CMN
214 git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id);
215 git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id);
7000f3fa
RB
216
217 /* TODO: Match git diff more closely */
218 if (delta->old_file.mode == delta->new_file.mode) {
a1683f28 219 git_buf_printf(out, "index %s..%s %o\n",
7000f3fa
RB
220 start_oid, end_oid, delta->old_file.mode);
221 } else {
222 if (delta->old_file.mode == 0) {
a1683f28 223 git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
7000f3fa 224 } else if (delta->new_file.mode == 0) {
a1683f28 225 git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
7000f3fa 226 } else {
a1683f28
RB
227 git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
228 git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
7000f3fa 229 }
a1683f28 230 git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
7000f3fa
RB
231 }
232
25e0b157 233 return git_buf_oom(out) ? -1 : 0;
7000f3fa
RB
234}
235
eb1c1707 236static int diff_delta_format_with_paths(
197b8966
RB
237 git_buf *out,
238 const git_diff_delta *delta,
239 const char *oldpfx,
240 const char *newpfx,
eb1c1707 241 const char *template)
7000f3fa 242{
7000f3fa 243 const char *oldpath = delta->old_file.path;
7000f3fa 244 const char *newpath = delta->new_file.path;
7000f3fa 245
9950bb4e 246 if (git_oid_iszero(&delta->old_file.id)) {
eb1c1707
RB
247 oldpfx = "";
248 oldpath = "/dev/null";
249 }
9950bb4e 250 if (git_oid_iszero(&delta->new_file.id)) {
eb1c1707
RB
251 newpfx = "";
252 newpath = "/dev/null";
253 }
254
255 return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
256}
257
258int git_diff_delta__format_file_header(
259 git_buf *out,
260 const git_diff_delta *delta,
261 const char *oldpfx,
262 const char *newpfx,
263 int oid_strlen)
264{
7000f3fa
RB
265 if (!oldpfx)
266 oldpfx = DIFF_OLD_PREFIX_DEFAULT;
7000f3fa
RB
267 if (!newpfx)
268 newpfx = DIFF_NEW_PREFIX_DEFAULT;
197b8966
RB
269 if (!oid_strlen)
270 oid_strlen = GIT_ABBREV_DEFAULT + 1;
7000f3fa 271
197b8966
RB
272 git_buf_clear(out);
273
274 git_buf_printf(out, "diff --git %s%s %s%s\n",
eb1c1707 275 oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
7000f3fa 276
25e0b157 277 GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen));
7000f3fa 278
eb1c1707
RB
279 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
280 diff_delta_format_with_paths(
281 out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
7000f3fa 282
197b8966
RB
283 return git_buf_oom(out) ? -1 : 0;
284}
7000f3fa 285
e349ed50
ET
286static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
287{
288 git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL;
289 const void *old_data, *new_data;
947a58c1
RB
290 git_off_t old_data_len, new_data_len;
291 unsigned long delta_data_len, inflated_len;
e349ed50 292 const char *out_type = "literal";
947a58c1 293 char *scan, *end;
e349ed50
ET
294 int error;
295
296 old_data = old ? git_blob_rawcontent(old) : NULL;
297 new_data = new ? git_blob_rawcontent(new) : NULL;
298
947a58c1
RB
299 old_data_len = old ? git_blob_rawsize(old) : 0;
300 new_data_len = new ? git_blob_rawsize(new) : 0;
c6320bec 301
bc81220d
RB
302 /* The git_delta function accepts unsigned long only */
303 if (!git__is_ulong(old_data_len) || !git__is_ulong(new_data_len))
304 return GIT_EBUFS;
e349ed50
ET
305
306 out = &deflate;
947a58c1 307 inflated_len = (unsigned long)new_data_len;
e349ed50
ET
308
309 if ((error = git_zstream_deflatebuf(
bc81220d 310 out, new_data, (size_t)new_data_len)) < 0)
e349ed50
ET
311 goto done;
312
c6320bec 313 /* The git_delta function accepts unsigned long only */
947a58c1
RB
314 if (!git__is_ulong((git_off_t)deflate.size)) {
315 error = GIT_EBUFS;
c6320bec
PK
316 goto done;
317 }
e349ed50 318
e349ed50 319 if (old && new) {
947a58c1
RB
320 void *delta_data = git_delta(
321 old_data, (unsigned long)old_data_len,
322 new_data, (unsigned long)new_data_len,
323 &delta_data_len, (unsigned long)deflate.size);
e349ed50
ET
324
325 if (delta_data) {
947a58c1
RB
326 error = git_zstream_deflatebuf(
327 &delta, delta_data, (size_t)delta_data_len);
328
329 git__free(delta_data);
e349ed50
ET
330
331 if (error < 0)
332 goto done;
333
334 if (delta.size < deflate.size) {
335 out = &delta;
336 out_type = "delta";
337 inflated_len = delta_data_len;
338 }
339 }
340 }
341
4c9ffdff 342 git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len);
e349ed50
ET
343 pi->line.num_lines++;
344
947a58c1
RB
345 for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) {
346 size_t chunk_len = end - scan;
347 if (chunk_len > 52)
348 chunk_len = 52;
e349ed50
ET
349
350 if (chunk_len <= 26)
c6320bec 351 git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
e349ed50 352 else
c6320bec 353 git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
e349ed50 354
e003f83a 355 git_buf_encode_base85(pi->buf, scan, chunk_len);
e349ed50
ET
356 git_buf_putc(pi->buf, '\n');
357
358 if (git_buf_oom(pi->buf)) {
359 error = -1;
360 goto done;
361 }
362
947a58c1 363 scan += chunk_len;
e349ed50
ET
364 pi->line.num_lines++;
365 }
366
367done:
368 git_buf_free(&deflate);
369 git_buf_free(&delta);
370
371 return error;
372}
373
374/* git diff --binary 8d7523f~2 8d7523f~1 */
375static int diff_print_patch_file_binary(
376 diff_print_info *pi, const git_diff_delta *delta,
377 const char *oldpfx, const char *newpfx)
378{
379 git_blob *old = NULL, *new = NULL;
380 const git_oid *old_id, *new_id;
381 int error;
947a58c1 382 size_t pre_binary_size;
e349ed50 383
947a58c1
RB
384 if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
385 goto noshow;
e349ed50 386
947a58c1 387 pre_binary_size = pi->buf->size;
e349ed50
ET
388 git_buf_printf(pi->buf, "GIT binary patch\n");
389 pi->line.num_lines++;
390
391 old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL;
392 new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL;
393
947a58c1
RB
394 if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0)
395 goto done;
396 if (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0)
397 goto done;
398
399 if ((error = print_binary_hunk(pi, old, new)) < 0 ||
e349ed50
ET
400 (error = git_buf_putc(pi->buf, '\n')) < 0 ||
401 (error = print_binary_hunk(pi, new, old)) < 0)
947a58c1
RB
402 {
403 if (error == GIT_EBUFS) {
404 giterr_clear();
405 git_buf_truncate(pi->buf, pre_binary_size);
406 goto noshow;
407 }
408 }
e349ed50
ET
409
410 pi->line.num_lines++;
411
412done:
413 git_blob_free(old);
414 git_blob_free(new);
415
416 return error;
947a58c1
RB
417
418noshow:
419 pi->line.num_lines = 1;
420 return diff_delta_format_with_paths(
421 pi->buf, delta, oldpfx, newpfx,
422 "Binary files %s%s and %s%s differ\n");
e349ed50
ET
423}
424
197b8966
RB
425static int diff_print_patch_file(
426 const git_diff_delta *delta, float progress, void *data)
427{
25e0b157 428 int error;
197b8966 429 diff_print_info *pi = data;
eb1c1707
RB
430 const char *oldpfx =
431 pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
432 const char *newpfx =
433 pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
197b8966 434
e349ed50
ET
435 bool binary = !!(delta->flags & GIT_DIFF_FLAG_BINARY);
436 bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
437 int oid_strlen = binary && show_binary ?
438 GIT_OID_HEXSZ + 1 : pi->oid_strlen;
439
197b8966 440 GIT_UNUSED(progress);
7000f3fa 441
197b8966
RB
442 if (S_ISDIR(delta->new_file.mode) ||
443 delta->status == GIT_DELTA_UNMODIFIED ||
444 delta->status == GIT_DELTA_IGNORED ||
61bef72d 445 delta->status == GIT_DELTA_UNREADABLE ||
197b8966 446 (delta->status == GIT_DELTA_UNTRACKED &&
10672e3e 447 (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
7000f3fa
RB
448 return 0;
449
25e0b157 450 if ((error = git_diff_delta__format_file_header(
e349ed50 451 pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0)
25e0b157 452 return error;
7000f3fa 453
3b5f7954
RB
454 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
455 pi->line.content = git_buf_cstr(pi->buf);
456 pi->line.content_len = git_buf_len(pi->buf);
457
25e0b157
RB
458 if ((error = pi->print_cb(delta, NULL, &pi->line, pi->payload)) != 0)
459 return error;
eb1c1707 460
e349ed50 461 if (!binary)
eb1c1707
RB
462 return 0;
463
464 git_buf_clear(pi->buf);
465
e349ed50 466 if ((error = diff_print_patch_file_binary(pi, delta, oldpfx, newpfx)) < 0)
25e0b157 467 return error;
eb1c1707 468
3b5f7954
RB
469 pi->line.origin = GIT_DIFF_LINE_BINARY;
470 pi->line.content = git_buf_cstr(pi->buf);
471 pi->line.content_len = git_buf_len(pi->buf);
3b5f7954 472
25e0b157 473 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
474}
475
a1683f28 476static int diff_print_patch_hunk(
7000f3fa 477 const git_diff_delta *d,
3b5f7954 478 const git_diff_hunk *h,
7000f3fa
RB
479 void *data)
480{
481 diff_print_info *pi = data;
482
483 if (S_ISDIR(d->new_file.mode))
484 return 0;
485
3b5f7954
RB
486 pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
487 pi->line.content = h->header;
488 pi->line.content_len = h->header_len;
7000f3fa 489
25e0b157 490 return pi->print_cb(d, h, &pi->line, pi->payload);
7000f3fa
RB
491}
492
a1683f28 493static int diff_print_patch_line(
7000f3fa 494 const git_diff_delta *delta,
3b5f7954
RB
495 const git_diff_hunk *hunk,
496 const git_diff_line *line,
7000f3fa
RB
497 void *data)
498{
499 diff_print_info *pi = data;
500
501 if (S_ISDIR(delta->new_file.mode))
502 return 0;
503
25e0b157 504 return pi->print_cb(delta, hunk, line, pi->payload);
7000f3fa
RB
505}
506
10672e3e
RB
507/* print a git_diff to an output callback */
508int git_diff_print(
3ff1d123 509 git_diff *diff,
10672e3e 510 git_diff_format_t format,
3ff1d123 511 git_diff_line_cb print_cb,
7000f3fa
RB
512 void *payload)
513{
514 int error;
515 git_buf buf = GIT_BUF_INIT;
516 diff_print_info pi;
10672e3e
RB
517 git_diff_file_cb print_file = NULL;
518 git_diff_hunk_cb print_hunk = NULL;
519 git_diff_line_cb print_line = NULL;
520
521 switch (format) {
522 case GIT_DIFF_FORMAT_PATCH:
523 print_file = diff_print_patch_file;
524 print_hunk = diff_print_patch_hunk;
525 print_line = diff_print_patch_line;
526 break;
527 case GIT_DIFF_FORMAT_PATCH_HEADER:
528 print_file = diff_print_patch_file;
529 break;
530 case GIT_DIFF_FORMAT_RAW:
531 print_file = diff_print_one_raw;
532 break;
533 case GIT_DIFF_FORMAT_NAME_ONLY:
534 print_file = diff_print_one_name_only;
535 break;
536 case GIT_DIFF_FORMAT_NAME_STATUS:
537 print_file = diff_print_one_name_status;
538 break;
539 default:
540 giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
541 return -1;
542 }
7000f3fa 543
10672e3e
RB
544 if (!(error = diff_print_info_init(
545 &pi, &buf, diff, format, print_cb, payload)))
96869a4e 546 {
7000f3fa 547 error = git_diff_foreach(
10672e3e 548 diff, print_file, print_hunk, print_line, &pi);
7000f3fa 549
25e0b157 550 if (error) /* make sure error message is set */
26c1cb91 551 giterr_set_after_callback_function(error, "git_diff_print");
96869a4e
RB
552 }
553
7000f3fa
RB
554 git_buf_free(&buf);
555
556 return error;
557}
558
3ff1d123
RB
559/* print a git_patch to an output callback */
560int git_patch_print(
561 git_patch *patch,
562 git_diff_line_cb print_cb,
7000f3fa
RB
563 void *payload)
564{
565 int error;
566 git_buf temp = GIT_BUF_INIT;
567 diff_print_info pi;
7000f3fa
RB
568
569 assert(patch && print_cb);
570
571 if (!(error = diff_print_info_init(
10672e3e
RB
572 &pi, &temp, git_patch__diff(patch),
573 GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
96869a4e 574 {
3ff1d123 575 error = git_patch__invoke_callbacks(
a1683f28
RB
576 patch, diff_print_patch_file, diff_print_patch_hunk,
577 diff_print_patch_line, &pi);
7000f3fa 578
25e0b157 579 if (error) /* make sure error message is set */
26c1cb91 580 giterr_set_after_callback_function(error, "git_patch_print");
96869a4e
RB
581 }
582
7000f3fa
RB
583 git_buf_free(&temp);
584
585 return error;
586}
587
27e54bcf 588int git_diff_print_callback__to_buf(
a1683f28 589 const git_diff_delta *delta,
3b5f7954
RB
590 const git_diff_hunk *hunk,
591 const git_diff_line *line,
a1683f28
RB
592 void *payload)
593{
594 git_buf *output = payload;
3b5f7954
RB
595 GIT_UNUSED(delta); GIT_UNUSED(hunk);
596
27e54bcf
RB
597 if (!output) {
598 giterr_set(GITERR_INVALID, "Buffer pointer must be provided");
599 return -1;
600 }
601
3b5f7954
RB
602 if (line->origin == GIT_DIFF_LINE_ADDITION ||
603 line->origin == GIT_DIFF_LINE_DELETION ||
604 line->origin == GIT_DIFF_LINE_CONTEXT)
605 git_buf_putc(output, line->origin);
606
607 return git_buf_put(output, line->content, line->content_len);
a1683f28
RB
608}
609
27e54bcf
RB
610int git_diff_print_callback__to_file_handle(
611 const git_diff_delta *delta,
612 const git_diff_hunk *hunk,
613 const git_diff_line *line,
614 void *payload)
615{
616 FILE *fp = payload ? payload : stdout;
617
618 GIT_UNUSED(delta); GIT_UNUSED(hunk);
619
620 if (line->origin == GIT_DIFF_LINE_CONTEXT ||
621 line->origin == GIT_DIFF_LINE_ADDITION ||
622 line->origin == GIT_DIFF_LINE_DELETION)
623 fputc(line->origin, fp);
624 fwrite(line->content, 1, line->content_len, fp);
625 return 0;
626}
627
450e8e9e 628/* print a git_patch to a git_buf */
1e4976cb 629int git_patch_to_buf(git_buf *out, git_patch *patch)
450e8e9e 630{
1e4976cb
RB
631 assert(out && patch);
632 git_buf_sanitize(out);
27e54bcf 633 return git_patch_print(patch, git_diff_print_callback__to_buf, out);
450e8e9e 634}