git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 02/13] list-objects-filter-map: extend oidmap to collect omitted objects

From
Junio C Hamano <gitster@pobox.com>
Date
Oct 25, 2017, 07:10 UTC
Message-ID
<xmqqwp3jhfct.fsf@gitster.mtv.corp.google.com>
In-Reply-To
<20171024185332.57261-3-git@jeffhostetler.com>
Jeff Hostetler <git@jeffhostetler.com> writes:
> From: Jeff Hostetler <jeffhost@microsoft.com>
>
> Create helper class to extend oidmap to collect a list of
> omitted or missing objects during traversal.

The reason why oidmap itself cannot be used is because the code wants to record not just the object name but something else about the object. And attributes that the code may care about we can see in this patch are the object type and the path it found.

Is the plan to extend this set of attributes over time as different "omitter"s are added? Why was "path" chosen as a member of the initial set and how it will be useful (also, what path would we record for tags and commits)?

These "future plans" needs revealed upfront, instead of (or in addition to) "will be used in a later commit". As it is hard to judge if "filter map" is an appropriate name for this thing without knowing _how_ it is envisioned to be used. "filter map" sounds more like a map function that is consulted when we decide if we want to drop the object, but from the looks of the code, it is used more to record what was done to these objects.

Is it really a "map" (i.e. whose primary focus is to find out what an object name is "mapped to" when we get an object name---e.g. we notice an otherwise connected object is missing, and consult this "map" to learn what the type/path is because we want to do X)? Or is it more like a "set of known-to-be-missing object" (i.e. whose primary point is to serve as a set of object names and what a name maps to is primarily for debugging)? These are easier to answer if we know how it will be used.

Show 23 quoted lines
> This will be used in a later commit by the list-object filtering
> code.
>
> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
> ---
> diff --git a/list-objects-filter-map.c b/list-objects-filter-map.c
> new file mode 100644
> index 0000000..7e496b3
> --- /dev/null
> +++ b/list-objects-filter-map.c
> @@ -0,0 +1,63 @@
> +#include "cache.h"
> +#include "list-objects-filter-map.h"
> +
> +int list_objects_filter_map_insert(struct oidmap *map,
> +				   const struct object_id *oid,
> +				   const char *pathname, enum object_type type)
> +{
> +	size_t len, size;
> +	struct list_objects_filter_map_entry *e;
> +
> +	if (oidmap_get(map, oid))
> +		return 1;

It is OK for the existing entry to record a path that is totally different from what the caller has. It is hard to judge without knowing what pathname the callers are expected to call this function with, but I am guessing that it is similar to the path shown in the output from "rev-list --objects"---and if that is the case, it is correct that the same object may be reached at different paths depending on what tree the traversal begins at, so pathname recorded in the map is merely "there is one tree somewhere that has this object at this path".

For that matter, the caller may have a completely different type from the object we saw earlier; not checking and flagging it as a possible error makes me feel somewhat uneasy, but there probably is little you can do at this layer of the code if you noticed such a discrepancy so it may be OK to punt.

Show 12 quoted lines
> +	len = ((pathname && *pathname) ? strlen(pathname) : 0);
> +	size = (offsetof(struct list_objects_filter_map_entry, pathname) + len + 1);
> +	e = xcalloc(1, size);
> +
> +	oidcpy(&e->entry.oid, oid);
> +	e->type = type;
> +	if (pathname && *pathname)
> +		strcpy(e->pathname, pathname);
> +
> +	oidmap_put(map, e);
> +	return 0;
> +}

The return value from the function needs to be documented in the header to help callers. It is not apparent why "we did already have one" and "we now newly added" is interesting to the callers, for example. An obvious alternative implementation of this function would return the pointer to an entry that records the object id (i.e. either the one that was already there, or the one we created because we saw this object for the first time), so that the caller can do something interesting to it---again, because the reason why we want this "filter map" is not explained at this stage, it is hard to tell what that "sometehing interesting" would be.

