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