checkout: retire --to option
Now that "git worktree add" has achieved user-facing feature-parity with "git checkout --to", retire the latter. Move the actual linked worktree creation functionality, prepare_linked_checkout() and its helpers, verbatim from checkout.c to worktree.c. This effectively reverts changes to checkout.c by 529fef2 (checkout: support checking out into a new working directory, 2014-11-30) with the exception of merge_working_tree() and switch_branches() which still require specialized knowledge that a the checkout is occurring in a newly-created linked worktree (signaled to them by the private GIT_CHECKOUT_NEW_WORKTREE environment variable). Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Eric Sunshine committed
Jul 6, 2015 at 13:30 UTC
b979d95027242455b10e6f566b0e96c5f30cc908
3 files changed
+139
-173
Documentation/git-checkout.txt
-7
@@ -225,13 +225,6 @@ This means that you can use `git checkout -p` to selectively discard
225
edits from your current working tree. See the ``Interactive Mode''
226
section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
227
228
---to=<path>::
229
- Check out a branch in a separate working directory at
230
- `<path>`. A new working directory is linked to the current
231
- repository, sharing everything except working directory
232
- specific files such as HEAD, index, etc. See
233
- linkgit:git-worktree[1] for a description of linked worktrees.
234
-
228
--ignore-other-worktrees::
229
`git checkout` refuses when the wanted ref is already checked
230
out by another worktree. This option makes it check the ref
builtin/checkout.c
+1
-160
@@ -19,8 +19,6 @@
19
#include "ll-merge.h"
20
#include "resolve-undo.h"
21
#include "submodule.h"
22
-#include "argv-array.h"
23
-#include "sigchain.h"
22
23
static const char * const checkout_usage[] = {
24
N_("git checkout [options] <branch>"),
@@ -51,8 +49,6 @@ struct checkout_opts {
49
struct pathspec pathspec;
50
struct tree *source_tree;
51
54
- const char *new_worktree;
55
- const char **saved_argv;
52
int new_worktree_mode;
53
};
54
@@ -255,9 +251,6 @@ static int checkout_paths(const struct checkout_opts *opts,
251
die(_("Cannot update paths and switch to branch '%s' at the same time."),
252
opts->new_branch);
253
258
- if (opts->new_worktree)
259
- die(_("'%s' cannot be used with updating paths"), "--to");
260
-
254
if (opts->patch_mode)
255
return run_add_interactive(revision, "--patch=checkout",
256
&opts->pathspec);
@@ -825,142 +818,6 @@ static int switch_branches(const struct checkout_opts *opts,
818
return ret || writeout_error;
819
}
820
828
-static char *junk_work_tree;
829
-static char *junk_git_dir;
830
-static int is_junk;
831
-static pid_t junk_pid;
832
-
833
-static void remove_junk(void)
834
-{
835
- struct strbuf sb = STRBUF_INIT;
836
- if (!is_junk || getpid() != junk_pid)
837
- return;
838
- if (junk_git_dir) {
839
- strbuf_addstr(&sb, junk_git_dir);
840
- remove_dir_recursively(&sb, 0);
841
- strbuf_reset(&sb);
842
- }
843
- if (junk_work_tree) {
844
- strbuf_addstr(&sb, junk_work_tree);
845
- remove_dir_recursively(&sb, 0);
846
- }
847
- strbuf_release(&sb);
848
-}
849
-
850
-static void remove_junk_on_signal(int signo)
851
-{
852
- remove_junk();
853
- sigchain_pop(signo);
854
- raise(signo);
855
-}
856
-
857
-static int prepare_linked_checkout(const char *path, const char **child_argv)
858
-{
859
- struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
860
- struct strbuf sb = STRBUF_INIT;
861
- const char *name;
862
- struct stat st;
863
- struct child_process cp;
864
- int counter = 0, len, ret;
865
- unsigned char rev[20];
866
-
867
- if (file_exists(path) && !is_empty_dir(path))
868
- die(_("'%s' already exists"), path);
869
-
870
- len = strlen(path);
871
- while (len && is_dir_sep(path[len - 1]))
872
- len--;
873
-
874
- for (name = path + len - 1; name > path; name--)
875
- if (is_dir_sep(*name)) {
876
- name++;
877
- break;
878
- }
879
- strbuf_addstr(&sb_repo,
880
- git_path("worktrees/%.*s", (int)(path + len - name), name));
881
- len = sb_repo.len;
882
- if (safe_create_leading_directories_const(sb_repo.buf))
883
- die_errno(_("could not create leading directories of '%s'"),
884
- sb_repo.buf);
885
- while (!stat(sb_repo.buf, &st)) {
886
- counter++;
887
- strbuf_setlen(&sb_repo, len);
888
- strbuf_addf(&sb_repo, "%d", counter);
889
- }
890
- name = strrchr(sb_repo.buf, '/') + 1;
891
-
892
- junk_pid = getpid();
893
- atexit(remove_junk);
894
- sigchain_push_common(remove_junk_on_signal);
895
-
896
- if (mkdir(sb_repo.buf, 0777))
897
- die_errno(_("could not create directory of '%s'"), sb_repo.buf);
898
- junk_git_dir = xstrdup(sb_repo.buf);
899
- is_junk = 1;
900
-
901
- /*
902
- * lock the incomplete repo so prune won't delete it, unlock
903
- * after the preparation is over.
904
- */
905
- strbuf_addf(&sb, "%s/locked", sb_repo.buf);
906
- write_file(sb.buf, 1, "initializing\n");
907
-
908
- strbuf_addf(&sb_git, "%s/.git", path);
909
- if (safe_create_leading_directories_const(sb_git.buf))
910
- die_errno(_("could not create leading directories of '%s'"),
911
- sb_git.buf);
912
- junk_work_tree = xstrdup(path);
913
-
914
- strbuf_reset(&sb);
915
- strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
916
- write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
917
- write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n",
918
- real_path(get_git_common_dir()), name);
919
- /*
920
- * This is to keep resolve_ref() happy. We need a valid HEAD
921
- * or is_git_directory() will reject the directory. Moreover, HEAD
922
- * in the new worktree must resolve to the same value as HEAD in
923
- * the current tree since the command invoked to populate the new
924
- * worktree will be handed the branch/ref specified by the user.
925
- * For instance, if the user asks for the new worktree to be based
926
- * at HEAD~5, then the resolved HEAD~5 in the new worktree must
927
- * match the resolved HEAD~5 in the current tree in order to match
928
- * the user's expectation.
929
- */
930
- if (!resolve_ref_unsafe("HEAD", 0, rev, NULL))
931
- die(_("unable to resolve HEAD"));
932
- strbuf_reset(&sb);
933
- strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
934
- write_file(sb.buf, 1, "%s\n", sha1_to_hex(rev));
935
- strbuf_reset(&sb);
936
- strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
937
- write_file(sb.buf, 1, "../..\n");
938
-
939
- fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
940
-
941
- setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
942
- setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
943
- setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
944
- memset(&cp, 0, sizeof(cp));
945
- cp.git_cmd = 1;
946
- cp.argv = child_argv;
947
- ret = run_command(&cp);
948
- if (!ret) {
949
- is_junk = 0;
950
- free(junk_work_tree);
951
- free(junk_git_dir);
952
- junk_work_tree = NULL;
953
- junk_git_dir = NULL;
954
- }
955
- strbuf_reset(&sb);
956
- strbuf_addf(&sb, "%s/locked", sb_repo.buf);
957
- unlink_or_warn(sb.buf);
958
- strbuf_release(&sb);
959
- strbuf_release(&sb_repo);
960
- strbuf_release(&sb_git);
961
- return ret;
962
-}
963
-
821
static int git_checkout_config(const char *var, const char *value, void *cb)
822
{
823
if (!strcmp(var, "diff.ignoresubmodules")) {
@@ -1299,13 +1156,6 @@ static int checkout_branch(struct checkout_opts *opts,
1156
free(head_ref);
1157
}
1158
1302
- if (opts->new_worktree) {
1303
- if (!new->commit)
1304
- die(_("no branch specified"));
1305
- return prepare_linked_checkout(opts->new_worktree,
1306
- opts->saved_argv);
1307
- }
1308
-
1159
if (!new->commit && opts->new_branch) {
1160
unsigned char rev[20];
1161
int flag;
@@ -1348,8 +1198,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
1198
N_("do not limit pathspecs to sparse entries only")),
1199
OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
1200
N_("second guess 'git checkout no-such-branch'")),
1351
- OPT_FILENAME(0, "to", &opts.new_worktree,
1352
- N_("check a branch out in a separate working directory")),
1201
OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
1202
N_("do not check if another worktree is holding the given ref")),
1203
OPT_END(),
@@ -1360,9 +1208,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
1208
opts.overwrite_ignore = 1;
1209
opts.prefix = prefix;
1210
1363
- opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2));
1364
- memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1));
1365
-
1211
gitmodules_config();
1212
git_config(git_checkout_config, &opts);
1213
@@ -1371,13 +1216,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
1216
argc = parse_options(argc, argv, prefix, options, checkout_usage,
1217
PARSE_OPT_KEEP_DASHDASH);
1218
1374
- /* recursive execution from checkout_new_worktree() */
1219
opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
1376
- if (opts.new_worktree_mode)
1377
- opts.new_worktree = NULL;
1220
1379
- if (!opts.new_worktree)
1380
- setup_work_tree();
1221
+ setup_work_tree();
1222
1223
if (conflict_style) {
1224
opts.merge = 1; /* implied */
builtin/worktree.c
+138
-6
@@ -4,6 +4,7 @@
4
#include "parse-options.h"
5
#include "argv-array.h"
6
#include "run-command.h"
7
+#include "sigchain.h"
8
9
static const char * const worktree_usage[] = {
10
N_("git worktree add [<options>] <path> <branch>"),
@@ -122,9 +123,144 @@ static int prune(int ac, const char **av, const char *prefix)
123
return 0;
124
}
125
126
+static char *junk_work_tree;
127
+static char *junk_git_dir;
128
+static int is_junk;
129
+static pid_t junk_pid;
130
+
131
+static void remove_junk(void)
132
+{
133
+ struct strbuf sb = STRBUF_INIT;
134
+ if (!is_junk || getpid() != junk_pid)
135
+ return;
136
+ if (junk_git_dir) {
137
+ strbuf_addstr(&sb, junk_git_dir);
138
+ remove_dir_recursively(&sb, 0);
139
+ strbuf_reset(&sb);
140
+ }
141
+ if (junk_work_tree) {
142
+ strbuf_addstr(&sb, junk_work_tree);
143
+ remove_dir_recursively(&sb, 0);
144
+ }
145
+ strbuf_release(&sb);
146
+}
147
+
148
+static void remove_junk_on_signal(int signo)
149
+{
150
+ remove_junk();
151
+ sigchain_pop(signo);
152
+ raise(signo);
153
+}
154
+
155
+static int add_worktree(const char *path, const char **child_argv)
156
+{
157
+ struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
158
+ struct strbuf sb = STRBUF_INIT;
159
+ const char *name;
160
+ struct stat st;
161
+ struct child_process cp;
162
+ int counter = 0, len, ret;
163
+ unsigned char rev[20];
164
+
165
+ if (file_exists(path) && !is_empty_dir(path))
166
+ die(_("'%s' already exists"), path);
167
+
168
+ len = strlen(path);
169
+ while (len && is_dir_sep(path[len - 1]))
170
+ len--;
171
+
172
+ for (name = path + len - 1; name > path; name--)
173
+ if (is_dir_sep(*name)) {
174
+ name++;
175
+ break;
176
+ }
177
+ strbuf_addstr(&sb_repo,
178
+ git_path("worktrees/%.*s", (int)(path + len - name), name));
179
+ len = sb_repo.len;
180
+ if (safe_create_leading_directories_const(sb_repo.buf))
181
+ die_errno(_("could not create leading directories of '%s'"),
182
+ sb_repo.buf);
183
+ while (!stat(sb_repo.buf, &st)) {
184
+ counter++;
185
+ strbuf_setlen(&sb_repo, len);
186
+ strbuf_addf(&sb_repo, "%d", counter);
187
+ }
188
+ name = strrchr(sb_repo.buf, '/') + 1;
189
+
190
+ junk_pid = getpid();
191
+ atexit(remove_junk);
192
+ sigchain_push_common(remove_junk_on_signal);
193
+
194
+ if (mkdir(sb_repo.buf, 0777))
195
+ die_errno(_("could not create directory of '%s'"), sb_repo.buf);
196
+ junk_git_dir = xstrdup(sb_repo.buf);
197
+ is_junk = 1;
198
+
199
+ /*
200
+ * lock the incomplete repo so prune won't delete it, unlock
201
+ * after the preparation is over.
202
+ */
203
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
204
+ write_file(sb.buf, 1, "initializing\n");
205
+
206
+ strbuf_addf(&sb_git, "%s/.git", path);
207
+ if (safe_create_leading_directories_const(sb_git.buf))
208
+ die_errno(_("could not create leading directories of '%s'"),
209
+ sb_git.buf);
210
+ junk_work_tree = xstrdup(path);
211
+
212
+ strbuf_reset(&sb);
213
+ strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
214
+ write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
215
+ write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n",
216
+ real_path(get_git_common_dir()), name);
217
+ /*
218
+ * This is to keep resolve_ref() happy. We need a valid HEAD
219
+ * or is_git_directory() will reject the directory. Moreover, HEAD
220
+ * in the new worktree must resolve to the same value as HEAD in
221
+ * the current tree since the command invoked to populate the new
222
+ * worktree will be handed the branch/ref specified by the user.
223
+ * For instance, if the user asks for the new worktree to be based
224
+ * at HEAD~5, then the resolved HEAD~5 in the new worktree must
225
+ * match the resolved HEAD~5 in the current tree in order to match
226
+ * the user's expectation.
227
+ */
228
+ if (!resolve_ref_unsafe("HEAD", 0, rev, NULL))
229
+ die(_("unable to resolve HEAD"));
230
+ strbuf_reset(&sb);
231
+ strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
232
+ write_file(sb.buf, 1, "%s\n", sha1_to_hex(rev));
233
+ strbuf_reset(&sb);
234
+ strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
235
+ write_file(sb.buf, 1, "../..\n");
236
+
237
+ fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
238
+
239
+ setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
240
+ setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
241
+ setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
242
+ memset(&cp, 0, sizeof(cp));
243
+ cp.git_cmd = 1;
244
+ cp.argv = child_argv;
245
+ ret = run_command(&cp);
246
+ if (!ret) {
247
+ is_junk = 0;
248
+ free(junk_work_tree);
249
+ free(junk_git_dir);
250
+ junk_work_tree = NULL;
251
+ junk_git_dir = NULL;
252
+ }
253
+ strbuf_reset(&sb);
254
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
255
+ unlink_or_warn(sb.buf);
256
+ strbuf_release(&sb);
257
+ strbuf_release(&sb_repo);
258
+ strbuf_release(&sb_git);
259
+ return ret;
260
+}
261
+
262
static int add(int ac, const char **av, const char *prefix)
263
{
127
- struct child_process c;
264
int force = 0, detach = 0;
265
const char *new_branch = NULL, *new_branch_force = NULL;
266
const char *path, *branch;
@@ -149,7 +285,6 @@ static int add(int ac, const char **av, const char *prefix)
285
branch = av[1];
286
287
argv_array_push(&cmd, "checkout");
152
- argv_array_pushl(&cmd, "--to", path, NULL);
288
if (force)
289
argv_array_push(&cmd, "--ignore-other-worktrees");
290
if (new_branch)
@@ -160,10 +295,7 @@ static int add(int ac, const char **av, const char *prefix)
295
argv_array_push(&cmd, "--detach");
296
argv_array_push(&cmd, branch);
297
163
- memset(&c, 0, sizeof(c));
164
- c.git_cmd = 1;
165
- c.argv = cmd.argv;
166
- return run_command(&c);
298
+ return add_worktree(path, cmd.argv);
299
}
300
301
int cmd_worktree(int ac, const char **av, const char *prefix)