Skip to content

Commit

Permalink
git/gogit: add tag info to commit when checking out via tag
Browse files Browse the repository at this point in the history
Signed-off-by: Sanskar Jaiswal <[email protected]>
  • Loading branch information
aryan9600 committed Aug 7, 2023
1 parent 101ecbf commit 432ebdc
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 4 deletions.
68 changes: 64 additions & 4 deletions git/gogit/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,10 @@ func (g *Client) cloneTag(ctx context.Context, url, tag string, opts repository.
Depth: depth,
RecurseSubmodules: recurseSubmodules(opts.RecurseSubmodules),
Progress: nil,
Tags: extgogit.NoTags,
CABundle: caBundle(g.authOpts),
ProxyOptions: g.proxy,
// Ask for the tag object that points to the commit to be sent as well.
Tags: extgogit.TagFollowing,
CABundle: caBundle(g.authOpts),
ProxyOptions: g.proxy,
}

repo, err := extgogit.CloneContext(ctx, g.storer, g.worktreeFS, cloneOpts)
Expand All @@ -189,8 +190,39 @@ func (g *Client) cloneTag(ctx context.Context, url, tag string, opts repository.
if err != nil {
return nil, fmt.Errorf("unable to resolve commit object for HEAD '%s': %w", head.Hash(), err)
}
commit, err := buildCommitWithRef(cc, ref)
if err != nil {
return nil, err
}

tagRef, err := repo.Tag(tag)
if err != nil {
return nil, fmt.Errorf("unable to find reference for tag '%s': %w", tag, err)
}

annotated := true
tagObj, err := repo.TagObject(tagRef.Hash())
if err != nil {
// If the tag ref exists but a tag object for the ref does not exist,
// treat the tag as a lightweight tag.
if err == plumbing.ErrObjectNotFound {
annotated = false
} else {
return nil, fmt.Errorf("unable to resolve tag object for tag '%s' with hash '%s': %w", tag, tagRef.Hash(), err)
}
}
// The commit is considered to have a parent only if the tag is annotated,
// as lightweight tags are essentially named pointers.
if annotated {
annotatedTag, err := buildTag(tagObj)
if err != nil {
return nil, err
}
commit.ReferencingTag = annotatedTag
}

g.repository = repo
return buildCommitWithRef(cc, ref)
return commit, nil
}

func (g *Client) cloneCommit(ctx context.Context, url, commit string, opts repository.CloneConfig) (*git.Commit, error) {
Expand Down Expand Up @@ -488,6 +520,34 @@ func buildSignature(s object.Signature) git.Signature {
}
}

func buildTag(t *object.Tag) (*git.AnnotatedTag, error) {
if t == nil {
return nil, fmt.Errorf("unable to contruct tag: no object")
}

encoded := &plumbing.MemoryObject{}
if err := t.EncodeWithoutSignature(encoded); err != nil {
return nil, fmt.Errorf("unable to encode tag '%s': %w", t.Name, err)
}
reader, err := encoded.Reader()
if err != nil {
return nil, fmt.Errorf("unable to encode tag '%s': %w", t.Name, err)
}
b, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("unable to read encoded tag '%s': %w", t.Name, err)
}

return &git.AnnotatedTag{
Hash: []byte(t.Hash.String()),
Name: t.Name,
Author: buildSignature(t.Tagger),
Signature: t.PGPSignature,
Encoded: b,
Message: t.Message,
}, nil
}

func buildCommitWithRef(c *object.Commit, ref plumbing.ReferenceName) (*git.Commit, error) {
if c == nil {
return nil, fmt.Errorf("unable to construct commit: no object")
Expand Down
10 changes: 10 additions & 0 deletions git/gogit/clone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,16 @@ func TestClone_cloneTag(t *testing.T) {
return
}

// Check if commit has a parent if the tag was annotated.
for _, tagInRepo := range tt.tagsInRepo {
if tagInRepo.annotated {
g.Expect(cc.ReferencingTag).ToNot(BeNil())
g.Expect(cc.ReferencingTag.Message).To(Equal(fmt.Sprintf("Annotated tag for: %s\n", tagInRepo.name)))
} else {
g.Expect(cc.ReferencingTag).To(BeNil())
}
}

// Check successful checkout results.
g.Expect(git.IsConcreteCommit(*cc)).To(Equal(tt.expectConcreteCommit))
targetTagHash := tagCommits[tt.checkoutTag]
Expand Down

0 comments on commit 432ebdc

Please sign in to comment.