We do this by popping off elements on the attribute stack, until we reach the level where a new .gitattributes was checked out. The next time someone calls git_checkattr(), it will reconstruct the attributes from that point. --- attr.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------- attr.h | 1 + entry.c | 23 ++++++++++++++++++++ 3 files changed, 84 insertions(+), 12 deletions(-) diff --git a/attr.c b/attr.c index 17f6a4d..7deb51a 100644 --- a/attr.c +++ b/attr.c @@ -455,6 +455,23 @@ static void bootstrap_attr_stack(void) } } +static void pop_attr_stack(const char *path, int dirlen) +{ + struct attr_stack *elem; + while (attr_stack && attr_stack->origin) { + int namelen = strlen(attr_stack->origin); + + elem = attr_stack; + if (namelen <= dirlen && + !strncmp(elem->origin, path, namelen)) + break; + + debug_pop(elem); + attr_stack = elem->prev; + free_attr_elem(elem); + } +} + static void prepare_attr_stack(const char *path, int dirlen) { struct attr_stack *elem, *info; @@ -489,18 +506,7 @@ static void prepare_attr_stack(const char *path, int dirlen) * Pop the ones from directories that are not the prefix of * the path we are checking. */ - while (attr_stack && attr_stack->origin) { - int namelen = strlen(attr_stack->origin); - - elem = attr_stack; - if (namelen <= dirlen && - !strncmp(elem->origin, path, namelen)) - break; - - debug_pop(elem); - attr_stack = elem->prev; - free_attr_elem(elem); - } + pop_attr_stack(path, dirlen); /* * Read from parent directories and push them down @@ -642,3 +648,45 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) return 0; } + +void git_attr_invalidate_path(const char *path) +{ + int dirlen; + const char *cp; + struct attr_stack *info, *elem; + + bootstrap_attr_stack(); + + /* + * Pop the "info" one that is always at the top of the stack. + */ + info = attr_stack; + attr_stack = info->prev; + + cp = strrchr(path, '/'); + dirlen = cp ? cp - path : 0; + /* Pop everything up to, and including, path. */ + pop_attr_stack(path, dirlen); + + if (!strcmp(path, "") && attr_stack->origin && !strcmp(attr_stack->origin, "")) { + /* Special handling when the root attributes must be invalidated. */ + elem = attr_stack; + debug_pop(elem); + attr_stack = elem->prev; + free_attr_elem(elem); + + if (!is_bare_repository()) { + elem = read_attr(GITATTRIBUTES_FILE, 1); + elem->origin = strdup(""); + elem->prev = attr_stack; + attr_stack = elem; + debug_push(elem); + } + } + + /* + * Finally push the "info" one at the top of the stack. + */ + info->prev = attr_stack; + attr_stack = info; +} diff --git a/attr.h b/attr.h index f1c2038..8f4135b 100644 --- a/attr.h +++ b/attr.h @@ -30,5 +30,6 @@ struct git_attr_check { }; int git_checkattr(const char *path, int, struct git_attr_check *); +void git_attr_invalidate_path(const char *path); #endif /* ATTR_H */ diff --git a/entry.c b/entry.c index 05aa58d..121c979 100644 --- a/entry.c +++ b/entry.c @@ -1,6 +1,7 @@ #include "cache.h" #include "blob.h" #include "dir.h" +#include "attr.h" static void create_directories(const char *path, const struct checkout *state) { @@ -91,6 +92,9 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout { int fd; long wrote; + int gitattrlen; + int pathlen; + char *inv_path; switch (ce->ce_mode & S_IFMT) { char *new; @@ -171,6 +175,25 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout return error("git checkout-index: unknown file mode for %s", path); } + gitattrlen = strlen(GITATTRIBUTES_FILE); + pathlen = strlen(path); + if (!strncmp(path + pathlen - gitattrlen, GITATTRIBUTES_FILE, gitattrlen)) { + /* Invalidate attributes if a new .gitattributes file was checked out. */ + inv_path = strrchr(path, '/'); + if (!inv_path) { + pathlen = 0; + inv_path = xmalloc(1); + *inv_path = '\0'; + } else { + pathlen = inv_path - path; + inv_path = xmalloc(pathlen + 1); + strncpy(inv_path, path, pathlen); + inv_path[pathlen] = '\0'; + } + git_attr_invalidate_path(inv_path); + free(inv_path); + } + if (state->refresh_cache) { struct stat st; lstat(ce->name, &st); -- 1.6.2.105.g16bc7.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html