diff options
Diffstat (limited to 'archive.c')
-rw-r--r-- | archive.c | 191 |
1 files changed, 150 insertions, 41 deletions
@@ -36,8 +36,8 @@ void init_archivers(void) } static void format_subst(const struct commit *commit, - const char *src, size_t len, - struct strbuf *buf) + const char *src, size_t len, + struct strbuf *buf) { char *to_free = NULL; struct strbuf fmt = STRBUF_INIT; @@ -70,13 +70,20 @@ static void format_subst(const struct commit *commit, free(to_free); } -void *object_file_to_archive(const struct archiver_args *args, - const char *path, const struct object_id *oid, - unsigned int mode, enum object_type *type, - unsigned long *sizep) +static void *object_file_to_archive(const struct archiver_args *args, + const char *path, + const struct object_id *oid, + unsigned int mode, + enum object_type *type, + unsigned long *sizep) { void *buffer; const struct commit *commit = args->convert ? args->commit : NULL; + struct checkout_metadata meta; + + init_checkout_metadata(&meta, args->refname, + args->commit_oid ? args->commit_oid : + (args->tree ? &args->tree->object.oid : NULL), oid); path += args->baselen; buffer = read_object_file(oid, type, sizep); @@ -85,7 +92,7 @@ void *object_file_to_archive(const struct archiver_args *args, size_t size = 0; strbuf_attach(&buf, buffer, *sizep, *sizep + 1); - convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf); + convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf, &meta); if (commit) format_subst(commit, buf.buf, buf.len, &buf); buffer = strbuf_detach(&buf, &size); @@ -140,6 +147,9 @@ static int write_archive_entry(const struct object_id *oid, const char *base, write_archive_entry_fn_t write_entry = c->write_entry; int err; const char *path_without_prefix; + unsigned long size; + void *buffer; + enum object_type type; args->convert = 0; strbuf_reset(&path); @@ -162,7 +172,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base, 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); + err = write_entry(args, oid, path.buf, path.len, mode, NULL, 0); if (err) return err; return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); @@ -170,7 +180,19 @@ static int write_archive_entry(const struct object_id *oid, const char *base, if (args->verbose) fprintf(stderr, "%.*s\n", (int)path.len, path.buf); - return write_entry(args, oid, path.buf, path.len, mode); + + /* Stream it? */ + if (S_ISREG(mode) && !args->convert && + oid_object_info(args->repo, oid, &size) == OBJ_BLOB && + size > big_file_threshold) + return write_entry(args, oid, path.buf, path.len, mode, NULL, size); + + buffer = object_file_to_archive(args, path.buf, oid, mode, &type, &size); + if (!buffer) + 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; } static void queue_directory(const unsigned char *sha1, @@ -244,6 +266,11 @@ static int queue_or_write_archive_entry(const struct object_id *oid, stage, context); } +struct extra_file_info { + char *base; + struct stat stat; +}; + int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry) { @@ -251,6 +278,10 @@ int write_archive_entries(struct archiver_args *args, struct unpack_trees_options opts; struct tree_desc t; int err; + struct strbuf path_in_archive = STRBUF_INIT; + struct strbuf content = STRBUF_INIT; + struct object_id fake_oid = null_oid; + int i; if (args->baselen > 0 && args->base[args->baselen - 1] == '/') { size_t len = args->baselen; @@ -260,7 +291,7 @@ int write_archive_entries(struct archiver_args *args, if (args->verbose) fprintf(stderr, "%.*s\n", (int)len, args->base); err = write_entry(args, &args->tree->object.oid, args->base, - len, 040777); + len, 040777, NULL, 0); if (err) return err; } @@ -285,7 +316,8 @@ int write_archive_entries(struct archiver_args *args, git_attr_set_direction(GIT_ATTR_INDEX); } - err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec, + err = read_tree_recursive(args->repo, args->tree, "", + 0, 0, &args->pathspec, queue_or_write_archive_entry, &context); if (err == READ_TREE_RECURSIVE) @@ -295,6 +327,33 @@ int write_archive_entries(struct archiver_args *args, free(context.bottom); context.bottom = next; } + + for (i = 0; i < args->extra_files.nr; i++) { + struct string_list_item *item = args->extra_files.items + i; + char *path = item->string; + struct extra_file_info *info = item->util; + + 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 (err) + break; + } + strbuf_release(&path_in_archive); + strbuf_release(&content); + return err; } @@ -346,7 +405,8 @@ static int path_exists(struct archiver_args *args, const char *path) ctx.args = args; parse_pathspec(&ctx.pathspec, 0, 0, "", paths); ctx.pathspec.recursive = 1; - ret = read_tree_recursive(args->tree, "", 0, 0, &ctx.pathspec, + ret = read_tree_recursive(args->repo, args->tree, "", + 0, 0, &ctx.pathspec, reject_entry, &ctx); clear_pathspec(&ctx.pathspec); return ret != 0; @@ -378,61 +438,103 @@ static void parse_treeish_arg(const char **argv, int remote) { const char *name = argv[0]; - const unsigned char *commit_sha1; + const struct object_id *commit_oid; time_t archive_time; struct tree *tree; const struct commit *commit; struct object_id oid; + char *ref = NULL; /* Remotes are only allowed to fetch actual refs */ if (remote && !remote_allow_unreachable) { - char *ref = NULL; const char *colon = strchrnul(name, ':'); int refnamelen = colon - name; - if (!dwim_ref(name, refnamelen, &oid, &ref)) - die("no such ref: %.*s", refnamelen, name); - free(ref); + if (!dwim_ref(name, refnamelen, &oid, &ref, 0)) + die(_("no such ref: %.*s"), refnamelen, name); + } else { + dwim_ref(name, strlen(name), &oid, &ref, 0); } if (get_oid(name, &oid)) - die("Not a valid object name"); + die(_("not a valid object name: %s"), name); commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1); if (commit) { - commit_sha1 = commit->object.oid.hash; + commit_oid = &commit->object.oid; archive_time = commit->date; } else { - commit_sha1 = NULL; + commit_oid = NULL; archive_time = time(NULL); } tree = parse_tree_indirect(&oid); if (tree == NULL) - die("not a tree object"); + die(_("not a tree object: %s"), oid_to_hex(&oid)); if (prefix) { struct object_id tree_oid; - unsigned int mode; + unsigned short mode; int err; - err = get_tree_entry(&tree->object.oid, prefix, &tree_oid, + 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"); + die(_("current working directory is untracked")); tree = parse_tree_indirect(&tree_oid); } + ar_args->refname = ref; ar_args->tree = tree; - ar_args->commit_sha1 = commit_sha1; + ar_args->commit_oid = commit_oid; ar_args->commit = commit; ar_args->time = archive_time; } -#define OPT__COMPR(s, v, h, p) \ - OPT_SET_INT_F(s, NULL, v, h, p, PARSE_OPT_NONEG) -#define OPT__COMPR_HIDDEN(s, v, p) \ - OPT_SET_INT_F(s, NULL, v, "", p, PARSE_OPT_NONEG | PARSE_OPT_HIDDEN) +static void extra_file_info_clear(void *util, const char *str) +{ + struct extra_file_info *info = util; + free(info->base); + free(info); +} + +static int add_file_cb(const struct option *opt, const char *arg, int unset) +{ + struct archiver_args *args = opt->value; + const char **basep = (const char **)opt->defval; + const char *base = *basep; + char *path; + struct string_list_item *item; + struct extra_file_info *info; + + if (unset) { + string_list_clear_func(&args->extra_files, + extra_file_info_clear); + return 0; + } + + 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->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); + return 0; +} + +static int number_callback(const struct option *opt, const char *arg, int unset) +{ + BUG_ON_OPT_NEG(unset); + *(int *)opt->value = strtol(arg, NULL, 10); + return 0; +} static int parse_archive_args(int argc, const char **argv, const struct archiver **ar, struct archiver_args *args, @@ -453,21 +555,16 @@ static int parse_archive_args(int argc, const char **argv, OPT_STRING(0, "format", &format, N_("fmt"), N_("archive format")), OPT_STRING(0, "prefix", &base, N_("prefix"), N_("prepend prefix to each pathname in the archive")), + { OPTION_CALLBACK, 0, "add-file", args, N_("file"), + 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")), - OPT__COMPR('0', &compression_level, N_("store only"), 0), - OPT__COMPR('1', &compression_level, N_("compress faster"), 1), - OPT__COMPR_HIDDEN('2', &compression_level, 2), - OPT__COMPR_HIDDEN('3', &compression_level, 3), - OPT__COMPR_HIDDEN('4', &compression_level, 4), - OPT__COMPR_HIDDEN('5', &compression_level, 5), - OPT__COMPR_HIDDEN('6', &compression_level, 6), - OPT__COMPR_HIDDEN('7', &compression_level, 7), - OPT__COMPR_HIDDEN('8', &compression_level, 8), - OPT__COMPR('9', &compression_level, N_("compress better"), 9), + OPT_NUMBER_CALLBACK(&compression_level, + N_("set compression level"), number_callback), OPT_GROUP(""), OPT_BOOL('l', "list", &list, N_("list supported archive formats")), @@ -487,6 +584,8 @@ static int parse_archive_args(int argc, const char **argv, die(_("Option --exec can only be used together with --remote")); if (output) die(_("Unexpected option --output")); + if (is_remote && args->extra_files.nr) + die(_("Options --add-file and --remote cannot be used together")); if (!base) base = ""; @@ -512,7 +611,9 @@ static int parse_archive_args(int argc, const char **argv, args->compression_level = Z_DEFAULT_COMPRESSION; if (compression_level != -1) { - if ((*ar)->flags & ARCHIVER_WANT_COMPRESSION_LEVELS) + int levels_ok = (*ar)->flags & ARCHIVER_WANT_COMPRESSION_LEVELS; + int high_ok = (*ar)->flags & ARCHIVER_HIGH_COMPRESSION_LEVELS; + if (levels_ok && (compression_level <= 9 || high_ok)) args->compression_level = compression_level; else { die(_("Argument not supported for format '%s': -%d"), @@ -533,11 +634,14 @@ int write_archive(int argc, const char **argv, const char *prefix, { const struct archiver *ar = NULL; struct archiver_args args; + int rc; git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable); git_config(git_default_config, NULL); args.repo = repo; + args.prefix = prefix; + string_list_init(&args.extra_files, 1); argc = parse_archive_args(argc, argv, &ar, &args, name_hint, remote); if (!startup_info->have_repository) { /* @@ -551,7 +655,12 @@ int write_archive(int argc, const char **argv, const char *prefix, parse_treeish_arg(argv, &args, prefix, remote); parse_pathspec_arg(argv + 1, &args); - return ar->write_archive(ar, &args); + rc = ar->write_archive(ar, &args); + + string_list_clear_func(&args.extra_files, extra_file_info_clear); + free(args.refname); + + return rc; } static int match_extension(const char *filename, const char *ext) |