Skip to contentSkip to conversation

list-objects-filter-large: add large blob filter to list-objects

PATCH In review Jeff Hostetler 2 participants 16 messages Sep 22, 2017 – Sep 29, 2017

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 5/13 list-objects-filter-large: add large blob filter to list-objects

From: Jeff Hostetler <[email protected]>

Create a filter for traverse_commit_list_worker() to omit blobs larger than a requested size from the result, but always include ".git*" special files.

---
 Makefile                    |   1 +
 list-objects-filter-large.c | 108 ++++++++++++++++++++++++++++++++++++++++++++
 list-objects-filter-large.h |  18 ++++++++
 3 files changed, 127 insertions(+)
 create mode 100644 list-objects-filter-large.c
 create mode 100644 list-objects-filter-large.h
Makefile +1 -0
index b98e3dc..f1f3979 100644
@@ -799,6 +799,7 @@ LIB_OBJS += line-log.o
 LIB_OBJS += line-range.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += list-objects-filter-all.o
+LIB_OBJS += list-objects-filter-large.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
list-objects-filter-large.c new file +108 -0

108 lines not on this page · raw message

list-objects-filter-large.h new file +18 -0
new file mode 100644
index 0000000..4a5c772
@@ -0,0 +1,18 @@
+#ifndef LIST_OBJECTS_FILTER_LARGE_H
+#define LIST_OBJECTS_FILTER_LARGE_H
+
+#include "oidset2.h"
+
+/*
+ * A filter for list-objects to omit large blobs,
+ * but always include ".git*" special files.
+ */
+void traverse_commit_list_omit_large_blobs(
+	struct rev_info *revs,
+	show_commit_fn show_commit,
+	show_object_fn show_object,
+	oidset2_foreach_cb print_omitted_object,
+	void *ctx_data,
+	int64_t large_byte_limit);
+
+#endif /* LIST_OBJECTS_FILTER_LARGE_H */
Signature · 1 line
2.9.3
#1 lore raw

PATCH 6/13 list-objects-filter-sparse: add sparse-checkout based filter

From: Jeff Hostetler <[email protected]>

Create a filter for traverse_commit_list_worker() to only include the blobs the would be referenced by a sparse-checkout using the given specification.

A future enhancement should be able to also omit unneeded tree objects, but that is not currently supported.

---
 Makefile                     |   1 +
 list-objects-filter-sparse.c | 221 +++++++++++++++++++++++++++++++++++++++++++
 list-objects-filter-sparse.h |  30 ++++++
 3 files changed, 252 insertions(+)
 create mode 100644 list-objects-filter-sparse.c
 create mode 100644 list-objects-filter-sparse.h
Makefile +1 -0
index f1f3979..6e0bd91 100644
@@ -800,6 +800,7 @@ LIB_OBJS += line-range.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += list-objects-filter-all.o
 LIB_OBJS += list-objects-filter-large.o
+LIB_OBJS += list-objects-filter-sparse.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
list-objects-filter-sparse.c new file +221 -0

221 lines not on this page · raw message

list-objects-filter-sparse.h new file +30 -0
new file mode 100644
index 0000000..aa89390
@@ -0,0 +1,30 @@
+#ifndef LIST_OBJECTS_FILTERS_SPARSE_H
+#define LIST_OBJECTS_FILTERS_SPARSE_H
+
+#include "oidset2.h"
+
+/*
+ * A filter driven by a sparse-checkout specification to only
+ * include blobs that a sparse checkout would populate.
+ *
+ * The sparse-checkout spec can be loaded from a blob with the
+ * given OID, a blob with a blob-ish path, or from a local pathname.
+ * We allow an OID because the repo may be bare or we may be doing
+ * the filtering on the server.
+ */
+void traverse_commit_list_use_blob(
+	struct rev_info *revs,
+	show_commit_fn show_commit,
+	show_object_fn show_object,
+	oidset2_foreach_cb print_omitted_object,
+	void *ctx_data,
+	struct object_id *oid);
+void traverse_commit_list_use_path(
+	struct rev_info *revs,
+	show_commit_fn show_commit,
+	show_object_fn show_object,
+	oidset2_foreach_cb print_omitted_object,
+	void *ctx_data,
+	const char *path);
+
+#endif /* LIST_OBJECTS_FILTERS_SPARSE_H */
Signature · 1 line
2.9.3
#2 lore raw

PATCH 7/13 object-filter: common declarations for object filtering

In reply to Jeff Hostetler

From: Jeff Hostetler <[email protected]>

Create common routines and defines for parsing object-filter-related command line arguments and pack-protocol fields.

---
 Makefile        |   1 +
 object-filter.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 object-filter.h | 173 ++++++++++++++++++++++++++++++++++++
 3 files changed, 443 insertions(+)
 create mode 100644 object-filter.c
 create mode 100644 object-filter.h
Makefile +1 -0
index 6e0bd91..bddd6b5 100644
@@ -818,6 +818,7 @@ LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
 LIB_OBJS += object.o
