Tag a release¶
CreateTag creates an annotated tag at a commit you already know, and does
not push it.
hash, err := r.CreateTag(ctx, repo.TagRequest{
Name: "v1.2.3",
Commit: merged, // a full 40-character SHA
Message: "Release v1.2.3",
Tagger: &object.Signature{
Name: "release-bot",
Email: "releases@example.com",
When: time.Now(),
},
})
Then publish it with the refspec you want:
err = r.Push(ctx, &git.PushOptions{
RemoteName: "origin",
RefSpecs: []config.RefSpec{"refs/tags/v1.2.3:refs/tags/v1.2.3"},
})
Creating and pushing are separate for the same reason CreateBranch and Push
are: combining them would hide a decision about what a failed push should do
with the local tag.
The commit is validated, not resolved¶
Commit must be a full SHA. A branch name is refused rather than looked up.
That is not fussiness. In a release workflow the caller has already established which commit to tag — typically by asking the forge which commit a merge request actually landed, because a forge can report one as merged before the commit is reachable. Resolving a name here would reopen exactly that race.
An abbreviation is refused too, and this is the one worth knowing about:
| you pass | plumbing.NewHash gives |
IsZero() |
|---|---|---|
main |
0000…0000 |
true |
0173e42 |
0173e40000000000000000000000000000000000 |
false |
An abbreviated SHA is silently zero-padded into a different, valid-looking hash
that points at nothing — and it is not zero, so the obvious guard misses it.
That is why this takes a string and checks it, rather than taking a
plumbing.Hash and trusting the caller to have built it from something real.
The tagger is required¶
Leave Tagger nil and go-git reads a name and email from the machine's git
configuration. The same release then carries a different author depending on
where it was cut, and in CI carries whatever identity the runner image happens
to have. CreateTag refuses instead.
Tags are not moved¶
Creating a tag that exists returns repo.ErrTagAlreadyExists, whatever commit
it currently points at. A published tag has been fetched by consumers, and
moving it means two different artefacts answer to one version.
There is no force option. A caller that genuinely needs to move a tag can reach
the repository through WithRepo, where it is visibly an escape hatch.
Errors¶
See errors for the full set:
ErrInvalidCommit, ErrMissingTagger, ErrMissingTagMessage,
ErrTagAlreadyExists, and ErrNoRepository when nothing is open.