@@ -777,14 +777,6 @@ def plan_worktree_sync(
777777 return entries
778778
779779
780- def _prepare_worktree (project : GitSync ) -> SyncResult :
781- """Check existing native ownership before the separate worktree-add command."""
782- return project .update_repo (
783- target = SyncTarget (commit = project .get_position ().revision ),
784- policy = SyncPolicy (drift = "keep" ),
785- )
786-
787-
788780def sync_worktree (
789781 repo_path : pathlib .Path ,
790782 wt_config : WorktreeConfigDict ,
@@ -826,6 +818,7 @@ def sync_worktree(
826818 try :
827819 validate_worktree_config (wt_config )
828820 target , policy = checkout_settings (wt_config , worktree = True )
821+ assert target is not None
829822 path = _resolve_worktree_path (wt_config , workspace_root )
830823 exists = _worktree_exists (repo_path , path )
831824 if path .exists () and not exists :
@@ -835,28 +828,19 @@ def sync_worktree(
835828 repo_path , path if exists else repo_path , repo_config
836829 )
837830 if not exists :
838- guard = _prepare_worktree (project )
839- if not guard .ok :
840- entry .result = guard
841- else :
842- resolved = project .resolve_target (target )
843- entry .target_position = resolved
844- attach = resolved .follows and not wt_config .get ("detach" , False )
845- _create_worktree (
846- repo_path ,
847- path ,
848- "branch" if attach else "commit" ,
849- resolved .ref_name if attach else resolved .revision ,
850- {** wt_config , "detach" : not attach },
851- start_point = resolved .revision ,
852- )
853- project = _worktree_project (repo_path , path , repo_config )
854- if entry .result is None :
855- execution_policy = policy if exists else SyncPolicy (dirty = policy .dirty )
831+ entry .result = project .create_worktree (
832+ path ,
833+ target = target ,
834+ detach = wt_config .get ("detach" , False ),
835+ lock = wt_config .get ("lock" , False ),
836+ lock_reason = wt_config .get ("lock_reason" ),
837+ set_remotes = repo_config is not None ,
838+ )
839+ else :
856840 entry .result = project .update_repo (
857841 set_remotes = repo_config is not None ,
858842 target = target ,
859- policy = execution_policy ,
843+ policy = policy ,
860844 detach = wt_config .get ("detach" , False ),
861845 )
862846 result = entry .result
@@ -871,7 +855,11 @@ def sync_worktree(
871855 )
872856 entry .detail = entry .error
873857 else :
874- entry .target_position = project .resolve_target (target )
858+ entry .target_position = (
859+ project .resolve_target (target )
860+ if exists
861+ else _worktree_project (repo_path , path , repo_config ).get_position ()
862+ )
875863 if entry .position is not None :
876864 entry .drifted = (
877865 entry .position .revision != entry .target_position .revision
@@ -904,127 +892,6 @@ def sync_worktree(
904892 return entry
905893
906894
907- def _create_worktree (
908- repo_path : pathlib .Path ,
909- worktree_path : pathlib .Path ,
910- ref_type : str ,
911- ref_value : str ,
912- wt_config : WorktreeConfigDict ,
913- * ,
914- start_point : str | None = None ,
915- ) -> None :
916- """Create a new worktree.
917-
918- Parameters
919- ----------
920- repo_path : pathlib.Path
921- Path to the main repository.
922- worktree_path : pathlib.Path
923- Path for the new worktree.
924- ref_type : str
925- Type of reference: 'tag', 'branch', or 'commit'.
926- ref_value : str
927- The reference value.
928- wt_config : WorktreeConfigDict
929- Full worktree configuration.
930-
931- Raises
932- ------
933- subprocess.CalledProcessError
934- If the git command fails (e.g., ref not found).
935- FileNotFoundError
936- If the repository path does not exist.
937-
938- Examples
939- --------
940- This function requires a valid git repository. When called with an invalid
941- path, it raises FileNotFoundError:
942-
943- >>> import pathlib
944- >>> _create_worktree(
945- ... pathlib.Path("/nonexistent"),
946- ... pathlib.Path("/tmp/wt"),
947- ... "tag",
948- ... "v1.0.0",
949- ... {"dir": "../wt", "tag": "v1.0.0"},
950- ... ) # doctest: +ELLIPSIS
951- Traceback (most recent call last):
952- ...
953- FileNotFoundError: ...
954- """
955- cmd = ["git" , "worktree" , "add" ]
956-
957- # Determine if we should detach
958- detach = wt_config .get ("detach" )
959- if detach is None :
960- # Default: detach for tags and commits, not for branches
961- detach = ref_type in ("tag" , "commit" , "rev" )
962-
963- if detach :
964- cmd .append ("--detach" )
965-
966- # Handle locking
967- # git worktree add --lock does NOT support --reason, so when lock_reason
968- # is specified, we skip --lock here and use "git worktree lock --reason" after
969- lock_reason = wt_config .get ("lock_reason" )
970- should_lock = wt_config .get ("lock" )
971- if should_lock and not lock_reason :
972- # Lock without reason - can use --lock flag directly
973- cmd .append ("--lock" )
974-
975- if ref_type == "branch" and not detach and start_point is not None :
976- branch_exists = (
977- subprocess .run (
978- ["git" , "show-ref" , "--verify" , "--quiet" , f"refs/heads/{ ref_value } " ],
979- cwd = repo_path ,
980- capture_output = True ,
981- check = False ,
982- ).returncode
983- == 0
984- )
985- if not branch_exists :
986- cmd .extend (["-b" , ref_value ])
987- ref_value = start_point
988- cmd .append (str (worktree_path ))
989- cmd .append (ref_value )
990-
991- subprocess .run (
992- cmd ,
993- cwd = repo_path ,
994- check = True ,
995- capture_output = True ,
996- text = True ,
997- )
998-
999- # Apply lock with reason via separate command
1000- # This handles both cases:
1001- # 1. lock=True with lock_reason - lock with reason
1002- # 2. lock_reason without explicit lock=True - also locks with reason
1003- if lock_reason :
1004- lock_cmd = [
1005- "git" ,
1006- "worktree" ,
1007- "lock" ,
1008- "--reason" ,
1009- lock_reason ,
1010- str (worktree_path ),
1011- ]
1012- try :
1013- subprocess .run (
1014- lock_cmd ,
1015- cwd = repo_path ,
1016- check = True ,
1017- capture_output = True ,
1018- text = True ,
1019- )
1020- except subprocess .CalledProcessError as e :
1021- log .warning (
1022- "Worktree created at %s but lock failed: %s" ,
1023- worktree_path ,
1024- e .stderr .strip () if e .stderr else str (e ),
1025- )
1026-
1027-
1028895def _update_worktree (worktree_path : pathlib .Path , branch : str ) -> None :
1029896 """Follow a branch with native fast-forward and dirty-abort safeguards.
1030897
0 commit comments