]> git.proxmox.com Git - libgit2.git/blame - src/diff_print.c
Upload to experimental
[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"
22a2d3d5 13#include "futils.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 50 pi->id_strlen = GIT_ABBREV_DEFAULT;
22a2d3d5 51 else if (git_repository__configmap_lookup(&pi->id_strlen, repo, GIT_CONFIGMAP_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{
c25aa7cd 98 GIT_ASSERT_ARG(patch);
be8479c9 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) {
ac3d33df 227 git_error_set(GIT_ERROR_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(
22a2d3d5
UG
272 git_buf *out, const git_diff_delta *delta, int id_strlen,
273 bool print_index)
7000f3fa
RB
274{
275 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
276
d68cb736 277 if (delta->old_file.mode &&
040ec883 278 id_strlen > delta->old_file.id_abbrev) {
ac3d33df 279 git_error_set(GIT_ERROR_PATCH,
909d5494 280 "the patch input contains %d id characters (cannot print %d)",
040ec883 281 delta->old_file.id_abbrev, id_strlen);
d68cb736
ET
282 return -1;
283 }
284
285 if ((delta->new_file.mode &&
040ec883 286 id_strlen > delta->new_file.id_abbrev)) {
ac3d33df 287 git_error_set(GIT_ERROR_PATCH,
909d5494 288 "the patch input contains %d id characters (cannot print %d)",
040ec883 289 delta->new_file.id_abbrev, id_strlen);
d68cb736
ET
290 return -1;
291 }
292
040ec883
ET
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);
7000f3fa 295
7000f3fa 296 if (delta->old_file.mode == delta->new_file.mode) {
22a2d3d5
UG
297 if (print_index)
298 git_buf_printf(out, "index %s..%s %o\n",
299 start_oid, end_oid, delta->old_file.mode);
7000f3fa 300 } else {
e2cdc145 301 if (delta->old_file.mode == 0)
a1683f28 302 git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
e2cdc145 303 else if (delta->new_file.mode == 0)
a1683f28 304 git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
e2cdc145
ET
305 else
306 diff_print_modes(out, delta);
307
22a2d3d5
UG
308 if (print_index)
309 git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
7000f3fa
RB
310 }
311
25e0b157 312 return git_buf_oom(out) ? -1 : 0;
7000f3fa
RB
313}
314
4ac2d8ac
ET
315static 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
eb1c1707 324static int diff_delta_format_with_paths(
197b8966
RB
325 git_buf *out,
326 const git_diff_delta *delta,
4ac2d8ac
ET
327 const char *template,
328 const char *oldpath,
329 const char *newpath)
7000f3fa 330{
22a2d3d5 331 if (git_oid_is_zero(&delta->old_file.id))
eb1c1707 332 oldpath = "/dev/null";
4ac2d8ac 333
22a2d3d5 334 if (git_oid_is_zero(&delta->new_file.id))
eb1c1707 335 newpath = "/dev/null";
eb1c1707 336
4ac2d8ac 337 return git_buf_printf(out, template, oldpath, newpath);
eb1c1707
RB
338}
339
22a2d3d5 340static int diff_delta_format_similarity_header(
19e46645
ET
341 git_buf *out,
342 const git_diff_delta *delta)
343{
4ac2d8ac 344 git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
1a79cd95 345 const char *type;
4ac2d8ac
ET
346 int error = 0;
347
19e46645 348 if (delta->similarity > 100) {
ac3d33df 349 git_error_set(GIT_ERROR_PATCH, "invalid similarity %d", delta->similarity);
4ac2d8ac
ET
350 error = -1;
351 goto done;
19e46645
ET
352 }
353
22a2d3d5 354 GIT_ASSERT(delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED);
1a79cd95
ET
355 if (delta->status == GIT_DELTA_RENAMED)
356 type = "rename";
1a79cd95 357 else
22a2d3d5 358 type = "copy";
1a79cd95 359
4ac2d8ac 360 if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 ||
22a2d3d5
UG
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)
4ac2d8ac
ET
364 goto done;
365
19e46645
ET
366 git_buf_printf(out,
367 "similarity index %d%%\n"
1a79cd95
ET
368 "%s from %s\n"
369 "%s to %s\n",
19e46645 370 delta->similarity,
1a79cd95
ET
371 type, old_path.ptr,
372 type, new_path.ptr);
19e46645 373
4ac2d8ac
ET
374 if (git_buf_oom(out))
375 error = -1;
376
377done:
ac3d33df
JK
378 git_buf_dispose(&old_path);
379 git_buf_dispose(&new_path);
4ac2d8ac
ET
380
381 return error;
19e46645
ET
382}
383
1a79cd95
ET
384static bool delta_is_unchanged(const git_diff_delta *delta)
385{
22a2d3d5
UG
386 if (git_oid_is_zero(&delta->old_file.id) &&
387 git_oid_is_zero(&delta->new_file.id))
1a79cd95
ET
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
eb1c1707
RB
400int git_diff_delta__format_file_header(
401 git_buf *out,
402 const git_diff_delta *delta,
403 const char *oldpfx,
404 const char *newpfx,
22a2d3d5
UG
405 int id_strlen,
406 bool print_index)
eb1c1707 407{
4ac2d8ac 408 git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
1a79cd95 409 bool unchanged = delta_is_unchanged(delta);
4ac2d8ac 410 int error = 0;
72806f4c 411
7000f3fa
RB
412 if (!oldpfx)
413 oldpfx = DIFF_OLD_PREFIX_DEFAULT;
7000f3fa
RB
414 if (!newpfx)
415 newpfx = DIFF_NEW_PREFIX_DEFAULT;
040ec883
ET
416 if (!id_strlen)
417 id_strlen = GIT_ABBREV_DEFAULT;
7000f3fa 418
4ac2d8ac
ET
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
197b8966
RB
425 git_buf_clear(out);
426
4ac2d8ac
ET
427 git_buf_printf(out, "diff --git %s %s\n",
428 old_path.ptr, new_path.ptr);
7000f3fa 429
22a2d3d5
UG
430 if (unchanged && delta->old_file.mode != delta->new_file.mode)
431 diff_print_modes(out, delta);
432
1a79cd95 433 if (delta->status == GIT_DELTA_RENAMED ||
22a2d3d5 434 (delta->status == GIT_DELTA_COPIED && unchanged)) {
1a79cd95 435 if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
4ac2d8ac
ET
436 goto done;
437 }
19e46645 438
e2cdc145 439 if (!unchanged) {
22a2d3d5
UG
440 if ((error = diff_print_oid_range(out, delta,
441 id_strlen, print_index)) < 0)
4ac2d8ac 442 goto done;
7000f3fa 443
72806f4c 444 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
4ac2d8ac
ET
445 diff_delta_format_with_paths(out, delta,
446 "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
72806f4c 447 }
7000f3fa 448
4ac2d8ac
ET
449 if (git_buf_oom(out))
450 error = -1;
451
452done:
ac3d33df
JK
453 git_buf_dispose(&old_path);
454 git_buf_dispose(&new_path);
4ac2d8ac
ET
455
456 return error;
197b8966 457}
7000f3fa 458
8147b1af
ET
459static 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)
e349ed50 465{
8147b1af
ET
466 const char *typename = type == GIT_DIFF_BINARY_DELTA ?
467 "delta" : "literal";
468 const char *scan, *end;
e349ed50 469
49840056 470 git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
e349ed50
ET
471 pi->line.num_lines++;
472
8147b1af 473 for (scan = data, end = data + datalen; scan < end; ) {
947a58c1
RB
474 size_t chunk_len = end - scan;
475 if (chunk_len > 52)
476 chunk_len = 52;
e349ed50
ET
477
478 if (chunk_len <= 26)
c6320bec 479 git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
e349ed50 480 else
c6320bec 481 git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
e349ed50 482
e003f83a 483 git_buf_encode_base85(pi->buf, scan, chunk_len);
e349ed50
ET
484 git_buf_putc(pi->buf, '\n');
485
8147b1af
ET
486 if (git_buf_oom(pi->buf))
487 return -1;
e349ed50 488
947a58c1 489 scan += chunk_len;
e349ed50
ET
490 pi->line.num_lines++;
491 }
e4b2b919 492 git_buf_putc(pi->buf, '\n');
e349ed50 493
22a2d3d5
UG
494 if (git_buf_oom(pi->buf))
495 return -1;
496
8147b1af
ET
497 return 0;
498}
e349ed50 499
4ac2d8ac
ET
500static int diff_print_patch_file_binary_noshow(
501 diff_print_info *pi, git_diff_delta *delta,
f941f035 502 const char *old_pfx, const char *new_pfx)
4ac2d8ac
ET
503{
504 git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
505 int error;
506
22a2d3d5
UG
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)
4ac2d8ac
ET
511 goto done;
512
4ac2d8ac 513 pi->line.num_lines = 1;
4ac2d8ac
ET
514
515done:
ac3d33df
JK
516 git_buf_dispose(&old_path);
517 git_buf_dispose(&new_path);
4ac2d8ac
ET
518 return error;
519}
520
e349ed50 521static int diff_print_patch_file_binary(
8147b1af
ET
522 diff_print_info *pi, git_diff_delta *delta,
523 const char *old_pfx, const char *new_pfx,
524 const git_diff_binary *binary)
e349ed50 525{
947a58c1 526 size_t pre_binary_size;
8147b1af 527 int error;
e349ed50 528
f4e3dae7
ET
529 if (delta->status == GIT_DELTA_UNMODIFIED)
530 return 0;
531
adedac5a 532 if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
4ac2d8ac 533 return diff_print_patch_file_binary_noshow(
f941f035 534 pi, delta, old_pfx, new_pfx);
e349ed50 535
947a58c1 536 pre_binary_size = pi->buf->size;
e349ed50
ET
537 git_buf_printf(pi->buf, "GIT binary patch\n");
538 pi->line.num_lines++;
539
8147b1af 540 if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
22a2d3d5
UG
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) {
947a58c1 544 if (error == GIT_EBUFS) {
ac3d33df 545 git_error_clear();
947a58c1 546 git_buf_truncate(pi->buf, pre_binary_size);
4ac2d8ac
ET
547
548 return diff_print_patch_file_binary_noshow(
f941f035 549 pi, delta, old_pfx, new_pfx);
947a58c1
RB
550 }
551 }
e349ed50
ET
552
553 pi->line.num_lines++;
e349ed50
ET
554 return error;
555}
556
197b8966
RB
557static int diff_print_patch_file(
558 const git_diff_delta *delta, float progress, void *data)
559{
25e0b157 560 int error;
197b8966 561 diff_print_info *pi = data;
eb1c1707 562 const char *oldpfx =
804d5fe9 563 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
eb1c1707 564 const char *newpfx =
804d5fe9 565 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
197b8966 566
8147b1af
ET
567 bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
568 (pi->flags & GIT_DIFF_FORCE_BINARY);
e349ed50 569 bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
adedac5a 570 int id_strlen = pi->id_strlen;
22a2d3d5 571 bool print_index = (pi->format != GIT_DIFF_FORMAT_PATCH_ID);
adedac5a
ET
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;
e349ed50 576
197b8966 577 GIT_UNUSED(progress);
7000f3fa 578
197b8966 579 if (S_ISDIR(delta->new_file.mode) ||
22a2d3d5
UG
580 delta->status == GIT_DELTA_UNMODIFIED ||
581 delta->status == GIT_DELTA_IGNORED ||
582 delta->status == GIT_DELTA_UNREADABLE ||
583 (delta->status == GIT_DELTA_UNTRACKED &&
10672e3e 584 (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
7000f3fa
RB
585 return 0;
586
22a2d3d5
UG
587 if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx,
588 id_strlen, print_index)) < 0)
25e0b157 589 return error;
7000f3fa 590
3b5f7954
RB
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
8147b1af
ET
595 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
596}
eb1c1707 597
8147b1af
ET
598static 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 =
804d5fe9 605 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
8147b1af 606 const char *new_pfx =
804d5fe9 607 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
8147b1af 608 int error;
eb1c1707
RB
609
610 git_buf_clear(pi->buf);
611
8147b1af
ET
612 if ((error = diff_print_patch_file_binary(
613 pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
25e0b157 614 return error;
eb1c1707 615
8147b1af
ET
616 pi->line.origin = GIT_DIFF_LINE_BINARY;
617 pi->line.content = git_buf_cstr(pi->buf);
3b5f7954 618 pi->line.content_len = git_buf_len(pi->buf);
3b5f7954 619
25e0b157 620 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
621}
622
a1683f28 623static int diff_print_patch_hunk(
7000f3fa 624 const git_diff_delta *d,
3b5f7954 625 const git_diff_hunk *h,
7000f3fa
RB
626 void *data)
627{
628 diff_print_info *pi = data;
629
630 if (S_ISDIR(d->new_file.mode))
631 return 0;
632
3b5f7954
RB
633 pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
634 pi->line.content = h->header;
635 pi->line.content_len = h->header_len;
7000f3fa 636
25e0b157 637 return pi->print_cb(d, h, &pi->line, pi->payload);
7000f3fa
RB
638}
639
a1683f28 640static int diff_print_patch_line(
7000f3fa 641 const git_diff_delta *delta,
3b5f7954
RB
642 const git_diff_hunk *hunk,
643 const git_diff_line *line,
7000f3fa
RB
644 void *data)
645{
646 diff_print_info *pi = data;
647
648 if (S_ISDIR(delta->new_file.mode))
649 return 0;
650
25e0b157 651 return pi->print_cb(delta, hunk, line, pi->payload);
7000f3fa
RB
652}
653
10672e3e
RB
654/* print a git_diff to an output callback */
655int git_diff_print(
3ff1d123 656 git_diff *diff,
10672e3e 657 git_diff_format_t format,
3ff1d123 658 git_diff_line_cb print_cb,
7000f3fa
RB
659 void *payload)
660{
661 int error;
662 git_buf buf = GIT_BUF_INIT;
663 diff_print_info pi;
10672e3e 664 git_diff_file_cb print_file = NULL;
8147b1af 665 git_diff_binary_cb print_binary = NULL;
10672e3e
RB
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;
8147b1af 672 print_binary = diff_print_patch_binary;
10672e3e
RB
673 print_hunk = diff_print_patch_hunk;
674 print_line = diff_print_patch_line;
675 break;
22a2d3d5
UG
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;
10672e3e
RB
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:
ac3d33df 694 git_error_set(GIT_ERROR_INVALID, "unknown diff output format (%d)", format);
10672e3e
RB
695 return -1;
696 }
7000f3fa 697
22a2d3d5
UG
698 if ((error = diff_print_info_init_fromdiff(&pi, &buf, diff, format, print_cb, payload)) < 0)
699 goto out;
7000f3fa 700
22a2d3d5
UG
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;
96869a4e
RB
704 }
705
22a2d3d5 706out:
ac3d33df 707 git_buf_dispose(&buf);
7000f3fa
RB
708 return error;
709}
710
27e54bcf 711int git_diff_print_callback__to_buf(
a1683f28 712 const git_diff_delta *delta,
3b5f7954
RB
713 const git_diff_hunk *hunk,
714 const git_diff_line *line,
a1683f28
RB
715 void *payload)
716{
717 git_buf *output = payload;
3b5f7954
RB
718 GIT_UNUSED(delta); GIT_UNUSED(hunk);
719
27e54bcf 720 if (!output) {
ac3d33df 721 git_error_set(GIT_ERROR_INVALID, "buffer pointer must be provided");
27e54bcf
RB
722 return -1;
723 }
724
3b5f7954 725 if (line->origin == GIT_DIFF_LINE_ADDITION ||
22a2d3d5
UG
726 line->origin == GIT_DIFF_LINE_DELETION ||
727 line->origin == GIT_DIFF_LINE_CONTEXT)
3b5f7954
RB
728 git_buf_putc(output, line->origin);
729
730 return git_buf_put(output, line->content, line->content_len);
a1683f28
RB
731}
732
27e54bcf
RB
733int 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;
22a2d3d5 740 int error;
27e54bcf 741
22a2d3d5
UG
742 GIT_UNUSED(delta);
743 GIT_UNUSED(hunk);
27e54bcf
RB
744
745 if (line->origin == GIT_DIFF_LINE_CONTEXT ||
22a2d3d5
UG
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
27e54bcf
RB
761 return 0;
762}
763
72827490
ET
764/* print a git_diff to a git_buf */
765int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
766{
c25aa7cd
PP
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
22a2d3d5 775 return git_diff_print(diff, format, git_diff_print_callback__to_buf, out);
72827490
ET
776}
777
804d5fe9
ET
778/* print a git_patch to an output callback */
779int git_patch_print(
780 git_patch *patch,
781 git_diff_line_cb print_cb,
782 void *payload)
783{
804d5fe9
ET
784 git_buf temp = GIT_BUF_INIT;
785 diff_print_info pi;
22a2d3d5 786 int error;
804d5fe9 787
c25aa7cd
PP
788 GIT_ASSERT_ARG(patch);
789 GIT_ASSERT_ARG(print_cb);
804d5fe9 790
22a2d3d5
UG
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;
804d5fe9
ET
799 }
800
22a2d3d5 801out:
ac3d33df 802 git_buf_dispose(&temp);
804d5fe9
ET
803 return error;
804}
805
450e8e9e 806/* print a git_patch to a git_buf */
1e4976cb 807int git_patch_to_buf(git_buf *out, git_patch *patch)
450e8e9e 808{
c25aa7cd
PP
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
27e54bcf 817 return git_patch_print(patch, git_diff_print_callback__to_buf, out);
450e8e9e 818}