+LIB_OBJS += object-filter.o
 LIB_OBJS += oidset.o
 LIB_OBJS += oidset2.o
 LIB_OBJS += pack-bitmap.o
object-filter.c new file +269 -0

269 lines not on this page · raw message

object-filter.h new file +173 -0

173 lines not on this page · raw message

Signature · 1 line
2.9.3
#3 lore raw

Re: [PATCH 07/13] object-filter: common declarations for object filtering

Quoted 6 lines · On Fri, 22 Sep 2017 20:30:11 +0000 Jeff Hostetler <[email protected]> wrote:
 Makefile        |   1 +
 object-filter.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 object-filter.h | 173 ++++++++++++++++++++++++++++++++++++
 3 files changed, 443 insertions(+)
 create mode 100644 object-filter.c
 create mode 100644 object-filter.h

I think these and list-objects-filter-* are multiple levels of indirection too many. Would a single file with a few implementations of filter_object_fn be sufficient?

#4 lore raw

Re: [PATCH 07/13] object-filter: common declarations for object filtering

Quoted 10 lines · Jonathan Tan
Quoted 6 lines · On Fri, 22 Sep 2017 20:30:11 +0000 Jeff Hostetler <[email protected]> wrote:
  Makefile        |   1 +
  object-filter.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  object-filter.h | 173 ++++++++++++++++++++++++++++++++++++
  3 files changed, 443 insertions(+)

create mode 100644 object-filter.c
create mode 100644 object-filter.h

Quoted 3 lines

I think these and list-objects-filter-* are multiple levels of indirection too many. Would a single file with a few implementations of filter_object_fn be sufficient?

I did that in my first draft and I found it confusing.

Each filter has 3 parts (some filter-specific data structures, a filter callback routine, a driver to call the traverse code). I found it easier to reason about each filter in isolation. And it makes it easier to work on each independently and keep their inclusion in separate commits.

#5 lore raw

Re: [PATCH 07/13] object-filter: common declarations for object filtering

Quoted 15 lines · On Wed, 27 Sep 2017 13:09:42 -0400 Jeff Hostetler <[email protected]> wrote:
Quoted 10 lines · Jonathan Tan
Quoted 6 lines · On Fri, 22 Sep 2017 20:30:11 +0000 Jeff Hostetler <[email protected]> wrote:
  Makefile        |   1 +
  object-filter.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  object-filter.h | 173 ++++++++++++++++++++++++++++++++++++
  3 files changed, 443 insertions(+)

create mode 100644 object-filter.c
create mode 100644 object-filter.h

Quoted 3 lines

I think these and list-objects-filter-* are multiple levels of indirection too many. Would a single file with a few implementations of filter_object_fn be sufficient?

Quoted 7 lines

I did that in my first draft and I found it confusing.

Each filter has 3 parts (some filter-specific data structures, a filter callback routine, a driver to call the traverse code). I found it easier to reason about each filter in isolation. And it makes it easier to work on each independently and keep their inclusion in separate commits.

I looked at object-filter.{c,h} a bit more. It seems that these files:
1) define a struct that contains all the options that we would want
2) supplies a way to populate this struct from code that uses parse-options
3) supplies a way to populate this struct from code that calculates options by hand
4) supplies a way to populate this struct from "protocol" ("<key>" or

    "<key> <value>" strings)

And the next commit takes the struct that object-filter.{c,h} produces and actually performs the traversal.

I think this can be significantly simplified, though. Would this work:
a) Define the object_filter_options struct, but make all fields correspond to a single parameter each. Define OBJECT_FILTER_OPTIONS_INIT to initialize everything to 0 except for large_byte_limit to ULONG_MAX (so that we can detect if something else is set to it).
b) Define one single OPT_PARSE_FILTER macro containing all the parameters. We can use the non-callback macros here. That solves 2) above.
c) Define a function that takes in (int *argc, char ***argv) that can "massage" it to remove all filter-related arguments, storing them in a object_filter_options struct. That solves 3) above. As discussed in the API documentation, this means that argument lists of the form "--unknown --known" (where "--unknown" takes an argument) are processed differently, but then again, rev-list never supported them anyway (it required "--unknown=<arg>").
d) Define a function that converts "<key>" into "--<key>" and "<key> <value>" into "--<key>=<value>", and use the existing mechanism. That solves 4) above.

This removes the need to maintain the lists of one-per-argument functions, including the parse_filter_* and opt_parse_filter_* functions declared in the header file. If we were to add a feature, we wouldn't need to change anything in the caller, and wouldn't need to hand-edit object_filter_hand_parse_arg() and object_filter_hand_parse_protocol().

#6 lore raw

Re: [PATCH 07/13] object-filter: common declarations for object filtering

Quoted 57 lines · Jonathan Tan
Quoted 15 lines · On Wed, 27 Sep 2017 13:09:42 -0400 Jeff Hostetler <[email protected]> wrote:
Quoted 10 lines · Jonathan Tan
Quoted 6 lines · On Fri, 22 Sep 2017 20:30:11 +0000 Jeff Hostetler <[email protected]> wrote:
   Makefile        |   1 +
   object-filter.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   object-filter.h | 173 ++++++++++++++++++++++++++++++++++++
   3 files changed, 443 insertions(+)

