Git for Go Teams
Go teams treat Git as the source of truth for code and for module versions.
Search across all documentation pages
Go teams treat Git as the source of truth for code and for module versions.
Tags on the default branch become semver releases that go get and module proxies consume.
That coupling shapes how you branch, review, tag, and wire CI around go.mod and go.sum.
go.sum change breaks downstream builds; clean history speeds bisect and release automation.go list -m resolved a version, or standardizing PR checks for Go repos.go mod tidy and proxy/sumdb policy. Large monorepos trade simple tagging for multi-module coordination.A Go module is a directory tree with go.mod at the root.
Consumers pin your module with a version string that usually comes from a Git tag on your repository's default branch.
The go command maps v1.2.3 tags to module versions and builds pseudo-versions from commit hashes when no tag exists.
Git therefore does double duty: it stores source and names releases.
Teams standardize on a trunk (often main) that always passes go test ./... and related lint gates.
Feature work happens on short-lived branches merged via pull requests.
Release branches are optional; many Go libraries tag directly from trunk after review.
.gitignore typically excludes binaries (/bin/, *.exe), vendor/ when not committed, IDE folders, and local go.work overrides when those are developer-specific.
go.mod and go.sum are always tracked.
For private modules, GOPRIVATE and VCS credentials must align with how CI clones and how developers authenticate.
When you push tag v1.4.0, module proxies (and direct go get) fetch that commit snapshot.
Import paths must match the module path in go.mod; tags on the wrong branch or without updated go.mod go directives can confuse consumers.
Semantic import versioning means v2+ modules live under a /v2 path suffix; Git tags still use v2.0.0 style semver.
Pull requests should show go.mod / go.sum diffs clearly because those files affect every downstream build.
CI runs on every push to validate the module graph before merge.
# Typical pre-merge checks (local or CI)
go test ./...
go vet ./...
go mod tidy && git diff --exit-code go.mod go.sumMonorepos may contain multiple go.mod files or a root go.work for local development.
Git still has one history, but each module may tag independently.
Cherry-picks and rebases interact with pseudo-versions: commit timestamps and hashes change what pseudo-version string the proxy serves.
Protected branch rules (required reviews, required status checks) replace informal "don't break main" culture with enforceable gates.
Signed tags (git tag -s) help consumers and security teams verify release integrity before a module proxy caches the version.
Retract in go.mod steers new resolves away from a bad Git tag without rewriting history.
Air-gapped or regulated environments may vendor modules; Git workflow then includes periodic go mod vendor refresh PRs with checksum review.
| Approach | Strength | Weakness | Best Fit |
|---|---|---|---|
| Trunk-based + tag on main | Fast releases, one integration line | Requires strong CI and small PRs | Libraries and most services |
| Release branch per major | Stabilization window for patches | Tag drift across branches | Long-supported enterprise products |
| Monorepo + go.work | Atomic cross-module changes | Complex tagging per module | Platform teams with shared libs |
| Fork + replace (temporary) | Unblocks emergency patches | Not publishable; easy to forget | Short-lived contrib workflows |
Observability hooks: record module version in binary builds with -ldflags from git describe or CI env vars so production logs match VCS state.
v1.2.3); lightweight tags and non-semver names are ignored or behave unexpectedly for go get.retract and a corrective tag.go.sum for reproducible, verified builds.Module consumers resolve versions from tags on your default branch.
Without semver tags, only pseudo-versions from commit hashes are available, which are harder to communicate in changelogs and support policies.
Long-lived branches diverge from trunk, increase merge conflicts in go.sum, and delay integration of security fixes.
Go teams favor days, not weeks, with CI proving the module still builds and tests clean.
go.mod and go.sum for any change that alters dependencies.
Source for packages you export, and go directive bumps when you adopt new language features.
GOPRIVATE tells the Go command to skip the public checksum database and module proxy for matching module paths.
Your Git credentials (HTTPS tokens or SSH) must allow CI and laptops to clone those private repos.
Many Go libraries tag patches from main after cherry-picking or direct fix PRs.
Release branches help when you must support multiple major lines with different patch cadences.
Pseudo-versions encode commit time and hash (for example v0.0.0-20240312120000-abcdef123456).
They let go get pin untagged commits reproducibly.
Only if you intentionally maintain lockstep releases.
Independent modules usually tag independently; document which paths each tag applies to.
Merged PRs change module content on trunk.
The next semver tag names the release consumers install; PR descriptions should note API or go.mod impact.
On personal feature branches before review, sometimes.
Never on shared branches or after a version tag consumers may already resolve.
.gitattributes for go.sum merge strategy (union), blame ignores for generated protobufs, and hooks or CI running go mod tidy before merge.
go.work enables local multi-module edits without replace commits.
Keep optional go.work out of shared branches unless the whole team uses the same workspace layout.
Generally you do not.
Build release artifacts in CI from tagged sources; store binaries in artifact storage, not the module repo.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 19, 2026