]>
Commit | Line | Data |
---|---|---|
cd33323b | 1 | /* |
74fa4bfa | 2 | * Copyright (C) 2012 the libgit2 contributors |
cd33323b RB |
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 | */ | |
cd33323b | 7 | #include "common.h" |
65b09b1d | 8 | #include "diff.h" |
74fa4bfa | 9 | #include "fileops.h" |
95dfb031 | 10 | #include "config.h" |
14a513e0 | 11 | #include "attr_file.h" |
60b9d3fc | 12 | #include "filter.h" |
cd33323b | 13 | |
41a82592 RB |
14 | static char *diff_prefix_from_pathspec(const git_strarray *pathspec) |
15 | { | |
16 | git_buf prefix = GIT_BUF_INIT; | |
17 | const char *scan; | |
18 | ||
19 | if (git_buf_common_prefix(&prefix, pathspec) < 0) | |
20 | return NULL; | |
21 | ||
22 | /* diff prefix will only be leading non-wildcards */ | |
ffbc689c | 23 | for (scan = prefix.ptr; *scan; ++scan) { |
24 | if (git__iswildcard(*scan) && | |
25 | (scan == prefix.ptr || (*(scan - 1) != '\\'))) | |
26 | break; | |
27 | } | |
41a82592 RB |
28 | git_buf_truncate(&prefix, scan - prefix.ptr); |
29 | ||
ffbc689c | 30 | if (prefix.size <= 0) { |
31 | git_buf_free(&prefix); | |
32 | return NULL; | |
33 | } | |
41a82592 | 34 | |
ffbc689c | 35 | git_buf_unescape(&prefix); |
36 | ||
37 | return git_buf_detach(&prefix); | |
41a82592 RB |
38 | } |
39 | ||
14a513e0 | 40 | static bool diff_pathspec_is_interesting(const git_strarray *pathspec) |
3a437590 | 41 | { |
14a513e0 | 42 | const char *str; |
3a437590 | 43 | |
14a513e0 RB |
44 | if (pathspec == NULL || pathspec->count == 0) |
45 | return false; | |
46 | if (pathspec->count > 1) | |
47 | return true; | |
48 | ||
49 | str = pathspec->strings[0]; | |
50 | if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.'))) | |
51 | return false; | |
52 | return true; | |
53 | } | |
54 | ||
55 | static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path) | |
56 | { | |
57 | unsigned int i; | |
58 | git_attr_fnmatch *match; | |
59 | ||
60 | if (!diff->pathspec.length) | |
61 | return true; | |
62 | ||
63 | git_vector_foreach(&diff->pathspec, i, match) { | |
a1773f9d | 64 | int result = strcmp(match->pattern, path) ? FNM_NOMATCH : 0; |
60b9d3fc RB |
65 | |
66 | if (((diff->opts.flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH) == 0) && | |
a1773f9d | 67 | result == FNM_NOMATCH) |
ffbc689c | 68 | result = p_fnmatch(match->pattern, path, 0); |
14a513e0 RB |
69 | |
70 | /* if we didn't match, look for exact dirname prefix match */ | |
3fbcac89 | 71 | if (result == FNM_NOMATCH && |
14a513e0 RB |
72 | (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 && |
73 | strncmp(path, match->pattern, match->length) == 0 && | |
74 | path[match->length] == '/') | |
75 | result = 0; | |
3a437590 | 76 | |
14a513e0 RB |
77 | if (result == 0) |
78 | return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true; | |
74fa4bfa | 79 | } |
3a437590 | 80 | |
14a513e0 | 81 | return false; |
3a437590 RB |
82 | } |
83 | ||
74fa4bfa | 84 | static git_diff_delta *diff_delta__alloc( |
a2e895be | 85 | git_diff_list *diff, |
e1bcc191 | 86 | git_delta_t status, |
74fa4bfa | 87 | const char *path) |
3a437590 | 88 | { |
a2e895be | 89 | git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta)); |
a2e895be | 90 | if (!delta) |
74fa4bfa | 91 | return NULL; |
3a437590 | 92 | |
16b83019 | 93 | delta->old_file.path = git_pool_strdup(&diff->pool, path); |
2de60205 | 94 | if (delta->old_file.path == NULL) { |
a2e895be | 95 | git__free(delta); |
74fa4bfa | 96 | return NULL; |
a2e895be | 97 | } |
40879fac | 98 | |
2de60205 | 99 | delta->new_file.path = delta->old_file.path; |
a2e895be | 100 | |
74fa4bfa RB |
101 | if (diff->opts.flags & GIT_DIFF_REVERSE) { |
102 | switch (status) { | |
e1bcc191 RB |
103 | case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break; |
104 | case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break; | |
74fa4bfa RB |
105 | default: break; /* leave other status values alone */ |
106 | } | |
107 | } | |
a2e895be RB |
108 | delta->status = status; |
109 | ||
74fa4bfa RB |
110 | return delta; |
111 | } | |
112 | ||
19fa2bc1 RB |
113 | static git_diff_delta *diff_delta__dup( |
114 | const git_diff_delta *d, git_pool *pool) | |
74fa4bfa RB |
115 | { |
116 | git_diff_delta *delta = git__malloc(sizeof(git_diff_delta)); | |
117 | if (!delta) | |
118 | return NULL; | |
119 | ||
120 | memcpy(delta, d, sizeof(git_diff_delta)); | |
121 | ||
16b83019 RB |
122 | delta->old_file.path = git_pool_strdup(pool, d->old_file.path); |
123 | if (delta->old_file.path == NULL) | |
19fa2bc1 | 124 | goto fail; |
a2e895be | 125 | |
2de60205 | 126 | if (d->new_file.path != d->old_file.path) { |
16b83019 RB |
127 | delta->new_file.path = git_pool_strdup(pool, d->new_file.path); |
128 | if (delta->new_file.path == NULL) | |
19fa2bc1 | 129 | goto fail; |
74fa4bfa | 130 | } else { |
2de60205 | 131 | delta->new_file.path = delta->old_file.path; |
74fa4bfa | 132 | } |
3a437590 | 133 | |
74fa4bfa | 134 | return delta; |
19fa2bc1 RB |
135 | |
136 | fail: | |
137 | git__free(delta); | |
138 | return NULL; | |
a2e895be RB |
139 | } |
140 | ||
74fa4bfa | 141 | static git_diff_delta *diff_delta__merge_like_cgit( |
19fa2bc1 | 142 | const git_diff_delta *a, const git_diff_delta *b, git_pool *pool) |
a2e895be | 143 | { |
145e696b | 144 | git_diff_delta *dup; |
a2e895be | 145 | |
74fa4bfa RB |
146 | /* Emulate C git for merging two diffs (a la 'git diff <sha>'). |
147 | * | |
148 | * When C git does a diff between the work dir and a tree, it actually | |
149 | * diffs with the index but uses the workdir contents. This emulates | |
150 | * those choices so we can emulate the type of diff. | |
145e696b RB |
151 | * |
152 | * We have three file descriptions here, let's call them: | |
153 | * f1 = a->old_file | |
154 | * f2 = a->new_file AND b->old_file | |
155 | * f3 = b->new_file | |
74fa4bfa | 156 | */ |
145e696b RB |
157 | |
158 | /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */ | |
159 | if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED) | |
160 | return diff_delta__dup(a, pool); | |
161 | ||
162 | /* otherwise, base this diff on the 'b' diff */ | |
163 | if ((dup = diff_delta__dup(b, pool)) == NULL) | |
164 | return NULL; | |
165 | ||
166 | /* If 'a' status is uninteresting, then we're done */ | |
167 | if (a->status == GIT_DELTA_UNMODIFIED) | |
168 | return dup; | |
169 | ||
170 | assert(a->status != GIT_DELTA_UNMODIFIED); | |
171 | assert(b->status != GIT_DELTA_UNMODIFIED); | |
172 | ||
173 | /* A cgit exception is that the diff of a file that is only in the | |
174 | * index (i.e. not in HEAD nor workdir) is given as empty. | |
175 | */ | |
176 | if (dup->status == GIT_DELTA_DELETED) { | |
177 | if (a->status == GIT_DELTA_ADDED) | |
e1bcc191 | 178 | dup->status = GIT_DELTA_UNMODIFIED; |
145e696b RB |
179 | /* else don't overwrite DELETE status */ |
180 | } else { | |
181 | dup->status = a->status; | |
74fa4bfa | 182 | } |
145e696b RB |
183 | |
184 | git_oid_cpy(&dup->old_file.oid, &a->old_file.oid); | |
185 | dup->old_file.mode = a->old_file.mode; | |
186 | dup->old_file.size = a->old_file.size; | |
187 | dup->old_file.flags = a->old_file.flags; | |
3a437590 | 188 | |
74fa4bfa | 189 | return dup; |
3a437590 RB |
190 | } |
191 | ||
74fa4bfa RB |
192 | static int diff_delta__from_one( |
193 | git_diff_list *diff, | |
e1bcc191 | 194 | git_delta_t status, |
74fa4bfa | 195 | const git_index_entry *entry) |
3a437590 | 196 | { |
66142ae0 RB |
197 | git_diff_delta *delta; |
198 | ||
199 | if (status == GIT_DELTA_IGNORED && | |
200 | (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0) | |
201 | return 0; | |
202 | ||
203 | if (status == GIT_DELTA_UNTRACKED && | |
204 | (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) | |
205 | return 0; | |
206 | ||
14a513e0 RB |
207 | if (!diff_path_matches_pathspec(diff, entry->path)) |
208 | return 0; | |
209 | ||
66142ae0 | 210 | delta = diff_delta__alloc(diff, status, entry->path); |
ae9e29fd | 211 | GITERR_CHECK_ALLOC(delta); |
3a437590 | 212 | |
74fa4bfa | 213 | /* This fn is just for single-sided diffs */ |
e1bcc191 | 214 | assert(status != GIT_DELTA_MODIFIED); |
3a437590 | 215 | |
e1bcc191 | 216 | if (delta->status == GIT_DELTA_DELETED) { |
2de60205 RB |
217 | delta->old_file.mode = entry->mode; |
218 | delta->old_file.size = entry->file_size; | |
219 | git_oid_cpy(&delta->old_file.oid, &entry->oid); | |
74fa4bfa | 220 | } else /* ADDED, IGNORED, UNTRACKED */ { |
2de60205 RB |
221 | delta->new_file.mode = entry->mode; |
222 | delta->new_file.size = entry->file_size; | |
223 | git_oid_cpy(&delta->new_file.oid, &entry->oid); | |
74fa4bfa | 224 | } |
3a437590 | 225 | |
2de60205 RB |
226 | delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID; |
227 | delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID; | |
3a437590 | 228 | |
ae9e29fd | 229 | if (git_vector_insert(&diff->deltas, delta) < 0) { |
19fa2bc1 | 230 | git__free(delta); |
ae9e29fd RB |
231 | return -1; |
232 | } | |
3a437590 | 233 | |
ae9e29fd | 234 | return 0; |
3a437590 RB |
235 | } |
236 | ||
74fa4bfa RB |
237 | static int diff_delta__from_two( |
238 | git_diff_list *diff, | |
e1bcc191 | 239 | git_delta_t status, |
2de60205 | 240 | const git_index_entry *old_entry, |
0abd7244 | 241 | uint32_t old_mode, |
2de60205 | 242 | const git_index_entry *new_entry, |
0abd7244 | 243 | uint32_t new_mode, |
74fa4bfa | 244 | git_oid *new_oid) |
65b09b1d | 245 | { |
74fa4bfa | 246 | git_diff_delta *delta; |
65b09b1d | 247 | |
66142ae0 RB |
248 | if (status == GIT_DELTA_UNMODIFIED && |
249 | (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) | |
250 | return 0; | |
251 | ||
74fa4bfa | 252 | if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) { |
0abd7244 RB |
253 | uint32_t temp_mode = old_mode; |
254 | const git_index_entry *temp_entry = old_entry; | |
2de60205 | 255 | old_entry = new_entry; |
0abd7244 RB |
256 | new_entry = temp_entry; |
257 | old_mode = new_mode; | |
258 | new_mode = temp_mode; | |
74fa4bfa | 259 | } |
65b09b1d | 260 | |
2de60205 | 261 | delta = diff_delta__alloc(diff, status, old_entry->path); |
ae9e29fd | 262 | GITERR_CHECK_ALLOC(delta); |
74fa4bfa | 263 | |
2de60205 | 264 | git_oid_cpy(&delta->old_file.oid, &old_entry->oid); |
60b9d3fc RB |
265 | delta->old_file.size = old_entry->file_size; |
266 | delta->old_file.mode = old_mode; | |
2de60205 | 267 | delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID; |
74fa4bfa | 268 | |
5f69a31f | 269 | git_oid_cpy(&delta->new_file.oid, &new_entry->oid); |
60b9d3fc RB |
270 | delta->new_file.size = new_entry->file_size; |
271 | delta->new_file.mode = new_mode; | |
5f69a31f RB |
272 | |
273 | if (new_oid) { | |
274 | if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) | |
275 | git_oid_cpy(&delta->old_file.oid, new_oid); | |
276 | else | |
277 | git_oid_cpy(&delta->new_file.oid, new_oid); | |
278 | } | |
279 | ||
2de60205 RB |
280 | if (new_oid || !git_oid_iszero(&new_entry->oid)) |
281 | delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID; | |
74fa4bfa | 282 | |
ae9e29fd | 283 | if (git_vector_insert(&diff->deltas, delta) < 0) { |
19fa2bc1 | 284 | git__free(delta); |
ae9e29fd RB |
285 | return -1; |
286 | } | |
3a437590 | 287 | |
ae9e29fd | 288 | return 0; |
65b09b1d RB |
289 | } |
290 | ||
19fa2bc1 | 291 | static char *diff_strdup_prefix(git_pool *pool, const char *prefix) |
a2e895be RB |
292 | { |
293 | size_t len = strlen(prefix); | |
19fa2bc1 RB |
294 | |
295 | /* append '/' at end if needed */ | |
296 | if (len > 0 && prefix[len - 1] != '/') | |
297 | return git_pool_strcat(pool, prefix, "/"); | |
298 | else | |
299 | return git_pool_strndup(pool, prefix, len + 1); | |
a2e895be RB |
300 | } |
301 | ||
74fa4bfa RB |
302 | static int diff_delta__cmp(const void *a, const void *b) |
303 | { | |
304 | const git_diff_delta *da = a, *db = b; | |
2de60205 | 305 | int val = strcmp(da->old_file.path, db->old_file.path); |
74fa4bfa RB |
306 | return val ? val : ((int)da->status - (int)db->status); |
307 | } | |
308 | ||
95dfb031 RB |
309 | static int config_bool(git_config *cfg, const char *name, int defvalue) |
310 | { | |
311 | int val = defvalue; | |
29e948de VM |
312 | |
313 | if (git_config_get_bool(&val, cfg, name) < 0) | |
95dfb031 | 314 | giterr_clear(); |
29e948de | 315 | |
95dfb031 RB |
316 | return val; |
317 | } | |
318 | ||
65b09b1d RB |
319 | static git_diff_list *git_diff_list_alloc( |
320 | git_repository *repo, const git_diff_options *opts) | |
321 | { | |
95dfb031 | 322 | git_config *cfg; |
14a513e0 | 323 | size_t i; |
65b09b1d | 324 | git_diff_list *diff = git__calloc(1, sizeof(git_diff_list)); |
a2e895be RB |
325 | if (diff == NULL) |
326 | return NULL; | |
327 | ||
f335ecd6 | 328 | GIT_REFCOUNT_INC(diff); |
a2e895be | 329 | diff->repo = repo; |
a2e895be | 330 | |
19fa2bc1 RB |
331 | if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 || |
332 | git_pool_init(&diff->pool, 1, 0) < 0) | |
333 | goto fail; | |
334 | ||
95dfb031 | 335 | /* load config values that affect diff behavior */ |
7784bcbb | 336 | if (git_repository_config__weakptr(&cfg, repo) < 0) |
95dfb031 RB |
337 | goto fail; |
338 | if (config_bool(cfg, "core.symlinks", 1)) | |
339 | diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS; | |
340 | if (config_bool(cfg, "core.ignorestat", 0)) | |
341 | diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED; | |
342 | if (config_bool(cfg, "core.filemode", 1)) | |
0abd7244 | 343 | diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS; |
95dfb031 RB |
344 | if (config_bool(cfg, "core.trustctime", 1)) |
345 | diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME; | |
346 | /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ | |
95dfb031 | 347 | |
a2e895be RB |
348 | if (opts == NULL) |
349 | return diff; | |
350 | ||
351 | memcpy(&diff->opts, opts, sizeof(git_diff_options)); | |
14a513e0 | 352 | memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec)); |
a2e895be | 353 | |
16b83019 | 354 | diff->opts.old_prefix = diff_strdup_prefix(&diff->pool, |
2de60205 | 355 | opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT); |
16b83019 | 356 | diff->opts.new_prefix = diff_strdup_prefix(&diff->pool, |
2de60205 | 357 | opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT); |
74fa4bfa | 358 | |
16b83019 | 359 | if (!diff->opts.old_prefix || !diff->opts.new_prefix) |
95dfb031 | 360 | goto fail; |
74fa4bfa | 361 | |
a2e895be | 362 | if (diff->opts.flags & GIT_DIFF_REVERSE) { |
2de60205 RB |
363 | char *swap = diff->opts.old_prefix; |
364 | diff->opts.old_prefix = diff->opts.new_prefix; | |
365 | diff->opts.new_prefix = swap; | |
65b09b1d | 366 | } |
a2e895be | 367 | |
14a513e0 RB |
368 | /* only copy pathspec if it is "interesting" so we can test |
369 | * diff->pathspec.length > 0 to know if it is worth calling | |
370 | * fnmatch as we iterate. | |
371 | */ | |
372 | if (!diff_pathspec_is_interesting(&opts->pathspec)) | |
373 | return diff; | |
74fa4bfa | 374 | |
821f6bc7 RB |
375 | if (git_vector_init( |
376 | &diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0) | |
14a513e0 RB |
377 | goto fail; |
378 | ||
379 | for (i = 0; i < opts->pathspec.count; ++i) { | |
380 | int ret; | |
381 | const char *pattern = opts->pathspec.strings[i]; | |
19fa2bc1 | 382 | git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch)); |
14a513e0 RB |
383 | if (!match) |
384 | goto fail; | |
2a99df69 | 385 | match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE; |
19fa2bc1 | 386 | ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern); |
904b67e6 | 387 | if (ret == GIT_ENOTFOUND) { |
14a513e0 RB |
388 | git__free(match); |
389 | continue; | |
390 | } else if (ret < 0) | |
391 | goto fail; | |
392 | ||
393 | if (git_vector_insert(&diff->pathspec, match) < 0) | |
394 | goto fail; | |
395 | } | |
a2e895be | 396 | |
65b09b1d | 397 | return diff; |
95dfb031 RB |
398 | |
399 | fail: | |
14a513e0 | 400 | git_diff_list_free(diff); |
95dfb031 | 401 | return NULL; |
65b09b1d RB |
402 | } |
403 | ||
f335ecd6 | 404 | static void diff_list_free(git_diff_list *diff) |
65b09b1d | 405 | { |
3a437590 | 406 | git_diff_delta *delta; |
14a513e0 | 407 | git_attr_fnmatch *match; |
3a437590 RB |
408 | unsigned int i; |
409 | ||
74fa4bfa | 410 | git_vector_foreach(&diff->deltas, i, delta) { |
19fa2bc1 | 411 | git__free(delta); |
74fa4bfa | 412 | diff->deltas.contents[i] = NULL; |
a2e895be | 413 | } |
74fa4bfa | 414 | git_vector_free(&diff->deltas); |
14a513e0 RB |
415 | |
416 | git_vector_foreach(&diff->pathspec, i, match) { | |
19fa2bc1 RB |
417 | git__free(match); |
418 | diff->pathspec.contents[i] = NULL; | |
14a513e0 RB |
419 | } |
420 | git_vector_free(&diff->pathspec); | |
421 | ||
19fa2bc1 | 422 | git_pool_clear(&diff->pool); |
65b09b1d RB |
423 | git__free(diff); |
424 | } | |
425 | ||
f335ecd6 RB |
426 | void git_diff_list_free(git_diff_list *diff) |
427 | { | |
428 | if (!diff) | |
429 | return; | |
430 | ||
431 | GIT_REFCOUNT_DEC(diff, diff_list_free); | |
432 | } | |
433 | ||
5f69a31f RB |
434 | void git_diff_list_addref(git_diff_list *diff) |
435 | { | |
436 | GIT_REFCOUNT_INC(diff); | |
437 | } | |
438 | ||
74fa4bfa | 439 | static int oid_for_workdir_item( |
65b09b1d | 440 | git_repository *repo, |
74fa4bfa RB |
441 | const git_index_entry *item, |
442 | git_oid *oid) | |
65b09b1d | 443 | { |
ae9e29fd | 444 | int result; |
74fa4bfa | 445 | git_buf full_path = GIT_BUF_INIT; |
a2e895be | 446 | |
ae9e29fd RB |
447 | if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0) |
448 | return -1; | |
a2e895be | 449 | |
ae9e29fd | 450 | /* calculate OID for file if possible*/ |
74fa4bfa | 451 | if (S_ISLNK(item->mode)) |
ae9e29fd RB |
452 | result = git_odb__hashlink(oid, full_path.ptr); |
453 | else if (!git__is_sizet(item->file_size)) { | |
454 | giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); | |
455 | result = -1; | |
456 | } else { | |
60b9d3fc RB |
457 | git_vector filters = GIT_VECTOR_INIT; |
458 | ||
459 | result = git_filters_load( | |
460 | &filters, repo, item->path, GIT_FILTER_TO_ODB); | |
461 | if (result >= 0) { | |
462 | int fd = git_futils_open_ro(full_path.ptr); | |
463 | if (fd < 0) | |
464 | result = fd; | |
465 | else { | |
466 | result = git_odb__hashfd_filtered( | |
467 | oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB, &filters); | |
468 | p_close(fd); | |
469 | } | |
74fa4bfa | 470 | } |
60b9d3fc RB |
471 | |
472 | git_filters_free(&filters); | |
a2e895be | 473 | } |
a2e895be | 474 | |
74fa4bfa | 475 | git_buf_free(&full_path); |
a2e895be | 476 | |
ae9e29fd | 477 | return result; |
a2e895be RB |
478 | } |
479 | ||
0abd7244 | 480 | #define MODE_BITS_MASK 0000777 |
95dfb031 | 481 | |
74fa4bfa | 482 | static int maybe_modified( |
2de60205 | 483 | git_iterator *old_iter, |
74fa4bfa | 484 | const git_index_entry *oitem, |
2de60205 | 485 | git_iterator *new_iter, |
74fa4bfa RB |
486 | const git_index_entry *nitem, |
487 | git_diff_list *diff) | |
e47329b6 | 488 | { |
74fa4bfa | 489 | git_oid noid, *use_noid = NULL; |
66142ae0 | 490 | git_delta_t status = GIT_DELTA_MODIFIED; |
95dfb031 RB |
491 | unsigned int omode = oitem->mode; |
492 | unsigned int nmode = nitem->mode; | |
e47329b6 | 493 | |
2de60205 | 494 | GIT_UNUSED(old_iter); |
e47329b6 | 495 | |
14a513e0 RB |
496 | if (!diff_path_matches_pathspec(diff, oitem->path)) |
497 | return 0; | |
498 | ||
da825c92 | 499 | /* on platforms with no symlinks, preserve mode of existing symlinks */ |
95dfb031 | 500 | if (S_ISLNK(omode) && S_ISREG(nmode) && |
5fdc41e7 RB |
501 | !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS) && |
502 | new_iter->type == GIT_ITERATOR_WORKDIR) | |
da825c92 | 503 | nmode = omode; |
e47329b6 | 504 | |
0abd7244 RB |
505 | /* on platforms with no execmode, just preserve old mode */ |
506 | if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) && | |
507 | (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) && | |
508 | new_iter->type == GIT_ITERATOR_WORKDIR) | |
509 | nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK); | |
e47329b6 | 510 | |
0abd7244 | 511 | /* support "assume unchanged" (poorly, b/c we still stat everything) */ |
95dfb031 RB |
512 | if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0) |
513 | status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ? | |
514 | GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED; | |
515 | ||
516 | /* support "skip worktree" index bit */ | |
517 | else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0) | |
66142ae0 | 518 | status = GIT_DELTA_UNMODIFIED; |
e47329b6 | 519 | |
66142ae0 | 520 | /* if basic type of file changed, then split into delete and add */ |
95dfb031 | 521 | else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { |
ae9e29fd RB |
522 | if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || |
523 | diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) | |
524 | return -1; | |
525 | return 0; | |
e47329b6 RB |
526 | } |
527 | ||
66142ae0 RB |
528 | /* if oids and modes match, then file is unmodified */ |
529 | else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 && | |
95dfb031 | 530 | omode == nmode) |
66142ae0 | 531 | status = GIT_DELTA_UNMODIFIED; |
e47329b6 | 532 | |
5f69a31f RB |
533 | /* if we have an unknown OID and a workdir iterator, then check some |
534 | * circumstances that can accelerate things or need special handling | |
0abd7244 | 535 | */ |
5f69a31f RB |
536 | else if (git_oid_iszero(&nitem->oid) && |
537 | new_iter->type == GIT_ITERATOR_WORKDIR) | |
0abd7244 | 538 | { |
95dfb031 | 539 | /* TODO: add check against index file st_mtime to avoid racy-git */ |
e47329b6 | 540 | |
5f69a31f RB |
541 | /* if the stat data looks exactly alike, then assume the same */ |
542 | if (omode == nmode && | |
543 | oitem->file_size == nitem->file_size && | |
95dfb031 RB |
544 | (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) || |
545 | (oitem->ctime.seconds == nitem->ctime.seconds)) && | |
74fa4bfa | 546 | oitem->mtime.seconds == nitem->mtime.seconds && |
95dfb031 RB |
547 | (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) || |
548 | (oitem->dev == nitem->dev)) && | |
74fa4bfa RB |
549 | oitem->ino == nitem->ino && |
550 | oitem->uid == nitem->uid && | |
551 | oitem->gid == nitem->gid) | |
66142ae0 RB |
552 | status = GIT_DELTA_UNMODIFIED; |
553 | ||
95dfb031 RB |
554 | else if (S_ISGITLINK(nmode)) { |
555 | git_submodule *sub; | |
556 | ||
557 | if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0) | |
558 | status = GIT_DELTA_UNMODIFIED; | |
559 | else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0) | |
560 | return -1; | |
aa13bf05 | 561 | else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL) |
95dfb031 RB |
562 | status = GIT_DELTA_UNMODIFIED; |
563 | else { | |
7e57d250 SC |
564 | unsigned int sm_status = 0; |
565 | if (git_submodule_status(&sm_status, sub) < 0) | |
566 | return -1; | |
567 | status = GIT_SUBMODULE_STATUS_IS_UNMODIFIED(sm_status) | |
568 | ? GIT_DELTA_UNMODIFIED : GIT_DELTA_MODIFIED; | |
95dfb031 RB |
569 | } |
570 | } | |
5f69a31f | 571 | } |
e47329b6 | 572 | |
5f69a31f RB |
573 | /* if we got here and decided that the files are modified, but we |
574 | * haven't calculated the OID of the new item, then calculate it now | |
575 | */ | |
576 | if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) { | |
577 | if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0) | |
ae9e29fd | 578 | return -1; |
5f69a31f | 579 | else if (omode == nmode && git_oid_equal(&oitem->oid, &noid)) |
66142ae0 | 580 | status = GIT_DELTA_UNMODIFIED; |
e47329b6 | 581 | |
74fa4bfa RB |
582 | /* store calculated oid so we don't have to recalc later */ |
583 | use_noid = &noid; | |
e47329b6 RB |
584 | } |
585 | ||
0abd7244 RB |
586 | return diff_delta__from_two( |
587 | diff, status, oitem, omode, nitem, nmode, use_noid); | |
e47329b6 RB |
588 | } |
589 | ||
ec40b7f9 PK |
590 | static int git_index_entry_cmp_case(const void *a, const void *b) |
591 | { | |
592 | const git_index_entry *entry_a = a; | |
593 | const git_index_entry *entry_b = b; | |
594 | ||
595 | return strcmp(entry_a->path, entry_b->path); | |
596 | } | |
597 | ||
598 | static int git_index_entry_cmp_icase(const void *a, const void *b) | |
599 | { | |
600 | const git_index_entry *entry_a = a; | |
601 | const git_index_entry *entry_b = b; | |
602 | ||
603 | return strcasecmp(entry_a->path, entry_b->path); | |
604 | } | |
605 | ||
74fa4bfa | 606 | static int diff_from_iterators( |
e47329b6 | 607 | git_repository *repo, |
74fa4bfa | 608 | const git_diff_options *opts, /**< can be NULL for defaults */ |
2de60205 RB |
609 | git_iterator *old_iter, |
610 | git_iterator *new_iter, | |
74fa4bfa | 611 | git_diff_list **diff_ptr) |
e47329b6 | 612 | { |
74fa4bfa | 613 | const git_index_entry *oitem, *nitem; |
b709e951 | 614 | git_buf ignore_prefix = GIT_BUF_INIT; |
74fa4bfa | 615 | git_diff_list *diff = git_diff_list_alloc(repo, opts); |
ec40b7f9 PK |
616 | git_vector_cmp entry_compare; |
617 | ||
ae9e29fd RB |
618 | if (!diff) |
619 | goto fail; | |
e47329b6 | 620 | |
2de60205 RB |
621 | diff->old_src = old_iter->type; |
622 | diff->new_src = new_iter->type; | |
e47329b6 | 623 | |
ec40b7f9 PK |
624 | /* Use case-insensitive compare if either iterator has |
625 | * the ignore_case bit set */ | |
626 | if (!old_iter->ignore_case && !new_iter->ignore_case) { | |
627 | entry_compare = git_index_entry_cmp_case; | |
628 | diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE; | |
629 | } else { | |
630 | entry_compare = git_index_entry_cmp_icase; | |
631 | diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE; | |
632 | ||
633 | /* If one of the iterators doesn't have ignore_case set, | |
634 | * then that's unfortunate because we'll have to spool | |
635 | * its data, sort it icase, and then use that for our | |
636 | * merge join to the other iterator that is icase sorted */ | |
637 | if (!old_iter->ignore_case) { | |
638 | if (git_iterator_spoolandsort(&old_iter, old_iter, git_index_entry_cmp_icase, true) < 0) | |
639 | goto fail; | |
640 | } else if (!new_iter->ignore_case) { | |
641 | if (git_iterator_spoolandsort(&new_iter, new_iter, git_index_entry_cmp_icase, true) < 0) | |
642 | goto fail; | |
643 | } | |
644 | } | |
645 | ||
16b83019 RB |
646 | if (git_iterator_current(old_iter, &oitem) < 0 || |
647 | git_iterator_current(new_iter, &nitem) < 0) | |
ae9e29fd | 648 | goto fail; |
65b09b1d | 649 | |
74fa4bfa | 650 | /* run iterators building diffs */ |
ae9e29fd | 651 | while (oitem || nitem) { |
cd33323b | 652 | |
74fa4bfa | 653 | /* create DELETED records for old items not matched in new */ |
ec40b7f9 | 654 | if (oitem && (!nitem || entry_compare(oitem, nitem) < 0)) { |
ae9e29fd | 655 | if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || |
16b83019 | 656 | git_iterator_advance(old_iter, &oitem) < 0) |
ae9e29fd | 657 | goto fail; |
65b09b1d | 658 | } |
a2e895be | 659 | |
74fa4bfa RB |
660 | /* create ADDED, TRACKED, or IGNORED records for new items not |
661 | * matched in old (and/or descend into directories as needed) | |
662 | */ | |
ec40b7f9 | 663 | else if (nitem && (!oitem || entry_compare(oitem, nitem) > 0)) { |
bd4ca902 | 664 | git_delta_t delta_type = GIT_DELTA_UNTRACKED; |
a2e895be | 665 | |
bd4ca902 | 666 | /* check if contained in ignored parent directory */ |
b709e951 | 667 | if (git_buf_len(&ignore_prefix) && |
ec40b7f9 | 668 | ITERATOR_PREFIXCMP(*old_iter, nitem->path, git_buf_cstr(&ignore_prefix)) == 0) |
bd4ca902 | 669 | delta_type = GIT_DELTA_IGNORED; |
74fa4bfa RB |
670 | |
671 | if (S_ISDIR(nitem->mode)) { | |
bd4ca902 RB |
672 | /* recurse into directory only if there are tracked items in |
673 | * it or if the user requested the contents of untracked | |
674 | * directories and it is not under an ignored directory. | |
4b136a94 | 675 | */ |
ec40b7f9 | 676 | if ((oitem && ITERATOR_PREFIXCMP(*old_iter, oitem->path, nitem->path) == 0) || |
bd4ca902 RB |
677 | (delta_type == GIT_DELTA_UNTRACKED && |
678 | (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0)) | |
4b136a94 | 679 | { |
bd4ca902 RB |
680 | /* if this directory is ignored, remember it as the |
681 | * "ignore_prefix" for processing contained items | |
682 | */ | |
683 | if (delta_type == GIT_DELTA_UNTRACKED && | |
684 | git_iterator_current_is_ignored(new_iter)) | |
b709e951 | 685 | git_buf_sets(&ignore_prefix, nitem->path); |
40879fac | 686 | |
16b83019 | 687 | if (git_iterator_advance_into_directory(new_iter, &nitem) < 0) |
ae9e29fd | 688 | goto fail; |
40879fac | 689 | |
74fa4bfa RB |
690 | continue; |
691 | } | |
65b09b1d | 692 | } |
bd4ca902 RB |
693 | |
694 | /* In core git, the next two "else if" clauses are effectively | |
695 | * reversed -- i.e. when an untracked file contained in an | |
696 | * ignored directory is individually ignored, it shows up as an | |
697 | * ignored file in the diff list, even though other untracked | |
698 | * files in the same directory are skipped completely. | |
699 | * | |
700 | * To me, this is odd. If the directory is ignored and the file | |
701 | * is untracked, we should skip it consistently, regardless of | |
702 | * whether it happens to match a pattern in the ignore file. | |
703 | * | |
704 | * To match the core git behavior, just reverse the following | |
705 | * two "else if" cases so that individual file ignores are | |
706 | * checked before container directory exclusions are used to | |
707 | * skip the file. | |
708 | */ | |
709 | else if (delta_type == GIT_DELTA_IGNORED) { | |
710 | if (git_iterator_advance(new_iter, &nitem) < 0) | |
711 | goto fail; | |
712 | continue; /* ignored parent directory, so skip completely */ | |
713 | } | |
714 | ||
715 | else if (git_iterator_current_is_ignored(new_iter)) | |
e1bcc191 | 716 | delta_type = GIT_DELTA_IGNORED; |
bd4ca902 RB |
717 | |
718 | else if (new_iter->type != GIT_ITERATOR_WORKDIR) | |
719 | delta_type = GIT_DELTA_ADDED; | |
74fa4bfa | 720 | |
ae9e29fd | 721 | if (diff_delta__from_one(diff, delta_type, nitem) < 0 || |
16b83019 | 722 | git_iterator_advance(new_iter, &nitem) < 0) |
ae9e29fd | 723 | goto fail; |
65b09b1d RB |
724 | } |
725 | ||
74fa4bfa RB |
726 | /* otherwise item paths match, so create MODIFIED record |
727 | * (or ADDED and DELETED pair if type changed) | |
a2e895be | 728 | */ |
ae9e29fd | 729 | else { |
f08c60a5 | 730 | assert(oitem && nitem && entry_compare(oitem, nitem) == 0); |
a2e895be | 731 | |
16b83019 RB |
732 | if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 || |
733 | git_iterator_advance(old_iter, &oitem) < 0 || | |
734 | git_iterator_advance(new_iter, &nitem) < 0) | |
ae9e29fd RB |
735 | goto fail; |
736 | } | |
65b09b1d RB |
737 | } |
738 | ||
2de60205 RB |
739 | git_iterator_free(old_iter); |
740 | git_iterator_free(new_iter); | |
b709e951 RB |
741 | git_buf_free(&ignore_prefix); |
742 | ||
74fa4bfa | 743 | *diff_ptr = diff; |
ae9e29fd | 744 | return 0; |
65b09b1d | 745 | |
ae9e29fd | 746 | fail: |
16b83019 RB |
747 | git_iterator_free(old_iter); |
748 | git_iterator_free(new_iter); | |
b709e951 RB |
749 | git_buf_free(&ignore_prefix); |
750 | ||
ae9e29fd RB |
751 | git_diff_list_free(diff); |
752 | *diff_ptr = NULL; | |
753 | return -1; | |
65b09b1d RB |
754 | } |
755 | ||
74fa4bfa RB |
756 | |
757 | int git_diff_tree_to_tree( | |
758 | git_repository *repo, | |
759 | const git_diff_options *opts, /**< can be NULL for defaults */ | |
2de60205 RB |
760 | git_tree *old_tree, |
761 | git_tree *new_tree, | |
74fa4bfa | 762 | git_diff_list **diff) |
65b09b1d | 763 | { |
74fa4bfa | 764 | git_iterator *a = NULL, *b = NULL; |
41a82592 | 765 | char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL; |
3a437590 | 766 | |
2de60205 | 767 | assert(repo && old_tree && new_tree && diff); |
3a437590 | 768 | |
41a82592 RB |
769 | if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 || |
770 | git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0) | |
ae9e29fd | 771 | return -1; |
3a437590 | 772 | |
41a82592 RB |
773 | git__free(prefix); |
774 | ||
74fa4bfa | 775 | return diff_from_iterators(repo, opts, a, b, diff); |
65b09b1d RB |
776 | } |
777 | ||
74fa4bfa RB |
778 | int git_diff_index_to_tree( |
779 | git_repository *repo, | |
780 | const git_diff_options *opts, | |
2de60205 | 781 | git_tree *old_tree, |
74fa4bfa | 782 | git_diff_list **diff) |
65b09b1d | 783 | { |
74fa4bfa | 784 | git_iterator *a = NULL, *b = NULL; |
41a82592 | 785 | char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL; |
3a437590 | 786 | |
7e000ab2 | 787 | assert(repo && diff); |
3a437590 | 788 | |
41a82592 | 789 | if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 || |
1d94a7d0 CMN |
790 | git_iterator_for_index_range(&b, repo, prefix, prefix) < 0) |
791 | goto on_error; | |
3a437590 | 792 | |
41a82592 RB |
793 | git__free(prefix); |
794 | ||
74fa4bfa | 795 | return diff_from_iterators(repo, opts, a, b, diff); |
1d94a7d0 CMN |
796 | |
797 | on_error: | |
798 | git__free(prefix); | |
799 | git_iterator_free(a); | |
800 | return -1; | |
65b09b1d RB |
801 | } |
802 | ||
74fa4bfa RB |
803 | int git_diff_workdir_to_index( |
804 | git_repository *repo, | |
805 | const git_diff_options *opts, | |
806 | git_diff_list **diff) | |
65b09b1d | 807 | { |
74fa4bfa | 808 | git_iterator *a = NULL, *b = NULL; |
41a82592 | 809 | char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL; |
3a437590 | 810 | |
74fa4bfa | 811 | assert(repo && diff); |
3a437590 | 812 | |
41a82592 | 813 | if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 || |
1d94a7d0 CMN |
814 | git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0) |
815 | goto on_error; | |
3a437590 | 816 | |
41a82592 RB |
817 | git__free(prefix); |
818 | ||
74fa4bfa | 819 | return diff_from_iterators(repo, opts, a, b, diff); |
1d94a7d0 CMN |
820 | |
821 | on_error: | |
822 | git__free(prefix); | |
823 | git_iterator_free(a); | |
824 | return -1; | |
65b09b1d RB |
825 | } |
826 | ||
65b09b1d | 827 | |
74fa4bfa RB |
828 | int git_diff_workdir_to_tree( |
829 | git_repository *repo, | |
830 | const git_diff_options *opts, | |
2de60205 | 831 | git_tree *old_tree, |
74fa4bfa | 832 | git_diff_list **diff) |
65b09b1d | 833 | { |
74fa4bfa | 834 | git_iterator *a = NULL, *b = NULL; |
41a82592 | 835 | char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL; |
3a437590 | 836 | |
2de60205 | 837 | assert(repo && old_tree && diff); |
3a437590 | 838 | |
41a82592 | 839 | if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 || |
1d94a7d0 CMN |
840 | git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0) |
841 | goto on_error; | |
3a437590 | 842 | |
41a82592 RB |
843 | git__free(prefix); |
844 | ||
74fa4bfa | 845 | return diff_from_iterators(repo, opts, a, b, diff); |
1d94a7d0 CMN |
846 | |
847 | on_error: | |
848 | git__free(prefix); | |
849 | git_iterator_free(a); | |
850 | return -1; | |
65b09b1d RB |
851 | } |
852 | ||
64286308 RB |
853 | |
854 | bool git_diff_delta__should_skip( | |
bae957b9 | 855 | const git_diff_options *opts, const git_diff_delta *delta) |
64286308 RB |
856 | { |
857 | uint32_t flags = opts ? opts->flags : 0; | |
858 | ||
859 | if (delta->status == GIT_DELTA_UNMODIFIED && | |
860 | (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) | |
861 | return true; | |
862 | ||
863 | if (delta->status == GIT_DELTA_IGNORED && | |
864 | (flags & GIT_DIFF_INCLUDE_IGNORED) == 0) | |
865 | return true; | |
866 | ||
867 | if (delta->status == GIT_DELTA_UNTRACKED && | |
868 | (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) | |
869 | return true; | |
870 | ||
871 | return false; | |
872 | } | |
873 | ||
874 | ||
74fa4bfa RB |
875 | int git_diff_merge( |
876 | git_diff_list *onto, | |
877 | const git_diff_list *from) | |
65b09b1d | 878 | { |
ae9e29fd | 879 | int error = 0; |
19fa2bc1 | 880 | git_pool onto_pool; |
74fa4bfa RB |
881 | git_vector onto_new; |
882 | git_diff_delta *delta; | |
ec40b7f9 | 883 | bool ignore_case = false; |
1db12b00 RB |
884 | unsigned int i, j; |
885 | ||
886 | assert(onto && from); | |
3a437590 | 887 | |
1db12b00 RB |
888 | if (!from->deltas.length) |
889 | return 0; | |
3a437590 | 890 | |
19fa2bc1 RB |
891 | if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 || |
892 | git_pool_init(&onto_pool, 1, 0) < 0) | |
ae9e29fd | 893 | return -1; |
74fa4bfa | 894 | |
ec40b7f9 PK |
895 | if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 || |
896 | (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0) | |
897 | { | |
898 | ignore_case = true; | |
899 | ||
900 | /* This function currently only supports merging diff lists that | |
901 | * are sorted identically. */ | |
902 | assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 && | |
903 | (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0); | |
904 | } | |
905 | ||
1db12b00 RB |
906 | for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) { |
907 | git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i); | |
908 | const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j); | |
ec40b7f9 | 909 | int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path); |
1db12b00 RB |
910 | |
911 | if (cmp < 0) { | |
19fa2bc1 | 912 | delta = diff_delta__dup(o, &onto_pool); |
74fa4bfa | 913 | i++; |
1db12b00 | 914 | } else if (cmp > 0) { |
19fa2bc1 | 915 | delta = diff_delta__dup(f, &onto_pool); |
74fa4bfa RB |
916 | j++; |
917 | } else { | |
19fa2bc1 | 918 | delta = diff_delta__merge_like_cgit(o, f, &onto_pool); |
74fa4bfa RB |
919 | i++; |
920 | j++; | |
921 | } | |
3a437590 | 922 | |
64286308 RB |
923 | /* the ignore rules for the target may not match the source |
924 | * or the result of a merged delta could be skippable... | |
925 | */ | |
926 | if (git_diff_delta__should_skip(&onto->opts, delta)) { | |
927 | git__free(delta); | |
928 | continue; | |
929 | } | |
930 | ||
a48ea31d | 931 | if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0) |
74fa4bfa | 932 | break; |
a2e895be | 933 | } |
65b09b1d | 934 | |
1db12b00 | 935 | if (!error) { |
74fa4bfa | 936 | git_vector_swap(&onto->deltas, &onto_new); |
19fa2bc1 | 937 | git_pool_swap(&onto->pool, &onto_pool); |
74fa4bfa | 938 | onto->new_src = from->new_src; |
145e696b RB |
939 | |
940 | /* prefix strings also come from old pool, so recreate those.*/ | |
941 | onto->opts.old_prefix = | |
71d27358 | 942 | git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix); |
145e696b | 943 | onto->opts.new_prefix = |
71d27358 | 944 | git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix); |
65b09b1d | 945 | } |
cd33323b | 946 | |
74fa4bfa | 947 | git_vector_foreach(&onto_new, i, delta) |
19fa2bc1 | 948 | git__free(delta); |
74fa4bfa | 949 | git_vector_free(&onto_new); |
19fa2bc1 | 950 | git_pool_clear(&onto_pool); |
cd33323b | 951 | |
74fa4bfa | 952 | return error; |
cd33323b | 953 | } |