create mode 100644 object-filter.c
create mode 100644 object-filter.h

Quoted 3 lines

I think these and list-objects-filter-* are multiple levels of indirection too many. Would a single file with a few implementations of filter_object_fn be sufficient?

Quoted 7 lines

I did that in my first draft and I found it confusing.

Each filter has 3 parts (some filter-specific data structures, a filter callback routine, a driver to call the traverse code). I found it easier to reason about each filter in isolation. And it makes it easier to work on each independently and keep their inclusion in separate commits.

I looked at object-filter.{c,h} a bit more. It seems that these files:
1) define a struct that contains all the options that we would want
2) supplies a way to populate this struct from code that uses parse-options
3) supplies a way to populate this struct from code that calculates options by hand
4) supplies a way to populate this struct from "protocol" ("<key>" or

     "<key> <value>" strings)

And the next commit takes the struct that object-filter.{c,h} produces and actually performs the traversal.

I think this can be significantly simplified, though. Would this work:
a) Define the object_filter_options struct, but make all fields correspond to a single parameter each. Define OBJECT_FILTER_OPTIONS_INIT to initialize everything to 0 except for large_byte_limit to ULONG_MAX (so that we can detect if something else is set to it).
b) Define one single OPT_PARSE_FILTER macro containing all the parameters. We can use the non-callback macros here. That solves 2) above.
c) Define a function that takes in (int *argc, char ***argv) that can "massage" it to remove all filter-related arguments, storing them in a object_filter_options struct. That solves 3) above. As discussed in the API documentation, this means that argument lists of the form "--unknown --known" (where "--unknown" takes an argument) are processed differently, but then again, rev-list never supported them anyway (it required "--unknown=<arg>").
d) Define a function that converts "<key>" into "--<key>" and "<key> <value>" into "--<key>=<value>", and use the existing mechanism. That solves 4) above.

Quoted 5 lines

This removes the need to maintain the lists of one-per-argument functions, including the parse_filter_* and opt_parse_filter_* functions declared in the header file. If we were to add a feature, we wouldn't need to change anything in the caller, and wouldn't need to hand-edit object_filter_hand_parse_arg() and object_filter_hand_parse_protocol().

