Clone and commit¶
The core round trip: acquire a repository, change files, record the change, publish it.
Clone into a directory¶
r, err := repo.NewRepo(repo.Settings{
Forge: repo.ForgeGitLab,
Token: repo.StaticToken(os.Getenv("GITLAB_TOKEN")),
FS: afero.NewOsFs(),
})
if err != nil {
return err
}
gitRepo, worktree, err := r.Clone(url, "/tmp/work", repo.WithShallowClone(1))
Clone targets an explicit path. OpenLocal opens a path that may or may not already be
a repository, and OpenInMemory clones into RAM — see work in memory.
All three bind the repository and worktree to r, so later calls need no handle.
They also return the raw *git.Repository and *git.Worktree for setup-time
convenience. Use them during setup only; do not hand them to other goroutines
(why).
Start a fresh repository¶
OpenLocal init-if-absent, which means it cannot tell "opened an existing repository"
from "initialised a new one". When that distinction matters — scaffolding that must never
git init inside someone's existing checkout — use the init-only pair:
found, err := repo.DiscoverRepository(target) // read-only upward probe
if err != nil {
return err
}
if !found {
_, _, err = r.InitLocal(target, "main") // ErrAlreadyRepository if it is one
}
DiscoverRepository walks upward using git's own discovery semantics, so a
subdirectory of a repository reports true. InitLocal defaults to branch main when
you pass "".
Change files¶
Write through the worktree filesystem — files written there are the worktree, with no sync step:
fs, err := r.WorkFS()
if err != nil {
return err
}
if err := afero.WriteFile(fs, "README.md", body, 0o644); err != nil {
return err
}
See read and write the worktree for the live-view-versus-copy distinction.
Stage and commit¶
AddAll honours .gitignore; Add(\".\") does not
AddAll wraps go-git's AddWithOptions{All: true}, which applies the repository's
ignore patterns — so build artefacts stay out of the commit. A plain Add(".") would
stage them. The .gitignore file itself is staged.
Commit accepts nil options; pass *git.CommitOptions to set the author, amend, or
sign. It returns the commit hash.
Branch and push¶
if err := r.CreateBranch("feature/x"); err != nil {
return err
}
if err := r.Push(nil); err != nil {
return err
}
Push(nil) uses the credentials resolved at construction. Pass *git.PushOptions to
override; when opts.Auth or opts.Progress is nil the configured value is filled in for
you, so you can set other fields without re-supplying credentials or losing the server's
output.
Set Settings.Progress before you push
Push is where the server talks back. Pre-receive hook rejection reasons and the
"To create a merge request, visit …" URL arrive on that sideband channel and
nowhere else — with Progress nil, a hook-rejected push gives you an error with the
explanation stripped out. See
diagnostics.
To move between existing refs, use Checkout(plumbing.NewBranchReferenceName("main")) or
CheckoutCommit(hash).
Order matters, and mis-ordering is an error not a panic¶
Every method has a defined unopened state. Call one before Clone/Open*/InitLocal
and you get a sentinel back:
Worktree operations return ErrNoWorktree; repository operations return
ErrNoRepository. InitLocal on an existing repository returns ErrAlreadyRepository.