]>
Commit | Line | Data |
---|---|---|
25ec7bca JH |
1 | #include "cache.h" |
2 | #include "commit.h" | |
3 | #include "config.h" | |
4 | #include "revision.h" | |
5 | #include "argv-array.h" | |
6 | #include "list-objects.h" | |
7 | #include "list-objects-filter.h" | |
8 | #include "list-objects-filter-options.h" | |
e987df5f MD |
9 | #include "url.h" |
10 | ||
11 | static int parse_combine_filter( | |
12 | struct list_objects_filter_options *filter_options, | |
13 | const char *arg, | |
14 | struct strbuf *errbuf); | |
25ec7bca JH |
15 | |
16 | /* | |
1dde5fa2 | 17 | * Parse value of the argument to the "filter" keyword. |
25ec7bca JH |
18 | * On the command line this looks like: |
19 | * --filter=<arg> | |
20 | * and in the pack protocol as: | |
21 | * "filter" SP <arg> | |
22 | * | |
23 | * The filter keyword will be used by many commands. | |
24 | * See Documentation/rev-list-options.txt for allowed values for <arg>. | |
25 | * | |
26 | * Capture the given arg as the "filter_spec". This can be forwarded to | |
87c2d9d3 JS |
27 | * subordinate commands when necessary (although it's better to pass it through |
28 | * expand_list_objects_filter_spec() first). We also "intern" the arg for the | |
29 | * convenience of the current command. | |
25ec7bca | 30 | */ |
1e1e39b3 JH |
31 | static int gently_parse_list_objects_filter( |
32 | struct list_objects_filter_options *filter_options, | |
33 | const char *arg, | |
34 | struct strbuf *errbuf) | |
25ec7bca JH |
35 | { |
36 | const char *v0; | |
37 | ||
1e1e39b3 | 38 | if (filter_options->choice) { |
842b0051 MD |
39 | strbuf_addstr( |
40 | errbuf, _("multiple filter-specs cannot be combined")); | |
1e1e39b3 JH |
41 | return 1; |
42 | } | |
25ec7bca | 43 | |
25ec7bca JH |
44 | if (!strcmp(arg, "blob:none")) { |
45 | filter_options->choice = LOFC_BLOB_NONE; | |
46 | return 0; | |
25ec7bca | 47 | |
1e1e39b3 JH |
48 | } else if (skip_prefix(arg, "blob:limit=", &v0)) { |
49 | if (git_parse_ulong(v0, &filter_options->blob_limit_value)) { | |
50 | filter_options->choice = LOFC_BLOB_LIMIT; | |
51 | return 0; | |
52 | } | |
25ec7bca | 53 | |
bc5975d2 | 54 | } else if (skip_prefix(arg, "tree:", &v0)) { |
c813a7c3 | 55 | if (!git_parse_ulong(v0, &filter_options->tree_exclude_depth)) { |
842b0051 | 56 | strbuf_addstr(errbuf, _("expected 'tree:<depth>'")); |
bc5975d2 MD |
57 | return 1; |
58 | } | |
c813a7c3 | 59 | filter_options->choice = LOFC_TREE_DEPTH; |
bc5975d2 MD |
60 | return 0; |
61 | ||
1e1e39b3 | 62 | } else if (skip_prefix(arg, "sparse:oid=", &v0)) { |
25ec7bca JH |
63 | struct object_context oc; |
64 | struct object_id sparse_oid; | |
65 | ||
66 | /* | |
67 | * Try to parse <oid-expression> into an OID for the current | |
68 | * command, but DO NOT complain if we don't have the blob or | |
69 | * ref locally. | |
70 | */ | |
3a7a698e | 71 | if (!get_oid_with_context(the_repository, v0, GET_OID_BLOB, |
25ec7bca JH |
72 | &sparse_oid, &oc)) |
73 | filter_options->sparse_oid_value = oiddup(&sparse_oid); | |
74 | filter_options->choice = LOFC_SPARSE_OID; | |
75 | return 0; | |
25ec7bca | 76 | |
1e1e39b3 | 77 | } else if (skip_prefix(arg, "sparse:path=", &v0)) { |
e693237e CC |
78 | if (errbuf) { |
79 | strbuf_addstr( | |
80 | errbuf, | |
81 | _("sparse:path filters support has been dropped")); | |
82 | } | |
83 | return 1; | |
e987df5f MD |
84 | |
85 | } else if (skip_prefix(arg, "combine:", &v0)) { | |
86 | return parse_combine_filter(filter_options, v0, errbuf); | |
87 | ||
25ec7bca | 88 | } |
5a59a230 NTND |
89 | /* |
90 | * Please update _git_fetch() in git-completion.bash when you | |
91 | * add new filters | |
92 | */ | |
25ec7bca | 93 | |
842b0051 | 94 | strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg); |
cc0b05a4 | 95 | |
1e1e39b3 JH |
96 | memset(filter_options, 0, sizeof(*filter_options)); |
97 | return 1; | |
98 | } | |
99 | ||
e987df5f MD |
100 | static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?"; |
101 | ||
102 | static int has_reserved_character( | |
103 | struct strbuf *sub_spec, struct strbuf *errbuf) | |
104 | { | |
105 | const char *c = sub_spec->buf; | |
106 | while (*c) { | |
107 | if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) { | |
108 | strbuf_addf( | |
109 | errbuf, | |
110 | _("must escape char in sub-filter-spec: '%c'"), | |
111 | *c); | |
112 | return 1; | |
113 | } | |
114 | c++; | |
115 | } | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
120 | static int parse_combine_subfilter( | |
121 | struct list_objects_filter_options *filter_options, | |
122 | struct strbuf *subspec, | |
123 | struct strbuf *errbuf) | |
124 | { | |
125 | size_t new_index = filter_options->sub_nr++; | |
126 | char *decoded; | |
127 | int result; | |
128 | ||
129 | ALLOC_GROW(filter_options->sub, filter_options->sub_nr, | |
130 | filter_options->sub_alloc); | |
131 | memset(&filter_options->sub[new_index], 0, | |
132 | sizeof(*filter_options->sub)); | |
133 | ||
134 | decoded = url_percent_decode(subspec->buf); | |
135 | ||
136 | result = has_reserved_character(subspec, errbuf) || | |
137 | gently_parse_list_objects_filter( | |
138 | &filter_options->sub[new_index], decoded, errbuf); | |
139 | ||
140 | free(decoded); | |
141 | return result; | |
142 | } | |
143 | ||
144 | static int parse_combine_filter( | |
145 | struct list_objects_filter_options *filter_options, | |
146 | const char *arg, | |
147 | struct strbuf *errbuf) | |
148 | { | |
149 | struct strbuf **subspecs = strbuf_split_str(arg, '+', 0); | |
150 | size_t sub; | |
151 | int result = 0; | |
152 | ||
153 | if (!subspecs[0]) { | |
154 | strbuf_addstr(errbuf, _("expected something after combine:")); | |
155 | result = 1; | |
156 | goto cleanup; | |
157 | } | |
158 | ||
159 | for (sub = 0; subspecs[sub] && !result; sub++) { | |
160 | if (subspecs[sub + 1]) { | |
161 | /* | |
162 | * This is not the last subspec. Remove trailing "+" so | |
163 | * we can parse it. | |
164 | */ | |
165 | size_t last = subspecs[sub]->len - 1; | |
166 | assert(subspecs[sub]->buf[last] == '+'); | |
167 | strbuf_remove(subspecs[sub], last, 1); | |
168 | } | |
169 | result = parse_combine_subfilter( | |
170 | filter_options, subspecs[sub], errbuf); | |
171 | } | |
172 | ||
173 | filter_options->choice = LOFC_COMBINE; | |
174 | ||
175 | cleanup: | |
176 | strbuf_list_free(subspecs); | |
177 | if (result) { | |
178 | list_objects_filter_release(filter_options); | |
179 | memset(filter_options, 0, sizeof(*filter_options)); | |
180 | } | |
181 | return result; | |
182 | } | |
183 | ||
1e1e39b3 JH |
184 | int parse_list_objects_filter(struct list_objects_filter_options *filter_options, |
185 | const char *arg) | |
186 | { | |
187 | struct strbuf buf = STRBUF_INIT; | |
e987df5f | 188 | filter_options->filter_spec = strdup(arg); |
1e1e39b3 JH |
189 | if (gently_parse_list_objects_filter(filter_options, arg, &buf)) |
190 | die("%s", buf.buf); | |
25ec7bca JH |
191 | return 0; |
192 | } | |
193 | ||
194 | int opt_parse_list_objects_filter(const struct option *opt, | |
195 | const char *arg, int unset) | |
196 | { | |
197 | struct list_objects_filter_options *filter_options = opt->value; | |
198 | ||
4875c979 | 199 | if (unset || !arg) { |
aa57b871 | 200 | list_objects_filter_set_no_filter(filter_options); |
4875c979 JH |
201 | return 0; |
202 | } | |
25ec7bca JH |
203 | |
204 | return parse_list_objects_filter(filter_options, arg); | |
205 | } | |
4875c979 | 206 | |
87c2d9d3 JS |
207 | void expand_list_objects_filter_spec( |
208 | const struct list_objects_filter_options *filter, | |
209 | struct strbuf *expanded_spec) | |
210 | { | |
211 | strbuf_init(expanded_spec, strlen(filter->filter_spec)); | |
212 | if (filter->choice == LOFC_BLOB_LIMIT) | |
213 | strbuf_addf(expanded_spec, "blob:limit=%lu", | |
214 | filter->blob_limit_value); | |
215 | else if (filter->choice == LOFC_TREE_DEPTH) | |
216 | strbuf_addf(expanded_spec, "tree:%lu", | |
217 | filter->tree_exclude_depth); | |
218 | else | |
219 | strbuf_addstr(expanded_spec, filter->filter_spec); | |
220 | } | |
221 | ||
4875c979 JH |
222 | void list_objects_filter_release( |
223 | struct list_objects_filter_options *filter_options) | |
224 | { | |
e987df5f MD |
225 | size_t sub; |
226 | ||
227 | if (!filter_options) | |
228 | return; | |
4875c979 JH |
229 | free(filter_options->filter_spec); |
230 | free(filter_options->sparse_oid_value); | |
e987df5f MD |
231 | for (sub = 0; sub < filter_options->sub_nr; sub++) |
232 | list_objects_filter_release(&filter_options->sub[sub]); | |
233 | free(filter_options->sub); | |
4875c979 JH |
234 | memset(filter_options, 0, sizeof(*filter_options)); |
235 | } | |
1e1e39b3 JH |
236 | |
237 | void partial_clone_register( | |
238 | const char *remote, | |
239 | const struct list_objects_filter_options *filter_options) | |
240 | { | |
241 | /* | |
242 | * Record the name of the partial clone remote in the | |
243 | * config and in the global variable -- the latter is | |
244 | * used throughout to indicate that partial clone is | |
245 | * enabled and to expect missing objects. | |
246 | */ | |
247 | if (repository_format_partial_clone && | |
248 | *repository_format_partial_clone && | |
249 | strcmp(remote, repository_format_partial_clone)) | |
250 | die(_("cannot change partial clone promisor remote")); | |
251 | ||
252 | git_config_set("core.repositoryformatversion", "1"); | |
253 | git_config_set("extensions.partialclone", remote); | |
254 | ||
255 | repository_format_partial_clone = xstrdup(remote); | |
256 | ||
257 | /* | |
258 | * Record the initial filter-spec in the config as | |
259 | * the default for subsequent fetches from this remote. | |
260 | */ | |
261 | core_partial_clone_filter_default = | |
262 | xstrdup(filter_options->filter_spec); | |
263 | git_config_set("core.partialclonefilter", | |
264 | core_partial_clone_filter_default); | |
265 | } | |
266 | ||
267 | void partial_clone_get_default_filter_spec( | |
268 | struct list_objects_filter_options *filter_options) | |
269 | { | |
842b0051 MD |
270 | struct strbuf errbuf = STRBUF_INIT; |
271 | ||
1e1e39b3 JH |
272 | /* |
273 | * Parse default value, but silently ignore it if it is invalid. | |
274 | */ | |
cac1137d JT |
275 | if (!core_partial_clone_filter_default) |
276 | return; | |
e987df5f MD |
277 | |
278 | filter_options->filter_spec = strdup(core_partial_clone_filter_default); | |
1e1e39b3 JH |
279 | gently_parse_list_objects_filter(filter_options, |
280 | core_partial_clone_filter_default, | |
842b0051 MD |
281 | &errbuf); |
282 | strbuf_release(&errbuf); | |
1e1e39b3 | 283 | } |