]> git.proxmox.com Git - libgit2.git/blame - src/diff_print.c
New upstream version 0.27.7+dfsg.1
[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 */
eae0bfdc 7
7000f3fa 8#include "common.h"
eae0bfdc 9
7000f3fa 10#include "diff.h"
804d5fe9 11#include "diff_file.h"
8d44f8b7 12#include "patch_generate.h"
f240acce 13#include "fileops.h"
e349ed50
ET
14#include "zstream.h"
15#include "blob.h"
16#include "delta.h"
27e54bcf 17#include "git2/sys/diff.h"
7000f3fa
RB
18
19typedef struct {
10672e3e 20 git_diff_format_t format;
3ff1d123 21 git_diff_line_cb print_cb;
7000f3fa 22 void *payload;
804d5fe9 23
7000f3fa 24 git_buf *buf;
804d5fe9
ET
25 git_diff_line line;
26
27 const char *old_prefix;
28 const char *new_prefix;
10672e3e 29 uint32_t flags;
040ec883 30 int id_strlen;
804d5fe9
ET
31
32 int (*strcomp)(const char *, const char *);
7000f3fa
RB
33} diff_print_info;
34
8147b1af 35static int diff_print_info_init__common(
7000f3fa 36 diff_print_info *pi,
10672e3e 37 git_buf *out,
8147b1af 38 git_repository *repo,
10672e3e
RB
39 git_diff_format_t format,
40 git_diff_line_cb cb,
41 void *payload)
7000f3fa 42{
8147b1af 43 pi->format = format;
7000f3fa 44 pi->print_cb = cb;
8147b1af
ET
45 pi->payload = payload;
46 pi->buf = out;
47
040ec883 48 if (!pi->id_strlen) {
8147b1af 49 if (!repo)
040ec883
ET
50 pi->id_strlen = GIT_ABBREV_DEFAULT;
51 else if (git_repository__cvar(&pi->id_strlen, repo, GIT_CVAR_ABBREV) < 0)
8147b1af
ET
52 return -1;
53 }
7000f3fa 54
040ec883
ET
55 if (pi->id_strlen > GIT_OID_HEXSZ)
56 pi->id_strlen = GIT_OID_HEXSZ;
7000f3fa 57
3b5f7954
RB
58 memset(&pi->line, 0, sizeof(pi->line));
59 pi->line.old_lineno = -1;
60 pi->line.new_lineno = -1;
8147b1af 61 pi->line.num_lines = 1;
3b5f7954 62
7000f3fa
RB
63 return 0;
64}
65
8147b1af
ET
66static 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
8147b1af
ET
78 if (diff) {
79 pi->flags = diff->opts.flags;
040ec883 80 pi->id_strlen = diff->opts.id_abbrev;
804d5fe9
ET
81 pi->old_prefix = diff->opts.old_prefix;
82 pi->new_prefix = diff->opts.new_prefix;
83
84 pi->strcomp = diff->strcomp;
8147b1af
ET
85 }
86
87 return diff_print_info_init__common(pi, out, repo, format, cb, payload);
88}
89
90static 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{
be8479c9
PS
98 assert(patch);
99
8147b1af
ET
100 memset(pi, 0, sizeof(diff_print_info));
101
8147b1af 102 pi->flags = patch->diff_opts.flags;
040ec883 103 pi->id_strlen = patch->diff_opts.id_abbrev;
804d5fe9
ET
104 pi->old_prefix = patch->diff_opts.old_prefix;
105 pi->new_prefix = patch->diff_opts.new_prefix;
8147b1af 106
804d5fe9 107 return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
8147b1af
ET
108}
109
a1683f28 110static char diff_pick_suffix(int mode)
7000f3fa
RB
111{
112 if (S_ISDIR(mode))
113 return '/';
a7fcc44d 114 else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
7000f3fa
RB
115 /* in git, modes are very regular, so we must have 0100755 mode */
116 return '*';
117 else
118 return ' ';
119}
120
121char git_diff_status_char(git_delta_t status)
122{
123 char code;
124
125 switch (status) {
85b7268e
AR
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;
4b3ec53c 133 case GIT_DELTA_TYPECHANGE: code = 'T'; break;
85b7268e
AR
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);
804d5fe9
ET
172 int(*strcomp)(const char *, const char *) = pi->strcomp ?
173 pi->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;
d68cb736 212 int id_abbrev;
7000f3fa
RB
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
10672e3e 218 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
7000f3fa
RB
219 return 0;
220
a1683f28 221 git_buf_clear(out);
7000f3fa 222
d68cb736
ET
223 id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
224 delta->new_file.id_abbrev;
225
040ec883 226 if (pi->id_strlen > id_abbrev) {
d68cb736 227 giterr_set(GITERR_PATCH,
909d5494 228 "the patch input contains %d id characters (cannot print %d)",
040ec883 229 id_abbrev, pi->id_strlen);
d68cb736
ET
230 return -1;
231 }
232
040ec883
ET
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);
7000f3fa
RB
235
236 git_buf_printf(
040ec883 237 out, (pi->id_strlen <= GIT_OID_HEXSZ) ?
12e422a0 238 ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
7000f3fa
RB
239 delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
240
241 if (delta->similarity > 0)
a1683f28 242 git_buf_printf(out, "%03u", delta->similarity);
7000f3fa 243
e4acc3ba 244 if (delta->old_file.path != delta->new_file.path)
7000f3fa 245 git_buf_printf(
a1683f28 246 out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
7000f3fa
RB
247 else
248 git_buf_printf(
a1683f28 249 out, "\t%s\n", delta->old_file.path ?
7000f3fa
RB
250 delta->old_file.path : delta->new_file.path);
251
a1683f28 252 if (git_buf_oom(out))
25e0b157 253 return -1;
7000f3fa 254
3b5f7954
RB
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
25e0b157 259 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
260}
261
e2cdc145
ET
262static 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
197b8966 271static int diff_print_oid_range(
040ec883 272 git_buf *out, const git_diff_delta *delta, int id_strlen)
7000f3fa
RB
273{
274 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
275
d68cb736 276 if (delta->old_file.mode &&
040ec883 277 id_strlen > delta->old_file.id_abbrev) {
d68cb736 278 giterr_set(GITERR_PATCH,
909d5494 279 "the patch input contains %d id characters (cannot print %d)",
040ec883 280 delta->old_file.id_abbrev, id_strlen);
d68cb736
ET
281 return -1;
282 }
283
284 if ((delta->new_file.mode &&
040ec883 285 id_strlen > delta->new_file.id_abbrev)) {
d68cb736 286 giterr_set(GITERR_PATCH,
909d5494 287 "the patch input contains %d id characters (cannot print %d)",
040ec883 288 delta->new_file.id_abbrev, id_strlen);
d68cb736
ET
289 return -1;
290 }
291
040ec883
ET
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);
7000f3fa 294
7000f3fa 295 if (delta->old_file.mode == delta->new_file.mode) {
a1683f28 296 git_buf_printf(out, "index %s..%s %o\n",
7000f3fa
RB
297 start_oid, end_oid, delta->old_file.mode);
298 } else {
e2cdc145 299 if (delta->old_file.mode == 0)
a1683f28 300 git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
e2cdc145 301 else if (delta->new_file.mode == 0)
a1683f28 302 git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
e2cdc145
ET
303 else
304 diff_print_modes(out, delta);
305
a1683f28 306 git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
7000f3fa
RB
307 }
308
25e0b157 309 return git_buf_oom(out) ? -1 : 0;
7000f3fa
RB
310}
311
4ac2d8ac
ET
312static 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
eb1c1707 321static int diff_delta_format_with_paths(
197b8966
RB
322 git_buf *out,
323 const git_diff_delta *delta,
4ac2d8ac
ET
324 const char *template,
325 const char *oldpath,
326 const char *newpath)
7000f3fa 327{
4ac2d8ac 328 if (git_oid_iszero(&delta->old_file.id))
eb1c1707 329 oldpath = "/dev/null";
4ac2d8ac
ET
330
331 if (git_oid_iszero(&delta->new_file.id))
eb1c1707 332 newpath = "/dev/null";
eb1c1707 333
4ac2d8ac 334 return git_buf_printf(out, template, oldpath, newpath);
eb1c1707
RB
335}
336
1a79cd95 337int diff_delta_format_similarity_header(
19e46645
ET
338 git_buf *out,
339 const git_diff_delta *delta)
340{
4ac2d8ac 341 git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
1a79cd95 342 const char *type;
4ac2d8ac
ET
343 int error = 0;
344
19e46645
ET
345 if (delta->similarity > 100) {
346 giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity);
4ac2d8ac
ET
347 error = -1;
348 goto done;
19e46645
ET
349 }
350
1a79cd95
ET
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
4ac2d8ac
ET
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
19e46645
ET
364 git_buf_printf(out,
365 "similarity index %d%%\n"
1a79cd95
ET
366 "%s from %s\n"
367 "%s to %s\n",
19e46645 368 delta->similarity,
1a79cd95
ET
369 type, old_path.ptr,
370 type, new_path.ptr);
19e46645 371
4ac2d8ac
ET
372 if (git_buf_oom(out))
373 error = -1;
374
375done:
376 git_buf_free(&old_path);
377 git_buf_free(&new_path);
378
379 return error;
19e46645
ET
380}
381
1a79cd95
ET
382static 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
eb1c1707
RB
398int git_diff_delta__format_file_header(
399 git_buf *out,
400 const git_diff_delta *delta,
401 const char *oldpfx,
402 const char *newpfx,
040ec883 403 int id_strlen)
eb1c1707 404{
4ac2d8ac 405 git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
1a79cd95 406 bool unchanged = delta_is_unchanged(delta);
4ac2d8ac 407 int error = 0;
72806f4c 408
7000f3fa
RB
409 if (!oldpfx)
410 oldpfx = DIFF_OLD_PREFIX_DEFAULT;
7000f3fa
RB
411 if (!newpfx)
412 newpfx = DIFF_NEW_PREFIX_DEFAULT;
040ec883
ET
413 if (!id_strlen)
414 id_strlen = GIT_ABBREV_DEFAULT;
7000f3fa 415
4ac2d8ac
ET
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
197b8966
RB
422 git_buf_clear(out);
423
4ac2d8ac
ET
424 git_buf_printf(out, "diff --git %s %s\n",
425 old_path.ptr, new_path.ptr);
7000f3fa 426
1a79cd95
ET
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)
4ac2d8ac
ET
430 goto done;
431 }
19e46645 432
e2cdc145 433 if (!unchanged) {
040ec883 434 if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0)
4ac2d8ac 435 goto done;
7000f3fa 436
72806f4c 437 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
4ac2d8ac
ET
438 diff_delta_format_with_paths(out, delta,
439 "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
72806f4c 440 }
7000f3fa 441
e2cdc145
ET
442 if (unchanged && delta->old_file.mode != delta->new_file.mode)
443 diff_print_modes(out, delta);
444
4ac2d8ac
ET
445 if (git_buf_oom(out))
446 error = -1;
447
448done:
449 git_buf_free(&old_path);
450 git_buf_free(&new_path);
451
452 return error;
197b8966 453}
7000f3fa 454
8147b1af
ET
455static 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)
e349ed50 461{
8147b1af
ET
462 const char *typename = type == GIT_DIFF_BINARY_DELTA ?
463 "delta" : "literal";
464 const char *scan, *end;
e349ed50 465
49840056 466 git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
e349ed50
ET
467 pi->line.num_lines++;
468
8147b1af 469 for (scan = data, end = data + datalen; scan < end; ) {
947a58c1
RB
470 size_t chunk_len = end - scan;
471 if (chunk_len > 52)
472 chunk_len = 52;
e349ed50
ET
473
474 if (chunk_len <= 26)
c6320bec 475 git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
e349ed50 476 else
c6320bec 477 git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
e349ed50 478
e003f83a 479 git_buf_encode_base85(pi->buf, scan, chunk_len);
e349ed50
ET
480 git_buf_putc(pi->buf, '\n');
481
8147b1af
ET
482 if (git_buf_oom(pi->buf))
483 return -1;
e349ed50 484
947a58c1 485 scan += chunk_len;
e349ed50
ET
486 pi->line.num_lines++;
487 }
e4b2b919 488 git_buf_putc(pi->buf, '\n');
e349ed50 489
8147b1af
ET
490 return 0;
491}
e349ed50 492
4ac2d8ac
ET
493static int diff_print_patch_file_binary_noshow(
494 diff_print_info *pi, git_diff_delta *delta,
f941f035 495 const char *old_pfx, const char *new_pfx)
4ac2d8ac
ET
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
4ac2d8ac
ET
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
511done:
512 git_buf_free(&old_path);
513 git_buf_free(&new_path);
514
515 return error;
516}
517
e349ed50 518static int diff_print_patch_file_binary(
8147b1af
ET
519 diff_print_info *pi, git_diff_delta *delta,
520 const char *old_pfx, const char *new_pfx,
521 const git_diff_binary *binary)
e349ed50 522{
947a58c1 523 size_t pre_binary_size;
8147b1af 524 int error;
e349ed50 525
f4e3dae7
ET
526 if (delta->status == GIT_DELTA_UNMODIFIED)
527 return 0;
528
adedac5a 529 if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
4ac2d8ac 530 return diff_print_patch_file_binary_noshow(
f941f035 531 pi, delta, old_pfx, new_pfx);
e349ed50 532
947a58c1 533 pre_binary_size = pi->buf->size;
e349ed50
ET
534 git_buf_printf(pi->buf, "GIT binary patch\n");
535 pi->line.num_lines++;
536
8147b1af
ET
537 if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
538 binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
8147b1af
ET
539 (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
540 binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
541
947a58c1
RB
542 if (error == GIT_EBUFS) {
543 giterr_clear();
544 git_buf_truncate(pi->buf, pre_binary_size);
4ac2d8ac
ET
545
546 return diff_print_patch_file_binary_noshow(
f941f035 547 pi, delta, old_pfx, new_pfx);
947a58c1
RB
548 }
549 }
e349ed50
ET
550
551 pi->line.num_lines++;
e349ed50
ET
552 return error;
553}
554
197b8966
RB
555static int diff_print_patch_file(
556 const git_diff_delta *delta, float progress, void *data)
557{
25e0b157 558 int error;
197b8966 559 diff_print_info *pi = data;
eb1c1707 560 const char *oldpfx =
804d5fe9 561 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
eb1c1707 562 const char *newpfx =
804d5fe9 563 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
197b8966 564
8147b1af
ET
565 bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
566 (pi->flags & GIT_DIFF_FORCE_BINARY);
e349ed50 567 bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
adedac5a
ET
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;
e349ed50 573
197b8966 574 GIT_UNUSED(progress);
7000f3fa 575
197b8966
RB
576 if (S_ISDIR(delta->new_file.mode) ||
577 delta->status == GIT_DELTA_UNMODIFIED ||
578 delta->status == GIT_DELTA_IGNORED ||
61bef72d 579 delta->status == GIT_DELTA_UNREADABLE ||
197b8966 580 (delta->status == GIT_DELTA_UNTRACKED &&
10672e3e 581 (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
7000f3fa
RB
582 return 0;
583
25e0b157 584 if ((error = git_diff_delta__format_file_header(
040ec883 585 pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0)
25e0b157 586 return error;
7000f3fa 587
3b5f7954
RB
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
8147b1af
ET
592 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
593}
eb1c1707 594
8147b1af
ET
595static 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 =
804d5fe9 602 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
8147b1af 603 const char *new_pfx =
804d5fe9 604 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
8147b1af 605 int error;
eb1c1707
RB
606
607 git_buf_clear(pi->buf);
608
8147b1af
ET
609 if ((error = diff_print_patch_file_binary(
610 pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
25e0b157 611 return error;
eb1c1707 612
8147b1af
ET
613 pi->line.origin = GIT_DIFF_LINE_BINARY;
614 pi->line.content = git_buf_cstr(pi->buf);
3b5f7954 615 pi->line.content_len = git_buf_len(pi->buf);
3b5f7954 616
25e0b157 617 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
618}
619
a1683f28 620static int diff_print_patch_hunk(
7000f3fa 621 const git_diff_delta *d,
3b5f7954 622 const git_diff_hunk *h,
7000f3fa
RB
623 void *data)
624{
625 diff_print_info *pi = data;
626
627 if (S_ISDIR(d->new_file.mode))
628 return 0;
629
3b5f7954
RB
630 pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
631 pi->line.content = h->header;
632 pi->line.content_len = h->header_len;
7000f3fa 633
25e0b157 634 return pi->print_cb(d, h, &pi->line, pi->payload);
7000f3fa
RB
635}
636
a1683f28 637static int diff_print_patch_line(
7000f3fa 638 const git_diff_delta *delta,
3b5f7954
RB
639 const git_diff_hunk *hunk,
640 const git_diff_line *line,
7000f3fa
RB
641 void *data)
642{
643 diff_print_info *pi = data;
644
645 if (S_ISDIR(delta->new_file.mode))
646 return 0;
647
25e0b157 648 return pi->print_cb(delta, hunk, line, pi->payload);
7000f3fa
RB
649}
650
10672e3e
RB
651/* print a git_diff to an output callback */
652int git_diff_print(
3ff1d123 653 git_diff *diff,
10672e3e 654 git_diff_format_t format,
3ff1d123 655 git_diff_line_cb print_cb,
7000f3fa
RB
656 void *payload)
657{
658 int error;
659 git_buf buf = GIT_BUF_INIT;
660 diff_print_info pi;
10672e3e 661 git_diff_file_cb print_file = NULL;
8147b1af 662 git_diff_binary_cb print_binary = NULL;
10672e3e
RB
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;
8147b1af 669 print_binary = diff_print_patch_binary;
10672e3e
RB
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:
909d5494 686 giterr_set(GITERR_INVALID, "unknown diff output format (%d)", format);
10672e3e
RB
687 return -1;
688 }
7000f3fa 689
8147b1af
ET
690 if (!(error = diff_print_info_init_fromdiff(
691 &pi, &buf, diff, format, print_cb, payload))) {
7000f3fa 692 error = git_diff_foreach(
8147b1af 693 diff, print_file, print_binary, print_hunk, print_line, &pi);
7000f3fa 694
25e0b157 695 if (error) /* make sure error message is set */
26c1cb91 696 giterr_set_after_callback_function(error, "git_diff_print");
96869a4e
RB
697 }
698
7000f3fa
RB
699 git_buf_free(&buf);
700
701 return error;
702}
703
27e54bcf 704int git_diff_print_callback__to_buf(
a1683f28 705 const git_diff_delta *delta,
3b5f7954
RB
706 const git_diff_hunk *hunk,
707 const git_diff_line *line,
a1683f28
RB
708 void *payload)
709{
710 git_buf *output = payload;
3b5f7954
RB
711 GIT_UNUSED(delta); GIT_UNUSED(hunk);
712
27e54bcf 713 if (!output) {
909d5494 714 giterr_set(GITERR_INVALID, "buffer pointer must be provided");
27e54bcf
RB
715 return -1;
716 }
717
3b5f7954
RB
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);
a1683f28
RB
724}
725
27e54bcf
RB
726int 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
72827490
ET
744/* print a git_diff to a git_buf */
745int 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
804d5fe9
ET
753/* print a git_patch to an output callback */
754int 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
450e8e9e 784/* print a git_patch to a git_buf */
1e4976cb 785int git_patch_to_buf(git_buf *out, git_patch *patch)
450e8e9e 786{
1e4976cb
RB
787 assert(out && patch);
788 git_buf_sanitize(out);
27e54bcf 789 return git_patch_print(patch, git_diff_print_callback__to_buf, out);
450e8e9e 790}