You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A config layer that wants one VCS-neutral description of a checkout (target ref, remotes, what to do when the checkout drifted from config or has uncommitted changes) cannot get the same answers from each backend. GitSync, HgSync, and SvnSync each take a different, mostly untyped set of options; only git can report where its working copy is or preserve uncommitted changes; and remotes exist only for git. This is the libvcs half of vcspull#577, and builds on the partial clone work in #553. Sized for one evening.
No backend reports the working copy's position beyond get_revision(). Nothing says which branch, bookmark, tag, or URL is checked out, or whether a named ref will move on the next update. Each VCS has its own notion: git's HEAD is "The current branch." and "The HEAD in such a state is called "detached"." (glossary at v2.55.0); Mercurial's working directory parent "Can be specified by the alias "."" and its active bookmark "will advance" on commit (glossary at 7.2.4); Subversion's BASE is "base rev of item's working copy" while HEAD is "latest in repository", and a subtree can be switched to another URL (svn.c at 1.14.5).
GitSync(remotes=...) and set_remotes() are git-only, although Mercurial's [paths] has the same shape: default is "The URL or directory to use when no source or remote is specified." and :pushurl is "The URL to use for push operations" (config help at 7.2.4).
Prompt
Give every backend the same three things a config layer needs: declared options, a position report, and dirty-state handling. Each in its own commit, with a test that fails without it.
These must hold; everything after them is open:
Each backend declares its clone and update options as a typed, introspectable set, so a caller can validate a config block against it and a test can check the two agree. An unknown option raises instead of being dropped.
Each backend can report where its working copy is, in its own terms: the checked-out ref and its kind (branch, bookmark, tag, URL), the base revision, and whether that ref follows new commits on update. That is enough for a caller to decide "drifted from config" without knowing the VCS.
Each backend can say whether the working copy has uncommitted changes and can set them aside before a destructive update, using the tool's own mechanism where one exists and a patch file where none does.
Nothing discards uncommitted work by default.
Direction, non-binding:
One options dataclass or TypedDict per backend, exported next to its Sync class: git with depth, git_filter, remotes, tls_verify; hg with its clone flags; svn with username, password, and depth in svn's sense. *Sync.__init__ takes those fields explicitly and BaseSync stops swallowing leftovers.
A small position type returned by a method on every backend: revision, ref name, ref kind, and a "follows" flag. git: HEAD plus attached or detached; Mercurial: . plus the active bookmark (.hg/bookmarks.current) or the current named branch; Subversion: svn info URL, relative URL, revision, and the switched flag.
Dirty handling per backend: git through the existing stash path (decide untracked files, Git update_repo fails when there are only untracked files #395); Mercurial shelve; Subversion a patch file from svn diff, since its shelving commands are still experimental (x-shelve).
Mercurial remotes: expose [paths] as remotes with fetch_url/push_url mapped to default and default:pushurl.
Details that cost time to rediscover:
Fetch and push targets are separate in four tools: git branch.<name>.pushRemote and remote.pushDefault (branch config at v2.55.0), Mercurial default:pushurl, jj git.push ("Unlike in Git, the remote to push to is not derived from the tracked remote bookmarks."), and got's send {} block in got.conf(5). Keep them separate in any remote model.
depth means history length in git and directory depth in Subversion (empty, files, immediates, infinity; svn calls it the "sticky ambient depth"). Do not share a field between backends.
Position is not one shape. git records a branch name; got records a branch reference plus a base commit and has no detached state ("Checking out work trees with an unknown branch is intentionally not supported.", got(1), got-worktree(5)); jj records only the "working-copy commit" @ and has "no concept of an active/current/checked-out bookmark" (bookmarks at v0.45.1); CVS records a per-directory sticky tag ("File containing the symbolic revision that was used at checkout.", cvs(5) in OpenCVS) that update -A resets. A "follows" flag covers all of them; a "current branch" string does not.
jj has no stash: "For when you would use git stashing, use jj edit <rev> for expected behavior." (FAQ at v0.45.1). Its changes are already a commit, so "preserve" is a no-op there.
The pytest plugin sets GIT_CONFIG, so a bare git config write in a test lands in the session-wide gitconfig and leaks into unrelated tests. Unset it in tests that write config.
Not doing:
jj, got, or CVS backends; the position type should leave room for them.
git glossary and branch config at v2.55.0, Mercurial glossary and config help at 7.2.4, jj bookmarks and FAQ at v0.45.1, Subversion svn.c at 1.14.5, got man pages, OpenCVS cvs(5): linked above
Summary
A config layer that wants one VCS-neutral description of a checkout (target ref, remotes, what to do when the checkout drifted from config or has uncommitted changes) cannot get the same answers from each backend.
GitSync,HgSync, andSvnSynceach take a different, mostly untyped set of options; only git can report where its working copy is or preserve uncommitted changes; and remotes exist only for git. This is the libvcs half of vcspull#577, and builds on the partial clone work in #553. Sized for one evening.Problem
GitSync.__init__takesremotes,git_shallow,tls_verify, anddepth(https://github.com/vcs-python/libvcs/blob/v0.46.0/src/libvcs/sync/git.py#L244-L254);HgSync.__init__andSvnSync.__init__take onlyurl,path, and**kwargs(https://github.com/vcs-python/libvcs/blob/v0.46.0/src/libvcs/sync/hg.py#L33-L39, https://github.com/vcs-python/libvcs/blob/v0.46.0/src/libvcs/sync/svn.py#L45-L51), with svn'susername/passworddocumented but only reachable through**kwargs.BaseSync.__init__accepts and drops unknown keyword arguments (https://github.com/vcs-python/libvcs/blob/v0.46.0/src/libvcs/sync/base.py#L157-L164), so a misspelled option is silently ignored: during the partial clone work, dropping the option that carried the filter produced a full clone with no error. A caller cannot enumerate what a backend accepts without reading its source.get_revision(). Nothing says which branch, bookmark, tag, or URL is checked out, or whether a named ref will move on the next update. Each VCS has its own notion: git'sHEADis "The current branch." and "The HEAD in such a state is called "detached"." (glossary at v2.55.0); Mercurial's working directory parent "Can be specified by the alias "."" and its active bookmark "will advance" on commit (glossary at 7.2.4); Subversion'sBASEis "base rev of item's working copy" whileHEADis "latest in repository", and a subtree can be switched to another URL (svn.cat 1.14.5).GitSync.update_repochecks for uncommitted changes and stashes them (https://github.com/vcs-python/libvcs/blob/v0.46.0/src/libvcs/sync/git.py#L616-L634), withuntracked_files="no"(see Git update_repo fails when there are only untracked files #395). A dirty Mercurial or Subversion working copy goes straight intohg pull -uorsvn update.GitSync(remotes=...)andset_remotes()are git-only, although Mercurial's[paths]has the same shape:defaultis "The URL or directory to use when no source or remote is specified." and:pushurlis "The URL to use for push operations" (config help at 7.2.4).Prompt
Give every backend the same three things a config layer needs: declared options, a position report, and dirty-state handling. Each in its own commit, with a test that fails without it.
These must hold; everything after them is open:
Direction, non-binding:
TypedDictper backend, exported next to itsSyncclass: git withdepth,git_filter,remotes,tls_verify; hg with its clone flags; svn withusername,password, anddepthin svn's sense.*Sync.__init__takes those fields explicitly andBaseSyncstops swallowing leftovers.HEADplus attached or detached; Mercurial:.plus the active bookmark (.hg/bookmarks.current) or the current named branch; Subversion:svn infoURL, relative URL, revision, and the switched flag.shelve; Subversion a patch file fromsvn diff, since its shelving commands are still experimental (x-shelve).[paths]as remotes withfetch_url/push_urlmapped todefaultanddefault:pushurl.Details that cost time to rediscover:
branch.<name>.pushRemoteandremote.pushDefault(branch config at v2.55.0), Mercurialdefault:pushurl, jjgit.push("Unlike in Git, the remote to push to is not derived from the tracked remote bookmarks."), and got'ssend {}block ingot.conf(5). Keep them separate in any remote model.depthmeans history length in git and directory depth in Subversion (empty,files,immediates,infinity; svn calls it the "sticky ambient depth"). Do not share a field between backends.got(1),got-worktree(5)); jj records only the "working-copy commit"@and has "no concept of an active/current/checked-out bookmark" (bookmarks at v0.45.1); CVS records a per-directory sticky tag ("File containing the symbolic revision that was used at checkout.",cvs(5)in OpenCVS) thatupdate -Aresets. A "follows" flag covers all of them; a "current branch" string does not.jj edit <rev>for expected behavior." (FAQ at v0.45.1). Its changes are already a commit, so "preserve" is a no-op there.GIT_CONFIG, so a baregit configwrite in a test lands in the session-wide gitconfig and leaks into unrelated tests. Unset it in tests that write config.Not doing:
GitSynccannot make partial clones (git clone --filter) #553).References
GitSynccannot make partial clones (git clone --filter) #553, partial clone support, which adds the first typed per-backend option (git_filter)svn.cat 1.14.5, got man pages, OpenCVScvs(5): linked above