From 2fbce0bfaca90dd71a2cc10a7fa82088150e04b7 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 2 Feb 2015 22:18:38 -0500 Subject: checkout test: ensure .gitattributes lifecycle The .gitattributes cache should not reload .gitattributes in the middle of checking out, only between checkout operations. Otherwise, we'll spend all our time stat'ing and read'ing the gitattributes. --- tests/checkout/tree.c | 50 +++++++++++++++++++++ .../6f/d5c7dd2ab27b48c493023f794be09861e9045f | 1 + .../c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe | Bin 0 -> 192 bytes .../e3/6900c3224db4adf4c7f7a09d4ac80247978a13 | Bin 0 -> 59 bytes tests/resources/testrepo/.gitted/refs/heads/ident | 1 + 5 files changed, 52 insertions(+) create mode 100644 tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f create mode 100644 tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe create mode 100644 tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13 create mode 100644 tests/resources/testrepo/.gitted/refs/heads/ident diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index f37e6d359..42f32a8d9 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -1130,3 +1130,53 @@ void test_checkout_tree__can_collect_perfdata(void) git_object_free(obj); } + +void update_attr_callback( + const char *path, + size_t completed_steps, + size_t total_steps, + void *payload) +{ + if (path && strcmp(path, "ident1.txt") == 0) + cl_git_write2file("testrepo/.gitattributes", + "*.txt ident\n", 12, O_RDWR|O_CREAT, 0666); +} + +void test_checkout_tree__caches_attributes_during_checkout(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid oid; + git_object *obj = NULL; + git_buf ident1 = GIT_BUF_INIT, ident2 = GIT_BUF_INIT; + char *ident_paths[] = { "ident1.txt", "ident2.txt" }; + + opts.progress_cb = update_attr_callback; + + assert_on_branch(g_repo, "master"); + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + opts.paths.strings = ident_paths; + opts.paths.count = 2; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/ident")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + + cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt")); + cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt")); + + cl_assert_equal_strn(ident1.ptr, "# $Id$", 6); + cl_assert_equal_strn(ident2.ptr, "# $Id$", 6); + + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + + cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt")); + cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt")); + + cl_assert_equal_strn(ident1.ptr, "# $Id: ", 7); + cl_assert_equal_strn(ident2.ptr, "# $Id: ", 7); + + git_buf_free(&ident1); + git_buf_free(&ident2); + git_object_free(obj); +} diff --git a/tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f b/tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f new file mode 100644 index 000000000..7f0c6fe4e --- /dev/null +++ b/tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f @@ -0,0 +1 @@ +xA!D}G Mr\m[F11gȢ(GRr3Co"v^hq<7AY{"&$DSg([B!ΡwƳYgl$%Eֲ'\d_w-[k'1hZt B&;:A"m%V \ No newline at end of file diff --git a/tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe b/tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe new file mode 100644 index 000000000..0975f7fdf Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe differ diff --git a/tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13 b/tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13 new file mode 100644 index 000000000..e74291fc1 Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13 differ diff --git a/tests/resources/testrepo/.gitted/refs/heads/ident b/tests/resources/testrepo/.gitted/refs/heads/ident new file mode 100644 index 000000000..2cfd880a3 --- /dev/null +++ b/tests/resources/testrepo/.gitted/refs/heads/ident @@ -0,0 +1 @@ +6fd5c7dd2ab27b48c493023f794be09861e9045f -- cgit v1.2.1 From 60561d54468d7097e04466fd8125be5231cea637 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Feb 2015 03:36:07 +0000 Subject: tests: update for new test data --- tests/refs/list.c | 4 ++-- tests/repo/iterator.c | 3 ++- tests/revwalk/basic.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/refs/list.c b/tests/refs/list.c index de5c0fd3d..374943b05 100644 --- a/tests/refs/list.c +++ b/tests/refs/list.c @@ -36,7 +36,7 @@ void test_refs_list__all(void) /* We have exactly 12 refs in total if we include the packed ones: * there is a reference that exists both in the packfile and as * loose, but we only list it once */ - cl_assert_equal_i((int)ref_list.count, 14); + cl_assert_equal_i((int)ref_list.count, 15); git_strarray_free(&ref_list); } @@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten "144344043ba4d4a405da03de3844aa829ae8be0e\n"); cl_git_pass(git_reference_list(&ref_list, g_repo)); - cl_assert_equal_i((int)ref_list.count, 14); + cl_assert_equal_i((int)ref_list.count, 15); git_strarray_free(&ref_list); } diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index a1aa4b2c7..26e8954fe 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -906,6 +906,7 @@ void test_repo_iterator__fs2(void) static const char *expect_base[] = { "heads/br2", "heads/dir", + "heads/ident", "heads/long-file-name", "heads/master", "heads/packed-test", @@ -923,7 +924,7 @@ void test_repo_iterator__fs2(void) cl_git_pass(git_iterator_for_filesystem( &i, "testrepo/.git/refs", 0, NULL, NULL)); - expect_iterator_items(i, 12, expect_base, 12, expect_base); + expect_iterator_items(i, 13, expect_base, 13, expect_base); git_iterator_free(i); } diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c index ff05fa249..4ae952775 100644 --- a/tests/revwalk/basic.c +++ b/tests/revwalk/basic.c @@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void) /* walking */; /* git log --branches --oneline | wc -l => 16 */ - cl_assert_equal_i(17, i); + cl_assert_equal_i(18, i); } void test_revwalk_basic__push_head(void) -- cgit v1.2.1 From 9f779aacdd950fd53a407da615ca60d628e31d35 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 29 Jan 2015 14:40:55 -0600 Subject: attrcache: don't re-read attrs during checkout During checkout, assume that the .gitattributes files aren't modified during the checkout. Instead, create an "attribute session" during checkout. Assume that attribute data read in the same checkout "session" hasn't been modified since the checkout started. (But allow subsequent checkouts to invalidate the cache.) Further, cache nonexistent git_attr_file data even when .gitattributes files are not found to prevent re-scanning for nonexistent files. --- src/attr.c | 66 +++++++++++++++++++++++++++++++++++++++----------------- src/attr_file.c | 57 +++++++++++++++++++++++++++++++++--------------- src/attr_file.h | 44 ++++++++++++++++++++++++++++--------- src/attrcache.c | 5 +++-- src/attrcache.h | 1 + src/checkout.c | 13 ++++++++--- src/filter.c | 29 ++++++++++++++++++++----- src/filter.h | 10 +++++++++ src/ignore.c | 4 ++-- src/repository.h | 2 ++ 10 files changed, 172 insertions(+), 59 deletions(-) diff --git a/src/attr.c b/src/attr.c index a02172689..df139e082 100644 --- a/src/attr.c +++ b/src/attr.c @@ -29,6 +29,7 @@ git_attr_t git_attr_value(const char *attr) static int collect_attr_files( git_repository *repo, + git_attr_session *attr_session, uint32_t flags, const char *path, git_vector *files); @@ -57,7 +58,7 @@ int git_attr_get( if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; - if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0) goto cleanup; memset(&attr, 0, sizeof(attr)); @@ -90,9 +91,10 @@ typedef struct { git_attr_assignment *found; } attr_get_many_info; -int git_attr_get_many( +int git_attr_get_many_with_session( const char **values, git_repository *repo, + git_attr_session *attr_session, uint32_t flags, const char *pathname, size_t num_attr, @@ -115,7 +117,7 @@ int git_attr_get_many( if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; - if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0) goto cleanup; info = git__calloc(num_attr, sizeof(attr_get_many_info)); @@ -161,6 +163,17 @@ cleanup: return error; } +int git_attr_get_many( + const char **values, + git_repository *repo, + uint32_t flags, + const char *pathname, + size_t num_attr, + const char **names) +{ + return git_attr_get_many_with_session( + values, repo, NULL, flags, pathname, num_attr, names); +} int git_attr_foreach( git_repository *repo, @@ -183,7 +196,7 @@ int git_attr_foreach( if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; - if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0 || + if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 || (error = git_strmap_alloc(&seen)) < 0) goto cleanup; @@ -219,6 +232,7 @@ cleanup: static int preload_attr_file( git_repository *repo, + git_attr_session *attr_session, git_attr_file_source source, const char *base, const char *file) @@ -229,19 +243,22 @@ static int preload_attr_file( if (!file) return 0; if (!(error = git_attr_cache__get( - &preload, repo, source, base, file, git_attr_file__parse_buffer))) + &preload, repo, attr_session, source, base, file, git_attr_file__parse_buffer))) git_attr_file__free(preload); return error; } -static int attr_setup(git_repository *repo) +static int attr_setup(git_repository *repo, git_attr_session *attr_session) { int error = 0; const char *workdir = git_repository_workdir(repo); git_index *idx = NULL; git_buf sys = GIT_BUF_INIT; + if (attr_session && attr_session->setup) + return 0; + if ((error = git_attr_cache__init(repo)) < 0) return error; @@ -251,7 +268,7 @@ static int attr_setup(git_repository *repo) if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) { error = preload_attr_file( - repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); + repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); git_buf_free(&sys); } if (error < 0) { @@ -263,25 +280,28 @@ static int attr_setup(git_repository *repo) } if ((error = preload_attr_file( - repo, GIT_ATTR_FILE__FROM_FILE, + repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0) return error; if ((error = preload_attr_file( - repo, GIT_ATTR_FILE__FROM_FILE, + repo, attr_session, GIT_ATTR_FILE__FROM_FILE, git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0) return error; if (workdir != NULL && (error = preload_attr_file( - repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0) + repo, attr_session, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0) return error; if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || (error = preload_attr_file( - repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0) + repo, attr_session, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0) return error; + if (attr_session) + attr_session->setup = 1; + return error; } @@ -321,6 +341,7 @@ int git_attr_add_macro( typedef struct { git_repository *repo; + git_attr_session *attr_session; uint32_t flags; const char *workdir; git_index *index; @@ -356,6 +377,7 @@ static int attr_decide_sources( static int push_attr_file( git_repository *repo, + git_attr_session *attr_session, git_vector *list, git_attr_file_source source, const char *base, @@ -364,8 +386,9 @@ static int push_attr_file( int error = 0; git_attr_file *file = NULL; - error = git_attr_cache__get( - &file, repo, source, base, filename, git_attr_file__parse_buffer); + error = git_attr_cache__get(&file, repo, attr_session, + source, base, filename, git_attr_file__parse_buffer); + if (error < 0) return error; @@ -387,8 +410,8 @@ static int push_one_attr(void *ref, const char *path) info->flags, info->workdir != NULL, info->index != NULL, src); for (i = 0; !error && i < n_src; ++i) - error = push_attr_file( - info->repo, info->files, src[i], path, GIT_ATTR_FILE); + error = push_attr_file(info->repo, info->attr_session, + info->files, src[i], path, GIT_ATTR_FILE); return error; } @@ -407,6 +430,7 @@ static void release_attr_files(git_vector *files) static int collect_attr_files( git_repository *repo, + git_attr_session *attr_session, uint32_t flags, const char *path, git_vector *files) @@ -416,7 +440,7 @@ static int collect_attr_files( const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; - if ((error = attr_setup(repo)) < 0) + if ((error = attr_setup(repo, attr_session)) < 0) return error; /* Resolve path in a non-bare repo */ @@ -435,12 +459,13 @@ static int collect_attr_files( */ error = push_attr_file( - repo, files, GIT_ATTR_FILE__FROM_FILE, + repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, git_repository_path(repo), GIT_ATTR_FILE_INREPO); if (error < 0) goto cleanup; - info.repo = repo; + info.repo = repo; + info.attr_session = attr_session; info.flags = flags; info.workdir = workdir; if (git_repository_index__weakptr(&info.index, repo) < 0) @@ -457,7 +482,7 @@ static int collect_attr_files( if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { error = push_attr_file( - repo, files, GIT_ATTR_FILE__FROM_FILE, + repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, NULL, git_repository_attr_cache(repo)->cfg_attr_file); if (error < 0) goto cleanup; @@ -467,7 +492,8 @@ static int collect_attr_files( error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); if (!error) error = push_attr_file( - repo, files, GIT_ATTR_FILE__FROM_FILE, NULL, dir.ptr); + repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, + NULL, dir.ptr); else if (error == GIT_ENOTFOUND) { giterr_clear(); error = 0; diff --git a/src/attr_file.c b/src/attr_file.c index 5b008b0e3..9896df4f9 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -96,6 +96,7 @@ static int attr_file_oid_from_index( int git_attr_file__load( git_attr_file **out, git_repository *repo, + git_attr_session *attr_session, git_attr_file_entry *entry, git_attr_file_source source, git_attr_file_parser parser) @@ -105,6 +106,7 @@ int git_attr_file__load( git_buf content = GIT_BUF_INIT; git_attr_file *file; struct stat st; + bool nonexistent = false; *out = NULL; @@ -127,22 +129,16 @@ int git_attr_file__load( case GIT_ATTR_FILE__FROM_FILE: { int fd; - if (p_stat(entry->fullpath, &st) < 0) - return git_path_set_error(errno, entry->fullpath, "stat"); - if (S_ISDIR(st.st_mode)) - return GIT_ENOTFOUND; - - /* For open or read errors, return ENOTFOUND to skip item */ + /* For open or read errors, pretend that we got ENOTFOUND. */ /* TODO: issue warning when warning API is available */ - if ((fd = git_futils_open_ro(entry->fullpath)) < 0) - return GIT_ENOTFOUND; - - error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size); - p_close(fd); - - if (error < 0) - return GIT_ENOTFOUND; + if (p_stat(entry->fullpath, &st) < 0 || + S_ISDIR(st.st_mode) || + (fd = git_futils_open_ro(entry->fullpath)) < 0 || + (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0) + nonexistent = true; + else + p_close(fd); break; } @@ -154,13 +150,21 @@ int git_attr_file__load( if ((error = git_attr_file__new(&file, entry, source)) < 0) goto cleanup; + /* store the key of the attr_reader; don't bother with cache + * invalidation during the same attr reader session. + */ + if (attr_session) + file->session_key = attr_session->key; + if (parser && (error = parser(repo, file, git_buf_cstr(&content))) < 0) { git_attr_file__free(file); goto cleanup; } - /* write cache breaker */ - if (source == GIT_ATTR_FILE__FROM_INDEX) + /* write cache breakers */ + if (nonexistent) + file->nonexistent = 1; + else if (source == GIT_ATTR_FILE__FROM_INDEX) git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); else if (source == GIT_ATTR_FILE__FROM_FILE) git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); @@ -175,11 +179,22 @@ cleanup: return error; } -int git_attr_file__out_of_date(git_repository *repo, git_attr_file *file) +int git_attr_file__out_of_date( + git_repository *repo, + git_attr_session *attr_session, + git_attr_file *file) { if (!file) return 1; + /* we are never out of date if we just created this data in the same + * attr_session; otherwise, nonexistent files must be invalidated + */ + if (attr_session && attr_session->key == file->session_key) + return 0; + else if (file->nonexistent) + return 1; + switch (file->source) { case GIT_ATTR_FILE__IN_MEMORY: return 0; @@ -831,3 +846,11 @@ void git_attr_rule__free(git_attr_rule *rule) git__free(rule); } +int git_attr_session__init(git_attr_session *session, git_repository *repo) +{ + assert(repo); + + session->key = git_atomic_inc(&repo->attr_session_key); + + return 0; +} diff --git a/src/attr_file.h b/src/attr_file.h index 93de84d12..b8e37256b 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -38,11 +38,11 @@ GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR) typedef enum { - GIT_ATTR_FILE__IN_MEMORY = 0, - GIT_ATTR_FILE__FROM_FILE = 1, - GIT_ATTR_FILE__FROM_INDEX = 2, + GIT_ATTR_FILE__IN_MEMORY = 0, + GIT_ATTR_FILE__FROM_FILE = 1, + GIT_ATTR_FILE__FROM_INDEX = 2, - GIT_ATTR_FILE_NUM_SOURCES = 3 + GIT_ATTR_FILE_NUM_SOURCES = 3 } git_attr_file_source; extern const char *git_attr__true; @@ -84,6 +84,8 @@ typedef struct { git_attr_file_source source; git_vector rules; /* vector of or */ git_pool pool; + unsigned int nonexistent:1; + int session_key; union { git_oid oid; git_futils_filestamp stamp; @@ -96,11 +98,6 @@ struct git_attr_file_entry { char fullpath[GIT_FLEX_ARRAY]; }; -typedef int (*git_attr_file_parser)( - git_repository *repo, - git_attr_file *file, - const char *data); - typedef struct { git_buf full; char *path; @@ -108,6 +105,32 @@ typedef struct { int is_dir; } git_attr_path; +/* A git_attr_session can provide an "instance" of reading, to prevent cache + * invalidation during a single operation instance (like checkout). + */ + +typedef struct { + int key; + unsigned int setup; +} git_attr_session; + +extern int git_attr_session__init(git_attr_session *attr_session, git_repository *repo); +extern void git_attr_session__free(git_attr_session *session); + +extern int git_attr_get_many_with_session( + const char **values_out, + git_repository *repo, + git_attr_session *attr_session, + uint32_t flags, + const char *path, + size_t num_attr, + const char **names); + +typedef int (*git_attr_file_parser)( + git_repository *repo, + git_attr_file *file, + const char *data); + /* * git_attr_file API */ @@ -122,6 +145,7 @@ void git_attr_file__free(git_attr_file *file); int git_attr_file__load( git_attr_file **out, git_repository *repo, + git_attr_session *attr_session, git_attr_file_entry *ce, git_attr_file_source source, git_attr_file_parser parser); @@ -130,7 +154,7 @@ int git_attr_file__load_standalone( git_attr_file **out, const char *path); int git_attr_file__out_of_date( - git_repository *repo, git_attr_file *file); + git_repository *repo, git_attr_session *session, git_attr_file *file); int git_attr_file__parse_buffer( git_repository *repo, git_attr_file *attrs, const char *data); diff --git a/src/attrcache.c b/src/attrcache.c index b4579bfc0..f75edad68 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -196,6 +196,7 @@ cleanup: int git_attr_cache__get( git_attr_file **out, git_repository *repo, + git_attr_session *attr_session, git_attr_file_source source, const char *base, const char *filename, @@ -211,8 +212,8 @@ int git_attr_cache__get( return error; /* load file if we don't have one or if existing one is out of date */ - if (!file || (error = git_attr_file__out_of_date(repo, file)) > 0) - error = git_attr_file__load(&updated, repo, entry, source, parser); + if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0) + error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser); /* if we loaded the file, insert into and/or update cache */ if (updated) { diff --git a/src/attrcache.h b/src/attrcache.h index be0a22f5c..44e1ffdce 100644 --- a/src/attrcache.h +++ b/src/attrcache.h @@ -31,6 +31,7 @@ extern int git_attr_cache__do_init(git_repository *repo); extern int git_attr_cache__get( git_attr_file **file, git_repository *repo, + git_attr_session *attr_session, git_attr_file_source source, const char *base, const char *filename, diff --git a/src/checkout.c b/src/checkout.c index 52a076da6..04493a3cb 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -28,6 +28,7 @@ #include "buf_text.h" #include "merge_file.h" #include "path.h" +#include "attr.h" /* See docs/checkout-internals.md for more information */ @@ -69,6 +70,7 @@ typedef struct { size_t completed_steps; git_checkout_perfdata perfdata; git_buf last_mkdir; + git_attr_session attr_session; } checkout_data; typedef struct { @@ -1425,8 +1427,8 @@ static int blob_content_to_file( hint_path = path; if (!data->opts.disable_filters) - error = git_filter_list_load( - &fl, git_blob_owner(blob), blob, hint_path, + error = git_filter_list__load_with_attr_session( + &fl, data->repo, &data->attr_session, blob, hint_path, GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT); if (!error) @@ -2008,7 +2010,8 @@ static int checkout_write_merge( in_data.ptr = (char *)result.ptr; in_data.size = result.len; - if ((error = git_filter_list_load(&fl, data->repo, NULL, git_buf_cstr(&path_workdir), + if ((error = git_filter_list__load_with_attr_session( + &fl, data->repo, &data->attr_session, NULL, git_buf_cstr(&path_workdir), GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT)) < 0 || (error = git_filter_list_apply_to_data(&out_data, fl, &in_data)) < 0) goto done; @@ -2218,6 +2221,8 @@ static void checkout_data_clear(checkout_data *data) git_index_free(data->index); data->index = NULL; + + git_attr_session__free(&data->attr_session); } static int checkout_data_init( @@ -2360,6 +2365,8 @@ static int checkout_data_init( data->workdir_len = git_buf_len(&data->path); + git_attr_session__init(&data->attr_session, data->repo); + cleanup: if (error < 0) checkout_data_clear(data); diff --git a/src/filter.c b/src/filter.c index b5a8bdd66..22eaf51a2 100644 --- a/src/filter.c +++ b/src/filter.c @@ -394,15 +394,19 @@ static int filter_list_new( } static int filter_list_check_attributes( - const char ***out, git_filter_def *fdef, const git_filter_source *src) + const char ***out, + git_repository *repo, + git_attr_session *attr_session, + git_filter_def *fdef, + const git_filter_source *src) { int error; size_t i; const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); GITERR_CHECK_ALLOC(strs); - error = git_attr_get_many( - strs, src->repo, 0, src->path, fdef->nattrs, fdef->attrs); + error = git_attr_get_many_with_session( + strs, repo, attr_session, 0, src->path, fdef->nattrs, fdef->attrs); /* if no values were found but no matches are needed, it's okay! */ if (error == GIT_ENOTFOUND && !fdef->nmatches) { @@ -448,9 +452,10 @@ int git_filter_list_new( return filter_list_new(out, &src); } -int git_filter_list_load( +int git_filter_list__load_with_attr_session( git_filter_list **filters, git_repository *repo, + git_attr_session *attr_session, git_blob *blob, /* can be NULL */ const char *path, git_filter_mode_t mode, @@ -481,7 +486,9 @@ int git_filter_list_load( continue; if (fdef->nattrs > 0) { - error = filter_list_check_attributes(&values, fdef, &src); + error = filter_list_check_attributes( + &values, repo, attr_session, fdef, &src); + if (error == GIT_ENOTFOUND) { error = 0; continue; @@ -523,6 +530,18 @@ int git_filter_list_load( return error; } +int git_filter_list_load( + git_filter_list **filters, + git_repository *repo, + git_blob *blob, /* can be NULL */ + const char *path, + git_filter_mode_t mode, + uint32_t options) +{ + return git_filter_list__load_with_attr_session( + filters, repo, NULL, blob, path, mode, options); +} + void git_filter_list_free(git_filter_list *fl) { uint32_t i; diff --git a/src/filter.h b/src/filter.h index 5a366108b..390ffebad 100644 --- a/src/filter.h +++ b/src/filter.h @@ -8,6 +8,7 @@ #define INCLUDE_filter_h__ #include "common.h" +#include "attr_file.h" #include "git2/filter.h" /* Amount of file to examine for NUL byte when checking binary-ness */ @@ -25,6 +26,15 @@ typedef enum { extern void git_filter_free(git_filter *filter); +extern int git_filter_list__load_with_attr_session( + git_filter_list **filters, + git_repository *repo, + git_attr_session *attr_session, + git_blob *blob, /* can be NULL */ + const char *path, + git_filter_mode_t mode, + uint32_t options); + /* * Available filters */ diff --git a/src/ignore.c b/src/ignore.c index 6e00f7db5..dd299f076 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -161,7 +161,7 @@ static int push_ignore_file( git_attr_file *file = NULL; error = git_attr_cache__get( - &file, ignores->repo, GIT_ATTR_FILE__FROM_FILE, + &file, ignores->repo, NULL, GIT_ATTR_FILE__FROM_FILE, base, filename, parse_ignore_file); if (error < 0) return error; @@ -189,7 +189,7 @@ static int get_internal_ignores(git_attr_file **out, git_repository *repo) return error; error = git_attr_cache__get( - out, repo, GIT_ATTR_FILE__IN_MEMORY, NULL, GIT_IGNORE_INTERNAL, NULL); + out, repo, NULL, GIT_ATTR_FILE__IN_MEMORY, NULL, GIT_IGNORE_INTERNAL, NULL); /* if internal rules list is empty, insert default rules */ if (!error && !(*out)->rules.length) diff --git a/src/repository.h b/src/repository.h index 6da8c289b..dffa9a8ae 100644 --- a/src/repository.h +++ b/src/repository.h @@ -133,6 +133,8 @@ struct git_repository { has_8dot3_default:1; unsigned int lru_counter; + git_atomic attr_session_key; + git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX]; }; -- cgit v1.2.1 From d4b1b76701cb0495993b31b1b0e98c0a4b49c0ce Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Feb 2015 00:03:49 -0500 Subject: checkout: cache system attributes file location --- src/attr.c | 68 +++++++++++++++++++++++++++++++++++++++++++-------------- src/attr_file.c | 10 +++++++++ src/attr_file.h | 4 +++- 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/attr.c b/src/attr.c index df139e082..020bb1c8b 100644 --- a/src/attr.c +++ b/src/attr.c @@ -249,6 +249,46 @@ static int preload_attr_file( return error; } +static int system_attr_file( + git_buf *out, + git_repository *repo, + git_attr_session *attr_session) +{ + int error; + + if (!attr_session) { + error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM); + + if (error == GIT_ENOTFOUND) + giterr_clear(); + + return error; + } + + if (!attr_session->init_sysdir) { + error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM); + + if (error == GIT_ENOTFOUND) + giterr_clear(); + else if (error) + return error; + + attr_session->init_sysdir = 1; + } + + if (attr_session->sysdir.size == 0) + return GIT_ENOTFOUND; + + /* We can safely provide a git_buf with no allocation (asize == 0) to + * a consumer. This allows them to treat this as a regular `git_buf`, + * but their call to `git_buf_free` will not attempt to free it. + */ + out->ptr = attr_session->sysdir.ptr; + out->size = attr_session->sysdir.size; + out->asize = 0; + return 0; +} + static int attr_setup(git_repository *repo, git_attr_session *attr_session) { int error = 0; @@ -256,7 +296,7 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) git_index *idx = NULL; git_buf sys = GIT_BUF_INIT; - if (attr_session && attr_session->setup) + if (attr_session && attr_session->init_setup) return 0; if ((error = git_attr_cache__init(repo)) < 0) @@ -266,18 +306,15 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) * definitions will be available for later file parsing */ - if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) { + error = system_attr_file(&sys, repo, attr_session); + + if (error == 0) error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); - git_buf_free(&sys); - } - if (error < 0) { - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - } else - return error; - } + else if (error != GIT_ENOTFOUND) + return error; + + git_buf_free(&sys); if ((error = preload_attr_file( repo, attr_session, GIT_ATTR_FILE__FROM_FILE, @@ -300,7 +337,7 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) return error; if (attr_session) - attr_session->setup = 1; + attr_session->init_setup = 1; return error; } @@ -489,15 +526,14 @@ static int collect_attr_files( } if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { - error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); + error = system_attr_file(&dir, repo, attr_session); + if (!error) error = push_attr_file( repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, NULL, dir.ptr); - else if (error == GIT_ENOTFOUND) { - giterr_clear(); + else if (error == GIT_ENOTFOUND) error = 0; - } } cleanup: diff --git a/src/attr_file.c b/src/attr_file.c index 9896df4f9..2ebd3b91f 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -854,3 +854,13 @@ int git_attr_session__init(git_attr_session *session, git_repository *repo) return 0; } + +void git_attr_session__free(git_attr_session *session) +{ + if (!session) + return; + + git_buf_free(&session->sysdir); + + memset(session, 0, sizeof(git_attr_session)); +} diff --git a/src/attr_file.h b/src/attr_file.h index b8e37256b..5c64f7cc3 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -111,7 +111,9 @@ typedef struct { typedef struct { int key; - unsigned int setup; + unsigned int init_setup:1, + init_sysdir:1; + git_buf sysdir; } git_attr_session; extern int git_attr_session__init(git_attr_session *attr_session, git_repository *repo); -- cgit v1.2.1 From f58cc280c9ddeff6fd85f56e70073c3ed4fb0650 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Feb 2015 00:28:32 -0500 Subject: attr_session: keep a temp buffer --- src/attr_file.c | 1 + src/attr_file.h | 1 + src/attrcache.c | 10 +++++++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/attr_file.c b/src/attr_file.c index 2ebd3b91f..8997946b9 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -861,6 +861,7 @@ void git_attr_session__free(git_attr_session *session) return; git_buf_free(&session->sysdir); + git_buf_free(&session->tmp); memset(session, 0, sizeof(git_attr_session)); } diff --git a/src/attr_file.h b/src/attr_file.h index 5c64f7cc3..aa9a16de0 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -114,6 +114,7 @@ typedef struct { unsigned int init_setup:1, init_sysdir:1; git_buf sysdir; + git_buf tmp; } git_attr_session; extern int git_attr_session__init(git_attr_session *attr_session, git_repository *repo); diff --git a/src/attrcache.c b/src/attrcache.c index f75edad68..9a9b9d09d 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -149,6 +149,7 @@ static int attr_cache_lookup( git_attr_file **out_file, git_attr_file_entry **out_entry, git_repository *repo, + git_attr_session *attr_session, git_attr_file_source source, const char *base, const char *filename) @@ -162,9 +163,12 @@ static int attr_cache_lookup( /* join base and path as needed */ if (base != NULL && git_path_root(filename) < 0) { - if (git_buf_joinpath(&path, base, filename) < 0) + git_buf *p = attr_session ? &attr_session->tmp : &path; + + if (git_buf_joinpath(p, base, filename) < 0) return -1; - filename = path.ptr; + + filename = p->ptr; } relfile = filename; @@ -208,7 +212,7 @@ int git_attr_cache__get( git_attr_file *file = NULL, *updated = NULL; if ((error = attr_cache_lookup( - &file, &entry, repo, source, base, filename)) < 0) + &file, &entry, repo, attr_session, source, base, filename)) < 0) return error; /* load file if we don't have one or if existing one is out of date */ -- cgit v1.2.1 From 500ec5431564d9167985a02909823e1286136d5b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Feb 2015 01:46:01 -0500 Subject: checkout: hold seen dir paths in a map --- src/checkout.c | 77 +++++++++++++++++++++++++++++----------------------------- src/fileops.c | 35 +++++++++++++++++++------- src/fileops.h | 13 ++++++++-- 3 files changed, 76 insertions(+), 49 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 04493a3cb..3f65a9ed7 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -29,6 +29,10 @@ #include "merge_file.h" #include "path.h" #include "attr.h" +#include "pool.h" +#include "strmap.h" + +GIT__USE_STRMAP; /* See docs/checkout-internals.md for more information */ @@ -69,7 +73,7 @@ typedef struct { size_t total_steps; size_t completed_steps; git_checkout_perfdata perfdata; - git_buf last_mkdir; + git_strmap *mkdir_map; git_attr_session attr_session; } checkout_data; @@ -1293,25 +1297,6 @@ fail: return error; } -static int checkout_mkdir( - checkout_data *data, - const char *path, - const char *base, - mode_t mode, - unsigned int flags) -{ - struct git_futils_mkdir_perfdata mkdir_perfdata = {0}; - - int error = git_futils_mkdir_withperf( - path, base, mode, flags, &mkdir_perfdata); - - data->perfdata.mkdir_calls += mkdir_perfdata.mkdir_calls; - data->perfdata.stat_calls += mkdir_perfdata.stat_calls; - data->perfdata.chmod_calls += mkdir_perfdata.chmod_calls; - - return error; -} - static bool should_remove_existing(checkout_data *data) { int ignorecase = 0; @@ -1327,31 +1312,43 @@ static bool should_remove_existing(checkout_data *data) #define MKDIR_REMOVE_EXISTING \ MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS +static int checkout_mkdir( + checkout_data *data, + const char *path, + const char *base, + mode_t mode, + unsigned int flags) +{ + struct git_futils_mkdir_options mkdir_opts = {0}; + int error; + + mkdir_opts.dir_map = data->mkdir_map; + mkdir_opts.pool = &data->pool; + + error = git_futils_mkdir_ext( + path, base, mode, flags, &mkdir_opts); + + data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls; + data->perfdata.stat_calls += mkdir_opts.perfdata.stat_calls; + data->perfdata.chmod_calls += mkdir_opts.perfdata.chmod_calls; + + return error; +} + static int mkpath2file( checkout_data *data, const char *path, unsigned int mode) { - git_buf *mkdir_path = &data->tmp; struct stat st; bool remove_existing = should_remove_existing(data); + unsigned int flags = + (remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL) | + GIT_MKDIR_SKIP_LAST; int error; - if ((error = git_buf_sets(mkdir_path, path)) < 0) + if ((error = checkout_mkdir( + data, path, data->opts.target_directory, mode, flags)) < 0) return error; - git_buf_rtruncate_at_char(mkdir_path, '/'); - - if (!data->last_mkdir.size || - data->last_mkdir.size != mkdir_path->size || - memcmp(mkdir_path->ptr, data->last_mkdir.ptr, mkdir_path->size) != 0) { - - if ((error = checkout_mkdir( - data, mkdir_path->ptr, data->opts.target_directory, mode, - remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0) - return error; - - git_buf_swap(&data->last_mkdir, mkdir_path); - } - if (remove_existing) { data->perfdata.stat_calls++; @@ -2215,13 +2212,16 @@ static void checkout_data_clear(checkout_data *data) git__free(data->pfx); data->pfx = NULL; - git_buf_free(&data->last_mkdir); + git_strmap_free(data->mkdir_map); + git_buf_free(&data->path); git_buf_free(&data->tmp); git_index_free(data->index); data->index = NULL; + git_strmap_free(data->mkdir_map); + git_attr_session__free(&data->attr_session); } @@ -2360,7 +2360,8 @@ static int checkout_data_init( (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 || (error = git_pool_init(&data->pool, 1, 0)) < 0 || (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 || - (error = git_path_to_dir(&data->path)) < 0) + (error = git_path_to_dir(&data->path)) < 0 || + (error = git_strmap_alloc(&data->mkdir_map)) < 0) goto cleanup; data->workdir_len = git_buf_len(&data->path); diff --git a/src/fileops.c b/src/fileops.c index 2ee9535be..ff2acfc2b 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -7,11 +7,14 @@ #include "common.h" #include "fileops.h" #include "global.h" +#include "strmap.h" #include #if GIT_WIN32 #include "win32/findfile.h" #endif +GIT__USE_STRMAP; + int git_futils_mkpath2file(const char *file_path, const mode_t mode) { return git_futils_mkdir( @@ -321,12 +324,12 @@ GIT_INLINE(int) validate_existing( return 0; } -int git_futils_mkdir_withperf( +int git_futils_mkdir_ext( const char *path, const char *base, mode_t mode, uint32_t flags, - struct git_futils_mkdir_perfdata *perfdata) + struct git_futils_mkdir_options *opts) { int error = -1; git_buf make_path = GIT_BUF_INIT; @@ -394,11 +397,14 @@ int git_futils_mkdir_withperf( *tail = '\0'; st.st_mode = 0; + if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr)) + continue; + /* See what's going on with this path component */ - perfdata->stat_calls++; + opts->perfdata.stat_calls++; if (p_lstat(make_path.ptr, &st) < 0) { - perfdata->mkdir_calls++; + opts->perfdata.mkdir_calls++; if (errno != ENOENT || p_mkdir(make_path.ptr, mode) < 0) { giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); @@ -416,7 +422,7 @@ int git_futils_mkdir_withperf( } if ((error = validate_existing( - make_path.ptr, &st, mode, flags, perfdata)) < 0) + make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0) goto done; } @@ -425,7 +431,7 @@ int git_futils_mkdir_withperf( (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) && st.st_mode != mode) { - perfdata->chmod_calls++; + opts->perfdata.chmod_calls++; if ((error = p_chmod(make_path.ptr, mode)) < 0 && lastch == '\0') { @@ -434,6 +440,17 @@ int git_futils_mkdir_withperf( goto done; } } + + if (opts->dir_map && opts->pool) { + char *cache_path = git_pool_malloc(opts->pool, make_path.size + 1); + GITERR_CHECK_ALLOC(cache_path); + + memcpy(cache_path, make_path.ptr, make_path.size + 1); + + git_strmap_insert(opts->dir_map, cache_path, cache_path, error); + if (error < 0) + goto done; + } } error = 0; @@ -441,7 +458,7 @@ int git_futils_mkdir_withperf( /* check that full path really is a directory if requested & needed */ if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && lastch != '\0') { - perfdata->stat_calls++; + opts->perfdata.stat_calls++; if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { giterr_set(GITERR_OS, "Path is not a directory '%s'", @@ -461,8 +478,8 @@ int git_futils_mkdir( mode_t mode, uint32_t flags) { - struct git_futils_mkdir_perfdata perfdata = {0}; - return git_futils_mkdir_withperf(path, base, mode, flags, &perfdata); + struct git_futils_mkdir_options options = {0}; + return git_futils_mkdir_ext(path, base, mode, flags, &options); } int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode) diff --git a/src/fileops.h b/src/fileops.h index 4aaf1781c..0f6466c59 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -11,6 +11,8 @@ #include "map.h" #include "posix.h" #include "path.h" +#include "pool.h" +#include "strmap.h" /** * Filebuffer methods @@ -95,6 +97,13 @@ struct git_futils_mkdir_perfdata size_t chmod_calls; }; +struct git_futils_mkdir_options +{ + git_strmap *dir_map; + git_pool *pool; + struct git_futils_mkdir_perfdata perfdata; +}; + /** * Create a directory or entire path. * @@ -106,10 +115,10 @@ struct git_futils_mkdir_perfdata * @param base Root for relative path. These directories will never be made. * @param mode The mode to use for created directories. * @param flags Combination of the mkdir flags above. - * @param perfdata Performance data, use `git_futils_mkdir` if you don't want this data. + * @param opts Extended options, use `git_futils_mkdir` if you are not interested. * @return 0 on success, else error code */ -extern int git_futils_mkdir_withperf(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_perfdata *perfdata); +extern int git_futils_mkdir_ext(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts); /** * Create a directory or entire path. Similar to `git_futils_mkdir_withperf` -- cgit v1.2.1 From fa89ff20cd777fafc3e971bbf8081d97ce082c15 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Feb 2015 05:35:24 +0000 Subject: remove some unused warnings --- src/attr.c | 5 ++--- tests/checkout/tree.c | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/attr.c b/src/attr.c index 020bb1c8b..2e1ef7c2a 100644 --- a/src/attr.c +++ b/src/attr.c @@ -251,7 +251,6 @@ static int preload_attr_file( static int system_attr_file( git_buf *out, - git_repository *repo, git_attr_session *attr_session) { int error; @@ -306,7 +305,7 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session) * definitions will be available for later file parsing */ - error = system_attr_file(&sys, repo, attr_session); + error = system_attr_file(&sys, attr_session); if (error == 0) error = preload_attr_file( @@ -526,7 +525,7 @@ static int collect_attr_files( } if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { - error = system_attr_file(&dir, repo, attr_session); + error = system_attr_file(&dir, attr_session); if (!error) error = push_attr_file( diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 42f32a8d9..239ee55bc 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -1137,6 +1137,10 @@ void update_attr_callback( size_t total_steps, void *payload) { + GIT_UNUSED(completed_steps); + GIT_UNUSED(total_steps); + GIT_UNUSED(payload); + if (path && strcmp(path, "ident1.txt") == 0) cl_git_write2file("testrepo/.gitattributes", "*.txt ident\n", 12, O_RDWR|O_CREAT, 0666); -- cgit v1.2.1