Skip to content

No shared working-copy contract across GitSync, HgSync, SvnSync #554

Description

@tony

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, 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.

Problem

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:

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions