2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
7 #include "git2/version.h"
10 #include "diff_generate.h"
15 #define DIFF_FLAG_IS_SET(DIFF,FLAG) \
16 (((DIFF)->opts.flags & (FLAG)) != 0)
17 #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \
18 (((DIFF)->opts.flags & (FLAG)) == 0)
19 #define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->opts.flags = \
20 (VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
22 GIT_INLINE(const char *) diff_delta__path(const git_diff_delta
*delta
)
24 const char *str
= delta
->old_file
.path
;
27 delta
->status
== GIT_DELTA_ADDED
||
28 delta
->status
== GIT_DELTA_RENAMED
||
29 delta
->status
== GIT_DELTA_COPIED
)
30 str
= delta
->new_file
.path
;
35 const char *git_diff_delta__path(const git_diff_delta
*delta
)
37 return diff_delta__path(delta
);
40 int git_diff_delta__cmp(const void *a
, const void *b
)
42 const git_diff_delta
*da
= a
, *db
= b
;
43 int val
= strcmp(diff_delta__path(da
), diff_delta__path(db
));
44 return val
? val
: ((int)da
->status
- (int)db
->status
);
47 int git_diff_delta__casecmp(const void *a
, const void *b
)
49 const git_diff_delta
*da
= a
, *db
= b
;
50 int val
= strcasecmp(diff_delta__path(da
), diff_delta__path(db
));
51 return val
? val
: ((int)da
->status
- (int)db
->status
);
54 int git_diff__entry_cmp(const void *a
, const void *b
)
56 const git_index_entry
*entry_a
= a
;
57 const git_index_entry
*entry_b
= b
;
59 return strcmp(entry_a
->path
, entry_b
->path
);
62 int git_diff__entry_icmp(const void *a
, const void *b
)
64 const git_index_entry
*entry_a
= a
;
65 const git_index_entry
*entry_b
= b
;
67 return strcasecmp(entry_a
->path
, entry_b
->path
);
70 void git_diff_free(git_diff
*diff
)
75 GIT_REFCOUNT_DEC(diff
, diff
->free_fn
);
78 void git_diff_addref(git_diff
*diff
)
80 GIT_REFCOUNT_INC(diff
);
83 size_t git_diff_num_deltas(const git_diff
*diff
)
86 return diff
->deltas
.length
;
89 size_t git_diff_num_deltas_of_type(const git_diff
*diff
, git_delta_t type
)
92 const git_diff_delta
*delta
;
96 git_vector_foreach(&diff
->deltas
, i
, delta
) {
97 count
+= (delta
->status
== type
);
103 const git_diff_delta
*git_diff_get_delta(const git_diff
*diff
, size_t idx
)
106 return git_vector_get(&diff
->deltas
, idx
);
109 int git_diff_is_sorted_icase(const git_diff
*diff
)
111 return (diff
->opts
.flags
& GIT_DIFF_IGNORE_CASE
) != 0;
114 int git_diff_get_perfdata(git_diff_perfdata
*out
, const git_diff
*diff
)
117 GITERR_CHECK_VERSION(out
, GIT_DIFF_PERFDATA_VERSION
, "git_diff_perfdata");
118 out
->stat_calls
= diff
->perf
.stat_calls
;
119 out
->oid_calculations
= diff
->perf
.oid_calculations
;
123 int git_diff_foreach(
125 git_diff_file_cb file_cb
,
126 git_diff_binary_cb binary_cb
,
127 git_diff_hunk_cb hunk_cb
,
128 git_diff_line_cb data_cb
,
132 git_diff_delta
*delta
;
137 git_vector_foreach(&diff
->deltas
, idx
, delta
) {
140 /* check flags against patch status */
141 if (git_diff_delta__should_skip(&diff
->opts
, delta
))
144 if ((error
= git_patch_from_diff(&patch
, diff
, idx
)) != 0)
147 error
= git_patch__invoke_callbacks(patch
, file_cb
, binary_cb
,
148 hunk_cb
, data_cb
, payload
);
149 git_patch_free(patch
);
158 int git_diff_format_email__append_header_tobuf(
161 const git_signature
*author
,
165 size_t total_patches
,
166 bool exclude_patchno_marker
)
168 char idstr
[GIT_OID_HEXSZ
+ 1];
169 char date_str
[GIT_DATE_RFC2822_SZ
];
172 git_oid_fmt(idstr
, id
);
173 idstr
[GIT_OID_HEXSZ
] = '\0';
175 if ((error
= git__date_rfc2822_fmt(date_str
, sizeof(date_str
),
179 error
= git_buf_printf(out
,
180 "From %s Mon Sep 17 00:00:00 2001\n" \
185 author
->name
, author
->email
,
191 if (!exclude_patchno_marker
) {
192 if (total_patches
== 1) {
193 error
= git_buf_puts(out
, "[PATCH] ");
195 error
= git_buf_printf(out
, "[PATCH %"PRIuZ
"/%"PRIuZ
"] ",
196 patch_no
, total_patches
);
203 error
= git_buf_printf(out
, "%s\n\n", summary
);
206 git_buf_puts(out
, body
);
208 if (out
->ptr
[out
->size
- 1] != '\n')
209 git_buf_putc(out
, '\n');
215 int git_diff_format_email__append_patches_tobuf(
222 deltas
= git_diff_num_deltas(diff
);
224 for (i
= 0; i
< deltas
; ++i
) {
225 git_patch
*patch
= NULL
;
227 if ((error
= git_patch_from_diff(&patch
, diff
, i
)) >= 0)
228 error
= git_patch_to_buf(out
, patch
);
230 git_patch_free(patch
);
239 int git_diff_format_email(
242 const git_diff_format_email_options
*opts
)
244 git_diff_stats
*stats
= NULL
;
245 char *summary
= NULL
, *loc
= NULL
;
247 unsigned int format_flags
= 0;
251 assert(out
&& diff
&& opts
);
252 assert(opts
->summary
&& opts
->id
&& opts
->author
);
254 GITERR_CHECK_VERSION(opts
,
255 GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION
,
256 "git_format_email_options");
258 ignore_marker
= (opts
->flags
&
259 GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER
) != 0;
261 if (!ignore_marker
) {
262 if (opts
->patch_no
> opts
->total_patches
) {
263 giterr_set(GITERR_INVALID
,
264 "patch %"PRIuZ
" out of range. max %"PRIuZ
,
265 opts
->patch_no
, opts
->total_patches
);
269 if (opts
->patch_no
== 0) {
270 giterr_set(GITERR_INVALID
,
271 "invalid patch no %"PRIuZ
". should be >0", opts
->patch_no
);
276 /* the summary we receive may not be clean.
277 * it could potentially contain new line characters
278 * or not be set, sanitize, */
279 if ((loc
= strpbrk(opts
->summary
, "\r\n")) != NULL
) {
282 if ((offset
= (loc
- opts
->summary
)) == 0) {
283 giterr_set(GITERR_INVALID
, "summary is empty");
288 GITERR_CHECK_ALLOC_ADD(&allocsize
, offset
, 1);
289 summary
= git__calloc(allocsize
, sizeof(char));
290 GITERR_CHECK_ALLOC(summary
);
292 strncpy(summary
, opts
->summary
, offset
);
295 error
= git_diff_format_email__append_header_tobuf(out
,
296 opts
->id
, opts
->author
, summary
== NULL
? opts
->summary
: summary
,
297 opts
->body
, opts
->patch_no
, opts
->total_patches
, ignore_marker
);
302 format_flags
= GIT_DIFF_STATS_FULL
| GIT_DIFF_STATS_INCLUDE_SUMMARY
;
304 if ((error
= git_buf_puts(out
, "---\n")) < 0 ||
305 (error
= git_diff_get_stats(&stats
, diff
)) < 0 ||
306 (error
= git_diff_stats_to_buf(out
, stats
, format_flags
, 0)) < 0 ||
307 (error
= git_buf_putc(out
, '\n')) < 0 ||
308 (error
= git_diff_format_email__append_patches_tobuf(out
, diff
)) < 0)
311 error
= git_buf_puts(out
, "--\nlibgit2 " LIBGIT2_VERSION
"\n\n");
315 git_diff_stats_free(stats
);
320 int git_diff_commit_as_email(
322 git_repository
*repo
,
325 size_t total_patches
,
326 git_diff_format_email_flags_t flags
,
327 const git_diff_options
*diff_opts
)
329 git_diff
*diff
= NULL
;
330 git_diff_format_email_options opts
=
331 GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT
;
334 assert (out
&& repo
&& commit
);
337 opts
.patch_no
= patch_no
;
338 opts
.total_patches
= total_patches
;
339 opts
.id
= git_commit_id(commit
);
340 opts
.summary
= git_commit_summary(commit
);
341 opts
.body
= git_commit_body(commit
);
342 opts
.author
= git_commit_author(commit
);
344 if ((error
= git_diff__commit(&diff
, repo
, commit
, diff_opts
)) < 0)
347 error
= git_diff_format_email(out
, diff
, &opts
);
353 int git_diff_init_options(git_diff_options
*opts
, unsigned int version
)
355 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
356 opts
, version
, git_diff_options
, GIT_DIFF_OPTIONS_INIT
);
360 int git_diff_find_init_options(
361 git_diff_find_options
*opts
, unsigned int version
)
363 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
364 opts
, version
, git_diff_find_options
, GIT_DIFF_FIND_OPTIONS_INIT
);
368 int git_diff_format_email_init_options(
369 git_diff_format_email_options
*opts
, unsigned int version
)
371 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
372 opts
, version
, git_diff_format_email_options
,
373 GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT
);