Skip to content

Commit

Permalink
[RFC] list-objects-filter: introduce new filter sparse:buffer=<spec>
Browse files Browse the repository at this point in the history
Although we already had a `--filter=sparse:oid=<oid>` which
can used to clone a repository with limited objects which meet
filter rules in the file corresponding to the <oid> on the git
server. But it can only read filter rules which have been record
in the git server before.

But sometimes users may want to pass their own filter rules
without upload it to the git server first. So now introduce a
new filter `sparse:buffer=<spec>`, which can pass users
required filterspec.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
  • Loading branch information
adlternative committed Aug 8, 2022
1 parent 679aad9 commit 2c23b69
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 5 deletions.
4 changes: 4 additions & 0 deletions Documentation/rev-list-options.txt
Expand Up @@ -913,6 +913,10 @@ specification contained in the blob (or blob-expression) '<blob-ish>'
to omit blobs that would not be required for a sparse checkout on
the requested refs.
+
The form '--filter=sparse:buffer=<spec>' uses a sparse-checkout
specification contained in the buffer to omit blobs that would not
be required for a sparse checkout on the requested refs.
+
The form '--filter=tree:<depth>' omits all blobs and trees whose depth
from the root tree is >= <depth> (minimum depth if an object is located
at multiple depths in the commits traversed). <depth>=0 will not include
Expand Down
6 changes: 1 addition & 5 deletions dir.c
Expand Up @@ -1037,10 +1037,6 @@ static void invalidate_directory(struct untracked_cache *uc,
dir->dirs[i]->recurse = 0;
}

static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl);

/* Flags for add_patterns() */
#define PATTERN_NOFOLLOW (1<<0)

Expand Down Expand Up @@ -1123,7 +1119,7 @@ static int add_patterns(const char *fname, const char *base, int baselen,
return 0;
}