Maybe. What I have here now is the result of adding these arguments to rev-list and pack-objects (in the current patch series), and also to fetch-pack, fetch, clone, upload-pack, index-pack, and the transport and protocol code (in a follow-on patch series that I've omitted for the moment). And there will probably be a few more, such as fsck, gc, and etc. I hesitate to refine the macros too much further until we've agreement on the overall approach and terms.

Thanks
Jeff

#7 lore raw

Re: [PATCH 07/13] object-filter: common declarations for object filtering

Quoted 7 lines · On Thu, 28 Sep 2017 10:33:39 -0400 Jeff Hostetler <[email protected]> wrote:

Maybe. What I have here now is the result of adding these arguments to rev-list and pack-objects (in the current patch series), and also to fetch-pack, fetch, clone, upload-pack, index-pack, and the transport and protocol code (in a follow-on patch series that I've omitted for the moment). And there will probably be a few more, such as fsck, gc, and etc. I hesitate to refine the macros too much further until we've agreement on the overall approach and terms.

Fair enough. My current opinion on the overall approach (others might differ, of course):
- Filtering based on a sparse checkout specification in rev-list and pack-objects sounds useful to me, and is worth the filtering mechanism.
- Filtering based on size (or based on type) still doesn't seem useful to me in rev-list, but if we're going to implement the filtering mechanism anyway, we might as well use the mechanism.
- Besides my comments in [1], I think the API could still be slightly better organized. For example, object-filter probably should be the one to define the traverse_ function that takes in struct object_filter_options, and optionally a set of excluded objects to populate.

[1] https://public-inbox.org/git/[email protected]/

#8 lore raw

PATCH 8/13 list-objects: add traverse_commit_list_filtered method

In reply to Jeff Hostetler

From: Jeff Hostetler <[email protected]>

Add traverse_commit_list_filtered() wrapper around the various filter methods using common data in object_filter_options.

---
 list-objects.c | 34 ++++++++++++++++++++++++++++++++++
 list-objects.h | 11 +++++++++++
 2 files changed, 45 insertions(+)
list-objects.c +34 -0
index 3e86008..0f063d9 100644
@@ -7,6 +7,9 @@
 #include "tree-walk.h"
 #include "revision.h"
 #include "list-objects.h"
+#include "list-objects-filter-all.h"
+#include "list-objects-filter-large.h"
+#include "list-objects-filter-sparse.h"
 
 static void process_blob(struct rev_info *revs,
 			 struct blob *blob,
@@ -266,3 +269,34 @@ void traverse_commit_list(struct rev_info *revs,
 		show_commit, show_object, show_data,
 		NULL, NULL);
 }
+
+void traverse_commit_list_filtered(
+	struct object_filter_options *filter_options,
+	struct rev_info *revs,
+	show_commit_fn show_commit,
+	show_object_fn show_object,
+	oidset2_foreach_cb print_omitted_object,
+	void *show_data)
+{
+	if (filter_options->omit_all_blobs)
+		traverse_commit_list_omit_all_blobs(
+			revs, show_commit, show_object, print_omitted_object, show_data);
+
+	else if (filter_options->omit_large_blobs)
+		traverse_commit_list_omit_large_blobs(
+			revs, show_commit, show_object, print_omitted_object, show_data,
+			(int64_t)(uint64_t)filter_options->large_byte_limit);
+
+	else if (filter_options->use_blob)
+		traverse_commit_list_use_blob(
+			revs, show_commit, show_object, print_omitted_object, show_data,
+			&filter_options->sparse_oid);
+
+	else if (filter_options->use_path)
+		traverse_commit_list_use_path(
+			revs, show_commit, show_object, print_omitted_object, show_data,
+			filter_options->sparse_value);
+
+	else
+		die("unspecified list-objects filter");
+}
list-objects.h +11 -0
index 39fcbb5..a8acedc 100644
@@ -1,6 +1,9 @@
 #ifndef LIST_OBJECTS_H
 #define LIST_OBJECTS_H
 
+#include "oidset2.h"
+#include "object-filter.h"
+
 typedef void (*show_commit_fn)(struct commit *, void *);
 typedef void (*show_object_fn)(struct object *, const char *, void *);
 void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
@@ -38,4 +41,12 @@ void traverse_commit_list_worker(
 	show_commit_fn, show_object_fn, void *show_data,
 	filter_object_fn filter, void *filter_data);
 
+void traverse_commit_list_filtered(
+	struct object_filter_options *filter_options,
+	struct rev_info *revs,
+	show_commit_fn show_commit,
+	show_object_fn show_object,
+	oidset2_foreach_cb print_omitted_object,
+	void *show_data);
+
 #endif
Signature · 1 line
2.9.3
#9 lore raw

PATCH 9/13 rev-list: add object filtering support

In reply to Jeff Hostetler

From: Jeff Hostetler <[email protected]>

Teach rev-list to use the filtering provided by the traverse_commit_list_filtered() interface to omit unwanted objects from the result.

This feature is only enabled when one of the "--objects*" options are used.

When the "--filter-print-manifest" option is used, the omitted objects and their sizes are printed at the end. These are marked with a "~". This can be combined with "--quiet" to get a list of just the omitted objects.

---
 builtin/rev-list.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 71 insertions(+), 2 deletions(-)
builtin/rev-list.c +71 -2
index 95d84d5..5f54495 100644
@@ -53,6 +53,8 @@ static const char rev_list_usage[] =
 
 static struct progress *progress;
 static unsigned progress_counter;
+static struct object_filter_options filter_options;
+static struct oidset2 missing_objects;
 
 static void finish_commit(struct commit *commit, void *data);
 static void show_commit(struct commit *commit, void *data)
@@ -179,8 +181,25 @@ static void finish_commit(struct commit *commit, void *data)
 static void finish_object(struct object *obj, const char *name, void *cb_data)
 {
 	struct rev_list_info *info = cb_data;
-	if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid))
+	if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
+		if (filter_options.print_missing) {
+			oidset2_insert(&missing_objects, &obj->oid, obj->type,
+				       -1, name);
+			return;
+		}
+		if (filter_options.relax) {
+			/*
+			 * Relax consistency checks to not complain about
+			 * omitted objects (presumably caused by use of
+			 * the previous use of the 'filter-objects' feature).
+			 *
+			 * Note that this is independent of any filtering that
+			 * we are doing in this run.
+			 */
+			return;
+		}
 		die("missing blob object '%s'", oid_to_hex(&obj->oid));
+	}
 	if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
 		parse_object(&obj->oid);
 }
@@ -200,6 +219,25 @@ static void show_edge(struct commit *commit)
 	printf("-%s\n", oid_to_hex(&commit->object.oid));
 }
 
+static void print_omitted_object(int i, int i_limit, struct oidset2_entry *e, void *cb_data)
+{
+	/* struct rev_list_info *info = cb_data; */
+	const char *tn = typename(e->type);
+
+	if (e->object_length == -1)
+		printf("~%s %s\n", oid_to_hex(&e->oid), tn);
+	else
+		printf("~%s %s %"PRIuMAX"\n", oid_to_hex(&e->oid), tn, e->object_length);
+}
+
+static void print_missing_object(int i, int i_limit, struct oidset2_entry *e, void *cb_data)
+{
+	/* struct rev_list_info *info = cb_data; */
+	const char *tn = typename(e->type);
+
+	printf("?%s %s\n", oid_to_hex(&e->oid), tn);
+}
+
 static void print_var_str(const char *var, const char *val)
 {
 	printf("%s='%s'\n", var, val);
@@ -333,6 +371,15 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 			show_progress = arg;
 			continue;
 		}
