From: Junio C Hamano Date: Wed, 8 Oct 2014 20:05:25 +0000 (-0700) Subject: Merge branch 'nd/archive-pathspec' X-Git-Tag: v2.2.0-rc0~63 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/b2c45f5b961b3b5d894fb723d2031e8ff41cadf7?hp=-c Merge branch 'nd/archive-pathspec' "git archive" learned to filter what gets archived with pathspec. * nd/archive-pathspec: archive: support filtering paths with glob --- b2c45f5b961b3b5d894fb723d2031e8ff41cadf7 diff --combined archive.c index 952a659bcb,5bdab7b148..94a9981adf --- a/archive.c +++ b/archive.c @@@ -5,6 -5,7 +5,7 @@@ #include "archive.h" #include "parse-options.h" #include "unpack-trees.h" + #include "dir.h" static char const * const archive_usage[] = { N_("git archive [options] [...]"), @@@ -17,7 -18,6 +18,7 @@@ static const struct archiver **archivers; static int nr_archivers; static int alloc_archivers; +static int remote_allow_unreachable; void register_archiver(struct archiver *ar) { @@@ -98,9 -98,19 +99,19 @@@ static void setup_archive_check(struct check[1].attr = attr_export_subst; } + struct directory { + struct directory *up; + unsigned char sha1[20]; + int baselen, len; + unsigned mode; + int stage; + char path[FLEX_ARRAY]; + }; + struct archiver_context { struct archiver_args *args; write_archive_entry_fn_t write_entry; + struct directory *bottom; }; static int write_archive_entry(const unsigned char *sha1, const char *base, @@@ -146,6 -156,65 +157,65 @@@ return write_entry(args, sha1, path.buf, path.len, mode); } + static void queue_directory(const unsigned char *sha1, + const char *base, int baselen, const char *filename, + unsigned mode, int stage, struct archiver_context *c) + { + struct directory *d; + d = xmallocz(sizeof(*d) + baselen + 1 + strlen(filename)); + d->up = c->bottom; + d->baselen = baselen; + d->mode = mode; + d->stage = stage; + c->bottom = d; + d->len = sprintf(d->path, "%.*s%s/", baselen, base, filename); + hashcpy(d->sha1, sha1); + } + + static int write_directory(struct archiver_context *c) + { + struct directory *d = c->bottom; + int ret; + + if (!d) + return 0; + c->bottom = d->up; + d->path[d->len - 1] = '\0'; /* no trailing slash */ + ret = + write_directory(c) || + write_archive_entry(d->sha1, d->path, d->baselen, + d->path + d->baselen, d->mode, + d->stage, c) != READ_TREE_RECURSIVE; + free(d); + return ret ? -1 : 0; + } + + static int queue_or_write_archive_entry(const unsigned char *sha1, + const char *base, int baselen, const char *filename, + unsigned mode, int stage, void *context) + { + struct archiver_context *c = context; + + while (c->bottom && + !(baselen >= c->bottom->len && + !strncmp(base, c->bottom->path, c->bottom->len))) { + struct directory *next = c->bottom->up; + free(c->bottom); + c->bottom = next; + } + + if (S_ISDIR(mode)) { + queue_directory(sha1, base, baselen, filename, + mode, stage, c); + return READ_TREE_RECURSIVE; + } + + if (write_directory(c)) + return -1; + return write_archive_entry(sha1, base, baselen, filename, mode, + stage, context); + } + int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry) { @@@ -167,6 -236,7 +237,7 @@@ return err; } + memset(&context, 0, sizeof(context)); context.args = args; context.write_entry = write_entry; @@@ -187,9 -257,17 +258,17 @@@ } err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec, - write_archive_entry, &context); + args->pathspec.has_wildcard ? + queue_or_write_archive_entry : + write_archive_entry, + &context); if (err == READ_TREE_RECURSIVE) err = 0; + while (context.bottom) { + struct directory *next = context.bottom->up; + free(context.bottom); + context.bottom = next; + } return err; } @@@ -211,7 -289,16 +290,16 @@@ static int reject_entry(const unsigned int baselen, const char *filename, unsigned mode, int stage, void *context) { - return -1; + int ret = -1; + if (S_ISDIR(mode)) { + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, base); + strbuf_addstr(&sb, filename); + if (!match_pathspec(context, sb.buf, sb.len, 0, NULL, 1)) + ret = READ_TREE_RECURSIVE; + strbuf_release(&sb); + } + return ret; } static int path_exists(struct tree *tree, const char *path) @@@ -221,7 -308,9 +309,9 @@@ int ret; parse_pathspec(&pathspec, 0, 0, "", paths); - ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL); + pathspec.recursive = 1; + ret = read_tree_recursive(tree, "", 0, 0, &pathspec, + reject_entry, &pathspec); free_pathspec(&pathspec); return ret != 0; } @@@ -237,6 -326,7 +327,7 @@@ static void parse_pathspec_arg(const ch parse_pathspec(&ar_args->pathspec, 0, PATHSPEC_PREFER_FULL, "", pathspec); + ar_args->pathspec.recursive = 1; if (pathspec) { while (*pathspec) { if (**pathspec && !path_exists(ar_args->tree, *pathspec)) @@@ -258,10 -348,10 +349,10 @@@ static void parse_treeish_arg(const cha unsigned char sha1[20]; /* Remotes are only allowed to fetch actual refs */ - if (remote) { + if (remote && !remote_allow_unreachable) { char *ref = NULL; - const char *colon = strchr(name, ':'); - int refnamelen = colon ? colon - name : strlen(name); + const char *colon = strchrnul(name, ':'); + int refnamelen = colon - name; if (!dwim_ref(name, refnamelen, sha1, &ref)) die("no such ref: %.*s", refnamelen, name); @@@ -412,9 -502,7 +503,9 @@@ int write_archive(int argc, const char if (setup_prefix && prefix == NULL) prefix = setup_git_directory_gently(&nongit); + git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable); git_config(git_default_config, NULL); + init_tar_archiver(); init_zip_archiver(); diff --combined t/t5000-tar-tree.sh index 7b8babd89b,c9abab493a..d01bbdc968 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@@ -72,7 -72,7 +72,7 @@@ check_tar() for header in *.paxheader do data=${header%.paxheader}.data && - if test -h $data -o -e $data + if test -h $data || test -e $data then path=$(get_pax_header $header path) && if test -n "$path" @@@ -119,10 -119,14 +119,10 @@@ test_expect_success 'echo ignore me >a/ignored && echo ignored export-ignore >.git/info/attributes' -test_expect_success \ - 'add files to repository' \ - 'find a -type f | xargs git update-index --add && - find a -type l | xargs git update-index --add && - treeid=`git write-tree` && - echo $treeid >treeid && - git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \ - git commit-tree $treeid >.git/info/attributes && @@@ -160,7 -164,7 +160,7 @@@ check_tar with_olde-prefix olde test_expect_success 'git archive on large files' ' test_config core.bigfilethreshold 1 && git archive HEAD >b3.tar && - test_cmp b.tar b3.tar + test_cmp_bin b.tar b3.tar ' test_expect_success \ @@@ -169,15 -173,15 +169,15 @@@ test_expect_success \ 'git archive vs. the same in a bare repo' \ - 'test_cmp b.tar b3.tar' + 'test_cmp_bin b.tar b3.tar' test_expect_success 'git archive with --output' \ 'git archive --output=b4.tar HEAD && - test_cmp b.tar b4.tar' + test_cmp_bin b.tar b4.tar' test_expect_success 'git archive --remote' \ 'git archive --remote=. HEAD >b5.tar && - test_cmp b.tar b5.tar' + test_cmp_bin b.tar b5.tar' test_expect_success \ 'validate file modification time' \ @@@ -194,7 -198,7 +194,7 @@@ test_expect_success test_expect_success 'git archive with --output, override inferred format' ' git archive --format=tar --output=d4.zip HEAD && - test_cmp b.tar d4.zip + test_cmp_bin b.tar d4.zip ' test_expect_success \ @@@ -203,21 -207,12 +203,21 @@@ test_expect_success 'clients cannot access unreachable commits' ' test_commit unreachable && - sha1=`git rev-parse HEAD` && + sha1=$(git rev-parse HEAD) && git reset --hard HEAD^ && git archive $sha1 >remote.tar && test_must_fail git archive --remote=. $sha1 >remote.tar ' +test_expect_success 'upload-archive can allow unreachable commits' ' + test_commit unreachable1 && + sha1=$(git rev-parse HEAD) && + git reset --hard HEAD^ && + git archive $sha1 >remote.tar && + test_config uploadarchive.allowUnreachable true && + git archive --remote=. $sha1 >remote.tar +' + test_expect_success 'setup tar filters' ' git config tar.tar.foo.command "tr ab ba" && git config tar.bar.command "tr ab ba" && @@@ -240,34 -235,34 +240,34 @@@ test_expect_success 'archive --list sho test_expect_success 'invoke tar filter by format' ' git archive --format=tar.foo HEAD >config.tar.foo && tr ab ba config.tar && - test_cmp b.tar config.tar && + test_cmp_bin b.tar config.tar && git archive --format=bar HEAD >config.bar && tr ab ba config.tar && - test_cmp b.tar config.tar + test_cmp_bin b.tar config.tar ' test_expect_success 'invoke tar filter by extension' ' git archive -o config-implicit.tar.foo HEAD && - test_cmp config.tar.foo config-implicit.tar.foo && + test_cmp_bin config.tar.foo config-implicit.tar.foo && git archive -o config-implicit.bar HEAD && - test_cmp config.tar.foo config-implicit.bar + test_cmp_bin config.tar.foo config-implicit.bar ' test_expect_success 'default output format remains tar' ' git archive -o config-implicit.baz HEAD && - test_cmp b.tar config-implicit.baz + test_cmp_bin b.tar config-implicit.baz ' test_expect_success 'extension matching requires dot' ' git archive -o config-implicittar.foo HEAD && - test_cmp b.tar config-implicittar.foo + test_cmp_bin b.tar config-implicittar.foo ' test_expect_success 'only enabled filters are available remotely' ' test_must_fail git archive --remote=. --format=tar.foo HEAD \ >remote.tar.foo && git archive --remote=. --format=bar >remote.bar HEAD && - test_cmp remote.bar config.bar + test_cmp_bin remote.bar config.bar ' test_expect_success GZIP 'git archive --format=tgz' ' @@@ -276,27 -271,27 +276,27 @@@ test_expect_success GZIP 'git archive --format=tar.gz' ' git archive --format=tar.gz HEAD >j1.tar.gz && - test_cmp j.tgz j1.tar.gz + test_cmp_bin j.tgz j1.tar.gz ' test_expect_success GZIP 'infer tgz from .tgz filename' ' git archive --output=j2.tgz HEAD && - test_cmp j.tgz j2.tgz + test_cmp_bin j.tgz j2.tgz ' test_expect_success GZIP 'infer tgz from .tar.gz filename' ' git archive --output=j3.tar.gz HEAD && - test_cmp j.tgz j3.tar.gz + test_cmp_bin j.tgz j3.tar.gz ' test_expect_success GZIP 'extract tgz file' ' gzip -d -c j.tar && - test_cmp b.tar j.tar + test_cmp_bin b.tar j.tar ' test_expect_success GZIP 'remote tar.gz is allowed by default' ' git archive --remote=. --format=tar.gz HEAD >remote.tar.gz && - test_cmp j.tgz remote.tar.gz + test_cmp_bin j.tgz remote.tar.gz ' test_expect_success GZIP 'remote tar.gz can be disabled' ' @@@ -305,4 -300,18 +305,18 @@@ >remote.tar.gz ' + test_expect_success 'archive and :(glob)' ' + git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual && + cat >expect </dev/null + ' + test_done