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)