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