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.
15 #include "diff_generate.h"
17 #include "git2/version.h"
18 #include "git2/email.h"
20 struct patch_id_args
{
26 GIT_INLINE(const char *) diff_delta__path(const git_diff_delta
*delta
)
28 const char *str
= delta
->old_file
.path
;
31 delta
->status
== GIT_DELTA_ADDED
||
32 delta
->status
== GIT_DELTA_RENAMED
||
33 delta
->status
== GIT_DELTA_COPIED
)
34 str
= delta
->new_file
.path
;
39 int git_diff_delta__cmp(const void *a
, const void *b
)
41 const git_diff_delta
*da
= a
, *db
= b
;
42 int val
= strcmp(diff_delta__path(da
), diff_delta__path(db
));
43 return val
? val
: ((int)da
->status
- (int)db
->status
);
46 int git_diff_delta__casecmp(const void *a
, const void *b
)
48 const git_diff_delta
*da
= a
, *db
= b
;
49 int val
= strcasecmp(diff_delta__path(da
), diff_delta__path(db
));
50 return val
? val
: ((int)da
->status
- (int)db
->status
);
53 int git_diff__entry_cmp(const void *a
, const void *b
)
55 const git_index_entry
*entry_a
= a
;
56 const git_index_entry
*entry_b
= b
;
58 return strcmp(entry_a
->path
, entry_b
->path
);
61 int git_diff__entry_icmp(const void *a
, const void *b
)
63 const git_index_entry
*entry_a
= a
;
64 const git_index_entry
*entry_b
= b
;
66 return strcasecmp(entry_a
->path
, entry_b
->path
);
69 void git_diff_free(git_diff
*diff
)
74 GIT_REFCOUNT_DEC(diff
, diff
->free_fn
);
77 void git_diff_addref(git_diff
*diff
)
79 GIT_REFCOUNT_INC(diff
);
82 size_t git_diff_num_deltas(const git_diff
*diff
)
85 return diff
->deltas
.length
;
88 size_t git_diff_num_deltas_of_type(const git_diff
*diff
, git_delta_t type
)
91 const git_diff_delta
*delta
;
95 git_vector_foreach(&diff
->deltas
, i
, delta
) {
96 count
+= (delta
->status
== type
);
102 const git_diff_delta
*git_diff_get_delta(const git_diff
*diff
, size_t idx
)
104 GIT_ASSERT_ARG_WITH_RETVAL(diff
, NULL
);
105 return git_vector_get(&diff
->deltas
, idx
);
108 int git_diff_is_sorted_icase(const git_diff
*diff
)
110 return (diff
->opts
.flags
& GIT_DIFF_IGNORE_CASE
) != 0;
113 int git_diff_get_perfdata(git_diff_perfdata
*out
, const git_diff
*diff
)
116 GIT_ERROR_CHECK_VERSION(out
, GIT_DIFF_PERFDATA_VERSION
, "git_diff_perfdata");
117 out
->stat_calls
= diff
->perf
.stat_calls
;
118 out
->oid_calculations
= diff
->perf
.oid_calculations
;
122 int git_diff_foreach(
124 git_diff_file_cb file_cb
,
125 git_diff_binary_cb binary_cb
,
126 git_diff_hunk_cb hunk_cb
,
127 git_diff_line_cb data_cb
,
131 git_diff_delta
*delta
;
134 GIT_ASSERT_ARG(diff
);
136 git_vector_foreach(&diff
->deltas
, idx
, delta
) {
139 /* check flags against patch status */
140 if (git_diff_delta__should_skip(&diff
->opts
, delta
))
143 if ((error
= git_patch_from_diff(&patch
, diff
, idx
)) != 0)
146 error
= git_patch__invoke_callbacks(patch
, file_cb
, binary_cb
,
147 hunk_cb
, data_cb
, payload
);
148 git_patch_free(patch
);
157 #ifndef GIT_DEPRECATE_HARD
159 int git_diff_format_email(
162 const git_diff_format_email_options
*opts
)
164 git_email_create_options email_create_opts
= GIT_EMAIL_CREATE_OPTIONS_INIT
;
168 GIT_ASSERT_ARG(diff
);
169 GIT_ASSERT_ARG(opts
&& opts
->summary
&& opts
->id
&& opts
->author
);
171 GIT_ERROR_CHECK_VERSION(opts
,
172 GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION
,
173 "git_format_email_options");
175 if ((opts
->flags
& GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER
) != 0)
176 email_create_opts
.subject_prefix
= "";
179 error
= git_email__append_from_diff(out
, diff
, opts
->patch_no
,
180 opts
->total_patches
, opts
->id
, opts
->summary
, opts
->body
,
181 opts
->author
, &email_create_opts
);
186 int git_diff_commit_as_email(
188 git_repository
*repo
,
191 size_t total_patches
,
193 const git_diff_options
*diff_opts
)
195 git_diff
*diff
= NULL
;
196 git_email_create_options opts
= GIT_EMAIL_CREATE_OPTIONS_INIT
;
197 const git_oid
*commit_id
;
198 const char *summary
, *body
;
199 const git_signature
*author
;
203 GIT_ASSERT_ARG(repo
);
204 GIT_ASSERT_ARG(commit
);
206 commit_id
= git_commit_id(commit
);
207 summary
= git_commit_summary(commit
);
208 body
= git_commit_body(commit
);
209 author
= git_commit_author(commit
);
211 if ((flags
& GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER
) != 0)
212 opts
.subject_prefix
= "";
214 if ((error
= git_diff__commit(&diff
, repo
, commit
, diff_opts
)) < 0)
217 error
= git_email_create_from_diff(out
, diff
, patch_no
, total_patches
, commit_id
, summary
, body
, author
, &opts
);
223 int git_diff_init_options(git_diff_options
*opts
, unsigned int version
)
225 return git_diff_options_init(opts
, version
);
228 int git_diff_find_init_options(
229 git_diff_find_options
*opts
, unsigned int version
)
231 return git_diff_find_options_init(opts
, version
);
234 int git_diff_format_email_options_init(
235 git_diff_format_email_options
*opts
, unsigned int version
)
237 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
238 opts
, version
, git_diff_format_email_options
,
239 GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT
);
243 int git_diff_format_email_init_options(
244 git_diff_format_email_options
*opts
, unsigned int version
)
246 return git_diff_format_email_options_init(opts
, version
);
251 int git_diff_options_init(git_diff_options
*opts
, unsigned int version
)
253 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
254 opts
, version
, git_diff_options
, GIT_DIFF_OPTIONS_INIT
);
258 int git_diff_find_options_init(
259 git_diff_find_options
*opts
, unsigned int version
)
261 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
262 opts
, version
, git_diff_find_options
, GIT_DIFF_FIND_OPTIONS_INIT
);
266 static int flush_hunk(git_oid
*result
, git_hash_ctx
*ctx
)
269 unsigned short carry
= 0;
272 if ((error
= git_hash_final(&hash
, ctx
)) < 0 ||
273 (error
= git_hash_init(ctx
)) < 0)
276 for (i
= 0; i
< GIT_OID_RAWSZ
; i
++) {
277 carry
+= result
->id
[i
] + hash
.id
[i
];
278 result
->id
[i
] = (unsigned char)carry
;
285 static void strip_spaces(git_buf
*buf
)
287 char *src
= buf
->ptr
, *dst
= buf
->ptr
;
291 while ((c
= *src
++) != '\0') {
292 if (!git__isspace(c
)) {
298 git_buf_truncate(buf
, len
);
301 static int diff_patchid_print_callback_to_buf(
302 const git_diff_delta
*delta
,
303 const git_diff_hunk
*hunk
,
304 const git_diff_line
*line
,
307 struct patch_id_args
*args
= (struct patch_id_args
*) payload
;
308 git_buf buf
= GIT_BUF_INIT
;
311 if (line
->origin
== GIT_DIFF_LINE_CONTEXT_EOFNL
||
312 line
->origin
== GIT_DIFF_LINE_ADD_EOFNL
||
313 line
->origin
== GIT_DIFF_LINE_DEL_EOFNL
)
316 if ((error
= git_diff_print_callback__to_buf(delta
, hunk
,
322 if (line
->origin
== GIT_DIFF_LINE_FILE_HDR
&&
324 (error
= flush_hunk(&args
->result
, &args
->ctx
) < 0))
327 if ((error
= git_hash_update(&args
->ctx
, buf
.ptr
, buf
.size
)) < 0)
330 if (line
->origin
== GIT_DIFF_LINE_FILE_HDR
&& args
->first_file
)
331 args
->first_file
= 0;
334 git_buf_dispose(&buf
);
338 int git_diff_patchid_options_init(git_diff_patchid_options
*opts
, unsigned int version
)
340 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
341 opts
, version
, git_diff_patchid_options
, GIT_DIFF_PATCHID_OPTIONS_INIT
);
345 int git_diff_patchid(git_oid
*out
, git_diff
*diff
, git_diff_patchid_options
*opts
)
347 struct patch_id_args args
;
350 GIT_ERROR_CHECK_VERSION(
351 opts
, GIT_DIFF_PATCHID_OPTIONS_VERSION
, "git_diff_patchid_options");
353 memset(&args
, 0, sizeof(args
));
355 if ((error
= git_hash_ctx_init(&args
.ctx
)) < 0)
358 if ((error
= git_diff_print(diff
,
359 GIT_DIFF_FORMAT_PATCH_ID
,
360 diff_patchid_print_callback_to_buf
,
364 if ((error
= (flush_hunk(&args
.result
, &args
.ctx
))) < 0)
367 git_oid_cpy(out
, &args
.result
);
370 git_hash_ctx_cleanup(&args
.ctx
);