archive: add --add-file
Allow users to append non-tracked files. This simplifies the generation of source packages with a few extra files, e.g. containing version information. They get the same access times and user information as tracked files. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
200589abcb
commit
2947a7930d
@ -55,6 +55,12 @@ OPTIONS
|
||||
--output=<file>::
|
||||
Write the archive to <file> instead of stdout.
|
||||
|
||||
--add-file=<file>::
|
||||
Add a non-tracked file to the archive. Can be repeated to add
|
||||
multiple files. The path of the file in the archive is built
|
||||
by concatenating the value for `--prefix` (if any) and the
|
||||
basename of <file>.
|
||||
|
||||
--worktree-attributes::
|
||||
Look for attributes in .gitattributes files in the working tree
|
||||
as well (see <<ATTRIBUTES>>).
|
||||
|
86
archive.c
86
archive.c
@ -266,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)
|
||||
{
|
||||
@ -273,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;
|
||||
@ -318,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;
|
||||
}
|
||||
|
||||
@ -457,6 +493,42 @@ static void parse_treeish_arg(const char **argv,
|
||||
ar_args->time = archive_time;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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) \
|
||||
@ -481,6 +553,9 @@ 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,
|
||||
@ -515,6 +590,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 = "";
|
||||
@ -561,11 +638,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) {
|
||||
/*
|
||||
@ -579,7 +659,11 @@ 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);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int match_extension(const char *filename, const char *ext)
|
||||
|
@ -9,6 +9,7 @@ struct repository;
|
||||
struct archiver_args {
|
||||
struct repository *repo;
|
||||
const char *refname;
|
||||
const char *prefix;
|
||||
const char *base;
|
||||
size_t baselen;
|
||||
struct tree *tree;
|
||||
@ -20,6 +21,7 @@ struct archiver_args {
|
||||
unsigned int worktree_attributes : 1;
|
||||
unsigned int convert : 1;
|
||||
int compression_level;
|
||||
struct string_list extra_files;
|
||||
};
|
||||
|
||||
/* main api */
|
||||
|
@ -94,6 +94,16 @@ check_tar() {
|
||||
'
|
||||
}
|
||||
|
||||
check_added() {
|
||||
dir=$1
|
||||
path_in_fs=$2
|
||||
path_in_archive=$3
|
||||
|
||||
test_expect_success " validate extra file $path_in_archive" '
|
||||
diff -r $path_in_fs $dir/$path_in_archive
|
||||
'
|
||||
}
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_oid_cache <<-EOF
|
||||
obj sha1:19f9c8273ec45a8938e6999cb59b3ff66739902a
|
||||
@ -164,6 +174,25 @@ test_expect_success 'git-archive --prefix=olde-' '
|
||||
|
||||
check_tar with_olde-prefix olde-
|
||||
|
||||
test_expect_success 'git archive --add-file' '
|
||||
echo untracked >untracked &&
|
||||
git archive --add-file=untracked HEAD >with_untracked.tar
|
||||
'
|
||||
|
||||
check_tar with_untracked
|
||||
check_added with_untracked untracked untracked
|
||||
|
||||
test_expect_success 'git archive --add-file twice' '
|
||||
echo untracked >untracked &&
|
||||
git archive --prefix=one/ --add-file=untracked \
|
||||
--prefix=two/ --add-file=untracked \
|
||||
--prefix= HEAD >with_untracked2.tar
|
||||
'
|
||||
|
||||
check_tar with_untracked2
|
||||
check_added with_untracked2 untracked one/untracked
|
||||
check_added with_untracked2 untracked two/untracked
|
||||
|
||||
test_expect_success 'git archive on large files' '
|
||||
test_config core.bigfilethreshold 1 &&
|
||||
git archive HEAD >b3.tar &&
|
||||
|
@ -72,6 +72,16 @@ check_zip() {
|
||||
"
|
||||
}
|
||||
|
||||
check_added() {
|
||||
dir=$1
|
||||
path_in_fs=$2
|
||||
path_in_archive=$3
|
||||
|
||||
test_expect_success UNZIP " validate extra file $path_in_archive" '
|
||||
diff -r $path_in_fs $dir/$path_in_archive
|
||||
'
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'populate workdir' \
|
||||
'mkdir a &&
|
||||
@ -188,4 +198,22 @@ test_expect_success 'git archive --format=zip on large files' '
|
||||
|
||||
check_zip large-compressed
|
||||
|
||||
test_expect_success 'git archive --format=zip --add-file' '
|
||||
echo untracked >untracked &&
|
||||
git archive --format=zip --add-file=untracked HEAD >with_untracked.zip
|
||||
'
|
||||
|
||||
check_zip with_untracked
|
||||
check_added with_untracked untracked untracked
|
||||
|
||||
test_expect_success 'git archive --format=zip --add-file twice' '
|
||||
echo untracked >untracked &&
|
||||
git archive --format=zip --prefix=one/ --add-file=untracked \
|
||||
--prefix=two/ --add-file=untracked \
|
||||
--prefix= HEAD >with_untracked2.zip
|
||||
'
|
||||
check_zip with_untracked2
|
||||
check_added with_untracked2 untracked one/untracked
|
||||
check_added with_untracked2 untracked two/untracked
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user