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.
16 #include "diff_generate.h"
18 #include "git2/version.h"
19 #include "git2/email.h"
21 struct patch_id_args
{
27 GIT_INLINE(const char *) diff_delta__path(const git_diff_delta
*delta
)
29 const char *str
= delta
->old_file
.path
;
32 delta
->status
== GIT_DELTA_ADDED
||
33 delta
->status
== GIT_DELTA_RENAMED
||
34 delta
->status
== GIT_DELTA_COPIED
)
35 str
= delta
->new_file
.path
;
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
)
105 GIT_ASSERT_ARG_WITH_RETVAL(diff
, NULL
);
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 GIT_ERROR_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
;
135 GIT_ASSERT_ARG(diff
);
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 #ifndef GIT_DEPRECATE_HARD
160 int git_diff_format_email(
163 const git_diff_format_email_options
*opts
)
165 git_email_create_options email_create_opts
= GIT_EMAIL_CREATE_OPTIONS_INIT
;
166 git_str email
= GIT_STR_INIT
;
170 GIT_ASSERT_ARG(diff
);
171 GIT_ASSERT_ARG(opts
&& opts
->summary
&& opts
->id
&& opts
->author
);
173 GIT_ERROR_CHECK_VERSION(opts
,
174 GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION
,
175 "git_format_email_options");
177 /* This is a `git_buf` special case; subsequent calls append. */
178 email
.ptr
= out
->ptr
;
179 email
.asize
= out
->reserved
;
180 email
.size
= out
->size
;
182 out
->ptr
= git_str__initstr
;
186 if ((opts
->flags
& GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER
) != 0)
187 email_create_opts
.subject_prefix
= "";
189 error
= git_email__append_from_diff(&email
, diff
, opts
->patch_no
,
190 opts
->total_patches
, opts
->id
, opts
->summary
, opts
->body
,
191 opts
->author
, &email_create_opts
);
196 error
= git_buf_fromstr(out
, &email
);
199 git_str_dispose(&email
);
203 int git_diff_commit_as_email(
205 git_repository
*repo
,
208 size_t total_patches
,
210 const git_diff_options
*diff_opts
)
212 git_diff
*diff
= NULL
;
213 git_email_create_options opts
= GIT_EMAIL_CREATE_OPTIONS_INIT
;
214 const git_oid
*commit_id
;
215 const char *summary
, *body
;
216 const git_signature
*author
;
220 GIT_ASSERT_ARG(repo
);
221 GIT_ASSERT_ARG(commit
);
223 commit_id
= git_commit_id(commit
);
224 summary
= git_commit_summary(commit
);
225 body
= git_commit_body(commit
);
226 author
= git_commit_author(commit
);
228 if ((flags
& GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER
) != 0)
229 opts
.subject_prefix
= "";
231 if ((error
= git_diff__commit(&diff
, repo
, commit
, diff_opts
)) < 0)
234 error
= git_email_create_from_diff(out
, diff
, patch_no
, total_patches
, commit_id
, summary
, body
, author
, &opts
);
240 int git_diff_init_options(git_diff_options
*opts
, unsigned int version
)
242 return git_diff_options_init(opts
, version
);
245 int git_diff_find_init_options(
246 git_diff_find_options
*opts
, unsigned int version
)
248 return git_diff_find_options_init(opts
, version
);
251 int git_diff_format_email_options_init(
252 git_diff_format_email_options
*opts
, unsigned int version
)
254 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
255 opts
, version
, git_diff_format_email_options
,
256 GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT
);
260 int git_diff_format_email_init_options(
261 git_diff_format_email_options
*opts
, unsigned int version
)
263 return git_diff_format_email_options_init(opts
, version
);
268 int git_diff_options_init(git_diff_options
*opts
, unsigned int version
)
270 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
271 opts
, version
, git_diff_options
, GIT_DIFF_OPTIONS_INIT
);
275 int git_diff_find_options_init(
276 git_diff_find_options
*opts
, unsigned int version
)
278 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
279 opts
, version
, git_diff_find_options
, GIT_DIFF_FIND_OPTIONS_INIT
);
283 static int flush_hunk(git_oid
*result
, git_hash_ctx
*ctx
)
286 unsigned short carry
= 0;
289 if ((error
= git_hash_final(hash
.id
, ctx
)) < 0 ||
290 (error
= git_hash_init(ctx
)) < 0)
293 for (i
= 0; i
< GIT_OID_RAWSZ
; i
++) {
294 carry
+= result
->id
[i
] + hash
.id
[i
];
295 result
->id
[i
] = (unsigned char)carry
;
302 static void strip_spaces(git_str
*buf
)
304 char *src
= buf
->ptr
, *dst
= buf
->ptr
;
308 while ((c
= *src
++) != '\0') {
309 if (!git__isspace(c
)) {
315 git_str_truncate(buf
, len
);
318 static int diff_patchid_print_callback_to_buf(
319 const git_diff_delta
*delta
,
320 const git_diff_hunk
*hunk
,
321 const git_diff_line
*line
,
324 struct patch_id_args
*args
= (struct patch_id_args
*) payload
;
325 git_str buf
= GIT_STR_INIT
;
328 if (line
->origin
== GIT_DIFF_LINE_CONTEXT_EOFNL
||
329 line
->origin
== GIT_DIFF_LINE_ADD_EOFNL
||
330 line
->origin
== GIT_DIFF_LINE_DEL_EOFNL
)
333 if ((error
= git_diff_print_callback__to_buf(delta
, hunk
,
339 if (line
->origin
== GIT_DIFF_LINE_FILE_HDR
&&
341 (error
= flush_hunk(&args
->result
, &args
->ctx
) < 0))
344 if ((error
= git_hash_update(&args
->ctx
, buf
.ptr
, buf
.size
)) < 0)
347 if (line
->origin
== GIT_DIFF_LINE_FILE_HDR
&& args
->first_file
)
348 args
->first_file
= 0;
351 git_str_dispose(&buf
);
355 int git_diff_patchid_options_init(git_diff_patchid_options
*opts
, unsigned int version
)
357 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
358 opts
, version
, git_diff_patchid_options
, GIT_DIFF_PATCHID_OPTIONS_INIT
);
362 int git_diff_patchid(git_oid
*out
, git_diff
*diff
, git_diff_patchid_options
*opts
)
364 struct patch_id_args args
;
367 GIT_ERROR_CHECK_VERSION(
368 opts
, GIT_DIFF_PATCHID_OPTIONS_VERSION
, "git_diff_patchid_options");
370 memset(&args
, 0, sizeof(args
));
372 if ((error
= git_hash_ctx_init(&args
.ctx
, GIT_HASH_ALGORITHM_SHA1
)) < 0)
375 if ((error
= git_diff_print(diff
,
376 GIT_DIFF_FORMAT_PATCH_ID
,
377 diff_patchid_print_callback_to_buf
,
381 if ((error
= (flush_hunk(&args
.result
, &args
.ctx
))) < 0)
384 git_oid_cpy(out
, &args
.result
);
387 git_hash_ctx_cleanup(&args
.ctx
);