Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1bc989b
vcspull(feat[worktree]) Add git worktree support
tony Jan 31, 2026
86f13a4
cli(fix[search,status]) Remove unused --include-worktrees flags
tony Jan 31, 2026
d9ab710
worktree_sync(fix[list_existing_worktrees]) Resolve paths for consist…
tony Jan 31, 2026
5839b72
worktree_sync(docs) Add doctests to all functions
tony Jan 31, 2026
4ef236b
tests(test_worktree) Add tests for CLI handlers and edge cases
tony Jan 31, 2026
1473ab8
tests(test_worktree) Add targeted tests for remaining coverage gaps
tony Jan 31, 2026
69dea4f
tests(test_worktree) Add parameterized tests for worktree options and…
tony Jan 31, 2026
e5cf2af
tests(test_worktree) Add tests for remaining coverage gaps (Phase 3)
tony Jan 31, 2026
e03b96e
cli/discover(fix[is_git_worktree]) Distinguish worktrees from submodules
tony Jan 31, 2026
8c3e233
cli/sync(fix[sync]) Honor dry_run flag for worktree sync
tony Jan 31, 2026
4d180d5
cli/discover(fix[discover_repos]) Fix .git detection asymmetry
tony Jan 31, 2026
413ccb9
worktree_sync(docs[_create_worktree,_update_worktree]) Fix non-functi…
tony Jan 31, 2026
32bd5af
cli/list(docs[list_repos]) Add missing include_worktrees parameter docs
tony Jan 31, 2026
af75c82
cli/worktree(docs) Add NumPy-style docstrings to CLI handlers
tony Jan 31, 2026
7d8c243
config(docs[_validate_worktrees_config]) Add required doctests
tony Jan 31, 2026
820210a
worktree_sync(refactor) Use namespace import for dataclasses
tony Jan 31, 2026
bbb6dac
cli/discover(fix[discover_repos]) Detect worktrees with .git files
tony Jan 31, 2026
4f45ca0
worktree_sync(docs[_create_worktree,_update_worktree]) Add required d…
tony Jan 31, 2026
729e4cf
.tool-versions(uv) uv 0.9.26 -> 0.9.28
tony Jan 31, 2026
53006e1
config_reader(test) Add regression test for nested list mapping bug
tony Feb 1, 2026
7d0f7f4
config_reader(fix[_DuplicateTrackingSafeLoader]) Fix false duplicates…
tony Feb 1, 2026
c80ba3b
config_reader(test) Remove xfail now that fix is applied
tony Feb 1, 2026
8640fba
worktree_sync(fix[_create_worktree]) Handle lock_reason via separate …
tony Feb 1, 2026
75108eb
worktree_sync(fix[_update_worktree]) Verify branch before pulling
tony Feb 1, 2026
7225fa0
worktree_sync(fix[_get_worktree_head]) Add debug log for silent excep…
tony Feb 1, 2026
7b906d8
types(docs[WorktreeConfigDict]) Fix lock_reason docstring accuracy
tony Feb 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
just 1.46.0
uv 0.9.26
uv 0.9.28
python 3.14 3.13.11 3.12.12 3.11.14 3.10.19
21 changes: 16 additions & 5 deletions src/vcspull/_internal/config_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,20 @@ def dump(self, fmt: FormatLiteral, indent: int = 2, **kwargs: t.Any) -> str:


class _DuplicateTrackingSafeLoader(yaml.SafeLoader):
"""SafeLoader that records duplicate top-level keys."""
"""SafeLoader that records duplicate top-level keys.

Notes
-----
PyYAML constructs sequence (list) items AFTER exiting parent mappings,
so simple depth-counting doesn't work for nested structures. Instead,
we explicitly track the root mapping node and only consider keys as
top-level if they're direct children of that root node.
"""

def __init__(self, stream: str) -> None:
super().__init__(stream)
self.top_level_key_values: dict[t.Any, list[t.Any]] = {}
self._mapping_depth = 0
self._root_mapping_node: yaml.nodes.MappingNode | None = None
self.top_level_items: list[tuple[t.Any, t.Any]] = []


Expand All @@ -232,7 +240,10 @@ def _duplicate_tracking_construct_mapping(
node: yaml.nodes.MappingNode,
deep: bool = False,
) -> dict[t.Any, t.Any]:
loader._mapping_depth += 1
# First mapping encountered is the root - remember it
if loader._root_mapping_node is None:
loader._root_mapping_node = node

loader.flatten_mapping(node)
mapping: dict[t.Any, t.Any] = {}

Expand All @@ -244,14 +255,14 @@ def _duplicate_tracking_construct_mapping(
key = construct(key_node)
value = construct(value_node)

if loader._mapping_depth == 1:
# Only track keys that are direct children of the root mapping
if node is loader._root_mapping_node:
duplicated_value = copy.deepcopy(value)
loader.top_level_key_values.setdefault(key, []).append(duplicated_value)
loader.top_level_items.append((copy.deepcopy(key), duplicated_value))

mapping[key] = value

loader._mapping_depth -= 1
return mapping


Expand Down
Loading