From: Derrick Stolee <dstolee@xxxxxxxxxxxxx> In anticipation of some new features in the sparse-checkout space, create a new sparse-checkout.c file at root to store the implementation of these features. Several helper methods from builtin/sparse-checkout.c are moved to this file. Signed-off-by: Derrick Stolee <dstolee@xxxxxxxxxxxxx> --- Makefile | 1 + builtin/sparse-checkout.c | 225 +----------------------------------- sparse-checkout.c | 233 ++++++++++++++++++++++++++++++++++++++ sparse-checkout.h | 16 +++ 4 files changed, 251 insertions(+), 224 deletions(-) create mode 100644 sparse-checkout.c create mode 100644 sparse-checkout.h diff --git a/Makefile b/Makefile index 9804a0758b2..2e8029b8026 100644 --- a/Makefile +++ b/Makefile @@ -985,6 +985,7 @@ LIB_OBJS += sha1-name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o LIB_OBJS += sigchain.o +LIB_OBJS += sparse-checkout.o LIB_OBJS += split-index.o LIB_OBJS += stable-qsort.o LIB_OBJS += strbuf.o diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 95d08824172..fd247e428e4 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -9,11 +9,11 @@ #include "string-list.h" #include "cache.h" #include "cache-tree.h" -#include "lockfile.h" #include "resolve-undo.h" #include "unpack-trees.h" #include "wt-status.h" #include "quote.h" +#include "sparse-checkout.h" static const char *empty_base = ""; @@ -22,30 +22,6 @@ static char const * const builtin_sparse_checkout_usage[] = { NULL }; -static char *get_sparse_checkout_filename(void) -{ - return git_pathdup("info/sparse-checkout"); -} - -static void write_patterns_to_file(FILE *fp, struct pattern_list *pl) -{ - int i; - - for (i = 0; i < pl->nr; i++) { - struct path_pattern *p = pl->patterns[i]; - - if (p->flags & PATTERN_FLAG_NEGATIVE) - fprintf(fp, "!"); - - fprintf(fp, "%s", p->pattern); - - if (p->flags & PATTERN_FLAG_MUSTBEDIR) - fprintf(fp, "/"); - - fprintf(fp, "\n"); - } -} - static int sparse_checkout_list(int argc, const char **argv) { struct pattern_list pl; @@ -92,153 +68,6 @@ static int sparse_checkout_list(int argc, const char **argv) return 0; } -static int update_working_directory(struct pattern_list *pl) -{ - enum update_sparsity_result result; - struct unpack_trees_options o; - struct lock_file lock_file = LOCK_INIT; - struct repository *r = the_repository; - - memset(&o, 0, sizeof(o)); - o.verbose_update = isatty(2); - o.update = 1; - o.head_idx = -1; - o.src_index = r->index; - o.dst_index = r->index; - o.skip_sparse_checkout = 0; - o.pl = pl; - - setup_work_tree(); - - repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR); - - setup_unpack_trees_porcelain(&o, "sparse-checkout"); - result = update_sparsity(&o); - clear_unpack_trees_porcelain(&o); - - if (result == UPDATE_SPARSITY_WARNINGS) - /* - * We don't do any special handling of warnings from untracked - * files in the way or dirty entries that can't be removed. - */ - result = UPDATE_SPARSITY_SUCCESS; - if (result == UPDATE_SPARSITY_SUCCESS) - write_locked_index(r->index, &lock_file, COMMIT_LOCK); - else - rollback_lock_file(&lock_file); - - return result; -} - -static char *escaped_pattern(char *pattern) -{ - char *p = pattern; - struct strbuf final = STRBUF_INIT; - - while (*p) { - if (is_glob_special(*p)) - strbuf_addch(&final, '\\'); - - strbuf_addch(&final, *p); - p++; - } - - return strbuf_detach(&final, NULL); -} - -static void write_cone_to_file(FILE *fp, struct pattern_list *pl) -{ - int i; - struct pattern_entry *pe; - struct hashmap_iter iter; - struct string_list sl = STRING_LIST_INIT_DUP; - struct strbuf parent_pattern = STRBUF_INIT; - - hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) { - if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL)) - continue; - - if (!hashmap_contains_parent(&pl->recursive_hashmap, - pe->pattern, - &parent_pattern)) - string_list_insert(&sl, pe->pattern); - } - - string_list_sort(&sl); - string_list_remove_duplicates(&sl, 0); - - fprintf(fp, "/*\n!/*/\n"); - - for (i = 0; i < sl.nr; i++) { - char *pattern = escaped_pattern(sl.items[i].string); - - if (strlen(pattern)) - fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern); - free(pattern); - } - - string_list_clear(&sl, 0); - - hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) { - if (!hashmap_contains_parent(&pl->recursive_hashmap, - pe->pattern, - &parent_pattern)) - string_list_insert(&sl, pe->pattern); - } - - strbuf_release(&parent_pattern); - - string_list_sort(&sl); - string_list_remove_duplicates(&sl, 0); - - for (i = 0; i < sl.nr; i++) { - char *pattern = escaped_pattern(sl.items[i].string); - fprintf(fp, "%s/\n", pattern); - free(pattern); - } -} - -static int write_patterns_and_update(struct pattern_list *pl) -{ - char *sparse_filename; - FILE *fp; - int fd; - struct lock_file lk = LOCK_INIT; - int result; - - sparse_filename = get_sparse_checkout_filename(); - - if (safe_create_leading_directories(sparse_filename)) - die(_("failed to create directory for sparse-checkout file")); - - fd = hold_lock_file_for_update(&lk, sparse_filename, - LOCK_DIE_ON_ERROR); - - result = update_working_directory(pl); - if (result) { - rollback_lock_file(&lk); - free(sparse_filename); - clear_pattern_list(pl); - update_working_directory(NULL); - return result; - } - - fp = xfdopen(fd, "w"); - - if (core_sparse_checkout_cone) - write_cone_to_file(fp, pl); - else - write_patterns_to_file(fp, pl); - - fflush(fp); - commit_lock_file(&lk); - - free(sparse_filename); - clear_pattern_list(pl); - - return 0; -} - enum sparse_checkout_mode { MODE_NO_PATTERNS = 0, MODE_ALL_PATTERNS = 1, @@ -339,58 +168,6 @@ static int sparse_checkout_init(int argc, const char **argv) return write_patterns_and_update(&pl); } -static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path) -{ - struct pattern_entry *e = xmalloc(sizeof(*e)); - e->patternlen = path->len; - e->pattern = strbuf_detach(path, NULL); - hashmap_entry_init(&e->ent, - ignore_case ? - strihash(e->pattern) : - strhash(e->pattern)); - - hashmap_add(&pl->recursive_hashmap, &e->ent); - - while (e->patternlen) { - char *slash = strrchr(e->pattern, '/'); - char *oldpattern = e->pattern; - size_t newlen; - - if (slash == e->pattern) - break; - - newlen = slash - e->pattern; - e = xmalloc(sizeof(struct pattern_entry)); - e->patternlen = newlen; - e->pattern = xstrndup(oldpattern, newlen); - hashmap_entry_init(&e->ent, - ignore_case ? - strihash(e->pattern) : - strhash(e->pattern)); - - if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL)) - hashmap_add(&pl->parent_hashmap, &e->ent); - } -} - -static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl) -{ - strbuf_trim(line); - - strbuf_trim_trailing_dir_sep(line); - - if (strbuf_normalize_path(line)) - die(_("could not normalize path %s"), line->buf); - - if (!line->len) - return; - - if (line->buf[0] != '/') - strbuf_insertstr(line, 0, "/"); - - insert_recursive_pattern(pl, line); -} - static char const * const builtin_sparse_checkout_set_usage[] = { N_("git sparse-checkout (set|add) (--stdin | <patterns>)"), NULL diff --git a/sparse-checkout.c b/sparse-checkout.c new file mode 100644 index 00000000000..5ada307b42c --- /dev/null +++ b/sparse-checkout.c @@ -0,0 +1,233 @@ +#include "cache.h" +#include "config.h" +#include "dir.h" +#include "lockfile.h" +#include "quote.h" +#include "repository.h" +#include "sparse-checkout.h" +#include "strbuf.h" +#include "string-list.h" +#include "unpack-trees.h" + +char *get_sparse_checkout_filename(void) +{ + return git_pathdup("info/sparse-checkout"); +} + +void write_patterns_to_file(FILE *fp, struct pattern_list *pl) +{ + int i; + + for (i = 0; i < pl->nr; i++) { + struct path_pattern *p = pl->patterns[i]; + + if (p->flags & PATTERN_FLAG_NEGATIVE) + fprintf(fp, "!"); + + fprintf(fp, "%s", p->pattern); + + if (p->flags & PATTERN_FLAG_MUSTBEDIR) + fprintf(fp, "/"); + + fprintf(fp, "\n"); + } +} + +int update_working_directory(struct pattern_list *pl) +{ + enum update_sparsity_result result; + struct unpack_trees_options o; + struct lock_file lock_file = LOCK_INIT; + struct repository *r = the_repository; + + memset(&o, 0, sizeof(o)); + o.verbose_update = isatty(2); + o.update = 1; + o.head_idx = -1; + o.src_index = r->index; + o.dst_index = r->index; + o.skip_sparse_checkout = 0; + o.pl = pl; + + setup_work_tree(); + + repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR); + + setup_unpack_trees_porcelain(&o, "sparse-checkout"); + result = update_sparsity(&o); + clear_unpack_trees_porcelain(&o); + + if (result == UPDATE_SPARSITY_WARNINGS) + /* + * We don't do any special handling of warnings from untracked + * files in the way or dirty entries that can't be removed. + */ + result = UPDATE_SPARSITY_SUCCESS; + if (result == UPDATE_SPARSITY_SUCCESS) + write_locked_index(r->index, &lock_file, COMMIT_LOCK); + else + rollback_lock_file(&lock_file); + + return result; +} + +static char *escaped_pattern(char *pattern) +{ + char *p = pattern; + struct strbuf final = STRBUF_INIT; + + while (*p) { + if (is_glob_special(*p)) + strbuf_addch(&final, '\\'); + + strbuf_addch(&final, *p); + p++; + } + + return strbuf_detach(&final, NULL); +} + +static void write_cone_to_file(FILE *fp, struct pattern_list *pl) +{ + int i; + struct pattern_entry *pe; + struct hashmap_iter iter; + struct string_list sl = STRING_LIST_INIT_DUP; + struct strbuf parent_pattern = STRBUF_INIT; + + hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) { + if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL)) + continue; + + if (!hashmap_contains_parent(&pl->recursive_hashmap, + pe->pattern, + &parent_pattern)) + string_list_insert(&sl, pe->pattern); + } + + string_list_sort(&sl); + string_list_remove_duplicates(&sl, 0); + + fprintf(fp, "/*\n!/*/\n"); + + for (i = 0; i < sl.nr; i++) { + char *pattern = escaped_pattern(sl.items[i].string); + + if (strlen(pattern)) + fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern); + free(pattern); + } + + string_list_clear(&sl, 0); + + hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) { + if (!hashmap_contains_parent(&pl->recursive_hashmap, + pe->pattern, + &parent_pattern)) + string_list_insert(&sl, pe->pattern); + } + + strbuf_release(&parent_pattern); + + string_list_sort(&sl); + string_list_remove_duplicates(&sl, 0); + + for (i = 0; i < sl.nr; i++) { + char *pattern = escaped_pattern(sl.items[i].string); + fprintf(fp, "%s/\n", pattern); + free(pattern); + } +} + +int write_patterns_and_update(struct pattern_list *pl) +{ + char *sparse_filename; + FILE *fp; + int fd; + struct lock_file lk = LOCK_INIT; + int result; + + sparse_filename = get_sparse_checkout_filename(); + + if (safe_create_leading_directories(sparse_filename)) + die(_("failed to create directory for sparse-checkout file")); + + fd = hold_lock_file_for_update(&lk, sparse_filename, + LOCK_DIE_ON_ERROR); + + result = update_working_directory(pl); + if (result) { + rollback_lock_file(&lk); + free(sparse_filename); + clear_pattern_list(pl); + update_working_directory(NULL); + return result; + } + + fp = xfdopen(fd, "w"); + + if (core_sparse_checkout_cone) + write_cone_to_file(fp, pl); + else + write_patterns_to_file(fp, pl); + + fflush(fp); + commit_lock_file(&lk); + + free(sparse_filename); + clear_pattern_list(pl); + + return 0; +} + +void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path) +{ + struct pattern_entry *e = xmalloc(sizeof(*e)); + e->patternlen = path->len; + e->pattern = strbuf_detach(path, NULL); + hashmap_entry_init(&e->ent, + ignore_case ? + strihash(e->pattern) : + strhash(e->pattern)); + + hashmap_add(&pl->recursive_hashmap, &e->ent); + + while (e->patternlen) { + char *slash = strrchr(e->pattern, '/'); + char *oldpattern = e->pattern; + size_t newlen; + + if (slash == e->pattern) + break; + + newlen = slash - e->pattern; + e = xmalloc(sizeof(struct pattern_entry)); + e->patternlen = newlen; + e->pattern = xstrndup(oldpattern, newlen); + hashmap_entry_init(&e->ent, + ignore_case ? + strihash(e->pattern) : + strhash(e->pattern)); + + if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL)) + hashmap_add(&pl->parent_hashmap, &e->ent); + } +} + +void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl) +{ + strbuf_trim(line); + + strbuf_trim_trailing_dir_sep(line); + + if (strbuf_normalize_path(line)) + die(_("could not normalize path %s"), line->buf); + + if (!line->len) + return; + + if (line->buf[0] != '/') + strbuf_insertstr(line, 0, "/"); + + insert_recursive_pattern(pl, line); +} diff --git a/sparse-checkout.h b/sparse-checkout.h new file mode 100644 index 00000000000..4148832760d --- /dev/null +++ b/sparse-checkout.h @@ -0,0 +1,16 @@ +#ifndef SPARSE_CHECKOUT_H +#define SPARSE_CHECKOUT_H + +#include "cache.h" +#include "repository.h" + +struct pattern_list; + +char *get_sparse_checkout_filename(void); +void write_patterns_to_file(FILE *fp, struct pattern_list *pl); +int update_working_directory(struct pattern_list *pl); +int write_patterns_and_update(struct pattern_list *pl); +void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path); +void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl); + +#endif -- gitgitgadget