branch: publish die_if_checked_out()

git-worktree currently conflates new branch creation, setting of HEAD in the new wortkree, and worktree population into a single sub-invocation of git-checkout. However, these operations will eventually be separated, and git-worktree itself will need to be able to detect if the branch is already checked out elsewhere, rather than relying upon git-branch to make this determination, so publish die_if_checked_out(). Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Eric Sunshine committed Jul 17, 2015 at 19:00 UTC ed89f84b3c05d6359aba842e245910c996d91859
3 files changed +74 -67
branch.c
+67
@@ -309,3 +309,70 @@ void remove_branch_state(void)
309 unlink(git_path("MERGE_MODE"));
310 unlink(git_path("SQUASH_MSG"));
311 }
312 +
313 +static void check_linked_checkout(const char *branch, const char *id)
314 +{
315 + struct strbuf sb = STRBUF_INIT;
316 + struct strbuf path = STRBUF_INIT;
317 + struct strbuf gitdir = STRBUF_INIT;
318 +
319 + /*
320 + * $GIT_COMMON_DIR/HEAD is practically outside
321 + * $GIT_DIR so resolve_ref_unsafe() won't work (it
322 + * uses git_path). Parse the ref ourselves.
323 + */
324 + if (id)
325 + strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
326 + else
327 + strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
328 +
329 + if (!strbuf_readlink(&sb, path.buf, 0)) {
330 + if (!starts_with(sb.buf, "refs/") ||
331 + check_refname_format(sb.buf, 0))
332 + goto done;
333 + } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&
334 + starts_with(sb.buf, "ref:")) {
335 + strbuf_remove(&sb, 0, strlen("ref:"));
336 + strbuf_trim(&sb);
337 + } else
338 + goto done;
339 + if (strcmp(sb.buf, branch))
340 + goto done;
341 + if (id) {
342 + strbuf_reset(&path);
343 + strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
344 + if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
345 + goto done;
346 + strbuf_rtrim(&gitdir);
347 + } else
348 + strbuf_addstr(&gitdir, get_git_common_dir());
349 + skip_prefix(branch, "refs/heads/", &branch);
350 + strbuf_strip_suffix(&gitdir, ".git");
351 + die(_("'%s' is already checked out at '%s'"), branch, gitdir.buf);
352 +done:
353 + strbuf_release(&path);
354 + strbuf_release(&sb);
355 + strbuf_release(&gitdir);
356 +}
357 +
358 +void die_if_checked_out(const char *branch)
359 +{
360 + struct strbuf path = STRBUF_INIT;
361 + DIR *dir;
362 + struct dirent *d;
363 +
364 + check_linked_checkout(branch, NULL);
365 +
366 + strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
367 + dir = opendir(path.buf);
368 + strbuf_release(&path);
369 + if (!dir)
370 + return;
371 +
372 + while ((d = readdir(dir)) != NULL) {
373 + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
374 + continue;
375 + check_linked_checkout(branch, d->d_name);
376 + }
377 + closedir(dir);
378 +}
branch.h
+7
@@ -52,4 +52,11 @@ extern void install_branch_config(int flag, const char *local, const char *origi
52 */
53 extern int read_branch_desc(struct strbuf *, const char *branch_name);
54
55 +/*
56 + * Check if a branch is checked out in the main worktree or any linked
57 + * worktree and die (with a message describing its checkout location) if
58 + * it is.
59 + */
60 +extern void die_if_checked_out(const char *branch);
61 +
62 #endif
builtin/checkout.c
-67
@@ -873,73 +873,6 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
873 return NULL;
874 }
875
876 -static void check_linked_checkout(const char *branch, const char *id)
877 -{
878 - struct strbuf sb = STRBUF_INIT;
879 - struct strbuf path = STRBUF_INIT;
880 - struct strbuf gitdir = STRBUF_INIT;
881 -
882 - /*
883 - * $GIT_COMMON_DIR/HEAD is practically outside
884 - * $GIT_DIR so resolve_ref_unsafe() won't work (it
885 - * uses git_path). Parse the ref ourselves.
886 - */
887 - if (id)
888 - strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
889 - else
890 - strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
891 -
892 - if (!strbuf_readlink(&sb, path.buf, 0)) {
893 - if (!starts_with(sb.buf, "refs/") ||
894 - check_refname_format(sb.buf, 0))
895 - goto done;
896 - } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&
897 - starts_with(sb.buf, "ref:")) {
898 - strbuf_remove(&sb, 0, strlen("ref:"));
899 - strbuf_trim(&sb);
900 - } else
901 - goto done;
902 - if (strcmp(sb.buf, branch))
903 - goto done;
904 - if (id) {
905 - strbuf_reset(&path);
906 - strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
907 - if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
908 - goto done;
909 - strbuf_rtrim(&gitdir);
910 - } else
911 - strbuf_addstr(&gitdir, get_git_common_dir());
912 - skip_prefix(branch, "refs/heads/", &branch);
913 - strbuf_strip_suffix(&gitdir, ".git");
914 - die(_("'%s' is already checked out at '%s'"), branch, gitdir.buf);
915 -done:
916 - strbuf_release(&path);
917 - strbuf_release(&sb);
918 - strbuf_release(&gitdir);
919 -}
920 -
921 -static void die_if_checked_out(const char *branch)
922 -{
923 - struct strbuf path = STRBUF_INIT;
924 - DIR *dir;
925 - struct dirent *d;
926 -
927 - check_linked_checkout(branch, NULL);
928 -
929 - strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
930 - dir = opendir(path.buf);
931 - strbuf_release(&path);
932 - if (!dir)
933 - return;
934 -
935 - while ((d = readdir(dir)) != NULL) {
936 - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
937 - continue;
938 - check_linked_checkout(branch, d->d_name);
939 - }
940 - closedir(dir);
941 -}
942 -
876 static int parse_branchname_arg(int argc, const char **argv,
877 int dwim_new_local_branch_ok,
878 struct branch_info *new,