diff options
Diffstat (limited to 'archive.c')
-rw-r--r-- | archive.c | 227 |
1 files changed, 165 insertions, 62 deletions
@@ -1,18 +1,28 @@ -#include "cache.h" +#include "git-compat-util.h" +#include "abspath.h" #include "config.h" +#include "convert.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" +#include "object-name.h" +#include "path.h" +#include "pretty.h" +#include "setup.h" #include "refs.h" -#include "object-store.h" +#include "object-store-ll.h" #include "commit.h" +#include "tree.h" #include "tree-walk.h" #include "attr.h" #include "archive.h" #include "parse-options.h" #include "unpack-trees.h" -#include "dir.h" +#include "quote.h" static char const * const archive_usage[] = { N_("git archive [<options>] <tree-ish> [<path>...]"), - N_("git archive --list"), + "git archive --list", N_("git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]"), N_("git archive --remote <repo> [--exec <cmd>] --list"), NULL @@ -58,7 +68,8 @@ static void format_subst(const struct commit *commit, strbuf_add(&fmt, b + 8, c - b - 8); strbuf_add(buf, src, b - src); - format_commit_message(commit, fmt.buf, buf, ctx); + repo_format_commit_message(the_repository, commit, fmt.buf, + buf, ctx); len -= c + 1 - src; src = c + 1; } @@ -83,7 +94,7 @@ static void *object_file_to_archive(const struct archiver_args *args, (args->tree ? &args->tree->object.oid : NULL), oid); path += args->baselen; - buffer = read_object_file(oid, type, sizep); + buffer = repo_read_object_file(the_repository, oid, type, sizep); if (buffer && S_ISREG(mode)) { struct strbuf buf = STRBUF_INIT; size_t size = 0; @@ -165,18 +176,39 @@ static int write_archive_entry(const struct object_id *oid, const char *base, args->convert = check_attr_export_subst(check); } + if (args->prefix) { + static struct strbuf new_path = STRBUF_INIT; + static struct strbuf buf = STRBUF_INIT; + const char *rel; + + rel = relative_path(path_without_prefix, args->prefix, &buf); + + /* + * We don't add an entry for the current working + * directory when we are at the root; skip it also when + * we're in a subdirectory or submodule. Skip entries + * higher up as well. + */ + if (!strcmp(rel, "./") || starts_with(rel, "../")) + return S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0; + + /* rel can refer to path, so don't edit it in place */ + strbuf_reset(&new_path); + strbuf_add(&new_path, args->base, args->baselen); + strbuf_addstr(&new_path, rel); + strbuf_swap(&path, &new_path); + } + + if (args->verbose) + fprintf(stderr, "%.*s\n", (int)path.len, path.buf); + if (S_ISDIR(mode) || S_ISGITLINK(mode)) { - if (args->verbose) - fprintf(stderr, "%.*s\n", (int)path.len, path.buf); err = write_entry(args, oid, path.buf, path.len, mode, NULL, 0); if (err) return err; return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); } - if (args->verbose) - fprintf(stderr, "%.*s\n", (int)path.len, path.buf); - /* Stream it? */ if (S_ISREG(mode) && !args->convert && oid_object_info(args->repo, oid, &size) == OBJ_BLOB && @@ -185,7 +217,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base, buffer = object_file_to_archive(args, path.buf, oid, mode, &type, &size); if (!buffer) - return error(_("cannot read %s"), oid_to_hex(oid)); + return error(_("cannot read '%s'"), oid_to_hex(oid)); err = write_entry(args, oid, path.buf, path.len, mode, buffer, size); free(buffer); return err; @@ -263,6 +295,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid, struct extra_file_info { char *base; struct stat stat; + void *content; }; int write_archive_entries(struct archiver_args *args, @@ -306,7 +339,8 @@ int write_archive_entries(struct archiver_args *args, opts.src_index = args->repo->index; opts.dst_index = args->repo->index; opts.fn = oneway_merge; - init_tree_desc(&t, args->tree->buffer, args->tree->size); + init_tree_desc(&t, &args->tree->object.oid, + args->tree->buffer, args->tree->size); if (unpack_trees(1, &t, &opts)) return -1; git_attr_set_direction(GIT_ATTR_INDEX); @@ -331,19 +365,27 @@ int write_archive_entries(struct archiver_args *args, put_be64(fake_oid.hash, i + 1); - strbuf_reset(&path_in_archive); - if (info->base) - strbuf_addstr(&path_in_archive, info->base); - strbuf_addstr(&path_in_archive, basename(path)); - - strbuf_reset(&content); - if (strbuf_read_file(&content, path, info->stat.st_size) < 0) - err = error_errno(_("could not read '%s'"), path); - else - err = write_entry(args, &fake_oid, path_in_archive.buf, - path_in_archive.len, - info->stat.st_mode, - content.buf, content.len); + if (!info->content) { + strbuf_reset(&path_in_archive); + if (info->base) + strbuf_addstr(&path_in_archive, info->base); + strbuf_addstr(&path_in_archive, basename(path)); + + strbuf_reset(&content); + if (strbuf_read_file(&content, path, info->stat.st_size) < 0) + err = error_errno(_("cannot read '%s'"), path); + else + err = write_entry(args, &fake_oid, path_in_archive.buf, + path_in_archive.len, + canon_mode(info->stat.st_mode), + content.buf, content.len); + } else { + err = write_entry(args, &fake_oid, + path, strlen(path), + canon_mode(info->stat.st_mode), + info->content, info->stat.st_size); + } + if (err) break; } @@ -372,7 +414,8 @@ struct path_exists_context { struct archiver_args *args; }; -static int reject_entry(const struct object_id *oid, struct strbuf *base, +static int reject_entry(const struct object_id *oid UNUSED, + struct strbuf *base, const char *filename, unsigned mode, void *context) { @@ -392,6 +435,27 @@ static int reject_entry(const struct object_id *oid, struct strbuf *base, return ret; } +static int reject_outside(const struct object_id *oid UNUSED, + struct strbuf *base, const char *filename, + unsigned mode, void *context) +{ + struct archiver_args *args = context; + struct strbuf buf = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + int ret = 0; + + if (S_ISDIR(mode)) + return READ_TREE_RECURSIVE; + + strbuf_addbuf(&path, base); + strbuf_addstr(&path, filename); + if (starts_with(relative_path(path.buf, args->prefix, &buf), "../")) + ret = -1; + strbuf_release(&buf); + strbuf_release(&path); + return ret; +} + static int path_exists(struct archiver_args *args, const char *path) { const char *paths[] = { path, NULL }; @@ -399,8 +463,13 @@ static int path_exists(struct archiver_args *args, const char *path) int ret; ctx.args = args; - parse_pathspec(&ctx.pathspec, 0, 0, "", paths); + parse_pathspec(&ctx.pathspec, 0, PATHSPEC_PREFER_CWD, + args->prefix, paths); ctx.pathspec.recursive = 1; + if (args->prefix && read_tree(args->repo, args->tree, &ctx.pathspec, + reject_outside, args)) + die(_("pathspec '%s' matches files outside the " + "current directory"), path); ret = read_tree(args->repo, args->tree, &ctx.pathspec, reject_entry, &ctx); @@ -416,9 +485,8 @@ static void parse_pathspec_arg(const char **pathspec, * Also if pathspec patterns are dependent, we're in big * trouble as we test each one separately */ - parse_pathspec(&ar_args->pathspec, 0, - PATHSPEC_PREFER_FULL, - "", pathspec); + parse_pathspec(&ar_args->pathspec, 0, PATHSPEC_PREFER_CWD, + ar_args->prefix, pathspec); ar_args->pathspec.recursive = 1; if (pathspec) { while (*pathspec) { @@ -430,8 +498,7 @@ static void parse_pathspec_arg(const char **pathspec, } static void parse_treeish_arg(const char **argv, - struct archiver_args *ar_args, const char *prefix, - int remote) + struct archiver_args *ar_args, int remote) { const char *name = argv[0]; const struct object_id *commit_oid; @@ -446,13 +513,14 @@ static void parse_treeish_arg(const char **argv, const char *colon = strchrnul(name, ':'); int refnamelen = colon - name; - if (!dwim_ref(name, refnamelen, &oid, &ref, 0)) + if (!repo_dwim_ref(the_repository, name, refnamelen, &oid, &ref, 0)) die(_("no such ref: %.*s"), refnamelen, name); } else { - dwim_ref(name, strlen(name), &oid, &ref, 0); + repo_dwim_ref(the_repository, name, strlen(name), &oid, &ref, + 0); } - if (get_oid(name, &oid)) + if (repo_get_oid(the_repository, name, &oid)) die(_("not a valid object name: %s"), name); commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1); @@ -463,25 +531,13 @@ static void parse_treeish_arg(const char **argv, commit_oid = NULL; archive_time = time(NULL); } + if (ar_args->mtime_option) + archive_time = approxidate(ar_args->mtime_option); tree = parse_tree_indirect(&oid); - if (tree == NULL) + if (!tree) die(_("not a tree object: %s"), oid_to_hex(&oid)); - if (prefix) { - struct object_id tree_oid; - unsigned short mode; - int err; - - err = get_tree_entry(ar_args->repo, - &tree->object.oid, - prefix, &tree_oid, - &mode); - if (err || !S_ISDIR(mode)) - die(_("current working directory is untracked")); - - tree = parse_tree_indirect(&tree_oid); - } ar_args->refname = ref; ar_args->tree = tree; ar_args->commit_oid = commit_oid; @@ -489,10 +545,11 @@ static void parse_treeish_arg(const char **argv, ar_args->time = archive_time; } -static void extra_file_info_clear(void *util, const char *str) +static void extra_file_info_clear(void *util, const char *str UNUSED) { struct extra_file_info *info = util; free(info->base); + free(info->content); free(info); } @@ -514,14 +571,49 @@ static int add_file_cb(const struct option *opt, const char *arg, int unset) if (!arg) return -1; - path = prefix_filename(args->prefix, arg); - item = string_list_append_nodup(&args->extra_files, path); - item->util = info = xmalloc(sizeof(*info)); + info = xmalloc(sizeof(*info)); info->base = xstrdup_or_null(base); - if (stat(path, &info->stat)) - die(_("File not found: %s"), path); - if (!S_ISREG(info->stat.st_mode)) - die(_("Not a regular file: %s"), path); + + if (!strcmp(opt->long_name, "add-file")) { + path = prefix_filename(args->prefix, arg); + if (stat(path, &info->stat)) + die(_("File not found: %s"), path); + if (!S_ISREG(info->stat.st_mode)) + die(_("Not a regular file: %s"), path); + info->content = NULL; /* read the file later */ + } else if (!strcmp(opt->long_name, "add-virtual-file")) { + struct strbuf buf = STRBUF_INIT; + const char *p = arg; + + if (*p != '"') + p = strchr(p, ':'); + else if (unquote_c_style(&buf, p, &p) < 0) + die(_("unclosed quote: '%s'"), arg); + + if (!p || *p != ':') + die(_("missing colon: '%s'"), arg); + + if (p == arg) + die(_("empty file name: '%s'"), arg); + + path = buf.len ? + strbuf_detach(&buf, NULL) : xstrndup(arg, p - arg); + + if (args->prefix) { + char *save = path; + path = prefix_filename(args->prefix, path); + free(save); + } + memset(&info->stat, 0, sizeof(info->stat)); + info->stat.st_mode = S_IFREG | 0644; + info->content = xstrdup(p + 1); + info->stat.st_size = strlen(info->content); + } else { + BUG("add_file_cb() called for %s", opt->long_name); + } + item = string_list_append_nodup(&args->extra_files, path); + item->util = info; + return 0; } @@ -541,6 +633,7 @@ static int parse_archive_args(int argc, const char **argv, const char *remote = NULL; const char *exec = NULL; const char *output = NULL; + const char *mtime_option = NULL; int compression_level = -1; int verbose = 0; int i; @@ -554,11 +647,17 @@ static int parse_archive_args(int argc, const char **argv, { OPTION_CALLBACK, 0, "add-file", args, N_("file"), N_("add untracked file to archive"), 0, add_file_cb, (intptr_t)&base }, + { OPTION_CALLBACK, 0, "add-virtual-file", args, + N_("path:content"), N_("add untracked file to archive"), 0, + add_file_cb, (intptr_t)&base }, OPT_STRING('o', "output", &output, N_("file"), N_("write the archive to this file")), OPT_BOOL(0, "worktree-attributes", &worktree_attributes, N_("read .gitattributes in working directory")), OPT__VERBOSE(&verbose, N_("report archived files on stderr")), + { OPTION_STRING, 0, "mtime", &mtime_option, N_("time"), + N_("set modification time of archive entries"), + PARSE_OPT_NONEG }, OPT_NUMBER_CALLBACK(&compression_level, N_("set compression level"), number_callback), OPT_GROUP(""), @@ -577,16 +676,18 @@ static int parse_archive_args(int argc, const char **argv, if (remote) die(_("Unexpected option --remote")); if (exec) - die(_("Option --exec can only be used together with --remote")); + die(_("the option '%s' requires '%s'"), "--exec", "--remote"); if (output) die(_("Unexpected option --output")); if (is_remote && args->extra_files.nr) - die(_("Options --add-file and --remote cannot be used together")); + die(_("options '%s' and '%s' cannot be used together"), "--add-file", "--remote"); if (!base) base = ""; if (list) { + if (argc) + die(_("extra command line parameter '%s'"), *argv); for (i = 0; i < nr_archivers; i++) if (!is_remote || archivers[i]->flags & ARCHIVER_REMOTE) printf("%s\n", archivers[i]->name); @@ -620,6 +721,7 @@ static int parse_archive_args(int argc, const char **argv, args->base = base; args->baselen = strlen(base); args->worktree_attributes = worktree_attributes; + args->mtime_option = mtime_option; return argc; } @@ -655,13 +757,14 @@ int write_archive(int argc, const char **argv, const char *prefix, setup_git_directory(); } - parse_treeish_arg(argv, &args, prefix, remote); + parse_treeish_arg(argv, &args, remote); parse_pathspec_arg(argv + 1, &args); rc = ar->write_archive(ar, &args); string_list_clear_func(&args.extra_files, extra_file_info_clear); free(args.refname); + clear_pathspec(&args.pathspec); return rc; } |