Merge branch 'jk/bundle-use-dash-for-stdfiles'

"git bundle" learned that "-" is a common way to say that the input
comes from the standard input and/or the output goes to the
standard output.  It used to work only for output and only from the
root level of the working tree.

* jk/bundle-use-dash-for-stdfiles:
  parse-options: use prefix_filename_except_for_dash() helper
  parse-options: consistently allocate memory in fix_filename()
  bundle: don't blindly apply prefix_filename() to "-"
  bundle: document handling of "-" as stdin
  bundle: let "-" mean stdin for reading operations
This commit is contained in:
Junio C Hamano 2023-03-19 15:03:12 -07:00
commit 95de376349
11 changed files with 82 additions and 19 deletions

View File

@ -66,7 +66,7 @@ create [options] <file> <git-rev-list-args>::
Used to create a bundle named 'file'. This requires the Used to create a bundle named 'file'. This requires the
'<git-rev-list-args>' arguments to define the bundle contents. '<git-rev-list-args>' arguments to define the bundle contents.
'options' contains the options specific to the 'git bundle create' 'options' contains the options specific to the 'git bundle create'
subcommand. subcommand. If 'file' is `-`, the bundle is written to stdout.
verify <file>:: verify <file>::
Used to check that a bundle file is valid and will apply Used to check that a bundle file is valid and will apply
@ -77,12 +77,13 @@ verify <file>::
Finally, information about additional capabilities, such as "object Finally, information about additional capabilities, such as "object
filter", is printed. See "Capabilities" in linkgit:gitformat-bundle[5] filter", is printed. See "Capabilities" in linkgit:gitformat-bundle[5]
for more information. The exit code is zero for success, but will for more information. The exit code is zero for success, but will
be nonzero if the bundle file is invalid. be nonzero if the bundle file is invalid. If 'file' is `-`, the
bundle is read from stdin.
list-heads <file>:: list-heads <file>::
Lists the references defined in the bundle. If followed by a Lists the references defined in the bundle. If followed by a
list of references, only references matching those given are list of references, only references matching those given are
printed out. printed out. If 'file' is `-`, the bundle is read from stdin.
unbundle <file>:: unbundle <file>::
Passes the objects in the bundle to 'git index-pack' Passes the objects in the bundle to 'git index-pack'
@ -90,6 +91,7 @@ unbundle <file>::
defined references. If a list of references is given, only defined references. If a list of references is given, only
references matching those in the list are printed. This command is references matching those in the list are printed. This command is
really plumbing, intended to be called only by 'git fetch'. really plumbing, intended to be called only by 'git fetch'.
If 'file' is `-`, the bundle is read from stdin.
<git-rev-list-args>:: <git-rev-list-args>::
A list of arguments, acceptable to 'git rev-parse' and A list of arguments, acceptable to 'git rev-parse' and

View File

@ -280,3 +280,10 @@ char *prefix_filename(const char *pfx, const char *arg)
#endif #endif
return strbuf_detach(&path, NULL); return strbuf_detach(&path, NULL);
} }
char *prefix_filename_except_for_dash(const char *pfx, const char *arg)
{
if (!strcmp(arg, "-"))
return xstrdup(arg);
return prefix_filename(pfx, arg);
}

View File

