1414from . import board_database , find_mpy_root
1515from .board_database import Board
1616
17+
18+ def get_git_directory (mpy_dir : Path ) -> Path | None :
19+ """
20+ Get the actual .git directory path, handling both regular repos and worktrees.
21+
22+ Args:
23+ mpy_dir: Path to the MicroPython repository root
24+
25+ Returns:
26+ Path to the actual .git directory, or None if not found
27+ """
28+ git_path = mpy_dir / ".git"
29+
30+ if not git_path .exists ():
31+ return None
32+
33+ if git_path .is_dir ():
34+ # Regular git repository
35+ return git_path
36+ elif git_path .is_file ():
37+ # Git worktree - .git file contains path to actual .git directory
38+ try :
39+ git_file_content = git_path .read_text ().strip ()
40+ if git_file_content .startswith ("gitdir: " ):
41+ gitdir_path = git_file_content [8 :] # Remove "gitdir: " prefix
42+ return Path (gitdir_path ).resolve ()
43+ except (OSError , ValueError ):
44+ pass
45+
46+ return None
47+
48+
49+ def get_main_git_directory (mpy_dir : Path ) -> Path | None :
50+ """
51+ Get the main .git directory for repos and worktrees.
52+
53+ For regular repos, returns None (no additional mount needed).
54+ For worktrees, returns the common git directory that needs to be mounted.
55+
56+ Args:
57+ mpy_dir: Path to the MicroPython repository root
58+
59+ Returns:
60+ Path to the main .git directory, or None if not needed
61+
62+ Raises:
63+ RuntimeError: If git command is not available
64+ """
65+ try :
66+ result = subprocess .run (
67+ ["git" , "rev-parse" , "--git-common-dir" ],
68+ cwd = mpy_dir ,
69+ capture_output = True ,
70+ text = True ,
71+ timeout = 5 ,
72+ )
73+ if result .returncode == 0 :
74+ git_common_dir = Path (result .stdout .strip ()).resolve ()
75+ if git_common_dir .exists () and git_common_dir .is_dir ():
76+ # If the git directory is already within mpy_dir, no mount needed
77+ if git_common_dir .is_relative_to (mpy_dir ):
78+ return None
79+ return git_common_dir
80+ except FileNotFoundError :
81+ raise RuntimeError (
82+ "Git command not found. Please install git to build with mpbuild."
83+ )
84+ except subprocess .TimeoutExpired :
85+ raise RuntimeError (
86+ "Git command timed out. Check git installation and repository state."
87+ )
88+
89+ return None
90+
91+
1792ARM_BUILD_CONTAINER = "micropython/build-micropython-arm"
1893BUILD_CONTAINERS = {
1994 "stm32" : ARM_BUILD_CONTAINER ,
@@ -107,6 +182,12 @@ def docker_build_cmd(
107182
108183 mpy_dir = str (port .directory_repo )
109184
185+ # Handle git worktrees by mounting the main .git directory
186+ git_volume_mount = ""
187+ main_git_dir = get_main_git_directory (Path (mpy_dir ))
188+ if main_git_dir :
189+ git_volume_mount = f"-v { main_git_dir } :{ main_git_dir } "
190+
110191 # Dynamically find all ttyACM and ttyUSB devices
111192 tty_devices = []
112193 for pattern in ["/dev/ttyACM*" , "/dev/ttyUSB*" ]:
@@ -125,6 +206,7 @@ def docker_build_cmd(
125206 f"{ '-it ' if docker_interactive else '' } "
126207 f"{ device_flags } " # provides access to USB and serial devices for deploy
127208 f"-v { mpy_dir } :{ mpy_dir } -w { mpy_dir } " # mount micropython dir with same path so elf/map paths match host
209+ f"{ git_volume_mount } " # mount actual .git directory for worktrees
128210 f"--user { uid } :{ gid } " # match running user id so generated files aren't owned by root
129211 f"-e HOME=/tmp " # set HOME to /tmp for container
130212 f"{ build_container } "
0 commit comments