Show 12 quoted lines
> +static int my_cmp(const void *a, const void *b)
> +{
> +	const struct oidmap_entry *ea, *eb;
> +
> +	ea = *(const struct oidmap_entry **)a;
> +	eb = *(const struct oidmap_entry **)b;
> +
> +	return oidcmp(&ea->oid, &eb->oid);
> +}
> +
> +void list_objects_filter_map_foreach(struct oidmap *map,
> +				     list_objects_filter_map_foreach_cb cb,

Name a typedef of a function as something_fn, not something_cb; something_cb is often the type of a struct to be fed to the callback function. And call such a parameter of type something_fn just fn.

Show 19 quoted lines
> +				     void *cb_data)
> +{
> +	struct hashmap_iter iter;
> +	struct list_objects_filter_map_entry **array;
> +	struct list_objects_filter_map_entry *e;
> +	int k, nr;
> +
> +	nr = hashmap_get_size(&map->map);
> +	if (!nr)
> +		return;
> +
> +	array = xcalloc(nr, sizeof(*e));
> +
> +	k = 0;
> +	hashmap_iter_init(&map->map, &iter);
> +	while ((e = hashmap_iter_next(&iter)))
> +		array[k++] = e;
> +
> +	QSORT(array, nr, my_cmp);

It is entirely unclear why foreach() must return the object names in order.

> +	for (k = 0; k < nr; k++)
> +		cb(k, nr, array[k], cb_data);

Also it is not clear if you wanted to expose the type of the entry to the callback function.

An obvious alternative
	fn(&array[k].entry.oid, cb_data);

would allow you to keep the type of map-entry private to the map, and also the callback does not need to know about k or nr.

I guess you are giving k and nr to allow the callers to do a progress bar? If that is the case, that's fine by me. I still do not understand why we uncondtionally sort, though.

Show 35 quoted lines
> +
> +	free(array);
> +}
> diff --git a/list-objects-filter-map.h b/list-objects-filter-map.h
> new file mode 100644
> index 0000000..794fc81
> --- /dev/null
> +++ b/list-objects-filter-map.h
> @@ -0,0 +1,26 @@
> +#ifndef LIST_OBJECTS_FILTER_MAP_H
> +#define LIST_OBJECTS_FILTER_MAP_H
> +
> +#include "oidmap.h"
> +
> +struct list_objects_filter_map_entry {
> +	struct oidmap_entry entry; /* must be first */
> +	enum object_type type;
> +	char pathname[FLEX_ARRAY];
> +};
> +
> +extern int list_objects_filter_map_insert(
> +	struct oidmap *map,
> +	const struct object_id *oid,
> +	const char *pathname, enum object_type type);
> +
> +typedef void (*list_objects_filter_map_foreach_cb)(
> +	int i, int i_limit,
> +	struct list_objects_filter_map_entry *e, void *cb_data);
> +
> +extern void list_objects_filter_map_foreach(
> +	struct oidmap *map,
> +	list_objects_filter_map_foreach_cb cb,
> +	void *cb_data);
> +
> +#endif /* LIST_OBJECTS_FILTER_MAP_H */
Previous: Jeff HostetlerNext: Jeff Hostetler
Message 3 of 37 in “WIP Partial clone part 1: object filtering”
  1. 00/13 WIP Partial clone part 1: object filteringJeff Hostetler, Oct 24, 2017
  2. 02/13 list-objects-filter-map: extend oidmap to collect omitted objectsJeff Hostetler, Oct 24, 2017
  3. Junio C HamanoOct 25, 2017
  4. Jeff HostetlerOct 25, 2017
  5. Junio C HamanoOct 26, 2017
  6. 06/13 list-objects-filter-sparse: add sparse filterJeff Hostetler, Oct 24, 2017
  7. 08/13 list-objects: add traverse_commit_list_filtered methodJeff Hostetler, Oct 24, 2017
  8. Jonathan TanOct 25, 2017
  9. Jeff HostetlerOct 25, 2017
  10. 11/13 t6112: rev-list object filtering testJeff Hostetler, Oct 24, 2017
  11. 05/13 list-objects-filter-blobs-limit: add large blob filteringJeff Hostetler, Oct 24, 2017
  12. 13/13 t5317: pack-objects object filtering testJeff Hostetler, Oct 24, 2017
  13. 12/13 pack-objects: add list-objects filteringJeff Hostetler, Oct 24, 2017
  14. 10/13 rev-list: add list-objects filtering supportJeff Hostetler, Oct 24, 2017
  15. Jonathan TanOct 25, 2017
  16. Jeff HostetlerOct 25, 2017
  17. 04/13 list-objects-filter-blobs-none: add filter to omit all blobsJeff Hostetler, Oct 24, 2017
  18. 09/13 extension.partialclone: introduce partial clone extensionJeff Hostetler, Oct 24, 2017
  19. 01/13 dir: allow exclusions from blob in addition to fileJeff Hostetler, Oct 24, 2017
  20. Eric SunshineOct 25, 2017
  21. Junio C HamanoOct 25, 2017
  22. Jeff HostetlerOct 25, 2017
  23. Junio C HamanoOct 26, 2017
  24. Jeff HostetlerOct 26, 2017
  25. 07/13 list-objects-filter-options: common argument parsingJeff Hostetler, Oct 24, 2017
  26. Jonathan TanOct 25, 2017
  27. Jeff HostetlerOct 25, 2017
  28. 03/13 list-objects: filter objects in traverse_commit_listJeff Hostetler, Oct 24, 2017
  29. Jonathan TanOct 25, 2017
  30. Jeff HostetlerOct 25, 2017
  31. Jonathan TanOct 25, 2017
  32. Junio C HamanoOct 25, 2017
  33. Jonathan TanOct 25, 2017
  34. Jeff HostetlerOct 25, 2017
  35. Junio C HamanoOct 26, 2017
  36. Junio C HamanoOct 26, 2017
  37. Jonathan NiederOct 30, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.