diff --git a/src/libgit2/config_cache.c b/src/libgit2/config_cache.c index 05d9d5828e0..79f1d0ae819 100644 --- a/src/libgit2/config_cache.c +++ b/src/libgit2/config_cache.c @@ -86,6 +86,8 @@ static struct map_data _configmaps[] = { {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT }, {"core.fsyncobjectfiles", NULL, 0, GIT_FSYNCOBJECTFILES_DEFAULT }, {"core.longpaths", NULL, 0, GIT_LONGPATHS_DEFAULT }, + {"core.sparsecheckout", NULL, 0, GIT_SPARSECHECKOUT_DEFAULT }, + {"core.sparsecheckoutcone", NULL, 0, GIT_SPARSECHECKOUTCONE_DEFAULT }, }; int git_config__configmap_lookup(int *out, git_config *config, git_configmap_item item) diff --git a/src/libgit2/repository.h b/src/libgit2/repository.h index 7da2d165577..324f0fcfb62 100644 --- a/src/libgit2/repository.h +++ b/src/libgit2/repository.h @@ -55,6 +55,8 @@ typedef enum { GIT_CONFIGMAP_PROTECTNTFS, /* core.protectNTFS */ GIT_CONFIGMAP_FSYNCOBJECTFILES, /* core.fsyncObjectFiles */ GIT_CONFIGMAP_LONGPATHS, /* core.longpaths */ + GIT_CONFIGMAP_SPARSECHECKOUT, /* core.sparseCheckout */ + GIT_CONFIGMAP_SPARSECHECKOUTCONE, /* core.sparseCheckoutCone */ GIT_CONFIGMAP_CACHE_MAX } git_configmap_item; @@ -123,7 +125,11 @@ typedef enum { /* core.fsyncObjectFiles */ GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CONFIGMAP_FALSE, /* core.longpaths */ - GIT_LONGPATHS_DEFAULT = GIT_CONFIGMAP_FALSE + GIT_LONGPATHS_DEFAULT = GIT_CONFIGMAP_FALSE, + /* core.sparsecheckout */ + GIT_SPARSECHECKOUT_DEFAULT = GIT_CONFIGMAP_FALSE, + /* core.sparsecheckoutcone */ + GIT_SPARSECHECKOUTCONE_DEFAULT = GIT_CONFIGMAP_TRUE } git_configmap_value; /* internal repository init flags */ diff --git a/src/libgit2/sparse_checkout.c b/src/libgit2/sparse_checkout.c new file mode 100644 index 00000000000..1aa3841643b --- /dev/null +++ b/src/libgit2/sparse_checkout.c @@ -0,0 +1,459 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "sparse_checkout.h" +#include "hashmap_str.h" +#include "parse.h" + +typedef struct { + char *path; +} git_sparse_checkout_map_entry; + +GIT_HASHMAP_STR_SETUP( + git_sparse_checkout_parentmap, + git_sparse_checkout_map_entry *); + +GIT_HASHMAP_STR_SETUP( + git_sparse_checkout_recursivemap, + git_sparse_checkout_map_entry *); + +typedef struct { + git_repository *repo; + int ignore_case; + + int full_cone; + git_sparse_checkout_parentmap parentmap; + git_sparse_checkout_recursivemap recursivemap; +} git_sparse_checkout; + +static int git_sparse_checkout__add_parentmap_entry( + git_sparse_checkout *sparse_checkout, + const char *key) +{ + int error = 0; + git_sparse_checkout_map_entry *entry; + + entry = git__calloc(1, sizeof(*entry)); + GIT_ERROR_CHECK_ALLOC(entry); + entry->path = git__strdup(key); + + git_sparse_checkout_parentmap_put( + &sparse_checkout->parentmap, entry->path, entry); + + printf("Added to parent map: %s\n", key); + + return error; +} + +static int git_sparse_checkout__remove_parentmap_entry( + git_sparse_checkout *sparse_checkout, + const char *key) +{ + int error = 0; + git_sparse_checkout_map_entry *entry; + + if (git_sparse_checkout_parentmap_get( + &entry, &sparse_checkout->parentmap, key) != 0) + return GIT_ENOTFOUND; + + if ((error = git_sparse_checkout_parentmap_remove( + &sparse_checkout->parentmap, key)) < 0) + return error; + + printf("Remove from parent map: %s\n", key); + + git__free(entry->path); + git__free(entry); + return error; +} + +static int git_sparse_checkout__add_recursivemap_entry( + git_sparse_checkout *sparse_checkout, + const char *key) +{ + int error = 0; + git_sparse_checkout_map_entry *entry; + + entry = git__calloc(1, sizeof(*entry)); + GIT_ERROR_CHECK_ALLOC(entry); + entry->path = git__strdup(key); + + git_sparse_checkout_recursivemap_put( + &sparse_checkout->recursivemap, entry->path, entry); + + printf("Added to recursive map: %s\n", key); + + return error; +} + +static int git_sparse_checkout__remove_recursivemap_entry( + git_sparse_checkout *sparse_checkout, + const char *key) +{ + int error = 0; + git_sparse_checkout_map_entry *entry; + + if (git_sparse_checkout_recursivemap_get( + &entry, &sparse_checkout->recursivemap, key) != 0) + return GIT_ENOTFOUND; + + if ((error = git_sparse_checkout_recursivemap_remove( + &sparse_checkout->recursivemap, key)) < 0) + return error; + + printf("Remove from recursive map: %s\n", key); + + git__free(entry->path); + git__free(entry); + return error; +} + +static int git_sparse_checkout__init( + git_sparse_checkout *sparse_checkout, + git_repository *repo) +{ + int error = 0; + + memset(sparse_checkout, 0, sizeof(*sparse_checkout)); + + sparse_checkout->repo = repo; + + /* Need to check ignore case */ + error = git_repository__configmap_lookup( + &sparse_checkout->ignore_case, repo, GIT_CONFIGMAP_IGNORECASE); + + return error; +} + +static void git_sparse_checkout__dispose(git_sparse_checkout *sparse_checkout) +{ + git_sparse_checkout_map_entry *e; + git_hashmap_iter_t iter = GIT_HASHMAP_ITER_INIT; + + while (git_sparse_checkout_parentmap_iterate( + &iter, NULL, &e, &sparse_checkout->parentmap) == 0) + git__free(e); + git_sparse_checkout_parentmap_dispose(&sparse_checkout->parentmap); + + while (git_sparse_checkout_recursivemap_iterate( + &iter, NULL, &e, &sparse_checkout->recursivemap) == 0) + git__free(e); + git_sparse_checkout_recursivemap_dispose( + &sparse_checkout->recursivemap); +} + +static int parse_sparse_checkout_buffer( + git_sparse_checkout *sparse_checkout, + const char *buf, + size_t len) +{ + int error = 0; + const char *scan = buf; + git_pool pool = GIT_POOL_INIT; + git_attr_fnmatch *match = NULL; + git_str truncated_match = GIT_STR_INIT; + + if (git_pool_init(&pool, 1) < 0) + goto cleanup; + + // printf("Buffer:\n%.*s\n", (int)len, buf); + + while (*scan) { + match = git__calloc(1, sizeof(git_attr_fnmatch)); + if (!match) + return -1; + + match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | + GIT_ATTR_FNMATCH_ALLOWNEG; + + /* + * context not needed as it's always the repository root + */ + if ((error = git_attr_fnmatch__parse( + match, &pool, NULL, &scan)) < 0) { + if (error != GIT_ENOTFOUND) { + goto cleanup; + } + error = 0; + continue; + } + + scan = git__next_line(scan); + + printf("%.*s ", (int)match->length, match->pattern); + printf("Negated = %d, ", + (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0); + printf("Directory = %d, ", + (match->flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0); + printf("Full Path = %d, ", + (match->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0); + printf("Has Wild = %d\n", + (match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0); + + if (git__strncmp("*", match->pattern, match->length) == 0 && + (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0 && + (match->flags & GIT_ATTR_FNMATCH_DIRECTORY) == 0) { + sparse_checkout->full_cone = 1; + git__free(match); + match = NULL; + continue; + } + + if (git__strncmp("*", match->pattern, match->length) == 0 && + (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 && + (match->flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0) { + sparse_checkout->full_cone = 0; + git__free(match); + match = NULL; + continue; + } + + if (((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) == 0 || + (match->flags & GIT_ATTR_FNMATCH_FULLPATH) == 0) && + git__strncmp("*", match->pattern, match->length) != 0) { + error = GIT_EINVALID; + git_error_set( + GIT_ERROR_INVALID, + "Invalid cone-mode pattern: %.*s", + (int)match->length, match->pattern); + goto cleanup; + } + + /* Negative matches must come after the identical non-negated + * match. It must exist in the recursive hash map. Suffix must + * equal "\*". + */ + if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) { + if (match->length < 3 && + git__suffixcmp(match->pattern, "/*") != 0) { + error = GIT_EINVALID; + git_error_set( + GIT_ERROR_INVALID, + "Invalid cone-mode pattern: %.*s", + (int)match->length, match->pattern); + goto cleanup; + } + + git_str_sets(&truncated_match, match->pattern); + git_str_shorten(&truncated_match, 2); + + // If in recursive hash map, remove and add to parent + // hash map. + + if (!git_sparse_checkout_recursivemap_contains( + &sparse_checkout->recursivemap, + truncated_match.ptr)) { + error = GIT_EINVALID; + git_error_set( + GIT_ERROR_INVALID, + "Invalid cone-mode pattern: %.*s", + (int)match->length, match->pattern); + goto cleanup; + } + + git_sparse_checkout__remove_recursivemap_entry( + sparse_checkout, truncated_match.ptr); + git_sparse_checkout__add_parentmap_entry( + sparse_checkout, truncated_match.ptr); + } else { + // Add to recursive hash map + git_sparse_checkout__add_recursivemap_entry( + sparse_checkout, match->pattern); + } + + git__free(match); + match = NULL; + } + +cleanup: + git_str_dispose(&truncated_match); + if (match) + git__free(match); + return error; +} + +static int read_sparse_checkout_file( + git_sparse_checkout *sparse_checkout, + git_repository *repo) +{ + int error = 0; + git_str contents = GIT_STR_INIT; + git_str info_path = GIT_STR_INIT; + git_str sparse_checkout_file_path = GIT_STR_INIT; + + if ((error = git_repository__item_path( + &info_path, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 || + (error = git_str_joinpath( + &sparse_checkout_file_path, info_path.ptr, + GIT_SPARSE_CHECKOUT_FILE)) < 0 || + (error = git_futils_readbuffer( + &contents, sparse_checkout_file_path.ptr)) < 0 || + (error = parse_sparse_checkout_buffer( + sparse_checkout, contents.ptr, contents.size)) < 0) { + goto cleanup; + } + +cleanup: + git_str_dispose(&contents); + git_str_dispose(&info_path); + git_str_dispose(&sparse_checkout_file_path); + return error; +} + +static int make_dummy_file_from_directory(git_str *path) +{ + if (path->ptr[path->size - 1] == '/') { + return git_str_putc(path, '-'); + } + return 0; +} + +static int path_is_tracked( + int *is_tracked, + git_sparse_checkout *sparse_checkout, + const char *pathname) +{ + int error = 0; + *is_tracked = 0; + git_str path = GIT_STR_INIT; + git_str dirname = GIT_STR_INIT; + + if (sparse_checkout->full_cone) { + *is_tracked = 1; + goto cleanup; + } + + // pathname could be directory or file + // If directory, append a random filename to the folder + // This is what Git does... + if ((error = git_str_sets(&path, pathname)) < 0 || + (error = make_dummy_file_from_directory(&path)) < 0 || + (error = git_fs_path_dirname_r(&dirname, path.ptr)) < 0) { + goto cleanup; + } + error = 0; + printf("dirname: %s\n", dirname.ptr); + + if (dirname.size == 1 && dirname.ptr[0] == '.') { + *is_tracked = 1; + goto cleanup; + } + + if (git_sparse_checkout_recursivemap_contains( + &sparse_checkout->recursivemap, path.ptr) || + git_sparse_checkout_parentmap_contains( + &sparse_checkout->parentmap, dirname.ptr)) { + *is_tracked = 1; + goto cleanup; + } + + while (dirname.size != 1 && dirname.ptr[0] != '.') { + if (git_sparse_checkout_recursivemap_contains( + &sparse_checkout->recursivemap, dirname.ptr)) { + *is_tracked = 1; + goto cleanup; + } + + if ((error = git_fs_path_dirname_r(&dirname, dirname.ptr)) < + 0) { + goto cleanup; + } + error = 0; + printf("dirname: %s\n", dirname.ptr); + } + +cleanup: + git_str_dispose(&dirname); + git_str_dispose(&path); + return error; +} + +int git_sparse_checkout__path_is_tracked( + int *is_tracked, + git_repository *repo, + const char *pathname) +{ + /* + * git: init_sparse_checkout_patterns -> get_sparse_checkout_patterns + * update_working_directory(struct pattern_list *pl) + */ + int error = 0; + int sparse_checkout_enabled = 0; + int sparse_checkout_cone_enabled = 0; + int ignore_case = 0; + git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN; + git_sparse_checkout sparse_checkout; + + GIT_ASSERT_ARG(repo); + GIT_ASSERT_ARG(is_tracked); + GIT_ASSERT_ARG(pathname); + + if ((error = git_repository__configmap_lookup( + &sparse_checkout_enabled, repo, + GIT_CONFIGMAP_SPARSECHECKOUT)) < 0 || + !sparse_checkout_enabled) { + *is_tracked = 1; + return error; + } + + if ((error = git_repository__configmap_lookup( + &sparse_checkout_cone_enabled, repo, + GIT_CONFIGMAP_SPARSECHECKOUTCONE)) < 0) { + *is_tracked = 1; + return error; + } + + if (!sparse_checkout_cone_enabled) { + /* + * We only support cone mode as non-cone mode is deprecated in + * Git. Should we error? What's the plan for Git? + */ + *is_tracked = 1; + git_error_set( + GIT_ERROR_CONFIG, + "non-cone mode for sparse checkout is not supported"); + error = GIT_ENOTSUPPORTED; + return error; + } + + /* + * https://git-scm.com/docs/git-sparse-checkout/2.42.0 + * Unless core.sparseCheckoutCone is explicitly set to false, Git will + * parse the sparse-checkout file expecting patterns of these types. Git + * will warn if the patterns do not match. If the patterns do match the + * expected format, then Git will use faster hash-based algorithms to + * compute inclusion in the sparse-checkout. If they do not match, git + * will behave as though core.sparseCheckoutCone was false, regardless + * of its setting. + */ + + /* In cone mode, .git/info/sparse-checkout file specifies directories to + * include. + * Parent pattern (include all files under "dir" but nothing below + * that): /dir/ + * !/dir/*\/ + * Recursive pattern (include everything in this folder): + * /dir/subdir/ + */ + + /* open and parse the file for the patterns, populating the parent and + * recursive hash maps */ + /* use attr_cache so gets cached on repository? Only real benefit + * appears to be reparsing when the file is updated. */ + + printf("\nCheck path is tracked: %s\n", pathname); + + if ((error = git_sparse_checkout__init(&sparse_checkout, repo)) < 0 || + (error = read_sparse_checkout_file(&sparse_checkout, repo)) < 0 || + (error = path_is_tracked(is_tracked, &sparse_checkout, pathname)) < + 0) + goto cleanup; + +cleanup: + git_sparse_checkout__dispose(&sparse_checkout); + return error; +} diff --git a/src/libgit2/sparse_checkout.h b/src/libgit2/sparse_checkout.h new file mode 100644 index 00000000000..924d04d0d46 --- /dev/null +++ b/src/libgit2/sparse_checkout.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_sparse_checkout_h__ +#define INCLUDE_sparse_checkout_h__ + +#include "common.h" + +#include "repository.h" + +#define GIT_SPARSE_CHECKOUT_FILE "sparse-checkout" + +extern int git_sparse_checkout__path_is_tracked( + int *is_tracked, + git_repository *repo, + const char *pathname); + +#endif diff --git a/tests/libgit2/sparse/config.c b/tests/libgit2/sparse/config.c new file mode 100644 index 00000000000..49ee798d6f7 --- /dev/null +++ b/tests/libgit2/sparse/config.c @@ -0,0 +1,46 @@ +#include "clar_libgit2.h" +#include "repository.h" + +static git_repository *g_repo = NULL; + +void test_sparse_config__initialize(void) +{ + g_repo = cl_git_sandbox_init("sparse"); +} + +void test_sparse_config__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_sparse_config__cone(void) +{ + git_config *config, *worktree; + int sparse_checkout = 0; + int sparse_checkout_cone = 0; + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_open_level( + &worktree, config, GIT_CONFIG_LEVEL_WORKTREE)); + + cl_git_pass(git_config_get_bool( + &sparse_checkout, worktree, "core.sparseCheckout")); + cl_assert_equal_b(true, sparse_checkout); + cl_git_pass(git_config_get_bool( + &sparse_checkout_cone, worktree, "core.sparseCheckoutCone")); + cl_assert_equal_b(true, sparse_checkout_cone); + + sparse_checkout = 0; + cl_git_pass(git_repository__configmap_lookup( + &sparse_checkout, g_repo, + GIT_CONFIGMAP_SPARSECHECKOUT)); + cl_assert_equal_b(true, sparse_checkout); + sparse_checkout_cone = 0; + cl_git_pass(git_repository__configmap_lookup( + &sparse_checkout_cone, g_repo, + GIT_CONFIGMAP_SPARSECHECKOUTCONE)); + cl_assert_equal_b(true, sparse_checkout_cone); + + git_config_free(worktree); + git_config_free(config); +} diff --git a/tests/libgit2/sparse/path.c b/tests/libgit2/sparse/path.c new file mode 100644 index 00000000000..544a3c2cf3a --- /dev/null +++ b/tests/libgit2/sparse/path.c @@ -0,0 +1,88 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "sparse_checkout.h" +#include "futils.h" + +static git_repository *g_repo = NULL; + +void test_sparse_path__initialize(void) +{ + g_repo = cl_git_sandbox_init("sparse"); +} + +void test_sparse_path__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +static void assert_is_tracked_( + bool expected, + const char *filepath, + const char *file, + const char *func, + int line) +{ + int is_tracked = 0; + + cl_git_expect( + git_sparse_checkout__path_is_tracked( + &is_tracked, g_repo, filepath), + 0, file, func, line); + + clar__assert_equal( + file, func, line, "expected != is_tracked", 1, "%d", + (int)(expected != 0), (int)(is_tracked != 0)); +} + +#define assert_is_tracked(expected, filepath) \ + assert_is_tracked_(expected, filepath, __FILE__, __func__, __LINE__) + +void test_sparse_path__basic(void) +{ + assert_is_tracked(true, "fileA.txt"); + assert_is_tracked(true, "fileB.txt"); + assert_is_tracked(true, "c"); + assert_is_tracked(true, "c/fileA.txt"); + assert_is_tracked(true, "c/d"); + assert_is_tracked(true, "c/d/fileA.txt"); + assert_is_tracked(true, "c/d/fileB.txt"); + assert_is_tracked(true, "c/d/e/fileB.txt"); + assert_is_tracked(false, "b/fileA.txt"); + assert_is_tracked(false, "b/d/fileA.txt"); + + assert_is_tracked(true, "c/"); + assert_is_tracked(true, "c/d/"); + assert_is_tracked(true, "c/d/e/"); + assert_is_tracked(true, "c/d/e/f/"); + assert_is_tracked(true, "c/d/e/f/g/"); + + assert_is_tracked(true, "b"); + assert_is_tracked(false, "b/"); + + assert_is_tracked(true, "doesnotexist.txt"); + assert_is_tracked(false, "does/not/exist.txt"); + + assert_is_tracked(false, "/"); +} + +void test_sparse_path__error_not_directory(void) +{ + int is_tracked = 0; + + cl_git_rewritefile( + "sparse/.git/info/sparse-checkout", "/a/b/c"); + cl_git_fail_with( + GIT_EINVALID, git_sparse_checkout__path_is_tracked( + &is_tracked, g_repo, "fileA.txt")); +} + +void test_sparse_path__error_space(void) +{ + int is_tracked = 0; + + cl_git_rewritefile("sparse/.git/info/sparse-checkout", " #comment"); + cl_git_fail_with( + GIT_EINVALID, git_sparse_checkout__path_is_tracked( + &is_tracked, g_repo, "fileA.txt")); +} diff --git a/tests/resources/sparse/.gitted/HEAD b/tests/resources/sparse/.gitted/HEAD new file mode 100644 index 00000000000..b870d82622c --- /dev/null +++ b/tests/resources/sparse/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/tests/resources/sparse/.gitted/config b/tests/resources/sparse/.gitted/config new file mode 100644 index 00000000000..2da1759d9bd --- /dev/null +++ b/tests/resources/sparse/.gitted/config @@ -0,0 +1,9 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true +[extensions] + worktreeConfig = true diff --git a/tests/resources/sparse/.gitted/config.worktree b/tests/resources/sparse/.gitted/config.worktree new file mode 100644 index 00000000000..703fe4cb62b --- /dev/null +++ b/tests/resources/sparse/.gitted/config.worktree @@ -0,0 +1,5 @@ +[core] + sparseCheckout = true + sparseCheckoutCone = true +[index] + sparse = false diff --git a/tests/resources/sparse/.gitted/description b/tests/resources/sparse/.gitted/description new file mode 100644 index 00000000000..498b267a8c7 --- /dev/null +++ b/tests/resources/sparse/.gitted/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/tests/resources/sparse/.gitted/index b/tests/resources/sparse/.gitted/index new file mode 100644 index 00000000000..2a81b4c394b Binary files /dev/null and b/tests/resources/sparse/.gitted/index differ diff --git a/tests/resources/sparse/.gitted/info/exclude b/tests/resources/sparse/.gitted/info/exclude new file mode 100644 index 00000000000..a5196d1be8f --- /dev/null +++ b/tests/resources/sparse/.gitted/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/tests/resources/sparse/.gitted/info/sparse-checkout b/tests/resources/sparse/.gitted/info/sparse-checkout new file mode 100644 index 00000000000..ab46f29b3d2 --- /dev/null +++ b/tests/resources/sparse/.gitted/info/sparse-checkout @@ -0,0 +1,15 @@ + +# commentA + +/* +# commentB + +!/*/ +/c/ + +!/c/*/ + +# commentC +/a/ +# commentD +/c/d/ diff --git a/tests/resources/sparse/.gitted/objects/02/69f9c363ca61eafe80a18381857f645b1a0e47 b/tests/resources/sparse/.gitted/objects/02/69f9c363ca61eafe80a18381857f645b1a0e47 new file mode 100644 index 00000000000..ddf6623deae Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/02/69f9c363ca61eafe80a18381857f645b1a0e47 differ diff --git a/tests/resources/sparse/.gitted/objects/1c/7a4d2ea77803076deb800b6ab85191dceb4b56 b/tests/resources/sparse/.gitted/objects/1c/7a4d2ea77803076deb800b6ab85191dceb4b56 new file mode 100644 index 00000000000..1b2ffd94995 Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/1c/7a4d2ea77803076deb800b6ab85191dceb4b56 differ diff --git a/tests/resources/sparse/.gitted/objects/33/c60f7155d1a2390854133026dc5f67ef8c77e0 b/tests/resources/sparse/.gitted/objects/33/c60f7155d1a2390854133026dc5f67ef8c77e0 new file mode 100644 index 00000000000..6d2709b7af5 --- /dev/null +++ b/tests/resources/sparse/.gitted/objects/33/c60f7155d1a2390854133026dc5f67ef8c77e0 @@ -0,0 +1,2 @@ +x¥ÍA +Â0…a×9ÅìÉÄê$ "¸A1 ¶´i MñúV¯àîñ/¾'9¥¾‚³¸©EŽ‚*Š™¢Rã[öíÞºè08Fk%†–„ /µË¼Dá)·‘çŽ'8I^ß}I\»w.ü“œÎ€ÔÐz‚­])³Öõ¼êŸŒ¹O}íy„ëÏ3ë4C3 \ No newline at end of file diff --git a/tests/resources/sparse/.gitted/objects/51/63c499306d855963c8a36d80abb8f3d0ceac35 b/tests/resources/sparse/.gitted/objects/51/63c499306d855963c8a36d80abb8f3d0ceac35 new file mode 100644 index 00000000000..be2b3665655 Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/51/63c499306d855963c8a36d80abb8f3d0ceac35 differ diff --git a/tests/resources/sparse/.gitted/objects/54/841cdb70bf59cf37273377a5dae0c17b56ce47 b/tests/resources/sparse/.gitted/objects/54/841cdb70bf59cf37273377a5dae0c17b56ce47 new file mode 100644 index 00000000000..ecffbf26eef Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/54/841cdb70bf59cf37273377a5dae0c17b56ce47 differ diff --git a/tests/resources/sparse/.gitted/objects/5e/325348137c382d932d6fe48d1352380a11c3d8 b/tests/resources/sparse/.gitted/objects/5e/325348137c382d932d6fe48d1352380a11c3d8 new file mode 100644 index 00000000000..65bf58a0803 Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/5e/325348137c382d932d6fe48d1352380a11c3d8 differ diff --git a/tests/resources/sparse/.gitted/objects/6b/4b3819187deca0bc2a1a35a8e023d4219843d8 b/tests/resources/sparse/.gitted/objects/6b/4b3819187deca0bc2a1a35a8e023d4219843d8 new file mode 100644 index 00000000000..7030c0d365d --- /dev/null +++ b/tests/resources/sparse/.gitted/objects/6b/4b3819187deca0bc2a1a35a8e023d4219843d8 @@ -0,0 +1 @@ +x+)JMU06g040031QHËÌIuÔ+©(ah{úöX¢ršPžU‰ðƒÈØ &›I® \ No newline at end of file diff --git a/tests/resources/sparse/.gitted/objects/6c/1ec57d81a7de748ba8b302d2192a100cd9b7c7 b/tests/resources/sparse/.gitted/objects/6c/1ec57d81a7de748ba8b302d2192a100cd9b7c7 new file mode 100644 index 00000000000..980e6745c9b Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/6c/1ec57d81a7de748ba8b302d2192a100cd9b7c7 differ diff --git a/tests/resources/sparse/.gitted/objects/78/fadb360d2ed95db233cc5aa17ad40d86ecbaf9 b/tests/resources/sparse/.gitted/objects/78/fadb360d2ed95db233cc5aa17ad40d86ecbaf9 new file mode 100644 index 00000000000..c9b8d8270ce Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/78/fadb360d2ed95db233cc5aa17ad40d86ecbaf9 differ diff --git a/tests/resources/sparse/.gitted/objects/7d/799f3d4592c4c0ec72a1a7cf3e71111b40a46a b/tests/resources/sparse/.gitted/objects/7d/799f3d4592c4c0ec72a1a7cf3e71111b40a46a new file mode 100644 index 00000000000..3c75a7f81b9 Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/7d/799f3d4592c4c0ec72a1a7cf3e71111b40a46a differ diff --git a/tests/resources/sparse/.gitted/objects/86/e5edc6612366127e2e3a7413e0595db10034b3 b/tests/resources/sparse/.gitted/objects/86/e5edc6612366127e2e3a7413e0595db10034b3 new file mode 100644 index 00000000000..e5ba127bb0c Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/86/e5edc6612366127e2e3a7413e0595db10034b3 differ diff --git a/tests/resources/sparse/.gitted/objects/8e/5fb0934b7422e90be895b355708f8836f2ab41 b/tests/resources/sparse/.gitted/objects/8e/5fb0934b7422e90be895b355708f8836f2ab41 new file mode 100644 index 00000000000..c9a99d2e439 Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/8e/5fb0934b7422e90be895b355708f8836f2ab41 differ diff --git a/tests/resources/sparse/.gitted/objects/8e/6ae1f061fc4b58e1d8a2725fb707863c0ce7f2 b/tests/resources/sparse/.gitted/objects/8e/6ae1f061fc4b58e1d8a2725fb707863c0ce7f2 new file mode 100644 index 00000000000..49a02873370 Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/8e/6ae1f061fc4b58e1d8a2725fb707863c0ce7f2 differ diff --git a/tests/resources/sparse/.gitted/objects/a9/6055bb0e7a0e7af3f1783fbfad767479950577 b/tests/resources/sparse/.gitted/objects/a9/6055bb0e7a0e7af3f1783fbfad767479950577 new file mode 100644 index 00000000000..99d5a53e6eb Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/a9/6055bb0e7a0e7af3f1783fbfad767479950577 differ diff --git a/tests/resources/sparse/.gitted/objects/e1/ccb792d95e6198f341ece98a7795eac3ca1b6f b/tests/resources/sparse/.gitted/objects/e1/ccb792d95e6198f341ece98a7795eac3ca1b6f new file mode 100644 index 00000000000..e9b8f419e5a Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/e1/ccb792d95e6198f341ece98a7795eac3ca1b6f differ diff --git a/tests/resources/sparse/.gitted/objects/ef/2e5779adebd7829227d5477f4cd737dcc25629 b/tests/resources/sparse/.gitted/objects/ef/2e5779adebd7829227d5477f4cd737dcc25629 new file mode 100644 index 00000000000..23adc98e42c Binary files /dev/null and b/tests/resources/sparse/.gitted/objects/ef/2e5779adebd7829227d5477f4cd737dcc25629 differ diff --git a/tests/resources/sparse/.gitted/refs/heads/main b/tests/resources/sparse/.gitted/refs/heads/main new file mode 100644 index 00000000000..5981dcdc2cd --- /dev/null +++ b/tests/resources/sparse/.gitted/refs/heads/main @@ -0,0 +1 @@ +33c60f7155d1a2390854133026dc5f67ef8c77e0 diff --git a/tests/resources/sparse/a/fileA.txt b/tests/resources/sparse/a/fileA.txt new file mode 100644 index 00000000000..0269f9c363c Binary files /dev/null and b/tests/resources/sparse/a/fileA.txt differ diff --git a/tests/resources/sparse/a/fileB.txt b/tests/resources/sparse/a/fileB.txt new file mode 100644 index 00000000000..8e5fb0934b7 Binary files /dev/null and b/tests/resources/sparse/a/fileB.txt differ diff --git a/tests/resources/sparse/c/d/fileA.txt b/tests/resources/sparse/c/d/fileA.txt new file mode 100644 index 00000000000..5e325348137 Binary files /dev/null and b/tests/resources/sparse/c/d/fileA.txt differ diff --git a/tests/resources/sparse/c/d/fileB.txt b/tests/resources/sparse/c/d/fileB.txt new file mode 100644 index 00000000000..54841cdb70b Binary files /dev/null and b/tests/resources/sparse/c/d/fileB.txt differ diff --git a/tests/resources/sparse/c/fileA.txt b/tests/resources/sparse/c/fileA.txt new file mode 100644 index 00000000000..5163c499306 Binary files /dev/null and b/tests/resources/sparse/c/fileA.txt differ diff --git a/tests/resources/sparse/fileA.txt b/tests/resources/sparse/fileA.txt new file mode 100644 index 00000000000..ef2e5779ade Binary files /dev/null and b/tests/resources/sparse/fileA.txt differ diff --git a/tests/resources/sparse/fileB.txt b/tests/resources/sparse/fileB.txt new file mode 100644 index 00000000000..e1ccb792d95 Binary files /dev/null and b/tests/resources/sparse/fileB.txt differ