Skip to content

Commit df0b6cf

Browse files
pcloudsgitster
authored andcommitted
worktree: new place for "git prune --worktrees"
Commit 23af91d (prune: strategies for linked checkouts - 2014-11-30) adds "--worktrees" to "git prune" without realizing that "git prune" is for object database only. This patch moves the same functionality to a new command "git worktree". Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]>
1 parent e1c1ab9 commit df0b6cf

11 files changed

+198
-114
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
/git-verify-tag
172172
/git-web--browse
173173
/git-whatchanged
174+
/git-worktree
174175
/git-write-tree
175176
/git-core-*/?*
176177
/gitweb/GITWEB-BUILD-OPTIONS

Documentation/git-prune.txt

-3
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ OPTIONS
4848
--expire <time>::
4949
Only expire loose objects older than <time>.
5050

51-
--worktrees::
52-
Prune dead working tree information in $GIT_DIR/worktrees.
53-
5451
<head>...::
5552
In addition to objects
5653
reachable from any of our references, keep objects

Documentation/git-worktree.txt

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
git-worktree(1)
2+
===============
3+
4+
NAME
5+
----
6+
git-worktree - Manage multiple worktrees
7+
8+
9+
SYNOPSIS
10+
--------
11+
[verse]
12+
'git worktree prune' [-n] [-v] [--expire <expire>]
13+
14+
DESCRIPTION
15+
-----------
16+
17+
Manage multiple worktrees attached to the same repository. These are
18+
created by the command `git checkout --to`.
19+
20+
COMMANDS
21+
--------
22+
prune::
23+
24+
Prune working tree information in $GIT_DIR/worktrees.
25+
26+
OPTIONS
27+
-------
28+
29+
-n::
30+
--dry-run::
31+
Do not remove anything; just report what it would
32+
remove.
33+
34+
-v::
35+
--verbose::
36+
Report all removals.
37+
38+
--expire <time>::
39+
Only expire unused worktrees older than <time>.
40+
41+
SEE ALSO
42+
--------
43+
44+
linkgit:git-checkout[1]
45+
46+
GIT
47+
---
48+
Part of the linkgit:git[1] suite

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,7 @@ BUILTIN_OBJS += builtin/var.o
886886
BUILTIN_OBJS += builtin/verify-commit.o
887887
BUILTIN_OBJS += builtin/verify-pack.o
888888
BUILTIN_OBJS += builtin/verify-tag.o
889+
BUILTIN_OBJS += builtin/worktree.o
889890
BUILTIN_OBJS += builtin/write-tree.o
890891

891892
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)

builtin.h

+1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ extern int cmd_verify_commit(int argc, const char **argv, const char *prefix);
133133
extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
134134
extern int cmd_version(int argc, const char **argv, const char *prefix);
135135
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
136+
extern int cmd_worktree(int argc, const char **argv, const char *prefix);
136137
extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
137138
extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
138139
extern int cmd_show_ref(int argc, const char **argv, const char *prefix);

builtin/gc.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
293293
argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
294294
argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
295295
argv_array_pushl(&prune, "prune", "--expire", NULL);
296-
argv_array_pushl(&prune_worktrees, "prune", "--worktrees", "--expire", NULL);
296+
argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
297297
argv_array_pushl(&rerere, "rerere", "gc", NULL);
298298

299299
gc_config();

builtin/prune.c

-99
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include "reachable.h"
77
#include "parse-options.h"
88
#include "progress.h"
9-
#include "dir.h"
109

1110
static const char * const prune_usage[] = {
1211
N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"),
@@ -76,95 +75,6 @@ static int prune_subdir(int nr, const char *path, void *data)
7675
return 0;
7776
}
7877

