Skip to content

Commit 7783eb2

Browse files
committed
Merge branch 'nd/multiple-work-trees'
"git checkout [<tree-ish>] <paths>" spent unnecessary cycles checking if the current branch was checked out elsewhere, when we know we are not switching the branches ourselves. * nd/multiple-work-trees: worktree: new place for "git prune --worktrees" checkout: don't check worktrees when not necessary
2 parents 721f5bb + df0b6cf commit 7783eb2

13 files changed

+217
-126
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
@@ -910,6 +910,7 @@ BUILTIN_OBJS += builtin/var.o
910910
BUILTIN_OBJS += builtin/verify-commit.o
911911
BUILTIN_OBJS += builtin/verify-pack.o
912912
BUILTIN_OBJS += builtin/verify-tag.o
913+
BUILTIN_OBJS += builtin/worktree.o
913914
BUILTIN_OBJS += builtin/write-tree.o
914915

915916
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/checkout.c

+11-12
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,6 @@ static int parse_branchname_arg(int argc, const char **argv,
11101110
{
11111111
struct tree **source_tree = &opts->source_tree;
11121112
const char **new_branch = &opts->new_branch;
1113-
int force_detach = opts->force_detach;
11141113
int argcount = 0;
11151114
unsigned char branch_rev[20];
11161115
const char *arg;
@@ -1231,17 +1230,6 @@ static int parse_branchname_arg(int argc, const char **argv,
12311230
else
12321231
new->path = NULL; /* not an existing branch */
12331232

1234-
if (new->path && !force_detach && !*new_branch) {
1235-
unsigned char sha1[20];
1236-
int flag;
1237-
char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
1238-
if (head_ref &&
1239-
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
1240-
!opts->ignore_other_worktrees)
1241-
check_linked_checkouts(new);
1242-
free(head_ref);
1243-
}
1244-
12451233
new->commit = lookup_commit_reference_gently(rev, 1);
12461234
if (!new->commit) {
12471235
/* not a commit */
@@ -1321,6 +1309,17 @@ static int checkout_branch(struct checkout_opts *opts,
13211309
die(_("Cannot switch branch to a non-commit '%s'"),
13221310
new->name);
13231311

1312+
if (new->path && !opts->force_detach && !opts->new_branch) {
1313+
unsigned char sha1[20];
1314+
int flag;
1315+
char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
1316+
if (head_ref &&
1317+
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
1318+
!opts->ignore_other_worktrees)
1319+
check_linked_checkouts(new);
1320+
free(head_ref);
1321+
}
1322+
13241323
if (opts->new_worktree)
13251324
return prepare_linked_checkout(opts, new);
13261325

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()
@@ -211,13 +119,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
211119

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

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

0 commit comments

Comments
 (0)