git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 10/13] worktree.c: check whether branch is rebased in another worktree

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Apr 22, 2016, 13:01 UTC
Message-ID
<1461330096-21783-11-git-send-email-pclouds@gmail.com>
In-Reply-To
<1461330096-21783-1-git-send-email-pclouds@gmail.com>
This function find_shared_symref() is used in a couple places:
1) in builtin/branch.c: it's used to detect if a branch is checked out
   elsewhere and refuse to delete the branch.
2) in builtin/notes.c: it's used to detect if a note is being merged in
   another worktree
3) in branch.c, the function die_if_checked_out() is actually used by
   "git checkout" and "git worktree add" to see if a branch is already
   checked out elsewhere and refuse the operation.

In cases 1 and 3, if a rebase is ongoing, "HEAD" will be in detached mode, find_shared_symref() fails to detect it and declares "no branch is checked out here", which is not really what we want.

This patch tightens the test. If the given symref is "HEAD", we try to detect if rebase is ongoing. If so return the branch being rebased. This makes checkout and branch delete operations safer because you can't checkout a branch being rebased in another place, or delete it.

Special case for checkout. If the current branch is being rebased, git-rebase.sh may use "git checkout" to abort and return back to the original branch. The updated test in find_shared_symref() will prevent that and "git rebase --abort" will fail as a result. find_shared_symref() and die_if_checked_out() have to learn a new option ignore_current_worktree to loosen the test a bit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c                |  4 ++--
 branch.h                |  2 +-
 builtin/checkout.c      |  2 +-
 builtin/worktree.c      |  4 ++--
 t/t2025-worktree-add.sh | 38 ++++++++++++++++++++++++++++++++++++++
 worktree.c              | 32 ++++++++++++++++++++++++++++++++
 6 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/branch.c b/branch.c
index 1f1fbf5..a5a8dcb 100644
--- a/branch.c
+++ b/branch.c
@@ -334,12 +334,12 @@ void remove_branch_state(void)
 	unlink(git_path_squash_msg());
 }
 
-void die_if_checked_out(const char *branch)
+void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
 	const struct worktree *wt;
 
 	wt = find_shared_symref("HEAD", branch);
-	if (!wt)
+	if (!wt || (ignore_current_worktree && wt->is_current))
 		return;
 	skip_prefix(branch, "refs/heads/", &branch);
 	die(_("'%s' is already checked out at '%s'"),
diff --git a/branch.h b/branch.h
index d69163d..b2f9649 100644
--- a/branch.h
+++ b/branch.h
@@ -58,7 +58,7 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
  * worktree and die (with a message describing its checkout location) if
  * it is.
  */
-extern void die_if_checked_out(const char *branch);
+extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
 /*
  * Update all per-worktree HEADs pointing at the old ref to point the new ref.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index efcbd8f..6041718 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1111,7 +1111,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
 		if (head_ref &&
 		    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
-			die_if_checked_out(new->path);
+			die_if_checked_out(new->path, 1);
 		free(head_ref);
 	}
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index d8e3795..12c0af7 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -205,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
 	if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
 		 ref_exists(symref.buf)) { /* it's a branch */
 		if (!opts->force)
-			die_if_checked_out(symref.buf);
+			die_if_checked_out(symref.buf, 0);
 	} else { /* must be a commit */
 		commit = lookup_commit_reference_by_name(refname);
 		if (!commit)
@@ -349,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
 		if (!opts.force &&
 		    !strbuf_check_branch_ref(&symref, opts.new_branch) &&
 		    ref_exists(symref.buf))
-			die_if_checked_out(symref.buf);
+			die_if_checked_out(symref.buf, 0);
 		strbuf_release(&symref);
 	}
 
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 3acb992..da54327 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -4,6 +4,8 @@ test_description='test git worktree add'
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success 'setup' '
 	test_commit init
 '
@@ -225,4 +227,40 @@ test_expect_success '"add" worktree with --checkout' '
 	test_cmp init.t swamp2/init.t
 '
 
