Getting started¶
Clone a public repository into memory and read a file out of it — no disk, no credentials, no cleanup.
A first repository¶
package main
import (
"fmt"
"log"
"github.com/spf13/afero"
"gitlab.com/phpboyscout/go/repo"
)
func main() {
r, err := repo.NewRepo(repo.Settings{
Forge: repo.ForgeGitLab,
FS: afero.NewOsFs(),
})
if err != nil {
log.Fatal(err)
}
// Clone into RAM, shallow — we only need the tip.
if _, _, err := r.OpenInMemory(
"https://gitlab.com/phpboyscout/go/repo.git", "main",
repo.WithShallowClone(1),
); err != nil {
log.Fatal(err)
}
file, err := r.GetFile("go.mod")
if err != nil {
log.Fatal(err)
}
contents, err := file.Contents()
if err != nil {
log.Fatal(err)
}
fmt.Println(contents)
}
Three things to notice, because they are the shape of the whole API:
No forge client anywhere. Forge is a string naming an authentication convention,
not a GitHub or GitLab SDK. Cloning a repository does not drag a vendor client into your
dependency graph — see why git needs no forge.
No credentials for a public repository. Add a Token for a private one; set
Private: true and a missing token becomes a clear error instead of a confusing git
failure. See authenticate to a forge.
OpenInMemory binds the repository to r. Later calls — GetFile, AddAll,
Commit — need no handle. Call one before opening and you get ErrNoRepository or
ErrNoWorktree back; never a panic.
Now write something¶
The worktree is an afero.Fs, and writes to it are the worktree — no sync step:
fs, err := r.WorkFS()
if err != nil {
log.Fatal(err)
}
if err := afero.WriteFile(fs, "NOTES.md", []byte("hello\n"), 0o644); err != nil {
log.Fatal(err)
}
if err := r.AddAll(); err != nil {
log.Fatal(err)
}
hash, err := r.Commit("docs: add notes", nil)
AddAll honours .gitignore, so build artefacts stay out of the commit. Because the
repository is in memory, this commit exists only in your process — nothing to clean up.
Where next¶
- Clone and commit — the everyday write path, on disk.
- Authenticate to a forge — tokens, SSH, per-forge conventions.
- Role interfaces — why your functions should take
TreeReader, notRepoLike. - Test with the role mocks — mock a role, or use a real in-memory repository.