]> git.proxmox.com Git - libgit2.git/blame - src/diff_print.c
Test has to work on case sensitive systems
[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
RB
9#include "diff_patch.h"
10#include "buffer.h"
7000f3fa
RB
11
12typedef struct {
13 git_diff_list *diff;
14 git_diff_data_cb print_cb;
15 void *payload;
16 git_buf *buf;
17 int oid_strlen;
18} diff_print_info;
19
20static int diff_print_info_init(
21 diff_print_info *pi,
22 git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
23{
24 assert(diff && diff->repo);
25
26 pi->diff = diff;
27 pi->print_cb = cb;
28 pi->payload = payload;
29 pi->buf = out;
30
31 if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
32 return -1;
33
34 pi->oid_strlen += 1; /* for NUL byte */
35
36 if (pi->oid_strlen < 2)
37 pi->oid_strlen = 2;
38 else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
39 pi->oid_strlen = GIT_OID_HEXSZ + 1;
40
41 return 0;
42}
43
44static char pick_suffix(int mode)
45{
46 if (S_ISDIR(mode))
47 return '/';
48 else if (mode & 0100) //-V536
49 /* in git, modes are very regular, so we must have 0100755 mode */
50 return '*';
51 else
52 return ' ';
53}
54
55char git_diff_status_char(git_delta_t status)
56{
57 char code;
58
59 switch (status) {
60 case GIT_DELTA_ADDED: code = 'A'; break;
61 case GIT_DELTA_DELETED: code = 'D'; break;
62 case GIT_DELTA_MODIFIED: code = 'M'; break;
63 case GIT_DELTA_RENAMED: code = 'R'; break;
64 case GIT_DELTA_COPIED: code = 'C'; break;
65 case GIT_DELTA_IGNORED: code = 'I'; break;
66 case GIT_DELTA_UNTRACKED: code = '?'; break;
67 default: code = ' '; break;
68 }
69
70 return code;
71}
72
73static int callback_error(void)
74{
75 giterr_clear();
76 return GIT_EUSER;
77}
78
79static int print_compact(
80 const git_diff_delta *delta, float progress, void *data)
81{
82 diff_print_info *pi = data;
83 char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
84
85 GIT_UNUSED(progress);
86
87 if (code == ' ')
88 return 0;
89
90 old_suffix = pick_suffix(delta->old_file.mode);
91 new_suffix = pick_suffix(delta->new_file.mode);
92
93 git_buf_clear(pi->buf);
94
95 if (delta->old_file.path != delta->new_file.path &&
96 pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
97 git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
98 delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
99 else if (delta->old_file.mode != delta->new_file.mode &&
100 delta->old_file.mode != 0 && delta->new_file.mode != 0)
101 git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
102 delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
103 else if (old_suffix != ' ')
104 git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
105 else
106 git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
107
108 if (git_buf_oom(pi->buf))
109 return -1;
110
111 if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
112 git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
113 return callback_error();
114
115 return 0;
116}
117
118int git_diff_print_compact(
119 git_diff_list *diff,
120 git_diff_data_cb print_cb,
121 void *payload)
122{
123 int error;
124 git_buf buf = GIT_BUF_INIT;
125 diff_print_info pi;
126
127 if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
128 error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi);
129
130 git_buf_free(&buf);
131
132 return error;
133}
134
135static int print_raw(
136 const git_diff_delta *delta, float progress, void *data)
137{
138 diff_print_info *pi = data;
139 char code = git_diff_status_char(delta->status);
140 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
141
142 GIT_UNUSED(progress);
143
144 if (code == ' ')
145 return 0;
146
147 git_buf_clear(pi->buf);
148
149 git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
150 git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
151
152 git_buf_printf(
153 pi->buf, ":%06o %06o %s... %s... %c",
154 delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
155
156 if (delta->similarity > 0)
157 git_buf_printf(pi->buf, "%03u", delta->similarity);
158
159 if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
160 git_buf_printf(
161 pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
162 else
163 git_buf_printf(
164 pi->buf, "\t%s\n", delta->old_file.path ?
165 delta->old_file.path : delta->new_file.path);
166
167 if (git_buf_oom(pi->buf))
168 return -1;
169
170 if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
171 git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
172 return callback_error();
173
174 return 0;
175}
176
177int git_diff_print_raw(
178 git_diff_list *diff,
179 git_diff_data_cb print_cb,
180 void *payload)
181{
182 int error;
183 git_buf buf = GIT_BUF_INIT;
184 diff_print_info pi;
185
186 if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
187 error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi);
188
189 git_buf_free(&buf);
190
191 return error;
192}
193
194static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
195{
196 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
197
198 git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
199 git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
200
201 /* TODO: Match git diff more closely */
202 if (delta->old_file.mode == delta->new_file.mode) {
203 git_buf_printf(pi->buf, "index %s..%s %o\n",
204 start_oid, end_oid, delta->old_file.mode);
205 } else {
206 if (delta->old_file.mode == 0) {
207 git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
208 } else if (delta->new_file.mode == 0) {
209 git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
210 } else {
211 git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
212 git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
213 }
214 git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
215 }
216
217 if (git_buf_oom(pi->buf))
218 return -1;
219
220 return 0;
221}
222
223static int print_patch_file(
224 const git_diff_delta *delta, float progress, void *data)
225{
226 diff_print_info *pi = data;
227 const char *oldpfx = pi->diff->opts.old_prefix;
228 const char *oldpath = delta->old_file.path;
229 const char *newpfx = pi->diff->opts.new_prefix;
230 const char *newpath = delta->new_file.path;
231
232 GIT_UNUSED(progress);
233
234 if (S_ISDIR(delta->new_file.mode) ||
235 delta->status == GIT_DELTA_UNMODIFIED ||
236 delta->status == GIT_DELTA_IGNORED ||
237 (delta->status == GIT_DELTA_UNTRACKED &&
238 (pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
239 return 0;
240
241 if (!oldpfx)
242 oldpfx = DIFF_OLD_PREFIX_DEFAULT;
243
244 if (!newpfx)
245 newpfx = DIFF_NEW_PREFIX_DEFAULT;
246
247 git_buf_clear(pi->buf);
248 git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
249
250 if (print_oid_range(pi, delta) < 0)
251 return -1;
252
253 if (git_oid_iszero(&delta->old_file.oid)) {
254 oldpfx = "";
255 oldpath = "/dev/null";
256 }
257 if (git_oid_iszero(&delta->new_file.oid)) {
258 newpfx = "";
259 newpath = "/dev/null";
260 }
261
262 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
263 git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
264 git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
265 }
266
267 if (git_buf_oom(pi->buf))
268 return -1;
269
270 if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
271 git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
272 return callback_error();
273
274 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
275 return 0;
276
277 git_buf_clear(pi->buf);
278 git_buf_printf(
279 pi->buf, "Binary files %s%s and %s%s differ\n",
280 oldpfx, oldpath, newpfx, newpath);
281 if (git_buf_oom(pi->buf))
282 return -1;
283
284 if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
285 git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
286 return callback_error();
287
288 return 0;
289}
290
291static int print_patch_hunk(
292 const git_diff_delta *d,
293 const git_diff_range *r,
294 const char *header,
295 size_t header_len,
296 void *data)
297{
298 diff_print_info *pi = data;
299
300 if (S_ISDIR(d->new_file.mode))
301 return 0;
302
303 git_buf_clear(pi->buf);
304 if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
305 return -1;
306
307 if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
308 git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
309 return callback_error();
310
311 return 0;
312}
313
314static int print_patch_line(
315 const git_diff_delta *delta,
316 const git_diff_range *range,
317 char line_origin, /* GIT_DIFF_LINE value from above */
318 const char *content,
319 size_t content_len,
320 void *data)
321{
322 diff_print_info *pi = data;
323
324 if (S_ISDIR(delta->new_file.mode))
325 return 0;
326
327 git_buf_clear(pi->buf);
328
329 if (line_origin == GIT_DIFF_LINE_ADDITION ||
330 line_origin == GIT_DIFF_LINE_DELETION ||
331 line_origin == GIT_DIFF_LINE_CONTEXT)
332 git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
333 else if (content_len > 0)
334 git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
335
336 if (git_buf_oom(pi->buf))
337 return -1;
338
339 if (pi->print_cb(delta, range, line_origin,
340 git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
341 return callback_error();
342
343 return 0;
344}
345
346int git_diff_print_patch(
347 git_diff_list *diff,
348 git_diff_data_cb print_cb,
349 void *payload)
350{
351 int error;
352 git_buf buf = GIT_BUF_INIT;
353 diff_print_info pi;
354
355 if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
356 error = git_diff_foreach(
357 diff, print_patch_file, print_patch_hunk, print_patch_line, &pi);
358
359 git_buf_free(&buf);
360
361 return error;
362}
363
364
365static int print_to_buffer_cb(
366 const git_diff_delta *delta,
367 const git_diff_range *range,
368 char line_origin,
369 const char *content,
370 size_t content_len,
371 void *payload)
372{
373 git_buf *output = payload;
374 GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
375 return git_buf_put(output, content, content_len);
376}
377
378int git_diff_patch_print(
379 git_diff_patch *patch,
380 git_diff_data_cb print_cb,
381 void *payload)
382{
383 int error;
384 git_buf temp = GIT_BUF_INIT;
385 diff_print_info pi;
7000f3fa
RB
386
387 assert(patch && print_cb);
388
389 if (!(error = diff_print_info_init(
360f42f4
RB
390 &pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
391 error = git_diff_patch__invoke_callbacks(
392 patch, print_patch_file, print_patch_hunk, print_patch_line, &pi);
7000f3fa
RB
393
394 git_buf_free(&temp);
395
396 return error;
397}
398
399int git_diff_patch_to_str(
400 char **string,
401 git_diff_patch *patch)
402{
403 int error;
404 git_buf output = GIT_BUF_INIT;
405
406 error = git_diff_patch_print(patch, print_to_buffer_cb, &output);
407
408 /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
409 * meaning a memory allocation failure, so just map to -1...
410 */
411 if (error == GIT_EUSER)
412 error = -1;
413
414 *string = git_buf_detach(&output);
415
416 return error;
417}