source src/status.c
Line | Flow | Count | Block(s) | Source |
---|---|---|---|---|
1 | - | /* | ||
2 | - | * Copyright (C) the libgit2 contributors. All rights reserved. | ||
3 | - | * | ||
4 | - | * This file is part of libgit2, distributed under the GNU GPL v2 with | ||
5 | - | * a Linking Exception. For full terms see the included COPYING file. | ||
6 | - | */ | ||
7 | - | |||
8 | - | #include "status.h" | ||
9 | - | |||
10 | - | #include "git2.h" | ||
11 | - | #include "futils.h" | ||
12 | - | #include "hash.h" | ||
13 | - | #include "vector.h" | ||
14 | - | #include "tree.h" | ||
15 | - | #include "git2/status.h" | ||
16 | - | #include "repository.h" | ||
17 | - | #include "ignore.h" | ||
18 | - | #include "index.h" | ||
19 | - | #include "wildmatch.h" | ||
20 | - | |||
21 | - | #include "git2/diff.h" | ||
22 | - | #include "diff.h" | ||
23 | - | #include "diff_generate.h" | ||
24 | - | |||
25 | 666 | 2 | static unsigned int index_delta2status(const git_diff_delta *head2idx) | |
26 | - | { | ||
27 | 666 | 2 | git_status_t st = GIT_STATUS_CURRENT; | |
28 | - | |||
29 | 666 | 2 | switch (head2idx->status) { | |
30 | - | case GIT_DELTA_ADDED: | ||
31 | - | case GIT_DELTA_COPIED: | ||
32 | 253 | 3 | st = GIT_STATUS_INDEX_NEW; | |
33 | 253 | 3 | break; | |
34 | - | case GIT_DELTA_DELETED: | ||
35 | 58 | 4 | st = GIT_STATUS_INDEX_DELETED; | |
36 | 58 | 4 | break; | |
37 | - | case GIT_DELTA_MODIFIED: | ||
38 | 138 | 5 | st = GIT_STATUS_INDEX_MODIFIED; | |
39 | 138 | 5 | break; | |
40 | - | case GIT_DELTA_RENAMED: | ||
41 | 18 | 6 | st = GIT_STATUS_INDEX_RENAMED; | |
42 | - | |||
43 | 18 | 6,7 | if (!git_oid_equal(&head2idx->old_file.id, &head2idx->new_file.id)) | |
44 | 5 | 8 | st |= GIT_STATUS_INDEX_MODIFIED; | |
45 | 18 | 9 | break; | |
46 | - | case GIT_DELTA_TYPECHANGE: | ||
47 | ##### | 10 | st = GIT_STATUS_INDEX_TYPECHANGE; | |
48 | ##### | 10 | break; | |
49 | - | case GIT_DELTA_CONFLICTED: | ||
50 | 6 | 11 | st = GIT_STATUS_CONFLICTED; | |
51 | 6 | 11 | break; | |
52 | - | default: | ||
53 | 193 | 12 | break; | |
54 | - | } | ||
55 | - | |||
56 | 666 | 13 | return st; | |
57 | - | } | ||
58 | - | |||
59 | 1031 | 2 | static unsigned int workdir_delta2status( | |
60 | - | git_diff *diff, git_diff_delta *idx2wd) | ||
61 | - | { | ||
62 | 1031 | 2 | git_status_t st = GIT_STATUS_CURRENT; | |
63 | - | |||
64 | 1031 | 2 | switch (idx2wd->status) { | |
65 | - | case GIT_DELTA_ADDED: | ||
66 | - | case GIT_DELTA_COPIED: | ||
67 | - | case GIT_DELTA_UNTRACKED: | ||
68 | 332 | 3 | st = GIT_STATUS_WT_NEW; | |
69 | 332 | 3 | break; | |
70 | - | case GIT_DELTA_UNREADABLE: | ||
71 | 1 | 4 | st = GIT_STATUS_WT_UNREADABLE; | |
72 | 1 | 4 | break; | |
73 | - | case GIT_DELTA_DELETED: | ||
74 | 86 | 5 | st = GIT_STATUS_WT_DELETED; | |
75 | 86 | 5 | break; | |
76 | - | case GIT_DELTA_MODIFIED: | ||
77 | 268 | 6 | st = GIT_STATUS_WT_MODIFIED; | |
78 | 268 | 6 | break; | |
79 | - | case GIT_DELTA_IGNORED: | ||
80 | 109 | 7 | st = GIT_STATUS_IGNORED; | |
81 | 109 | 7 | break; | |
82 | - | case GIT_DELTA_RENAMED: | ||
83 | 19 | 8 | st = GIT_STATUS_WT_RENAMED; | |
84 | - | |||
85 | 19 | 8,9 | if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) { | |
86 | - | /* if OIDs don't match, we might need to calculate them now to | ||
87 | - | * discern between RENAMED vs RENAMED+MODIFED | ||
88 | - | */ | ||
89 | 19 | 10-12 | if (git_oid_is_zero(&idx2wd->old_file.id) && | |
90 | ##### | 12,14 | diff->old_src == GIT_ITERATOR_WORKDIR && | |
91 | ##### | 13,13 | !git_diff__oid_for_file( | |
92 | - | &idx2wd->old_file.id, diff, idx2wd->old_file.path, | ||
93 | ##### | 13 | idx2wd->old_file.mode, idx2wd->old_file.size)) | |
94 | ##### | 15 | idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; | |
95 | - | |||
96 | 19 | 16-18 | if (git_oid_is_zero(&idx2wd->new_file.id) && | |
97 | 19 | 18,20 | diff->new_src == GIT_ITERATOR_WORKDIR && | |
98 | 19 | 19,19 | !git_diff__oid_for_file( | |
99 | - | &idx2wd->new_file.id, diff, idx2wd->new_file.path, | ||
100 | 19 | 19 | idx2wd->new_file.mode, idx2wd->new_file.size)) | |
101 | 19 | 21 | idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; | |
102 | - | |||
103 | 19 | 22,23 | if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) | |
104 | 6 | 24 | st |= GIT_STATUS_WT_MODIFIED; | |
105 | - | } | ||
106 | 19 | 25 | break; | |
107 | - | case GIT_DELTA_TYPECHANGE: | ||
108 | ##### | 26 | st = GIT_STATUS_WT_TYPECHANGE; | |
109 | ##### | 26 | break; | |
110 | - | case GIT_DELTA_CONFLICTED: | ||
111 | 6 | 27 | st = GIT_STATUS_CONFLICTED; | |
112 | 6 | 27 | break; | |
113 | - | default: | ||
114 | 210 | 28 | break; | |
115 | - | } | ||
116 | - | |||
117 | 1031 | 29 | return st; | |
118 | - | } | ||
119 | - | |||
120 | 1278 | 2 | static bool status_is_included( | |
121 | - | git_status_list *status, | ||
122 | - | git_diff_delta *head2idx, | ||
123 | - | git_diff_delta *idx2wd) | ||
124 | - | { | ||
125 | 1278 | 2 | if (!(status->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES)) | |
126 | 1052 | 3 | return 1; | |
127 | - | |||
128 | - | /* if excluding submodules and this is a submodule everywhere */ | ||
129 | 226 | 4 | if (head2idx) { | |
130 | 126 | 5,6 | if (head2idx->status != GIT_DELTA_ADDED && | |
131 | 54 | 6 | head2idx->old_file.mode != GIT_FILEMODE_COMMIT) | |
132 | 54 | 7 | return 1; | |
133 | 72 | 8,9 | if (head2idx->status != GIT_DELTA_DELETED && | |
134 | 72 | 9 | head2idx->new_file.mode != GIT_FILEMODE_COMMIT) | |
135 | 72 | 10 | return 1; | |
136 | - | } | ||
137 | 100 | 11 | if (idx2wd) { | |
138 | 100 | 12,13 | if (idx2wd->status != GIT_DELTA_ADDED && | |
139 | 100 | 13 | idx2wd->old_file.mode != GIT_FILEMODE_COMMIT) | |
140 | 100 | 14 | return 1; | |
141 | ##### | 15,16 | if (idx2wd->status != GIT_DELTA_DELETED && | |
142 | ##### | 16 | idx2wd->new_file.mode != GIT_FILEMODE_COMMIT) | |
143 | ##### | 17 | return 1; | |
144 | - | } | ||
145 | - | |||
146 | - | /* only get here if every valid mode is GIT_FILEMODE_COMMIT */ | ||
147 | ##### | 18 | return 0; | |
148 | - | } | ||
149 | - | |||
150 | 1278 | 2 | static git_status_t status_compute( | |
151 | - | git_status_list *status, | ||
152 | - | git_diff_delta *head2idx, | ||
153 | - | git_diff_delta *idx2wd) | ||
154 | - | { | ||
155 | 1278 | 2 | git_status_t st = GIT_STATUS_CURRENT; | |
156 | - | |||
157 | 1278 | 2 | if (head2idx) | |
158 | 666 | 3,4 | st |= index_delta2status(head2idx); | |
159 | - | |||
160 | 1278 | 5 | if (idx2wd) | |
161 | 1031 | 6,7 | st |= workdir_delta2status(status->idx2wd, idx2wd); | |
162 | - | |||
163 | 1278 | 8 | return st; | |
164 | - | } | ||
165 | - | |||
166 | 1278 | 2 | static int status_collect( | |
167 | - | git_diff_delta *head2idx, | ||
168 | - | git_diff_delta *idx2wd, | ||
169 | - | void *payload) | ||
170 | - | { | ||
171 | 1278 | 2 | git_status_list *status = payload; | |
172 | - | git_status_entry *status_entry; | ||
173 | - | |||
174 | 1278 | 2,3 | if (!status_is_included(status, head2idx, idx2wd)) | |
175 | ##### | 4 | return 0; | |
176 | - | |||
177 | 1278 | 5 | status_entry = git__malloc(sizeof(git_status_entry)); | |
178 | 1278 | 6,7 | GIT_ERROR_CHECK_ALLOC(status_entry); | |
179 | - | |||
180 | 1278 | 8 | status_entry->status = status_compute(status, head2idx, idx2wd); | |
181 | 1278 | 9 | status_entry->head_to_index = head2idx; | |
182 | 1278 | 9 | status_entry->index_to_workdir = idx2wd; | |
183 | - | |||
184 | 1278 | 9 | return git_vector_insert(&status->paired, status_entry); | |
185 | - | } | ||
186 | - | |||
187 | 80 | 2 | GIT_INLINE(int) status_entry_cmp_base( | |
188 | - | const void *a, | ||
189 | - | const void *b, | ||
190 | - | int (*strcomp)(const char *a, const char *b)) | ||
191 | - | { | ||
192 | 80 | 2 | const git_status_entry *entry_a = a; | |
193 | 80 | 2 | const git_status_entry *entry_b = b; | |
194 | - | const git_diff_delta *delta_a, *delta_b; | ||
195 | - | |||
196 | 80 | 2-4 | delta_a = entry_a->index_to_workdir ? entry_a->index_to_workdir : | |
197 | - | entry_a->head_to_index; | ||
198 | 80 | 5-7 | delta_b = entry_b->index_to_workdir ? entry_b->index_to_workdir : | |
199 | - | entry_b->head_to_index; | ||
200 | - | |||
201 | 80 | 8,9 | if (!delta_a && delta_b) | |
202 | ##### | 10 | return -1; | |
203 | 80 | 11,12 | if (delta_a && !delta_b) | |
204 | ##### | 13 | return 1; | |
205 | 80 | 14,15 | if (!delta_a && !delta_b) | |
206 | ##### | 16 | return 0; | |
207 | - | |||
208 | 80 | 17 | return strcomp(delta_a->new_file.path, delta_b->new_file.path); | |
209 | - | } | ||
210 | - | |||
211 | 35 | 2 | static int status_entry_icmp(const void *a, const void *b) | |
212 | - | { | ||
213 | 35 | 2 | return status_entry_cmp_base(a, b, git__strcasecmp); | |
214 | - | } | ||
215 | - | |||
216 | 45 | 2 | static int status_entry_cmp(const void *a, const void *b) | |
217 | - | { | ||
218 | 45 | 2 | return status_entry_cmp_base(a, b, git__strcmp); | |
219 | - | } | ||
220 | - | |||
221 | 664 | 2 | static git_status_list *git_status_list_alloc(git_index *index) | |
222 | - | { | ||
223 | 664 | 2 | git_status_list *status = NULL; | |
224 | - | int (*entrycmp)(const void *a, const void *b); | ||
225 | - | |||
226 | 664 | 2,3 | if (!(status = git__calloc(1, sizeof(git_status_list)))) | |
227 | ##### | 4 | return NULL; | |
228 | - | |||
229 | 664 | 5-7 | entrycmp = index->ignore_case ? status_entry_icmp : status_entry_cmp; | |
230 | - | |||
231 | 664 | 8,9 | if (git_vector_init(&status->paired, 0, entrycmp) < 0) { | |
232 | ##### | 10 | git__free(status); | |
233 | ##### | 11 | return NULL; | |
234 | - | } | ||
235 | - | |||
236 | 664 | 12 | return status; | |
237 | - | } | ||
238 | - | |||
239 | 665 | 2 | static int status_validate_options(const git_status_options *opts) | |
240 | - | { | ||
241 | 665 | 2 | if (!opts) | |
242 | 112 | 3 | return 0; | |
243 | - | |||
244 | 553 | 4-6 | GIT_ERROR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); | |
245 | - | |||
246 | 553 | 7 | if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) { | |
247 | ##### | 8 | git_error_set(GIT_ERROR_INVALID, "unknown status 'show' option"); | |
248 | ##### | 9 | return -1; | |
249 | - | } | ||
250 | - | |||
251 | 553 | 10,11 | if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 && | |
252 | ##### | 11 | (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) { | |
253 | ##### | 12 | git_error_set(GIT_ERROR_INVALID, "updating index from status " | |
254 | - | "is not allowed when index refresh is disabled"); | ||
255 | ##### | 13 | return -1; | |
256 | - | } | ||
257 | - | |||
258 | 553 | 14 | return 0; | |
259 | - | } | ||
260 | - | |||
261 | 665 | 2 | int git_status_list_new( | |
262 | - | git_status_list **out, | ||
263 | - | git_repository *repo, | ||
264 | - | const git_status_options *opts) | ||
265 | - | { | ||
266 | 665 | 2 | git_index *index = NULL; | |
267 | 665 | 2 | git_status_list *status = NULL; | |
268 | 665 | 2 | git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT; | |
269 | 665 | 2 | git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT; | |
270 | 665 | 2 | git_tree *head = NULL; | |
271 | 665 | 5 | git_status_show_t show = | |
272 | 665 | 2-4 | opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; | |
273 | 665 | 5 | int error = 0; | |
274 | 665 | 5-7 | unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS; | |
275 | - | |||
276 | 665 | 8 | *out = NULL; | |
277 | - | |||
278 | 665 | 8,9 | if (status_validate_options(opts) < 0) | |
279 | ##### | 10 | return -1; | |
280 | - | |||
281 | 665 | 11-14 | if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 || | |
282 | - | (error = git_repository_index(&index, repo)) < 0) | ||
283 | 1 | 15 | return error; | |
284 | - | |||
285 | 664 | 16,17 | if (opts != NULL && opts->baseline != NULL) { | |
286 | 1 | 18 | head = opts->baseline; | |
287 | - | } else { | ||
288 | - | /* if there is no HEAD, that's okay - we'll make an empty iterator */ | ||
289 | 663 | 19,20 | if ((error = git_repository_head_tree(&head, repo)) < 0) { | |
290 | 95 | 21,22 | if (error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH) | |
291 | ##### | 23 | goto done; | |
292 | 95 | 24 | git_error_clear(); | |
293 | - | } | ||
294 | - | } | ||
295 | - | |||
296 | - | /* refresh index from disk unless prevented */ | ||
297 | 664 | 25,27 | if ((flags & GIT_STATUS_OPT_NO_REFRESH) == 0 && | |
298 | 664 | 26 | git_index_read_safely(index) < 0) | |
299 | ##### | 28 | git_error_clear(); | |
300 | - | |||
301 | 664 | 29 | status = git_status_list_alloc(index); | |
302 | 664 | 30,31 | GIT_ERROR_CHECK_ALLOC(status); | |
303 | - | |||
304 | 664 | 32 | if (opts) { | |
305 | 552 | 33 | memcpy(&status->opts, opts, sizeof(git_status_options)); | |
306 | 552 | 33 | memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); | |
307 | - | } | ||
308 | - | |||
309 | 664 | 34 | diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE; | |
310 | 664 | 34 | findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED; | |
311 | - | |||
312 | 664 | 34 | if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) | |
313 | 636 | 35 | diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED; | |
314 | 664 | 36 | if ((flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0) | |
315 | 565 | 37 | diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED; | |
316 | 664 | 38 | if ((flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0) | |
317 | 419 | 39 | diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED; | |
318 | 664 | 40 | if ((flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0) | |
319 | 620 | 41 | diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS; | |
320 | 664 | 42 | if ((flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0) | |
321 | 423 | 43 | diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH; | |
322 | 664 | 44 | if ((flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0) | |
323 | 424 | 45 | diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS; | |
324 | 664 | 46 | if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) | |
325 | 69 | 47 | diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES; | |
326 | 664 | 48 | if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) | |
327 | 2 | 49 | diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX; | |
328 | 664 | 50 | if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0) | |
329 | 3 | 51 | diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE; | |
330 | 664 | 52 | if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0) | |
331 | 2 | 53 | diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED; | |
332 | - | |||
333 | 664 | 54 | if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0) | |
334 | 6 | 55,55 | findopt.flags = findopt.flags | | |
335 | - | GIT_DIFF_FIND_AND_BREAK_REWRITES | | ||
336 | 6 | 55 | GIT_DIFF_FIND_RENAMES_FROM_REWRITES | | |
337 | - | GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY; | ||
338 | - | |||
339 | 664 | 56 | if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) { | |
340 | 658 | 57,58 | if ((error = git_diff_tree_to_index( | |
341 | - | &status->head2idx, repo, head, index, &diffopt)) < 0) | ||
342 | ##### | 59 | goto done; | |
343 | - | |||
344 | 658 | 60-62 | if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 && | |
345 | 12 | 61 | (error = git_diff_find_similar(status->head2idx, &findopt)) < 0) | |
346 | ##### | 63 | goto done; | |
347 | - | } | ||
348 | - | |||
349 | 664 | 64 | if (show != GIT_STATUS_SHOW_INDEX_ONLY) { | |
350 | 662 | 65,66 | if ((error = git_diff_index_to_workdir( | |
351 | - | &status->idx2wd, repo, index, &diffopt)) < 0) { | ||
352 | ##### | 67 | goto done; | |
353 | - | } | ||
354 | - | |||
355 | 662 | 68-70 | if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && | |
356 | 11 | 69 | (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0) | |
357 | ##### | 71 | goto done; | |
358 | - | } | ||
359 | - | |||
360 | 664 | 72 | error = git_diff__paired_foreach( | |
361 | - | status->head2idx, status->idx2wd, status_collect, status); | ||
362 | 664 | 73 | if (error < 0) | |
363 | ##### | 74 | goto done; | |
364 | - | |||
365 | 664 | 75 | if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY) | |
366 | 1 | 76 | git_vector_set_cmp(&status->paired, status_entry_cmp); | |
367 | 664 | 77 | if (flags & GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY) | |
368 | 2 | 78 | git_vector_set_cmp(&status->paired, status_entry_icmp); | |
369 | - | |||
370 | 664 | 79 | if ((flags & | |
371 | - | (GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | | ||
372 | - | GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR | | ||
373 | - | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY | | ||
374 | - | GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)) != 0) | ||
375 | 18 | 80 | git_vector_sort(&status->paired); | |
376 | - | |||
377 | - | done: | ||
378 | 664 | 81 | if (error < 0) { | |
379 | ##### | 82 | git_status_list_free(status); | |
380 | ##### | 83 | status = NULL; | |
381 | - | } | ||
382 | - | |||
383 | 664 | 84 | *out = status; | |
384 | - | |||
385 | 664 | 84,85 | if (opts == NULL || opts->baseline != head) | |
386 | 615 | 86 | git_tree_free(head); | |
387 | 664 | 87 | git_index_free(index); | |
388 | - | |||
389 | 664 | 88 | return error; | |
390 | - | } | ||
391 | - | |||
392 | 72 | 2 | size_t git_status_list_entrycount(git_status_list *status) | |
393 | - | { | ||
394 | 72 | 2,3 | assert(status); | |
395 | - | |||
396 | 72 | 4 | return status->paired.length; | |
397 | - | } | ||
398 | - | |||
399 | 120 | 2 | const git_status_entry *git_status_byindex(git_status_list *status, size_t i) | |
400 | - | { | ||
401 | 120 | 2,3 | assert(status); | |
402 | - | |||
403 | 120 | 4 | return git_vector_get(&status->paired, i); | |
404 | - | } | ||
405 | - | |||
406 | 664 | 2 | void git_status_list_free(git_status_list *status) | |
407 | - | { | ||
408 | 664 | 2 | if (status == NULL) | |
409 | 664 | 3,9 | return; | |
410 | - | |||
411 | 664 | 4 | git_diff_free(status->head2idx); | |
412 | 664 | 5 | git_diff_free(status->idx2wd); | |
413 | - | |||
414 | 664 | 6 | git_vector_free_deep(&status->paired); | |
415 | - | |||
416 | 664 | 7 | git__memzero(status, sizeof(*status)); | |
417 | 664 | 8 | git__free(status); | |
418 | - | } | ||
419 | - | |||
420 | 611 | 2 | int git_status_foreach_ext( | |
421 | - | git_repository *repo, | ||
422 | - | const git_status_options *opts, | ||
423 | - | git_status_cb cb, | ||
424 | - | void *payload) | ||
425 | - | { | ||
426 | - | git_status_list *status; | ||
427 | - | const git_status_entry *status_entry; | ||
428 | - | size_t i; | ||
429 | 611 | 2 | int error = 0; | |
430 | - | |||
431 | 611 | 2,3 | if ((error = git_status_list_new(&status, repo, opts)) < 0) { | |
432 | 1 | 4 | return error; | |
433 | - | } | ||
434 | - | |||
435 | 1513 | 5,12-14 | git_vector_foreach(&status->paired, i, status_entry) { | |
436 | 965 | 6,9 | const char *path = status_entry->head_to_index ? | |
437 | 965 | 6-8 | status_entry->head_to_index->old_file.path : | |
438 | 492 | 8 | status_entry->index_to_workdir->old_file.path; | |
439 | - | |||
440 | 965 | 9,10 | if ((error = cb(path, status_entry->status, payload)) != 0) { | |
441 | 62 | 11 | git_error_set_after_callback(error); | |
442 | 62 | 15 | break; | |
443 | - | } | ||
444 | - | } | ||
445 | - | |||
446 | 610 | 16 | git_status_list_free(status); | |
447 | - | |||
448 | 610 | 17 | return error; | |
449 | - | } | ||
450 | - | |||
451 | 88 | 2 | int git_status_foreach(git_repository *repo, git_status_cb cb, void *payload) | |
452 | - | { | ||
453 | 88 | 2 | return git_status_foreach_ext(repo, NULL, cb, payload); | |
454 | - | } | ||
455 | - | |||
456 | - | struct status_file_info { | ||
457 | - | char *expected; | ||
458 | - | unsigned int count; | ||
459 | - | unsigned int status; | ||
460 | - | int wildmatch_flags; | ||
461 | - | int ambiguous; | ||
462 | - | }; | ||
463 | - | |||
464 | 339 | 2 | static int get_one_status(const char *path, unsigned int status, void *data) | |
465 | - | { | ||
466 | 339 | 2 | struct status_file_info *sfi = data; | |
467 | - | int (*strcomp)(const char *a, const char *b); | ||
468 | - | |||
469 | 339 | 2 | sfi->count++; | |
470 | 339 | 2 | sfi->status = status; | |
471 | - | |||
472 | 339 | 2-4 | strcomp = (sfi->wildmatch_flags & WM_CASEFOLD) ? git__strcasecmp : git__strcmp; | |
473 | - | |||
474 | 339 | 5,7 | if (sfi->count > 1 || | |
475 | 339 | 6,9 | (strcomp(sfi->expected, path) != 0 && | |
476 | 1 | 8 | wildmatch(sfi->expected, path, sfi->wildmatch_flags) != 0)) | |
477 | - | { | ||
478 | 1 | 10 | sfi->ambiguous = true; | |
479 | 1 | 10 | return GIT_EAMBIGUOUS; /* git_error_set will be done by caller */ | |
480 | - | } | ||
481 | - | |||
482 | 338 | 11 | return 0; | |
483 | - | } | ||
484 | - | |||
485 | 414 | 2 | int git_status_file( | |
486 | - | unsigned int *status_flags, | ||
487 | - | git_repository *repo, | ||
488 | - | const char *path) | ||
489 | - | { | ||
490 | - | int error; | ||
491 | 414 | 2 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
492 | 414 | 2 | struct status_file_info sfi = {0}; | |
493 | - | git_index *index; | ||
494 | - | |||
495 | 414 | 2-5 | assert(status_flags && repo && path); | |
496 | - | |||
497 | 414 | 6,7 | if ((error = git_repository_index__weakptr(&index, repo)) < 0) | |
498 | ##### | 8 | return error; | |
499 | - | |||
500 | 414 | 9,10 | if ((sfi.expected = git__strdup(path)) == NULL) | |
501 | ##### | 11 | return -1; | |
502 | 414 | 12 | if (index->ignore_case) | |
503 | 3 | 13 | sfi.wildmatch_flags = WM_CASEFOLD; | |
504 | - | |||
505 | 414 | 14 | opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; | |
506 | 414 | 14 | opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | | |
507 | - | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS | | ||
508 | - | GIT_STATUS_OPT_INCLUDE_UNTRACKED | | ||
509 | - | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | | ||
510 | - | GIT_STATUS_OPT_INCLUDE_UNMODIFIED | | ||
511 | - | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH; | ||
512 | 414 | 14 | opts.pathspec.count = 1; | |
513 | 414 | 14 | opts.pathspec.strings = &sfi.expected; | |
514 | - | |||
515 | 414 | 14 | error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi); | |
516 | - | |||
517 | 414 | 15,16 | if (error < 0 && sfi.ambiguous) { | |
518 | 1 | 17 | git_error_set(GIT_ERROR_INVALID, | |
519 | - | "ambiguous path '%s' given to git_status_file", sfi.expected); | ||
520 | 1 | 18 | error = GIT_EAMBIGUOUS; | |
521 | - | } | ||
522 | - | |||
523 | 414 | 19,20 | if (!error && !sfi.count) { | |
524 | 74 | 21 | git_error_set(GIT_ERROR_INVALID, | |
525 | - | "attempt to get status of nonexistent file '%s'", path); | ||
526 | 74 | 22 | error = GIT_ENOTFOUND; | |
527 | - | } | ||
528 | - | |||
529 | 414 | 23 | *status_flags = sfi.status; | |
530 | - | |||
531 | 414 | 23 | git__free(sfi.expected); | |
532 | - | |||
533 | 414 | 24 | return error; | |
534 | - | } | ||
535 | - | |||
536 | 165 | 2 | int git_status_should_ignore( | |
537 | - | int *ignored, | ||
538 | - | git_repository *repo, | ||
539 | - | const char *path) | ||
540 | - | { | ||
541 | 165 | 2 | return git_ignore_path_is_ignored(ignored, repo, path); | |
542 | - | } | ||
543 | - | |||
544 | 1 | 2 | int git_status_options_init(git_status_options *opts, unsigned int version) | |
545 | - | { | ||
546 | 1 | 2-4 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( | |
547 | - | opts, version, git_status_options, GIT_STATUS_OPTIONS_INIT); | ||
548 | 1 | 5 | return 0; | |
549 | - | } | ||
550 | - | |||
551 | - | #ifndef GIT_DEPRECATE_HARD | ||
552 | ##### | 2 | int git_status_init_options(git_status_options *opts, unsigned int version) | |
553 | - | { | ||
554 | ##### | 2 | return git_status_options_init(opts, version); | |
555 | - | } | ||
556 | - | #endif | ||
557 | - | |||
558 | 3 | 2 | int git_status_list_get_perfdata( | |
559 | - | git_diff_perfdata *out, const git_status_list *status) | ||
560 | - | { | ||
561 | 3 | 2,3 | assert(out); | |
562 | 3 | 4-6 | GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata"); | |
563 | - | |||
564 | 3 | 7 | out->stat_calls = 0; | |
565 | 3 | 7 | out->oid_calculations = 0; | |
566 | - | |||
567 | 3 | 7 | if (status->head2idx) { | |
568 | 3 | 8 | out->stat_calls += status->head2idx->perf.stat_calls; | |
569 | 3 | 8 | out->oid_calculations += status->head2idx->perf.oid_calculations; | |
570 | - | } | ||
571 | 3 | 9 | if (status->idx2wd) { | |
572 | 3 | 10 | out->stat_calls += status->idx2wd->perf.stat_calls; | |
573 | 3 | 10 | out->oid_calculations += status->idx2wd->perf.oid_calculations; | |
574 | - | } | ||
575 | - | |||
576 | 3 | 11 | return 0; | |
577 | - | } | ||
578 | - |