+test_expect_success 'put a worktree under rebase' '
+	git worktree add under-rebase &&
+	(
+		cd under-rebase &&
+		set_fake_editor &&
+		FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+		git worktree list | grep "under-rebase.*detached HEAD"
+	)
+'
+
+test_expect_success 'add a worktree, checking out a rebased branch' '
+	test_must_fail git worktree add new-rebase under-rebase &&
+	! test -d new-rebase
+'
+
+test_expect_success 'checking out a rebased branch from another worktree' '
+	git worktree add new-place &&
+	test_must_fail git -C new-place checkout under-rebase
+'
+
+test_expect_success 'not allow to delete a branch under rebase' '
+	(
+		cd under-rebase &&
+		test_must_fail git branch -D under-rebase
+	)
+'
+
+test_expect_success 'check out from current worktree branch ok' '
+	(
+		cd under-rebase &&
+		git checkout under-rebase &&
+		git checkout - &&
+		git rebase --abort
+	)
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index 927905b..5043756 100644
--- a/worktree.c
+++ b/worktree.c
@@ -3,6 +3,7 @@
 #include "strbuf.h"
 #include "worktree.h"
 #include "dir.h"
+#include "wt-status.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -215,6 +216,30 @@ const char *get_worktree_git_dir(const struct worktree *wt)
 		return git_common_path("worktrees/%s", wt->id);
 }
 
+static int is_worktree_being_rebased(const struct worktree *wt,
+				     const char *target)
+{
+	struct wt_status_state state;
+	int found_rebase;
+
+	memset(&state, 0, sizeof(state));
+	found_rebase = wt_status_check_rebase(wt, &state) &&
+		((state.rebase_in_progress ||
+		  state.rebase_interactive_in_progress) &&
+		 state.branch &&
+		 starts_with(target, "refs/heads/") &&
+		 !strcmp(state.branch, target + strlen("refs/heads/")));
+	free(state.branch);
+	free(state.onto);
+	return found_rebase;
+}
+
+/*
+ * note: this function should be able to detect shared symref even if
+ * HEAD is temporarily detached (e.g. in the middle of rebase or
+ * bisect). New commands that do similar things should update this
+ * function as well.
+ */
 const struct worktree *find_shared_symref(const char *symref,
 					  const char *target)
 {
@@ -231,6 +256,13 @@ const struct worktree *find_shared_symref(const char *symref,
 	for (i = 0; worktrees[i]; i++) {
 		struct worktree *wt = worktrees[i];
 
+		if (wt->is_detached && !strcmp(symref, "HEAD")) {
+			if (is_worktree_being_rebased(wt, target)) {
+				existing = wt;
+				break;
+			}
+		}
+
 		strbuf_reset(&path);
 		strbuf_reset(&sb);
 		strbuf_addf(&path, "%s/%s",
-- 
2.8.0.rc0.210.gd302cd2
Previous: Nguyễn Thái Ngọc DuyNext: Nguyễn Thái Ngọc Duy
Message 158 of 162 in “possible to checkout same branch in different worktree”
  1. Reto HablützelApr 15, 2016
  2. Junio C HamanoApr 15, 2016
  3. Duy NguyenApr 16, 2016
  4. 0/7 fix checking out a being-rebased branchNguyễn Thái Ngọc Duy, Apr 17, 2016
  5. 1/7 path.c: add git_common_path() and strbuf_git_common_path()Nguyễn Thái Ngọc Duy, Apr 17, 2016
  6. 2/7 worktree.c: store "id" instead of "git_dir"Nguyễn Thái Ngọc Duy, Apr 17, 2016
  7. 3/7 path.c: refactor and add worktree_git_path()Nguyễn Thái Ngọc Duy, Apr 17, 2016
  8. 4/7 worktree.c: add worktree_git_path_..._head()Nguyễn Thái Ngọc Duy, Apr 17, 2016
  9. 5/7 wt-status.c: make wt_status_get_state() support worktreeNguyễn Thái Ngọc Duy, Apr 17, 2016
  10. 6/7 worktree.c: avoid referencing to worktrees[i] multiple timesNguyễn Thái Ngọc Duy, Apr 17, 2016
  11. 7/7 checkout: prevent checking out a branch being rebased in another worktreeNguyễn Thái Ngọc Duy, Apr 17, 2016
  12. Junio C HamanoApr 18, 2016
  13. Duy NguyenApr 19, 2016
  14. Junio C HamanoApr 19, 2016
  15. 00/12 fix checking out a being-rebased branchNguyễn Thái Ngọc Duy, Apr 20, 2016
  16. 01/12 path.c: add git_common_path() and strbuf_git_common_path()Nguyễn Thái Ngọc Duy, Apr 20, 2016
  17. Eric SunshineApr 20, 2016
  18. Duy NguyenApr 21, 2016
  19. 02/12 worktree.c: store "id" instead of "git_dir"Nguyễn Thái Ngọc Duy, Apr 20, 2016
  20. 03/12 worktree.c: make find_shared_symref() return struct worktree *Nguyễn Thái Ngọc Duy, Apr 20, 2016
  21. Junio C HamanoApr 20, 2016
  22. Eric SunshineApr 21, 2016
  23. 04/12 worktree.c: mark current worktreeNguyễn Thái Ngọc Duy, Apr 20, 2016
  24. Eric SunshineApr 21, 2016
  25. Duy NguyenApr 21, 2016
  26. Duy NguyenApr 21, 2016
  27. Eric SunshineApr 21, 2016
  28. Jeff KingApr 21, 2016
  29. Junio C HamanoApr 21, 2016
  30. Jeff KingApr 21, 2016
  31. Junio C HamanoApr 21, 2016
  32. 05/12 path.c: refactor and add worktree_git_path()Nguyễn Thái Ngọc Duy, Apr 20, 2016
  33. 06/12 wt-status.c: split rebase detection out of wt_status_get_state()Nguyễn Thái Ngọc Duy, Apr 20, 2016
  34. Ramsay JonesApr 20, 2016
  35. Duy NguyenApr 20, 2016
  36. 07/12 wt-status.c: make wt_status_check_rebase() work on any worktreeNguyễn Thái Ngọc Duy, Apr 20, 2016
  37. 08/12 worktree.c: avoid referencing to worktrees[i] multiple timesNguyễn Thái Ngọc Duy, Apr 20, 2016
  38. 09/12 worktree.c: test if branch being rebased in another worktreeNguyễn Thái Ngọc Duy, Apr 20, 2016
  39. Junio C HamanoApr 20, 2016
  40. Duy NguyenApr 21, 2016
  41. 10/12 wt-status.c: split bisect detection out of wt_status_get_state()Nguyễn Thái Ngọc Duy, Apr 20, 2016
  42. 11/12 worktree.c: test if branch being bisected in another worktreeNguyễn Thái Ngọc Duy, Apr 20, 2016
  43. 12/12 branch: do not rename a branch under bisect or rebaseNguyễn Thái Ngọc Duy, Apr 20, 2016
  44. Junio C HamanoApr 20, 2016
  45. 00/13 nd/worktree-various-headsNguyễn Thái Ngọc Duy, Apr 22, 2016
  46. 01/13 dir.c: rename str(n)cmp_icase to fspath(n)cmpNguyễn Thái Ngọc Duy, Apr 22, 2016
  47. 02/13 path.c: add git_common_path() and strbuf_git_common_path()Nguyễn Thái Ngọc Duy, Apr 22, 2016
  48. 03/13 worktree.c: store "id" instead of "git_dir"Nguyễn Thái Ngọc Duy, Apr 22, 2016
  49. 04/13 worktree.c: make find_shared_symref() return struct worktree *Nguyễn Thái Ngọc Duy, Apr 22, 2016
  50. 05/13 worktree.c: mark current worktreeNguyễn Thái Ngọc Duy, Apr 22, 2016
  51. Eric SunshineMay 6, 2016
  52. Duy NguyenMay 6, 2016
  53. Duy NguyenMay 10, 2016
  54. 1/7 completion: support git-worktreeNguyễn Thái Ngọc Duy, May 10, 2016
  55. 2/7 worktree.c: rewrite mark_current_worktree() to avoid strbufNguyễn Thái Ngọc Duy, May 10, 2016
  56. Eric SunshineMay 11, 2016
  57. 3/7 git-worktree.txt: keep subcommand listing in alphabetical orderNguyễn Thái Ngọc Duy, May 10, 2016
  58. 4/7 worktree.c: use is_dot_or_dotdot()Nguyễn Thái Ngọc Duy, May 10, 2016
  59. 5/7 worktree.c: add clear_worktree()Nguyễn Thái Ngọc Duy, May 10, 2016
  60. Eric SunshineMay 11, 2016
  61. Eric SunshineMay 11, 2016
  62. Duy NguyenMay 22, 2016
  63. 6/7 worktree: avoid 0{40}, too many zeroes, hard to readNguyễn Thái Ngọc Duy, May 10, 2016
  64. 7/7 worktree: simplify prefixing pathsNguyễn Thái Ngọc Duy, May 10, 2016
  65. Junio C HamanoMay 10, 2016
  66. Duy NguyenMay 11, 2016
  67. Eric SunshineMay 11, 2016
  68. 1/5 worktree.c: add find_worktree_by_path()Nguyễn Thái Ngọc Duy, May 10, 2016
  69. 2/5 worktree.c: add is_main_worktree()Nguyễn Thái Ngọc Duy, May 10, 2016
  70. Eric SunshineMay 13, 2016
  71. 3/5 worktree.c: add is_worktree_locked()Nguyễn Thái Ngọc Duy, May 10, 2016
  72. Eric SunshineMay 13, 2016
  73. Duy NguyenMay 22, 2016
  74. Eric SunshineMay 23, 2016
  75. 4/5 worktree: add "lock" commandNguyễn Thái Ngọc Duy, May 10, 2016
  76. Eric SunshineMay 16, 2016
  77. Duy NguyenMay 22, 2016
  78. Eric SunshineMay 23, 2016
  79. Eric SunshineMay 16, 2016
  80. 5/5 worktree: add "unlock" commandNguyễn Thái Ngọc Duy, May 10, 2016
  81. Eric SunshineMay 16, 2016
  82. Eric SunshineMay 13, 2016
  83. Junio C HamanoMay 10, 2016
  84. Junio C HamanoMay 10, 2016
  85. Duy NguyenMay 11, 2016
  86. 0/6 nd/worktree-cleanup-post-head-protection updateNguyễn Thái Ngọc Duy, May 22, 2016
  87. 1/6 completion: support git-worktreeNguyễn Thái Ngọc Duy, May 22, 2016
  88. 2/6 worktree.c: rewrite mark_current_worktree() to avoid strbufNguyễn Thái Ngọc Duy, May 22, 2016
  89. 3/6 git-worktree.txt: keep subcommand listing in alphabetical orderNguyễn Thái Ngọc Duy, May 22, 2016
  90. 4/6 worktree.c: use is_dot_or_dotdot()Nguyễn Thái Ngọc Duy, May 22, 2016
  91. 5/6 worktree: avoid 0{40}, too many zeroes, hard to readNguyễn Thái Ngọc Duy, May 22, 2016
  92. 6/6 worktree: simplify prefixing pathsNguyễn Thái Ngọc Duy, May 22, 2016
  93. Eric SunshineMay 22, 2016
  94. Eric SunshineMay 23, 2016
  95. Duy NguyenMay 23, 2016
  96. Eric SunshineMay 23, 2016
  97. 0/5 worktree lock/unlockNguyễn Thái Ngọc Duy, May 22, 2016
  98. 1/5 worktree.c: add find_worktree_by_path()Nguyễn Thái Ngọc Duy, May 22, 2016
  99. Eric SunshineMay 23, 2016
  100. Eric SunshineMay 23, 2016
  101. Duy NguyenMay 30, 2016
  102. 2/5 worktree.c: add is_main_worktree()Nguyễn Thái Ngọc Duy, May 22, 2016
  103. 3/5 worktree.c: add is_worktree_locked()Nguyễn Thái Ngọc Duy, May 22, 2016
  104. 4/5 worktree: add "lock" commandNguyễn Thái Ngọc Duy, May 22, 2016
  105. Eric SunshineMay 23, 2016
  106. 5/5 worktree: add "unlock" commandNguyễn Thái Ngọc Duy, May 22, 2016
  107. Eric SunshineMay 23, 2016
  108. Eric SunshineMay 23, 2016
  109. Duy NguyenMay 23, 2016
  110. 0/6 worktree lock/unlockNguyễn Thái Ngọc Duy, May 30, 2016
  111. 1/6 worktree.c: add find_worktree()Nguyễn Thái Ngọc Duy, May 30, 2016
  112. 2/6 worktree.c: find_worktree() learns to identify worktrees by basenameNguyễn Thái Ngọc Duy, May 30, 2016
  113. Junio C HamanoMay 31, 2016
  114. Duy NguyenJun 1, 2016
  115. Junio C HamanoJun 1, 2016
  116. Duy NguyenJun 2, 2016
  117. Junio C HamanoJun 2, 2016
  118. Duy NguyenJun 3, 2016
  119. Junio C HamanoJun 3, 2016
  120. Johannes SchindelinJun 5, 2016
  121. 3/6 worktree.c: add is_main_worktree()Nguyễn Thái Ngọc Duy, May 30, 2016
  122. Junio C HamanoMay 31, 2016
  123. 4/6 worktree.c: retrieve lock status (and optionally reason) in get_worktrees()Nguyễn Thái Ngọc Duy, May 30, 2016
  124. Junio C HamanoMay 31, 2016
  125. Duy NguyenJun 1, 2016
  126. Junio C HamanoJun 1, 2016
  127. 5/6 worktree: add "lock" commandNguyễn Thái Ngọc Duy, May 30, 2016
  128. Junio C HamanoMay 31, 2016
  129. Junio C HamanoMay 31, 2016
  130. 6/6 worktree: add "unlock" commandNguyễn Thái Ngọc Duy, May 30, 2016
  131. Junio C HamanoMay 31, 2016
  132. Duy NguyenJun 1, 2016
  133. Junio C HamanoMay 31, 2016
  134. Junio C HamanoMay 31, 2016
  135. 0/6 worktree lock/unlockNguyễn Thái Ngọc Duy, Jun 3, 2016
  136. 1/6 worktree.c: add find_worktree()Nguyễn Thái Ngọc Duy, Jun 3, 2016
  137. Ramsay JonesJun 3, 2016
  138. Duy NguyenJun 13, 2016
  139. 2/6 worktree.c: add is_main_worktree()Nguyễn Thái Ngọc Duy, Jun 3, 2016
  140. 3/6 worktree.c: add is_worktree_locked()Nguyễn Thái Ngọc Duy, Jun 3, 2016
  141. 4/6 worktree: add "lock" commandNguyễn Thái Ngọc Duy, Jun 3, 2016
  142. 5/6 worktree: add "unlock" commandNguyễn Thái Ngọc Duy, Jun 3, 2016
  143. 6/6 worktree.c: find_worktree() search by path suffixNguyễn Thái Ngọc Duy, Jun 3, 2016
  144. Junio C HamanoJun 4, 2016
  145. 0/6 worktree lock/unlockNguyễn Thái Ngọc Duy, Jun 13, 2016
  146. 1/6 worktree.c: add find_worktree()Nguyễn Thái Ngọc Duy, Jun 13, 2016
  147. 2/6 worktree.c: add is_main_worktree()Nguyễn Thái Ngọc Duy, Jun 13, 2016
  148. 3/6 worktree.c: add is_worktree_locked()Nguyễn Thái Ngọc Duy, Jun 13, 2016
  149. 4/6 worktree: add "lock" commandNguyễn Thái Ngọc Duy, Jun 13, 2016
  150. 5/6 worktree: add "unlock" commandNguyễn Thái Ngọc Duy, Jun 13, 2016
  151. 6/6 worktree.c: find_worktree() search by path suffixNguyễn Thái Ngọc Duy, Jun 13, 2016
  152. Torsten BögershausenJun 27, 2016
  153. fixup! worktree: add "lock" commandNguyễn Thái Ngọc Duy, Jun 30, 2016
  154. 06/13 path.c: refactor and add worktree_git_path()Nguyễn Thái Ngọc Duy, Apr 22, 2016
  155. 07/13 wt-status.c: split rebase detection out of wt_status_get_state()Nguyễn Thái Ngọc Duy, Apr 22, 2016
  156. 08/13 wt-status.c: make wt_status_check_rebase() work on any worktreeNguyễn Thái Ngọc Duy, Apr 22, 2016
  157. 09/13 worktree.c: avoid referencing to worktrees[i] multiple timesNguyễn Thái Ngọc Duy, Apr 22, 2016
  158. 10/13 worktree.c: check whether branch is rebased in another worktreeNguyễn Thái Ngọc Duy, Apr 22, 2016
  159. 11/13 wt-status.c: split bisect detection out of wt_status_get_state()Nguyễn Thái Ngọc Duy, Apr 22, 2016
  160. 12/13 worktree.c: check whether branch is bisected in another worktreeNguyễn Thái Ngọc Duy, Apr 22, 2016
  161. Eric SunshineMay 11, 2016
  162. 13/13 branch: do not rename a branch under bisect or rebaseNguyễn Thái Ngọc Duy, Apr 22, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.