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 (
"context"
"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, since we only need the tip. The context lets a hung
// remote be cancelled; every network-touching call takes one.
ctx := context.Background()
if _, _, err := r.OpenInMemory(
ctx, "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(ctx, "docs: add notes", &git.CommitOptions{
Author: &object.Signature{
Name: "You",
Email: "you@example.com",
When: time.Now(),
},
})
That needs three more imports: time, git "github.com/go-git/go-git/v5", and
"github.com/go-git/go-git/v5/plumbing/object".
AddAll honours .gitignore, so build artefacts stay out of the commit. Because the
repository is in memory, this commit exists only in your process, so there is nothing to
clean up.
Why the author is spelled out¶
Commit also accepts nil options, and most examples use that. It works only where git
is already configured: with nil, go-git resolves the author from user.name and
user.email in your git configuration. Where none is set, which is the normal state
inside a container or a CI runner, the commit fails with author field is required.
Naming the author explicitly makes the step work everywhere, which is why it is written that way here. This module supplies no identity of its own; see the errors reference.
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.
- Settings reference: every field you can put in that
repo.Settings{}, and what its default is. - What this module does not do: read it before you go
looking for a
Logor aMerge.