Skip to content

fix(vcs): VcsGit IsCloned validates HEAD; Clone recovers atomically#207

Open
gmoigneu wants to merge 1 commit into
mainfrom
fix/125-git-iscloned-validation
Open

fix(vcs): VcsGit IsCloned validates HEAD; Clone recovers atomically#207
gmoigneu wants to merge 1 commit into
mainfrom
fix/125-git-iscloned-validation

Conversation

@gmoigneu

@gmoigneu gmoigneu commented May 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • `VcsGit.IsCloned` now opens the repo via `gogit.PlainOpen` and verifies a resolvable HEAD instead of just checking for a `.git` entry. Broken / half-cloned trees correctly report not-cloned so the caller falls back to `Clone`.
  • `VcsGit.Clone` stages into a sibling temp dir and atomically renames into place. If the destination already exists (leftover from a previously broken clone) it is removed before the swap. Recovery is automatic — no `--repair` flag needed.
  • A crash mid-clone leaves either the previous tree intact or the new one fully installed; never a half-written hybrid.

Why

The historical `IsCloned` returned true for any directory containing a `.git` entry — including non-git folders that happened to have one, half-cloned destinations from a Ctrl-C / network-drop, and renamed-into-place dirs. `Update` then opened the broken state and surfaced a cryptic go-git error with no recovery path.

Pattern parity

This matches the staging-temp-dir + atomic rename used in #121 (skill install) and #124 (archive update). The three backends that overlay-wrote before now have consistent file-system semantics.

Test plan

  • `go build ./...`
  • `go vet ./...`
  • `go test ./...` — full suite green
  • New tests: `IsCloned_FakeDotGit`, `IsCloned_PartialClone`, `Clone_RecoversFromBrokenDest`
  • Pre-existing skill test that faked a `.git` dir now uses a real one-commit init via a small helper.

Closes #125

Two related git robustness gaps closed.

A. IsCloned no longer returns true for any directory containing a
   .git entry. It now opens the repository via gogit.PlainOpen and
   verifies a resolvable HEAD; broken or partially-cloned destinations
   report not-cloned, and the manager naturally falls back to Clone.

B. Clone now stages into a sibling temp dir and atomically renames
   into place. If the destination already exists (left behind by a
   previously broken clone) it is removed before the swap, so the
   recovery path is automatic — no --repair flag needed. A crash
   mid-clone leaves either the previous tree or the new one, never a
   half-written hybrid.

The same staging+rename pattern matches #121 (skill install) and #124
(archive update); the file system semantics are now consistent across
the three backends that overlay-wrote before.

Tests:
- TestVcsGit_IsCloned_FakeDotGit: a directory with an empty .git/
  reports false (was true before).
- TestVcsGit_IsCloned_PartialClone: a half-fetched repo with HEAD
  pointing at an unresolvable ref reports false.
- TestVcsGit_Clone_RecoversFromBrokenDest: clone over a leftover
  broken dir succeeds, removes the stale leftover file, installs the
  source content.
- TestVcsGit_IsCloned_True updated to use makeLocalRepo (which
  commits) instead of bare PlainInit (which has no HEAD and now
  correctly reports false).
- skill TestManager_Sync_PreCachedRemoteSource updated: instead of
  faking a .git directory it uses a new helper that initialises a
  real git repo with one commit.

Closes #125
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.

vcs: VcsGit.IsCloned should validate repo and recover from partial clones

1 participant