diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2021-07-29 08:48:17 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-29 08:48:17 -0400 |
commit | 003a1df6540c9f24042e496b64b87223dff29cf0 (patch) | |
tree | 7faabe8ab3dcaeddc0686d305b41c9200dd35b6e | |
parent | f313b3834be2dcb60c9dccd118686f0043153a2c (diff) | |
parent | 1439b9ff05524949b6b3fa6cad716a9bb3cbc249 (diff) | |
download | libgit2-003a1df6540c9f24042e496b64b87223dff29cf0.tar.gz |
Merge pull request #5952 from libgit2/ethomson/attrs_from_commit
Support reading attributes from a specific commit
24 files changed, 588 insertions, 193 deletions
diff --git a/include/git2/attr.h b/include/git2/attr.h index a3ab5a7a2..62c2ed6e7 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -130,9 +130,32 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr); * * Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes * from a `.gitattributes` file in the repository at the HEAD revision. + * + * Passing the `GIT_ATTR_CHECK_INCLUDE_COMMIT` flag will use attributes + * from a `.gitattributes` file in a specific commit. */ #define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2) #define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3) +#define GIT_ATTR_CHECK_INCLUDE_COMMIT (1 << 4) + +/** +* An options structure for querying attributes. +*/ +typedef struct { + unsigned int version; + + /** A combination of GIT_ATTR_CHECK flags */ + unsigned int flags; + + /** + * The commit to load attributes from, when + * `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified. + */ + git_oid *commit_id; +} git_attr_options; + +#define GIT_ATTR_OPTIONS_VERSION 1 +#define GIT_ATTR_OPTIONS_INIT {GIT_ATTR_OPTIONS_VERSION} /** * Look up the value of one git attribute for path. @@ -157,6 +180,28 @@ GIT_EXTERN(int) git_attr_get( const char *name); /** + * Look up the value of one git attribute for path with extended options. + * + * @param value_out Output of the value of the attribute. Use the GIT_ATTR_... + * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just + * use the string value for attributes set to a value. You + * should NOT modify or free this value. + * @param repo The repository containing the path. + * @param opts The `git_attr_options` to use when querying these attributes. + * @param path The path to check for attributes. Relative paths are + * interpreted relative to the repo root. The file does + * not have to exist, but if it does not, then it will be + * treated as a plain file (not a directory). + * @param name The name of the attribute to look up. + */ +GIT_EXTERN(int) git_attr_get_ext( + const char **value_out, + git_repository *repo, + git_attr_options *opts, + const char *path, + const char *name); + +/** * Look up a list of git attributes for path. * * Use this if you have a known list of attributes that you want to @@ -194,6 +239,30 @@ GIT_EXTERN(int) git_attr_get_many( const char **names); /** + * Look up a list of git attributes for path with extended options. + * + * @param values_out An array of num_attr entries that will have string + * pointers written into it for the values of the attributes. + * You should not modify or free the values that are written + * into this array (although of course, you should free the + * array itself if you allocated it). + * @param repo The repository containing the path. + * @param opts The `git_attr_options` to use when querying these attributes. + * @param path The path inside the repo to check attributes. This + * does not have to exist, but if it does not, then + * it will be treated as a plain file (i.e. not a directory). + * @param num_attr The number of attributes being looked up + * @param names An array of num_attr strings containing attribute names. + */ +GIT_EXTERN(int) git_attr_get_many_ext( + const char **values_out, + git_repository *repo, + git_attr_options *opts, + const char *path, + size_t num_attr, + const char **names); + +/** * The callback used with git_attr_foreach. * * This callback will be invoked only once per attribute name, even if there @@ -232,6 +301,26 @@ GIT_EXTERN(int) git_attr_foreach( void *payload); /** + * Loop over all the git attributes for a path with extended options. + * + * @param repo The repository containing the path. + * @param opts The `git_attr_options` to use when querying these attributes. + * @param path Path inside the repo to check attributes. This does not have + * to exist, but if it does not, then it will be treated as a + * plain file (i.e. not a directory). + * @param callback Function to invoke on each attribute name and value. + * See git_attr_foreach_cb. + * @param payload Passed on as extra parameter to callback function. + * @return 0 on success, non-zero callback return value, or error code + */ +GIT_EXTERN(int) git_attr_foreach_ext( + git_repository *repo, + git_attr_options *opts, + const char *path, + git_attr_foreach_cb callback, + void *payload); + +/** * Flush the gitattributes cache. * * Call this if you have reason to believe that the attributes files on diff --git a/include/git2/blob.h b/include/git2/blob.h index 3f6738675..fceb5c771 100644 --- a/include/git2/blob.h +++ b/include/git2/blob.h @@ -114,6 +114,12 @@ typedef enum { * in the HEAD commit. */ GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD = (1 << 2), + + /** + * When set, filters will be loaded from a `.gitattributes` file + * in the specified commit. + */ + GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT = (1 << 3), } git_blob_filter_flag_t; /** @@ -128,6 +134,12 @@ typedef struct { /** Flags to control the filtering process, see `git_blob_filter_flag_t` above */ uint32_t flags; + + /** + * The commit to load attributes from, when + * `GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. + */ + git_oid *commit_id; } git_blob_filter_options; #define GIT_BLOB_FILTER_OPTIONS_VERSION 1 diff --git a/include/git2/filter.h b/include/git2/filter.h index a0185ee88..044c3b870 100644 --- a/include/git2/filter.h +++ b/include/git2/filter.h @@ -49,9 +49,34 @@ typedef enum { /** Load attributes from `.gitattributes` in the root of HEAD */ GIT_FILTER_ATTRIBUTES_FROM_HEAD = (1u << 2), + + /** + * Load attributes from `.gitattributes` in a given commit. + * This can only be specified in a `git_filter_options`. + */ + GIT_FILTER_ATTRIBUTES_FROM_COMMIT = (1u << 3), } git_filter_flag_t; /** + * Filtering options + */ +typedef struct { + unsigned int version; + + /** See `git_filter_flag_t` above */ + uint32_t flags; + + /** + * The commit to load attributes from, when + * `GIT_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. + */ + git_oid *commit_id; +} git_filter_options; + + #define GIT_FILTER_OPTIONS_VERSION 1 + #define GIT_FILTER_OPTIONS_INIT {GIT_FILTER_OPTIONS_VERSION} + +/** * A filter that can transform file data * * This represents a filter that can be used to transform or even replace @@ -104,6 +129,29 @@ GIT_EXTERN(int) git_filter_list_load( uint32_t flags); /** + * Load the filter list for a given path. + * + * This will return 0 (success) but set the output git_filter_list to NULL + * if no filters are requested for the given file. + * + * @param filters Output newly created git_filter_list (or NULL) + * @param repo Repository object that contains `path` + * @param blob The blob to which the filter will be applied (if known) + * @param path Relative path of the file to be filtered + * @param mode Filtering direction (WT->ODB or ODB->WT) + * @param opts The `git_filter_options` to use when loading filters + * @return 0 on success (which could still return NULL if no filters are + * needed for the requested file), <0 on error + */ +GIT_EXTERN(int) git_filter_list_load_ext( + git_filter_list **filters, + git_repository *repo, + git_blob *blob, + const char *path, + git_filter_mode_t mode, + git_filter_options *opts); + +/** * Query the filter list to see if a given filter (by name) will run. * The built-in filters "crlf" and "ident" can be queried, otherwise this * is the name of the filter specified by the filter attribute. diff --git a/src/attr.c b/src/attr.c index cab5e8b97..03b720c5a 100644 --- a/src/attr.c +++ b/src/attr.c @@ -36,16 +36,16 @@ git_attr_value_t git_attr_value(const char *attr) static int collect_attr_files( git_repository *repo, git_attr_session *attr_session, - uint32_t flags, + git_attr_options *opts, const char *path, git_vector *files); static void release_attr_files(git_vector *files); -int git_attr_get( +int git_attr_get_ext( const char **value, git_repository *repo, - uint32_t flags, + git_attr_options *opts, const char *pathname, const char *name) { @@ -61,6 +61,7 @@ int git_attr_get( GIT_ASSERT_ARG(value); GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(name); + GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options"); *value = NULL; @@ -70,7 +71,7 @@ int git_attr_get( if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0) return -1; - if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0) goto cleanup; memset(&attr, 0, sizeof(attr)); @@ -97,6 +98,20 @@ cleanup: return error; } +int git_attr_get( + const char **value, + git_repository *repo, + uint32_t flags, + const char *pathname, + const char *name) +{ + git_attr_options opts = GIT_ATTR_OPTIONS_INIT; + + opts.flags = flags; + + return git_attr_get_ext(value, repo, &opts, pathname, name); +} + typedef struct { git_attr_name name; @@ -107,7 +122,7 @@ int git_attr_get_many_with_session( const char **values, git_repository *repo, git_attr_session *attr_session, - uint32_t flags, + git_attr_options *opts, const char *pathname, size_t num_attr, const char **names) @@ -129,6 +144,7 @@ int git_attr_get_many_with_session( GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(pathname); GIT_ASSERT_ARG(names); + GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options"); if (git_repository_is_bare(repo)) dir_flag = GIT_DIR_FLAG_FALSE; @@ -136,7 +152,7 @@ int git_attr_get_many_with_session( if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0) return -1; - if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0) goto cleanup; info = git__calloc(num_attr, sizeof(attr_get_many_info)); @@ -190,8 +206,24 @@ int git_attr_get_many( size_t num_attr, const char **names) { + git_attr_options opts = GIT_ATTR_OPTIONS_INIT; + + opts.flags = flags; + return git_attr_get_many_with_session( - values, repo, NULL, flags, pathname, num_attr, names); + values, repo, NULL, &opts, pathname, num_attr, names); +} + +int git_attr_get_many_ext( + const char **values, + git_repository *repo, + git_attr_options *opts, + const char *pathname, + size_t num_attr, + const char **names) +{ + return git_attr_get_many_with_session( + values, repo, NULL, opts, pathname, num_attr, names); } int git_attr_foreach( @@ -201,6 +233,20 @@ int git_attr_foreach( int (*callback)(const char *name, const char *value, void *payload), void *payload) { + git_attr_options opts = GIT_ATTR_OPTIONS_INIT; + + opts.flags = flags; + + return git_attr_foreach_ext(repo, &opts, pathname, callback, payload); +} + +int git_attr_foreach_ext( + git_repository *repo, + git_attr_options *opts, + const char *pathname, + int (*callback)(const char *name, const char *value, void *payload), + void *payload) +{ int error; git_attr_path path; git_vector files = GIT_VECTOR_INIT; @@ -213,6 +259,7 @@ int git_attr_foreach( GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(callback); + GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options"); if (git_repository_is_bare(repo)) dir_flag = GIT_DIR_FLAG_FALSE; @@ -220,7 +267,7 @@ int git_attr_foreach( if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0) return -1; - if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 || + if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 || (error = git_strmap_new(&seen)) < 0) goto cleanup; @@ -253,26 +300,43 @@ cleanup: return error; } -static int preload_attr_file( +static int preload_attr_source( git_repository *repo, git_attr_session *attr_session, - git_attr_file_source source, - const char *base, - const char *file, - bool allow_macros) + git_attr_file_source *source) { int error; git_attr_file *preload = NULL; - if (!file) + if (!source) return 0; - if (!(error = git_attr_cache__get(&preload, repo, attr_session, source, base, file, - git_attr_file__parse_buffer, allow_macros))) + + error = git_attr_cache__get(&preload, repo, attr_session, source, + git_attr_file__parse_buffer, true); + + if (!error) git_attr_file__free(preload); return error; } +GIT_INLINE(int) preload_attr_file( + git_repository *repo, + git_attr_session *attr_session, + const char *base, + const char *filename) +{ + git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE }; + + if (!filename) + return 0; + + source.base = base; + source.filename = filename; + + return preload_attr_source(repo, attr_session, &source); +} + static int system_attr_file( git_buf *out, git_attr_session *attr_session) @@ -314,9 +378,12 @@ static int system_attr_file( static int attr_setup( git_repository *repo, git_attr_session *attr_session, - uint32_t flags) + git_attr_options *opts) { - git_buf path = GIT_BUF_INIT; + git_buf system = GIT_BUF_INIT, info = GIT_BUF_INIT; + git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL }; + git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL }; + git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL }; git_index *idx = NULL; const char *workdir; int error = 0; @@ -332,45 +399,51 @@ static int attr_setup( * definitions will be available for later file parsing. */ - if ((error = system_attr_file(&path, attr_session)) < 0 || - (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE, - NULL, path.ptr, true)) < 0) { + if ((error = system_attr_file(&system, attr_session)) < 0 || + (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) { if (error != GIT_ENOTFOUND) goto out; + + error = 0; } - if ((error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE, - NULL, git_repository_attr_cache(repo)->cfg_attr_file, true)) < 0) + if ((error = preload_attr_file(repo, attr_session, NULL, + git_repository_attr_cache(repo)->cfg_attr_file)) < 0) goto out; - git_buf_clear(&path); /* git_repository_item_path expects an empty buffer, because it uses git_buf_set */ - if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 || - (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE, - path.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) { + if ((error = git_repository_item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 || + (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) { if (error != GIT_ENOTFOUND) goto out; + + error = 0; } if ((workdir = git_repository_workdir(repo)) != NULL && - (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_FILE, - workdir, GIT_ATTR_FILE, true)) < 0) + (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0) goto out; if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || - (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_INDEX, - NULL, GIT_ATTR_FILE, true)) < 0) + (error = preload_attr_source(repo, attr_session, &index_source)) < 0) goto out; - if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 && - (error = preload_attr_file(repo, attr_session, GIT_ATTR_FILE__FROM_HEAD, - NULL, GIT_ATTR_FILE, true)) < 0) + if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) && + (error = preload_attr_source(repo, attr_session, &head_source)) < 0) goto out; + if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) { + commit_source.commit_id = opts->commit_id; + + if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0) + goto out; + } + if (attr_session) attr_session->init_setup = 1; out: - git_buf_dispose(&path); + git_buf_dispose(&system); + git_buf_dispose(&info); return error; } @@ -415,56 +488,60 @@ int git_attr_add_macro( typedef struct { git_repository *repo; git_attr_session *attr_session; - uint32_t flags; + git_attr_options *opts; const char *workdir; git_index *index; git_vector *files; } attr_walk_up_info; static int attr_decide_sources( - uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs) + uint32_t flags, + bool has_wd, + bool has_index, + git_attr_file_source_t *srcs) { int count = 0; switch (flags & 0x03) { case GIT_ATTR_CHECK_FILE_THEN_INDEX: if (has_wd) - srcs[count++] = GIT_ATTR_FILE__FROM_FILE; + srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE; if (has_index) - srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX; break; case GIT_ATTR_CHECK_INDEX_THEN_FILE: if (has_index) - srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX; if (has_wd) - srcs[count++] = GIT_ATTR_FILE__FROM_FILE; + srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE; break; case GIT_ATTR_CHECK_INDEX_ONLY: if (has_index) - srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX; break; } - if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) - srcs[count++] = GIT_ATTR_FILE__FROM_HEAD; + if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0 || + (flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0) + srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT; return count; } -static int push_attr_file( +static int push_attr_source( git_repository *repo, git_attr_session *attr_session, git_vector *list, - git_attr_file_source source, - const char *base, - const char *filename, + git_attr_file_source *source, bool allow_macros) { int error = 0; git_attr_file *file = NULL; error = git_attr_cache__get(&file, repo, attr_session, - source, base, filename, git_attr_file__parse_buffer, allow_macros); + source, + git_attr_file__parse_buffer, + allow_macros); if (error < 0) return error; @@ -477,20 +554,40 @@ static int push_attr_file( return error; } +GIT_INLINE(int) push_attr_file( + git_repository *repo, + git_attr_session *attr_session, + git_vector *list, + const char *base, + const char *filename) +{ + git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename }; + return push_attr_source(repo, attr_session, list, &source, true); +} + static int push_one_attr(void *ref, const char *path) { attr_walk_up_info *info = (attr_walk_up_info *)ref; - git_attr_file_source src[GIT_ATTR_FILE_NUM_SOURCES]; + git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES]; int error = 0, n_src, i; bool allow_macros; - n_src = attr_decide_sources( - info->flags, info->workdir != NULL, info->index != NULL, src); + n_src = attr_decide_sources(info->opts ? info->opts->flags : 0, + info->workdir != NULL, + info->index != NULL, + src); + allow_macros = info->workdir ? !strcmp(info->workdir, path) : false; - for (i = 0; !error && i < n_src; ++i) - error = push_attr_file(info->repo, info->attr_session, info->files, - src[i], path, GIT_ATTR_FILE, allow_macros); + for (i = 0; !error && i < n_src; ++i) { + git_attr_file_source source = { src[i], path, GIT_ATTR_FILE }; + + if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts) + source.commit_id = info->opts->commit_id; + + error = push_attr_source(info->repo, info->attr_session, info->files, + &source, allow_macros); + } return error; } @@ -510,7 +607,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, + git_attr_options *opts, const char *path, git_vector *files) { @@ -519,7 +616,7 @@ static int collect_attr_files( const char *workdir = git_repository_workdir(repo); attr_walk_up_info info = { NULL }; - if ((error = attr_setup(repo, attr_session, flags)) < 0) + if ((error = attr_setup(repo, attr_session, opts)) < 0) return error; /* Resolve path in a non-bare repo */ @@ -542,15 +639,14 @@ static int collect_attr_files( */ if ((error = git_repository_item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 || - (error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, - attrfile.ptr, GIT_ATTR_FILE_INREPO, true)) < 0) { + (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) { if (error != GIT_ENOTFOUND) goto cleanup; } info.repo = repo; info.attr_session = attr_session; - info.flags = flags; + info.opts = opts; info.workdir = workdir; if (git_repository_index__weakptr(&info.index, repo) < 0) git_error_clear(); /* no error even if there is no index */ @@ -565,18 +661,16 @@ static int collect_attr_files( goto cleanup; if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { - error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, - NULL, git_repository_attr_cache(repo)->cfg_attr_file, true); + error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file); if (error < 0) goto cleanup; } - if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { + if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { error = system_attr_file(&dir, attr_session); if (!error) - error = push_attr_file(repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE, - NULL, dir.ptr, true); + error = push_attr_file(repo, attr_session, files, NULL, dir.ptr); else if (error == GIT_ENOTFOUND) error = 0; } diff --git a/src/attr_file.c b/src/attr_file.c index 82692727d..f8627381c 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -33,7 +33,7 @@ static void attr_file_free(git_attr_file *file) int git_attr_file__new( git_attr_file **out, git_attr_file_entry *entry, - git_attr_file_source source) + git_attr_file_source *source) { git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file)); GIT_ERROR_CHECK_ALLOC(attrs); @@ -47,8 +47,8 @@ int git_attr_file__new( goto on_error; GIT_REFCOUNT_INC(attrs); - attrs->entry = entry; - attrs->source = source; + attrs->entry = entry; + memcpy(&attrs->source, source, sizeof(git_attr_file_source)); *out = attrs; return 0; @@ -108,11 +108,12 @@ int git_attr_file__load( git_repository *repo, git_attr_session *attr_session, git_attr_file_entry *entry, - git_attr_file_source source, + git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros) { int error = 0; + git_commit *commit = NULL; git_tree *tree = NULL; git_tree_entry *tree_entry = NULL; git_blob *blob = NULL; @@ -128,11 +129,11 @@ int git_attr_file__load( *out = NULL; - switch (source) { - case GIT_ATTR_FILE__IN_MEMORY: + switch (source->type) { + case GIT_ATTR_FILE_SOURCE_MEMORY: /* in-memory attribute file doesn't need data */ break; - case GIT_ATTR_FILE__FROM_INDEX: { + case GIT_ATTR_FILE_SOURCE_INDEX: { if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 || (error = git_blob_lookup(&blob, repo, &id)) < 0) return error; @@ -145,7 +146,7 @@ int git_attr_file__load( git_buf_put(&content, git_blob_rawcontent(blob), (size_t)blobsize); break; } - case GIT_ATTR_FILE__FROM_FILE: { + case GIT_ATTR_FILE_SOURCE_FILE: { int fd = -1; /* For open or read errors, pretend that we got ENOTFOUND. */ @@ -162,10 +163,31 @@ int git_attr_file__load( break; } - case GIT_ATTR_FILE__FROM_HEAD: { - if ((error = git_repository_head_tree(&tree, repo)) < 0 || - (error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0 || - (error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0) + case GIT_ATTR_FILE_SOURCE_COMMIT: { + if (source->commit_id) { + if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0 || + (error = git_commit_tree(&tree, commit)) < 0) + goto cleanup; + } else { + if ((error = git_repository_head_tree(&tree, repo)) < 0) + goto cleanup; + } + + if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) { + /* + * If the attributes file does not exist, we can + * cache an empty file for this commit to prevent + * needless future lookups. + */ + if (error == GIT_ENOTFOUND) { + error = 0; + break; + } + + goto cleanup; + } + + if ((error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0) goto cleanup; /* @@ -182,7 +204,7 @@ int git_attr_file__load( break; } default: - git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source); + git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source->type); return -1; } @@ -210,11 +232,11 @@ int git_attr_file__load( /* write cache breakers */ if (nonexistent) file->nonexistent = 1; - else if (source == GIT_ATTR_FILE__FROM_INDEX) + else if (source->type == GIT_ATTR_FILE_SOURCE_INDEX) git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); - else if (source == GIT_ATTR_FILE__FROM_HEAD) + else if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT) git_oid_cpy(&file->cache_data.oid, git_tree_id(tree)); - else if (source == GIT_ATTR_FILE__FROM_FILE) + else if (source->type == GIT_ATTR_FILE_SOURCE_FILE) git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); /* else always cacheable */ @@ -224,6 +246,7 @@ cleanup: git_blob_free(blob); git_tree_entry_free(tree_entry); git_tree_free(tree); + git_commit_free(commit); git_buf_dispose(&content); return error; @@ -232,7 +255,8 @@ cleanup: int git_attr_file__out_of_date( git_repository *repo, git_attr_session *attr_session, - git_attr_file *file) + git_attr_file *file, + git_attr_file_source *source) { if (!file) return 1; @@ -245,15 +269,15 @@ int git_attr_file__out_of_date( else if (file->nonexistent) return 1; - switch (file->source) { - case GIT_ATTR_FILE__IN_MEMORY: + switch (file->source.type) { + case GIT_ATTR_FILE_SOURCE_MEMORY: return 0; - case GIT_ATTR_FILE__FROM_FILE: + case GIT_ATTR_FILE_SOURCE_FILE: return git_futils_filestamp_check( &file->cache_data.stamp, file->entry->fullpath); - case GIT_ATTR_FILE__FROM_INDEX: { + case GIT_ATTR_FILE_SOURCE_INDEX: { int error; git_oid id; @@ -264,21 +288,34 @@ int git_attr_file__out_of_date( return (git_oid__cmp(&file->cache_data.oid, &id) != 0); } - case GIT_ATTR_FILE__FROM_HEAD: { - git_tree *tree; + case GIT_ATTR_FILE_SOURCE_COMMIT: { + git_tree *tree = NULL; int error; - if ((error = git_repository_head_tree(&tree, repo)) < 0) + if (source->commit_id) { + git_commit *commit = NULL; + + if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0) + return error; + + error = git_commit_tree(&tree, commit); + + git_commit_free(commit); + } else { + error = git_repository_head_tree(&tree, repo); + } + + if (error < 0) return error; - error = git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)); + error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0); git_tree_free(tree); return error; } default: - git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source); + git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source.type); return -1; } } @@ -389,6 +426,7 @@ int git_attr_file__lookup_one( int git_attr_file__load_standalone(git_attr_file **out, const char *path) { git_buf content = GIT_BUF_INIT; + git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE }; git_attr_file *file = NULL; int error; @@ -400,7 +438,7 @@ int git_attr_file__load_standalone(git_attr_file **out, const char *path) * don't have to free it - freeing file+pool will free cache entry, too. */ - if ((error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE)) < 0 || + if ((error = git_attr_file__new(&file, NULL, &source)) < 0 || (error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 || (error = git_attr_cache__alloc_file_entry(&file->entry, NULL, NULL, path, &file->pool)) < 0) goto out; diff --git a/src/attr_file.h b/src/attr_file.h index 617436b14..16e33caf1 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -37,12 +37,30 @@ (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO) typedef enum { - GIT_ATTR_FILE__IN_MEMORY = 0, - GIT_ATTR_FILE__FROM_FILE = 1, - GIT_ATTR_FILE__FROM_INDEX = 2, - GIT_ATTR_FILE__FROM_HEAD = 3, + GIT_ATTR_FILE_SOURCE_MEMORY = 0, + GIT_ATTR_FILE_SOURCE_FILE = 1, + GIT_ATTR_FILE_SOURCE_INDEX = 2, + GIT_ATTR_FILE_SOURCE_COMMIT = 3, - GIT_ATTR_FILE_NUM_SOURCES = 4 + GIT_ATTR_FILE_NUM_SOURCES = 4 +} git_attr_file_source_t; + +typedef struct { + /* The source location for the attribute file. */ + git_attr_file_source_t type; + + /* + * The filename of the attribute file to read (relative to the + * given base path). + */ + const char *base; + const char *filename; + + /* + * The commit ID when the given source type is a commit (or NULL + * for the repository's HEAD commit.) + */ + git_oid *commit_id; } git_attr_file_source; extern const char *git_attr__true; @@ -124,7 +142,7 @@ extern int git_attr_get_many_with_session( const char **values_out, git_repository *repo, git_attr_session *attr_session, - uint32_t flags, + git_attr_options *opts, const char *path, size_t num_attr, const char **names); @@ -142,7 +160,7 @@ typedef int (*git_attr_file_parser)( int git_attr_file__new( git_attr_file **out, git_attr_file_entry *entry, - git_attr_file_source source); + git_attr_file_source *source); void git_attr_file__free(git_attr_file *file); @@ -151,7 +169,7 @@ int git_attr_file__load( git_repository *repo, git_attr_session *attr_session, git_attr_file_entry *ce, - git_attr_file_source source, + git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros); @@ -159,7 +177,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_session *session, git_attr_file *file); + git_repository *repo, git_attr_session *session, git_attr_file *file, git_attr_file_source *source); int git_attr_file__parse_buffer( git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros); diff --git a/src/attrcache.c b/src/attrcache.c index 32513da01..7fe2bfbdb 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -112,7 +112,7 @@ static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) * Replace the existing value if another thread has * created it in the meantime. */ - old = git_atomic_swap(entry->file[file->source], file); + old = git_atomic_swap(entry->file[file->source.type], file); if (old) { GIT_REFCOUNT_OWN(old, NULL); @@ -136,7 +136,7 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) return error; if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) - old = git_atomic_compare_and_swap(&entry->file[file->source], file, NULL); + old = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL); attr_cache_unlock(cache); @@ -158,41 +158,42 @@ static int attr_cache_lookup( 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) + git_attr_file_source *source) { int error = 0; git_buf path = GIT_BUF_INIT; - const char *wd = git_repository_workdir(repo), *relfile; + const char *wd = git_repository_workdir(repo); + const char *filename; git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_file_entry *entry = NULL; git_attr_file *file = NULL; /* join base and path as needed */ - if (base != NULL && git_path_root(filename) < 0) { + if (source->base != NULL && git_path_root(source->filename) < 0) { git_buf *p = attr_session ? &attr_session->tmp : &path; - if (git_buf_joinpath(p, base, filename) < 0 || + if (git_buf_joinpath(p, source->base, source->filename) < 0 || git_path_validate_workdir_buf(repo, p) < 0) return -1; filename = p->ptr; + } else { + filename = source->filename; } - relfile = filename; - if (wd && !git__prefixcmp(relfile, wd)) - relfile += strlen(wd); + if (wd && !git__prefixcmp(filename, wd)) + filename += strlen(wd); /* check cache for existing entry */ if ((error = attr_cache_lock(cache)) < 0) goto cleanup; - entry = attr_cache_lookup_entry(cache, relfile); - if (!entry) - error = attr_cache_make_entry(&entry, repo, relfile); - else if (entry->file[source] != NULL) { - file = entry->file[source]; + entry = attr_cache_lookup_entry(cache, filename); + + if (!entry) { + error = attr_cache_make_entry(&entry, repo, filename); + } else if (entry->file[source->type] != NULL) { + file = entry->file[source->type]; GIT_REFCOUNT_INC(file); } @@ -210,9 +211,7 @@ 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, + git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros) { @@ -221,19 +220,21 @@ int git_attr_cache__get( git_attr_file_entry *entry = NULL; git_attr_file *file = NULL, *updated = NULL; - if ((error = attr_cache_lookup( - &file, &entry, repo, attr_session, source, base, filename)) < 0) + if ((error = attr_cache_lookup(&file, &entry, repo, attr_session, source)) < 0) 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, attr_session, file)) > 0) - error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser, allow_macros); + if (!file || + (error = git_attr_file__out_of_date(repo, attr_session, file, source)) > 0) + error = git_attr_file__load(&updated, repo, attr_session, + entry, source, parser, + allow_macros); /* if we loaded the file, insert into and/or update cache */ if (updated) { - if ((error = attr_cache_upsert(cache, updated)) < 0) + if ((error = attr_cache_upsert(cache, updated)) < 0) { git_attr_file__free(updated); - else { + } else { git_attr_file__free(file); /* offset incref from lookup */ file = updated; } @@ -260,7 +261,7 @@ int git_attr_cache__get( bool git_attr_cache__is_cached( git_repository *repo, - git_attr_file_source source, + git_attr_file_source_t source_type, const char *filename) { git_attr_cache *cache = git_repository_attr_cache(repo); @@ -273,7 +274,7 @@ bool git_attr_cache__is_cached( if ((entry = git_strmap_get(files, filename)) == NULL) return false; - return entry && (entry->file[source] != NULL); + return entry && (entry->file[source_type] != NULL); } diff --git a/src/attrcache.h b/src/attrcache.h index 5e2fb29f4..b13e0e8f0 100644 --- a/src/attrcache.h +++ b/src/attrcache.h @@ -31,16 +31,14 @@ 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, + git_attr_file_source *source, git_attr_file_parser parser, bool allow_macros); extern bool git_attr_cache__is_cached( git_repository *repo, - git_attr_file_source source, - const char *path); + git_attr_file_source_t source_type, + const char *filename); extern int git_attr_cache__alloc_file_entry( git_attr_file_entry **out, diff --git a/src/blob.c b/src/blob.c index 169e34503..01ebf075e 100644 --- a/src/blob.c +++ b/src/blob.c @@ -421,7 +421,7 @@ int git_blob_filter( int error = 0; git_filter_list *fl = NULL; git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT; - git_filter_flag_t flags = GIT_FILTER_DEFAULT; + git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT; GIT_ASSERT_ARG(blob); GIT_ASSERT_ARG(path); @@ -441,14 +441,19 @@ int git_blob_filter( return 0; if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) - flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES; + filter_opts.flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES; if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD) != 0) - flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD; + filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD; - if (!(error = git_filter_list_load( + if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) { + filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_COMMIT; + filter_opts.commit_id = opts.commit_id; + } + + if (!(error = git_filter_list_load_ext( &fl, git_blob_owner(blob), blob, path, - GIT_FILTER_TO_WORKTREE, flags))) { + GIT_FILTER_TO_WORKTREE, &filter_opts))) { error = git_filter_list_apply_to_blob(out, fl, blob); diff --git a/src/checkout.c b/src/checkout.c index 2f84df7a0..31d473ec8 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1513,7 +1513,7 @@ static int blob_content_to_file( int flags = data->opts.file_open_flags; mode_t file_mode = data->opts.file_mode ? data->opts.file_mode : entry_filemode; - git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT; + git_filter_session filter_session = GIT_FILTER_SESSION_INIT; struct checkout_stream writer; mode_t mode; git_filter_list *fl = NULL; @@ -1536,13 +1536,13 @@ static int blob_content_to_file( return fd; } - filter_opts.attr_session = &data->attr_session; - filter_opts.temp_buf = &data->tmp; + filter_session.attr_session = &data->attr_session; + filter_session.temp_buf = &data->tmp; if (!data->opts.disable_filters && - (error = git_filter_list__load_ext( + (error = git_filter_list__load( &fl, data->repo, blob, hint_path, - GIT_FILTER_TO_WORKTREE, &filter_opts))) { + GIT_FILTER_TO_WORKTREE, &filter_session))) { p_close(fd); return error; } @@ -2064,7 +2064,7 @@ static int checkout_write_merge( git_merge_file_result result = {0}; git_filebuf output = GIT_FILEBUF_INIT; git_filter_list *fl = NULL; - git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT; + git_filter_session filter_session = GIT_FILTER_SESSION_INIT; int error = 0; if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3) @@ -2114,12 +2114,12 @@ static int checkout_write_merge( in_data.ptr = (char *)result.ptr; in_data.size = result.len; - filter_opts.attr_session = &data->attr_session; - filter_opts.temp_buf = &data->tmp; + filter_session.attr_session = &data->attr_session; + filter_session.temp_buf = &data->tmp; - if ((error = git_filter_list__load_ext( + if ((error = git_filter_list__load( &fl, data->repo, NULL, git_buf_cstr(&path_workdir), - GIT_FILTER_TO_WORKTREE, &filter_opts)) < 0 || + GIT_FILTER_TO_WORKTREE, &filter_session)) < 0 || (error = git_filter_list__convert_buf(&out_data, fl, &in_data)) < 0) goto done; } else { diff --git a/src/filter.c b/src/filter.c index 6c09a6ad5..eed175e88 100644 --- a/src/filter.c +++ b/src/filter.c @@ -19,12 +19,12 @@ #include "array.h" struct git_filter_source { - git_repository *repo; - const char *path; - git_oid oid; /* zero if unknown (which is likely) */ - uint16_t filemode; /* zero if unknown */ - git_filter_mode_t mode; - uint32_t flags; + git_repository *repo; + const char *path; + git_oid oid; /* zero if unknown (which is likely) */ + uint16_t filemode; /* zero if unknown */ + git_filter_mode_t mode; + git_filter_options options; }; typedef struct { @@ -396,7 +396,7 @@ git_filter_mode_t git_filter_source_mode(const git_filter_source *src) uint32_t git_filter_source_flags(const git_filter_source *src) { - return src->flags; + return src->options.flags; } static int filter_list_new( @@ -416,7 +416,8 @@ static int filter_list_new( fl->source.repo = src->repo; fl->source.path = fl->path; fl->source.mode = src->mode; - fl->source.flags = src->flags; + + memcpy(&fl->source.options, &src->options, sizeof(git_filter_options)); *out = fl; return 0; @@ -425,25 +426,30 @@ static int filter_list_new( static int filter_list_check_attributes( const char ***out, git_repository *repo, - git_attr_session *attr_session, + git_filter_session *filter_session, git_filter_def *fdef, const git_filter_source *src) { const char **strs = git__calloc(fdef->nattrs, sizeof(const char *)); - uint32_t flags = 0; + git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT; size_t i; int error; GIT_ERROR_CHECK_ALLOC(strs); - if ((src->flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) - flags |= GIT_ATTR_CHECK_NO_SYSTEM; + if ((src->options.flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) + attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM; + + if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0) + attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD; - if ((src->flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0) - flags |= GIT_ATTR_CHECK_INCLUDE_HEAD; + if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) { + attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_COMMIT; + attr_opts.commit_id = src->options.commit_id; + } error = git_attr_get_many_with_session( - strs, repo, attr_session, flags, src->path, fdef->nattrs, fdef->attrs); + strs, repo, filter_session->attr_session, &attr_opts, 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) { @@ -488,17 +494,17 @@ int git_filter_list_new( src.repo = repo; src.path = NULL; src.mode = mode; - src.flags = flags; + src.options.flags = flags; return filter_list_new(out, &src); } -int git_filter_list__load_ext( +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, - git_filter_options *filter_opts) + git_filter_session *filter_session) { int error = 0; git_filter_list *fl = NULL; @@ -515,7 +521,8 @@ int git_filter_list__load_ext( src.repo = repo; src.path = path; src.mode = mode; - src.flags = filter_opts->flags; + + memcpy(&src.options, &filter_session->options, sizeof(git_filter_options)); if (blob) git_oid_cpy(&src.oid, git_blob_id(blob)); @@ -529,7 +536,8 @@ int git_filter_list__load_ext( if (fdef->nattrs > 0) { error = filter_list_check_attributes( - &values, repo, filter_opts->attr_session, fdef, &src); + &values, repo, + filter_session, fdef, &src); if (error == GIT_ENOTFOUND) { error = 0; @@ -556,7 +564,7 @@ int git_filter_list__load_ext( if ((error = filter_list_new(&fl, &src)) < 0) break; - fl->temp_buf = filter_opts->temp_buf; + fl->temp_buf = filter_session->temp_buf; } fe = git_array_alloc(fl->filters); @@ -580,6 +588,23 @@ int git_filter_list__load_ext( return error; } +int git_filter_list_load_ext( + git_filter_list **filters, + git_repository *repo, + git_blob *blob, /* can be NULL */ + const char *path, + git_filter_mode_t mode, + git_filter_options *opts) +{ + git_filter_session filter_session = GIT_FILTER_SESSION_INIT; + + if (opts) + memcpy(&filter_session.options, opts, sizeof(git_filter_options)); + + return git_filter_list__load( + filters, repo, blob, path, mode, &filter_session); +} + int git_filter_list_load( git_filter_list **filters, git_repository *repo, @@ -588,12 +613,12 @@ int git_filter_list_load( git_filter_mode_t mode, uint32_t flags) { - git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT; + git_filter_session filter_session = GIT_FILTER_SESSION_INIT; - filter_opts.flags = flags; + filter_session.options.flags = flags; - return git_filter_list__load_ext( - filters, repo, blob, path, mode, &filter_opts); + return git_filter_list__load( + filters, repo, blob, path, mode, &filter_session); } void git_filter_list_free(git_filter_list *fl) diff --git a/src/filter.h b/src/filter.h index bbc4c0fb2..55ed50e97 100644 --- a/src/filter.h +++ b/src/filter.h @@ -16,24 +16,24 @@ #define GIT_FILTER_BYTES_TO_CHECK_NUL 8000 typedef struct { + git_filter_options options; git_attr_session *attr_session; git_buf *temp_buf; - uint32_t flags; -} git_filter_options; +} git_filter_session; -#define GIT_FILTER_OPTIONS_INIT {0} +#define GIT_FILTER_SESSION_INIT {GIT_FILTER_OPTIONS_INIT, 0} extern int git_filter_global_init(void); extern void git_filter_free(git_filter *filter); -extern int git_filter_list__load_ext( +extern 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, - git_filter_options *filter_opts); + git_filter_session *filter_session); /* * The given input buffer will be converted to the given output buffer. diff --git a/src/ignore.c b/src/ignore.c index 948c58d85..f7551cddb 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -247,11 +247,12 @@ static int push_ignore_file( const char *base, const char *filename) { - int error = 0; + git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename }; git_attr_file *file = NULL; + int error = 0; + + error = git_attr_cache__get(&file, ignores->repo, NULL, &source, parse_ignore_file, false); - error = git_attr_cache__get(&file, ignores->repo, NULL, GIT_ATTR_FILE__FROM_FILE, - base, filename, parse_ignore_file, false); if (error < 0) return error; @@ -272,13 +273,13 @@ static int push_one_ignore(void *payload, const char *path) static int get_internal_ignores(git_attr_file **out, git_repository *repo) { + git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_MEMORY, NULL, GIT_IGNORE_INTERNAL }; int error; if ((error = git_attr_cache__init(repo)) < 0) return error; - error = git_attr_cache__get(out, repo, NULL, GIT_ATTR_FILE__IN_MEMORY, NULL, - GIT_IGNORE_INTERNAL, NULL, false); + error = git_attr_cache__get(out, repo, NULL, &source, NULL, false); /* if internal rules list is empty, insert default rules */ if (!error && !(*out)->rules.length) diff --git a/tests/attr/lookup.c b/tests/attr/lookup.c index 29a66b001..bfb2e872f 100644 --- a/tests/attr/lookup.c +++ b/tests/attr/lookup.c @@ -236,6 +236,7 @@ void test_attr_lookup__check_attr_examples(void) void test_attr_lookup__from_buffer(void) { git_attr_file *file; + git_attr_file_source source = {0}; struct attr_expected cases[] = { { "abc", "foo", EXPECT_TRUE, NULL }, @@ -250,7 +251,7 @@ void test_attr_lookup__from_buffer(void) { NULL, NULL, 0, NULL } }; - cl_git_pass(git_attr_file__new(&file, NULL, 0)); + cl_git_pass(git_attr_file__new(&file, NULL, &source)); cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz", true)); diff --git a/tests/attr/repo.c b/tests/attr/repo.c index 36beeb095..eabc033eb 100644 --- a/tests/attr/repo.c +++ b/tests/attr/repo.c @@ -71,11 +71,11 @@ void test_attr_repo__get_one(void) } cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes")); cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes")); cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes")); } void test_attr_repo__get_one_start_deep(void) @@ -92,11 +92,11 @@ void test_attr_repo__get_one_start_deep(void) } cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes")); cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes")); cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes")); } void test_attr_repo__get_many(void) @@ -341,7 +341,7 @@ void test_attr_repo__sysdir_with_session(void) g_repo = cl_git_sandbox_reopen(); cl_git_pass(git_attr_session__init(&session, g_repo)); - cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, 0, "file", ARRAY_SIZE(attrs), attrs)); + cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, NULL, "file", ARRAY_SIZE(attrs), attrs)); cl_assert_equal_s(values[0], "1"); cl_assert_equal_s(values[1], "2"); diff --git a/tests/filter/bare.c b/tests/filter/bare.c index 7319b5203..f8e34232f 100644 --- a/tests/filter/bare.c +++ b/tests/filter/bare.c @@ -132,3 +132,63 @@ void test_filter_bare__sanitizes(void) git_blob_free(blob); } +void test_filter_bare__from_specific_commit_one(void) +{ + git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT; + git_blob *blob; + git_buf buf = { 0 }; + git_oid commit_id; + + cl_git_pass(git_oid_fromstr(&commit_id, "b8986fec0f7bde90f78ac72706e782d82f24f2f0")); + + opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES; + opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT; + opts.commit_id = &commit_id; + + cl_git_pass(git_revparse_single( + (git_object **)&blob, g_repo, "055c872")); /* ident */ + + cl_assert_equal_s("$Id$\n", git_blob_rawcontent(blob)); + + cl_git_pass(git_blob_filter(&buf, blob, "ident.bin", &opts)); + cl_assert_equal_s("$Id$\n", buf.ptr); + + cl_git_pass(git_blob_filter(&buf, blob, "ident.identlf", &opts)); + cl_assert_equal_s("$Id: 055c8729cdcc372500a08db659c045e16c4409fb $\n", buf.ptr); + + git_buf_dispose(&buf); + git_blob_free(blob); +} + +void test_filter_bare__from_specific_commit_with_no_attributes_file(void) +{ + git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT; + git_blob *blob; + git_buf buf = { 0 }; + git_oid commit_id; + + cl_git_pass(git_oid_fromstr(&commit_id, "5afb6a14a864e30787857dd92af837e8cdd2cb1b")); + + opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES; + opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT; + opts.commit_id = &commit_id; + + cl_git_pass(git_revparse_single( + (git_object **)&blob, g_repo, "799770d")); /* all-lf */ + + cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob)); + + cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &opts)); + cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr); + + /* we never convert CRLF -> LF on platforms that have LF */ + cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &opts)); + cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr); + + /* we never convert CRLF -> LF on platforms that have LF */ + cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &opts)); + cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr); + + git_buf_dispose(&buf); + git_blob_free(blob); +} diff --git a/tests/ignore/status.c b/tests/ignore/status.c index 188fbe469..83e821bdd 100644 --- a/tests/ignore/status.c +++ b/tests/ignore/status.c @@ -70,9 +70,9 @@ void test_ignore_status__0(void) /* confirm that ignore files were cached */ cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/exclude")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/exclude")); cl_assert(git_attr_cache__is_cached( - g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitignore")); + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitignore")); } diff --git a/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb b/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb Binary files differnew file mode 100644 index 000000000..44076ca39 --- /dev/null +++ b/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb diff --git a/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d b/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d Binary files differnew file mode 100644 index 000000000..ca97967b8 --- /dev/null +++ b/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d diff --git a/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c b/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c Binary files differnew file mode 100644 index 000000000..0576e6229 --- /dev/null +++ b/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c diff --git a/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0 b/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0 Binary files differnew file mode 100644 index 000000000..7501c88e6 --- /dev/null +++ b/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0 diff --git a/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0 b/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0 new file mode 100644 index 000000000..d745d20e6 --- /dev/null +++ b/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0 @@ -0,0 +1,3 @@ +xM +0]s%3Dt
$b6FQ2McnSWU`6*CĽGw,|S,\#Y8t);
qȳ +5VG-~ۥ2=`ֲml?13cn
H]aj@U
\ No newline at end of file diff --git a/tests/resources/crlf.git/refs/heads/ident b/tests/resources/crlf.git/refs/heads/ident new file mode 100644 index 000000000..8732f0cb9 --- /dev/null +++ b/tests/resources/crlf.git/refs/heads/ident @@ -0,0 +1 @@ +b8986fec0f7bde90f78ac72706e782d82f24f2f0 diff --git a/tests/resources/crlf.git/refs/heads/no-ident b/tests/resources/crlf.git/refs/heads/no-ident new file mode 100644 index 000000000..fa9a6737b --- /dev/null +++ b/tests/resources/crlf.git/refs/heads/no-ident @@ -0,0 +1 @@ +1ec507638b806aba45d6142082885f2a9e88322d |