Skip to content

fix(filter_repos): match full repo paths and normalize trailing slashes#539

Draft
tony wants to merge 3 commits intomasterfrom
sync-edgecases
Draft

fix(filter_repos): match full repo paths and normalize trailing slashes#539
tony wants to merge 3 commits intomasterfrom
sync-edgecases

Conversation

@tony
Copy link
Copy Markdown
Member

@tony tony commented Apr 5, 2026

Summary

  • Fix filter_repos() so that passing a full repo path (e.g. /home/d/study/typescript/tanstack-router, as produced by shell glob expansion) correctly matches the configured repository — previously only the parent directory was matched against the pattern, so exact repo paths never resolved.
  • Fix workspace-root patterns with a trailing slash (e.g. ~/study/typescript/) silently returning no results due to the slash surviving into the fnmatch comparison against the normalized stored path.
  • Fix tilde and $HOME patterns (e.g. ~/study/typescript/tanstack-router) not matching stored absolute paths by expanding them via the existing expand_dir() before comparison.
  • Add two regression tests for the new matching modes: filtering by exact full repo path, and filtering by workspace root with a trailing slash.

Root cause

filter_repos() compared only pathlib.Path(r["path"]).parent against the pattern — designed for wildcard workspace patterns like *github_projects*. This broke when the shell expanded a glob such as ~/study/typescript/tanstack-* into a list of absolute repo paths before handing them to vcspull: discover found those repos in config via dict-key lookup, but sync could not.

Changes

src/vcspull/config.pyfilter_repos() path block:

  • Normalize the pattern through pathlib.Path to strip trailing slashes
  • Expand ~ / $HOME via expand_dir() when present, so tilde patterns match stored absolute paths
  • OR the existing parent-dir fnmatch with a new full-path fnmatch, making exact repo paths resolve without breaking any existing wildcard behavior

Design decisions

OR semantics, not replacement: matching against both r["path"].parent (existing) and r["path"] (new) is strictly additive — it can only produce more matches. All existing wildcard tests continue to pass unchanged.

Reuse expand_dir(): already defined in the same module with expandvars + expanduser semantics; no duplication needed.

Test plan

  • test_filter_dir_exact_repo_path — full absolute repo path resolves to the correct entry
  • test_filter_dir_workspace_root_trailing_slash — workspace root with trailing slash resolves all repos under it
  • test_filter_dir — existing wildcard pattern (*github_project*) still passes
  • uv run ruff check . --fix --show-fixes — no lint errors
  • uv run ruff format . — no formatting changes
  • uv run mypy — no type errors
  • uv run py.test --reruns 0 — full suite passes

tony added 3 commits April 5, 2026 09:03
why: filter_repos() only compares r["path"].parent against the path pattern,
so full repo paths (e.g. from zsh glob expansion of tanstack-*) and workspace
roots with trailing slashes never match — discoverable via discover but not
via sync.
what:
- add test_filter_dir_exact_repo_path (xfail): full repo path like
  /home/me/myproject/github_projects/kaptan should filter to that repo
- add test_filter_dir_workspace_root_trailing_slash (xfail): workspace root
  with trailing slash should match all repos under it
why: vcspull sync ~/study/typescript/tanstack-* passes zsh-expanded absolute
paths like /home/d/study/typescript/tanstack-router; filter_repos only matched
against r["path"].parent (/home/d/study/typescript), so the full path never
matched. Tilde-prefixed paths and workspace roots with trailing slashes also
failed silently.
what:
- also match fnmatch against r["path"] (full repo path) in addition to parent
- expand ~ and $HOME in pattern before comparing against absolute stored paths
- normalize trailing slash via pathlib.Path so /foo/bar/ matches parent /foo/bar
…ing fixed

why: filter_repos now matches against both r["path"].parent and r["path"],
and normalizes trailing slashes, so the regression tests pass unconditionally.
what:
- remove @pytest.mark.xfail from test_filter_dir_exact_repo_path
- remove @pytest.mark.xfail from test_filter_dir_workspace_root_trailing_slash
- remove unused pytest import
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant