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_format_email__append_header_tobuf(
126 const git_signature
*author
,
130 size_t total_patches
,
131 bool exclude_patchno_marker
)
133 char idstr
[GIT_OID_HEXSZ
+ 1];
134 char date_str
[GIT_DATE_RFC2822_SZ
];
137 git_oid_fmt(idstr
, id
);
138 idstr
[GIT_OID_HEXSZ
] = '\0';
140 if ((error
= git__date_rfc2822_fmt(date_str
, sizeof(date_str
),
144 error
= git_buf_printf(out
,
145 "From %s Mon Sep 17 00:00:00 2001\n" \
150 author
->name
, author
->email
,
156 if (!exclude_patchno_marker
) {
157 if (total_patches
== 1) {
158 error
= git_buf_puts(out
, "[PATCH] ");
160 error
= git_buf_printf(out
, "[PATCH %"PRIuZ
"/%"PRIuZ
"] ",
161 patch_no
, total_patches
);
168 error
= git_buf_printf(out
, "%s\n\n", summary
);
171 git_buf_puts(out
, body
);
173 if (out
->ptr
[out
->size
- 1] != '\n')
174 git_buf_putc(out
, '\n');
180 int git_diff_format_email__append_patches_tobuf(
187 deltas
= git_diff_num_deltas(diff
);
189 for (i
= 0; i
< deltas
; ++i
) {
190 git_patch
*patch
= NULL
;
192 if ((error
= git_patch_from_diff(&patch
, diff
, i
)) >= 0)
193 error
= git_patch_to_buf(out
, patch
);
195 git_patch_free(patch
);
204 int git_diff_format_email(
207 const git_diff_format_email_options
*opts
)
209 git_diff_stats
*stats
= NULL
;
210 char *summary
= NULL
, *loc
= NULL
;
212 unsigned int format_flags
= 0;
216 assert(out
&& diff
&& opts
);
217 assert(opts
->summary
&& opts
->id
&& opts
->author
);
219 GITERR_CHECK_VERSION(opts
,
220 GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION
,
221 "git_format_email_options");
223 ignore_marker
= (opts
->flags
&
224 GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER
) != 0;
226 if (!ignore_marker
) {
227 if (opts
->patch_no
> opts
->total_patches
) {
228 giterr_set(GITERR_INVALID
,
229 "patch %"PRIuZ
" out of range. max %"PRIuZ
,
230 opts
->patch_no
, opts
->total_patches
);
234 if (opts
->patch_no
== 0) {
235 giterr_set(GITERR_INVALID
,
236 "invalid patch no %"PRIuZ
". should be >0", opts
->patch_no
);
241 /* the summary we receive may not be clean.
242 * it could potentially contain new line characters
243 * or not be set, sanitize, */
244 if ((loc
= strpbrk(opts
->summary
, "\r\n")) != NULL
) {
247 if ((offset
= (loc
- opts
->summary
)) == 0) {
248 giterr_set(GITERR_INVALID
, "summary is empty");
253 GITERR_CHECK_ALLOC_ADD(&allocsize
, offset
, 1);
254 summary
= git__calloc(allocsize
, sizeof(char));
255 GITERR_CHECK_ALLOC(summary
);
257 strncpy(summary
, opts
->summary
, offset
);
260 error
= git_diff_format_email__append_header_tobuf(out
,
261 opts
->id
, opts
->author
, summary
== NULL
? opts
->summary
: summary
,
262 opts
->body
, opts
->patch_no
, opts
->total_patches
, ignore_marker
);
267 format_flags
= GIT_DIFF_STATS_FULL
| GIT_DIFF_STATS_INCLUDE_SUMMARY
;
269 if ((error
= git_buf_puts(out
, "---\n")) < 0 ||
270 (error
= git_diff_get_stats(&stats
, diff
)) < 0 ||
271 (error
= git_diff_stats_to_buf(out
, stats
, format_flags
, 0)) < 0 ||
272 (error
= git_buf_putc(out
, '\n')) < 0 ||
273 (error
= git_diff_format_email__append_patches_tobuf(out
, diff
)) < 0)
276 error
= git_buf_puts(out
, "--\nlibgit2 " LIBGIT2_VERSION
"\n\n");
280 git_diff_stats_free(stats
);
285 int git_diff_commit_as_email(
287 git_repository
*repo
,
290 size_t total_patches
,
291 git_diff_format_email_flags_t flags
,
292 const git_diff_options
*diff_opts
)
294 git_diff
*diff
= NULL
;
295 git_diff_format_email_options opts
=
296 GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT
;
299 assert (out
&& repo
&& commit
);
302 opts
.patch_no
= patch_no
;
303 opts
.total_patches
= total_patches
;
304 opts
.id
= git_commit_id(commit
);
305 opts
.summary
= git_commit_summary(commit
);
306 opts
.body
= git_commit_body(commit
);
307 opts
.author
= git_commit_author(commit
);
309 if ((error
= git_diff__commit(&diff
, repo
, commit
, diff_opts
)) < 0)
312 error
= git_diff_format_email(out
, diff
, &opts
);
318 int git_diff_init_options(git_diff_options
*opts
, unsigned int version
)
320 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
321 opts
, version
, git_diff_options
, GIT_DIFF_OPTIONS_INIT
);
325 int git_diff_find_init_options(
326 git_diff_find_options
*opts
, unsigned int version
)
328 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
329 opts
, version
, git_diff_find_options
, GIT_DIFF_FIND_OPTIONS_INIT
);
333 int git_diff_format_email_init_options(
334 git_diff_format_email_options
*opts
, unsigned int version
)
336 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
337 opts
, version
, git_diff_format_email_options
,
338 GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT
);