about summary refs log tree commit diff
path: root/third_party/git/list-objects-filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/git/list-objects-filter.c')
-rw-r--r--third_party/git/list-objects-filter.c382
1 files changed, 95 insertions, 287 deletions
diff --git a/third_party/git/list-objects-filter.c b/third_party/git/list-objects-filter.c
index 1e8d4e763d..36e1f774bc 100644
--- a/third_party/git/list-objects-filter.c
+++ b/third_party/git/list-objects-filter.c
@@ -26,46 +26,11 @@
  */
 #define FILTER_SHOWN_BUT_REVISIT (1<<21)
 
-struct subfilter {
-	struct filter *filter;
-	struct oidset seen;
-	struct oidset omits;
-	struct object_id skip_tree;
-	unsigned is_skipping_tree : 1;
-};
-
-struct filter {
-	enum list_objects_filter_result (*filter_object_fn)(
-		struct repository *r,
-		enum list_objects_filter_situation filter_situation,
-		struct object *obj,
-		const char *pathname,
-		const char *filename,
-		struct oidset *omits,
-		void *filter_data);
-
-	/*
-	 * Optional. If this function is supplied and the filter needs
-	 * to collect omits, then this function is called once before
-	 * free_fn is called.
-	 *
-	 * This is required because the following two conditions hold:
-	 *
-	 *   a. A tree filter can add and remove objects as an object
-	 *      graph is traversed.
-	 *   b. A combine filter's omit set is the union of all its
-	 *      subfilters, which may include tree: filters.
-	 *
-	 * As such, the omits sets must be separate sets, and can only
-	 * be unioned after the traversal is completed.
-	 */
-	void (*finalize_omits_fn)(struct oidset *omits, void *filter_data);
-
-	void (*free_fn)(void *filter_data);
-
-	void *filter_data;
-
-	/* If non-NULL, the filter collects a list of the omitted OIDs here. */
+/*
+ * A filter for list-objects to omit ALL blobs from the traversal.
+ * And to OPTIONALLY collect a list of the omitted OIDs.
+ */
+struct filter_blobs_none_data {
 	struct oidset *omits;
 };
 
@@ -75,9 +40,10 @@ static enum list_objects_filter_result filter_blobs_none(
 	struct object *obj,
 	const char *pathname,
 	const char *filename,
-	struct oidset *omits,
 	void *filter_data_)
 {
+	struct filter_blobs_none_data *filter_data = filter_data_;
+
 	switch (filter_situation) {
 	default:
 		BUG("unknown filter_situation: %d", filter_situation);
@@ -95,18 +61,24 @@ static enum list_objects_filter_result filter_blobs_none(
 		assert(obj->type == OBJ_BLOB);
 		assert((obj->flags & SEEN) == 0);
 
-		if (omits)
-			oidset_insert(omits, &obj->oid);
+		if (filter_data->omits)
+			oidset_insert(filter_data->omits, &obj->oid);
 		return LOFR_MARK_SEEN; /* but not LOFR_DO_SHOW (hard omit) */
 	}
 }
 
-static void filter_blobs_none__init(
+static void *filter_blobs_none__init(
+	struct oidset *omitted,
 	struct list_objects_filter_options *filter_options,
-	struct filter *filter)
+	filter_object_fn *filter_fn,
+	filter_free_fn *filter_free_fn)
 {
-	filter->filter_object_fn = filter_blobs_none;
-	filter->free_fn = free;
+	struct filter_blobs_none_data *d = xcalloc(1, sizeof(*d));
+	d->omits = omitted;
+
+	*filter_fn = filter_blobs_none;
+	*filter_free_fn = free;
+	return d;
 }
 
 /*
@@ -114,6 +86,8 @@ static void filter_blobs_none__init(
  * Can OPTIONALLY collect a list of the omitted OIDs.
  */
 struct filter_trees_depth_data {
+	struct oidset *omits;
+
 	/*
 	 * Maps trees to the minimum depth at which they were seen. It is not
 	 * necessary to re-traverse a tree at deeper or equal depths than it has
@@ -136,16 +110,16 @@ struct seen_map_entry {
 /* Returns 1 if the oid was in the omits set before it was invoked. */
 static int filter_trees_update_omits(
 	struct object *obj,
-	struct oidset *omits,
+	struct filter_trees_depth_data *filter_data,
 	int include_it)
 {
-	if (!omits)
+	if (!filter_data->omits)
 		return 0;
 
 	if (include_it)
-		return oidset_remove(omits, &obj->oid);
+		return oidset_remove(filter_data->omits, &obj->oid);
 	else
-		return oidset_insert(omits, &obj->oid);
+		return oidset_insert(filter_data->omits, &obj->oid);
 }
 
 static enum list_objects_filter_result filter_trees_depth(
@@ -154,7 +128,6 @@ static enum list_objects_filter_result filter_trees_depth(
 	struct object *obj,
 	const char *pathname,
 	const char *filename,
-	struct oidset *omits,
 	void *filter_data_)
 {
 	struct filter_trees_depth_data *filter_data = filter_data_;
@@ -179,7 +152,7 @@ static enum list_objects_filter_result filter_trees_depth(
 		return LOFR_ZERO;
 
 	case LOFS_BLOB:
-		filter_trees_update_omits(obj, omits, include_it);
+		filter_trees_update_omits(obj, filter_data, include_it);
 		return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO;
 
 	case LOFS_BEGIN_TREE:
@@ -200,12 +173,12 @@ static enum list_objects_filter_result filter_trees_depth(
 			filter_res = LOFR_SKIP_TREE;
 		} else {
 			int been_omitted = filter_trees_update_omits(
-				obj, omits, include_it);
+				obj, filter_data, include_it);
 			seen_info->depth = filter_data->current_depth;
 
 			if (include_it)
 				filter_res = LOFR_DO_SHOW;
-			else if (omits && !been_omitted)
+			else if (filter_data->omits && !been_omitted)
 				/*
 				 * Must update omit information of children
 				 * recursively; they have not been omitted yet.
@@ -228,18 +201,21 @@ static void filter_trees_free(void *filter_data) {
 	free(d);
 }
 
-static void filter_trees_depth__init(
+static void *filter_trees_depth__init(
+	struct oidset *omitted,
 	struct list_objects_filter_options *filter_options,
-	struct filter *filter)
+	filter_object_fn *filter_fn,
+	filter_free_fn *filter_free_fn)
 {
 	struct filter_trees_depth_data *d = xcalloc(1, sizeof(*d));
+	d->omits = omitted;
 	oidmap_init(&d->seen_at_depth, 0);
 	d->exclude_depth = filter_options->tree_exclude_depth;
 	d->current_depth = 0;
 
-	filter->filter_data = d;
-	filter->filter_object_fn = filter_trees_depth;
-	filter->free_fn = filter_trees_free;
+	*filter_fn = filter_trees_depth;
+	*filter_free_fn = filter_trees_free;
+	return d;
 }
 
 /*
@@ -247,6 +223,7 @@ static void filter_trees_depth__init(
  * And to OPTIONALLY collect a list of the omitted OIDs.
  */
 struct filter_blobs_limit_data {
+	struct oidset *omits;
 	unsigned long max_bytes;
 };
 
@@ -256,7 +233,6 @@ static enum list_objects_filter_result filter_blobs_limit(
 	struct object *obj,
 	const char *pathname,
 	const char *filename,
-	struct oidset *omits,
 	void *filter_data_)
 {
 	struct filter_blobs_limit_data *filter_data = filter_data_;
@@ -294,27 +270,30 @@ static enum list_objects_filter_result filter_blobs_limit(
 		if (object_length < filter_data->max_bytes)
 			goto include_it;
 
-		if (omits)
-			oidset_insert(omits, &obj->oid);
+		if (filter_data->omits)
+			oidset_insert(filter_data->omits, &obj->oid);
 		return LOFR_MARK_SEEN; /* but not LOFR_DO_SHOW (hard omit) */
 	}
 
 include_it:
-	if (omits)
-		oidset_remove(omits, &obj->oid);
+	if (filter_data->omits)
+		oidset_remove(filter_data->omits, &obj->oid);
 	return LOFR_MARK_SEEN | LOFR_DO_SHOW;
 }
 
-static void filter_blobs_limit__init(
+static void *filter_blobs_limit__init(
+	struct oidset *omitted,
 	struct list_objects_filter_options *filter_options,
-	struct filter *filter)
+	filter_object_fn *filter_fn,
+	filter_free_fn *filter_free_fn)
 {
 	struct filter_blobs_limit_data *d = xcalloc(1, sizeof(*d));
+	d->omits = omitted;
 	d->max_bytes = filter_options->blob_limit_value;
 
-	filter->filter_data = d;
-	filter->filter_object_fn = filter_blobs_limit;
-	filter->free_fn = free;
+	*filter_fn = filter_blobs_limit;
+	*filter_free_fn = free;
+	return d;
 }
 
 /*
@@ -328,12 +307,12 @@ static void filter_blobs_limit__init(
  */
 struct frame {
 	/*
-	 * default_match is the usual default include/exclude value that
+	 * defval is the usual default include/exclude value that
 	 * should be inherited as we recurse into directories based
 	 * upon pattern matching of the directory itself or of a
 	 * containing directory.
 	 */
-	enum pattern_match_result default_match;
+	int defval;
 
 	/*
 	 * 1 if the directory (recursively) contains any provisionally
@@ -347,7 +326,8 @@ struct frame {
 };
 
 struct filter_sparse_data {
-	struct pattern_list pl;
+	struct oidset *omits;
+	struct exclude_list el;
 
 	size_t nr, alloc;
 	struct frame *array_frame;
@@ -359,13 +339,11 @@ static enum list_objects_filter_result filter_sparse(
 	struct object *obj,
 	const char *pathname,
 	const char *filename,
-	struct oidset *omits,
 	void *filter_data_)
 {
 	struct filter_sparse_data *filter_data = filter_data_;
-	int dtype;
+	int val, dtype;
 	struct frame *frame;
-	enum pattern_match_result match;
 
 	switch (filter_situation) {
 	default:
@@ -374,15 +352,15 @@ static enum list_objects_filter_result filter_sparse(
 	case LOFS_BEGIN_TREE:
 		assert(obj->type == OBJ_TREE);
 		dtype = DT_DIR;
-		match = path_matches_pattern_list(pathname, strlen(pathname),
-						  filename, &dtype, &filter_data->pl,
-						  r->index);
-		if (match == UNDECIDED)
-			match = filter_data->array_frame[filter_data->nr - 1].default_match;
+		val = is_excluded_from_list(pathname, strlen(pathname),
+					    filename, &dtype, &filter_data->el,
+					    r->index);
+		if (val < 0)
+			val = filter_data->array_frame[filter_data->nr - 1].defval;
 
 		ALLOC_GROW(filter_data->array_frame, filter_data->nr + 1,
 			   filter_data->alloc);
-		filter_data->array_frame[filter_data->nr].default_match = match;
+		filter_data->array_frame[filter_data->nr].defval = val;
 		filter_data->array_frame[filter_data->nr].child_prov_omit = 0;
 		filter_data->nr++;
 
@@ -436,14 +414,14 @@ static enum list_objects_filter_result filter_sparse(
 		frame = &filter_data->array_frame[filter_data->nr - 1];
 
 		dtype = DT_REG;
-		match = path_matches_pattern_list(pathname, strlen(pathname),
-					    filename, &dtype, &filter_data->pl,
+		val = is_excluded_from_list(pathname, strlen(pathname),
+					    filename, &dtype, &filter_data->el,
 					    r->index);
-		if (match == UNDECIDED)
-			match = frame->default_match;
-		if (match == MATCHED) {
-			if (omits)
-				oidset_remove(omits, &obj->oid);
+		if (val < 0)
+			val = frame->defval;
+		if (val > 0) {
+			if (filter_data->omits)
+				oidset_remove(filter_data->omits, &obj->oid);
 			return LOFR_MARK_SEEN | LOFR_DO_SHOW;
 		}
 
@@ -457,8 +435,8 @@ static enum list_objects_filter_result filter_sparse(
 		 * Leave the LOFR_ bits unset so that if the blob appears
 		 * again in the traversal, we will be asked again.
 		 */
-		if (omits)
-			oidset_insert(omits, &obj->oid);
+		if (filter_data->omits)
+			oidset_insert(filter_data->omits, &obj->oid);
 
 		/*
 		 * Remember that at least 1 blob in this tree was
@@ -478,169 +456,33 @@ static void filter_sparse_free(void *filter_data)
 	free(d);
 }
 
-static void filter_sparse_oid__init(
+static void *filter_sparse_oid__init(
+	struct oidset *omitted,
 	struct list_objects_filter_options *filter_options,
-	struct filter *filter)
+	filter_object_fn *filter_fn,
+	filter_free_fn *filter_free_fn)
 {
 	struct filter_sparse_data *d = xcalloc(1, sizeof(*d));
-	struct object_context oc;
-	struct object_id sparse_oid;
-
-	if (get_oid_with_context(the_repository,
-				 filter_options->sparse_oid_name,
-				 GET_OID_BLOB, &sparse_oid, &oc))
-		die(_("unable to access sparse blob in '%s'"),
-		    filter_options->sparse_oid_name);
-	if (add_patterns_from_blob_to_list(&sparse_oid, "", 0, &d->pl) < 0)
-		die(_("unable to parse sparse filter data in %s"),
-		    oid_to_hex(&sparse_oid));
+	d->omits = omitted;
+	if (add_excludes_from_blob_to_list(filter_options->sparse_oid_value,
+					   NULL, 0, &d->el) < 0)
+		die("could not load filter specification");
 
 	ALLOC_GROW(d->array_frame, d->nr + 1, d->alloc);
-	d->array_frame[d->nr].default_match = 0; /* default to include */
+	d->array_frame[d->nr].defval = 0; /* default to include */
 	d->array_frame[d->nr].child_prov_omit = 0;
 	d->nr++;
 
-	filter->filter_data = d;
-	filter->filter_object_fn = filter_sparse;
-	filter->free_fn = filter_sparse_free;
-}
-
-/* A filter which only shows objects shown by all sub-filters. */
-struct combine_filter_data {
-	struct subfilter *sub;
-	size_t nr;
-};
-
-static enum list_objects_filter_result process_subfilter(
-	struct repository *r,
-	enum list_objects_filter_situation filter_situation,
-	struct object *obj,
-	const char *pathname,
-	const char *filename,
-	struct subfilter *sub)
-{
-	enum list_objects_filter_result result;
-
-	/*
-	 * Check and update is_skipping_tree before oidset_contains so
-	 * that is_skipping_tree gets unset even when the object is
-	 * marked as seen.  As of this writing, no filter uses
-	 * LOFR_MARK_SEEN on trees that also uses LOFR_SKIP_TREE, so the
-	 * ordering is only theoretically important. Be cautious if you
-	 * change the order of the below checks and more filters have
-	 * been added!
-	 */
-	if (sub->is_skipping_tree) {
-		if (filter_situation == LOFS_END_TREE &&
-		    oideq(&obj->oid, &sub->skip_tree))
-			sub->is_skipping_tree = 0;
-		else
-			return LOFR_ZERO;
-	}
-	if (oidset_contains(&sub->seen, &obj->oid))
-		return LOFR_ZERO;
-
-	result = list_objects_filter__filter_object(
-		r, filter_situation, obj, pathname, filename, sub->filter);
-
-	if (result & LOFR_MARK_SEEN)
-		oidset_insert(&sub->seen, &obj->oid);
-
-	if (result & LOFR_SKIP_TREE) {
-		sub->is_skipping_tree = 1;
-		sub->skip_tree = obj->oid;
-	}
-
-	return result;
+	*filter_fn = filter_sparse;
+	*filter_free_fn = filter_sparse_free;
+	return d;
 }
 
-static enum list_objects_filter_result filter_combine(
-	struct repository *r,
-	enum list_objects_filter_situation filter_situation,
-	struct object *obj,
-	const char *pathname,
-	const char *filename,
-	struct oidset *omits,
-	void *filter_data)
-{
-	struct combine_filter_data *d = filter_data;
-	enum list_objects_filter_result combined_result =
-		LOFR_DO_SHOW | LOFR_MARK_SEEN | LOFR_SKIP_TREE;
-	size_t sub;
-
-	for (sub = 0; sub < d->nr; sub++) {
-		enum list_objects_filter_result sub_result = process_subfilter(
-			r, filter_situation, obj, pathname, filename,
-			&d->sub[sub]);
-		if (!(sub_result & LOFR_DO_SHOW))
-			combined_result &= ~LOFR_DO_SHOW;
-		if (!(sub_result & LOFR_MARK_SEEN))
-			combined_result &= ~LOFR_MARK_SEEN;
-		if (!d->sub[sub].is_skipping_tree)
-			combined_result &= ~LOFR_SKIP_TREE;
-	}
-
-	return combined_result;
-}
-
-static void filter_combine__free(void *filter_data)
-{
-	struct combine_filter_data *d = filter_data;
-	size_t sub;
-	for (sub = 0; sub < d->nr; sub++) {
-		list_objects_filter__free(d->sub[sub].filter);
-		oidset_clear(&d->sub[sub].seen);
-		if (d->sub[sub].omits.set.size)
-			BUG("expected oidset to be cleared already");
-	}
-	free(d->sub);
-}
-
-static void add_all(struct oidset *dest, struct oidset *src) {
-	struct oidset_iter iter;
-	struct object_id *src_oid;
-
-	oidset_iter_init(src, &iter);
-	while ((src_oid = oidset_iter_next(&iter)) != NULL)
-		oidset_insert(dest, src_oid);
-}
-
-static void filter_combine__finalize_omits(
-	struct oidset *omits,
-	void *filter_data)
-{
-	struct combine_filter_data *d = filter_data;
-	size_t sub;
-
-	for (sub = 0; sub < d->nr; sub++) {
-		add_all(omits, &d->sub[sub].omits);
-		oidset_clear(&d->sub[sub].omits);
-	}
-}
-
-static void filter_combine__init(
-	struct list_objects_filter_options *filter_options,
-	struct filter* filter)
-{
-	struct combine_filter_data *d = xcalloc(1, sizeof(*d));
-	size_t sub;
-
-	d->nr = filter_options->sub_nr;
-	d->sub = xcalloc(d->nr, sizeof(*d->sub));
-	for (sub = 0; sub < d->nr; sub++)
-		d->sub[sub].filter = list_objects_filter__init(
-			filter->omits ? &d->sub[sub].omits : NULL,
-			&filter_options->sub[sub]);
-
-	filter->filter_data = d;
-	filter->filter_object_fn = filter_combine;
-	filter->free_fn = filter_combine__free;
-	filter->finalize_omits_fn = filter_combine__finalize_omits;
-}
-
-typedef void (*filter_init_fn)(
+typedef void *(*filter_init_fn)(
+	struct oidset *omitted,
 	struct list_objects_filter_options *filter_options,
-	struct filter *filter);
+	filter_object_fn *filter_fn,
+	filter_free_fn *filter_free_fn);
 
 /*
  * Must match "enum list_objects_filter_choice".
@@ -651,14 +493,14 @@ static filter_init_fn s_filters[] = {
 	filter_blobs_limit__init,
 	filter_trees_depth__init,
 	filter_sparse_oid__init,
-	filter_combine__init,
 };
 
-struct filter *list_objects_filter__init(
+void *list_objects_filter__init(
 	struct oidset *omitted,
-	struct list_objects_filter_options *filter_options)
+	struct list_objects_filter_options *filter_options,
+	filter_object_fn *filter_fn,
+	filter_free_fn *filter_free_fn)
 {
-	struct filter *filter;
 	filter_init_fn init_fn;
 
 	assert((sizeof(s_filters) / sizeof(s_filters[0])) == LOFC__COUNT);
@@ -668,44 +510,10 @@ struct filter *list_objects_filter__init(
 		    filter_options->choice);
 
 	init_fn = s_filters[filter_options->choice];
-	if (!init_fn)
-		return NULL;
-
-	filter = xcalloc(1, sizeof(*filter));
-	filter->omits = omitted;
-	init_fn(filter_options, filter);
-	return filter;
-}
-
-enum list_objects_filter_result list_objects_filter__filter_object(
-	struct repository *r,
-	enum list_objects_filter_situation filter_situation,
-	struct object *obj,
-	const char *pathname,
-	const char *filename,
-	struct filter *filter)
-{
-	if (filter && (obj->flags & NOT_USER_GIVEN))
-		return filter->filter_object_fn(r, filter_situation, obj,
-						pathname, filename,
-						filter->omits,
-						filter->filter_data);
-	/*
-	 * No filter is active or user gave object explicitly. In this case,
-	 * always show the object (except when LOFS_END_TREE, since this tree
-	 * had already been shown when LOFS_BEGIN_TREE).
-	 */
-	if (filter_situation == LOFS_END_TREE)
-		return 0;
-	return LOFR_MARK_SEEN | LOFR_DO_SHOW;
-}
-
-void list_objects_filter__free(struct filter *filter)
-{
-	if (!filter)
-		return;
-	if (filter->finalize_omits_fn && filter->omits)
-		filter->finalize_omits_fn(filter->omits, filter->filter_data);
-	filter->free_fn(filter->filter_data);
-	free(filter);
+	if (init_fn)
+		return init_fn(omitted, filter_options,
+			       filter_fn, filter_free_fn);
+	*filter_fn = NULL;
+	*filter_free_fn = NULL;
+	return NULL;
 }