79-
static int prune_worktree(const char *id, struct strbuf *reason)
80-
{
81-
struct stat st;
82-
char *path;
83-
int fd, len;
84-
85-
if (!is_directory(git_path("worktrees/%s", id))) {
86-
strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
87-
return 1;
88-
}
89-
if (file_exists(git_path("worktrees/%s/locked", id)))
90-
return 0;
91-
if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
92-
strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
93-
return 1;
94-
}
95-
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
96-
if (fd < 0) {
97-
strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
98-
id, strerror(errno));
99-
return 1;
100-
}
101-
len = st.st_size;
102-
path = xmalloc(len + 1);
103-
read_in_full(fd, path, len);
104-
close(fd);
105-
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
106-
len--;
107-
if (!len) {
108-
strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
109-
free(path);
110-
return 1;
111-
}
112-
path[len] = '\0';
113-
if (!file_exists(path)) {
114-
struct stat st_link;
115-
free(path);
116-
/*
117-
* the repo is moved manually and has not been
118-
* accessed since?
119-
*/
120-
if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
121-
st_link.st_nlink > 1)
122-
return 0;
123-
if (st.st_mtime <= expire) {
124-
strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
125-
return 1;
126-
} else {
127-
return 0;
128-
}
129-
}
130-
free(path);
131-
return 0;
132-
}
133-
134-
static void prune_worktrees(void)
135-
{
136-
struct strbuf reason = STRBUF_INIT;
137-
struct strbuf path = STRBUF_INIT;
138-
DIR *dir = opendir(git_path("worktrees"));
139-
struct dirent *d;
140-
int ret;
141-
if (!dir)
142-
return;
143-
while ((d = readdir(dir)) != NULL) {
144-
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
145-
continue;
146-
strbuf_reset(&reason);
147-
if (!prune_worktree(d->d_name, &reason))
148-
continue;
149-
if (show_only || verbose)
150-
printf("%s\n", reason.buf);
151-
if (show_only)
152-
continue;
153-
strbuf_reset(&path);
154-
strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
155-
ret = remove_dir_recursively(&path, 0);
156-
if (ret < 0 && errno == ENOTDIR)
157-
ret = unlink(path.buf);
158-
if (ret)
159-
error(_("failed to remove: %s"), strerror(errno));
160-
}
161-
closedir(dir);
162-
if (!show_only)
163-
rmdir(git_path("worktrees"));
164-
strbuf_release(&reason);
165-
strbuf_release(&path);
166-
}
167-
16878
/*
16979
* Write errors (particularly out of space) can result in
17080
* failed temporary packs (and more rarely indexes and other
@@ -191,12 +101,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
191101
{
192102
struct rev_info revs;
193103
struct progress *progress = NULL;
194-
int do_prune_worktrees = 0;
195104
const struct option options[] = {
196105
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
197106
OPT__VERBOSE(&verbose, N_("report pruned objects")),
198107
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
199-
OPT_BOOL(0, "worktrees", &do_prune_worktrees, N_("prune .git/worktrees")),
200108
OPT_EXPIRY_DATE(0, "expire", &expire,
201109
N_("expire objects older than <time>")),
202110
OPT_END()
@@ -210,13 +118,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
210118

211119
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
212120

213-
if (do_prune_worktrees) {
214-
if (argc)
215-
die(_("--worktrees does not take extra arguments"));
216-
prune_worktrees();
217-
return 0;
218-
}
219-
220121
while (argc--) {
221122
unsigned char sha1[20];
222123
const char *name = *argv++;

builtin/worktree.c

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "cache.h"
2+
#include "builtin.h"
3+
#include "dir.h"
4+
#include "parse-options.h"
5+
6+
static const char * const worktree_usage[] = {
7+
N_("git worktree prune [<options>]"),
8+
NULL
9+
};
10+
11+
static int show_only;
12+
static int verbose;
13+
static unsigned long expire;
14+
15+
static int prune_worktree(const char *id, struct strbuf *reason)
16+
{
17+
struct stat st;
18+
char *path;
19+
int fd, len;
20+
21+
if (!is_directory(git_path("worktrees/%s", id))) {
22+
strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
23+
return 1;
24+
}
25+
if (file_exists(git_path("worktrees/%s/locked", id)))
26+
return 0;
27+
if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
28+
strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
29+
return 1;
30+
}
31+
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
32+
if (fd < 0) {
33+
strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
34+
id, strerror(errno));
35+
return 1;
36+
}
37+
len = st.st_size;
38+
path = xmalloc(len + 1);
39+
read_in_full(fd, path, len);
40+
close(fd);
41+
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
42+
len--;
43+
if (!len) {
44+
strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
45+
free(path);
46+
return 1;
47+
}
48+
path[len] = '\0';
49+
if (!file_exists(path)) {
50+
struct stat st_link;
51+
free(path);
52+
/*
53+
* the repo is moved manually and has not been
54+
* accessed since?
55+
*/
56+
if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
57+
st_link.st_nlink > 1)
58+
return 0;
59+
if (st.st_mtime <= expire) {
60+
strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
61+
return 1;
62+
} else {
63+
return 0;
64+
}
65+
}
66+
free(path);
67+
return 0;
68+
}
69+
70+
static void prune_worktrees(void)
71+
{
72+
struct strbuf reason = STRBUF_INIT;
73+
struct strbuf path = STRBUF_INIT;
74+
DIR *dir = opendir(git_path("worktrees"));
75+
struct dirent *d;
76+
int ret;
77+
if (!dir)
78+
return;
79+
while ((d = readdir(dir)) != NULL) {
80+
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
81+
continue;
82+
strbuf_reset(&reason);
83+
if (!prune_worktree(d->d_name, &reason))
84+
continue;
85+
if (show_only || verbose)
86+
printf("%s\n", reason.buf);
87+
if (show_only)
88+
continue;
89+
strbuf_reset(&path);
90+
strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
91+
ret = remove_dir_recursively(&path, 0);
92+
if (ret < 0 && errno == ENOTDIR)
93+
ret = unlink(path.buf);
94+
if (ret)
95+
error(_("failed to remove: %s"), strerror(errno));
96+
}
97+
closedir(dir);
98+
if (!show_only)
99+
rmdir(git_path("worktrees"));
100+
strbuf_release(&reason);
101+
strbuf_release(&path);
102+
}
103+
104+
static int prune(int ac, const char **av, const char *prefix)
105+
{
106+
struct option options[] = {
107+
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
108+
OPT__VERBOSE(&verbose, N_("report pruned objects")),
109+
OPT_EXPIRY_DATE(0, "expire", &expire,
110+
N_("expire objects older than <time>")),
111+
OPT_END()
112+
};
113+
114+
expire = ULONG_MAX;
115+
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
116+
if (ac)
117+
usage_with_options(worktree_usage, options);
118+
prune_worktrees();
119+
return 0;
120+
}
121+
122+
int cmd_worktree(int ac, const char **av, const char *prefix)
123+
{
124+
struct option options[] = {
125+
OPT_END()
126+
};
127+
128+
if (ac < 2)
129+
usage_with_options(worktree_usage, options);
130+
if (!strcmp(av[1], "prune"))
131+
return prune(ac - 1, av + 1, prefix);
132+
usage_with_options(worktree_usage, options);
133+
}

command-list.txt

+1
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,5 @@ git-verify-pack plumbinginterrogators
138138
git-verify-tag ancillaryinterrogators
139139
gitweb ancillaryinterrogators
140140
git-whatchanged ancillaryinterrogators
141+
git-worktree mainporcelain
141142
git-write-tree plumbingmanipulators

git.c

+1
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ static struct cmd_struct commands[] = {
484484
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
485485
{ "version", cmd_version },
486486
{ "whatchanged", cmd_whatchanged, RUN_SETUP },
487+
{ "worktree", cmd_worktree, RUN_SETUP },
487488
{ "write-tree", cmd_write_tree, RUN_SETUP },
488489
};
489490

0 commit comments

Comments
 (0)