Module: Esp::Vcs
- Defined in:
- lib/esp/vcs.rb
Overview
A thin wrapper over the ‘git` CLI, scoped to a working-tree `root`. Backs the diff-review loop (step 20): list working-tree changes, show one file’s diff, stage approved files, discard rejected ones. We shell out rather than bind libgit2 (nothing extra to ship) — the project is already a git repo.
Every method takes an explicit ‘root` so it operates on the *user’s* mod project, never the toolchain repo, and so it’s testable against a scratch repo. Git failures raise GitError; the Operations layer maps that to a caller-facing error.
Defined Under Namespace
Class Method Summary collapse
-
.changes(root:, scope: nil) ⇒ Object
Working-tree changes under ‘scope` (a path prefix, e.g. “mods”), each a Change with status ∈ added | modified | deleted | renamed and a staged flag.
-
.discard(root:, path:) ⇒ Object
Discard a rejected change: restore a tracked file to HEAD, delete an agent-created untracked file.
-
.file_diff(root:, path:) ⇒ Object
Unified diff of one file against HEAD.
-
.run_git_init(root) ⇒ Object
Initialise a git repo at ‘root`.
-
.stage(root:, paths:) ⇒ Object
Stage approved paths (‘git add`).
- .tracked?(root, path) ⇒ Boolean
Class Method Details
.changes(root:, scope: nil) ⇒ Object
Working-tree changes under ‘scope` (a path prefix, e.g. “mods”), each a Change with status ∈ added | modified | deleted | renamed and a staged flag. Untracked files show up as `added`.
22 23 24 25 |
# File 'lib/esp/vcs.rb', line 22 def changes(root:, scope: nil) scope_args = scope.nil? || scope.empty? ? [] : ['--', scope] parse_status(capture(root, 'status', '--porcelain=v1', '-z', *scope_args)) end |
.discard(root:, path:) ⇒ Object
Discard a rejected change: restore a tracked file to HEAD, delete an agent-created untracked file. Destructive — callers confirm first.
50 51 52 53 54 55 56 |
# File 'lib/esp/vcs.rb', line 50 def discard(root:, path:) if tracked?(root, path) run(root, 'checkout', 'HEAD', '--', path) else File.delete(File.join(root, path)) end end |
.file_diff(root:, path:) ⇒ Object
Unified diff of one file against HEAD. An untracked (agent-created) file has no HEAD entry, so we diff it against the null device to render it as an all-add patch.
30 31 32 33 34 35 36 37 38 |
# File 'lib/esp/vcs.rb', line 30 def file_diff(root:, path:) if tracked?(root, path) capture(root, 'diff', 'HEAD', '--', path) else # --no-index exits 1 whenever the files differ; that's expected here, # not a failure. capture(root, 'diff', '--no-index', '--', File::NULL, path, allow_fail: true) end end |
.run_git_init(root) ⇒ Object
Initialise a git repo at ‘root`. Used by the new-project flow (step 23 slice 5) so the freshly-scaffolded project tree is a tracked working tree from minute one.
65 66 67 68 |
# File 'lib/esp/vcs.rb', line 65 def run_git_init(root) FileUtils.mkdir_p(root) run(root, 'init', '-q') end |
.stage(root:, paths:) ⇒ Object
Stage approved paths (‘git add`). The change stays in the working tree; the human commits when ready.
42 43 44 45 46 |
# File 'lib/esp/vcs.rb', line 42 def stage(root:, paths:) return if paths.empty? run(root, 'add', '--', *paths) end |