]>
Commit | Line | Data |
---|---|---|
68535125 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
68535125 | 3 | * |
bb742ede VM |
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. | |
68535125 VM |
6 | */ |
7 | ||
eae0bfdc PP |
8 | #include "index.h" |
9 | ||
68535125 VM |
10 | #include <stddef.h> |
11 | ||
c3a20d5c | 12 | #include "repository.h" |
599f2849 | 13 | #include "tree.h" |
b4171320 | 14 | #include "tree-cache.h" |
68535125 | 15 | #include "hash.h" |
ad9a921b RB |
16 | #include "iterator.h" |
17 | #include "pathspec.h" | |
f30fff45 | 18 | #include "ignore.h" |
a16e4172 | 19 | #include "blob.h" |
af1d5239 | 20 | #include "idxmap.h" |
cb0ff012 | 21 | #include "diff.h" |
5625d86b | 22 | #include "varint.h" |
f30fff45 | 23 | |
44908fe7 | 24 | #include "git2/odb.h" |
c07d9c95 | 25 | #include "git2/oid.h" |
44908fe7 | 26 | #include "git2/blob.h" |
da825c92 | 27 | #include "git2/config.h" |
75d1c8c6 | 28 | #include "git2/sys/index.h" |
68535125 | 29 | |
0a78a52e CMN |
30 | static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths, |
31 | unsigned int flags, | |
32 | git_index_matched_path_cb cb, void *payload); | |
33 | ||
68535125 VM |
34 | #define minimal_entry_size (offsetof(struct entry_short, path)) |
35 | ||
68535125 VM |
36 | static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ; |
37 | static const size_t INDEX_HEADER_SIZE = 12; | |
38 | ||
5625d86b DT |
39 | static const unsigned int INDEX_VERSION_NUMBER_DEFAULT = 2; |
40 | static const unsigned int INDEX_VERSION_NUMBER_LB = 2; | |
348c7335 | 41 | static const unsigned int INDEX_VERSION_NUMBER_EXT = 3; |
5625d86b DT |
42 | static const unsigned int INDEX_VERSION_NUMBER_COMP = 4; |
43 | static const unsigned int INDEX_VERSION_NUMBER_UB = 4; | |
348c7335 VM |
44 | |
45 | static const unsigned int INDEX_HEADER_SIG = 0x44495243; | |
46 | static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'}; | |
4c0b6a6d | 47 | static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'}; |
0462fba5 | 48 | static const char INDEX_EXT_CONFLICT_NAME_SIG[] = {'N', 'A', 'M', 'E'}; |
68535125 | 49 | |
9462c471 VM |
50 | #define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx))) |
51 | ||
68535125 VM |
52 | struct index_header { |
53 | uint32_t signature; | |
54 | uint32_t version; | |
55 | uint32_t entry_count; | |
56 | }; | |
57 | ||
58 | struct index_extension { | |
59 | char signature[4]; | |
60 | uint32_t extension_size; | |
61 | }; | |
62 | ||
a44fc1d4 VM |
63 | struct entry_time { |
64 | uint32_t seconds; | |
65 | uint32_t nanoseconds; | |
66 | }; | |
67 | ||
68535125 | 68 | struct entry_short { |
a44fc1d4 VM |
69 | struct entry_time ctime; |
70 | struct entry_time mtime; | |
68535125 VM |
71 | uint32_t dev; |
72 | uint32_t ino; | |
73 | uint32_t mode; | |
74 | uint32_t uid; | |
75 | uint32_t gid; | |
76 | uint32_t file_size; | |
77 | git_oid oid; | |
78 | uint16_t flags; | |
683581a3 | 79 | char path[1]; /* arbitrary length */ |
68535125 VM |
80 | }; |
81 | ||
82 | struct entry_long { | |
a44fc1d4 VM |
83 | struct entry_time ctime; |
84 | struct entry_time mtime; | |
68535125 VM |
85 | uint32_t dev; |
86 | uint32_t ino; | |
87 | uint32_t mode; | |
88 | uint32_t uid; | |
89 | uint32_t gid; | |
90 | uint32_t file_size; | |
91 | git_oid oid; | |
92 | uint16_t flags; | |
93 | uint16_t flags_extended; | |
683581a3 | 94 | char path[1]; /* arbitrary length */ |
68535125 VM |
95 | }; |
96 | ||
f45ec1a0 ET |
97 | struct entry_srch_key { |
98 | const char *path; | |
8a2834d3 | 99 | size_t pathlen; |
f45ec1a0 ET |
100 | int stage; |
101 | }; | |
102 | ||
8a2834d3 RB |
103 | struct entry_internal { |
104 | git_index_entry entry; | |
105 | size_t pathlen; | |
106 | char path[GIT_FLEX_ARRAY]; | |
107 | }; | |
108 | ||
109 | struct reuc_entry_internal { | |
110 | git_index_reuc_entry entry; | |
111 | size_t pathlen; | |
112 | char path[GIT_FLEX_ARRAY]; | |
113 | }; | |
114 | ||
ac3d33df JK |
115 | bool git_index__enforce_unsaved_safety = false; |
116 | ||
68535125 | 117 | /* local declarations */ |
ac3d33df | 118 | static int read_extension(size_t *read_len, git_index *index, const char *buffer, size_t buffer_size); |
68535125 VM |
119 | static int read_header(struct index_header *dest, const void *buffer); |
120 | ||
348c7335 | 121 | static int parse_index(git_index *index, const char *buffer, size_t buffer_size); |
4604a654 | 122 | static bool is_index_extended(git_index *index); |
5e947c91 | 123 | static int write_index(git_oid *checksum, git_index *index, git_filebuf *file); |
68535125 | 124 | |
89886d0b | 125 | static void index_entry_free(git_index_entry *entry); |
f45ec1a0 ET |
126 | static void index_entry_reuc_free(git_index_reuc_entry *reuc); |
127 | ||
22a2d3d5 UG |
128 | GIT_INLINE(int) index_map_set(git_idxmap *map, git_index_entry *e, bool ignore_case) |
129 | { | |
130 | if (ignore_case) | |
131 | return git_idxmap_icase_set((git_idxmap_icase *) map, e, e); | |
132 | else | |
133 | return git_idxmap_set(map, e, e); | |
134 | } | |
135 | ||
136 | GIT_INLINE(int) index_map_delete(git_idxmap *map, git_index_entry *e, bool ignore_case) | |
137 | { | |
138 | if (ignore_case) | |
139 | return git_idxmap_icase_delete((git_idxmap_icase *) map, e); | |
140 | else | |
141 | return git_idxmap_delete(map, e); | |
142 | } | |
143 | ||
144 | GIT_INLINE(int) index_map_resize(git_idxmap *map, size_t count, bool ignore_case) | |
145 | { | |
146 | if (ignore_case) | |
147 | return git_idxmap_icase_resize((git_idxmap_icase *) map, count); | |
148 | else | |
149 | return git_idxmap_resize(map, count); | |
150 | } | |
151 | ||
3b4c401a | 152 | int git_index_entry_srch(const void *key, const void *array_member) |
c4034e63 | 153 | { |
f45ec1a0 | 154 | const struct entry_srch_key *srch_key = key; |
8a2834d3 | 155 | const struct entry_internal *entry = array_member; |
3158e2fe RB |
156 | int cmp; |
157 | size_t len1, len2, len; | |
c4034e63 | 158 | |
8a2834d3 RB |
159 | len1 = srch_key->pathlen; |
160 | len2 = entry->pathlen; | |
53bec813 | 161 | len = len1 < len2 ? len1 : len2; |
f45ec1a0 | 162 | |
53bec813 VM |
163 | cmp = memcmp(srch_key->path, entry->path, len); |
164 | if (cmp) | |
165 | return cmp; | |
166 | if (len1 < len2) | |
167 | return -1; | |
168 | if (len1 > len2) | |
169 | return 1; | |
f45ec1a0 | 170 | |
53bec813 | 171 | if (srch_key->stage != GIT_INDEX_STAGE_ANY) |
ac3d33df | 172 | return srch_key->stage - GIT_INDEX_ENTRY_STAGE(&entry->entry); |
53bec813 VM |
173 | |
174 | return 0; | |
c4034e63 VM |
175 | } |
176 | ||
3b4c401a | 177 | int git_index_entry_isrch(const void *key, const void *array_member) |
f45ec1a0 ET |
178 | { |
179 | const struct entry_srch_key *srch_key = key; | |
8a2834d3 | 180 | const struct entry_internal *entry = array_member; |
3158e2fe RB |
181 | int cmp; |
182 | size_t len1, len2, len; | |
f45ec1a0 | 183 | |
8a2834d3 RB |
184 | len1 = srch_key->pathlen; |
185 | len2 = entry->pathlen; | |
b747eb14 | 186 | len = len1 < len2 ? len1 : len2; |
f45ec1a0 | 187 | |
b747eb14 | 188 | cmp = strncasecmp(srch_key->path, entry->path, len); |
f45ec1a0 | 189 | |
b747eb14 ET |
190 | if (cmp) |
191 | return cmp; | |
192 | if (len1 < len2) | |
193 | return -1; | |
194 | if (len1 > len2) | |
195 | return 1; | |
196 | ||
197 | if (srch_key->stage != GIT_INDEX_STAGE_ANY) | |
ac3d33df | 198 | return srch_key->stage - GIT_INDEX_ENTRY_STAGE(&entry->entry); |
b747eb14 ET |
199 | |
200 | return 0; | |
f45ec1a0 ET |
201 | } |
202 | ||
3b4c401a | 203 | static int index_entry_srch_path(const void *path, const void *array_member) |
ec40b7f9 PK |
204 | { |
205 | const git_index_entry *entry = array_member; | |
206 | ||
f45ec1a0 ET |
207 | return strcmp((const char *)path, entry->path); |
208 | } | |
209 | ||
3b4c401a | 210 | static int index_entry_isrch_path(const void *path, const void *array_member) |
f45ec1a0 ET |
211 | { |
212 | const git_index_entry *entry = array_member; | |
213 | ||
214 | return strcasecmp((const char *)path, entry->path); | |
ec40b7f9 PK |
215 | } |
216 | ||
3b4c401a | 217 | int git_index_entry_cmp(const void *a, const void *b) |
c4034e63 | 218 | { |
f45ec1a0 | 219 | int diff; |
ae9f771c KS |
220 | const git_index_entry *entry_a = a; |
221 | const git_index_entry *entry_b = b; | |
c4034e63 | 222 | |
f45ec1a0 ET |
223 | diff = strcmp(entry_a->path, entry_b->path); |
224 | ||
225 | if (diff == 0) | |
ac3d33df | 226 | diff = (GIT_INDEX_ENTRY_STAGE(entry_a) - GIT_INDEX_ENTRY_STAGE(entry_b)); |
f45ec1a0 ET |
227 | |
228 | return diff; | |
c4034e63 VM |
229 | } |
230 | ||
3b4c401a | 231 | int git_index_entry_icmp(const void *a, const void *b) |
ec40b7f9 | 232 | { |
f45ec1a0 | 233 | int diff; |
ec40b7f9 PK |
234 | const git_index_entry *entry_a = a; |
235 | const git_index_entry *entry_b = b; | |
236 | ||
f45ec1a0 ET |
237 | diff = strcasecmp(entry_a->path, entry_b->path); |
238 | ||
239 | if (diff == 0) | |
ac3d33df | 240 | diff = (GIT_INDEX_ENTRY_STAGE(entry_a) - GIT_INDEX_ENTRY_STAGE(entry_b)); |
f45ec1a0 ET |
241 | |
242 | return diff; | |
243 | } | |
244 | ||
0462fba5 ET |
245 | static int conflict_name_cmp(const void *a, const void *b) |
246 | { | |
247 | const git_index_name_entry *name_a = a; | |
248 | const git_index_name_entry *name_b = b; | |
81b7dec4 | 249 | |
0462fba5 ET |
250 | if (name_a->ancestor && !name_b->ancestor) |
251 | return 1; | |
81b7dec4 | 252 | |
0462fba5 ET |
253 | if (!name_a->ancestor && name_b->ancestor) |
254 | return -1; | |
81b7dec4 | 255 | |
0462fba5 ET |
256 | if (name_a->ancestor) |
257 | return strcmp(name_a->ancestor, name_b->ancestor); | |
81b7dec4 | 258 | |
0462fba5 ET |
259 | if (!name_a->ours || !name_b->ours) |
260 | return 0; | |
81b7dec4 | 261 | |
0462fba5 ET |
262 | return strcmp(name_a->ours, name_b->ours); |
263 | } | |
264 | ||
e1807113 VM |
265 | /** |
266 | * TODO: enable this when resolving case insensitive conflicts | |
267 | */ | |
81b7dec4 | 268 | #if 0 |
0462fba5 ET |
269 | static int conflict_name_icmp(const void *a, const void *b) |
270 | { | |
271 | const git_index_name_entry *name_a = a; | |
272 | const git_index_name_entry *name_b = b; | |
81b7dec4 | 273 | |
0462fba5 ET |
274 | if (name_a->ancestor && !name_b->ancestor) |
275 | return 1; | |
81b7dec4 | 276 | |
0462fba5 ET |
277 | if (!name_a->ancestor && name_b->ancestor) |
278 | return -1; | |
81b7dec4 | 279 | |
0462fba5 ET |
280 | if (name_a->ancestor) |
281 | return strcasecmp(name_a->ancestor, name_b->ancestor); | |
81b7dec4 | 282 | |
0462fba5 ET |
283 | if (!name_a->ours || !name_b->ours) |
284 | return 0; | |
81b7dec4 | 285 | |
0462fba5 ET |
286 | return strcasecmp(name_a->ours, name_b->ours); |
287 | } | |
81b7dec4 | 288 | #endif |
0462fba5 | 289 | |
f45ec1a0 ET |
290 | static int reuc_srch(const void *key, const void *array_member) |
291 | { | |
292 | const git_index_reuc_entry *reuc = array_member; | |
293 | ||
294 | return strcmp(key, reuc->path); | |
ec40b7f9 PK |
295 | } |
296 | ||
f45ec1a0 | 297 | static int reuc_isrch(const void *key, const void *array_member) |
4c0b6a6d | 298 | { |
f45ec1a0 | 299 | const git_index_reuc_entry *reuc = array_member; |
4c0b6a6d | 300 | |
f45ec1a0 | 301 | return strcasecmp(key, reuc->path); |
4c0b6a6d JP |
302 | } |
303 | ||
f45ec1a0 | 304 | static int reuc_cmp(const void *a, const void *b) |
4c0b6a6d | 305 | { |
f45ec1a0 ET |
306 | const git_index_reuc_entry *info_a = a; |
307 | const git_index_reuc_entry *info_b = b; | |
4c0b6a6d JP |
308 | |
309 | return strcmp(info_a->path, info_b->path); | |
310 | } | |
c4034e63 | 311 | |
f45ec1a0 ET |
312 | static int reuc_icmp(const void *a, const void *b) |
313 | { | |
314 | const git_index_reuc_entry *info_a = a; | |
315 | const git_index_reuc_entry *info_b = b; | |
316 | ||
317 | return strcasecmp(info_a->path, info_b->path); | |
318 | } | |
319 | ||
178aa39c RB |
320 | static void index_entry_reuc_free(git_index_reuc_entry *reuc) |
321 | { | |
178aa39c RB |
322 | git__free(reuc); |
323 | } | |
324 | ||
325 | static void index_entry_free(git_index_entry *entry) | |
326 | { | |
b63b3b0e JG |
327 | if (!entry) |
328 | return; | |
329 | ||
ea642d61 | 330 | memset(&entry->id, 0, sizeof(entry->id)); |
178aa39c RB |
331 | git__free(entry); |
332 | } | |
333 | ||
05d47768 | 334 | unsigned int git_index__create_mode(unsigned int mode) |
c1a2a14e JP |
335 | { |
336 | if (S_ISLNK(mode)) | |
337 | return S_IFLNK; | |
da825c92 | 338 | |
c1802641 SS |
339 | if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR)) |
340 | return (S_IFLNK | S_IFDIR); | |
da825c92 | 341 | |
f240acce | 342 | return S_IFREG | GIT_PERMS_CANONICAL(mode); |
c1a2a14e JP |
343 | } |
344 | ||
da825c92 RB |
345 | static unsigned int index_merge_mode( |
346 | git_index *index, git_index_entry *existing, unsigned int mode) | |
347 | { | |
348 | if (index->no_symlinks && S_ISREG(mode) && | |
349 | existing && S_ISLNK(existing->mode)) | |
350 | return existing->mode; | |
351 | ||
352 | if (index->distrust_filemode && S_ISREG(mode)) | |
353 | return (existing && S_ISREG(existing->mode)) ? | |
05d47768 | 354 | existing->mode : git_index__create_mode(0666); |
da825c92 | 355 | |
05d47768 | 356 | return git_index__create_mode(mode); |
da825c92 RB |
357 | } |
358 | ||
52bb0476 RB |
359 | GIT_INLINE(int) index_find_in_entries( |
360 | size_t *out, git_vector *entries, git_vector_cmp entry_srch, | |
361 | const char *path, size_t path_len, int stage) | |
362 | { | |
363 | struct entry_srch_key srch_key; | |
364 | srch_key.path = path; | |
365 | srch_key.pathlen = !path_len ? strlen(path) : path_len; | |
366 | srch_key.stage = stage; | |
367 | return git_vector_bsearch2(out, entries, entry_srch, &srch_key); | |
368 | } | |
369 | ||
370 | GIT_INLINE(int) index_find( | |
371 | size_t *out, git_index *index, | |
9d81509a | 372 | const char *path, size_t path_len, int stage) |
52bb0476 | 373 | { |
9d81509a | 374 | git_vector_sort(&index->entries); |
52bb0476 RB |
375 | |
376 | return index_find_in_entries( | |
377 | out, &index->entries, index->entries_search, path, path_len, stage); | |
378 | } | |
379 | ||
cb53669e | 380 | void git_index__set_ignore_case(git_index *index, bool ignore_case) |
ec40b7f9 | 381 | { |
3f0d0c85 PK |
382 | index->ignore_case = ignore_case; |
383 | ||
3b4c401a RB |
384 | if (ignore_case) { |
385 | index->entries_cmp_path = git__strcasecmp_cb; | |
386 | index->entries_search = git_index_entry_isrch; | |
387 | index->entries_search_path = index_entry_isrch_path; | |
388 | index->reuc_search = reuc_isrch; | |
389 | } else { | |
390 | index->entries_cmp_path = git__strcmp_cb; | |
391 | index->entries_search = git_index_entry_srch; | |
392 | index->entries_search_path = index_entry_srch_path; | |
393 | index->reuc_search = reuc_srch; | |
394 | } | |
22b6b82f | 395 | |
3b4c401a RB |
396 | git_vector_set_cmp(&index->entries, |
397 | ignore_case ? git_index_entry_icmp : git_index_entry_cmp); | |
9d81509a | 398 | git_vector_sort(&index->entries); |
f45ec1a0 | 399 | |
22b6b82f | 400 | git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp); |
f45ec1a0 | 401 | git_vector_sort(&index->reuc); |
ec40b7f9 PK |
402 | } |
403 | ||
9462c471 | 404 | int git_index_open(git_index **index_out, const char *index_path) |
68535125 VM |
405 | { |
406 | git_index *index; | |
3dbee456 | 407 | int error = -1; |
68535125 | 408 | |
c25aa7cd | 409 | GIT_ASSERT_ARG(index_out); |
68535125 | 410 | |
7c7ff7d1 | 411 | index = git__calloc(1, sizeof(git_index)); |
ac3d33df | 412 | GIT_ERROR_CHECK_ALLOC(index); |
68535125 | 413 | |
22a2d3d5 UG |
414 | if (git_pool_init(&index->tree_pool, 1) < 0) |
415 | goto fail; | |
19c88310 | 416 | |
276ea401 VM |
417 | if (index_path != NULL) { |
418 | index->index_file_path = git__strdup(index_path); | |
3dbee456 RB |
419 | if (!index->index_file_path) |
420 | goto fail; | |
276ea401 VM |
421 | |
422 | /* Check if index file is stored on disk already */ | |
423 | if (git_path_exists(index->index_file_path) == true) | |
424 | index->on_disk = 1; | |
425 | } | |
68535125 | 426 | |
3b4c401a | 427 | if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 || |
22a2d3d5 UG |
428 | git_idxmap_new(&index->entries_map) < 0 || |
429 | git_vector_init(&index->names, 8, conflict_name_cmp) < 0 || | |
430 | git_vector_init(&index->reuc, 8, reuc_cmp) < 0 || | |
431 | git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0) | |
3dbee456 | 432 | goto fail; |
348c7335 | 433 | |
3b4c401a RB |
434 | index->entries_cmp_path = git__strcmp_cb; |
435 | index->entries_search = git_index_entry_srch; | |
436 | index->entries_search_path = index_entry_srch_path; | |
f45ec1a0 | 437 | index->reuc_search = reuc_srch; |
5625d86b | 438 | index->version = INDEX_VERSION_NUMBER_DEFAULT; |
ec40b7f9 | 439 | |
3dbee456 RB |
440 | if (index_path != NULL && (error = git_index_read(index, true)) < 0) |
441 | goto fail; | |
bd15b513 | 442 | |
1795f879 | 443 | *index_out = index; |
9462c471 | 444 | GIT_REFCOUNT_INC(index); |
276ea401 | 445 | |
bd15b513 | 446 | return 0; |
3dbee456 RB |
447 | |
448 | fail: | |
19c88310 | 449 | git_pool_clear(&index->tree_pool); |
3dbee456 RB |
450 | git_index_free(index); |
451 | return error; | |
68535125 VM |
452 | } |
453 | ||
1e808f9c VM |
454 | int git_index_new(git_index **out) |
455 | { | |
456 | return git_index_open(out, NULL); | |
457 | } | |
458 | ||
9462c471 | 459 | static void index_free(git_index *index) |
c3a20d5c | 460 | { |
dac16048 RB |
461 | /* index iterators increment the refcount of the index, so if we |
462 | * get here then there should be no outstanding iterators. | |
463 | */ | |
c25aa7cd PP |
464 | if (git_atomic32_get(&index->readers)) |
465 | return; | |
dac16048 | 466 | |
9462c471 | 467 | git_index_clear(index); |
af1d5239 | 468 | git_idxmap_free(index->entries_map); |
9462c471 | 469 | git_vector_free(&index->entries); |
d8041638 | 470 | git_vector_free(&index->names); |
f45ec1a0 | 471 | git_vector_free(&index->reuc); |
dac16048 | 472 | git_vector_free(&index->deleted); |
c3dd69a9 | 473 | |
9462c471 | 474 | git__free(index->index_file_path); |
f658dc43 | 475 | |
6de9b2ee | 476 | git__memzero(index, sizeof(*index)); |
9462c471 | 477 | git__free(index); |
c3a20d5c VM |
478 | } |
479 | ||
348c7335 VM |
480 | void git_index_free(git_index *index) |
481 | { | |
f7e59c4d | 482 | if (index == NULL) |
348c7335 VM |
483 | return; |
484 | ||
9462c471 | 485 | GIT_REFCOUNT_DEC(index, index_free); |
348c7335 VM |
486 | } |
487 | ||
dac16048 RB |
488 | /* call with locked index */ |
489 | static void index_free_deleted(git_index *index) | |
68535125 | 490 | { |
c25aa7cd | 491 | int readers = (int)git_atomic32_get(&index->readers); |
10c06114 | 492 | size_t i; |
6fd195d7 | 493 | |
ea642d61 | 494 | if (readers > 0 || !index->deleted.length) |
dac16048 RB |
495 | return; |
496 | ||
8a2834d3 | 497 | for (i = 0; i < index->deleted.length; ++i) { |
c25aa7cd | 498 | git_index_entry *ie = git_atomic_swap(index->deleted.contents[i], NULL); |
8a2834d3 RB |
499 | index_entry_free(ie); |
500 | } | |
dac16048 RB |
501 | |
502 | git_vector_clear(&index->deleted); | |
503 | } | |
504 | ||
8a2834d3 RB |
505 | /* call with locked index */ |
506 | static int index_remove_entry(git_index *index, size_t pos) | |
dac16048 RB |
507 | { |
508 | int error = 0; | |
509 | git_index_entry *entry = git_vector_get(&index->entries, pos); | |
510 | ||
13deb874 | 511 | if (entry != NULL) { |
dac16048 | 512 | git_tree_cache_invalidate_path(index->tree, entry->path); |
22a2d3d5 | 513 | index_map_delete(index->entries_map, entry, index->ignore_case); |
13deb874 | 514 | } |
dac16048 | 515 | |
dac16048 RB |
516 | error = git_vector_remove(&index->entries, pos); |
517 | ||
518 | if (!error) { | |
c25aa7cd | 519 | if (git_atomic32_get(&index->readers) > 0) { |
dac16048 | 520 | error = git_vector_insert(&index->deleted, entry); |
ea642d61 | 521 | } else { |
8a2834d3 | 522 | index_entry_free(entry); |
ea642d61 | 523 | } |
ac3d33df JK |
524 | |
525 | index->dirty = 1; | |
dac16048 RB |
526 | } |
527 | ||
dac16048 | 528 | return error; |
03a89070 | 529 | } |
eac76c23 | 530 | |
3dbee456 | 531 | int git_index_clear(git_index *index) |
03a89070 | 532 | { |
dac16048 RB |
533 | int error = 0; |
534 | ||
c25aa7cd | 535 | GIT_ASSERT_ARG(index); |
03a89070 | 536 | |
ac3d33df | 537 | index->dirty = 1; |
3dbee456 | 538 | index->tree = NULL; |
19c88310 | 539 | git_pool_clear(&index->tree_pool); |
3dbee456 | 540 | |
af1d5239 | 541 | git_idxmap_clear(index->entries_map); |
dac16048 | 542 | while (!error && index->entries.length > 0) |
8a2834d3 | 543 | error = index_remove_entry(index, index->entries.length - 1); |
22a2d3d5 UG |
544 | |
545 | if (error) | |
546 | goto done; | |
547 | ||
dac16048 RB |
548 | index_free_deleted(index); |
549 | ||
22a2d3d5 UG |
550 | if ((error = git_index_name_clear(index)) < 0 || |
551 | (error = git_index_reuc_clear(index)) < 0) | |
552 | goto done; | |
1fed6b07 | 553 | |
8ff0f325 | 554 | git_futils_filestamp_set(&index->stamp, NULL); |
68535125 | 555 | |
22a2d3d5 | 556 | done: |
dac16048 | 557 | return error; |
68535125 VM |
558 | } |
559 | ||
33f95a9b | 560 | static int create_index_error(int error, const char *msg) |
561 | { | |
ac3d33df | 562 | git_error_set_str(GIT_ERROR_INDEX, msg); |
33f95a9b | 563 | return error; |
564 | } | |
565 | ||
72556cc6 | 566 | int git_index_set_caps(git_index *index, int caps) |
da825c92 | 567 | { |
66566516 | 568 | unsigned int old_ignore_case; |
ec40b7f9 | 569 | |
c25aa7cd | 570 | GIT_ASSERT_ARG(index); |
da825c92 | 571 | |
ec40b7f9 PK |
572 | old_ignore_case = index->ignore_case; |
573 | ||
ac3d33df | 574 | if (caps == GIT_INDEX_CAPABILITY_FROM_OWNER) { |
eac76c23 | 575 | git_repository *repo = INDEX_OWNER(index); |
da825c92 RB |
576 | int val; |
577 | ||
eac76c23 RB |
578 | if (!repo) |
579 | return create_index_error( | |
909d5494 | 580 | -1, "cannot access repository to set index caps"); |
da825c92 | 581 | |
22a2d3d5 | 582 | if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_IGNORECASE)) |
da825c92 | 583 | index->ignore_case = (val != 0); |
22a2d3d5 | 584 | if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_FILEMODE)) |
da825c92 | 585 | index->distrust_filemode = (val == 0); |
22a2d3d5 | 586 | if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_SYMLINKS)) |
0e2dd29b | 587 | index->no_symlinks = (val == 0); |
da825c92 RB |
588 | } |
589 | else { | |
ac3d33df JK |
590 | index->ignore_case = ((caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0); |
591 | index->distrust_filemode = ((caps & GIT_INDEX_CAPABILITY_NO_FILEMODE) != 0); | |
592 | index->no_symlinks = ((caps & GIT_INDEX_CAPABILITY_NO_SYMLINKS) != 0); | |
da825c92 RB |
593 | } |
594 | ||
5425097f | 595 | if (old_ignore_case != index->ignore_case) { |
66566516 | 596 | git_index__set_ignore_case(index, (bool)index->ignore_case); |
ec40b7f9 PK |
597 | } |
598 | ||
da825c92 RB |
599 | return 0; |
600 | } | |
601 | ||
72556cc6 | 602 | int git_index_caps(const git_index *index) |
da825c92 | 603 | { |
ac3d33df JK |
604 | return ((index->ignore_case ? GIT_INDEX_CAPABILITY_IGNORE_CASE : 0) | |
605 | (index->distrust_filemode ? GIT_INDEX_CAPABILITY_NO_FILEMODE : 0) | | |
606 | (index->no_symlinks ? GIT_INDEX_CAPABILITY_NO_SYMLINKS : 0)); | |
da825c92 RB |
607 | } |
608 | ||
5e947c91 CMN |
609 | const git_oid *git_index_checksum(git_index *index) |
610 | { | |
611 | return &index->checksum; | |
612 | } | |
613 | ||
614 | /** | |
615 | * Returns 1 for changed, 0 for not changed and <0 for errors | |
616 | */ | |
617 | static int compare_checksum(git_index *index) | |
618 | { | |
997e0301 | 619 | int fd; |
5e947c91 CMN |
620 | ssize_t bytes_read; |
621 | git_oid checksum = {{ 0 }}; | |
622 | ||
623 | if ((fd = p_open(index->index_file_path, O_RDONLY)) < 0) | |
624 | return fd; | |
625 | ||
997e0301 | 626 | if (p_lseek(fd, -20, SEEK_END) < 0) { |
5e947c91 | 627 | p_close(fd); |
ac3d33df | 628 | git_error_set(GIT_ERROR_OS, "failed to seek to end of file"); |
5e947c91 CMN |
629 | return -1; |
630 | } | |
631 | ||
632 | bytes_read = p_read(fd, &checksum, GIT_OID_RAWSZ); | |
633 | p_close(fd); | |
634 | ||
635 | if (bytes_read < 0) | |
636 | return -1; | |
637 | ||
638 | return !!git_oid_cmp(&checksum, &index->checksum); | |
639 | } | |
640 | ||
8e5a8ef8 | 641 | int git_index_read(git_index *index, int force) |
68535125 | 642 | { |
8ff0f325 | 643 | int error = 0, updated; |
13224ea4 | 644 | git_buf buffer = GIT_BUF_INIT; |
4bf630b6 | 645 | git_futils_filestamp stamp = index->stamp; |
68535125 | 646 | |
33f95a9b | 647 | if (!index->index_file_path) |
648 | return create_index_error(-1, | |
909d5494 | 649 | "failed to read index: The index is in-memory only"); |
68535125 | 650 | |
da7b78fa | 651 | index->on_disk = git_path_exists(index->index_file_path); |
652 | ||
653 | if (!index->on_disk) { | |
ac3d33df JK |
654 | if (force && (error = git_index_clear(index)) < 0) |
655 | return error; | |
656 | ||
657 | index->dirty = 0; | |
7c7ff7d1 | 658 | return 0; |
68535125 VM |
659 | } |
660 | ||
5e947c91 CMN |
661 | if ((updated = git_futils_filestamp_check(&stamp, index->index_file_path) < 0) || |
662 | ((updated = compare_checksum(index)) < 0)) { | |
ac3d33df JK |
663 | git_error_set( |
664 | GIT_ERROR_INDEX, | |
909d5494 | 665 | "failed to read index: '%s' no longer exists", |
7d490872 | 666 | index->index_file_path); |
8ff0f325 | 667 | return updated; |
7d490872 | 668 | } |
ac3d33df | 669 | |
7d490872 RB |
670 | if (!updated && !force) |
671 | return 0; | |
8ff0f325 VM |
672 | |
673 | error = git_futils_readbuffer(&buffer, index->index_file_path); | |
7c7ff7d1 RB |
674 | if (error < 0) |
675 | return error; | |
68535125 | 676 | |
19c88310 CMN |
677 | index->tree = NULL; |
678 | git_pool_clear(&index->tree_pool); | |
679 | ||
3dbee456 RB |
680 | error = git_index_clear(index); |
681 | ||
682 | if (!error) | |
683 | error = parse_index(index, buffer.ptr, buffer.size); | |
68535125 | 684 | |
ac3d33df | 685 | if (!error) { |
8ff0f325 | 686 | git_futils_filestamp_set(&index->stamp, &stamp); |
ac3d33df JK |
687 | index->dirty = 0; |
688 | } | |
68535125 | 689 | |
ac3d33df | 690 | git_buf_dispose(&buffer); |
68535125 VM |
691 | return error; |
692 | } | |
693 | ||
ac3d33df JK |
694 | int git_index_read_safely(git_index *index) |
695 | { | |
696 | if (git_index__enforce_unsaved_safety && index->dirty) { | |
697 | git_error_set(GIT_ERROR_INDEX, | |
698 | "the index has unsaved changes that would be overwritten by this operation"); | |
699 | return GIT_EINDEXDIRTY; | |
700 | } | |
701 | ||
702 | return git_index_read(index, false); | |
703 | } | |
704 | ||
db0e7878 | 705 | int git_index__changed_relative_to( |
624c949f | 706 | git_index *index, const git_oid *checksum) |
db0e7878 RB |
707 | { |
708 | /* attempt to update index (ignoring errors) */ | |
709 | if (git_index_read(index, false) < 0) | |
ac3d33df | 710 | git_error_clear(); |
db0e7878 | 711 | |
624c949f | 712 | return !!git_oid_cmp(&index->checksum, checksum); |
db0e7878 RB |
713 | } |
714 | ||
25e84f95 | 715 | static bool is_racy_entry(git_index *index, const git_index_entry *entry) |
74975846 CMN |
716 | { |
717 | /* Git special-cases submodules in the check */ | |
718 | if (S_ISGITLINK(entry->mode)) | |
719 | return false; | |
720 | ||
25e84f95 | 721 | return git_index_entry_newer_than_index(entry, index); |
74975846 CMN |
722 | } |
723 | ||
316b820b CMN |
724 | /* |
725 | * Force the next diff to take a look at those entries which have the | |
726 | * same timestamp as the current index. | |
727 | */ | |
74975846 | 728 | static int truncate_racily_clean(git_index *index) |
316b820b CMN |
729 | { |
730 | size_t i; | |
74975846 | 731 | int error; |
316b820b | 732 | git_index_entry *entry; |
74975846 | 733 | git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; |
cb0ff012 ET |
734 | git_diff *diff = NULL; |
735 | git_vector paths = GIT_VECTOR_INIT; | |
736 | git_diff_delta *delta; | |
74975846 CMN |
737 | |
738 | /* Nothing to do if there's no repo to talk about */ | |
739 | if (!INDEX_OWNER(index)) | |
740 | return 0; | |
316b820b | 741 | |
74975846 CMN |
742 | /* If there's no workdir, we can't know where to even check */ |
743 | if (!git_repository_workdir(INDEX_OWNER(index))) | |
744 | return 0; | |
745 | ||
746 | diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH; | |
316b820b | 747 | git_vector_foreach(&index->entries, i, entry) { |
ac3d33df | 748 | if ((entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE) == 0 && |
25e84f95 | 749 | is_racy_entry(index, entry)) |
cb0ff012 ET |
750 | git_vector_insert(&paths, (char *)entry->path); |
751 | } | |
74975846 | 752 | |
cb0ff012 ET |
753 | if (paths.length == 0) |
754 | goto done; | |
74975846 | 755 | |
cb0ff012 ET |
756 | diff_opts.pathspec.count = paths.length; |
757 | diff_opts.pathspec.strings = (char **)paths.contents; | |
74975846 | 758 | |
cb0ff012 ET |
759 | if ((error = git_diff_index_to_workdir(&diff, INDEX_OWNER(index), index, &diff_opts)) < 0) |
760 | return error; | |
74975846 | 761 | |
cb0ff012 ET |
762 | git_vector_foreach(&diff->deltas, i, delta) { |
763 | entry = (git_index_entry *)git_index_get_bypath(index, delta->old_file.path, 0); | |
764 | ||
765 | /* Ensure that we have a stage 0 for this file (ie, it's not a | |
766 | * conflict), otherwise smudging it is quite pointless. | |
767 | */ | |
ac3d33df | 768 | if (entry) { |
cb0ff012 | 769 | entry->file_size = 0; |
ac3d33df JK |
770 | index->dirty = 1; |
771 | } | |
316b820b | 772 | } |
74975846 | 773 | |
cb0ff012 ET |
774 | done: |
775 | git_diff_free(diff); | |
776 | git_vector_free(&paths); | |
74975846 | 777 | return 0; |
316b820b CMN |
778 | } |
779 | ||
5625d86b DT |
780 | unsigned git_index_version(git_index *index) |
781 | { | |
c25aa7cd | 782 | GIT_ASSERT_ARG(index); |
5625d86b DT |
783 | |
784 | return index->version; | |
785 | } | |
786 | ||
787 | int git_index_set_version(git_index *index, unsigned int version) | |
788 | { | |
c25aa7cd | 789 | GIT_ASSERT_ARG(index); |
5625d86b DT |
790 | |
791 | if (version < INDEX_VERSION_NUMBER_LB || | |
792 | version > INDEX_VERSION_NUMBER_UB) { | |
ac3d33df | 793 | git_error_set(GIT_ERROR_INDEX, "invalid version number"); |
5625d86b DT |
794 | return -1; |
795 | } | |
796 | ||
797 | index->version = version; | |
798 | ||
799 | return 0; | |
800 | } | |
801 | ||
68535125 VM |
802 | int git_index_write(git_index *index) |
803 | { | |
55798fd1 | 804 | git_indexwriter writer = GIT_INDEXWRITER_INIT; |
348c7335 | 805 | int error; |
68535125 | 806 | |
316b820b CMN |
807 | truncate_racily_clean(index); |
808 | ||
ac3d33df JK |
809 | if ((error = git_indexwriter_init(&writer, index)) == 0 && |
810 | (error = git_indexwriter_commit(&writer)) == 0) | |
811 | index->dirty = 0; | |
68535125 | 812 | |
55798fd1 | 813 | git_indexwriter_cleanup(&writer); |
68535125 | 814 | |
55798fd1 | 815 | return error; |
68535125 | 816 | } |
41f1f9d7 | 817 | |
c25aa7cd | 818 | const char *git_index_path(const git_index *index) |
41f1f9d7 | 819 | { |
c25aa7cd | 820 | GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); |
41f1f9d7 RB |
821 | return index->index_file_path; |
822 | } | |
68535125 | 823 | |
276ea401 VM |
824 | int git_index_write_tree(git_oid *oid, git_index *index) |
825 | { | |
826 | git_repository *repo; | |
827 | ||
c25aa7cd PP |
828 | GIT_ASSERT_ARG(oid); |
829 | GIT_ASSERT_ARG(index); | |
276ea401 | 830 | |
95d73de1 | 831 | repo = INDEX_OWNER(index); |
276ea401 | 832 | |
33f95a9b | 833 | if (repo == NULL) |
834 | return create_index_error(-1, "Failed to write tree. " | |
909d5494 | 835 | "the index file is not backed up by an existing repository"); |
276ea401 VM |
836 | |
837 | return git_tree__write_index(oid, index, repo); | |
838 | } | |
839 | ||
dac16048 RB |
840 | int git_index_write_tree_to( |
841 | git_oid *oid, git_index *index, git_repository *repo) | |
276ea401 | 842 | { |
c25aa7cd PP |
843 | GIT_ASSERT_ARG(oid); |
844 | GIT_ASSERT_ARG(index); | |
845 | GIT_ASSERT_ARG(repo); | |
846 | ||
276ea401 VM |
847 | return git_tree__write_index(oid, index, repo); |
848 | } | |
849 | ||
a8122b5d | 850 | size_t git_index_entrycount(const git_index *index) |
0be42199 | 851 | { |
c25aa7cd PP |
852 | GIT_ASSERT_ARG(index); |
853 | ||
a8122b5d | 854 | return index->entries.length; |
0be42199 SC |
855 | } |
856 | ||
16248ee2 RB |
857 | const git_index_entry *git_index_get_byindex( |
858 | git_index *index, size_t n) | |
4c0b6a6d | 859 | { |
c25aa7cd PP |
860 | GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); |
861 | ||
9d81509a | 862 | git_vector_sort(&index->entries); |
f45ec1a0 | 863 | return git_vector_get(&index->entries, n); |
4c0b6a6d JP |
864 | } |
865 | ||
16248ee2 RB |
866 | const git_index_entry *git_index_get_bypath( |
867 | git_index *index, const char *path, int stage) | |
68535125 | 868 | { |
af1d5239 | 869 | git_index_entry key = {{ 0 }}; |
22a2d3d5 | 870 | git_index_entry *value; |
f45ec1a0 | 871 | |
c25aa7cd | 872 | GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); |
f45ec1a0 | 873 | |
af1d5239 | 874 | key.path = path; |
ac3d33df | 875 | GIT_INDEX_ENTRY_STAGE_SET(&key, stage); |
af1d5239 | 876 | |
22a2d3d5 UG |
877 | if (index->ignore_case) |
878 | value = git_idxmap_icase_get((git_idxmap_icase *) index->entries_map, &key); | |
879 | else | |
880 | value = git_idxmap_get(index->entries_map, &key); | |
f45ec1a0 | 881 | |
22a2d3d5 UG |
882 | if (!value) { |
883 | git_error_set(GIT_ERROR_INDEX, "index does not contain '%s'", path); | |
884 | return NULL; | |
885 | } | |
af1d5239 | 886 | |
22a2d3d5 | 887 | return value; |
68535125 VM |
888 | } |
889 | ||
14997dc5 RB |
890 | void git_index_entry__init_from_stat( |
891 | git_index_entry *entry, struct stat *st, bool trust_mode) | |
7c7ff7d1 | 892 | { |
b2ca8d9c ET |
893 | entry->ctime.seconds = (int32_t)st->st_ctime; |
894 | entry->mtime.seconds = (int32_t)st->st_mtime; | |
e9e6df2c | 895 | #if defined(GIT_USE_NSEC) |
3d6a42d1 ET |
896 | entry->mtime.nanoseconds = st->st_mtime_nsec; |
897 | entry->ctime.nanoseconds = st->st_ctime_nsec; | |
e7de893e | 898 | #endif |
7c7ff7d1 RB |
899 | entry->dev = st->st_rdev; |
900 | entry->ino = st->st_ino; | |
14997dc5 | 901 | entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ? |
05d47768 | 902 | git_index__create_mode(0666) : git_index__create_mode(st->st_mode); |
7c7ff7d1 RB |
903 | entry->uid = st->st_uid; |
904 | entry->gid = st->st_gid; | |
b2ca8d9c | 905 | entry->file_size = (uint32_t)st->st_size; |
7c7ff7d1 RB |
906 | } |
907 | ||
0cc20a8c VM |
908 | static void index_entry_adjust_namemask( |
909 | git_index_entry *entry, | |
910 | size_t path_length) | |
911 | { | |
ac3d33df | 912 | entry->flags &= ~GIT_INDEX_ENTRY_NAMEMASK; |
0cc20a8c | 913 | |
ac3d33df JK |
914 | if (path_length < GIT_INDEX_ENTRY_NAMEMASK) |
915 | entry->flags |= path_length & GIT_INDEX_ENTRY_NAMEMASK; | |
0cc20a8c | 916 | else |
ac3d33df | 917 | entry->flags |= GIT_INDEX_ENTRY_NAMEMASK; |
0cc20a8c VM |
918 | } |
919 | ||
318b825e ET |
920 | /* When `from_workdir` is true, we will validate the paths to avoid placing |
921 | * paths that are invalid for the working directory on the current filesystem | |
922 | * (eg, on Windows, we will disallow `GIT~1`, `AUX`, `COM1`, etc). This | |
923 | * function will *always* prevent `.git` and directory traversal `../` from | |
924 | * being added to the index. | |
925 | */ | |
a64119e3 ET |
926 | static int index_entry_create( |
927 | git_index_entry **out, | |
928 | git_repository *repo, | |
318b825e | 929 | const char *path, |
4b3ec53c | 930 | struct stat *st, |
318b825e | 931 | bool from_workdir) |
8a2834d3 | 932 | { |
f1453c59 | 933 | size_t pathlen = strlen(path), alloclen; |
0d388adc | 934 | struct entry_internal *entry; |
318b825e | 935 | unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS; |
4b3ec53c | 936 | uint16_t mode = 0; |
318b825e ET |
937 | |
938 | /* always reject placing `.git` in the index and directory traversal. | |
939 | * when requested, disallow platform-specific filenames and upgrade to | |
940 | * the platform-specific `.git` tests (eg, `git~1`, etc). | |
941 | */ | |
942 | if (from_workdir) | |
943 | path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS; | |
4b3ec53c XL |
944 | if (st) |
945 | mode = st->st_mode; | |
0d388adc | 946 | |
c25aa7cd | 947 | if (!git_path_validate(repo, path, mode, path_valid_flags)) { |
ac3d33df | 948 | git_error_set(GIT_ERROR_INDEX, "invalid path: '%s'", path); |
0d388adc | 949 | return -1; |
a64119e3 | 950 | } |
0d388adc | 951 | |
ac3d33df JK |
952 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(struct entry_internal), pathlen); |
953 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); | |
f1453c59 | 954 | entry = git__calloc(1, alloclen); |
ac3d33df | 955 | GIT_ERROR_CHECK_ALLOC(entry); |
8a2834d3 RB |
956 | |
957 | entry->pathlen = pathlen; | |
958 | memcpy(entry->path, path, pathlen); | |
959 | entry->entry.path = entry->path; | |
960 | ||
0d388adc VM |
961 | *out = (git_index_entry *)entry; |
962 | return 0; | |
8a2834d3 RB |
963 | } |
964 | ||
a16e4172 | 965 | static int index_entry_init( |
a64119e3 ET |
966 | git_index_entry **entry_out, |
967 | git_index *index, | |
968 | const char *rel_path) | |
76159921 | 969 | { |
a16e4172 | 970 | int error = 0; |
97769280 | 971 | git_index_entry *entry = NULL; |
4b3ec53c | 972 | git_buf path = GIT_BUF_INIT; |
76159921 KS |
973 | struct stat st; |
974 | git_oid oid; | |
4b3ec53c | 975 | git_repository *repo; |
9462c471 | 976 | |
33f95a9b | 977 | if (INDEX_OWNER(index) == NULL) |
978 | return create_index_error(-1, | |
909d5494 | 979 | "could not initialize index entry. " |
33f95a9b | 980 | "Index is not backed up by an existing repository."); |
981 | ||
4b3ec53c XL |
982 | /* |
983 | * FIXME: this is duplicated with the work in | |
984 | * git_blob__create_from_paths. It should accept an optional stat | |
985 | * structure so we can pass in the one we have to do here. | |
986 | */ | |
987 | repo = INDEX_OWNER(index); | |
988 | if (git_repository__ensure_not_bare(repo, "create blob from file") < 0) | |
989 | return GIT_EBAREREPO; | |
990 | ||
c25aa7cd | 991 | if (git_repository_workdir_path(&path, repo, rel_path) < 0) |
4b3ec53c XL |
992 | return -1; |
993 | ||
994 | error = git_path_lstat(path.ptr, &st); | |
ac3d33df | 995 | git_buf_dispose(&path); |
4b3ec53c XL |
996 | |
997 | if (error < 0) | |
998 | return error; | |
999 | ||
1000 | if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, &st, true) < 0) | |
0d388adc VM |
1001 | return -1; |
1002 | ||
a16e4172 RB |
1003 | /* write the blob to disk and get the oid and stat info */ |
1004 | error = git_blob__create_from_paths( | |
1005 | &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true); | |
76159921 | 1006 | |
0d388adc VM |
1007 | if (error < 0) { |
1008 | index_entry_free(entry); | |
1009 | return error; | |
1010 | } | |
76159921 | 1011 | |
d541170c | 1012 | entry->id = oid; |
8a2834d3 | 1013 | git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); |
76159921 | 1014 | |
8a2834d3 | 1015 | *entry_out = (git_index_entry *)entry; |
7c7ff7d1 | 1016 | return 0; |
76159921 KS |
1017 | } |
1018 | ||
8a2834d3 RB |
1019 | static git_index_reuc_entry *reuc_entry_alloc(const char *path) |
1020 | { | |
392702ee | 1021 | size_t pathlen = strlen(path), |
f1453c59 ET |
1022 | structlen = sizeof(struct reuc_entry_internal), |
1023 | alloclen; | |
392702ee ET |
1024 | struct reuc_entry_internal *entry; |
1025 | ||
f1453c59 ET |
1026 | if (GIT_ADD_SIZET_OVERFLOW(&alloclen, structlen, pathlen) || |
1027 | GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1)) | |
392702ee | 1028 | return NULL; |
392702ee | 1029 | |
f1453c59 | 1030 | entry = git__calloc(1, alloclen); |
8a2834d3 RB |
1031 | if (!entry) |
1032 | return NULL; | |
1033 | ||
1034 | entry->pathlen = pathlen; | |
1035 | memcpy(entry->path, path, pathlen); | |
1036 | entry->entry.path = entry->path; | |
1037 | ||
1038 | return (git_index_reuc_entry *)entry; | |
1039 | } | |
1040 | ||
f45ec1a0 ET |
1041 | static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, |
1042 | const char *path, | |
bec65a5e ET |
1043 | int ancestor_mode, const git_oid *ancestor_oid, |
1044 | int our_mode, const git_oid *our_oid, | |
1045 | int their_mode, const git_oid *their_oid) | |
f45ec1a0 ET |
1046 | { |
1047 | git_index_reuc_entry *reuc = NULL; | |
1048 | ||
c25aa7cd PP |
1049 | GIT_ASSERT_ARG(reuc_out); |
1050 | GIT_ASSERT_ARG(path); | |
f45ec1a0 | 1051 | |
8a2834d3 | 1052 | *reuc_out = reuc = reuc_entry_alloc(path); |
ac3d33df | 1053 | GIT_ERROR_CHECK_ALLOC(reuc); |
f45ec1a0 | 1054 | |
80a834a5 | 1055 | if ((reuc->mode[0] = ancestor_mode) > 0) { |
c25aa7cd | 1056 | GIT_ASSERT(ancestor_oid); |
eb3c247a | 1057 | git_oid_cpy(&reuc->oid[0], ancestor_oid); |
80a834a5 | 1058 | } |
f45ec1a0 | 1059 | |
80a834a5 | 1060 | if ((reuc->mode[1] = our_mode) > 0) { |
c25aa7cd | 1061 | GIT_ASSERT(our_oid); |
eb3c247a | 1062 | git_oid_cpy(&reuc->oid[1], our_oid); |
80a834a5 | 1063 | } |
f45ec1a0 | 1064 | |
80a834a5 | 1065 | if ((reuc->mode[2] = their_mode) > 0) { |
c25aa7cd | 1066 | GIT_ASSERT(their_oid); |
eb3c247a | 1067 | git_oid_cpy(&reuc->oid[2], their_oid); |
80a834a5 | 1068 | } |
f45ec1a0 | 1069 | |
f45ec1a0 ET |
1070 | return 0; |
1071 | } | |
1072 | ||
a32bc85e ET |
1073 | static void index_entry_cpy( |
1074 | git_index_entry *tgt, | |
5f32c506 | 1075 | const git_index_entry *src) |
8a2834d3 | 1076 | { |
0fc8e1f6 | 1077 | const char *tgt_path = tgt->path; |
8a2834d3 | 1078 | memcpy(tgt, src, sizeof(*tgt)); |
a32bc85e | 1079 | tgt->path = tgt_path; |
8a2834d3 RB |
1080 | } |
1081 | ||
a64119e3 ET |
1082 | static int index_entry_dup( |
1083 | git_index_entry **out, | |
a32bc85e | 1084 | git_index *index, |
a64119e3 | 1085 | const git_index_entry *src) |
51917d9c | 1086 | { |
4b3ec53c | 1087 | if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0) |
5f32c506 | 1088 | return -1; |
51917d9c | 1089 | |
5f32c506 ET |
1090 | index_entry_cpy(*out, src); |
1091 | return 0; | |
1092 | } | |
3dbee456 | 1093 | |
5f32c506 ET |
1094 | static void index_entry_cpy_nocache( |
1095 | git_index_entry *tgt, | |
1096 | const git_index_entry *src) | |
1097 | { | |
1098 | git_oid_cpy(&tgt->id, &src->id); | |
1099 | tgt->mode = src->mode; | |
1100 | tgt->flags = src->flags; | |
ac3d33df | 1101 | tgt->flags_extended = (src->flags_extended & GIT_INDEX_ENTRY_EXTENDED_FLAGS); |
5f32c506 ET |
1102 | } |
1103 | ||
1104 | static int index_entry_dup_nocache( | |
1105 | git_index_entry **out, | |
1106 | git_index *index, | |
1107 | const git_index_entry *src) | |
1108 | { | |
4b3ec53c | 1109 | if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0) |
0d388adc | 1110 | return -1; |
51917d9c | 1111 | |
5f32c506 | 1112 | index_entry_cpy_nocache(*out, src); |
3dbee456 | 1113 | return 0; |
51917d9c KS |
1114 | } |
1115 | ||
53bec813 | 1116 | static int has_file_name(git_index *index, |
1eefd356 | 1117 | const git_index_entry *entry, size_t pos, int ok_to_replace) |
53bec813 | 1118 | { |
1eefd356 | 1119 | size_t len = strlen(entry->path); |
ac3d33df | 1120 | int stage = GIT_INDEX_ENTRY_STAGE(entry); |
1eefd356 | 1121 | const char *name = entry->path; |
53bec813 | 1122 | |
1eefd356 | 1123 | while (pos < index->entries.length) { |
8a2834d3 | 1124 | struct entry_internal *p = index->entries.contents[pos++]; |
53bec813 | 1125 | |
8a2834d3 | 1126 | if (len >= p->pathlen) |
53bec813 | 1127 | break; |
1eefd356 | 1128 | if (memcmp(name, p->path, len)) |
53bec813 | 1129 | break; |
ac3d33df | 1130 | if (GIT_INDEX_ENTRY_STAGE(&p->entry) != stage) |
53bec813 | 1131 | continue; |
1eefd356 | 1132 | if (p->path[len] != '/') |
53bec813 | 1133 | continue; |
53bec813 | 1134 | if (!ok_to_replace) |
ac3d33df | 1135 | return -1; |
3dbee456 | 1136 | |
8a2834d3 | 1137 | if (index_remove_entry(index, --pos) < 0) |
3dbee456 | 1138 | break; |
53bec813 | 1139 | } |
ac3d33df | 1140 | return 0; |
53bec813 VM |
1141 | } |
1142 | ||
1143 | /* | |
1144 | * Do we have another file with a pathname that is a proper | |
1145 | * subset of the name we're trying to add? | |
1146 | */ | |
1147 | static int has_dir_name(git_index *index, | |
1148 | const git_index_entry *entry, int ok_to_replace) | |
1149 | { | |
ac3d33df | 1150 | int stage = GIT_INDEX_ENTRY_STAGE(entry); |
1eefd356 VM |
1151 | const char *name = entry->path; |
1152 | const char *slash = name + strlen(name); | |
53bec813 VM |
1153 | |
1154 | for (;;) { | |
8a2834d3 | 1155 | size_t len, pos; |
53bec813 VM |
1156 | |
1157 | for (;;) { | |
1158 | if (*--slash == '/') | |
1159 | break; | |
1eefd356 | 1160 | if (slash <= entry->path) |
ac3d33df | 1161 | return 0; |
53bec813 VM |
1162 | } |
1163 | len = slash - name; | |
1164 | ||
9d81509a | 1165 | if (!index_find(&pos, index, name, len, stage)) { |
1eefd356 | 1166 | if (!ok_to_replace) |
ac3d33df | 1167 | return -1; |
1eefd356 | 1168 | |
8a2834d3 | 1169 | if (index_remove_entry(index, pos) < 0) |
3dbee456 | 1170 | break; |
1eefd356 | 1171 | continue; |
53bec813 | 1172 | } |
53bec813 VM |
1173 | |
1174 | /* | |
1175 | * Trivial optimization: if we find an entry that | |
1176 | * already matches the sub-directory, then we know | |
1177 | * we're ok, and we can exit. | |
1178 | */ | |
8a2834d3 RB |
1179 | for (; pos < index->entries.length; ++pos) { |
1180 | struct entry_internal *p = index->entries.contents[pos]; | |
1eefd356 | 1181 | |
8a2834d3 RB |
1182 | if (p->pathlen <= len || |
1183 | p->path[len] != '/' || | |
1eefd356 | 1184 | memcmp(p->path, name, len)) |
53bec813 | 1185 | break; /* not our subdirectory */ |
1eefd356 | 1186 | |
ac3d33df JK |
1187 | if (GIT_INDEX_ENTRY_STAGE(&p->entry) == stage) |
1188 | return 0; | |
53bec813 VM |
1189 | } |
1190 | } | |
8a2834d3 | 1191 | |
ac3d33df | 1192 | return 0; |
53bec813 | 1193 | } |
1eefd356 | 1194 | |
b794cbcd | 1195 | static int check_file_directory_collision(git_index *index, |
1eefd356 VM |
1196 | git_index_entry *entry, size_t pos, int ok_to_replace) |
1197 | { | |
ac3d33df JK |
1198 | if (has_file_name(index, entry, pos, ok_to_replace) < 0 || |
1199 | has_dir_name(index, entry, ok_to_replace) < 0) { | |
1200 | git_error_set(GIT_ERROR_INDEX, | |
8a2834d3 | 1201 | "'%s' appears as both a file and a directory", entry->path); |
1eefd356 VM |
1202 | return -1; |
1203 | } | |
1204 | ||
1205 | return 0; | |
1206 | } | |
53bec813 | 1207 | |
280adb3f | 1208 | static int canonicalize_directory_path( |
21515f22 ET |
1209 | git_index *index, |
1210 | git_index_entry *entry, | |
1211 | git_index_entry *existing) | |
280adb3f ET |
1212 | { |
1213 | const git_index_entry *match, *best = NULL; | |
1214 | char *search, *sep; | |
1215 | size_t pos, search_len, best_len; | |
1216 | ||
1217 | if (!index->ignore_case) | |
1218 | return 0; | |
1219 | ||
1220 | /* item already exists in the index, simply re-use the existing case */ | |
21515f22 ET |
1221 | if (existing) { |
1222 | memcpy((char *)entry->path, existing->path, strlen(existing->path)); | |
280adb3f ET |
1223 | return 0; |
1224 | } | |
1225 | ||
1226 | /* nothing to do */ | |
1227 | if (strchr(entry->path, '/') == NULL) | |
1228 | return 0; | |
1229 | ||
1230 | if ((search = git__strdup(entry->path)) == NULL) | |
1231 | return -1; | |
1232 | ||
1233 | /* starting at the parent directory and descending to the root, find the | |
1234 | * common parent directory. | |
1235 | */ | |
1236 | while (!best && (sep = strrchr(search, '/'))) { | |
1237 | sep[1] = '\0'; | |
1238 | ||
1239 | search_len = strlen(search); | |
1240 | ||
1241 | git_vector_bsearch2( | |
1242 | &pos, &index->entries, index->entries_search_path, search); | |
1243 | ||
1244 | while ((match = git_vector_get(&index->entries, pos))) { | |
ac3d33df | 1245 | if (GIT_INDEX_ENTRY_STAGE(match) != 0) { |
280adb3f | 1246 | /* conflicts do not contribute to canonical paths */ |
626f9e24 | 1247 | } else if (strncmp(search, match->path, search_len) == 0) { |
280adb3f ET |
1248 | /* prefer an exact match to the input filename */ |
1249 | best = match; | |
1250 | best_len = search_len; | |
1251 | break; | |
1252 | } else if (strncasecmp(search, match->path, search_len) == 0) { | |
1253 | /* continue walking, there may be a path with an exact | |
1254 | * (case sensitive) match later in the index, but use this | |
1255 | * as the best match until that happens. | |
1256 | */ | |
1257 | if (!best) { | |
1258 | best = match; | |
1259 | best_len = search_len; | |
1260 | } | |
1261 | } else { | |
1262 | break; | |
1263 | } | |
1264 | ||
1265 | pos++; | |
1266 | } | |
1267 | ||
1268 | sep[0] = '\0'; | |
1269 | } | |
1270 | ||
1271 | if (best) | |
1272 | memcpy((char *)entry->path, best->path, best_len); | |
1273 | ||
1274 | git__free(search); | |
1275 | return 0; | |
1276 | } | |
1277 | ||
ea642d61 RB |
1278 | static int index_no_dups(void **old, void *new) |
1279 | { | |
1280 | const git_index_entry *entry = new; | |
1281 | GIT_UNUSED(old); | |
ac3d33df JK |
1282 | git_error_set(GIT_ERROR_INDEX, "'%s' appears multiple times at stage %d", |
1283 | entry->path, GIT_INDEX_ENTRY_STAGE(entry)); | |
ea642d61 RB |
1284 | return GIT_EEXISTS; |
1285 | } | |
1286 | ||
21515f22 | 1287 | static void index_existing_and_best( |
128e94bb | 1288 | git_index_entry **existing, |
21515f22 | 1289 | size_t *existing_position, |
128e94bb | 1290 | git_index_entry **best, |
21515f22 ET |
1291 | git_index *index, |
1292 | const git_index_entry *entry) | |
1293 | { | |
128e94bb | 1294 | git_index_entry *e; |
21515f22 ET |
1295 | size_t pos; |
1296 | int error; | |
1297 | ||
1298 | error = index_find(&pos, | |
ac3d33df | 1299 | index, entry->path, 0, GIT_INDEX_ENTRY_STAGE(entry)); |
21515f22 ET |
1300 | |
1301 | if (error == 0) { | |
1302 | *existing = index->entries.contents[pos]; | |
1303 | *existing_position = pos; | |
1304 | *best = index->entries.contents[pos]; | |
1305 | return; | |
1306 | } | |
1307 | ||
1308 | *existing = NULL; | |
1309 | *existing_position = 0; | |
1310 | *best = NULL; | |
1311 | ||
ac3d33df | 1312 | if (GIT_INDEX_ENTRY_STAGE(entry) == 0) { |
21515f22 ET |
1313 | for (; pos < index->entries.length; pos++) { |
1314 | int (*strcomp)(const char *a, const char *b) = | |
1315 | index->ignore_case ? git__strcasecmp : git__strcmp; | |
1316 | ||
1317 | e = index->entries.contents[pos]; | |
1318 | ||
1319 | if (strcomp(entry->path, e->path) != 0) | |
1320 | break; | |
1321 | ||
ac3d33df | 1322 | if (GIT_INDEX_ENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) { |
21515f22 ET |
1323 | *best = e; |
1324 | continue; | |
1325 | } else { | |
1326 | *best = e; | |
1327 | break; | |
1328 | } | |
1329 | } | |
1330 | } | |
1331 | } | |
1332 | ||
3dbee456 RB |
1333 | /* index_insert takes ownership of the new entry - if it can't insert |
1334 | * it, then it will return an error **and also free the entry**. When | |
1335 | * it replaces an existing entry, it will update the entry_ptr with the | |
1336 | * actual entry in the index (and free the passed in one). | |
6ddf533a | 1337 | * |
280adb3f ET |
1338 | * trust_path is whether we use the given path, or whether (on case |
1339 | * insensitive systems only) we try to canonicalize the given path to | |
1340 | * be within an existing directory. | |
6ddf533a | 1341 | * |
d3282680 | 1342 | * trust_mode is whether we trust the mode in entry_ptr. |
6ddf533a ET |
1343 | * |
1344 | * trust_id is whether we trust the id or it should be validated. | |
3dbee456 RB |
1345 | */ |
1346 | static int index_insert( | |
280adb3f ET |
1347 | git_index *index, |
1348 | git_index_entry **entry_ptr, | |
1349 | int replace, | |
1350 | bool trust_path, | |
6ddf533a ET |
1351 | bool trust_mode, |
1352 | bool trust_id) | |
68535125 | 1353 | { |
21515f22 | 1354 | git_index_entry *existing, *best, *entry; |
ac3d33df JK |
1355 | size_t path_length, position; |
1356 | int error; | |
68535125 | 1357 | |
c25aa7cd PP |
1358 | GIT_ASSERT_ARG(index); |
1359 | GIT_ASSERT_ARG(entry_ptr); | |
3dbee456 RB |
1360 | |
1361 | entry = *entry_ptr; | |
68535125 | 1362 | |
ac3d33df | 1363 | /* Make sure that the path length flag is correct */ |
8a2834d3 | 1364 | path_length = ((struct entry_internal *)entry)->pathlen; |
0cc20a8c | 1365 | index_entry_adjust_namemask(entry, path_length); |
c3a20d5c | 1366 | |
ac3d33df JK |
1367 | /* This entry is now up-to-date and should not be checked for raciness */ |
1368 | entry->flags_extended |= GIT_INDEX_ENTRY_UPTODATE; | |
d1101263 | 1369 | |
8a2834d3 RB |
1370 | git_vector_sort(&index->entries); |
1371 | ||
ac3d33df JK |
1372 | /* |
1373 | * Look if an entry with this path already exists, either staged, or (if | |
21515f22 ET |
1374 | * this entry is a regular staged item) as the "ours" side of a conflict. |
1375 | */ | |
1376 | index_existing_and_best(&existing, &position, &best, index, entry); | |
1377 | ||
ac3d33df | 1378 | /* Update the file mode */ |
21515f22 ET |
1379 | entry->mode = trust_mode ? |
1380 | git_index__create_mode(entry->mode) : | |
1381 | index_merge_mode(index, best, entry->mode); | |
da825c92 | 1382 | |
ac3d33df JK |
1383 | /* Canonicalize the directory name */ |
1384 | if (!trust_path && (error = canonicalize_directory_path(index, entry, best)) < 0) | |
1385 | goto out; | |
280adb3f | 1386 | |
ac3d33df JK |
1387 | /* Ensure that the given id exists (unless it's a submodule) */ |
1388 | if (!trust_id && INDEX_OWNER(index) && | |
1389 | (entry->mode & GIT_FILEMODE_COMMIT) != GIT_FILEMODE_COMMIT) { | |
6ddf533a ET |
1390 | |
1391 | if (!git_object__is_valid(INDEX_OWNER(index), &entry->id, | |
ac3d33df | 1392 | git_object__type_from_filemode(entry->mode))) { |
6ddf533a | 1393 | error = -1; |
ac3d33df JK |
1394 | goto out; |
1395 | } | |
6ddf533a ET |
1396 | } |
1397 | ||
ac3d33df JK |
1398 | /* Look for tree / blob name collisions, removing conflicts if requested */ |
1399 | if ((error = check_file_directory_collision(index, entry, position, replace)) < 0) | |
1400 | goto out; | |
280adb3f | 1401 | |
ac3d33df JK |
1402 | /* |
1403 | * If we are replacing an existing item, overwrite the existing entry | |
3dbee456 RB |
1404 | * and return it in place of the passed in one. |
1405 | */ | |
ac3d33df | 1406 | if (existing) { |
5f32c506 ET |
1407 | if (replace) { |
1408 | index_entry_cpy(existing, entry); | |
1409 | ||
1410 | if (trust_path) | |
1411 | memcpy((char *)existing->path, entry->path, strlen(entry->path)); | |
1412 | } | |
1413 | ||
8a2834d3 | 1414 | index_entry_free(entry); |
ac3d33df JK |
1415 | *entry_ptr = existing; |
1416 | } else { | |
1417 | /* | |
1418 | * If replace is not requested or no existing entry exists, insert | |
ea642d61 RB |
1419 | * at the sorted position. (Since we re-sort after each insert to |
1420 | * check for dups, this is actually cheaper in the long run.) | |
8a2834d3 | 1421 | */ |
22a2d3d5 UG |
1422 | if ((error = git_vector_insert_sorted(&index->entries, entry, index_no_dups)) < 0 || |
1423 | (error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0) | |
ac3d33df | 1424 | goto out; |
dac16048 | 1425 | } |
8cc16e29 | 1426 | |
ac3d33df JK |
1427 | index->dirty = 1; |
1428 | ||
1429 | out: | |
3dbee456 RB |
1430 | if (error < 0) { |
1431 | index_entry_free(*entry_ptr); | |
1432 | *entry_ptr = NULL; | |
1433 | } | |
68535125 | 1434 | |
3dbee456 | 1435 | return error; |
68535125 VM |
1436 | } |
1437 | ||
f45ec1a0 | 1438 | static int index_conflict_to_reuc(git_index *index, const char *path) |
f7a5058a | 1439 | { |
0e0108f7 | 1440 | const git_index_entry *conflict_entries[3]; |
f45ec1a0 | 1441 | int ancestor_mode, our_mode, their_mode; |
bec65a5e | 1442 | git_oid const *ancestor_oid, *our_oid, *their_oid; |
76159921 | 1443 | int ret; |
f7a5058a | 1444 | |
f45ec1a0 ET |
1445 | if ((ret = git_index_conflict_get(&conflict_entries[0], |
1446 | &conflict_entries[1], &conflict_entries[2], index, path)) < 0) | |
7c7ff7d1 | 1447 | return ret; |
f7a5058a | 1448 | |
f45ec1a0 ET |
1449 | ancestor_mode = conflict_entries[0] == NULL ? 0 : conflict_entries[0]->mode; |
1450 | our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode; | |
1451 | their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode; | |
f7a5058a | 1452 | |
d541170c CMN |
1453 | ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->id; |
1454 | our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->id; | |
1455 | their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->id; | |
f45ec1a0 ET |
1456 | |
1457 | if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid, | |
1458 | our_mode, our_oid, their_mode, their_oid)) >= 0) | |
1459 | ret = git_index_conflict_remove(index, path); | |
1460 | ||
1461 | return ret; | |
f7a5058a VM |
1462 | } |
1463 | ||
eae0bfdc | 1464 | GIT_INLINE(bool) is_file_or_link(const int filemode) |
a275fbc0 DP |
1465 | { |
1466 | return (filemode == GIT_FILEMODE_BLOB || | |
1467 | filemode == GIT_FILEMODE_BLOB_EXECUTABLE || | |
eae0bfdc PP |
1468 | filemode == GIT_FILEMODE_LINK); |
1469 | } | |
1470 | ||
1471 | GIT_INLINE(bool) valid_filemode(const int filemode) | |
1472 | { | |
1473 | return (is_file_or_link(filemode) || filemode == GIT_FILEMODE_COMMIT); | |
a275fbc0 DP |
1474 | } |
1475 | ||
22a2d3d5 | 1476 | int git_index_add_from_buffer( |
807566d5 | 1477 | git_index *index, const git_index_entry *source_entry, |
a275fbc0 DP |
1478 | const void *buffer, size_t len) |
1479 | { | |
1480 | git_index_entry *entry = NULL; | |
1481 | int error = 0; | |
1482 | git_oid id; | |
1483 | ||
c25aa7cd PP |
1484 | GIT_ASSERT_ARG(index); |
1485 | GIT_ASSERT_ARG(source_entry && source_entry->path); | |
a275fbc0 DP |
1486 | |
1487 | if (INDEX_OWNER(index) == NULL) | |
1488 | return create_index_error(-1, | |
909d5494 | 1489 | "could not initialize index entry. " |
a275fbc0 DP |
1490 | "Index is not backed up by an existing repository."); |
1491 | ||
eae0bfdc | 1492 | if (!is_file_or_link(source_entry->mode)) { |
ac3d33df | 1493 | git_error_set(GIT_ERROR_INDEX, "invalid filemode"); |
a275fbc0 DP |
1494 | return -1; |
1495 | } | |
1496 | ||
22a2d3d5 UG |
1497 | if (len > UINT32_MAX) { |
1498 | git_error_set(GIT_ERROR_INDEX, "buffer is too large"); | |
1499 | return -1; | |
1500 | } | |
1501 | ||
a32bc85e | 1502 | if (index_entry_dup(&entry, index, source_entry) < 0) |
a275fbc0 DP |
1503 | return -1; |
1504 | ||
22a2d3d5 | 1505 | error = git_blob_create_from_buffer(&id, INDEX_OWNER(index), buffer, len); |
a275fbc0 DP |
1506 | if (error < 0) { |
1507 | index_entry_free(entry); | |
1508 | return error; | |
1509 | } | |
1510 | ||
1511 | git_oid_cpy(&entry->id, &id); | |
22a2d3d5 | 1512 | entry->file_size = (uint32_t)len; |
a275fbc0 | 1513 | |
6ddf533a | 1514 | if ((error = index_insert(index, &entry, 1, true, true, true)) < 0) |
a275fbc0 DP |
1515 | return error; |
1516 | ||
1517 | /* Adding implies conflict was resolved, move conflict entries to REUC */ | |
1518 | if ((error = index_conflict_to_reuc(index, entry->path)) < 0 && error != GIT_ENOTFOUND) | |
1519 | return error; | |
1520 | ||
1521 | git_tree_cache_invalidate_path(index->tree, entry->path); | |
1522 | return 0; | |
1523 | } | |
1524 | ||
ea961abf CMN |
1525 | static int add_repo_as_submodule(git_index_entry **out, git_index *index, const char *path) |
1526 | { | |
1527 | git_repository *sub; | |
1528 | git_buf abspath = GIT_BUF_INIT; | |
1529 | git_repository *repo = INDEX_OWNER(index); | |
1530 | git_reference *head; | |
1531 | git_index_entry *entry; | |
1532 | struct stat st; | |
1533 | int error; | |
1534 | ||
c25aa7cd | 1535 | if ((error = git_repository_workdir_path(&abspath, repo, path)) < 0) |
ea961abf CMN |
1536 | return error; |
1537 | ||
1538 | if ((error = p_stat(abspath.ptr, &st)) < 0) { | |
ac3d33df | 1539 | git_error_set(GIT_ERROR_OS, "failed to stat repository dir"); |
ea961abf CMN |
1540 | return -1; |
1541 | } | |
1542 | ||
4b3ec53c XL |
1543 | if (index_entry_create(&entry, INDEX_OWNER(index), path, &st, true) < 0) |
1544 | return -1; | |
1545 | ||
ea961abf CMN |
1546 | git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); |
1547 | ||
1548 | if ((error = git_repository_open(&sub, abspath.ptr)) < 0) | |
1549 | return error; | |
1550 | ||
1551 | if ((error = git_repository_head(&head, sub)) < 0) | |
1552 | return error; | |
1553 | ||
1554 | git_oid_cpy(&entry->id, git_reference_target(head)); | |
1555 | entry->mode = GIT_FILEMODE_COMMIT; | |
1556 | ||
1557 | git_reference_free(head); | |
1558 | git_repository_free(sub); | |
ac3d33df | 1559 | git_buf_dispose(&abspath); |
ea961abf CMN |
1560 | |
1561 | *out = entry; | |
1562 | return 0; | |
1563 | } | |
a275fbc0 | 1564 | |
25743bd7 | 1565 | int git_index_add_bypath(git_index *index, const char *path) |
f7a5058a | 1566 | { |
f45ec1a0 ET |
1567 | git_index_entry *entry = NULL; |
1568 | int ret; | |
1569 | ||
c25aa7cd PP |
1570 | GIT_ASSERT_ARG(index); |
1571 | GIT_ASSERT_ARG(path); | |
f45ec1a0 | 1572 | |
247d27c2 | 1573 | if ((ret = index_entry_init(&entry, index, path)) == 0) |
6ddf533a | 1574 | ret = index_insert(index, &entry, 1, false, false, true); |
247d27c2 CMN |
1575 | |
1576 | /* If we were given a directory, let's see if it's a submodule */ | |
1577 | if (ret < 0 && ret != GIT_EDIRECTORY) | |
1578 | return ret; | |
1579 | ||
1580 | if (ret == GIT_EDIRECTORY) { | |
1581 | git_submodule *sm; | |
1582 | git_error_state err; | |
1583 | ||
ac3d33df | 1584 | git_error_state_capture(&err, ret); |
247d27c2 CMN |
1585 | |
1586 | ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path); | |
1587 | if (ret == GIT_ENOTFOUND) | |
ac3d33df | 1588 | return git_error_state_restore(&err); |
247d27c2 | 1589 | |
ac3d33df | 1590 | git_error_state_free(&err); |
ea961abf CMN |
1591 | |
1592 | /* | |
1593 | * EEXISTS means that there is a repository at that path, but it's not known | |
1594 | * as a submodule. We add its HEAD as an entry and don't register it. | |
1595 | */ | |
1596 | if (ret == GIT_EEXISTS) { | |
1597 | if ((ret = add_repo_as_submodule(&entry, index, path)) < 0) | |
1598 | return ret; | |
1599 | ||
6ddf533a | 1600 | if ((ret = index_insert(index, &entry, 1, false, false, true)) < 0) |
ea961abf CMN |
1601 | return ret; |
1602 | } else if (ret < 0) { | |
1603 | return ret; | |
1604 | } else { | |
1605 | ret = git_submodule_add_to_index(sm, false); | |
1606 | git_submodule_free(sm); | |
1607 | return ret; | |
1608 | } | |
247d27c2 | 1609 | } |
f45ec1a0 ET |
1610 | |
1611 | /* Adding implies conflict was resolved, move conflict entries to REUC */ | |
1612 | if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND) | |
3dbee456 | 1613 | return ret; |
f45ec1a0 ET |
1614 | |
1615 | git_tree_cache_invalidate_path(index->tree, entry->path); | |
1616 | return 0; | |
f939d39b KS |
1617 | } |
1618 | ||
25743bd7 ET |
1619 | int git_index_remove_bypath(git_index *index, const char *path) |
1620 | { | |
1621 | int ret; | |
1622 | ||
c25aa7cd PP |
1623 | GIT_ASSERT_ARG(index); |
1624 | GIT_ASSERT_ARG(path); | |
25743bd7 ET |
1625 | |
1626 | if (((ret = git_index_remove(index, path, 0)) < 0 && | |
1627 | ret != GIT_ENOTFOUND) || | |
1628 | ((ret = index_conflict_to_reuc(index, path)) < 0 && | |
1629 | ret != GIT_ENOTFOUND)) | |
1630 | return ret; | |
1631 | ||
3ab5a659 | 1632 | if (ret == GIT_ENOTFOUND) |
ac3d33df | 1633 | git_error_clear(); |
3ab5a659 | 1634 | |
25743bd7 ET |
1635 | return 0; |
1636 | } | |
1637 | ||
879ebab3 VM |
1638 | int git_index__fill(git_index *index, const git_vector *source_entries) |
1639 | { | |
1640 | const git_index_entry *source_entry = NULL; | |
22a2d3d5 | 1641 | int error = 0; |
879ebab3 | 1642 | size_t i; |
879ebab3 | 1643 | |
c25aa7cd | 1644 | GIT_ASSERT_ARG(index); |
879ebab3 | 1645 | |
d7d46cfb VM |
1646 | if (!source_entries->length) |
1647 | return 0; | |
1648 | ||
22a2d3d5 UG |
1649 | if (git_vector_size_hint(&index->entries, source_entries->length) < 0 || |
1650 | index_map_resize(index->entries_map, (size_t)(source_entries->length * 1.3), | |
1651 | index->ignore_case) < 0) | |
1652 | return -1; | |
d7d46cfb | 1653 | |
879ebab3 VM |
1654 | git_vector_foreach(source_entries, i, source_entry) { |
1655 | git_index_entry *entry = NULL; | |
1656 | ||
22a2d3d5 | 1657 | if ((error = index_entry_dup(&entry, index, source_entry)) < 0) |
879ebab3 VM |
1658 | break; |
1659 | ||
0cc20a8c | 1660 | index_entry_adjust_namemask(entry, ((struct entry_internal *)entry)->pathlen); |
ac3d33df | 1661 | entry->flags_extended |= GIT_INDEX_ENTRY_UPTODATE; |
0cc20a8c | 1662 | entry->mode = git_index__create_mode(entry->mode); |
879ebab3 | 1663 | |
22a2d3d5 | 1664 | if ((error = git_vector_insert(&index->entries, entry)) < 0) |
879ebab3 VM |
1665 | break; |
1666 | ||
22a2d3d5 | 1667 | if ((error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0) |
879ebab3 | 1668 | break; |
ac3d33df JK |
1669 | |
1670 | index->dirty = 1; | |
879ebab3 VM |
1671 | } |
1672 | ||
22a2d3d5 | 1673 | if (!error) |
879ebab3 VM |
1674 | git_vector_sort(&index->entries); |
1675 | ||
22a2d3d5 | 1676 | return error; |
879ebab3 VM |
1677 | } |
1678 | ||
052a2ffd | 1679 | |
f45ec1a0 | 1680 | int git_index_add(git_index *index, const git_index_entry *source_entry) |
f939d39b KS |
1681 | { |
1682 | git_index_entry *entry = NULL; | |
1683 | int ret; | |
1684 | ||
c25aa7cd PP |
1685 | GIT_ASSERT_ARG(index); |
1686 | GIT_ASSERT_ARG(source_entry && source_entry->path); | |
f939d39b | 1687 | |
052a2ffd | 1688 | if (!valid_filemode(source_entry->mode)) { |
ac3d33df | 1689 | git_error_set(GIT_ERROR_INDEX, "invalid entry mode"); |
052a2ffd CMN |
1690 | return -1; |
1691 | } | |
1692 | ||
a32bc85e | 1693 | if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 || |
6ddf533a | 1694 | (ret = index_insert(index, &entry, 1, true, true, false)) < 0) |
7c7ff7d1 | 1695 | return ret; |
f939d39b | 1696 | |
e23ede0d | 1697 | git_tree_cache_invalidate_path(index->tree, entry->path); |
7c7ff7d1 | 1698 | return 0; |
f7a5058a VM |
1699 | } |
1700 | ||
f45ec1a0 | 1701 | int git_index_remove(git_index *index, const char *path, int stage) |
68535125 | 1702 | { |
8a2834d3 | 1703 | int error; |
11d9f6b3 | 1704 | size_t position; |
af1d5239 | 1705 | git_index_entry remove_key = {{ 0 }}; |
b419fe2d | 1706 | |
af1d5239 | 1707 | remove_key.path = path; |
ac3d33df | 1708 | GIT_INDEX_ENTRY_STAGE_SET(&remove_key, stage); |
81b76367 | 1709 | |
22a2d3d5 | 1710 | index_map_delete(index->entries_map, &remove_key, index->ignore_case); |
af1d5239 | 1711 | |
9d81509a | 1712 | if (index_find(&position, index, path, 0, stage) < 0) { |
ac3d33df JK |
1713 | git_error_set( |
1714 | GIT_ERROR_INDEX, "index does not contain %s at stage %d", path, stage); | |
8a2834d3 RB |
1715 | error = GIT_ENOTFOUND; |
1716 | } else { | |
1717 | error = index_remove_entry(index, position); | |
ae99f5e2 | 1718 | } |
f45ec1a0 | 1719 | |
8a2834d3 | 1720 | return error; |
c4034e63 | 1721 | } |
68535125 | 1722 | |
7fc00435 RB |
1723 | int git_index_remove_directory(git_index *index, const char *dir, int stage) |
1724 | { | |
1725 | git_buf pfx = GIT_BUF_INIT; | |
1726 | int error = 0; | |
1727 | size_t pos; | |
1728 | git_index_entry *entry; | |
1729 | ||
8a2834d3 RB |
1730 | if (!(error = git_buf_sets(&pfx, dir)) && |
1731 | !(error = git_path_to_dir(&pfx))) | |
9d81509a | 1732 | index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY); |
7fc00435 | 1733 | |
8a2834d3 | 1734 | while (!error) { |
7fc00435 RB |
1735 | entry = git_vector_get(&index->entries, pos); |
1736 | if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0) | |
1737 | break; | |
1738 | ||
ac3d33df | 1739 | if (GIT_INDEX_ENTRY_STAGE(entry) != stage) { |
7fc00435 RB |
1740 | ++pos; |
1741 | continue; | |
1742 | } | |
1743 | ||
8a2834d3 | 1744 | error = index_remove_entry(index, pos); |
7fc00435 | 1745 | |
8a2834d3 | 1746 | /* removed entry at 'pos' so we don't need to increment */ |
7fc00435 RB |
1747 | } |
1748 | ||
ac3d33df | 1749 | git_buf_dispose(&pfx); |
7fc00435 RB |
1750 | |
1751 | return error; | |
1752 | } | |
1753 | ||
c097f717 LY |
1754 | int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix) |
1755 | { | |
1756 | int error = 0; | |
1757 | size_t pos; | |
1758 | const git_index_entry *entry; | |
1759 | ||
9d81509a | 1760 | index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY); |
c097f717 LY |
1761 | entry = git_vector_get(&index->entries, pos); |
1762 | if (!entry || git__prefixcmp(entry->path, prefix) != 0) | |
1763 | error = GIT_ENOTFOUND; | |
1764 | ||
1765 | if (!error && at_pos) | |
1766 | *at_pos = pos; | |
1767 | ||
c097f717 LY |
1768 | return error; |
1769 | } | |
1770 | ||
52bb0476 | 1771 | int git_index__find_pos( |
54edbb98 RB |
1772 | size_t *out, git_index *index, const char *path, size_t path_len, int stage) |
1773 | { | |
c25aa7cd PP |
1774 | GIT_ASSERT_ARG(index); |
1775 | GIT_ASSERT_ARG(path); | |
9d81509a | 1776 | return index_find(out, index, path, path_len, stage); |
f45ec1a0 ET |
1777 | } |
1778 | ||
11d9f6b3 | 1779 | int git_index_find(size_t *at_pos, git_index *index, const char *path) |
c4034e63 | 1780 | { |
11d9f6b3 | 1781 | size_t pos; |
f45ec1a0 | 1782 | |
c25aa7cd PP |
1783 | GIT_ASSERT_ARG(index); |
1784 | GIT_ASSERT_ARG(path); | |
f45ec1a0 | 1785 | |
52bb0476 RB |
1786 | if (git_vector_bsearch2( |
1787 | &pos, &index->entries, index->entries_search_path, path) < 0) { | |
ac3d33df | 1788 | git_error_set(GIT_ERROR_INDEX, "index does not contain %s", path); |
11d9f6b3 | 1789 | return GIT_ENOTFOUND; |
6fee906c | 1790 | } |
f45ec1a0 ET |
1791 | |
1792 | /* Since our binary search only looked at path, we may be in the | |
16248ee2 RB |
1793 | * middle of a list of stages. |
1794 | */ | |
52bb0476 RB |
1795 | for (; pos > 0; --pos) { |
1796 | const git_index_entry *prev = git_vector_get(&index->entries, pos - 1); | |
f45ec1a0 ET |
1797 | |
1798 | if (index->entries_cmp_path(prev->path, path) != 0) | |
1799 | break; | |
f45ec1a0 ET |
1800 | } |
1801 | ||
11d9f6b3 PK |
1802 | if (at_pos) |
1803 | *at_pos = pos; | |
1804 | ||
1805 | return 0; | |
68535125 VM |
1806 | } |
1807 | ||
f45ec1a0 ET |
1808 | int git_index_conflict_add(git_index *index, |
1809 | const git_index_entry *ancestor_entry, | |
1810 | const git_index_entry *our_entry, | |
1811 | const git_index_entry *their_entry) | |
1812 | { | |
1813 | git_index_entry *entries[3] = { 0 }; | |
a8122b5d | 1814 | unsigned short i; |
f45ec1a0 ET |
1815 | int ret = 0; |
1816 | ||
c25aa7cd | 1817 | GIT_ASSERT_ARG(index); |
f45ec1a0 | 1818 | |
5f32c506 ET |
1819 | if ((ancestor_entry && |
1820 | (ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0) || | |
1821 | (our_entry && | |
1822 | (ret = index_entry_dup(&entries[1], index, our_entry)) < 0) || | |
1823 | (their_entry && | |
1824 | (ret = index_entry_dup(&entries[2], index, their_entry)) < 0)) | |
3dbee456 | 1825 | goto on_error; |
f45ec1a0 | 1826 | |
d67f270e ET |
1827 | /* Validate entries */ |
1828 | for (i = 0; i < 3; i++) { | |
1829 | if (entries[i] && !valid_filemode(entries[i]->mode)) { | |
ac3d33df | 1830 | git_error_set(GIT_ERROR_INDEX, "invalid filemode for stage %d entry", |
16604d74 | 1831 | i + 1); |
6c7cee42 RD |
1832 | ret = -1; |
1833 | goto on_error; | |
d67f270e ET |
1834 | } |
1835 | } | |
1836 | ||
ecd60a56 ET |
1837 | /* Remove existing index entries for each path */ |
1838 | for (i = 0; i < 3; i++) { | |
1839 | if (entries[i] == NULL) | |
1840 | continue; | |
1841 | ||
1842 | if ((ret = git_index_remove(index, entries[i]->path, 0)) != 0) { | |
1843 | if (ret != GIT_ENOTFOUND) | |
1844 | goto on_error; | |
1845 | ||
ac3d33df | 1846 | git_error_clear(); |
ecd60a56 ET |
1847 | ret = 0; |
1848 | } | |
1849 | } | |
1850 | ||
1851 | /* Add the conflict entries */ | |
f45ec1a0 ET |
1852 | for (i = 0; i < 3; i++) { |
1853 | if (entries[i] == NULL) | |
1854 | continue; | |
1855 | ||
1856 | /* Make sure stage is correct */ | |
ac3d33df | 1857 | GIT_INDEX_ENTRY_STAGE_SET(entries[i], i + 1); |
f45ec1a0 | 1858 | |
6ddf533a | 1859 | if ((ret = index_insert(index, &entries[i], 1, true, true, false)) < 0) |
f45ec1a0 | 1860 | goto on_error; |
3dbee456 RB |
1861 | |
1862 | entries[i] = NULL; /* don't free if later entry fails */ | |
f45ec1a0 ET |
1863 | } |
1864 | ||
3cf58e66 | 1865 | return 0; |
f45ec1a0 ET |
1866 | |
1867 | on_error: | |
1868 | for (i = 0; i < 3; i++) { | |
1869 | if (entries[i] != NULL) | |
1870 | index_entry_free(entries[i]); | |
1871 | } | |
1872 | ||
1873 | return ret; | |
1874 | } | |
1875 | ||
0e0108f7 ET |
1876 | static int index_conflict__get_byindex( |
1877 | const git_index_entry **ancestor_out, | |
1878 | const git_index_entry **our_out, | |
1879 | const git_index_entry **their_out, | |
1880 | git_index *index, | |
1881 | size_t n) | |
f45ec1a0 | 1882 | { |
0e0108f7 ET |
1883 | const git_index_entry *conflict_entry; |
1884 | const char *path = NULL; | |
1885 | size_t count; | |
1886 | int stage, len = 0; | |
f45ec1a0 | 1887 | |
c25aa7cd PP |
1888 | GIT_ASSERT_ARG(ancestor_out); |
1889 | GIT_ASSERT_ARG(our_out); | |
1890 | GIT_ASSERT_ARG(their_out); | |
1891 | GIT_ASSERT_ARG(index); | |
f30fff45 | 1892 | |
f45ec1a0 ET |
1893 | *ancestor_out = NULL; |
1894 | *our_out = NULL; | |
1895 | *their_out = NULL; | |
1896 | ||
0e0108f7 ET |
1897 | for (count = git_index_entrycount(index); n < count; ++n) { |
1898 | conflict_entry = git_vector_get(&index->entries, n); | |
f45ec1a0 | 1899 | |
0e0108f7 | 1900 | if (path && index->entries_cmp_path(conflict_entry->path, path) != 0) |
f45ec1a0 ET |
1901 | break; |
1902 | ||
ac3d33df | 1903 | stage = GIT_INDEX_ENTRY_STAGE(conflict_entry); |
0e0108f7 | 1904 | path = conflict_entry->path; |
f30fff45 | 1905 | |
f45ec1a0 ET |
1906 | switch (stage) { |
1907 | case 3: | |
1908 | *their_out = conflict_entry; | |
0e0108f7 | 1909 | len++; |
f45ec1a0 ET |
1910 | break; |
1911 | case 2: | |
1912 | *our_out = conflict_entry; | |
0e0108f7 | 1913 | len++; |
f45ec1a0 ET |
1914 | break; |
1915 | case 1: | |
1916 | *ancestor_out = conflict_entry; | |
0e0108f7 | 1917 | len++; |
f45ec1a0 ET |
1918 | break; |
1919 | default: | |
1920 | break; | |
1921 | }; | |
f45ec1a0 ET |
1922 | } |
1923 | ||
0e0108f7 ET |
1924 | return len; |
1925 | } | |
1926 | ||
1927 | int git_index_conflict_get( | |
1928 | const git_index_entry **ancestor_out, | |
1929 | const git_index_entry **our_out, | |
1930 | const git_index_entry **their_out, | |
1931 | git_index *index, | |
1932 | const char *path) | |
1933 | { | |
1934 | size_t pos; | |
1935 | int len = 0; | |
1936 | ||
c25aa7cd PP |
1937 | GIT_ASSERT_ARG(ancestor_out); |
1938 | GIT_ASSERT_ARG(our_out); | |
1939 | GIT_ASSERT_ARG(their_out); | |
1940 | GIT_ASSERT_ARG(index); | |
1941 | GIT_ASSERT_ARG(path); | |
0e0108f7 ET |
1942 | |
1943 | *ancestor_out = NULL; | |
1944 | *our_out = NULL; | |
1945 | *their_out = NULL; | |
1946 | ||
1947 | if (git_index_find(&pos, index, path) < 0) | |
1948 | return GIT_ENOTFOUND; | |
1949 | ||
1950 | if ((len = index_conflict__get_byindex( | |
1951 | ancestor_out, our_out, their_out, index, pos)) < 0) | |
1952 | return len; | |
1953 | else if (len == 0) | |
1954 | return GIT_ENOTFOUND; | |
1955 | ||
1956 | return 0; | |
f45ec1a0 ET |
1957 | } |
1958 | ||
aba6b5ed | 1959 | static int index_conflict_remove(git_index *index, const char *path) |
f45ec1a0 | 1960 | { |
aba6b5ed | 1961 | size_t pos = 0; |
f45ec1a0 | 1962 | git_index_entry *conflict_entry; |
050cf8b8 | 1963 | int error = 0; |
f45ec1a0 | 1964 | |
aba6b5ed | 1965 | if (path != NULL && git_index_find(&pos, index, path) < 0) |
11d9f6b3 | 1966 | return GIT_ENOTFOUND; |
f45ec1a0 | 1967 | |
3dbee456 | 1968 | while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) { |
f45ec1a0 | 1969 | |
aba6b5ed RB |
1970 | if (path != NULL && |
1971 | index->entries_cmp_path(conflict_entry->path, path) != 0) | |
f45ec1a0 ET |
1972 | break; |
1973 | ||
ac3d33df | 1974 | if (GIT_INDEX_ENTRY_STAGE(conflict_entry) == 0) { |
f45ec1a0 ET |
1975 | pos++; |
1976 | continue; | |
1977 | } | |
1978 | ||
8a2834d3 | 1979 | if ((error = index_remove_entry(index, pos)) < 0) |
3dbee456 | 1980 | break; |
f45ec1a0 ET |
1981 | } |
1982 | ||
3dbee456 | 1983 | return error; |
f45ec1a0 ET |
1984 | } |
1985 | ||
aba6b5ed | 1986 | int git_index_conflict_remove(git_index *index, const char *path) |
f45ec1a0 | 1987 | { |
c25aa7cd PP |
1988 | GIT_ASSERT_ARG(index); |
1989 | GIT_ASSERT_ARG(path); | |
aba6b5ed | 1990 | return index_conflict_remove(index, path); |
f45ec1a0 ET |
1991 | } |
1992 | ||
aba6b5ed | 1993 | int git_index_conflict_cleanup(git_index *index) |
f45ec1a0 | 1994 | { |
c25aa7cd | 1995 | GIT_ASSERT_ARG(index); |
aba6b5ed | 1996 | return index_conflict_remove(index, NULL); |
f45ec1a0 ET |
1997 | } |
1998 | ||
16248ee2 | 1999 | int git_index_has_conflicts(const git_index *index) |
7cc1bf0f | 2000 | { |
16248ee2 | 2001 | size_t i; |
7cc1bf0f | 2002 | git_index_entry *entry; |
2003 | ||
c25aa7cd | 2004 | GIT_ASSERT_ARG(index); |
7cc1bf0f | 2005 | |
2006 | git_vector_foreach(&index->entries, i, entry) { | |
ac3d33df | 2007 | if (GIT_INDEX_ENTRY_STAGE(entry) > 0) |
7cc1bf0f | 2008 | return 1; |
2009 | } | |
2010 | ||
2011 | return 0; | |
2012 | } | |
2013 | ||
ac3d33df JK |
2014 | int git_index_iterator_new( |
2015 | git_index_iterator **iterator_out, | |
2016 | git_index *index) | |
2017 | { | |
2018 | git_index_iterator *it; | |
2019 | int error; | |
2020 | ||
c25aa7cd PP |
2021 | GIT_ASSERT_ARG(iterator_out); |
2022 | GIT_ASSERT_ARG(index); | |
ac3d33df JK |
2023 | |
2024 | it = git__calloc(1, sizeof(git_index_iterator)); | |
2025 | GIT_ERROR_CHECK_ALLOC(it); | |
2026 | ||
2027 | if ((error = git_index_snapshot_new(&it->snap, index)) < 0) { | |
2028 | git__free(it); | |
2029 | return error; | |
2030 | } | |
2031 | ||
2032 | it->index = index; | |
2033 | ||
2034 | *iterator_out = it; | |
2035 | return 0; | |
2036 | } | |
2037 | ||
2038 | int git_index_iterator_next( | |
2039 | const git_index_entry **out, | |
2040 | git_index_iterator *it) | |
2041 | { | |
c25aa7cd PP |
2042 | GIT_ASSERT_ARG(out); |
2043 | GIT_ASSERT_ARG(it); | |
ac3d33df JK |
2044 | |
2045 | if (it->cur >= git_vector_length(&it->snap)) | |
2046 | return GIT_ITEROVER; | |
2047 | ||
2048 | *out = (git_index_entry *)git_vector_get(&it->snap, it->cur++); | |
2049 | return 0; | |
2050 | } | |
2051 | ||
2052 | void git_index_iterator_free(git_index_iterator *it) | |
2053 | { | |
2054 | if (it == NULL) | |
2055 | return; | |
2056 | ||
2057 | git_index_snapshot_release(&it->snap, it->index); | |
2058 | git__free(it); | |
2059 | } | |
2060 | ||
0e0108f7 ET |
2061 | int git_index_conflict_iterator_new( |
2062 | git_index_conflict_iterator **iterator_out, | |
2063 | git_index *index) | |
2064 | { | |
2065 | git_index_conflict_iterator *it = NULL; | |
2066 | ||
c25aa7cd PP |
2067 | GIT_ASSERT_ARG(iterator_out); |
2068 | GIT_ASSERT_ARG(index); | |
0e0108f7 ET |
2069 | |
2070 | it = git__calloc(1, sizeof(git_index_conflict_iterator)); | |
ac3d33df | 2071 | GIT_ERROR_CHECK_ALLOC(it); |
0e0108f7 ET |
2072 | |
2073 | it->index = index; | |
2074 | ||
2075 | *iterator_out = it; | |
2076 | return 0; | |
2077 | } | |
2078 | ||
2079 | int git_index_conflict_next( | |
2080 | const git_index_entry **ancestor_out, | |
2081 | const git_index_entry **our_out, | |
2082 | const git_index_entry **their_out, | |
2083 | git_index_conflict_iterator *iterator) | |
2084 | { | |
2085 | const git_index_entry *entry; | |
2086 | int len; | |
2087 | ||
c25aa7cd PP |
2088 | GIT_ASSERT_ARG(ancestor_out); |
2089 | GIT_ASSERT_ARG(our_out); | |
2090 | GIT_ASSERT_ARG(their_out); | |
2091 | GIT_ASSERT_ARG(iterator); | |
0e0108f7 ET |
2092 | |
2093 | *ancestor_out = NULL; | |
2094 | *our_out = NULL; | |
2095 | *their_out = NULL; | |
2096 | ||
2097 | while (iterator->cur < iterator->index->entries.length) { | |
2098 | entry = git_index_get_byindex(iterator->index, iterator->cur); | |
2099 | ||
9f545b9d | 2100 | if (git_index_entry_is_conflict(entry)) { |
0e0108f7 ET |
2101 | if ((len = index_conflict__get_byindex( |
2102 | ancestor_out, | |
2103 | our_out, | |
2104 | their_out, | |
2105 | iterator->index, | |
2106 | iterator->cur)) < 0) | |
2107 | return len; | |
2108 | ||
2109 | iterator->cur += len; | |
2110 | return 0; | |
2111 | } | |
2112 | ||
2113 | iterator->cur++; | |
2114 | } | |
2115 | ||
2116 | return GIT_ITEROVER; | |
2117 | } | |
2118 | ||
2119 | void git_index_conflict_iterator_free(git_index_conflict_iterator *iterator) | |
2120 | { | |
2121 | if (iterator == NULL) | |
2122 | return; | |
2123 | ||
2124 | git__free(iterator); | |
2125 | } | |
2126 | ||
2fe8157e | 2127 | size_t git_index_name_entrycount(git_index *index) |
0462fba5 | 2128 | { |
c25aa7cd | 2129 | GIT_ASSERT_ARG(index); |
2fe8157e | 2130 | return index->names.length; |
0462fba5 ET |
2131 | } |
2132 | ||
2133 | const git_index_name_entry *git_index_name_get_byindex( | |
2134 | git_index *index, size_t n) | |
2135 | { | |
c25aa7cd | 2136 | GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); |
1fed6b07 | 2137 | |
0462fba5 ET |
2138 | git_vector_sort(&index->names); |
2139 | return git_vector_get(&index->names, n); | |
2140 | } | |
2141 | ||
3dbee456 RB |
2142 | static void index_name_entry_free(git_index_name_entry *ne) |
2143 | { | |
2144 | if (!ne) | |
2145 | return; | |
2146 | git__free(ne->ancestor); | |
2147 | git__free(ne->ours); | |
2148 | git__free(ne->theirs); | |
2149 | git__free(ne); | |
2150 | } | |
2151 | ||
0462fba5 ET |
2152 | int git_index_name_add(git_index *index, |
2153 | const char *ancestor, const char *ours, const char *theirs) | |
2154 | { | |
2155 | git_index_name_entry *conflict_name; | |
2156 | ||
c25aa7cd | 2157 | GIT_ASSERT_ARG((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); |
0462fba5 ET |
2158 | |
2159 | conflict_name = git__calloc(1, sizeof(git_index_name_entry)); | |
ac3d33df | 2160 | GIT_ERROR_CHECK_ALLOC(conflict_name); |
1fed6b07 | 2161 | |
3dbee456 RB |
2162 | if ((ancestor && !(conflict_name->ancestor = git__strdup(ancestor))) || |
2163 | (ours && !(conflict_name->ours = git__strdup(ours))) || | |
2164 | (theirs && !(conflict_name->theirs = git__strdup(theirs))) || | |
2165 | git_vector_insert(&index->names, conflict_name) < 0) | |
2166 | { | |
2167 | index_name_entry_free(conflict_name); | |
2168 | return -1; | |
0462fba5 | 2169 | } |
1fed6b07 | 2170 | |
ac3d33df | 2171 | index->dirty = 1; |
3dbee456 | 2172 | return 0; |
0462fba5 ET |
2173 | } |
2174 | ||
22a2d3d5 | 2175 | int git_index_name_clear(git_index *index) |
0462fba5 ET |
2176 | { |
2177 | size_t i; | |
2178 | git_index_name_entry *conflict_name; | |
2179 | ||
c25aa7cd | 2180 | GIT_ASSERT_ARG(index); |
1fed6b07 | 2181 | |
3dbee456 RB |
2182 | git_vector_foreach(&index->names, i, conflict_name) |
2183 | index_name_entry_free(conflict_name); | |
1fed6b07 | 2184 | |
0462fba5 | 2185 | git_vector_clear(&index->names); |
ac3d33df JK |
2186 | |
2187 | index->dirty = 1; | |
22a2d3d5 UG |
2188 | |
2189 | return 0; | |
0462fba5 ET |
2190 | } |
2191 | ||
2fe8157e | 2192 | size_t git_index_reuc_entrycount(git_index *index) |
f45ec1a0 | 2193 | { |
c25aa7cd | 2194 | GIT_ASSERT_ARG(index); |
2fe8157e | 2195 | return index->reuc.length; |
f45ec1a0 ET |
2196 | } |
2197 | ||
d307a013 VM |
2198 | static int index_reuc_on_dup(void **old, void *new) |
2199 | { | |
2200 | index_entry_reuc_free(*old); | |
2201 | *old = new; | |
2202 | return GIT_EEXISTS; | |
2203 | } | |
2204 | ||
bec65a5e ET |
2205 | static int index_reuc_insert( |
2206 | git_index *index, | |
d307a013 | 2207 | git_index_reuc_entry *reuc) |
f45ec1a0 | 2208 | { |
d307a013 | 2209 | int res; |
f45ec1a0 | 2210 | |
c25aa7cd PP |
2211 | GIT_ASSERT_ARG(index); |
2212 | GIT_ASSERT_ARG(reuc && reuc->path != NULL); | |
2213 | GIT_ASSERT(git_vector_is_sorted(&index->reuc)); | |
f45ec1a0 | 2214 | |
d307a013 | 2215 | res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup); |
ac3d33df JK |
2216 | index->dirty = 1; |
2217 | ||
d307a013 | 2218 | return res == GIT_EEXISTS ? 0 : res; |
f45ec1a0 ET |
2219 | } |
2220 | ||
2221 | int git_index_reuc_add(git_index *index, const char *path, | |
bec65a5e ET |
2222 | int ancestor_mode, const git_oid *ancestor_oid, |
2223 | int our_mode, const git_oid *our_oid, | |
2224 | int their_mode, const git_oid *their_oid) | |
f45ec1a0 ET |
2225 | { |
2226 | git_index_reuc_entry *reuc = NULL; | |
2227 | int error = 0; | |
2228 | ||
c25aa7cd PP |
2229 | GIT_ASSERT_ARG(index); |
2230 | GIT_ASSERT_ARG(path); | |
f45ec1a0 | 2231 | |
3dbee456 RB |
2232 | if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, |
2233 | ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || | |
d307a013 | 2234 | (error = index_reuc_insert(index, reuc)) < 0) |
f45ec1a0 | 2235 | index_entry_reuc_free(reuc); |
f45ec1a0 ET |
2236 | |
2237 | return error; | |
3dbee456 | 2238 | } |
f45ec1a0 | 2239 | |
11d9f6b3 | 2240 | int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path) |
245adf4f | 2241 | { |
11d9f6b3 | 2242 | return git_vector_bsearch2(at_pos, &index->reuc, index->reuc_search, path); |
245adf4f KS |
2243 | } |
2244 | ||
f45ec1a0 | 2245 | const git_index_reuc_entry *git_index_reuc_get_bypath( |
7c7ff7d1 | 2246 | git_index *index, const char *path) |
4c0b6a6d | 2247 | { |
11d9f6b3 | 2248 | size_t pos; |
c25aa7cd PP |
2249 | |
2250 | GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); | |
2251 | GIT_ASSERT_ARG_WITH_RETVAL(path, NULL); | |
4c0b6a6d | 2252 | |
f45ec1a0 | 2253 | if (!index->reuc.length) |
f4e2aca2 | 2254 | return NULL; |
4c0b6a6d | 2255 | |
c25aa7cd | 2256 | GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL); |
f45ec1a0 | 2257 | |
11d9f6b3 | 2258 | if (git_index_reuc_find(&pos, index, path) < 0) |
f4e2aca2 | 2259 | return NULL; |
4c0b6a6d | 2260 | |
f45ec1a0 | 2261 | return git_vector_get(&index->reuc, pos); |
4c0b6a6d JP |
2262 | } |
2263 | ||
f45ec1a0 | 2264 | const git_index_reuc_entry *git_index_reuc_get_byindex( |
b8457baa | 2265 | git_index *index, size_t n) |
f11e0797 | 2266 | { |
c25aa7cd PP |
2267 | GIT_ASSERT_ARG_WITH_RETVAL(index, NULL); |
2268 | GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL); | |
f45ec1a0 | 2269 | |
f45ec1a0 ET |
2270 | return git_vector_get(&index->reuc, n); |
2271 | } | |
2272 | ||
f45d51ff | 2273 | int git_index_reuc_remove(git_index *index, size_t position) |
f45ec1a0 ET |
2274 | { |
2275 | int error; | |
2276 | git_index_reuc_entry *reuc; | |
2277 | ||
c25aa7cd PP |
2278 | GIT_ASSERT_ARG(index); |
2279 | GIT_ASSERT(git_vector_is_sorted(&index->reuc)); | |
f45ec1a0 ET |
2280 | |
2281 | reuc = git_vector_get(&index->reuc, position); | |
10c06114 | 2282 | error = git_vector_remove(&index->reuc, position); |
f45ec1a0 ET |
2283 | |
2284 | if (!error) | |
2285 | index_entry_reuc_free(reuc); | |
2286 | ||
ac3d33df | 2287 | index->dirty = 1; |
f45ec1a0 | 2288 | return error; |
f11e0797 RG |
2289 | } |
2290 | ||
22a2d3d5 | 2291 | int git_index_reuc_clear(git_index *index) |
5bddabcc ET |
2292 | { |
2293 | size_t i; | |
5bddabcc | 2294 | |
c25aa7cd | 2295 | GIT_ASSERT_ARG(index); |
5bddabcc | 2296 | |
178aa39c | 2297 | for (i = 0; i < index->reuc.length; ++i) |
c25aa7cd | 2298 | index_entry_reuc_free(git_atomic_swap(index->reuc.contents[i], NULL)); |
5bddabcc ET |
2299 | |
2300 | git_vector_clear(&index->reuc); | |
ac3d33df JK |
2301 | |
2302 | index->dirty = 1; | |
22a2d3d5 UG |
2303 | |
2304 | return 0; | |
5bddabcc ET |
2305 | } |
2306 | ||
7c7ff7d1 RB |
2307 | static int index_error_invalid(const char *message) |
2308 | { | |
ac3d33df | 2309 | git_error_set(GIT_ERROR_INDEX, "invalid data in index - %s", message); |
7c7ff7d1 RB |
2310 | return -1; |
2311 | } | |
2312 | ||
f45ec1a0 | 2313 | static int read_reuc(git_index *index, const char *buffer, size_t size) |
4c0b6a6d | 2314 | { |
f4e2aca2 VM |
2315 | const char *endptr; |
2316 | size_t len; | |
4c0b6a6d JP |
2317 | int i; |
2318 | ||
43efc449 RB |
2319 | /* If called multiple times, the vector might already be initialized */ |
2320 | if (index->reuc._alloc_size == 0 && | |
2321 | git_vector_init(&index->reuc, 16, reuc_cmp) < 0) | |
7c7ff7d1 | 2322 | return -1; |
4c0b6a6d JP |
2323 | |
2324 | while (size) { | |
f45ec1a0 | 2325 | git_index_reuc_entry *lost; |
4c0b6a6d | 2326 | |
57f31f05 | 2327 | len = p_strnlen(buffer, size) + 1; |
4c0b6a6d | 2328 | if (size <= len) |
f45ec1a0 | 2329 | return index_error_invalid("reading reuc entries"); |
4c0b6a6d | 2330 | |
8a2834d3 | 2331 | lost = reuc_entry_alloc(buffer); |
ac3d33df | 2332 | GIT_ERROR_CHECK_ALLOC(lost); |
4c0b6a6d | 2333 | |
4c0b6a6d JP |
2334 | size -= len; |
2335 | buffer += len; | |
2336 | ||
7c7ff7d1 | 2337 | /* read 3 ASCII octal numbers for stage entries */ |
4c0b6a6d | 2338 | for (i = 0; i < 3; i++) { |
0f1e2d20 | 2339 | int64_t tmp; |
b16692fa | 2340 | |
6c7cee42 | 2341 | if (git__strntol64(&tmp, buffer, size, &endptr, 8) < 0 || |
7c7ff7d1 | 2342 | !endptr || endptr == buffer || *endptr || |
4aaae935 | 2343 | tmp < 0 || tmp > UINT32_MAX) { |
8d6ef4bf | 2344 | index_entry_reuc_free(lost); |
f45ec1a0 | 2345 | return index_error_invalid("reading reuc entry stage"); |
8d6ef4bf | 2346 | } |
f4e2aca2 | 2347 | |
4aaae935 | 2348 | lost->mode[i] = (uint32_t)tmp; |
b16692fa | 2349 | |
ae9f771c | 2350 | len = (endptr + 1) - buffer; |
8d6ef4bf RD |
2351 | if (size <= len) { |
2352 | index_entry_reuc_free(lost); | |
f45ec1a0 | 2353 | return index_error_invalid("reading reuc entry stage"); |
8d6ef4bf | 2354 | } |
f4e2aca2 | 2355 | |
4c0b6a6d JP |
2356 | size -= len; |
2357 | buffer += len; | |
2358 | } | |
2359 | ||
7c7ff7d1 | 2360 | /* read up to 3 OIDs for stage entries */ |
4c0b6a6d JP |
2361 | for (i = 0; i < 3; i++) { |
2362 | if (!lost->mode[i]) | |
2363 | continue; | |
8d6ef4bf RD |
2364 | if (size < 20) { |
2365 | index_entry_reuc_free(lost); | |
f45ec1a0 | 2366 | return index_error_invalid("reading reuc entry oid"); |
8d6ef4bf | 2367 | } |
7c7ff7d1 | 2368 | |
7d9cc9f8 | 2369 | git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer); |
4c0b6a6d JP |
2370 | size -= 20; |
2371 | buffer += 20; | |
2372 | } | |
43efc449 RB |
2373 | |
2374 | /* entry was read successfully - insert into reuc vector */ | |
2375 | if (git_vector_insert(&index->reuc, lost) < 0) | |
2376 | return -1; | |
4c0b6a6d JP |
2377 | } |
2378 | ||
f45ec1a0 | 2379 | /* entries are guaranteed to be sorted on-disk */ |
882c7742 | 2380 | git_vector_set_sorted(&index->reuc, true); |
f45ec1a0 | 2381 | |
7c7ff7d1 | 2382 | return 0; |
4c0b6a6d JP |
2383 | } |
2384 | ||
0462fba5 ET |
2385 | |
2386 | static int read_conflict_names(git_index *index, const char *buffer, size_t size) | |
2387 | { | |
2388 | size_t len; | |
1fed6b07 | 2389 | |
0462fba5 ET |
2390 | /* This gets called multiple times, the vector might already be initialized */ |
2391 | if (index->names._alloc_size == 0 && | |
2392 | git_vector_init(&index->names, 16, conflict_name_cmp) < 0) | |
2393 | return -1; | |
2394 | ||
2395 | #define read_conflict_name(ptr) \ | |
57f31f05 | 2396 | len = p_strnlen(buffer, size) + 1; \ |
7808c937 PS |
2397 | if (size < len) { \ |
2398 | index_error_invalid("reading conflict name entries"); \ | |
2399 | goto out_err; \ | |
2400 | } \ | |
0462fba5 ET |
2401 | if (len == 1) \ |
2402 | ptr = NULL; \ | |
2403 | else { \ | |
2404 | ptr = git__malloc(len); \ | |
ac3d33df | 2405 | GIT_ERROR_CHECK_ALLOC(ptr); \ |
0462fba5 ET |
2406 | memcpy(ptr, buffer, len); \ |
2407 | } \ | |
2408 | \ | |
2409 | buffer += len; \ | |
2410 | size -= len; | |
1fed6b07 | 2411 | |
0462fba5 ET |
2412 | while (size) { |
2413 | git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry)); | |
ac3d33df | 2414 | GIT_ERROR_CHECK_ALLOC(conflict_name); |
0462fba5 ET |
2415 | |
2416 | read_conflict_name(conflict_name->ancestor); | |
2417 | read_conflict_name(conflict_name->ours); | |
2418 | read_conflict_name(conflict_name->theirs); | |
1fed6b07 | 2419 | |
0462fba5 | 2420 | if (git_vector_insert(&index->names, conflict_name) < 0) |
7808c937 PS |
2421 | goto out_err; |
2422 | ||
2423 | continue; | |
2424 | ||
2425 | out_err: | |
2426 | git__free(conflict_name->ancestor); | |
2427 | git__free(conflict_name->ours); | |
2428 | git__free(conflict_name->theirs); | |
2429 | git__free(conflict_name); | |
2430 | return -1; | |
0462fba5 ET |
2431 | } |
2432 | ||
2433 | #undef read_conflict_name | |
1fed6b07 | 2434 | |
0462fba5 | 2435 | /* entries are guaranteed to be sorted on-disk */ |
882c7742 | 2436 | git_vector_set_sorted(&index->names, true); |
1fed6b07 | 2437 | |
2438 | return 0; | |
0462fba5 ET |
2439 | } |
2440 | ||
29f498e0 PS |
2441 | static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flags) |
2442 | { | |
2443 | if (varint_len) { | |
ac3d33df | 2444 | if (flags & GIT_INDEX_ENTRY_EXTENDED) |
29f498e0 PS |
2445 | return offsetof(struct entry_long, path) + path_len + 1 + varint_len; |
2446 | else | |
2447 | return offsetof(struct entry_short, path) + path_len + 1 + varint_len; | |
2448 | } else { | |
350d2c47 | 2449 | #define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) |
ac3d33df | 2450 | if (flags & GIT_INDEX_ENTRY_EXTENDED) |
350d2c47 | 2451 | return entry_size(struct entry_long, path_len); |
29f498e0 | 2452 | else |
350d2c47 PS |
2453 | return entry_size(struct entry_short, path_len); |
2454 | #undef entry_size | |
29f498e0 PS |
2455 | } |
2456 | } | |
2457 | ||
eae0bfdc | 2458 | static int read_entry( |
a64119e3 | 2459 | git_index_entry **out, |
eae0bfdc | 2460 | size_t *out_size, |
a64119e3 ET |
2461 | git_index *index, |
2462 | const void *buffer, | |
5625d86b | 2463 | size_t buffer_size, |
11d0be23 | 2464 | const char *last) |
68535125 VM |
2465 | { |
2466 | size_t path_length, entry_size; | |
68535125 | 2467 | const char *path_ptr; |
ff97778a | 2468 | struct entry_short source; |
8a2834d3 | 2469 | git_index_entry entry = {{0}}; |
5625d86b DT |
2470 | bool compressed = index->version >= INDEX_VERSION_NUMBER_COMP; |
2471 | char *tmp_path = NULL; | |
68535125 VM |
2472 | |
2473 | if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size) | |
eae0bfdc | 2474 | return -1; |
68535125 | 2475 | |
ff97778a JG |
2476 | /* buffer is not guaranteed to be aligned */ |
2477 | memcpy(&source, buffer, sizeof(struct entry_short)); | |
2478 | ||
2479 | entry.ctime.seconds = (git_time_t)ntohl(source.ctime.seconds); | |
2480 | entry.ctime.nanoseconds = ntohl(source.ctime.nanoseconds); | |
2481 | entry.mtime.seconds = (git_time_t)ntohl(source.mtime.seconds); | |
2482 | entry.mtime.nanoseconds = ntohl(source.mtime.nanoseconds); | |
2483 | entry.dev = ntohl(source.dev); | |
2484 | entry.ino = ntohl(source.ino); | |
2485 | entry.mode = ntohl(source.mode); | |
2486 | entry.uid = ntohl(source.uid); | |
2487 | entry.gid = ntohl(source.gid); | |
2488 | entry.file_size = ntohl(source.file_size); | |
2489 | git_oid_cpy(&entry.id, &source.oid); | |
2490 | entry.flags = ntohs(source.flags); | |
8a2834d3 | 2491 | |
ac3d33df | 2492 | if (entry.flags & GIT_INDEX_ENTRY_EXTENDED) { |
ff97778a JG |
2493 | uint16_t flags_raw; |
2494 | size_t flags_offset; | |
68535125 | 2495 | |
ff97778a JG |
2496 | flags_offset = offsetof(struct entry_long, flags_extended); |
2497 | memcpy(&flags_raw, (const char *) buffer + flags_offset, | |
2498 | sizeof(flags_raw)); | |
2499 | flags_raw = ntohs(flags_raw); | |
2500 | ||
2501 | memcpy(&entry.flags_extended, &flags_raw, sizeof(flags_raw)); | |
2502 | path_ptr = (const char *) buffer + offsetof(struct entry_long, path); | |
68535125 | 2503 | } else |
ff97778a | 2504 | path_ptr = (const char *) buffer + offsetof(struct entry_short, path); |
68535125 | 2505 | |
5625d86b | 2506 | if (!compressed) { |
ac3d33df | 2507 | path_length = entry.flags & GIT_INDEX_ENTRY_NAMEMASK; |
68535125 | 2508 | |
5625d86b DT |
2509 | /* if this is a very long string, we must find its |
2510 | * real length without overflowing */ | |
2511 | if (path_length == 0xFFF) { | |
2512 | const char *path_end; | |
68535125 | 2513 | |
5625d86b DT |
2514 | path_end = memchr(path_ptr, '\0', buffer_size); |
2515 | if (path_end == NULL) | |
eae0bfdc | 2516 | return -1; |
68535125 | 2517 | |
5625d86b DT |
2518 | path_length = path_end - path_ptr; |
2519 | } | |
68535125 | 2520 | |
29f498e0 | 2521 | entry_size = index_entry_size(path_length, 0, entry.flags); |
5625d86b DT |
2522 | entry.path = (char *)path_ptr; |
2523 | } else { | |
eae0bfdc PP |
2524 | size_t varint_len, last_len, prefix_len, suffix_len, path_len; |
2525 | uintmax_t strip_len; | |
2526 | ||
2527 | strip_len = git_decode_varint((const unsigned char *)path_ptr, &varint_len); | |
2528 | last_len = strlen(last); | |
2529 | ||
2530 | if (varint_len == 0 || last_len < strip_len) | |
5625d86b DT |
2531 | return index_error_invalid("incorrect prefix length"); |
2532 | ||
ac3d33df | 2533 | prefix_len = last_len - (size_t)strip_len; |
eae0bfdc PP |
2534 | suffix_len = strlen(path_ptr + varint_len); |
2535 | ||
ac3d33df JK |
2536 | GIT_ERROR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len); |
2537 | GIT_ERROR_CHECK_ALLOC_ADD(&path_len, path_len, 1); | |
eae0bfdc PP |
2538 | |
2539 | if (path_len > GIT_PATH_MAX) | |
2540 | return index_error_invalid("unreasonable path length"); | |
2541 | ||
febe8c14 | 2542 | tmp_path = git__malloc(path_len); |
ac3d33df | 2543 | GIT_ERROR_CHECK_ALLOC(tmp_path); |
febe8c14 PS |
2544 | |
2545 | memcpy(tmp_path, last, prefix_len); | |
2546 | memcpy(tmp_path + prefix_len, path_ptr + varint_len, suffix_len + 1); | |
29f498e0 | 2547 | entry_size = index_entry_size(suffix_len, varint_len, entry.flags); |
5625d86b DT |
2548 | entry.path = tmp_path; |
2549 | } | |
2550 | ||
eae0bfdc PP |
2551 | if (entry_size == 0) |
2552 | return -1; | |
2553 | ||
83e0392c | 2554 | if (INDEX_FOOTER_SIZE + entry_size > buffer_size) |
eae0bfdc | 2555 | return -1; |
83e0392c | 2556 | |
5625d86b DT |
2557 | if (index_entry_dup(out, index, &entry) < 0) { |
2558 | git__free(tmp_path); | |
eae0bfdc | 2559 | return -1; |
5625d86b | 2560 | } |
68535125 | 2561 | |
5625d86b | 2562 | git__free(tmp_path); |
eae0bfdc PP |
2563 | *out_size = entry_size; |
2564 | return 0; | |
68535125 VM |
2565 | } |
2566 | ||
2567 | static int read_header(struct index_header *dest, const void *buffer) | |
2568 | { | |
ae9f771c | 2569 | const struct index_header *source = buffer; |
68535125 | 2570 | |
348c7335 VM |
2571 | dest->signature = ntohl(source->signature); |
2572 | if (dest->signature != INDEX_HEADER_SIG) | |
7c7ff7d1 | 2573 | return index_error_invalid("incorrect header signature"); |
68535125 VM |
2574 | |
2575 | dest->version = ntohl(source->version); | |
5625d86b DT |
2576 | if (dest->version < INDEX_VERSION_NUMBER_LB || |
2577 | dest->version > INDEX_VERSION_NUMBER_UB) | |
7c7ff7d1 | 2578 | return index_error_invalid("incorrect header version"); |
68535125 VM |
2579 | |
2580 | dest->entry_count = ntohl(source->entry_count); | |
7c7ff7d1 | 2581 | return 0; |
68535125 VM |
2582 | } |
2583 | ||
ac3d33df | 2584 | static int read_extension(size_t *read_len, git_index *index, const char *buffer, size_t buffer_size) |
68535125 | 2585 | { |
68535125 VM |
2586 | struct index_extension dest; |
2587 | size_t total_size; | |
2588 | ||
ff97778a JG |
2589 | /* buffer is not guaranteed to be aligned */ |
2590 | memcpy(&dest, buffer, sizeof(struct index_extension)); | |
2591 | dest.extension_size = ntohl(dest.extension_size); | |
68535125 VM |
2592 | |
2593 | total_size = dest.extension_size + sizeof(struct index_extension); | |
2594 | ||
57f31f05 ET |
2595 | if (dest.extension_size > total_size || |
2596 | buffer_size < total_size || | |
ac3d33df JK |
2597 | buffer_size - total_size < INDEX_FOOTER_SIZE) { |
2598 | index_error_invalid("extension is truncated"); | |
2599 | return -1; | |
2600 | } | |
68535125 VM |
2601 | |
2602 | /* optional extension */ | |
2603 | if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') { | |
2604 | /* tree cache */ | |
2605 | if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) { | |
19c88310 | 2606 | if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size, &index->tree_pool) < 0) |
ac3d33df | 2607 | return -1; |
4c0b6a6d | 2608 | } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) { |
f45ec1a0 | 2609 | if (read_reuc(index, buffer + 8, dest.extension_size) < 0) |
ac3d33df | 2610 | return -1; |
0462fba5 ET |
2611 | } else if (memcmp(dest.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4) == 0) { |
2612 | if (read_conflict_names(index, buffer + 8, dest.extension_size) < 0) | |
ac3d33df | 2613 | return -1; |
68535125 | 2614 | } |
f4e2aca2 VM |
2615 | /* else, unsupported extension. We cannot parse this, but we can skip |
2616 | * it by returning `total_size */ | |
68535125 VM |
2617 | } else { |
2618 | /* we cannot handle non-ignorable extensions; | |
2619 | * in fact they aren't even defined in the standard */ | |
ac3d33df JK |
2620 | git_error_set(GIT_ERROR_INDEX, "unsupported mandatory extension: '%.4s'", dest.signature); |
2621 | return -1; | |
68535125 VM |
2622 | } |
2623 | ||
ac3d33df JK |
2624 | *read_len = total_size; |
2625 | ||
2626 | return 0; | |
68535125 VM |
2627 | } |
2628 | ||
348c7335 | 2629 | static int parse_index(git_index *index, const char *buffer, size_t buffer_size) |
68535125 | 2630 | { |
3dbee456 | 2631 | int error = 0; |
68535125 | 2632 | unsigned int i; |
2aee1aa4 | 2633 | struct index_header header = { 0 }; |
68535125 | 2634 | git_oid checksum_calculated, checksum_expected; |
11d0be23 | 2635 | const char *last = NULL; |
5625d86b | 2636 | const char *empty = ""; |
68535125 VM |
2637 | |
2638 | #define seek_forward(_increase) { \ | |
17ef678c RB |
2639 | if (_increase >= buffer_size) { \ |
2640 | error = index_error_invalid("ran out of data while parsing"); \ | |
2641 | goto done; } \ | |
68535125 VM |
2642 | buffer += _increase; \ |
2643 | buffer_size -= _increase;\ | |
2644 | } | |
2645 | ||
2646 | if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE) | |
7c7ff7d1 | 2647 | return index_error_invalid("insufficient buffer space"); |
68535125 VM |
2648 | |
2649 | /* Precalculate the SHA1 of the files's contents -- we'll match it to | |
2650 | * the provided SHA1 in the footer */ | |
ae9f771c | 2651 | git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE); |
68535125 VM |
2652 | |
2653 | /* Parse header */ | |
3dbee456 RB |
2654 | if ((error = read_header(&header, buffer)) < 0) |
2655 | return error; | |
68535125 | 2656 | |
5625d86b DT |
2657 | index->version = header.version; |
2658 | if (index->version >= INDEX_VERSION_NUMBER_COMP) | |
11d0be23 | 2659 | last = empty; |
5625d86b | 2660 | |
68535125 VM |
2661 | seek_forward(INDEX_HEADER_SIZE); |
2662 | ||
c25aa7cd | 2663 | GIT_ASSERT(!index->entries.length); |
68535125 | 2664 | |
22a2d3d5 UG |
2665 | if ((error = index_map_resize(index->entries_map, header.entry_count, index->ignore_case)) < 0) |
2666 | return error; | |
af1d5239 | 2667 | |
68535125 | 2668 | /* Parse all the entries */ |
c4034e63 | 2669 | for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { |
eae0bfdc PP |
2670 | git_index_entry *entry = NULL; |
2671 | size_t entry_size; | |
68535125 | 2672 | |
eae0bfdc | 2673 | if ((error = read_entry(&entry, &entry_size, index, buffer, buffer_size, last)) < 0) { |
3dbee456 RB |
2674 | error = index_error_invalid("invalid entry"); |
2675 | goto done; | |
2676 | } | |
68535125 | 2677 | |
8a2834d3 RB |
2678 | if ((error = git_vector_insert(&index->entries, entry)) < 0) { |
2679 | index_entry_free(entry); | |
3dbee456 | 2680 | goto done; |
8a2834d3 | 2681 | } |
c4034e63 | 2682 | |
22a2d3d5 | 2683 | if ((error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0) { |
af1d5239 CMN |
2684 | index_entry_free(entry); |
2685 | goto done; | |
2686 | } | |
9d81509a | 2687 | error = 0; |
af1d5239 | 2688 | |
11d0be23 PS |
2689 | if (index->version >= INDEX_VERSION_NUMBER_COMP) |
2690 | last = entry->path; | |
2691 | ||
68535125 VM |
2692 | seek_forward(entry_size); |
2693 | } | |
2694 | ||
3dbee456 RB |
2695 | if (i != header.entry_count) { |
2696 | error = index_error_invalid("header entries changed while parsing"); | |
2697 | goto done; | |
2698 | } | |
c3a20d5c | 2699 | |
68535125 VM |
2700 | /* There's still space for some extensions! */ |
2701 | while (buffer_size > INDEX_FOOTER_SIZE) { | |
2702 | size_t extension_size; | |
2703 | ||
ac3d33df | 2704 | if ((error = read_extension(&extension_size, index, buffer, buffer_size)) < 0) { |
3dbee456 RB |
2705 | goto done; |
2706 | } | |
68535125 VM |
2707 | |
2708 | seek_forward(extension_size); | |
2709 | } | |
2710 | ||
3dbee456 RB |
2711 | if (buffer_size != INDEX_FOOTER_SIZE) { |
2712 | error = index_error_invalid( | |
2713 | "buffer size does not match index footer size"); | |
2714 | goto done; | |
2715 | } | |
68535125 VM |
2716 | |
2717 | /* 160-bit SHA-1 over the content of the index file before this checksum. */ | |
fa48608e | 2718 | git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer); |
68535125 | 2719 | |
3dbee456 RB |
2720 | if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) { |
2721 | error = index_error_invalid( | |
2722 | "calculated checksum does not match expected"); | |
2723 | goto done; | |
2724 | } | |
68535125 | 2725 | |
5e947c91 CMN |
2726 | git_oid_cpy(&index->checksum, &checksum_calculated); |
2727 | ||
68535125 VM |
2728 | #undef seek_forward |
2729 | ||
43709ca8 RB |
2730 | /* Entries are stored case-sensitively on disk, so re-sort now if |
2731 | * in-memory index is supposed to be case-insensitive | |
2732 | */ | |
2733 | git_vector_set_sorted(&index->entries, !index->ignore_case); | |
9d81509a | 2734 | git_vector_sort(&index->entries); |
27fe6efe | 2735 | |
ac3d33df | 2736 | index->dirty = 0; |
3dbee456 RB |
2737 | done: |
2738 | return error; | |
68535125 VM |
2739 | } |
2740 | ||
4604a654 | 2741 | static bool is_index_extended(git_index *index) |
1648fbd3 | 2742 | { |
16248ee2 | 2743 | size_t i, extended; |
7c7ff7d1 | 2744 | git_index_entry *entry; |
1648fbd3 VM |
2745 | |
2746 | extended = 0; | |
2747 | ||
7c7ff7d1 | 2748 | git_vector_foreach(&index->entries, i, entry) { |
ac3d33df JK |
2749 | entry->flags &= ~GIT_INDEX_ENTRY_EXTENDED; |
2750 | if (entry->flags_extended & GIT_INDEX_ENTRY_EXTENDED_FLAGS) { | |
1648fbd3 | 2751 | extended++; |
ac3d33df | 2752 | entry->flags |= GIT_INDEX_ENTRY_EXTENDED; |
1648fbd3 VM |
2753 | } |
2754 | } | |
7c7ff7d1 | 2755 | |
4604a654 | 2756 | return (extended > 0); |
1648fbd3 VM |
2757 | } |
2758 | ||
8ceb890b | 2759 | static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char *last) |
68535125 | 2760 | { |
356f11fe | 2761 | void *mem = NULL; |
ac3d33df | 2762 | struct entry_short ondisk; |
817c2820 | 2763 | size_t path_len, disk_size; |
46b67034 | 2764 | int varint_len = 0; |
348c7335 | 2765 | char *path; |
5625d86b DT |
2766 | const char *path_start = entry->path; |
2767 | size_t same_len = 0; | |
68535125 | 2768 | |
8a2834d3 | 2769 | path_len = ((struct entry_internal *)entry)->pathlen; |
68535125 | 2770 | |
5625d86b | 2771 | if (last) { |
8ceb890b | 2772 | const char *last_c = last; |
5625d86b DT |
2773 | |
2774 | while (*path_start == *last_c) { | |
2775 | if (!*path_start || !*last_c) | |
2776 | break; | |
2777 | ++path_start; | |
2778 | ++last_c; | |
2779 | ++same_len; | |
2780 | } | |
2781 | path_len -= same_len; | |
22a2d3d5 | 2782 | varint_len = git_encode_varint(NULL, 0, strlen(last) - same_len); |
5625d86b DT |
2783 | } |
2784 | ||
46b67034 | 2785 | disk_size = index_entry_size(path_len, varint_len, entry->flags); |
68535125 | 2786 | |
7c7ff7d1 RB |
2787 | if (git_filebuf_reserve(file, &mem, disk_size) < 0) |
2788 | return -1; | |
817c2820 | 2789 | |
ac3d33df | 2790 | memset(mem, 0x0, disk_size); |
68535125 | 2791 | |
3dd26d1e VM |
2792 | /** |
2793 | * Yes, we have to truncate. | |
2794 | * | |
2795 | * The on-disk format for Index entries clearly defines | |
2796 | * the time and size fields to be 4 bytes each -- so even if | |
2797 | * we store these values with 8 bytes on-memory, they must | |
2798 | * be truncated to 4 bytes before writing to disk. | |
2799 | * | |
2800 | * In 2038 I will be either too dead or too rich to care about this | |
2801 | */ | |
ac3d33df JK |
2802 | ondisk.ctime.seconds = htonl((uint32_t)entry->ctime.seconds); |
2803 | ondisk.mtime.seconds = htonl((uint32_t)entry->mtime.seconds); | |
2804 | ondisk.ctime.nanoseconds = htonl(entry->ctime.nanoseconds); | |
2805 | ondisk.mtime.nanoseconds = htonl(entry->mtime.nanoseconds); | |
2806 | ondisk.dev = htonl(entry->dev); | |
2807 | ondisk.ino = htonl(entry->ino); | |
2808 | ondisk.mode = htonl(entry->mode); | |
2809 | ondisk.uid = htonl(entry->uid); | |
2810 | ondisk.gid = htonl(entry->gid); | |
2811 | ondisk.file_size = htonl((uint32_t)entry->file_size); | |
2812 | ||
2813 | git_oid_cpy(&ondisk.oid, &entry->id); | |
2814 | ||
2815 | ondisk.flags = htons(entry->flags); | |
2816 | ||
2817 | if (entry->flags & GIT_INDEX_ENTRY_EXTENDED) { | |
22a2d3d5 | 2818 | const size_t path_offset = offsetof(struct entry_long, path); |
ac3d33df JK |
2819 | struct entry_long ondisk_ext; |
2820 | memcpy(&ondisk_ext, &ondisk, sizeof(struct entry_short)); | |
2821 | ondisk_ext.flags_extended = htons(entry->flags_extended & | |
2822 | GIT_INDEX_ENTRY_EXTENDED_FLAGS); | |
22a2d3d5 UG |
2823 | memcpy(mem, &ondisk_ext, path_offset); |
2824 | path = (char *)mem + path_offset; | |
2825 | disk_size -= path_offset; | |
064a60e9 | 2826 | } else { |
22a2d3d5 UG |
2827 | const size_t path_offset = offsetof(struct entry_short, path); |
2828 | memcpy(mem, &ondisk, path_offset); | |
2829 | path = (char *)mem + path_offset; | |
2830 | disk_size -= path_offset; | |
064a60e9 | 2831 | } |
348c7335 | 2832 | |
5625d86b | 2833 | if (last) { |
064a60e9 | 2834 | varint_len = git_encode_varint((unsigned char *) path, |
22a2d3d5 | 2835 | disk_size, strlen(last) - same_len); |
c25aa7cd PP |
2836 | GIT_ASSERT(varint_len > 0); |
2837 | ||
064a60e9 PS |
2838 | path += varint_len; |
2839 | disk_size -= varint_len; | |
2840 | ||
2841 | /* | |
2842 | * If using path compression, we are not allowed | |
2843 | * to have additional trailing NULs. | |
2844 | */ | |
c25aa7cd | 2845 | GIT_ASSERT(disk_size == path_len + 1); |
064a60e9 PS |
2846 | } else { |
2847 | /* | |
2848 | * If no path compression is used, we do have | |
2849 | * NULs as padding. As such, simply assert that | |
2850 | * we have enough space left to write the path. | |
2851 | */ | |
c25aa7cd | 2852 | GIT_ASSERT(disk_size > path_len); |
5625d86b | 2853 | } |
064a60e9 PS |
2854 | |
2855 | memcpy(path, path_start, path_len + 1); | |
68535125 | 2856 | |
7c7ff7d1 | 2857 | return 0; |
68535125 VM |
2858 | } |
2859 | ||
817c2820 | 2860 | static int write_entries(git_index *index, git_filebuf *file) |
348c7335 | 2861 | { |
ec40b7f9 | 2862 | int error = 0; |
16248ee2 | 2863 | size_t i; |
c25aa7cd | 2864 | git_vector case_sorted = GIT_VECTOR_INIT, *entries = NULL; |
ec40b7f9 | 2865 | git_index_entry *entry; |
8ceb890b | 2866 | const char *last = NULL; |
ec40b7f9 PK |
2867 | |
2868 | /* If index->entries is sorted case-insensitively, then we need | |
2869 | * to re-sort it case-sensitively before writing */ | |
2870 | if (index->ignore_case) { | |
c25aa7cd PP |
2871 | if ((error = git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp)) < 0) |
2872 | goto done; | |
2873 | ||
ec40b7f9 | 2874 | git_vector_sort(&case_sorted); |
3dbee456 RB |
2875 | entries = &case_sorted; |
2876 | } else { | |
2877 | entries = &index->entries; | |
68535125 VM |
2878 | } |
2879 | ||
5625d86b | 2880 | if (index->version >= INDEX_VERSION_NUMBER_COMP) |
8ceb890b | 2881 | last = ""; |
5625d86b | 2882 | |
8ceb890b | 2883 | git_vector_foreach(entries, i, entry) { |
5625d86b | 2884 | if ((error = write_disk_entry(file, entry, last)) < 0) |
ec40b7f9 | 2885 | break; |
8ceb890b PS |
2886 | if (index->version >= INDEX_VERSION_NUMBER_COMP) |
2887 | last = entry->path; | |
2888 | } | |
ec40b7f9 | 2889 | |
c25aa7cd PP |
2890 | done: |
2891 | git_vector_free(&case_sorted); | |
ec40b7f9 | 2892 | return error; |
348c7335 VM |
2893 | } |
2894 | ||
f45ec1a0 ET |
2895 | static int write_extension(git_filebuf *file, struct index_extension *header, git_buf *data) |
2896 | { | |
2897 | struct index_extension ondisk; | |
f45ec1a0 ET |
2898 | |
2899 | memset(&ondisk, 0x0, sizeof(struct index_extension)); | |
2900 | memcpy(&ondisk, header, 4); | |
2901 | ondisk.extension_size = htonl(header->extension_size); | |
2902 | ||
c2f8b215 CMN |
2903 | git_filebuf_write(file, &ondisk, sizeof(struct index_extension)); |
2904 | return git_filebuf_write(file, data->ptr, data->size); | |
f45ec1a0 ET |
2905 | } |
2906 | ||
0462fba5 ET |
2907 | static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *conflict_name) |
2908 | { | |
2909 | int error = 0; | |
2910 | ||
2911 | if (conflict_name->ancestor == NULL) | |
2912 | error = git_buf_put(name_buf, "\0", 1); | |
2913 | else | |
2914 | error = git_buf_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1); | |
1fed6b07 | 2915 | |
0462fba5 ET |
2916 | if (error != 0) |
2917 | goto on_error; | |
2918 | ||
2919 | if (conflict_name->ours == NULL) | |
2920 | error = git_buf_put(name_buf, "\0", 1); | |
2921 | else | |
2922 | error = git_buf_put(name_buf, conflict_name->ours, strlen(conflict_name->ours) + 1); | |
2923 | ||
2924 | if (error != 0) | |
2925 | goto on_error; | |
2926 | ||
2927 | if (conflict_name->theirs == NULL) | |
2928 | error = git_buf_put(name_buf, "\0", 1); | |
2929 | else | |
2930 | error = git_buf_put(name_buf, conflict_name->theirs, strlen(conflict_name->theirs) + 1); | |
2931 | ||
2932 | on_error: | |
2933 | return error; | |
2934 | } | |
2935 | ||
2936 | static int write_name_extension(git_index *index, git_filebuf *file) | |
2937 | { | |
2938 | git_buf name_buf = GIT_BUF_INIT; | |
2939 | git_vector *out = &index->names; | |
2940 | git_index_name_entry *conflict_name; | |
2941 | struct index_extension extension; | |
2942 | size_t i; | |
2943 | int error = 0; | |
1fed6b07 | 2944 | |
0462fba5 ET |
2945 | git_vector_foreach(out, i, conflict_name) { |
2946 | if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0) | |
2947 | goto done; | |
2948 | } | |
1fed6b07 | 2949 | |
0462fba5 ET |
2950 | memset(&extension, 0x0, sizeof(struct index_extension)); |
2951 | memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4); | |
2952 | extension.extension_size = (uint32_t)name_buf.size; | |
1fed6b07 | 2953 | |
0462fba5 | 2954 | error = write_extension(file, &extension, &name_buf); |
1fed6b07 | 2955 | |
ac3d33df | 2956 | git_buf_dispose(&name_buf); |
1fed6b07 | 2957 | |
0462fba5 ET |
2958 | done: |
2959 | return error; | |
2960 | } | |
2961 | ||
f45ec1a0 ET |
2962 | static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc) |
2963 | { | |
2964 | int i; | |
2965 | int error = 0; | |
2966 | ||
2967 | if ((error = git_buf_put(reuc_buf, reuc->path, strlen(reuc->path) + 1)) < 0) | |
2968 | return error; | |
2969 | ||
2970 | for (i = 0; i < 3; i++) { | |
2971 | if ((error = git_buf_printf(reuc_buf, "%o", reuc->mode[i])) < 0 || | |
2972 | (error = git_buf_put(reuc_buf, "\0", 1)) < 0) | |
2973 | return error; | |
2974 | } | |
2975 | ||
2976 | for (i = 0; i < 3; i++) { | |
2977 | if (reuc->mode[i] && (error = git_buf_put(reuc_buf, (char *)&reuc->oid[i].id, GIT_OID_RAWSZ)) < 0) | |
2978 | return error; | |
2979 | } | |
2980 | ||
2981 | return 0; | |
2982 | } | |
2983 | ||
2984 | static int write_reuc_extension(git_index *index, git_filebuf *file) | |
2985 | { | |
2986 | git_buf reuc_buf = GIT_BUF_INIT; | |
2987 | git_vector *out = &index->reuc; | |
2988 | git_index_reuc_entry *reuc; | |
2989 | struct index_extension extension; | |
16248ee2 | 2990 | size_t i; |
f45ec1a0 ET |
2991 | int error = 0; |
2992 | ||
2993 | git_vector_foreach(out, i, reuc) { | |
2994 | if ((error = create_reuc_extension_data(&reuc_buf, reuc)) < 0) | |
2995 | goto done; | |
2996 | } | |
2997 | ||
2998 | memset(&extension, 0x0, sizeof(struct index_extension)); | |
2999 | memcpy(&extension.signature, INDEX_EXT_UNMERGED_SIG, 4); | |
a8122b5d | 3000 | extension.extension_size = (uint32_t)reuc_buf.size; |
f45ec1a0 ET |
3001 | |
3002 | error = write_extension(file, &extension, &reuc_buf); | |
3003 | ||
ac3d33df | 3004 | git_buf_dispose(&reuc_buf); |
f45ec1a0 ET |
3005 | |
3006 | done: | |
3007 | return error; | |
3008 | } | |
3009 | ||
c2f8b215 CMN |
3010 | static int write_tree_extension(git_index *index, git_filebuf *file) |
3011 | { | |
3012 | struct index_extension extension; | |
3013 | git_buf buf = GIT_BUF_INIT; | |
3014 | int error; | |
3015 | ||
3016 | if (index->tree == NULL) | |
3017 | return 0; | |
3018 | ||
3019 | if ((error = git_tree_cache_write(&buf, index->tree)) < 0) | |
3020 | return error; | |
3021 | ||
3022 | memset(&extension, 0x0, sizeof(struct index_extension)); | |
3023 | memcpy(&extension.signature, INDEX_EXT_TREECACHE_SIG, 4); | |
3024 | extension.extension_size = (uint32_t)buf.size; | |
3025 | ||
3026 | error = write_extension(file, &extension, &buf); | |
3027 | ||
ac3d33df | 3028 | git_buf_dispose(&buf); |
c2f8b215 CMN |
3029 | |
3030 | return error; | |
3031 | } | |
3032 | ||
27bc41cf ET |
3033 | static void clear_uptodate(git_index *index) |
3034 | { | |
3035 | git_index_entry *entry; | |
3036 | size_t i; | |
3037 | ||
3038 | git_vector_foreach(&index->entries, i, entry) | |
ac3d33df | 3039 | entry->flags_extended &= ~GIT_INDEX_ENTRY_UPTODATE; |
27bc41cf ET |
3040 | } |
3041 | ||
5e947c91 | 3042 | static int write_index(git_oid *checksum, git_index *index, git_filebuf *file) |
348c7335 | 3043 | { |
348c7335 | 3044 | git_oid hash_final; |
348c7335 | 3045 | struct index_header header; |
4604a654 | 3046 | bool is_extended; |
b1f2c2e2 | 3047 | uint32_t index_version_number; |
348c7335 | 3048 | |
c25aa7cd PP |
3049 | GIT_ASSERT_ARG(index); |
3050 | GIT_ASSERT_ARG(file); | |
348c7335 | 3051 | |
5625d86b DT |
3052 | if (index->version <= INDEX_VERSION_NUMBER_EXT) { |
3053 | is_extended = is_index_extended(index); | |
3054 | index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER_LB; | |
3055 | } else { | |
3056 | index_version_number = index->version; | |
3057 | } | |
1648fbd3 | 3058 | |
348c7335 | 3059 | header.signature = htonl(INDEX_HEADER_SIG); |
b1f2c2e2 | 3060 | header.version = htonl(index_version_number); |
b8457baa | 3061 | header.entry_count = htonl((uint32_t)index->entries.length); |
348c7335 | 3062 | |
7c7ff7d1 RB |
3063 | if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0) |
3064 | return -1; | |
348c7335 | 3065 | |
7c7ff7d1 RB |
3066 | if (write_entries(index, file) < 0) |
3067 | return -1; | |
68535125 | 3068 | |
c2f8b215 CMN |
3069 | /* write the tree cache extension */ |
3070 | if (index->tree != NULL && write_tree_extension(index, file) < 0) | |
3071 | return -1; | |
f45ec1a0 | 3072 | |
0462fba5 ET |
3073 | /* write the rename conflict extension */ |
3074 | if (index->names.length > 0 && write_name_extension(index, file) < 0) | |
3075 | return -1; | |
1fed6b07 | 3076 | |
f45ec1a0 ET |
3077 | /* write the reuc extension */ |
3078 | if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0) | |
3079 | return -1; | |
68535125 | 3080 | |
817c2820 VM |
3081 | /* get out the hash for all the contents we've appended to the file */ |
3082 | git_filebuf_hash(&hash_final, file); | |
5e947c91 | 3083 | git_oid_cpy(checksum, &hash_final); |
817c2820 VM |
3084 | |
3085 | /* write it at the end of the file */ | |
27bc41cf ET |
3086 | if (git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ) < 0) |
3087 | return -1; | |
3088 | ||
3089 | /* file entries are no longer up to date */ | |
3090 | clear_uptodate(index); | |
3091 | ||
3092 | return 0; | |
68535125 | 3093 | } |
3a42e0a3 VM |
3094 | |
3095 | int git_index_entry_stage(const git_index_entry *entry) | |
3096 | { | |
ac3d33df | 3097 | return GIT_INDEX_ENTRY_STAGE(entry); |
3a42e0a3 | 3098 | } |
599f2849 | 3099 | |
9f545b9d ET |
3100 | int git_index_entry_is_conflict(const git_index_entry *entry) |
3101 | { | |
ac3d33df | 3102 | return (GIT_INDEX_ENTRY_STAGE(entry) > 0); |
9f545b9d ET |
3103 | } |
3104 | ||
f1587b97 | 3105 | typedef struct read_tree_data { |
a64119e3 | 3106 | git_index *index; |
03a89070 | 3107 | git_vector *old_entries; |
178aa39c | 3108 | git_vector *new_entries; |
dac16048 | 3109 | git_vector_cmp entry_cmp; |
6843cebe | 3110 | git_tree_cache *tree; |
f1587b97 BS |
3111 | } read_tree_data; |
3112 | ||
03a89070 RB |
3113 | static int read_tree_cb( |
3114 | const char *root, const git_tree_entry *tentry, void *payload) | |
599f2849 | 3115 | { |
03a89070 RB |
3116 | read_tree_data *data = payload; |
3117 | git_index_entry *entry = NULL, *old_entry; | |
599f2849 | 3118 | git_buf path = GIT_BUF_INIT; |
dac16048 | 3119 | size_t pos; |
599f2849 | 3120 | |
9d0011fd | 3121 | if (git_tree_entry__is_tree(tentry)) |
7c7ff7d1 | 3122 | return 0; |
599f2849 | 3123 | |
7c7ff7d1 RB |
3124 | if (git_buf_joinpath(&path, root, tentry->filename) < 0) |
3125 | return -1; | |
599f2849 | 3126 | |
4b3ec53c | 3127 | if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, NULL, false) < 0) |
0d388adc | 3128 | return -1; |
599f2849 CB |
3129 | |
3130 | entry->mode = tentry->attr; | |
60a194aa | 3131 | git_oid_cpy(&entry->id, git_tree_entry_id(tentry)); |
5425097f | 3132 | |
03a89070 | 3133 | /* look for corresponding old entry and copy data to new entry */ |
dac16048 | 3134 | if (data->old_entries != NULL && |
52bb0476 | 3135 | !index_find_in_entries( |
dac16048 RB |
3136 | &pos, data->old_entries, data->entry_cmp, path.ptr, 0, 0) && |
3137 | (old_entry = git_vector_get(data->old_entries, pos)) != NULL && | |
3138 | entry->mode == old_entry->mode && | |
3139 | git_oid_equal(&entry->id, &old_entry->id)) | |
3140 | { | |
5f32c506 | 3141 | index_entry_cpy(entry, old_entry); |
dac16048 | 3142 | entry->flags_extended = 0; |
03a89070 RB |
3143 | } |
3144 | ||
0cc20a8c | 3145 | index_entry_adjust_namemask(entry, path.size); |
ac3d33df | 3146 | git_buf_dispose(&path); |
599f2849 | 3147 | |
178aa39c | 3148 | if (git_vector_insert(data->new_entries, entry) < 0) { |
599f2849 | 3149 | index_entry_free(entry); |
7c7ff7d1 RB |
3150 | return -1; |
3151 | } | |
3152 | ||
3153 | return 0; | |
599f2849 CB |
3154 | } |
3155 | ||
f45d51ff | 3156 | int git_index_read_tree(git_index *index, const git_tree *tree) |
599f2849 | 3157 | { |
03a89070 RB |
3158 | int error = 0; |
3159 | git_vector entries = GIT_VECTOR_INIT; | |
af1d5239 | 3160 | git_idxmap *entries_map; |
03a89070 | 3161 | read_tree_data data; |
af1d5239 CMN |
3162 | size_t i; |
3163 | git_index_entry *e; | |
3164 | ||
22a2d3d5 | 3165 | if (git_idxmap_new(&entries_map) < 0) |
af1d5239 | 3166 | return -1; |
03a89070 | 3167 | |
178aa39c | 3168 | git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */ |
03a89070 | 3169 | |
a64119e3 | 3170 | data.index = index; |
178aa39c RB |
3171 | data.old_entries = &index->entries; |
3172 | data.new_entries = &entries; | |
dac16048 | 3173 | data.entry_cmp = index->entries_search; |
599f2849 | 3174 | |
6843cebe CMN |
3175 | index->tree = NULL; |
3176 | git_pool_clear(&index->tree_pool); | |
3177 | ||
9d81509a | 3178 | git_vector_sort(&index->entries); |
03a89070 | 3179 | |
af1d5239 CMN |
3180 | if ((error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data)) < 0) |
3181 | goto cleanup; | |
03a89070 | 3182 | |
22a2d3d5 UG |
3183 | if ((error = index_map_resize(entries_map, entries.length, index->ignore_case)) < 0) |
3184 | goto cleanup; | |
3dbee456 | 3185 | |
af1d5239 | 3186 | git_vector_foreach(&entries, i, e) { |
22a2d3d5 | 3187 | if ((error = index_map_set(entries_map, e, index->ignore_case)) < 0) { |
ac3d33df | 3188 | git_error_set(GIT_ERROR_INDEX, "failed to insert entry into map"); |
af1d5239 | 3189 | return error; |
dac16048 | 3190 | } |
dab89f9b RB |
3191 | } |
3192 | ||
af1d5239 CMN |
3193 | error = 0; |
3194 | ||
3195 | git_vector_sort(&entries); | |
3196 | ||
9d81509a | 3197 | if ((error = git_index_clear(index)) < 0) { |
af1d5239 | 3198 | /* well, this isn't good */; |
af1d5239 CMN |
3199 | } else { |
3200 | git_vector_swap(&entries, &index->entries); | |
c25aa7cd | 3201 | entries_map = git_atomic_swap(index->entries_map, entries_map); |
af1d5239 CMN |
3202 | } |
3203 | ||
ac3d33df JK |
3204 | index->dirty = 1; |
3205 | ||
af1d5239 | 3206 | cleanup: |
178aa39c | 3207 | git_vector_free(&entries); |
af1d5239 | 3208 | git_idxmap_free(entries_map); |
6843cebe CMN |
3209 | if (error < 0) |
3210 | return error; | |
3211 | ||
3212 | error = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool); | |
03a89070 RB |
3213 | |
3214 | return error; | |
599f2849 | 3215 | } |
b1be9dd0 | 3216 | |
6f7ec728 | 3217 | static int git_index_read_iterator( |
35d39761 | 3218 | git_index *index, |
6f7ec728 ET |
3219 | git_iterator *new_iterator, |
3220 | size_t new_length_hint) | |
35d39761 ET |
3221 | { |
3222 | git_vector new_entries = GIT_VECTOR_INIT, | |
3223 | remove_entries = GIT_VECTOR_INIT; | |
0bf77e32 | 3224 | git_idxmap *new_entries_map = NULL; |
35d39761 | 3225 | git_iterator *index_iterator = NULL; |
ed1c6446 | 3226 | git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; |
35d39761 ET |
3227 | const git_index_entry *old_entry, *new_entry; |
3228 | git_index_entry *entry; | |
3229 | size_t i; | |
3230 | int error; | |
3231 | ||
c25aa7cd | 3232 | GIT_ASSERT((new_iterator->flags & GIT_ITERATOR_DONT_IGNORE_CASE)); |
6f7ec728 ET |
3233 | |
3234 | if ((error = git_vector_init(&new_entries, new_length_hint, index->entries._cmp)) < 0 || | |
22a2d3d5 UG |
3235 | (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 || |
3236 | (error = git_idxmap_new(&new_entries_map)) < 0) | |
35d39761 ET |
3237 | goto done; |
3238 | ||
22a2d3d5 UG |
3239 | if (new_length_hint && (error = index_map_resize(new_entries_map, new_length_hint, |
3240 | index->ignore_case)) < 0) | |
3241 | goto done; | |
0bf77e32 | 3242 | |
6249d960 ET |
3243 | opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | |
3244 | GIT_ITERATOR_INCLUDE_CONFLICTS; | |
ed1c6446 | 3245 | |
6f7ec728 ET |
3246 | if ((error = git_iterator_for_index(&index_iterator, |
3247 | git_index_owner(index), index, &opts)) < 0 || | |
3248 | ((error = git_iterator_current(&old_entry, index_iterator)) < 0 && | |
35d39761 | 3249 | error != GIT_ITEROVER) || |
0226f7dd | 3250 | ((error = git_iterator_current(&new_entry, new_iterator)) < 0 && |
35d39761 ET |
3251 | error != GIT_ITEROVER)) |
3252 | goto done; | |
3253 | ||
3254 | while (true) { | |
5f32c506 ET |
3255 | git_index_entry |
3256 | *dup_entry = NULL, | |
3257 | *add_entry = NULL, | |
3258 | *remove_entry = NULL; | |
35d39761 ET |
3259 | int diff; |
3260 | ||
93de20b8 ET |
3261 | error = 0; |
3262 | ||
35d39761 ET |
3263 | if (old_entry && new_entry) |
3264 | diff = git_index_entry_cmp(old_entry, new_entry); | |
3265 | else if (!old_entry && new_entry) | |
3266 | diff = 1; | |
3267 | else if (old_entry && !new_entry) | |
3268 | diff = -1; | |
3269 | else | |
3270 | break; | |
3271 | ||
3272 | if (diff < 0) { | |
0bf77e32 | 3273 | remove_entry = (git_index_entry *)old_entry; |
35d39761 | 3274 | } else if (diff > 0) { |
5f32c506 | 3275 | dup_entry = (git_index_entry *)new_entry; |
35d39761 ET |
3276 | } else { |
3277 | /* Path and stage are equal, if the OID is equal, keep it to | |
3278 | * keep the stat cache data. | |
3279 | */ | |
046ec3c9 ET |
3280 | if (git_oid_equal(&old_entry->id, &new_entry->id) && |
3281 | old_entry->mode == new_entry->mode) { | |
0bf77e32 | 3282 | add_entry = (git_index_entry *)old_entry; |
35d39761 | 3283 | } else { |
5f32c506 | 3284 | dup_entry = (git_index_entry *)new_entry; |
0bf77e32 | 3285 | remove_entry = (git_index_entry *)old_entry; |
35d39761 ET |
3286 | } |
3287 | } | |
3288 | ||
5f32c506 ET |
3289 | if (dup_entry) { |
3290 | if ((error = index_entry_dup_nocache(&add_entry, index, dup_entry)) < 0) | |
3291 | goto done; | |
9167c145 ET |
3292 | |
3293 | index_entry_adjust_namemask(add_entry, | |
3294 | ((struct entry_internal *)add_entry)->pathlen); | |
5f32c506 ET |
3295 | } |
3296 | ||
46082c38 ET |
3297 | /* invalidate this path in the tree cache if this is new (to |
3298 | * invalidate the parent trees) | |
3299 | */ | |
3300 | if (dup_entry && !remove_entry && index->tree) | |
3301 | git_tree_cache_invalidate_path(index->tree, dup_entry->path); | |
3302 | ||
0bf77e32 ET |
3303 | if (add_entry) { |
3304 | if ((error = git_vector_insert(&new_entries, add_entry)) == 0) | |
22a2d3d5 UG |
3305 | error = index_map_set(new_entries_map, add_entry, |
3306 | index->ignore_case); | |
0bf77e32 ET |
3307 | } |
3308 | ||
b057fdef | 3309 | if (remove_entry && error >= 0) |
0bf77e32 ET |
3310 | error = git_vector_insert(&remove_entries, remove_entry); |
3311 | ||
3312 | if (error < 0) { | |
ac3d33df | 3313 | git_error_set(GIT_ERROR_INDEX, "failed to insert entry"); |
f80852af | 3314 | goto done; |
0bf77e32 ET |
3315 | } |
3316 | ||
35d39761 ET |
3317 | if (diff <= 0) { |
3318 | if ((error = git_iterator_advance(&old_entry, index_iterator)) < 0 && | |
3319 | error != GIT_ITEROVER) | |
3320 | goto done; | |
3321 | } | |
3322 | ||
3323 | if (diff >= 0) { | |
3324 | if ((error = git_iterator_advance(&new_entry, new_iterator)) < 0 && | |
3325 | error != GIT_ITEROVER) | |
3326 | goto done; | |
3327 | } | |
3328 | } | |
3329 | ||
22a2d3d5 UG |
3330 | if ((error = git_index_name_clear(index)) < 0 || |
3331 | (error = git_index_reuc_clear(index)) < 0) | |
3332 | goto done; | |
35d39761 ET |
3333 | |
3334 | git_vector_swap(&new_entries, &index->entries); | |
c25aa7cd | 3335 | new_entries_map = git_atomic_swap(index->entries_map, new_entries_map); |
35d39761 ET |
3336 | |
3337 | git_vector_foreach(&remove_entries, i, entry) { | |
3338 | if (index->tree) | |
3339 | git_tree_cache_invalidate_path(index->tree, entry->path); | |
3340 | ||
3341 | index_entry_free(entry); | |
3342 | } | |
3343 | ||
6f7ec728 ET |
3344 | clear_uptodate(index); |
3345 | ||
ac3d33df | 3346 | index->dirty = 1; |
35d39761 ET |
3347 | error = 0; |
3348 | ||
3349 | done: | |
0bf77e32 | 3350 | git_idxmap_free(new_entries_map); |
35d39761 ET |
3351 | git_vector_free(&new_entries); |
3352 | git_vector_free(&remove_entries); | |
3353 | git_iterator_free(index_iterator); | |
6f7ec728 ET |
3354 | return error; |
3355 | } | |
3356 | ||
3357 | int git_index_read_index( | |
3358 | git_index *index, | |
3359 | const git_index *new_index) | |
3360 | { | |
3361 | git_iterator *new_iterator = NULL; | |
3362 | git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; | |
3363 | int error; | |
3364 | ||
6249d960 ET |
3365 | opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | |
3366 | GIT_ITERATOR_INCLUDE_CONFLICTS; | |
6f7ec728 ET |
3367 | |
3368 | if ((error = git_iterator_for_index(&new_iterator, | |
6249d960 ET |
3369 | git_index_owner(new_index), (git_index *)new_index, &opts)) < 0 || |
3370 | (error = git_index_read_iterator(index, new_iterator, | |
3371 | new_index->entries.length)) < 0) | |
6f7ec728 ET |
3372 | goto done; |
3373 | ||
6f7ec728 | 3374 | done: |
35d39761 ET |
3375 | git_iterator_free(new_iterator); |
3376 | return error; | |
3377 | } | |
3378 | ||
b1be9dd0 | 3379 | git_repository *git_index_owner(const git_index *index) |
3380 | { | |
3381 | return INDEX_OWNER(index); | |
3382 | } | |
f30fff45 | 3383 | |
0a78a52e CMN |
3384 | enum { |
3385 | INDEX_ACTION_NONE = 0, | |
3386 | INDEX_ACTION_UPDATE = 1, | |
3387 | INDEX_ACTION_REMOVE = 2, | |
3388 | INDEX_ACTION_ADDALL = 3, | |
3389 | }; | |
3390 | ||
f30fff45 RB |
3391 | int git_index_add_all( |
3392 | git_index *index, | |
3393 | const git_strarray *paths, | |
3394 | unsigned int flags, | |
3395 | git_index_matched_path_cb cb, | |
3396 | void *payload) | |
3397 | { | |
3398 | int error; | |
3399 | git_repository *repo; | |
3400 | git_iterator *wditer = NULL; | |
d2ce27dd | 3401 | git_pathspec ps; |
f30fff45 | 3402 | bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0; |
f30fff45 | 3403 | |
c25aa7cd | 3404 | GIT_ASSERT_ARG(index); |
f30fff45 | 3405 | |
f30fff45 RB |
3406 | repo = INDEX_OWNER(index); |
3407 | if ((error = git_repository__ensure_not_bare(repo, "index add all")) < 0) | |
3408 | return error; | |
3409 | ||
d2ce27dd | 3410 | if ((error = git_pathspec__init(&ps, paths)) < 0) |
f30fff45 RB |
3411 | return error; |
3412 | ||
3413 | /* optionally check that pathspec doesn't mention any ignored files */ | |
3414 | if ((flags & GIT_INDEX_ADD_CHECK_PATHSPEC) != 0 && | |
3415 | (flags & GIT_INDEX_ADD_FORCE) == 0 && | |
3416 | (error = git_ignore__check_pathspec_for_exact_ignores( | |
3417 | repo, &ps.pathspec, no_fnmatch)) < 0) | |
3418 | goto cleanup; | |
3419 | ||
0a78a52e | 3420 | error = index_apply_to_wd_diff(index, INDEX_ACTION_ADDALL, paths, flags, cb, payload); |
f30fff45 | 3421 | |
0a78a52e | 3422 | if (error) |
ac3d33df | 3423 | git_error_set_after_callback(error); |
f30fff45 RB |
3424 | |
3425 | cleanup: | |
3426 | git_iterator_free(wditer); | |
d2ce27dd | 3427 | git_pathspec__clear(&ps); |
f30fff45 RB |
3428 | |
3429 | return error; | |
3430 | } | |
3431 | ||
197307f6 CMN |
3432 | struct foreach_diff_data { |
3433 | git_index *index; | |
3434 | const git_pathspec *pathspec; | |
0a78a52e | 3435 | unsigned int flags; |
197307f6 CMN |
3436 | git_index_matched_path_cb cb; |
3437 | void *payload; | |
3438 | }; | |
3439 | ||
3440 | static int apply_each_file(const git_diff_delta *delta, float progress, void *payload) | |
3441 | { | |
3442 | struct foreach_diff_data *data = payload; | |
3443 | const char *match, *path; | |
3444 | int error = 0; | |
3445 | ||
3446 | GIT_UNUSED(progress); | |
3447 | ||
3448 | path = delta->old_file.path; | |
3449 | ||
3450 | /* We only want those which match the pathspecs */ | |
3451 | if (!git_pathspec__match( | |
3452 | &data->pathspec->pathspec, path, false, (bool)data->index->ignore_case, | |
3453 | &match, NULL)) | |
3454 | return 0; | |
3455 | ||
3456 | if (data->cb) | |
3457 | error = data->cb(path, match, data->payload); | |
3458 | ||
3459 | if (error > 0) /* skip this entry */ | |
3460 | return 0; | |
3461 | if (error < 0) /* actual error */ | |
3462 | return error; | |
3463 | ||
9b3e41f7 ET |
3464 | /* If the workdir item does not exist, remove it from the index. */ |
3465 | if ((delta->new_file.flags & GIT_DIFF_FLAG_EXISTS) == 0) | |
197307f6 CMN |
3466 | error = git_index_remove_bypath(data->index, path); |
3467 | else | |
0a78a52e | 3468 | error = git_index_add_bypath(data->index, delta->new_file.path); |
197307f6 CMN |
3469 | |
3470 | return error; | |
3471 | } | |
3472 | ||
3473 | static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths, | |
0a78a52e | 3474 | unsigned int flags, |
197307f6 CMN |
3475 | git_index_matched_path_cb cb, void *payload) |
3476 | { | |
3477 | int error; | |
3478 | git_diff *diff; | |
3479 | git_pathspec ps; | |
3480 | git_repository *repo; | |
0a78a52e | 3481 | git_diff_options opts = GIT_DIFF_OPTIONS_INIT; |
197307f6 CMN |
3482 | struct foreach_diff_data data = { |
3483 | index, | |
3484 | NULL, | |
0a78a52e | 3485 | flags, |
197307f6 CMN |
3486 | cb, |
3487 | payload, | |
3488 | }; | |
3489 | ||
c25aa7cd PP |
3490 | GIT_ASSERT_ARG(index); |
3491 | GIT_ASSERT_ARG(action == INDEX_ACTION_UPDATE || action == INDEX_ACTION_ADDALL); | |
197307f6 CMN |
3492 | |
3493 | repo = INDEX_OWNER(index); | |
3494 | ||
3495 | if (!repo) { | |
3496 | return create_index_error(-1, | |
3497 | "cannot run update; the index is not backed up by a repository."); | |
3498 | } | |
3499 | ||
3500 | /* | |
3501 | * We do the matching ourselves intead of passing the list to | |
3502 | * diff because we want to tell the callback which one | |
3503 | * matched, which we do not know if we ask diff to filter for us. | |
3504 | */ | |
3505 | if ((error = git_pathspec__init(&ps, paths)) < 0) | |
3506 | return error; | |
3507 | ||
2b2dfe80 | 3508 | opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE; |
0a78a52e | 3509 | if (action == INDEX_ACTION_ADDALL) { |
fa9a969d | 3510 | opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | |
cbfeecf3 | 3511 | GIT_DIFF_RECURSE_UNTRACKED_DIRS; |
fa9a969d | 3512 | |
0a78a52e CMN |
3513 | if (flags == GIT_INDEX_ADD_FORCE) |
3514 | opts.flags |= GIT_DIFF_INCLUDE_IGNORED; | |
3515 | } | |
3516 | ||
3517 | if ((error = git_diff_index_to_workdir(&diff, repo, index, &opts)) < 0) | |
197307f6 CMN |
3518 | goto cleanup; |
3519 | ||
3520 | data.pathspec = &ps; | |
8147b1af | 3521 | error = git_diff_foreach(diff, apply_each_file, NULL, NULL, NULL, &data); |
197307f6 CMN |
3522 | git_diff_free(diff); |
3523 | ||
3524 | if (error) /* make sure error is set if callback stopped iteration */ | |
ac3d33df | 3525 | git_error_set_after_callback(error); |
197307f6 CMN |
3526 | |
3527 | cleanup: | |
3528 | git_pathspec__clear(&ps); | |
3529 | return error; | |
3530 | } | |
3531 | ||
f30fff45 RB |
3532 | static int index_apply_to_all( |
3533 | git_index *index, | |
3534 | int action, | |
3535 | const git_strarray *paths, | |
3536 | git_index_matched_path_cb cb, | |
3537 | void *payload) | |
3538 | { | |
3539 | int error = 0; | |
3540 | size_t i; | |
d2ce27dd | 3541 | git_pathspec ps; |
f30fff45 | 3542 | const char *match; |
7863523a | 3543 | git_buf path = GIT_BUF_INIT; |
f30fff45 | 3544 | |
c25aa7cd | 3545 | GIT_ASSERT_ARG(index); |
f30fff45 | 3546 | |
d2ce27dd | 3547 | if ((error = git_pathspec__init(&ps, paths)) < 0) |
f30fff45 RB |
3548 | return error; |
3549 | ||
3550 | git_vector_sort(&index->entries); | |
3551 | ||
3552 | for (i = 0; !error && i < index->entries.length; ++i) { | |
3553 | git_index_entry *entry = git_vector_get(&index->entries, i); | |
3554 | ||
3555 | /* check if path actually matches */ | |
d2ce27dd | 3556 | if (!git_pathspec__match( |
66566516 | 3557 | &ps.pathspec, entry->path, false, (bool)index->ignore_case, |
d2ce27dd | 3558 | &match, NULL)) |
f30fff45 RB |
3559 | continue; |
3560 | ||
3561 | /* issue notification callback if requested */ | |
3562 | if (cb && (error = cb(entry->path, match, payload)) != 0) { | |
3563 | if (error > 0) { /* return > 0 means skip this one */ | |
3564 | error = 0; | |
3565 | continue; | |
3566 | } | |
26c1cb91 | 3567 | if (error < 0) /* return < 0 means abort */ |
f30fff45 | 3568 | break; |
f30fff45 RB |
3569 | } |
3570 | ||
7863523a RB |
3571 | /* index manipulation may alter entry, so don't depend on it */ |
3572 | if ((error = git_buf_sets(&path, entry->path)) < 0) | |
3573 | break; | |
3574 | ||
f30fff45 RB |
3575 | switch (action) { |
3576 | case INDEX_ACTION_NONE: | |
3577 | break; | |
3578 | case INDEX_ACTION_UPDATE: | |
7863523a | 3579 | error = git_index_add_bypath(index, path.ptr); |
f30fff45 RB |
3580 | |
3581 | if (error == GIT_ENOTFOUND) { | |
ac3d33df | 3582 | git_error_clear(); |
f30fff45 | 3583 | |
7863523a | 3584 | error = git_index_remove_bypath(index, path.ptr); |
f30fff45 RB |
3585 | |
3586 | if (!error) /* back up foreach if we removed this */ | |
3587 | i--; | |
3588 | } | |
3589 | break; | |
3590 | case INDEX_ACTION_REMOVE: | |
7863523a | 3591 | if (!(error = git_index_remove_bypath(index, path.ptr))) |
f30fff45 RB |
3592 | i--; /* back up foreach if we removed this */ |
3593 | break; | |
3594 | default: | |
ac3d33df | 3595 | git_error_set(GIT_ERROR_INVALID, "unknown index action %d", action); |
f30fff45 RB |
3596 | error = -1; |
3597 | break; | |
3598 | } | |
3599 | } | |
3600 | ||
ac3d33df | 3601 | git_buf_dispose(&path); |
d2ce27dd | 3602 | git_pathspec__clear(&ps); |
f30fff45 RB |
3603 | |
3604 | return error; | |
3605 | } | |
3606 | ||
3607 | int git_index_remove_all( | |
3608 | git_index *index, | |
3609 | const git_strarray *pathspec, | |
3610 | git_index_matched_path_cb cb, | |
3611 | void *payload) | |
3612 | { | |
26c1cb91 | 3613 | int error = index_apply_to_all( |
f30fff45 | 3614 | index, INDEX_ACTION_REMOVE, pathspec, cb, payload); |
26c1cb91 RB |
3615 | |
3616 | if (error) /* make sure error is set if callback stopped iteration */ | |
ac3d33df | 3617 | git_error_set_after_callback(error); |
26c1cb91 RB |
3618 | |
3619 | return error; | |
f30fff45 RB |
3620 | } |
3621 | ||
3622 | int git_index_update_all( | |
3623 | git_index *index, | |
3624 | const git_strarray *pathspec, | |
3625 | git_index_matched_path_cb cb, | |
3626 | void *payload) | |
3627 | { | |
0a78a52e | 3628 | int error = index_apply_to_wd_diff(index, INDEX_ACTION_UPDATE, pathspec, 0, cb, payload); |
26c1cb91 | 3629 | if (error) /* make sure error is set if callback stopped iteration */ |
ac3d33df | 3630 | git_error_set_after_callback(error); |
26c1cb91 RB |
3631 | |
3632 | return error; | |
f30fff45 | 3633 | } |
54edbb98 | 3634 | |
52bb0476 | 3635 | int git_index_snapshot_new(git_vector *snap, git_index *index) |
54edbb98 RB |
3636 | { |
3637 | int error; | |
3638 | ||
3639 | GIT_REFCOUNT_INC(index); | |
dac16048 | 3640 | |
c25aa7cd | 3641 | git_atomic32_inc(&index->readers); |
54edbb98 | 3642 | git_vector_sort(&index->entries); |
dac16048 | 3643 | |
52bb0476 | 3644 | error = git_vector_dup(snap, &index->entries, index->entries._cmp); |
54edbb98 RB |
3645 | |
3646 | if (error < 0) | |
6c7cee42 | 3647 | git_index_snapshot_release(snap, index); |
54edbb98 RB |
3648 | |
3649 | return error; | |
3650 | } | |
3651 | ||
52bb0476 | 3652 | void git_index_snapshot_release(git_vector *snap, git_index *index) |
54edbb98 | 3653 | { |
52bb0476 RB |
3654 | git_vector_free(snap); |
3655 | ||
c25aa7cd | 3656 | git_atomic32_dec(&index->readers); |
dac16048 | 3657 | |
8a2834d3 | 3658 | git_index_free(index); |
54edbb98 | 3659 | } |
52bb0476 RB |
3660 | |
3661 | int git_index_snapshot_find( | |
3662 | size_t *out, git_vector *entries, git_vector_cmp entry_srch, | |
3663 | const char *path, size_t path_len, int stage) | |
3664 | { | |
3665 | return index_find_in_entries(out, entries, entry_srch, path, path_len, stage); | |
3666 | } | |
55798fd1 ET |
3667 | |
3668 | int git_indexwriter_init( | |
3669 | git_indexwriter *writer, | |
3670 | git_index *index) | |
3671 | { | |
3672 | int error; | |
3673 | ||
41fae48d ET |
3674 | GIT_REFCOUNT_INC(index); |
3675 | ||
55798fd1 ET |
3676 | writer->index = index; |
3677 | ||
3678 | if (!index->index_file_path) | |
3679 | return create_index_error(-1, | |
909d5494 | 3680 | "failed to write index: The index is in-memory only"); |
55798fd1 ET |
3681 | |
3682 | if ((error = git_filebuf_open( | |
3683 | &writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) { | |
41fae48d | 3684 | |
55798fd1 | 3685 | if (error == GIT_ELOCKED) |
ac3d33df | 3686 | git_error_set(GIT_ERROR_INDEX, "the index is locked; this might be due to a concurrent or crashed process"); |
55798fd1 ET |
3687 | |
3688 | return error; | |
3689 | } | |
3690 | ||
41fae48d ET |
3691 | writer->should_write = 1; |
3692 | ||
3693 | return 0; | |
3694 | } | |
3695 | ||
3696 | int git_indexwriter_init_for_operation( | |
3697 | git_indexwriter *writer, | |
3698 | git_repository *repo, | |
3699 | unsigned int *checkout_strategy) | |
3700 | { | |
3701 | git_index *index; | |
3702 | int error; | |
3703 | ||
3704 | if ((error = git_repository_index__weakptr(&index, repo)) < 0 || | |
3705 | (error = git_indexwriter_init(writer, index)) < 0) | |
3706 | return error; | |
3707 | ||
3708 | writer->should_write = (*checkout_strategy & GIT_CHECKOUT_DONT_WRITE_INDEX) == 0; | |
3709 | *checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX; | |
3710 | ||
55798fd1 ET |
3711 | return 0; |
3712 | } | |
3713 | ||
3714 | int git_indexwriter_commit(git_indexwriter *writer) | |
3715 | { | |
3716 | int error; | |
5e947c91 | 3717 | git_oid checksum = {{ 0 }}; |
55798fd1 | 3718 | |
41fae48d ET |
3719 | if (!writer->should_write) |
3720 | return 0; | |
3721 | ||
9d81509a | 3722 | git_vector_sort(&writer->index->entries); |
55798fd1 ET |
3723 | git_vector_sort(&writer->index->reuc); |
3724 | ||
5e947c91 | 3725 | if ((error = write_index(&checksum, writer->index, &writer->file)) < 0) { |
55798fd1 ET |
3726 | git_indexwriter_cleanup(writer); |
3727 | return error; | |
3728 | } | |
3729 | ||
3730 | if ((error = git_filebuf_commit(&writer->file)) < 0) | |
3731 | return error; | |
3732 | ||
3733 | if ((error = git_futils_filestamp_check( | |
3734 | &writer->index->stamp, writer->index->index_file_path)) < 0) { | |
ac3d33df | 3735 | git_error_set(GIT_ERROR_OS, "could not read index timestamp"); |
55798fd1 ET |
3736 | return -1; |
3737 | } | |
3738 | ||
ac3d33df | 3739 | writer->index->dirty = 0; |
55798fd1 | 3740 | writer->index->on_disk = 1; |
5e947c91 | 3741 | git_oid_cpy(&writer->index->checksum, &checksum); |
55798fd1 | 3742 | |
41fae48d ET |
3743 | git_index_free(writer->index); |
3744 | writer->index = NULL; | |
3745 | ||
55798fd1 ET |
3746 | return 0; |
3747 | } | |
3748 | ||
3749 | void git_indexwriter_cleanup(git_indexwriter *writer) | |
3750 | { | |
3751 | git_filebuf_cleanup(&writer->file); | |
41fae48d ET |
3752 | |
3753 | git_index_free(writer->index); | |
3754 | writer->index = NULL; | |
55798fd1 | 3755 | } |
22a2d3d5 UG |
3756 | |
3757 | /* Deprecated functions */ | |
3758 | ||
3759 | #ifndef GIT_DEPRECATE_HARD | |
3760 | int git_index_add_frombuffer( | |
3761 | git_index *index, const git_index_entry *source_entry, | |
3762 | const void *buffer, size_t len) | |
3763 | { | |
3764 | return git_index_add_from_buffer(index, source_entry, buffer, len); | |
3765 | } | |
3766 | #endif |