Skip to content

Commit 04b397c

Browse files
committed
🔧 Explicit Git root configuration option
1 parent cb37a48 commit 04b397c

4 files changed

Lines changed: 112 additions & 4 deletions

File tree

‎docs/source/components/configuration.rst‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ Configures how **Sphinx-CodeLinks** analyse source files to extract markers from
361361
get_need_id_refs = true
362362
get_oneline_needs = true
363363
get_rst = true
364+
# Optional: Explicit Git root for Bazel or deeply nested configs
365+
# git_root = "/path/to/repo"
364366
365367
[codelinks.projects.my_project.analyse.oneline_comment_style]
366368
start_sequence = "@"
@@ -420,6 +422,29 @@ Enables the extraction of marked RST text from source code comments. When enable
420422
[codelinks.projects.my_project.analyse]
421423
get_rst = false
422424
425+
.. _`git_root`:
426+
427+
git_root
428+
^^^^^^^^
429+
430+
Specifies an explicit path to the Git repository root directory. This option is particularly useful in environments where the standard Git root auto-detection fails, such as:
431+
432+
- **Bazel builds**: Where the execution path differs from the standard repository layout
433+
- **Deeply nested configurations**: Where ``conf.py`` is located in a deep subdirectory far from the repository root
434+
- **Custom build systems**: Where the working directory is different from the source repository
435+
436+
When not set, **Sphinx-CodeLinks** will automatically traverse parent directories to locate the ``.git`` folder.
437+
438+
**Type:** ``str`` (path)
439+
**Default:** Not set (auto-detection)
440+
441+
.. code-block:: toml
442+
443+
[codelinks.projects.my_project.analyse]
444+
git_root = "/absolute/path/to/repo"
445+
446+
.. note:: When ``git_root`` is explicitly set, **Sphinx-CodeLinks** will use this path directly without attempting auto-detection. Ensure the path points to a valid Git repository containing a ``.git`` directory.
447+
423448
.. _`oneline_comment_style`:
424449

425450
analyse.oneline_comment_style

