Skip to content

Commit 58142c0

Browse files
pcloudsgitster
authored andcommitted
worktree: add "lock" command
Helped-by: Eric Sunshine <[email protected]> Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 346ef53 commit 58142c0

File tree

4 files changed

+110
-7
lines changed

4 files changed

+110
-7
lines changed

Documentation/git-worktree.txt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ SYNOPSIS
1111
[verse]
1212
'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
1313
'git worktree list' [--porcelain]
14+
'git worktree lock' [--reason <string>] <worktree>
1415
'git worktree prune' [-n] [-v] [--expire <expire>]
1516

1617
DESCRIPTION
@@ -38,9 +39,8 @@ section "DETAILS" for more information.
3839

3940
If a linked working tree is stored on a portable device or network share
4041
which is not always mounted, you can prevent its administrative files from
41-
being pruned by creating a file named 'locked' alongside the other
42-
administrative files, optionally containing a plain text reason that
43-
pruning should be suppressed. See section "DETAILS" for more information.
42+
being pruned by issuing the `git worktree lock` command, optionally
43+
specifying `--reason` to explain why the working tree is locked.
4444

4545
COMMANDS
4646
--------
@@ -61,6 +61,14 @@ each of the linked worktrees. The output details include if the worktree is
6161
bare, the revision currently checked out, and the branch currently checked out
6262
(or 'detached HEAD' if none).
6363

64+
lock::
65+
66+
If a working tree is on a portable device or network share which
67+
is not always mounted, lock it to prevent its administrative
68+
files from being pruned automatically. This also prevents it from
69+
being moved or deleted. Optionally, specify a reason for the lock
70+
with `--reason`.
71+
6472
prune::
6573

6674
Prune working tree information in $GIT_DIR/worktrees.
@@ -110,6 +118,13 @@ OPTIONS
110118
--expire <time>::
111119
With `prune`, only expire unused working trees older than <time>.
112120

121+
--reason <string>::
122+
With `lock`, an explanation why the working tree is locked.
123+
124+
<worktree>::
125+
Working trees can be identified by path, either relative or
126+
absolute.
127+
113128
DETAILS
114129
-------
115130
Each linked working tree has a private sub-directory in the repository's
@@ -150,7 +165,8 @@ instead.
150165

151166
To prevent a $GIT_DIR/worktrees entry from being pruned (which
152167
can be useful in some situations, such as when the
153-
entry's working tree is stored on a portable device), add a file named
168+
entry's working tree is stored on a portable device), use the
169+
`git worktree lock` command, which adds a file named
154170
'locked' to the entry's directory. The file contains the reason in
155171
plain text. For example, if a linked working tree's `.git` file points
156172
to `/path/main/.git/worktrees/test-next` then a file named
@@ -226,8 +242,6 @@ performed manually, such as:
226242
- `remove` to remove a linked working tree and its administrative files (and
227243
warn if the working tree is dirty)
228244
- `mv` to move or rename a working tree and update its administrative files
229-
- `lock` to prevent automatic pruning of administrative files (for instance,
230-
for a working tree on a portable device)
231245

232246
GIT
233247
---

builtin/worktree.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
static const char * const worktree_usage[] = {
1515
N_("git worktree add [<options>] <path> [<branch>]"),
1616
N_("git worktree list [<options>]"),
17+
N_("git worktree lock [<options>] <path>"),
1718
N_("git worktree prune [<options>]"),
1819
NULL
1920
};
@@ -459,6 +460,41 @@ static int list(int ac, const char **av, const char *prefix)
459460
return 0;
460461
}
461462

463+
static int lock_worktree(int ac, const char **av, const char *prefix)
464+
{
465+
const char *reason = "", *old_reason;
466+
struct option options[] = {
467+
OPT_STRING(0, "reason", &reason, N_("string"),
468+
N_("reason for locking")),
469+
OPT_END()
470+
};
471+
struct worktree **worktrees, *wt;
472+
473+
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
474+
if (ac != 1)
475+
usage_with_options(worktree_usage, options);
476+
477+
worktrees = get_worktrees();
478+
wt = find_worktree(worktrees, prefix, av[0]);
479+
if (!wt)
480+
die(_("'%s' is not a working tree"), av[0]);
481+
if (is_main_worktree(wt))
482+
die(_("The main working tree cannot be locked or unlocked"));
483+
484+
old_reason = is_worktree_locked(wt);
485+
if (old_reason) {
486+
if (*old_reason)
487+
die(_("'%s' is already locked, reason: %s"),
488+
av[0], old_reason);
489+
die(_("'%s' is already locked"), av[0]);
490+
}
491+
492+
write_file(git_common_path("worktrees/%s/locked", wt->id),
493+
"%s", reason);
494+
free_worktrees(worktrees);
495+
return 0;
496+
}
497+
462498
int cmd_worktree(int ac, const char **av, const char *prefix)
463499
{
464500
struct option options[] = {
@@ -475,5 +511,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
475511
return prune(ac - 1, av + 1, prefix);
476512
if (!strcmp(av[1], "list"))
477513
return list(ac - 1, av + 1, prefix);
514+
if (!strcmp(av[1], "lock"))
515+
return lock_worktree(ac - 1, av + 1, prefix);
478516
usage_with_options(worktree_usage, options);
479517
}

contrib/completion/git-completion.bash

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2597,7 +2597,7 @@ _git_whatchanged ()
25972597

25982598
_git_worktree ()
25992599
{
2600-
local subcommands="add list prune"
2600+
local subcommands="add list lock prune"
26012601
local subcommand="$(__git_find_on_cmdline "$subcommands")"
26022602
if [ -z "$subcommand" ]; then
26032603
__gitcomp "$subcommands"
@@ -2609,6 +2609,9 @@ _git_worktree ()
26092609
list,--*)
26102610
__gitcomp "--porcelain"
26112611
;;
2612+
lock,--*)
2613+
__gitcomp "--reason"
2614+
;;
26122615
prune,--*)
26132616
__gitcomp "--dry-run --expire --verbose"
26142617
;;

t/t2028-worktree-move.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/sh
2+
3+
test_description='test git worktree move, remove, lock and unlock'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'setup' '
8+
test_commit init &&
9+
git worktree add source &&
10+
git worktree list --porcelain | grep "^worktree" >actual &&
11+
cat <<-EOF >expected &&
12+
worktree $(pwd)
13+
worktree $(pwd)/source
14+
EOF
15+
test_cmp expected actual
16+
'
17+
18+
test_expect_success 'lock main worktree' '
19+
test_must_fail git worktree lock .
20+
'
21+
22+
test_expect_success 'lock linked worktree' '
23+
git worktree lock --reason hahaha source &&
24+
echo hahaha >expected &&
25+
test_cmp expected .git/worktrees/source/locked
26+
'
27+
28+
test_expect_success 'lock linked worktree from another worktree' '
29+
rm .git/worktrees/source/locked &&
30+
git worktree add elsewhere &&
31+
git -C elsewhere worktree lock --reason hahaha ../source &&
32+
echo hahaha >expected &&
33+
test_cmp expected .git/worktrees/source/locked
34+
'
35+
36+
test_expect_success 'lock worktree twice' '
37+
test_must_fail git worktree lock source &&
38+
echo hahaha >expected &&
39+
test_cmp expected .git/worktrees/source/locked
40+
'
41+
42+
test_expect_success 'lock worktree twice (from the locked worktree)' '
43+
test_must_fail git -C source worktree lock . &&
44+
echo hahaha >expected &&
45+
test_cmp expected .git/worktrees/source/locked
46+
'
47+
48+
test_done

0 commit comments

Comments
 (0)