]> git.proxmox.com Git - libgit2.git/blame - src/status.c
Improve error propogation in checkout
[libgit2.git] / src / status.c
CommitLineData
205166d2 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
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
8#include "common.h"
9#include "git2.h"
10#include "fileops.h"
11#include "hash.h"
3af6b34a
JP
12#include "vector.h"
13#include "tree.h"
14#include "git2/status.h"
56453d34 15#include "repository.h"
df743c7d 16#include "ignore.h"
205166d2 17
a48ea31d
RB
18#include "git2/diff.h"
19#include "diff.h"
20
a48ea31d
RB
21static unsigned int index_delta2status(git_delta_t index_status)
22{
23 unsigned int st = GIT_STATUS_CURRENT;
24
25 switch (index_status) {
26 case GIT_DELTA_ADDED:
27 case GIT_DELTA_COPIED:
28 case GIT_DELTA_RENAMED:
29 st = GIT_STATUS_INDEX_NEW;
30 break;
31 case GIT_DELTA_DELETED:
32 st = GIT_STATUS_INDEX_DELETED;
33 break;
34 case GIT_DELTA_MODIFIED:
35 st = GIT_STATUS_INDEX_MODIFIED;
36 break;
37 default:
38 break;
39 }
40
41 return st;
42}
43
44static unsigned int workdir_delta2status(git_delta_t workdir_status)
45{
46 unsigned int st = GIT_STATUS_CURRENT;
47
48 switch (workdir_status) {
49 case GIT_DELTA_ADDED:
50 case GIT_DELTA_COPIED:
51 case GIT_DELTA_RENAMED:
52 case GIT_DELTA_UNTRACKED:
53 st = GIT_STATUS_WT_NEW;
54 break;
55 case GIT_DELTA_DELETED:
56 st = GIT_STATUS_WT_DELETED;
57 break;
58 case GIT_DELTA_MODIFIED:
59 st = GIT_STATUS_WT_MODIFIED;
60 break;
61 case GIT_DELTA_IGNORED:
62 st = GIT_STATUS_IGNORED;
63 break;
64 default:
65 break;
66 }
67
68 return st;
69}
70
71int git_status_foreach_ext(
72 git_repository *repo,
41a82592 73 const git_status_options *opts,
a48ea31d
RB
74 int (*cb)(const char *, unsigned int, void *),
75 void *cbdata)
76{
1db12b00 77 int err = 0, cmp;
a48ea31d
RB
78 git_diff_options diffopt;
79 git_diff_list *idx2head = NULL, *wd2idx = NULL;
80 git_tree *head = NULL;
81 git_status_show_t show =
82 opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
83 git_diff_delta *i2h, *w2i;
b8457baa 84 size_t i, j, i_max, j_max;
ec40b7f9 85 bool ignore_case = false;
a48ea31d
RB
86
87 assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
88
f917481e
RB
89 if ((err = git_repository_head_tree(&head, repo)) < 0)
90 return err;
a48ea31d
RB
91
92 memset(&diffopt, 0, sizeof(diffopt));
4b136a94
RB
93 memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
94
66142ae0
RB
95 if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
96 diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
97 if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
98 diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
99 if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
100 diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
4b136a94
RB
101 if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
102 diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
a1773f9d 103 if ((opts->flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
104 diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
66142ae0 105 /* TODO: support EXCLUDE_SUBMODULES flag */
a48ea31d 106
7e000ab2 107 if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
a48ea31d
RB
108 (err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
109 goto cleanup;
110
111 if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
112 (err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
113 goto cleanup;
114
115 if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
1db12b00
RB
116 for (i = 0; !err && i < idx2head->deltas.length; i++) {
117 i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
5dca2010
RB
118 if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
119 err = GIT_EUSER;
1db12b00 120 }
a48ea31d
RB
121 git_diff_list_free(idx2head);
122 idx2head = NULL;
123 }
124
1db12b00
RB
125 i_max = idx2head ? idx2head->deltas.length : 0;
126 j_max = wd2idx ? wd2idx->deltas.length : 0;
127
ec40b7f9
PK
128 if (idx2head && wd2idx &&
129 (0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) ||
130 0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)))
131 {
132 /* Then use the ignore-case sorter... */
133 ignore_case = true;
134
135 /* and assert that both are ignore-case sorted. If this function
136 * ever needs to support merge joining result sets that are not sorted
137 * by the same function, then it will need to be extended to do a spool
138 * and sort on one of the results before merge joining */
139 assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) &&
140 0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE));
141 }
142
1db12b00
RB
143 for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
144 i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
145 w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
146
ec40b7f9 147 cmp = !w2i ? -1 : !i2h ? 1 : STRCMP_CASESELECT(ignore_case, i2h->old_file.path, w2i->old_file.path);
1db12b00
RB
148
149 if (cmp < 0) {
5dca2010
RB
150 if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
151 err = GIT_EUSER;
1db12b00
RB
152 i++;
153 } else if (cmp > 0) {
5dca2010
RB
154 if (cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata))
155 err = GIT_EUSER;
1db12b00
RB
156 j++;
157 } else {
5dca2010
RB
158 if (cb(i2h->old_file.path, index_delta2status(i2h->status) |
159 workdir_delta2status(w2i->status), cbdata))
160 err = GIT_EUSER;
1db12b00
RB
161 i++; j++;
162 }
163 }
a48ea31d
RB
164
165cleanup:
166 git_tree_free(head);
167 git_diff_list_free(idx2head);
168 git_diff_list_free(wd2idx);
5dca2010 169
f335ecd6
RB
170 if (err == GIT_EUSER)
171 giterr_clear();
172
a48ea31d
RB
173 return err;
174}
175
176int git_status_foreach(
177 git_repository *repo,
178 int (*callback)(const char *, unsigned int, void *),
179 void *payload)
180{
181 git_status_options opts;
182
14a513e0 183 memset(&opts, 0, sizeof(opts));
66142ae0 184 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
a48ea31d 185 opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
c8838ee9
RB
186 GIT_STATUS_OPT_INCLUDE_UNTRACKED |
187 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
a48ea31d
RB
188
189 return git_status_foreach_ext(repo, &opts, callback, payload);
190}
191
41a82592 192struct status_file_info {
5dca2010 193 char *expected;
41a82592
RB
194 unsigned int count;
195 unsigned int status;
5dca2010 196 int ambiguous;
3af6b34a
JP
197};
198
41a82592 199static int get_one_status(const char *path, unsigned int status, void *data)
df743c7d 200{
41a82592 201 struct status_file_info *sfi = data;
df743c7d 202
41a82592
RB
203 sfi->count++;
204 sfi->status = status;
df743c7d 205
ffbc689c 206 if (sfi->count > 1 ||
207 (strcmp(sfi->expected, path) != 0 &&
208 p_fnmatch(sfi->expected, path, 0) != 0)) {
41a82592
RB
209 giterr_set(GITERR_INVALID,
210 "Ambiguous path '%s' given to git_status_file", sfi->expected);
5dca2010 211 sfi->ambiguous = true;
ffbc689c 212 return GIT_EAMBIGUOUS;
0d0fa7c3 213 }
d8b903da 214
41a82592 215 return 0;
d8b903da 216}
217
0d0fa7c3 218int git_status_file(
41a82592
RB
219 unsigned int *status_flags,
220 git_repository *repo,
221 const char *path)
20361b2f 222{
41a82592
RB
223 int error;
224 git_status_options opts;
225 struct status_file_info sfi;
20361b2f 226
56453d34 227 assert(status_flags && repo && path);
228
41a82592
RB
229 memset(&sfi, 0, sizeof(sfi));
230 if ((sfi.expected = git__strdup(path)) == NULL)
722c08af 231 return -1;
20361b2f 232
41a82592
RB
233 memset(&opts, 0, sizeof(opts));
234 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
235 opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
236 GIT_STATUS_OPT_INCLUDE_UNTRACKED |
237 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
238 GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
239 opts.pathspec.count = 1;
240 opts.pathspec.strings = &sfi.expected;
df743c7d 241
41a82592 242 error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
df743c7d 243
5dca2010
RB
244 if (error < 0 && sfi.ambiguous)
245 error = GIT_EAMBIGUOUS;
246
41a82592
RB
247 if (!error && !sfi.count) {
248 giterr_set(GITERR_INVALID,
249 "Attempt to get status of nonexistent file '%s'", path);
250 error = GIT_ENOTFOUND;
df743c7d
RB
251 }
252
41a82592 253 *status_flags = sfi.status;
20361b2f 254
41a82592 255 git__free(sfi.expected);
0d0fa7c3 256
56453d34 257 return error;
20361b2f 258}
d8b903da 259
dc13f1f7 260int git_status_should_ignore(
41a82592
RB
261 int *ignored,
262 git_repository *repo,
263 const char *path)
cfbc880d 264{
2fb4e9b3 265 return git_ignore_path_is_ignored(ignored, repo, path);
cfbc880d
RB
266}
267