‎src/sphinx_codelinks/analyse/analyse.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ def __init__(
6565
self.oneline_needs: list[OneLineNeed] = []
6666
self.marked_rst: list[MarkedRst] = []
6767
self.all_marked_content: list[NeedIdRefs | OneLineNeed | MarkedRst] = []
68-
self.git_root: Path | None = utils.locate_git_root(self.analyse_config.src_dir)
68+
# Use explicitly configured git_root if provided, otherwise auto-detect
69+
if self.analyse_config.git_root is not None:
70+
self.git_root: Path | None = self.analyse_config.git_root.resolve()
71+
else:
72+
self.git_root = utils.locate_git_root(self.analyse_config.src_dir)
6973
self.git_remote_url: str | None = (
7074
utils.get_remote_url(self.git_root) if self.git_root else None
7175
)

‎src/sphinx_codelinks/config.py‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ class AnalyseSectionConfigType(TypedDict, total=False):
305305
get_oneline_needs: bool
306306
get_rst: bool
307307
outdir: str
308+
git_root: str
308309
need_id_refs: NeedIdRefsConfigType
309310
marked_rst: MarkedRstConfigType
310311
oneline_comment_style: OneLineCommentStyleType
@@ -319,6 +320,7 @@ class SourceAnalyseConfigType(TypedDict, total=False):
319320
get_need_id_refs: bool
320321
get_oneline_needs: bool
321322
get_rst: bool
323+
git_root: Path | None
322324
need_id_refs_config: NeedIdRefsConfig
323325
marked_rst_config: MarkedRstConfig
324326
oneline_comment_style: OneLineCommentStyle
@@ -361,6 +363,12 @@ def field_names(cls) -> set[str]:
361363
get_rst: bool = field(default=False, metadata={"schema": {"type": "boolean"}})
362364
"""Whether to extract rst texts from comments"""
363365

366+
git_root: Path | None = field(
367+
default=None, metadata={"schema": {"type": ["string", "null"]}}
368+
)
369+
"""Explicit path to the Git repository root. If not set, it will be auto-detected
370+
by traversing parent directories. Useful for Bazel builds or deeply nested configs."""
371+
364372
need_id_refs_config: NeedIdRefsConfig = field(default_factory=NeedIdRefsConfig)
365373
"""Configuration for extracting need id references from comments."""
366374

@@ -765,9 +773,11 @@ def convert_analyse_config(
765773
if config_dict:
766774
for k, v in config_dict.items():
767775
if k not in {"online_comment_style", "need_id_refs", "marked_rst"}:
768-
analyse_config_dict[k] = ( # type: ignore[literal-required] # dynamical assignment
769-
Path(v) if k == "src_dic" and isinstance(v, str) else v
770-
)
776+
# Convert string paths to Path objects
777+
if k in {"src_dir", "git_root"} and isinstance(v, str):
778+
analyse_config_dict[k] = Path(v) # type: ignore[literal-required]
779+
else:
780+
analyse_config_dict[k] = v # type: ignore[literal-required] # dynamical assignment
771781

772782
# Get oneline_comment_style configuration
773783
oneline_comment_style_dict: OneLineCommentStyleType | None = config_dict.get(

‎tests/test_analyse.py‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,72 @@ def test_analyse_oneline_needs(
141141
for src_file in src_analyse.src_files:
142142
cnt_comments += len(src_file.src_comments)
143143
assert cnt_comments == result["num_comments"]
144+
145+
146+
def test_explicit_git_root_configuration(tmp_path):
147+
"""Test that explicit git_root configuration is used instead of auto-detection."""
148+
# Create a fake git repo structure in tmp_path
149+
fake_git_root = tmp_path / "fake_repo"
150+
fake_git_root.mkdir()
151+
(fake_git_root / ".git").mkdir()
152+
153+
# Create a minimal .git/config with remote URL
154+
git_config = fake_git_root / ".git" / "config"
155+
git_config.write_text(
156+
'[remote "origin"]\n url = https://github.com/test/repo.git\n'
157+
)
158+
159+
# Create HEAD file pointing to a branch ref
160+
git_head = fake_git_root / ".git" / "HEAD"
161+
git_head.write_text("ref: refs/heads/main\n")
162+
163+
# Create the refs/heads/main file with the commit hash
164+
refs_dir = fake_git_root / ".git" / "refs" / "heads"
165+
refs_dir.mkdir(parents=True)
166+
(refs_dir / "main").write_text("abc123def456\n")
167+
168+
# Create source file in a deeply nested location
169+
src_dir = tmp_path / "deeply" / "nested" / "src"
170+
src_dir.mkdir(parents=True)
171+
src_file = src_dir / "test.c"
172+
src_file.write_text("// @Test, TEST_1\nvoid test() {}\n")
173+
174+
# Configure with explicit git_root
175+
src_analyse_config = SourceAnalyseConfig(
176+
src_files=[src_file],
177+
src_dir=src_dir,
178+
get_need_id_refs=False,
179+
get_oneline_needs=True,
180+
get_rst=False,
181+
git_root=fake_git_root,
182+
)
183+
184+
src_analyse = SourceAnalyse(src_analyse_config)
185+
186+
# Verify the explicit git_root was used
187+
assert src_analyse.git_root == fake_git_root.resolve()
188+
assert src_analyse.git_remote_url == "https://github.com/test/repo.git"
189+
assert src_analyse.git_commit_rev == "abc123def456"
190+
191+
192+
def test_git_root_auto_detection_when_not_configured(tmp_path):
193+
"""Test that git_root is auto-detected when not explicitly configured."""
194+
src_dir = TEST_DIR / "data" / "dcdc"
195+
src_paths = [src_dir / "charge" / "demo_1.cpp"]
196+
197+
# Don't set git_root - it should auto-detect
198+
src_analyse_config = SourceAnalyseConfig(
199+
src_files=src_paths,
200+
src_dir=src_dir,
201+
get_need_id_refs=False,
202+
get_oneline_needs=True,
203+
get_rst=False,
204+
# git_root is not set, so auto-detection should be used
205+
)
206+
207+
src_analyse = SourceAnalyse(src_analyse_config)
208+
209+
# The test is running inside a git repo, so git_root should be detected
210+
# We just verify it's not None (since this test runs in the sphinx-codelinks repo)
211+
assert src_analyse.git_root is not None
212+
assert (src_analyse.git_root / ".git").exists()

0 commit comments

Comments
 (0)