+		if (object_filter_hand_parse_arg(
+			    &filter_options, arg, 1, 1, 1)) {
+			if (!revs.blob_objects)
+				die(_("object filtering requires --objects"));
+			if (filter_options.use_blob &&
+			    !oidcmp(&filter_options.sparse_oid, &null_oid))
+				die(_("invalid sparse value"));
+			continue;
+		}
 		usage(rev_list_usage);
 
 	}
@@ -357,6 +404,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	if (revs.show_notes)
 		die(_("rev-list does not support display of notes"));
 
+	if (object_filter_enabled(&filter_options)) {
+		if (use_bitmap_index)
+			die(_("cannot combine --use-bitmap-index with object filtering"));
+	}
+
 	save_commit_buffer = (revs.verbose_header ||
 			      revs.grep_filter.pattern_list ||
 			      revs.grep_filter.header_list);
@@ -401,7 +453,24 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 			return show_bisect_vars(&info, reaches, all);
 	}
 
-	traverse_commit_list(&revs, show_commit, show_object, &info);
+	if (filter_options.print_missing)
+		memset(&missing_objects, 0, sizeof(missing_objects));
+
+	if (object_filter_enabled(&filter_options))
+		traverse_commit_list_filtered(
+			&filter_options, &revs,
+			show_commit, show_object,
+			(filter_options.print_omitted
+			 ? print_omitted_object
+			 : NULL),
+			&info);
+	else
+		traverse_commit_list(&revs, show_commit, show_object, &info);
+
+	if (filter_options.print_missing) {
+		oidset2_foreach(&missing_objects, print_missing_object, &info);
+		oidset2_clear(&missing_objects);
+	}
 
 	stop_progress(&progress);
 
Signature · 1 line
2.9.3
#10 lore raw

Re: [PATCH 09/13] rev-list: add object filtering support

builtin/rev-list.c static void finish_commit(struct commit *commit, void *data) · line 190

Quoted 1 line · On Fri, 22 Sep 2017 20:30:13 +0000 Jeff Hostetler <[email protected]> wrote:
builtin/rev-list.c +1 -0
+		if (filter_options.relax) {

Add some documentation about how this differs from ignore_missing_links in struct rev_info.

#11 lore raw

Re: [PATCH 09/13] rev-list: add object filtering support

Quoted 8 lines · Jonathan Tan

builtin/rev-list.c static void finish_commit(struct commit *commit, void *data) · line 190

Quoted 1 line · On Fri, 22 Sep 2017 20:30:13 +0000 Jeff Hostetler <[email protected]> wrote:
builtin/rev-list.c +1 -0
+		if (filter_options.relax) {

Add some documentation about how this differs from ignore_missing_links in struct rev_info.

It's unclear what that field means. (It's not documented that I saw.) And it is explicitly turned on and off in pack-bitmap.c, so again I'm not sure how that agrees with what I'm doing.

My relax field is probably an interim thing (to allow rev-list, index-pack, and friends not complain about missing objects during my development).

I suspect that you and I will need to merge our efforts here. You have a similar variable "revs->exclude_promisor_objects". But I need to study how our usages differ.

Thanks,
Jeff

#12 lore raw

PATCH 10/13 rev-list: add filtering help text

In reply to Jeff Hostetler

From: Jeff Hostetler <[email protected]>

---
 Documentation/git-rev-list.txt     |  9 ++++++++-
 Documentation/rev-list-options.txt | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 1 deletion(-)
Documentation/git-rev-list.txt +8 -1
index ef22f17..b2e8255 100644
@@ -47,7 +47,14 @@ SYNOPSIS
 	     [ --fixed-strings | -F ]
 	     [ --date=<format>]
 	     [ [ --objects | --objects-edge | --objects-edge-aggressive ]
-	       [ --unpacked ] ]
+	       [ --unpacked ]
+	       [ [ --filter-omit-all-blobs |
+		   --filter-omit-large-blobs=<n>[kmg] |
+		   --filter-use-blob=<blob-ish> |
+		   --filter-use-path=<path> ]
+		 [ --filter-print-missing ]
+		 [ --filter-print-omitted ] ] ]
+	     [ --filter-relax ]
 	     [ --pretty | --header ]
 	     [ --bisect ]
 	     [ --bisect-vars ]
Documentation/rev-list-options.txt +32 -0
index a6cf9eb..5c7d53c 100644
@@ -698,6 +698,38 @@ ifdef::git-rev-list[]
 --unpacked::
 	Only useful with `--objects`; print the object IDs that are not
 	in packs.