@ -81,7 +81,7 @@ static int run_remote_archiver(int argc, const char **argv,
int cmd_archive(int argc, const char **argv, const char *prefix) int cmd_archive(int argc, const char **argv, const char *prefix)
{ {
const char *exec = "git-upload-archive"; const char *exec = "git-upload-archive";
const char *output = NULL; char *output = NULL;
const char *remote = NULL; const char *remote = NULL;
struct option local_opts[] = { struct option local_opts[] = {
OPT_FILENAME('o', "output", &output, OPT_FILENAME('o', "output", &output,
@ -106,5 +106,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
setvbuf(stderr, NULL, _IOLBF, BUFSIZ); setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
UNLEAK(output);
return write_archive(argc, argv, prefix, the_repository, output, 0); return write_archive(argc, argv, prefix, the_repository, output, 0);
} }

View File

@ -59,7 +59,7 @@ static int parse_options_cmd_bundle(int argc,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc) if (!argc)
usage_msg_opt(_("need a <file> argument"), usagestr, options); usage_msg_opt(_("need a <file> argument"), usagestr, options);
*bundle_file = prefix_filename(prefix, argv[0]); *bundle_file = prefix_filename_except_for_dash(prefix, argv[0]);
return argc; return argc;
} }
@ -108,6 +108,23 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
return ret; return ret;
} }
/*
* Similar to read_bundle_header(), but handle "-" as stdin.
*/
static int open_bundle(const char *path, struct bundle_header *header,
const char **name)
{
if (!strcmp(path, "-")) {
if (name)
*name = "<stdin>";
return read_bundle_header_fd(0, header, "<stdin>");
}
if (name)
*name = path;
return read_bundle_header(path, header);
}
static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
struct bundle_header header = BUNDLE_HEADER_INIT; struct bundle_header header = BUNDLE_HEADER_INIT;
int bundle_fd = -1; int bundle_fd = -1;
@ -119,12 +136,13 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
OPT_END() OPT_END()
}; };
char *bundle_file; char *bundle_file;
const char *name;
argc = parse_options_cmd_bundle(argc, argv, prefix, argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_verify_usage, options, &bundle_file); builtin_bundle_verify_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */ /* bundle internals use argv[1] as further parameters */
if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) {
ret = 1; ret = 1;
goto cleanup; goto cleanup;
} }
@ -135,7 +153,7 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
goto cleanup; goto cleanup;
} }
fprintf(stderr, _("%s is okay\n"), bundle_file); fprintf(stderr, _("%s is okay\n"), name);
ret = 0; ret = 0;
cleanup: cleanup:
free(bundle_file); free(bundle_file);
@ -156,7 +174,7 @@ static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix
builtin_bundle_list_heads_usage, options, &bundle_file); builtin_bundle_list_heads_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */ /* bundle internals use argv[1] as further parameters */
if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
ret = 1; ret = 1;
goto cleanup; goto cleanup;
} }
@ -186,7 +204,7 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
builtin_bundle_unbundle_usage, options, &bundle_file); builtin_bundle_unbundle_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */ /* bundle internals use argv[1] as further parameters */
if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
ret = 1; ret = 1;
goto cleanup; goto cleanup;
} }

View File

@ -76,7 +76,7 @@ struct checkout_opts {
const char *ignore_unmerged_opt; const char *ignore_unmerged_opt;
int ignore_unmerged; int ignore_unmerged;
int pathspec_file_nul; int pathspec_file_nul;
const char *pathspec_from_file; char *pathspec_from_file;
const char *new_branch; const char *new_branch;
const char *new_branch_force; const char *new_branch_force;
@ -1890,6 +1890,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
options, checkout_usage, &new_branch_info); options, checkout_usage, &new_branch_info);
branch_info_release(&new_branch_info); branch_info_release(&new_branch_info);
clear_pathspec(&opts.pathspec); clear_pathspec(&opts.pathspec);
free(opts.pathspec_from_file);
FREE_AND_NULL(options); FREE_AND_NULL(options);
return ret; return ret;
} }

View File

@ -318,7 +318,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
int reset_type = NONE, update_ref_status = 0, quiet = 0; int reset_type = NONE, update_ref_status = 0, quiet = 0;
int no_refresh = 0; int no_refresh = 0;
int patch_mode = 0, pathspec_file_nul = 0, unborn; int patch_mode = 0, pathspec_file_nul = 0, unborn;
const char *rev, *pathspec_from_file = NULL; const char *rev;
char *pathspec_from_file = NULL;
struct object_id oid; struct object_id oid;
struct pathspec pathspec; struct pathspec pathspec;
int intent_to_add = 0; int intent_to_add = 0;
@ -496,5 +497,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
cleanup: cleanup:
clear_pathspec(&pathspec); clear_pathspec(&pathspec);
free(pathspec_from_file);
return update_ref_status; return update_ref_status;
} }

View File

