Summary
Config discovery is split by direction. Commands that read a config — sync, list, status, search, and fmt's discovery path — look in the vcspull config directory and the home directory. Commands that write one — add, discover, import — look only at home, then fall back to the current directory or to ~/.vcspull.yaml. A config kept in the config directory is therefore readable but not writable: vcspull add reports success against a file that vcspull sync will never load.
Reproduction
- Keep the only config in the vcspull config directory:
$ cat ~/.config/vcspull/team.yaml
~/code/:
existing-repo: git+ssh://git@example.com/team/existing-repo.git
- Confirm the read side finds it:
$ vcspull list
• existing-repo → ~/code/existing-repo
- From a directory that is not home, add a repository with no
-f/--file:
$ vcspull add https://git.example.com/team/new-repo.git --workspace '~/code/' --yes
Found new repository to import:
+ new-repo (https://git.example.com/team/new-repo.git)
• workspace: ~/code/
↳ path: ~/code/new-repo
? Import this repository? [y/N]: y (auto-confirm)
No config specified and no default found, will create at ~/work/.vcspull.yaml
Config file ~/work/.vcspull.yaml not found. A new one will be created.
✓ Successfully added 'new-repo' (git+https://git.example.com/team/new-repo.git) to ~/work/.vcspull.yaml under '~/code/'.
- Ask the read side for what was just added:
$ vcspull list
• existing-repo → ~/code/existing-repo
Expected
vcspull add writes to the config the read commands already discover, or declines and names the ambiguity. A repository the CLI reports as added is a repository vcspull sync will act on.
Actual
add reports success against ~/work/.vcspull.yaml, a path no read command loads, and new-repo is invisible from that point on:
$ vcspull sync --all --dry-run
Plan: 1 to clone (+), 0 to update (~), 0 unchanged (✓), 0 blocked (⚠), 0 errors (✗)
~/code/
+ existing-repo ~/code/existing-repo missing
Tip: run without --dry-run to apply. Use --show-unchanged to include ✓ rows.
vcspull import is the same class of gap, with the fallback going to home rather than the current directory:
$ vcspull import gitlab --help
-f, --file FILE Config file to write to (default: ~/.vcspull.yaml)
That is what forces --file onto both halves of a scripted refresh: vcspull import --file <path> to write, then vcspull sync --file <path> to act, even though sync alone would have discovered the file had import been willing to write there.
Environment
Versions
$ vcspull --version
vcspull 1.66.0, libvcs 0.44.0
Python 3.14.6, Linux.
Evidence
The read and write paths call different discovery functions
Read paths resolve through find_config_files(include_home=True), which starts at the config directory and adds home:
if path is None:
path = get_config_dir()
if include_home is True:
config_files.extend(find_home_config_files())
Write paths call find_home_config_files() directly, which never consults get_config_dir().
Proposal
Give the write commands the same discovery the read commands already use, so that the set of files vcspull writes to is the set it reads from.
Load-bearing constraints:
- A config the read commands discover is a config the write commands can write to without
--file.
- No write silently lands in a file the read commands will not load.
- Where discovery finds more than one candidate, the command says so rather than picking;
add already has an ambiguity path for the multiple-home-config case.
- An existing
~/.vcspull.yaml-only setup keeps working with no change and no new file appearing beside it.
Acceptance:
- With the only config in the vcspull config directory,
vcspull add and vcspull import with no --file write to it, and the added entry appears in vcspull list and in a vcspull sync --all plan.
- With no config anywhere, the file the write commands create is one the read commands then discover.
- With configs in both the config directory and home, the command reports the ambiguity instead of choosing.
- A scripted import followed by a sync against a discovered config needs
--file on neither.
Not doing: changing where vcspull looks for configs on the read side, and moving or migrating anyone's existing files. #361 covers the layout question separately.
References
Summary
Config discovery is split by direction. Commands that read a config —
sync,list,status,search, andfmt's discovery path — look in the vcspull config directory and the home directory. Commands that write one —add,discover,import— look only at home, then fall back to the current directory or to~/.vcspull.yaml. A config kept in the config directory is therefore readable but not writable:vcspull addreports success against a file thatvcspull syncwill never load.Reproduction
-f/--file:Expected
vcspull addwrites to the config the read commands already discover, or declines and names the ambiguity. A repository the CLI reports as added is a repositoryvcspull syncwill act on.Actual
addreports success against~/work/.vcspull.yaml, a path no read command loads, andnew-repois invisible from that point on:vcspull importis the same class of gap, with the fallback going to home rather than the current directory:That is what forces
--fileonto both halves of a scripted refresh:vcspull import --file <path>to write, thenvcspull sync --file <path>to act, even thoughsyncalone would have discovered the file hadimportbeen willing to write there.Environment
Versions
Python 3.14.6, Linux.
Evidence
The read and write paths call different discovery functions
Read paths resolve through
find_config_files(include_home=True), which starts at the config directory and adds home:Write paths call
find_home_config_files()directly, which never consultsget_config_dir().Proposal
Give the write commands the same discovery the read commands already use, so that the set of files vcspull writes to is the set it reads from.
Load-bearing constraints:
--file.addalready has an ambiguity path for the multiple-home-config case.~/.vcspull.yaml-only setup keeps working with no change and no new file appearing beside it.Acceptance:
vcspull addandvcspull importwith no--filewrite to it, and the added entry appears invcspull listand in avcspull sync --allplan.--fileon neither.Not doing: changing where
vcspulllooks for configs on the read side, and moving or migrating anyone's existing files. #361 covers the layout question separately.References
sync.py#L1483, andfind_config_fileswith its config-directory defaultadd.py#L521,discover.py#L406,import_cmd/_common.py#L556VCSPULL_CONFIGDIRandXDG_CONFIG_HOME:util.py#L13