+
+--filter-omit-all-blobs::
+	Only useful with one of the `--objects*`; omits all blobs from
+	the printed list of objects.
+
+--filter-omit-large-blobs=<n>[kmg]::
+	Only useful with one of the `--objects*`; omits blobs larger than
+	n bytes from the printed list of objects.  May optionally be
+	followed by 'k', 'm', or 'g' units.  Value may be zero.  Special
+	files (matching ".git*") are always included, regardless of size.
+
+--filter-use-blob=<blob-ish>::
+--filter-use-path=<path>::
+	Only useful with one of the `--objects*`; uses a sparse-checkout
+	specification contained in the given object or file to filter the
+	result to only contain blobs referenced by such a sparse-checkout.
+
+--filter-print-missing::
+	Prints a list of the missing objects for the requested traversal.
+	Object IDs are prefixed with a ``?'' character.  The object type
+	is printed after the ID.  This may be used with or without any of
+	the above filtering options.
+
+--filter-print-omitted::
+	Only useful with one of the above `--filter*`; prints a list
+	of the omitted objects.  Object IDs are prefixed with a ``~''
+	character.  The object size is printed after the ID.
+
+--filter-relax::
+	Relax consistency checking for missing blobs.  Do not warn of
+	missing blobs during normal (non-filtering) object traversal
+	following an earlier partial/narrow clone or fetch.
 endif::git-rev-list[]
 
 --no-walk[=(sorted|unsorted)]::
Signature · 1 line
2.9.3
#13 lore raw

PATCH 11/13 t6112: rev-list object filtering test

In reply to Jeff Hostetler

From: Jeff Hostetler <[email protected]>

---
 t/t6112-rev-list-filters-objects.sh | 237 ++++++++++++++++++++++++++++++++++++
 1 file changed, 237 insertions(+)
 create mode 100755 t/t6112-rev-list-filters-objects.sh
