зеркало из https://github.com/microsoft/git.git
Merge branch 'rs/worktree-list-verbose'
`git worktree list` now annotates worktrees as prunable, shows locked and prunable attributes in --porcelain mode, and gained a --verbose option. * rs/worktree-list-verbose: worktree: teach `list` verbose mode worktree: teach `list` to annotate prunable worktree worktree: teach `list --porcelain` to annotate locked worktree t2402: ensure locked worktree is properly cleaned up worktree: teach worktree_lock_reason() to gently handle main worktree worktree: teach worktree to lazy-load "prunable" reason worktree: libify should_prune_worktree()
This commit is contained in:
Коммит
02fb21617e
|
@ -97,8 +97,9 @@ list::
|
|||
List details of each working tree. The main working tree is listed first,
|
||||
followed by each of the linked working trees. The output details include
|
||||
whether the working tree is bare, the revision currently checked out, the
|
||||
branch currently checked out (or "detached HEAD" if none), and "locked" if
|
||||
the worktree is locked.
|
||||
branch currently checked out (or "detached HEAD" if none), "locked" if
|
||||
the worktree is locked, "prunable" if the worktree can be pruned by `prune`
|
||||
command.
|
||||
|
||||
lock::
|
||||
|
||||
|
@ -231,9 +232,14 @@ This can also be set up as the default behaviour by using the
|
|||
-v::
|
||||
--verbose::
|
||||
With `prune`, report all removals.
|
||||
+
|
||||
With `list`, output additional information about worktrees (see below).
|
||||
|
||||
--expire <time>::
|
||||
With `prune`, only expire unused working trees older than `<time>`.
|
||||
+
|
||||
With `list`, annotate missing working trees as prunable if they are
|
||||
older than `<time>`.
|
||||
|
||||
--reason <string>::
|
||||
With `lock`, an explanation why the working tree is locked.
|
||||
|
@ -372,13 +378,46 @@ $ git worktree list
|
|||
/path/to/other-linked-worktree 1234abc (detached HEAD)
|
||||
------------
|
||||
|
||||
The command also shows annotations for each working tree, according to its state.
|
||||
These annotations are:
|
||||
|
||||
* `locked`, if the working tree is locked.
|
||||
* `prunable`, if the working tree can be pruned via `git worktree prune`.
|
||||
|
||||
------------
|
||||
$ git worktree list
|
||||
/path/to/linked-worktree abcd1234 [master]
|
||||
/path/to/locked-worktreee acbd5678 (brancha) locked
|
||||
/path/to/prunable-worktree 5678abc (detached HEAD) prunable
|
||||
------------
|
||||
|
||||
For these annotations, a reason might also be available and this can be
|
||||
seen using the verbose mode. The annotation is then moved to the next line
|
||||
indented followed by the additional information.
|
||||
|
||||
------------
|
||||
$ git worktree list --verbose
|
||||
/path/to/linked-worktree abcd1234 [master]
|
||||
/path/to/locked-worktree-no-reason abcd5678 (detached HEAD) locked
|
||||
/path/to/locked-worktree-with-reason 1234abcd (brancha)
|
||||
locked: working tree path is mounted on a portable device
|
||||
/path/to/prunable-worktree 5678abc1 (detached HEAD)
|
||||
prunable: gitdir file points to non-existent location
|
||||
------------
|
||||
|
||||
Note that the annotation is moved to the next line if the additional
|
||||
information is available, otherwise it stays on the same line as the
|
||||
working tree itself.
|
||||
|
||||
Porcelain Format
|
||||
~~~~~~~~~~~~~~~~
|
||||
The porcelain format has a line per attribute. Attributes are listed with a
|
||||
label and value separated by a single space. Boolean attributes (like `bare`
|
||||
and `detached`) are listed as a label only, and are present only
|
||||
if the value is true. The first attribute of a working tree is always
|
||||
`worktree`, an empty line indicates the end of the record. For example:
|
||||
if the value is true. Some attributes (like `locked`) can be listed as a label
|
||||
only or with a value depending upon whether a reason is available. The first
|
||||
attribute of a working tree is always `worktree`, an empty line indicates the
|
||||
end of the record. For example:
|
||||
|
||||
------------
|
||||
$ git worktree list --porcelain
|
||||
|
@ -393,6 +432,33 @@ worktree /path/to/other-linked-worktree
|
|||
HEAD 1234abc1234abc1234abc1234abc1234abc1234a
|
||||
detached
|
||||
|
||||
worktree /path/to/linked-worktree-locked-no-reason
|
||||
HEAD 5678abc5678abc5678abc5678abc5678abc5678c
|
||||
branch refs/heads/locked-no-reason
|
||||
locked
|
||||
|
||||
worktree /path/to/linked-worktree-locked-with-reason
|
||||
HEAD 3456def3456def3456def3456def3456def3456b
|
||||
branch refs/heads/locked-with-reason
|
||||
locked reason why is locked
|
||||
|
||||
worktree /path/to/linked-worktree-prunable
|
||||
HEAD 1233def1234def1234def1234def1234def1234b
|
||||
detached
|
||||
prunable gitdir file points to non-existent location
|
||||
|
||||
------------
|
||||
|
||||
If the lock reason contains "unusual" characters such as newline, they
|
||||
are escaped and the entire reason is quoted as explained for the
|
||||
configuration variable `core.quotePath` (see linkgit:git-config[1]).
|
||||
For Example:
|
||||
|
||||
------------
|
||||
$ git worktree list --porcelain
|
||||
...
|
||||
locked "reason\nwhy is locked"
|
||||
...
|
||||
------------
|
||||
|
||||
EXAMPLES
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "submodule.h"
|
||||
#include "utf8.h"
|
||||
#include "worktree.h"
|
||||
#include "quote.h"
|
||||
|
||||
static const char * const worktree_usage[] = {
|
||||
N_("git worktree add [<options>] <path> [<commit-ish>]"),
|
||||
|
@ -67,79 +68,6 @@ static void delete_worktrees_dir_if_empty(void)
|
|||
rmdir(git_path("worktrees")); /* ignore failed removal */
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if worktree entry should be pruned, along with the reason for
|
||||
* pruning. Otherwise, return false and the worktree's path, or NULL if it
|
||||
* cannot be determined. Caller is responsible for freeing returned path.
|
||||
*/
|
||||
static int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath)
|
||||
{
|
||||
struct stat st;
|
||||
char *path;
|
||||
int fd;
|
||||
size_t len;
|
||||
ssize_t read_result;
|
||||
|
||||
*wtpath = NULL;
|
||||
if (!is_directory(git_path("worktrees/%s", id))) {
|
||||
strbuf_addstr(reason, _("not a valid directory"));
|
||||
return 1;
|
||||
}
|
||||
if (file_exists(git_path("worktrees/%s/locked", id)))
|
||||
return 0;
|
||||
if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
|
||||
strbuf_addstr(reason, _("gitdir file does not exist"));
|
||||
return 1;
|
||||
}
|
||||
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
strbuf_addf(reason, _("unable to read gitdir file (%s)"),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
len = xsize_t(st.st_size);
|
||||
path = xmallocz(len);
|
||||
|
||||
read_result = read_in_full(fd, path, len);
|
||||
if (read_result < 0) {
|
||||
strbuf_addf(reason, _("unable to read gitdir file (%s)"),
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
free(path);
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (read_result != len) {
|
||||
strbuf_addf(reason,
|
||||
_("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
|
||||
(uintmax_t)len, (uintmax_t)read_result);
|
||||
free(path);
|
||||
return 1;
|
||||
}
|
||||
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
|
||||
len--;
|
||||
if (!len) {
|
||||
strbuf_addstr(reason, _("invalid gitdir file"));
|
||||
free(path);
|
||||
return 1;
|
||||
}
|
||||
path[len] = '\0';
|
||||
if (!file_exists(path)) {
|
||||
if (stat(git_path("worktrees/%s/index", id), &st) ||
|
||||
st.st_mtime <= expire) {
|
||||
strbuf_addstr(reason, _("gitdir file points to non-existent location"));
|
||||
free(path);
|
||||
return 1;
|
||||
} else {
|
||||
*wtpath = path;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*wtpath = path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prune_worktree(const char *id, const char *reason)
|
||||
{
|
||||
if (show_only || verbose)
|
||||
|
@ -195,7 +123,7 @@ static void prune_worktrees(void)
|
|||
if (is_dot_or_dotdot(d->d_name))
|
||||
continue;
|
||||
strbuf_reset(&reason);
|
||||
if (should_prune_worktree(d->d_name, &reason, &path))
|
||||
if (should_prune_worktree(d->d_name, &reason, &path, expire))
|
||||
prune_worktree(d->d_name, reason.buf);
|
||||
else if (path)
|
||||
string_list_append(&kept, path)->util = xstrdup(d->d_name);
|
||||
|
@ -642,6 +570,8 @@ static int add(int ac, const char **av, const char *prefix)
|
|||
|
||||
static void show_worktree_porcelain(struct worktree *wt)
|
||||
{
|
||||
const char *reason;
|
||||
|
||||
printf("worktree %s\n", wt->path);
|
||||
if (wt->is_bare)
|
||||
printf("bare\n");
|
||||
|
@ -652,6 +582,20 @@ static void show_worktree_porcelain(struct worktree *wt)
|
|||
else if (wt->head_ref)
|
||||
printf("branch %s\n", wt->head_ref);
|
||||
}
|
||||
|
||||
reason = worktree_lock_reason(wt);
|
||||
if (reason && *reason) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
quote_c_style(reason, &sb, NULL, 0);
|
||||
printf("locked %s\n", sb.buf);
|
||||
strbuf_release(&sb);
|
||||
} else if (reason)
|
||||
printf("locked\n");
|
||||
|
||||
reason = worktree_prune_reason(wt, expire);
|
||||
if (reason)
|
||||
printf("prunable %s\n", reason);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
@ -660,6 +604,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
|
|||
struct strbuf sb = STRBUF_INIT;
|
||||
int cur_path_len = strlen(wt->path);
|
||||
int path_adj = cur_path_len - utf8_strwidth(wt->path);
|
||||
const char *reason;
|
||||
|
||||
strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
|
||||
if (wt->is_bare)
|
||||
|
@ -677,9 +622,18 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
|
|||
strbuf_addstr(&sb, "(error)");
|
||||
}
|
||||
|
||||
if (!is_main_worktree(wt) && worktree_lock_reason(wt))
|
||||
reason = worktree_lock_reason(wt);
|
||||
if (verbose && reason && *reason)
|
||||
strbuf_addf(&sb, "\n\tlocked: %s", reason);
|
||||
else if (reason)
|
||||
strbuf_addstr(&sb, " locked");
|
||||
|
||||
reason = worktree_prune_reason(wt, expire);
|
||||
if (verbose && reason)
|
||||
strbuf_addf(&sb, "\n\tprunable: %s", reason);
|
||||
else if (reason)
|
||||
strbuf_addstr(&sb, " prunable");
|
||||
|
||||
printf("%s\n", sb.buf);
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
@ -723,12 +677,18 @@ static int list(int ac, const char **av, const char *prefix)
|
|||
|
||||
struct option options[] = {
|
||||
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
|
||||
OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")),
|
||||
OPT_EXPIRY_DATE(0, "expire", &expire,
|
||||
N_("add 'prunable' annotation to worktrees older than <time>")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
expire = TIME_MAX;
|
||||
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
|
||||
if (ac)
|
||||
usage_with_options(worktree_usage, options);
|
||||
else if (verbose && porcelain)
|
||||
die(_("--verbose and --porcelain are mutually exclusive"));
|
||||
else {
|
||||
struct worktree **worktrees = get_worktrees();
|
||||
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
|
||||
|
|
|
@ -69,11 +69,107 @@ test_expect_success '"list" all worktrees with locked annotation' '
|
|||
git worktree add --detach locked main &&
|
||||
git worktree add --detach unlocked main &&
|
||||
git worktree lock locked &&
|
||||
test_when_finished "git worktree unlock locked" &&
|
||||
git worktree list >out &&
|
||||
grep "/locked *[0-9a-f].* locked$" out &&
|
||||
! grep "/unlocked *[0-9a-f].* locked$" out
|
||||
'
|
||||
|
||||
test_expect_success '"list" all worktrees --porcelain with locked' '
|
||||
test_when_finished "rm -rf locked1 locked2 unlocked out actual expect && git worktree prune" &&
|
||||
echo "locked" >expect &&
|
||||
echo "locked with reason" >>expect &&
|
||||
git worktree add --detach locked1 &&
|
||||
git worktree add --detach locked2 &&
|
||||
# unlocked worktree should not be annotated with "locked"
|
||||
git worktree add --detach unlocked &&
|
||||
git worktree lock locked1 &&
|
||||
test_when_finished "git worktree unlock locked1" &&
|
||||
git worktree lock locked2 --reason "with reason" &&
|
||||
test_when_finished "git worktree unlock locked2" &&
|
||||
git worktree list --porcelain >out &&
|
||||
grep "^locked" out >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '"list" all worktrees --porcelain with locked reason newline escaped' '
|
||||
test_when_finished "rm -rf locked_lf locked_crlf out actual expect && git worktree prune" &&
|
||||
printf "locked \"locked\\\\r\\\\nreason\"\n" >expect &&
|
||||
printf "locked \"locked\\\\nreason\"\n" >>expect &&
|
||||
git worktree add --detach locked_lf &&
|
||||
git worktree add --detach locked_crlf &&
|
||||
git worktree lock locked_lf --reason "$(printf "locked\nreason")" &&
|
||||
test_when_finished "git worktree unlock locked_lf" &&
|
||||
git worktree lock locked_crlf --reason "$(printf "locked\r\nreason")" &&
|
||||
test_when_finished "git worktree unlock locked_crlf" &&
|
||||
git worktree list --porcelain >out &&
|
||||
grep "^locked" out >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '"list" all worktrees with prunable annotation' '
|
||||
test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
|
||||
git worktree add --detach prunable &&
|
||||
git worktree add --detach unprunable &&
|
||||
rm -rf prunable &&
|
||||
git worktree list >out &&
|
||||
grep "/prunable *[0-9a-f].* prunable$" out &&
|
||||
! grep "/unprunable *[0-9a-f].* prunable$"
|
||||
'
|
||||
|
||||
test_expect_success '"list" all worktrees --porcelain with prunable' '
|
||||
test_when_finished "rm -rf prunable out && git worktree prune" &&
|
||||
git worktree add --detach prunable &&
|
||||
rm -rf prunable &&
|
||||
git worktree list --porcelain >out &&
|
||||
sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
|
||||
test_i18ngrep "^prunable gitdir file points to non-existent location$" only_prunable
|
||||
'
|
||||
|
||||
test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
|
||||
test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
|
||||
git worktree add --detach prunable &&
|
||||
git worktree add --detach unprunable &&
|
||||
rm -rf prunable &&
|
||||
git worktree list >out &&
|
||||
grep "/prunable *[0-9a-f].* prunable$" out &&
|
||||
! grep "/unprunable *[0-9a-f].* unprunable$" out &&
|
||||
git worktree prune --verbose >out &&
|
||||
test_i18ngrep "^Removing worktrees/prunable" out &&
|
||||
test_i18ngrep ! "^Removing worktrees/unprunable" out
|
||||
'
|
||||
|
||||
test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
|
||||
test_must_fail git worktree list --verbose --porcelain
|
||||
'
|
||||
|
||||
test_expect_success '"list" all worktrees --verbose with locked' '
|
||||
test_when_finished "rm -rf locked1 locked2 out actual expect && git worktree prune" &&
|
||||
git worktree add locked1 --detach &&
|
||||
git worktree add locked2 --detach &&
|
||||
git worktree lock locked1 &&
|
||||
test_when_finished "git worktree unlock locked1" &&
|
||||
git worktree lock locked2 --reason "with reason" &&
|
||||
test_when_finished "git worktree unlock locked2" &&
|
||||
echo "$(git -C locked2 rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
|
||||
printf "\tlocked: with reason\n" >>expect &&
|
||||
git worktree list --verbose >out &&
|
||||
grep "/locked1 *[0-9a-f].* locked$" out &&
|
||||
sed -n "s/ */ /g;/\/locked2 *[0-9a-f].*$/,/locked: .*$/p" <out >actual &&
|
||||
test_cmp actual expect
|
||||
'
|
||||
|
||||
test_expect_success '"list" all worktrees --verbose with prunable' '
|
||||
test_when_finished "rm -rf prunable out actual expect && git worktree prune" &&
|
||||
git worktree add prunable --detach &&
|
||||
echo "$(git -C prunable rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
|
||||
printf "\tprunable: gitdir file points to non-existent location\n" >>expect &&
|
||||
rm -rf prunable &&
|
||||
git worktree list --verbose >out &&
|
||||
sed -n "s/ */ /g;/\/prunable *[0-9a-f].*$/,/prunable: .*$/p" <out >actual &&
|
||||
test_i18ncmp actual expect
|
||||
'
|
||||
|
||||
test_expect_success 'bare repo setup' '
|
||||
git init --bare bare1 &&
|
||||
echo "data" >file1 &&
|
||||
|
|
91
worktree.c
91
worktree.c
|
@ -15,6 +15,7 @@ void free_worktrees(struct worktree **worktrees)
|
|||
free(worktrees[i]->id);
|
||||
free(worktrees[i]->head_ref);
|
||||
free(worktrees[i]->lock_reason);
|
||||
free(worktrees[i]->prune_reason);
|
||||
free(worktrees[i]);
|
||||
}
|
||||
free (worktrees);
|
||||
|
@ -224,7 +225,8 @@ int is_main_worktree(const struct worktree *wt)
|
|||
|
||||
const char *worktree_lock_reason(struct worktree *wt)
|
||||
{
|
||||
assert(!is_main_worktree(wt));
|
||||
if (is_main_worktree(wt))
|
||||
return NULL;
|
||||
|
||||
if (!wt->lock_reason_valid) {
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
|
@ -245,6 +247,25 @@ const char *worktree_lock_reason(struct worktree *wt)
|
|||
return wt->lock_reason;
|
||||
}
|
||||
|
||||
const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
|
||||
{
|
||||
struct strbuf reason = STRBUF_INIT;
|
||||
char *path = NULL;
|
||||
|
||||
if (is_main_worktree(wt))
|
||||
return NULL;
|
||||
if (wt->prune_reason_valid)
|
||||
return wt->prune_reason;
|
||||
|
||||
if (should_prune_worktree(wt->id, &reason, &path, expire))
|
||||
wt->prune_reason = strbuf_detach(&reason, NULL);
|
||||
wt->prune_reason_valid = 1;
|
||||
|
||||
strbuf_release(&reason);
|
||||
free(path);
|
||||
return wt->prune_reason;
|
||||
}
|
||||
|
||||
/* convenient wrapper to deal with NULL strbuf */
|
||||
static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
|
||||
{
|
||||
|
@ -741,3 +762,71 @@ done:
|
|||
strbuf_release(&realdotgit);
|
||||
strbuf_release(&dotgit);
|
||||
}
|
||||
|
||||
int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, timestamp_t expire)
|
||||
{
|
||||
struct stat st;
|
||||
char *path;
|
||||
int fd;
|
||||
size_t len;
|
||||
ssize_t read_result;
|
||||
|
||||
*wtpath = NULL;
|
||||
if (!is_directory(git_path("worktrees/%s", id))) {
|
||||
strbuf_addstr(reason, _("not a valid directory"));
|
||||
return 1;
|
||||
}
|
||||
if (file_exists(git_path("worktrees/%s/locked", id)))
|
||||
return 0;
|
||||
if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
|
||||
strbuf_addstr(reason, _("gitdir file does not exist"));
|
||||
return 1;
|
||||
}
|
||||
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
strbuf_addf(reason, _("unable to read gitdir file (%s)"),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
len = xsize_t(st.st_size);
|
||||
path = xmallocz(len);
|
||||
|
||||
read_result = read_in_full(fd, path, len);
|
||||
if (read_result < 0) {
|
||||
strbuf_addf(reason, _("unable to read gitdir file (%s)"),
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
free(path);
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (read_result != len) {
|
||||
strbuf_addf(reason,
|
||||
_("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
|
||||
(uintmax_t)len, (uintmax_t)read_result);
|
||||
free(path);
|
||||
return 1;
|
||||
}
|
||||
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
|
||||
len--;
|
||||
if (!len) {
|
||||
strbuf_addstr(reason, _("invalid gitdir file"));
|
||||
free(path);
|
||||
return 1;
|
||||
}
|
||||
path[len] = '\0';
|
||||
if (!file_exists(path)) {
|
||||
if (stat(git_path("worktrees/%s/index", id), &st) ||
|
||||
st.st_mtime <= expire) {
|
||||
strbuf_addstr(reason, _("gitdir file points to non-existent location"));
|
||||
free(path);
|
||||
return 1;
|
||||
} else {
|
||||
*wtpath = path;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*wtpath = path;
|
||||
return 0;
|
||||
}
|
||||
|
|
23
worktree.h
23
worktree.h
|
@ -11,11 +11,13 @@ struct worktree {
|
|||
char *id;
|
||||
char *head_ref; /* NULL if HEAD is broken or detached */
|
||||
char *lock_reason; /* private - use worktree_lock_reason */
|
||||
char *prune_reason; /* private - use worktree_prune_reason */
|
||||
struct object_id head_oid;
|
||||
int is_detached;
|
||||
int is_bare;
|
||||
int is_current;
|
||||
int lock_reason_valid; /* private */
|
||||
int prune_reason_valid; /* private */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -73,6 +75,27 @@ int is_main_worktree(const struct worktree *wt);
|
|||
*/
|
||||
const char *worktree_lock_reason(struct worktree *wt);
|
||||
|
||||
/*
|
||||
* Return the reason string if the given worktree should be pruned, otherwise
|
||||
* NULL if it should not be pruned. `expire` defines a grace period to prune
|
||||
* the worktree when its path does not exist.
|
||||
*/
|
||||
const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire);
|
||||
|
||||
/*
|
||||
* Return true if worktree entry should be pruned, along with the reason for
|
||||
* pruning. Otherwise, return false and the worktree's path in `wtpath`, or
|
||||
* NULL if it cannot be determined. Caller is responsible for freeing
|
||||
* returned path.
|
||||
*
|
||||
* `expire` defines a grace period to prune the worktree when its path
|
||||
* does not exist.
|
||||
*/
|
||||
int should_prune_worktree(const char *id,
|
||||
struct strbuf *reason,
|
||||
char **wtpath,
|
||||
timestamp_t expire);
|
||||
|
||||
#define WT_VALIDATE_WORKTREE_MISSING_OK (1 << 0)
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче