mirror of
https://github.com/git/git
synced 2024-10-28 19:25:47 +00:00
worktree: add -z option for list subcommand
Add a -z option to be used in conjunction with --porcelain that gives NUL-terminated output. As 'worktree list --porcelain' does not quote worktree paths this enables it to handle worktree paths that contain newlines. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
dab1b7905d
commit
d97eb302ea
3 changed files with 55 additions and 20 deletions
|
@ -10,7 +10,7 @@ SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
|
'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
|
||||||
'git worktree list' [-v | --porcelain]
|
'git worktree list' [-v | --porcelain [-z]]
|
||||||
'git worktree lock' [--reason <string>] <worktree>
|
'git worktree lock' [--reason <string>] <worktree>
|
||||||
'git worktree move' <worktree> <new-path>
|
'git worktree move' <worktree> <new-path>
|
||||||
'git worktree prune' [-n] [-v] [--expire <expire>]
|
'git worktree prune' [-n] [-v] [--expire <expire>]
|
||||||
|
@ -223,7 +223,14 @@ This can also be set up as the default behaviour by using the
|
||||||
--porcelain::
|
--porcelain::
|
||||||
With `list`, output in an easy-to-parse format for scripts.
|
With `list`, output in an easy-to-parse format for scripts.
|
||||||
This format will remain stable across Git versions and regardless of user
|
This format will remain stable across Git versions and regardless of user
|
||||||
configuration. See below for details.
|
configuration. It is recommended to combine this with `-z`.
|
||||||
|
See below for details.
|
||||||
|
|
||||||
|
-z::
|
||||||
|
Terminate each line with a NUL rather than a newline when
|
||||||
|
`--porcelain` is specified with `list`. This makes it possible
|
||||||
|
to parse the output when a worktree path contains a newline
|
||||||
|
character.
|
||||||
|
|
||||||
-q::
|
-q::
|
||||||
--quiet::
|
--quiet::
|
||||||
|
@ -411,7 +418,8 @@ working tree itself.
|
||||||
|
|
||||||
Porcelain Format
|
Porcelain Format
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
The porcelain format has a line per attribute. Attributes are listed with a
|
The porcelain format has a line per attribute. If `-z` is given then the lines
|
||||||
|
are terminated with NUL rather than a newline. Attributes are listed with a
|
||||||
label and value separated by a single space. Boolean attributes (like `bare`
|
label and value separated by a single space. Boolean attributes (like `bare`
|
||||||
and `detached`) are listed as a label only, and are present only
|
and `detached`) are listed as a label only, and are present only
|
||||||
if the value is true. Some attributes (like `locked`) can be listed as a label
|
if the value is true. Some attributes (like `locked`) can be listed as a label
|
||||||
|
@ -449,7 +457,7 @@ prunable gitdir file points to non-existent location
|
||||||
|
|
||||||
------------
|
------------
|
||||||
|
|
||||||
If the lock reason contains "unusual" characters such as newline, they
|
Unless `-z` is used any "unusual" characters in the lock reason such as newlines
|
||||||
are escaped and the entire reason is quoted as explained for the
|
are escaped and the entire reason is quoted as explained for the
|
||||||
configuration variable `core.quotePath` (see linkgit:git-config[1]).
|
configuration variable `core.quotePath` (see linkgit:git-config[1]).
|
||||||
For Example:
|
For Example:
|
||||||
|
|
|
@ -575,35 +575,37 @@ static int add(int ac, const char **av, const char *prefix)
|
||||||
return add_worktree(path, branch, &opts);
|
return add_worktree(path, branch, &opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_worktree_porcelain(struct worktree *wt)
|
static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
|
||||||
{
|
{
|
||||||
const char *reason;
|
const char *reason;
|
||||||
|
|
||||||
printf("worktree %s\n", wt->path);
|
printf("worktree %s%c", wt->path, line_terminator);
|
||||||
if (wt->is_bare)
|
if (wt->is_bare)
|
||||||
printf("bare\n");
|
printf("bare%c", line_terminator);
|
||||||
else {
|
else {
|
||||||
printf("HEAD %s\n", oid_to_hex(&wt->head_oid));
|
printf("HEAD %s%c", oid_to_hex(&wt->head_oid), line_terminator);
|
||||||
if (wt->is_detached)
|
if (wt->is_detached)
|
||||||
printf("detached\n");
|
printf("detached%c", line_terminator);
|
||||||
else if (wt->head_ref)
|
else if (wt->head_ref)
|
||||||
printf("branch %s\n", wt->head_ref);
|
printf("branch %s%c", wt->head_ref, line_terminator);
|
||||||
}
|
}
|
||||||
|
|
||||||
reason = worktree_lock_reason(wt);
|
reason = worktree_lock_reason(wt);
|
||||||
if (reason && *reason) {
|
if (reason) {
|
||||||
struct strbuf sb = STRBUF_INIT;
|
fputs("locked", stdout);
|
||||||
quote_c_style(reason, &sb, NULL, 0);
|
if (*reason) {
|
||||||
printf("locked %s\n", sb.buf);
|
fputc(' ', stdout);
|
||||||
strbuf_release(&sb);
|
write_name_quoted(reason, stdout, line_terminator);
|
||||||
} else if (reason)
|
} else {
|
||||||
printf("locked\n");
|
fputc(line_terminator, stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reason = worktree_prune_reason(wt, expire);
|
reason = worktree_prune_reason(wt, expire);
|
||||||
if (reason)
|
if (reason)
|
||||||
printf("prunable %s\n", reason);
|
printf("prunable %s%c", reason, line_terminator);
|
||||||
|
|
||||||
printf("\n");
|
fputc(line_terminator, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
|
static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
|
||||||
|
@ -681,12 +683,15 @@ static void pathsort(struct worktree **wt)
|
||||||
static int list(int ac, const char **av, const char *prefix)
|
static int list(int ac, const char **av, const char *prefix)
|
||||||
{
|
{
|
||||||
int porcelain = 0;
|
int porcelain = 0;
|
||||||
|
int line_terminator = '\n';
|
||||||
|
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
|
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
|
||||||
OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")),
|
OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")),
|
||||||
OPT_EXPIRY_DATE(0, "expire", &expire,
|
OPT_EXPIRY_DATE(0, "expire", &expire,
|
||||||
N_("add 'prunable' annotation to worktrees older than <time>")),
|
N_("add 'prunable' annotation to worktrees older than <time>")),
|
||||||
|
OPT_SET_INT('z', NULL, &line_terminator,
|
||||||
|
N_("terminate records with a NUL character"), '\0'),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -696,6 +701,8 @@ static int list(int ac, const char **av, const char *prefix)
|
||||||
usage_with_options(worktree_usage, options);
|
usage_with_options(worktree_usage, options);
|
||||||
else if (verbose && porcelain)
|
else if (verbose && porcelain)
|
||||||
die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
|
die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
|
||||||
|
else if (!line_terminator && !porcelain)
|
||||||
|
die(_("the option '%s' requires '%s'"), "-z", "--porcelain");
|
||||||
else {
|
else {
|
||||||
struct worktree **worktrees = get_worktrees();
|
struct worktree **worktrees = get_worktrees();
|
||||||
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
|
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
|
||||||
|
@ -708,7 +715,8 @@ static int list(int ac, const char **av, const char *prefix)
|
||||||
|
|
||||||
for (i = 0; worktrees[i]; i++) {
|
for (i = 0; worktrees[i]; i++) {
|
||||||
if (porcelain)
|
if (porcelain)
|
||||||
show_worktree_porcelain(worktrees[i]);
|
show_worktree_porcelain(worktrees[i],
|
||||||
|
line_terminator);
|
||||||
else
|
else
|
||||||
show_worktree(worktrees[i], path_maxlen, abbrev);
|
show_worktree(worktrees[i], path_maxlen, abbrev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,25 @@ test_expect_success '"list" all worktrees --porcelain' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '"list" all worktrees --porcelain -z' '
|
||||||
|
test_when_finished "rm -rf here _actual actual expect &&
|
||||||
|
git worktree prune" &&
|
||||||
|
printf "worktree %sQHEAD %sQbranch %sQQ" \
|
||||||
|
"$(git rev-parse --show-toplevel)" \
|
||||||
|
$(git rev-parse HEAD --symbolic-full-name HEAD) >expect &&
|
||||||
|
git worktree add --detach here main &&
|
||||||
|
printf "worktree %sQHEAD %sQdetachedQQ" \
|
||||||
|
"$(git -C here rev-parse --show-toplevel)" \
|
||||||
|
"$(git rev-parse HEAD)" >>expect &&
|
||||||
|
git worktree list --porcelain -z >_actual &&
|
||||||
|
nul_to_q <_actual >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '"list" -z fails without --porcelain' '
|
||||||
|
test_must_fail git worktree list -z
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success '"list" all worktrees with locked annotation' '
|
test_expect_success '"list" all worktrees with locked annotation' '
|
||||||
test_when_finished "rm -rf locked unlocked out && git worktree prune" &&
|
test_when_finished "rm -rf locked unlocked out && git worktree prune" &&
|
||||||
git worktree add --detach locked main &&
|
git worktree add --detach locked main &&
|
||||||
|
|
Loading…
Reference in a new issue