t/t6112-rev-list-filters-objects.sh new file +237 -0
new file mode 100755
index 0000000..66ff022
@@ -0,0 +1,237 @@
+#!/bin/sh
+
+test_description='git rev-list with object filtering'
+
+. ./test-lib.sh
+
+# test the omit-all filter
+
+test_expect_success 'setup' '
+	echo "{print \$1}" >print_1.awk &&
+	echo "{print \$2}" >print_2.awk &&
+
+	for n in 1 2 3 4 5
+	do
+		echo $n > file.$n
+		git add file.$n
+		git commit -m "$n"
+	done
+'
+
+# Verify the omitted ("~OID") lines match the predicted list of OIDs.
+test_expect_success 'omit-all-blobs omitted 5 blobs' '
+	git ls-files -s file.1 file.2 file.3 file.4 file.5 \
+		| awk -f print_2.awk \
+		| sort >expected &&
+	git rev-list HEAD --quiet --objects --filter-print-omitted --filter-omit-all-blobs \
+		| awk -f print_1.awk \
+		| sed "s/~//" >observed &&
+	test_cmp observed expected
+'
+
+# Verify the complete OID list matches the unfiltered OIDs plus the omitted OIDs.
+test_expect_success 'omit-all-blobs nothing else changed' '
+	git rev-list HEAD --objects \
+		| awk -f print_1.awk \
+		| sort >expected &&
+	git rev-list HEAD --objects --filter-print-omitted --filter-omit-all-blobs \
+		| awk -f print_1.awk \
+		| sed "s/~//" \
+		| sort >observed &&
+	test_cmp observed expected
+'
+
+# test the size-based filtering.
+
+test_expect_success 'setup_large' '
+	for n in 1000 10000
+	do
+		printf "%"$n"s" X > large.$n
+		git add large.$n
+		git commit -m "$n"
+	done
+'
+
+test_expect_success 'omit-large-blobs omit 2 blobs' '
+	git ls-files -s large.1000 large.10000 \
+		| awk -f print_2.awk \
+		| sort >expected &&
+	git rev-list HEAD --quiet --objects --filter-print-omitted --filter-omit-large-blobs=500 \
+		| awk -f print_1.awk \
+		| sed "s/~//" >observed &&
+	test_cmp observed expected
+'
+
+test_expect_success 'omit-large-blobs nothing else changed' '
+	git rev-list HEAD --objects \
+		| awk -f print_1.awk \
+		| sort >expected &&
+	git rev-list HEAD --objects --filter-print-omitted --filter-omit-large-blobs=500 \
+		| awk -f print_1.awk \
+		| sed "s/~//" \
+		| sort >observed &&
+	test_cmp observed expected
+'
+
+# boundary test around the size parameter.
+# filter is strictly less than the value, so size 500 and 1000 should have the
+# same results, but 1001 should filter more.
+
+test_expect_success 'omit-large-blobs omit 2 blobs' '
+	git ls-files -s large.1000 large.10000 \
+		| awk -f print_2.awk \
+		| sort >expected &&
+	git rev-list HEAD --quiet --objects --filter-print-omitted --filter-omit-large-blobs=1000 \
+		| awk -f print_1.awk \
+		| sed "s/~//" >observed &&
+	test_cmp observed expected
+'
+
+test_expect_success 'omit-large-blobs omit 1 blob' '
+	git ls-files -s large.10000 \
+		| awk -f print_2.awk \
+		| sort >expected &&
+	git rev-list HEAD --quiet --objects --filter-print-omitted --filter-omit-large-blobs=1001 \
+		| awk -f print_1.awk \
+		| sed "s/~//" >observed &&
+	test_cmp observed expected
+'
+
+test_expect_success 'omit-large-blobs omit 1 blob (1k)' '
+	git ls-files -s large.10000 \
+		| awk -f print_2.awk \
+		| sort >expected &&
+	git rev-list HEAD --quiet --objects --filter-print-omitted --filter-omit-large-blobs=1k \
+		| awk -f print_1.awk \
+		| sed "s/~//" >observed &&
+	test_cmp observed expected
+'
+
+test_expect_success 'omit-large-blobs omit no blob (1m)' '
+	cat </dev/null >expected &&
+	git rev-list HEAD --quiet --objects --filter-print-omitted --filter-omit-large-blobs=1m \
+		| awk -f print_1.awk \
+		| sed "s/~//" >observed &&
+	test_cmp observed expected
+'
+
+# Test sparse-pattern filtering (using explicit local patterns).
+# We use the same disk format as sparse-checkout to specify the
+# filtering, but do not require sparse-checkout to be enabled.
+
+test_expect_success 'setup using sparse file' '
+	mkdir dir1 &&
+	for n in sparse1 sparse2
+	do
+		echo $n > $n
+		git add $n
+		echo dir1/$n > dir1/$n
+		git add dir1/$n
+	done &&
+	git commit -m "sparse" &&
+	echo dir1/ >pattern1 &&
+	echo sparse1 >pattern2
+'
+
+# pattern1 should only include the 2 dir1/* files.
+# and omit the 5 file.*, 2 large.*, and 2 top-level sparse* files.
+test_expect_success 'sparse using path pattern1' '
+	git rev-list HEAD --objects --filter-print-omitted --filter-use-path=pattern1 >out &&
+
+	grep "^~" out >blobs.omitted &&
+	test $(cat blobs.omitted | wc -l) = 9 &&
+
+	grep "dir1/sparse" out >blobs.included &&
+	test $(cat blobs.included | wc -l) = 2
+'
+
+# pattern2 should include the sparse1 and dir1/sparse1.
+# and omit the 5 file.*, 2 large.*, and the 2 sparse2 files.
+test_expect_success 'sparse using path pattern2' '
+	git rev-list HEAD --objects --filter-print-omitted --filter-use-path=pattern2 >out &&
+
+	grep "^~" out >blobs.omitted &&
+	test $(cat blobs.omitted | wc -l) = 9 &&
+
+	grep "sparse1" out >blobs.included &&
+	test $(cat blobs.included | wc -l) = 2
+'
+
+# Test sparse-pattern filtering (using a blob in the repo).
+# This could be used to later let pack-objects do filtering.
+
+# pattern1 should only include the 2 dir1/* files.
+# and omit the 5 file.*, 2 large.*, 2 top-level sparse*, and 1 pattern file.
+test_expect_success 'sparse using OID for pattern1' '
+	git add pattern1 &&
+	git commit -m "pattern1" &&
+
+	git rev-list HEAD --objects >normal.output &&
+	grep "pattern1" <normal.output | awk "{print \$1;}" >pattern1.oid &&
+
+	git rev-list HEAD --objects --filter-print-omitted --filter-use-blob=`cat pattern1.oid` >out &&
+
+	grep "^~" out >blobs.omitted &&
+	test $(cat blobs.omitted | wc -l) = 10 &&
+
+	grep "dir1/sparse" out >blobs.included &&
+	test $(cat blobs.included | wc -l) = 2
+'
+
+# repeat previous test but use blob-ish expression rather than OID.
+test_expect_success 'sparse using blob-ish to get OID for pattern spec' '
+	git rev-list HEAD --objects --filter-print-omitted --filter-use-blob=HEAD:pattern1 >out &&
+
+	grep "^~" out >blobs.omitted &&
+	test $(cat blobs.omitted | wc -l) = 10 &&
+
+	grep "dir1/sparse" out >blobs.included &&
+	test $(cat blobs.included | wc -l) = 2
+'
+
+# pattern2 should include the sparse1 and dir1/sparse1.
+# and omit the 5 file.*, 2 large.*, 2 top-level sparse*, and 2 pattern files.
+test_expect_success 'sparse using OID for pattern2' '
+	git add pattern2 &&
+	git commit -m "pattern2" &&
+
+	git rev-list HEAD --objects >normal.output &&
+	grep "pattern2" <normal.output | awk "{print \$1;}" >pattern2.oid &&
+
+	git rev-list HEAD --objects --filter-print-omitted --filter-use-blob=`cat pattern2.oid` >out &&
+
+	grep "^~" out >blobs.omitted &&
+	test $(cat blobs.omitted | wc -l) = 11 &&
+
+	grep "sparse1" out >blobs.included &&
+	test $(cat blobs.included | wc -l) = 2
+'
+
+# repeat previous test but use blob-ish expression rather than OID.
+test_expect_success 'sparse using blob-ish rather than OID for pattern2' '
+	git rev-list HEAD --objects --filter-print-omitted --filter-use-blob=HEAD:pattern2 >out &&
+
+	grep "^~" out >blobs.omitted &&
+	test $(cat blobs.omitted | wc -l) = 11 &&
+
+	grep "sparse1" out >blobs.included &&
+	test $(cat blobs.included | wc -l) = 2
+'
+
+# delete some loose objects and test rev-list printing them as missing.
+test_expect_success 'print missing objects' '
+	git ls-files -s file.1 file.2 file.3 file.4 file.5 \
+		| awk -f print_2.awk \
+		| sort >expected &&
+	for id in `cat expected | sed "s|..|&/|"`
+	do
+		rm .git/objects/$id
+	done &&
+	git rev-list --quiet HEAD --filter-print-missing --objects \
+		| awk -f print_1.awk \
+		| sed "s/?//" \
+		| sort >observed &&
+	test_cmp observed expected
+'
+
+test_done
Signature · 1 line
2.9.3
#14 lore raw

