Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add utility methods for executing batched tag operations #138

Merged
merged 3 commits into from
Sep 18, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions tag.go
Original file line number Diff line number Diff line change
@@ -27,6 +27,18 @@ import (
"strings"
)

// ErrMissingTagCommitRef is raised when a git tag is missing an
// associated commit hash
type ErrMissingTagCommitRef struct {
// Tag reference
Tag string
}

// Error returns a friendly formatted message of the current error
func (e ErrMissingTagCommitRef) Error() string {
return fmt.Sprintf("tag commit ref mismatch. tag: %s is missing a corresponding commit ref", e.Tag)
}

// SortKey represents a structured [field name] that can be used as a sort key
// when analysing referenced objects such as tags
//
@@ -227,6 +239,51 @@ func (c *Client) Tag(tag string, opts ...CreateTagOption) (string, error) {
return c.exec(fmt.Sprintf("git push origin '%s'", tag))
}

// TagBatch attempts to create a batch of tags against a specific point within
// a repositories history. All tags are created locally and then pushed in
// a single transaction to the remote. This behavior is enforced by explicitly
// enabling the [WithLocalOnly] option
func (c *Client) TagBatch(tags []string, opts ...CreateTagOption) (string, error) {
if len(tags) == 0 {
return "", nil
}

opts = append(opts, WithLocalOnly())
for _, tag := range tags {
c.Tag(tag, opts...)
}

return c.Push(WithRefSpecs(tags...))
}

// TagBatchAt attempts to create a batch of tags that target specific commits
// within a repositories history. Any number of pairs consisting of a tag and
// commit hash must be provided.
//
// TagBatchAt([]string{"0.1.0", "740a8b9", "0.2.0", "9e7dfbb"})
//
// All tags are created locally and then pushed in a single transaction to the
// remote. This behavior is enforced by explicitly enabling the [WithLocalOnly]
// option
func (c *Client) TagBatchAt(pairs []string, opts ...CreateTagOption) (string, error) {
if len(pairs) == 0 {
return "", nil
}

if len(pairs)%2 != 0 {
return "", ErrMissingTagCommitRef{Tag: pairs[len(pairs)-1]}
}

opts = append(opts, WithLocalOnly())
var refs []string
for i := 0; i < len(pairs); i += 2 {
c.Tag(pairs[i], append(opts, WithCommitRef(pairs[i+1]))...)
refs = append(refs, pairs[i])
}

return c.Push(WithRefSpecs(refs...))
}

// ListTagsOption provides a way for setting specific options during a list
// tags operation. Each supported option can customize the way in which the
// tags are queried and returned from the current repository (workng directory)
29 changes: 29 additions & 0 deletions tag_test.go
Original file line number Diff line number Diff line change
@@ -120,6 +120,35 @@ test: expand current test suite using golden files`
assert.Contains(t, out, "commit "+glog[1].Hash)
}

func TestTagBatch(t *testing.T) {
gittest.InitRepository(t, gittest.WithLog("fix: race condition when writing to map"))
glog := gittest.Log(t)
require.Len(t, glog, 2)

client, _ := git.NewClient()
_, err := client.TagBatch([]string{"0.1.1", "0.1.2"})

require.NoError(t, err)
assert.Contains(t, gittest.Show(t, "0.1.1"), "commit "+glog[0].Hash)
assert.Contains(t, gittest.Show(t, "0.1.2"), "commit "+glog[0].Hash)
}

func TestTagBatchAt(t *testing.T) {
log := `fix(ui): fix glitchy transitions within dashboard
feat(store): switch to using redis as a cache
ci: update to use a matrix based testing pipeline`
gittest.InitRepository(t, gittest.WithLog(log))
glog := gittest.Log(t)
require.Len(t, glog, 4)

client, _ := git.NewClient()
_, err := client.TagBatchAt([]string{"store/0.2.0", glog[1].AbbrevHash, "ui/0.3.0", glog[0].Hash})

require.NoError(t, err)
assert.Contains(t, gittest.Show(t, "ui/0.3.0"), "commit "+glog[0].Hash)
assert.Contains(t, gittest.Show(t, "store/0.2.0"), "commit "+glog[1].Hash)
}

func TestDeleteTags(t *testing.T) {
log := "(tag: 0.1.0, tag: 0.2.0) feat(ui): add new fancy button to ui"
gittest.InitRepository(t, gittest.WithLog(log))