]>
Commit | Line | Data |
---|---|---|
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 |
21 | static 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 | ||
44 | static 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 | ||
71 | int 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 | |
165 | cleanup: | |
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 | ||
176 | int 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 | 192 | struct 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 | 199 | static 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 | 218 | int 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 | 260 | int 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 |