PATCH 12/13 pack-objects: add object filtering support

In reply to Jeff Hostetler

From: Jeff Hostetler <[email protected]>

Teach pack-objects to use the filtering provided by the traverse_commit_list_filtered() interface to omit unwanted objects from the resulting packfile.

This feature is intended for partial clone/fetch.

Filtering requires the use of the "--stdout" option.

---
 builtin/pack-objects.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)
builtin/pack-objects.c +23 -1
index f4a8441..1712d1b 100644
@@ -78,6 +78,8 @@ static unsigned long cache_max_small_delta_size = 1000;
 
 static unsigned long window_memory_limit = 0;
 
+static struct object_filter_options filter_options;
+
 /*
  * stats
  */
@@ -2810,7 +2812,12 @@ static void get_object_list(int ac, const char **av)
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
 	mark_edges_uninteresting(&revs, show_edge);
-	traverse_commit_list(&revs, show_commit, show_object, NULL);
+	if (object_filter_enabled(&filter_options))
+		traverse_commit_list_filtered(&filter_options, &revs,
+					      show_commit, show_object,
+					      NULL, NULL);
+	else
+		traverse_commit_list(&revs, show_commit, show_object, NULL);
 
 	if (unpack_unreachable_expiration) {
 		revs.ignore_missing_links = 1;
@@ -2946,6 +2953,15 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 			 N_("use a bitmap index if available to speed up counting objects")),
 		OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
 			 N_("write a bitmap index together with the pack index")),
+
+		OPT_PARSE_FILTER_OMIT_ALL_BLOBS(&filter_options),
+		OPT_PARSE_FILTER_OMIT_LARGE_BLOBS(&filter_options),
+		OPT_PARSE_FILTER_USE_BLOB(&filter_options),
+		OPT_PARSE_FILTER_USE_PATH(&filter_options),
+		/* not needed: OPT_PARSE_FILTER_PRINT_MISSING */
+		/* not needed: OPT_PARSE_FILTER_PRINT_OMITTED */
+		/* not needed: OPT_PARSE_FILTER_RELAX */
+
 		OPT_END(),
 	};
 
@@ -3022,6 +3038,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 	if (!rev_list_all || !rev_list_reflog || !rev_list_index)
 		unpack_unreachable_expiration = 0;
 
+	if (object_filter_enabled(&filter_options)) {
+		if (!pack_to_stdout)
+			die("cannot use filtering with an indexable pack.");
+		use_bitmap_index = 0;
+	}
+
 	/*
 	 * "soft" reasons not to use bitmaps - for on-disk repack by default we want
 	 *
Signature · 1 line
2.9.3
#15 lore raw

PATCH 13/13 pack-objects: add filtering help text

In reply to Jeff Hostetler

From: Jeff Hostetler <[email protected]>

Add help text for new object filtering options to
pack-objects documentation.

---
 Documentation/git-pack-objects.txt | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
Documentation/git-pack-objects.txt +17 -0
index 8973510..1ed7d4b 100644
@@ -231,6 +231,23 @@ So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle.
 	With this option, parents that are hidden by grafts are packed
 	nevertheless.
 
+--filter-omit-all-blobs::
+	Requires `--stdout`.  Omits all blobs from the packfile.
+
+--filter-omit-large-blobs=<n>[kmg]::
+	Requires `--stdout`.  Omits large blobs larger than n bytes from
+	the packfile.  May optionally be followed by 'k', 'm', or 'g' units.
+	Value may be zero.  Special files (matching ".git*") are always
+	included, regardless of size.
+
+--filter-use-blob=<blob-ish>::
+--filter-use-path=<path>::
+	Requires `--stdout`.  Use a sparse-checkout specification to
+	filter the resulting packfile to only contain the blobs that
+	would be referenced by such a sparse-checkout.  `<path>` specifies
+	a local pathname.  `<blob-ish>` specifies an expression that can
+	be evaluated to a blob.
+
 SEE ALSO
 --------
 linkgit:git-rev-list[1]
Signature · 1 line
2.9.3