static int add_patterns_from_buffer(char *buf, size_t size,
int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl)
{
Expand Down
3 changes: 3 additions & 0 deletions dir.h
Expand Up @@ -440,6 +440,9 @@ void add_patterns_from_file(struct dir_struct *, const char *fname);
int add_patterns_from_blob_to_list(struct object_id *oid,
const char *base, int baselen,
struct pattern_list *pl);
int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl);
void parse_path_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
void add_pattern(const char *string, const char *base,
int baselen, struct pattern_list *pl, int srcpos);
Expand Down
8 changes: 8 additions & 0 deletions list-objects-filter-options.c
Expand Up @@ -29,6 +29,8 @@ const char *list_object_filter_config_name(enum list_objects_filter_choice c)
return "tree";
case LOFC_SPARSE_OID:
return "sparse:oid";
case LOFC_SPARSE_BUFFER:
return "sparse:buffer";
case LOFC_OBJECT_TYPE:
return "object:type";
case LOFC_COMBINE:
Expand Down Expand Up @@ -84,6 +86,11 @@ int gently_parse_list_objects_filter(
}
return 1;

} else if (skip_prefix(arg, "sparse:buffer=", &v0)) {
filter_options->spec_buffer = xstrdup(v0);
filter_options->choice = LOFC_SPARSE_BUFFER;
return 0;

} else if (skip_prefix(arg, "object:type=", &v0)) {
int type = type_from_string_gently(v0, strlen(v0), 1);
if (type < 0) {
Expand Down Expand Up @@ -338,6 +345,7 @@ void list_objects_filter_release(
return;
string_list_clear(&filter_options->filter_spec, /*free_util=*/0);
free(filter_options->sparse_oid_name);
free(filter_options->spec_buffer);
for (sub = 0; sub < filter_options->sub_nr; sub++)
list_objects_filter_release(&filter_options->sub[sub]);
free(filter_options->sub);
Expand Down
3 changes: 3 additions & 0 deletions list-objects-filter-options.h
Expand Up @@ -14,6 +14,7 @@ enum list_objects_filter_choice {
LOFC_BLOB_LIMIT,
LOFC_TREE_DEPTH,
LOFC_SPARSE_OID,
LOFC_SPARSE_BUFFER,
LOFC_OBJECT_TYPE,
LOFC_COMBINE,
LOFC__COUNT /* must be last */
Expand Down Expand Up @@ -58,6 +59,8 @@ struct list_objects_filter_options {
unsigned long tree_exclude_depth;
enum object_type object_type;

char *spec_buffer;

/* LOFC_COMBINE values */

/* This array contains all the subfilters which this filter combines. */
Expand Down
27 changes: 27 additions & 0 deletions list-objects-filter.c
Expand Up @@ -545,6 +545,32 @@ static void filter_sparse_oid__init(
filter->free_fn = filter_sparse_free;
}

static void filter_sparse_file_path__init(
struct list_objects_filter_options *filter_options,
struct filter *filter)
{
struct filter_sparse_data *d = xcalloc(1, sizeof(*d));
struct strbuf strbuf = STRBUF_INIT;
char *buf;
size_t size;

strbuf_addf(&strbuf, "%s\n", filter_options->spec_buffer);
buf = strbuf_detach(&strbuf, &size);
if (add_patterns_from_buffer(buf, size, "", 0, &d->pl) < 0)
die(_("unable to parse sparse filter data: %s"),
filter_options->spec_buffer);
strbuf_release(&strbuf);

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].child_prov_omit = 0;
d->nr++;

filter->filter_data = d;
filter->filter_object_fn = filter_sparse;
filter->free_fn = filter_sparse_free;
}

/*
* A filter for list-objects to omit large blobs.
* And to OPTIONALLY collect a list of the omitted OIDs.
Expand Down Expand Up @@ -766,6 +792,7 @@ static filter_init_fn s_filters[] = {
filter_blobs_limit__init,
filter_trees_depth__init,
filter_sparse_oid__init,
filter_sparse_file_path__init,
filter_object_type__init,
filter_combine__init,
};
Expand Down
83 changes: 83 additions & 0 deletions t/t5616-partial-clone.sh
Expand Up @@ -421,6 +421,9 @@ test_expect_success 'setup src repo for sparse filter' '
git -C sparse-src config --local uploadpack.allowanysha1inwant 1 &&
test_commit -C sparse-src one &&
test_commit -C sparse-src two &&
mkdir sparse-src/three &&
test_commit -C sparse-src/three four &&
test_commit -C sparse-src/three five &&
echo /one.t >sparse-src/only-one &&
git -C sparse-src add . &&
git -C sparse-src commit -m "add sparse checkout files"
Expand Down Expand Up @@ -451,6 +454,86 @@ test_expect_success 'partial clone with unresolvable sparse filter fails cleanly
test_i18ngrep "unable to parse sparse filter data in" err
'

test_expect_success 'partial clone with sparse:buffer filter with single file succeeds' '
rm -rf dst.git &&
git clone --no-local --bare \
--filter=sparse:buffer=one.t \
sparse-src dst.git &&
(
cd dst.git &&
git rev-list --objects --missing=print HEAD >out &&
grep "^$(git rev-parse HEAD:one.t)" out &&
grep "^?$(git rev-parse HEAD:two.t)" out &&
grep "^?$(git rev-parse HEAD:three/four.t)" out &&
grep "^?$(git rev-parse HEAD:three/five.t)" out
)
'

test_expect_success 'partial clone with sparse:buffer filter with single dir succeeds' '
rm -rf dst.git &&
git clone --no-local --bare \
--filter=sparse:buffer=three \
sparse-src dst.git &&
(
cd dst.git &&
git rev-list --objects --missing=print HEAD >out &&
grep "^?$(git rev-parse HEAD:one.t)" out &&
grep "^?$(git rev-parse HEAD:two.t)" out &&
grep "^$(git rev-parse HEAD:three/four.t)" out &&
grep "^$(git rev-parse HEAD:three/five.t)" out
)
'

test_expect_success 'partial clone with sparse:buffer filter with filterspec file succeeds' '
test_write_lines one.t three > filterspec &&
rm -rf dst.git &&
git clone --no-local --bare \
--filter="sparse:buffer=`cat filterspec`" \
sparse-src dst.git &&
(
cd dst.git &&
git rev-list --objects --missing=print HEAD >out &&
grep "^$(git rev-parse HEAD:one.t)" out &&
grep "^?$(git rev-parse HEAD:two.t)" out &&
grep "^$(git rev-parse HEAD:three/four.t)" out &&
grep "^$(git rev-parse HEAD:three/five.t)" out
)
'

test_expect_success 'partial clone with sparse:buffer filter with filterspec file and special character' '
cat >filterspec <<-EOF &&
three
!three/four.t
EOF
rm -rf dst.git &&
git clone --no-local --bare \
--filter="sparse:buffer=`cat filterspec`" \
sparse-src dst.git &&
(
cd dst.git &&
git rev-list --objects --missing=print HEAD >out &&
grep "^?$(git rev-parse HEAD:one.t)" out &&
grep "^?$(git rev-parse HEAD:two.t)" out &&
grep "^?$(git rev-parse HEAD:three/four.t)" out &&
grep "^$(git rev-parse HEAD:three/five.t)" out
)
'

test_expect_success 'partial clone with sparse:buffer filter with unknown filterspec' '
rm -rf dst.git &&
git clone --no-local --bare \
--filter=sparse:buffer=unknown \
sparse-src dst.git &&
(
cd dst.git &&
git rev-list --objects --missing=print HEAD >out &&
grep "^?$(git rev-parse HEAD:one.t)" out &&
grep "^?$(git rev-parse HEAD:two.t)" out &&
grep "^?$(git rev-parse HEAD:three/four.t)" out &&
grep "^?$(git rev-parse HEAD:three/five.t)" out
)
'

setup_triangle () {
rm -rf big-blob.txt server client promisor-remote &&

Expand Down

0 comments on commit 2c23b69

Please sign in to comment.