THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
9.8KB

  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"
  9. #include "promisor-remote.h"
  10. #include "trace.h"
  11. #include "url.h"
  12. static int parse_combine_filter(
  13. struct list_objects_filter_options *filter_options,
  14. const char *arg,
  15. struct strbuf *errbuf);
  16. /*
  17. * Parse value of the argument to the "filter" keyword.
  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
  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.
  30. */
  31. static int gently_parse_list_objects_filter(
  32. struct list_objects_filter_options *filter_options,
  33. const char *arg,
  34. struct strbuf *errbuf)
  35. {
  36. const char *v0;
  37. if (!arg)
  38. return 0;
  39. if (filter_options->choice)
  40. BUG("filter_options already populated");
  41. if (!strcmp(arg, "blob:none")) {
  42. filter_options->choice = LOFC_BLOB_NONE;
  43. return 0;
  44. } else if (skip_prefix(arg, "blob:limit=", &v0)) {
  45. if (git_parse_ulong(v0, &filter_options->blob_limit_value)) {
  46. filter_options->choice = LOFC_BLOB_LIMIT;
  47. return 0;
  48. }
  49. } else if (skip_prefix(arg, "tree:", &v0)) {
  50. if (!git_parse_ulong(v0, &filter_options->tree_exclude_depth)) {
  51. strbuf_addstr(errbuf, _("expected 'tree:<depth>'"));
  52. return 1;
  53. }
  54. filter_options->choice = LOFC_TREE_DEPTH;
  55. return 0;
  56. } else if (skip_prefix(arg, "sparse:oid=", &v0)) {
  57. filter_options->sparse_oid_name = xstrdup(v0);
  58. filter_options->choice = LOFC_SPARSE_OID;
  59. return 0;
  60. } else if (skip_prefix(arg, "sparse:path=", &v0)) {
  61. if (errbuf) {
  62. strbuf_addstr(
  63. errbuf,
  64. _("sparse:path filters support has been dropped"));
  65. }
  66. return 1;
  67. } else if (skip_prefix(arg, "combine:", &v0)) {
  68. return parse_combine_filter(filter_options, v0, errbuf);
  69. }
  70. /*
  71. * Please update _git_fetch() in git-completion.bash when you
  72. * add new filters
  73. */
  74. strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg);
  75. memset(filter_options, 0, sizeof(*filter_options));
  76. return 1;
  77. }
  78. static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?";
  79. static int has_reserved_character(
  80. struct strbuf *sub_spec, struct strbuf *errbuf)
  81. {
  82. const char *c = sub_spec->buf;
  83. while (*c) {
  84. if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) {
  85. strbuf_addf(
  86. errbuf,
  87. _("must escape char in sub-filter-spec: '%c'"),
  88. *c);
  89. return 1;
  90. }
  91. c++;
  92. }
  93. return 0;
  94. }
  95. static int parse_combine_subfilter(
  96. struct list_objects_filter_options *filter_options,
  97. struct strbuf *subspec,
  98. struct strbuf *errbuf)
  99. {
  100. size_t new_index = filter_options->sub_nr;
  101. char *decoded;
  102. int result;
  103. ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1,
  104. filter_options->sub_alloc);
  105. decoded = url_percent_decode(subspec->buf);
  106. result = has_reserved_character(subspec, errbuf) ||
  107. gently_parse_list_objects_filter(
  108. &filter_options->sub[new_index], decoded, errbuf);
  109. free(decoded);
  110. return result;
  111. }
  112. static int parse_combine_filter(
  113. struct list_objects_filter_options *filter_options,
  114. const char *arg,
  115. struct strbuf *errbuf)
  116. {
  117. struct strbuf **subspecs = strbuf_split_str(arg, '+', 0);
  118. size_t sub;
  119. int result = 0;
  120. if (!subspecs[0]) {
  121. strbuf_addstr(errbuf, _("expected something after combine:"));
  122. result = 1;
  123. goto cleanup;
  124. }
  125. for (sub = 0; subspecs[sub] && !result; sub++) {
  126. if (subspecs[sub + 1]) {
  127. /*
  128. * This is not the last subspec. Remove trailing "+" so
  129. * we can parse it.
  130. */
  131. size_t last = subspecs[sub]->len - 1;
  132. assert(subspecs[sub]->buf[last] == '+');
  133. strbuf_remove(subspecs[sub], last, 1);
  134. }
  135. result = parse_combine_subfilter(
  136. filter_options, subspecs[sub], errbuf);
  137. }
  138. filter_options->choice = LOFC_COMBINE;
  139. cleanup:
  140. strbuf_list_free(subspecs);
  141. if (result) {
  142. list_objects_filter_release(filter_options);
  143. memset(filter_options, 0, sizeof(*filter_options));
  144. }
  145. return result;
  146. }
  147. static int allow_unencoded(char ch)
  148. {
  149. if (ch <= ' ' || ch == '%' || ch == '+')
  150. return 0;
  151. return !strchr(RESERVED_NON_WS, ch);
  152. }
  153. static void filter_spec_append_urlencode(
  154. struct list_objects_filter_options *filter, const char *raw)
  155. {
  156. struct strbuf buf = STRBUF_INIT;
  157. strbuf_addstr_urlencode(&buf, raw, allow_unencoded);
  158. trace_printf("Add to combine filter-spec: %s\n", buf.buf);
  159. string_list_append(&filter->filter_spec, strbuf_detach(&buf, NULL));
  160. }
  161. /*
  162. * Changes filter_options into an equivalent LOFC_COMBINE filter options
  163. * instance. Does not do anything if filter_options is already LOFC_COMBINE.
  164. */
  165. static void transform_to_combine_type(
  166. struct list_objects_filter_options *filter_options)
  167. {
  168. assert(filter_options->choice);
  169. if (filter_options->choice == LOFC_COMBINE)
  170. return;
  171. {
  172. const int initial_sub_alloc = 2;
  173. struct list_objects_filter_options *sub_array =
  174. xcalloc(initial_sub_alloc, sizeof(*sub_array));
  175. sub_array[0] = *filter_options;
  176. memset(filter_options, 0, sizeof(*filter_options));
  177. filter_options->sub = sub_array;
  178. filter_options->sub_alloc = initial_sub_alloc;
  179. }
  180. filter_options->sub_nr = 1;
  181. filter_options->choice = LOFC_COMBINE;
  182. string_list_append(&filter_options->filter_spec, xstrdup("combine:"));
  183. filter_spec_append_urlencode(
  184. filter_options,
  185. list_objects_filter_spec(&filter_options->sub[0]));
  186. /*
  187. * We don't need the filter_spec strings for subfilter specs, only the
  188. * top level.
  189. */
  190. string_list_clear(&filter_options->sub[0].filter_spec, /*free_util=*/0);
  191. }
  192. void list_objects_filter_die_if_populated(
  193. struct list_objects_filter_options *filter_options)
  194. {
  195. if (filter_options->choice)
  196. die(_("multiple filter-specs cannot be combined"));
  197. }
  198. void parse_list_objects_filter(
  199. struct list_objects_filter_options *filter_options,
  200. const char *arg)
  201. {
  202. struct strbuf errbuf = STRBUF_INIT;
  203. int parse_error;
  204. if (!filter_options->choice) {
  205. string_list_append(&filter_options->filter_spec, xstrdup(arg));
  206. parse_error = gently_parse_list_objects_filter(
  207. filter_options, arg, &errbuf);
  208. } else {
  209. /*
  210. * Make filter_options an LOFC_COMBINE spec so we can trivially
  211. * add subspecs to it.
  212. */
  213. transform_to_combine_type(filter_options);
  214. string_list_append(&filter_options->filter_spec, xstrdup("+"));
  215. filter_spec_append_urlencode(filter_options, arg);
  216. ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1,
  217. filter_options->sub_alloc);
  218. parse_error = gently_parse_list_objects_filter(
  219. &filter_options->sub[filter_options->sub_nr - 1], arg,
  220. &errbuf);
  221. }
  222. if (parse_error)
  223. die("%s", errbuf.buf);
  224. }
  225. int opt_parse_list_objects_filter(const struct option *opt,
  226. const char *arg, int unset)
  227. {
  228. struct list_objects_filter_options *filter_options = opt->value;
  229. if (unset || !arg)
  230. list_objects_filter_set_no_filter(filter_options);
  231. else
  232. parse_list_objects_filter(filter_options, arg);
  233. return 0;
  234. }
  235. const char *list_objects_filter_spec(struct list_objects_filter_options *filter)
  236. {
  237. if (!filter->filter_spec.nr)
  238. BUG("no filter_spec available for this filter");
  239. if (filter->filter_spec.nr != 1) {
  240. struct strbuf concatted = STRBUF_INIT;
  241. strbuf_add_separated_string_list(
  242. &concatted, "", &filter->filter_spec);
  243. string_list_clear(&filter->filter_spec, /*free_util=*/0);
  244. string_list_append(
  245. &filter->filter_spec, strbuf_detach(&concatted, NULL));
  246. }
  247. return filter->filter_spec.items[0].string;
  248. }
  249. const char *expand_list_objects_filter_spec(
  250. struct list_objects_filter_options *filter)
  251. {
  252. if (filter->choice == LOFC_BLOB_LIMIT) {
  253. struct strbuf expanded_spec = STRBUF_INIT;
  254. strbuf_addf(&expanded_spec, "blob:limit=%lu",
  255. filter->blob_limit_value);
  256. string_list_clear(&filter->filter_spec, /*free_util=*/0);
  257. string_list_append(
  258. &filter->filter_spec,
  259. strbuf_detach(&expanded_spec, NULL));
  260. }
  261. return list_objects_filter_spec(filter);
  262. }
  263. void list_objects_filter_release(
  264. struct list_objects_filter_options *filter_options)
  265. {
  266. size_t sub;
  267. if (!filter_options)
  268. return;
  269. string_list_clear(&filter_options->filter_spec, /*free_util=*/0);
  270. free(filter_options->sparse_oid_name);
  271. for (sub = 0; sub < filter_options->sub_nr; sub++)
  272. list_objects_filter_release(&filter_options->sub[sub]);
  273. free(filter_options->sub);
  274. memset(filter_options, 0, sizeof(*filter_options));
  275. }
  276. void partial_clone_register(
  277. const char *remote,
  278. struct list_objects_filter_options *filter_options)
  279. {
  280. char *cfg_name;
  281. char *filter_name;
  282. /* Check if it is already registered */
  283. if (!promisor_remote_find(remote)) {
  284. git_config_set("core.repositoryformatversion", "1");
  285. /* Add promisor config for the remote */
  286. cfg_name = xstrfmt("remote.%s.promisor", remote);
  287. git_config_set(cfg_name, "true");
  288. free(cfg_name);
  289. }
  290. /*
  291. * Record the initial filter-spec in the config as
  292. * the default for subsequent fetches from this remote.
  293. */
  294. filter_name = xstrfmt("remote.%s.partialclonefilter", remote);
  295. /* NEEDSWORK: 'expand' result leaking??? */
  296. git_config_set(filter_name,
  297. expand_list_objects_filter_spec(filter_options));
  298. free(filter_name);
  299. /* Make sure the config info are reset */
  300. promisor_remote_reinit();
  301. }
  302. void partial_clone_get_default_filter_spec(
  303. struct list_objects_filter_options *filter_options,
  304. const char *remote)
  305. {
  306. struct promisor_remote *promisor = promisor_remote_find(remote);
  307. struct strbuf errbuf = STRBUF_INIT;
  308. /*
  309. * Parse default value, but silently ignore it if it is invalid.
  310. */
  311. if (!promisor)
  312. return;
  313. string_list_append(&filter_options->filter_spec,
  314. promisor->partial_clone_filter);
  315. gently_parse_list_objects_filter(filter_options,
  316. promisor->partial_clone_filter,
  317. &errbuf);
  318. strbuf_release(&errbuf);
  319. }