On Thu, Apr 16, 2009 at 8:50 PM, Nguyen Thai Ngoc Duy <pclouds@xxxxxxxxx> wrote: > I was thinking about loading .gitattributes inside write_archive_entry > too, to avoid calling read_tree_recursive twice, but it requires > .gitattributes to be traversed first. Won't work if there are files > .abc, .def... > > If read_tree_recusive() expose its tree to read_tree_fn_t, we can then > look ahead and load .gitattributes, but that requires changing > read_tree_fn_t interface. I'll see if it's feasible to make a > customized read_tree_recusive() just for archive.c Here it is (again on top of your patch). Need to read directories twice, but not as bad as read_tree_recursive() twice. -- Duy
diff --git a/archive.c b/archive.c index 0ce628b..8df53a8 100644 --- a/archive.c +++ b/archive.c @@ -97,6 +97,43 @@ struct archiver_context { write_archive_entry_fn_t write_entry; }; +static int read_gitattr_to_index(struct tree *tree, const char *base, int baselen, struct archiver_args *args) +{ + struct tree_desc desc; + struct name_entry entry; + struct cache_entry *ce; + unsigned int size; + int pathlen; + + if (parse_tree(tree)) + return -1; + + init_tree_desc(&desc, tree->buffer, tree->size); + + while (tree_entry(&desc, &entry)) { + if (S_ISDIR(entry.mode) || S_ISGITLINK(entry.mode)) + continue; + if (strcmp(entry.path, GITATTRIBUTES_FILE)) + continue; + pathlen = tree_entry_len(entry.path, entry.sha1); + baselen -= args->baselen; /* remove user prefix */ + if (baselen) + baselen++; /* slash */ + size = cache_entry_size(baselen + pathlen); + ce = xcalloc(1, size); + ce->ce_mode = create_ce_mode(entry.mode); + ce->ce_flags = create_ce_flags(baselen + pathlen, 0); + if (baselen) { + memcpy(ce->name, base + args->baselen, baselen-1); + ce->name[baselen-1] = '/'; + } + memcpy(ce->name + baselen, entry.path, pathlen + 1); + hashcpy(ce->sha1, entry.sha1); + return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK); + } + return 0; +} + static int write_archive_entry(const unsigned char *sha1, const char *base, int baselen, const char *filename, unsigned mode, int stage, void *context) @@ -119,11 +156,25 @@ static int write_archive_entry(const unsigned char *sha1, const char *base, strbuf_addstr(&path, filename); path_without_prefix = path.buf + args->baselen; - setup_archive_check(check); - if (!git_checkattr(path_without_prefix, ARRAY_SIZE(check), check)) { - if (ATTR_TRUE(check[0].value)) - return 0; - convert = ATTR_TRUE(check[1].value); + if (S_ISDIR(mode)) { + /* + * we want to read .gitattributes before any entry is processed + * so every time we get a directory entry, we look ahead to see + * if there is .gitattributes and load it + * + * later when the directory is processed, .gitattributes is + * already ready in index for git_checkattr() + */ + if (!args->worktree_attributes) + read_gitattr_to_index(lookup_tree(sha1), base, baselen, args); + } + else { + setup_archive_check(check); + if (!git_checkattr(path_without_prefix, ARRAY_SIZE(check), check)) { + if (ATTR_TRUE(check[0].value)) + return 0; + convert = ATTR_TRUE(check[1].value); + } } if (S_ISDIR(mode) || S_ISGITLINK(mode)) { @@ -151,8 +202,6 @@ int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry) { struct archiver_context context; - struct unpack_trees_options opts; - struct tree_desc t; int err; if (args->baselen > 0 && args->base[args->baselen - 1] == '/') { @@ -171,19 +220,9 @@ int write_archive_entries(struct archiver_args *args, context.args = args; context.write_entry = write_entry; - /* - * Setup index and instruct attr to read index only - */ if (!args->worktree_attributes) { - memset(&opts, 0, sizeof(opts)); - opts.index_only = 1; - opts.head_idx = -1; - opts.src_index = &the_index; - opts.dst_index = &the_index; - opts.fn = oneway_merge; - init_tree_desc(&t, args->tree->buffer, args->tree->size); - if (unpack_trees(1, &t, &opts)) - return -1; + /* read .gitattributes at root if any */ + read_gitattr_to_index(args->tree, args->base, args->baselen, args); git_attr_set_direction(GIT_ATTR_INDEX, &the_index); } @@ -209,13 +248,23 @@ static const struct archiver *lookup_archiver(const char *name) } static void parse_pathspec_arg(const char **pathspec, - struct archiver_args *ar_args) + struct archiver_args *ar_args, + const char *prefix) { - ar_args->pathspec = get_pathspec(ar_args->base, pathspec); + struct strbuf s = STRBUF_INIT; + if (ar_args->base) + strbuf_addstr(&s, ar_args->base); + if (prefix) + strbuf_addstr(&s, prefix); + ar_args->pathspec = get_pathspec(s.len ? s.buf : NULL, pathspec); + /* + * s.buf must never be freed because + * get_pathspec does not duplicate it + */ } static void parse_treeish_arg(const char **argv, - struct archiver_args *ar_args, const char *prefix) + struct archiver_args *ar_args) { const char *name = argv[0]; const unsigned char *commit_sha1; @@ -240,18 +289,6 @@ static void parse_treeish_arg(const char **argv, if (tree == NULL) die("not a tree object"); - if (prefix) { - unsigned char tree_sha1[20]; - unsigned int mode; - int err; - - err = get_tree_entry(tree->object.sha1, prefix, - tree_sha1, &mode); - if (err || !S_ISDIR(mode)) - die("current working directory is untracked"); - - tree = parse_tree_indirect(tree_sha1); - } ar_args->tree = tree; ar_args->commit_sha1 = commit_sha1; ar_args->commit = commit; @@ -360,8 +397,8 @@ int write_archive(int argc, const char **argv, const char *prefix, if (setup_prefix && prefix == NULL) prefix = setup_git_directory(); - parse_treeish_arg(argv, &args, prefix); - parse_pathspec_arg(argv + 1, &args); + parse_treeish_arg(argv, &args); + parse_pathspec_arg(argv + 1, &args, prefix); git_config(git_default_config, NULL);