@ -429,7 +429,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
int create_reflog = 0; int create_reflog = 0;
int annotate = 0, force = 0; int annotate = 0, force = 0;
int cmdmode = 0, create_tag_object = 0; int cmdmode = 0, create_tag_object = 0;
const char *msgfile = NULL, *keyid = NULL; char *msgfile = NULL;
const char *keyid = NULL;
struct msg_arg msg = { .buf = STRBUF_INIT }; struct msg_arg msg = { .buf = STRBUF_INIT };
struct ref_transaction *transaction; struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT; struct strbuf err = STRBUF_INIT;
@ -639,5 +640,6 @@ cleanup:
strbuf_release(&reflog_msg); strbuf_release(&reflog_msg);
strbuf_release(&msg.buf); strbuf_release(&msg.buf);
strbuf_release(&err); strbuf_release(&err);
free(msgfile);
return ret; return ret;
} }

View File

@ -610,6 +610,9 @@ char *prefix_path_gently(const char *prefix, int len, int *remaining, const char
*/ */
char *prefix_filename(const char *prefix, const char *path); char *prefix_filename(const char *prefix, const char *path);
/* Likewise, but path=="-" always yields "-" */
char *prefix_filename_except_for_dash(const char *prefix, const char *path);
int check_filename(const char *prefix, const char *name); int check_filename(const char *prefix, const char *name);
void verify_filename(const char *prefix, void verify_filename(const char *prefix,
const char *name, const char *name,

View File

@ -59,12 +59,12 @@ static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p,
return 0; return 0;
} }
static void fix_filename(const char *prefix, const char **file) static void fix_filename(const char *prefix, char **file)
{ {
if (!file || !*file || !prefix || is_absolute_path(*file) if (!file || !*file)
|| !strcmp("-", *file)) ; /* leave as NULL */
return; else
*file = prefix_filename(prefix, *file); *file = prefix_filename_except_for_dash(prefix, *file);
} }
static enum parse_opt_result opt_command_mode_error( static enum parse_opt_result opt_command_mode_error(
@ -177,7 +177,7 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
err = get_arg(p, opt, flags, (const char **)opt->value); err = get_arg(p, opt, flags, (const char **)opt->value);
if (!err) if (!err)
fix_filename(p->prefix, (const char **)opt->value); fix_filename(p->prefix, (char **)opt->value);
return err; return err;
case OPTION_CALLBACK: case OPTION_CALLBACK:

View File

@ -6,7 +6,7 @@
int cmd__parse_pathspec_file(int argc, const char **argv) int cmd__parse_pathspec_file(int argc, const char **argv)
{ {
struct pathspec pathspec; struct pathspec pathspec;
const char *pathspec_from_file = NULL; char *pathspec_from_file = NULL;
int pathspec_file_nul = 0, i; int pathspec_file_nul = 0, i;
static const char *const usage[] = { static const char *const usage[] = {
@ -29,5 +29,6 @@ int cmd__parse_pathspec_file(int argc, const char **argv)
printf("%s\n", pathspec.items[i].original); printf("%s\n", pathspec.items[i].original);
clear_pathspec(&pathspec); clear_pathspec(&pathspec);
free(pathspec_from_file);
return 0; return 0;
} }

View File

@ -619,4 +619,30 @@ test_expect_success TTY 'create --quiet disables all bundle progress' '
test_must_be_empty err test_must_be_empty err
' '
test_expect_success 'read bundle over stdin' '
git bundle create some.bundle HEAD &&
git bundle verify - <some.bundle 2>err &&
grep "<stdin> is okay" err &&
git bundle list-heads some.bundle >expect &&
git bundle list-heads - <some.bundle >actual &&
test_cmp expect actual &&
git bundle unbundle some.bundle >expect &&
git bundle unbundle - <some.bundle >actual &&
test_cmp expect actual
'
test_expect_success 'send a bundle to standard output' '
git bundle create - --all HEAD >bundle-one &&
mkdir -p down &&
git -C down bundle create - --all HEAD >bundle-two &&
git bundle verify bundle-one &&
git bundle verify bundle-two &&
git ls-remote bundle-one >expect &&
git ls-remote bundle-two >actual &&
test_cmp expect actual
'
test_done test_done