From 0c15a61c8756f3295afcc80277f7753b971f3a63 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Fri, 5 Dec 2025 04:12:11 +0000 Subject: [PATCH] types: introduce Commit type Change-Id: rkowwwsvpoqxrloxzrnvnplkpkymxzyx this is a backwards compatible structure that is capable of deserializing both go-git Commit structs and the existing types.NiceDiff.Commit struct. Signed-off-by: oppiliappan --- types/commit.go | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 types/commit.go diff --git a/types/commit.go b/types/commit.go new file mode 100644 index 00000000..d52e2066 --- /dev/null +++ b/types/commit.go @@ -0,0 +1,168 @@ +package types + +import ( + "bytes" + "encoding/json" + "fmt" + "maps" + "strings" + + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" +) + +type Commit struct { + // hash of the commit object. + Hash plumbing.Hash `json:"hash,omitempty"` + + // author is the original author of the commit. + Author object.Signature `json:"author"` + + // committer is the one performing the commit, might be different from author. + Committer object.Signature `json:"committer"` + + // message is the commit message, contains arbitrary text. + Message string `json:"message"` + + // treehash is the hash of the root tree of the commit. + Tree string `json:"tree"` + + // parents are the hashes of the parent commits of the commit. + ParentHashes []plumbing.Hash `json:"parent_hashes,omitempty"` + + // pgpsignature is the pgp signature of the commit. + PGPSignature string `json:"pgp_signature,omitempty"` + + // mergetag is the embedded tag object when a merge commit is created by + // merging a signed tag. + MergeTag string `json:"merge_tag,omitempty"` + + // changeid is a unique identifier for the change (e.g., gerrit change-id). + ChangeId string `json:"change_id,omitempty"` + + // extraheaders contains additional headers not captured by other fields. + ExtraHeaders map[string][]byte `json:"extra_headers,omitempty"` + + // deprecated: kept for backwards compatibility with old json format. + This string `json:"this,omitempty"` + + // deprecated: kept for backwards compatibility with old json format. + Parent string `json:"parent,omitempty"` +} + +// types.Commit is an unify two commit structs: +// - git.object.Commit from +// - types.NiceDiff.commit +// +// to do this in backwards compatible fashion, we define the base struct +// to use the same fields as NiceDiff.Commit, and then we also unmarshal +// the struct fields from go-git structs, this custom unmarshal makes sense +// of both representations and unifies them to have maximal data in either +// form. +func (c *Commit) UnmarshalJSON(data []byte) error { + type Alias Commit + + aux := &struct { + *object.Commit + *Alias + }{ + Alias: (*Alias)(c), + } + + if err := json.Unmarshal(data, aux); err != nil { + return err + } + + c.FromGoGitCommit(aux.Commit) + + return nil +} + +// fill in as much of Commit as possible from the given go-git commit +func (c *Commit) FromGoGitCommit(gc *object.Commit) { + if gc == nil { + return + } + + if c.Hash.IsZero() { + c.Hash = gc.Hash + } + if c.This == "" { + c.This = gc.Hash.String() + } + if isEmptySignature(c.Author) { + c.Author = gc.Author + } + if isEmptySignature(c.Committer) { + c.Committer = gc.Committer + } + if c.Message == "" { + c.Message = gc.Message + } + if c.Tree == "" { + c.Tree = gc.TreeHash.String() + } + if c.PGPSignature == "" { + c.PGPSignature = gc.PGPSignature + } + if c.MergeTag == "" { + c.MergeTag = gc.MergeTag + } + + if len(c.ParentHashes) == 0 { + c.ParentHashes = gc.ParentHashes + } + if c.Parent == "" && len(gc.ParentHashes) > 0 { + c.Parent = gc.ParentHashes[0].String() + } + + if len(c.ExtraHeaders) == 0 { + c.ExtraHeaders = make(map[string][]byte) + maps.Copy(c.ExtraHeaders, gc.ExtraHeaders) + } + + if c.ChangeId == "" { + if v, ok := gc.ExtraHeaders["change-id"]; ok { + c.ChangeId = string(v) + } + } +} + +func isEmptySignature(s object.Signature) bool { + return s.Email == "" && s.Name == "" && s.When.IsZero() +} + +// produce a verifiable payload from this commit's metadata +func (c *Commit) Payload() string { + author := bytes.NewBuffer([]byte{}) + c.Author.Encode(author) + + committer := bytes.NewBuffer([]byte{}) + c.Committer.Encode(committer) + + payload := strings.Builder{} + + fmt.Fprintf(&payload, "tree %s\n", c.Tree) + + if len(c.ParentHashes) > 0 { + for _, p := range c.ParentHashes { + fmt.Fprintf(&payload, "parent %s\n", p.String()) + } + } else { + // present for backwards compatibility + fmt.Fprintf(&payload, "parent %s\n", c.Parent) + } + + fmt.Fprintf(&payload, "author %s\n", author.String()) + fmt.Fprintf(&payload, "committer %s\n", committer.String()) + + if c.ChangeId != "" { + fmt.Fprintf(&payload, "change-id %s\n", c.ChangeId) + } else if v, ok := c.ExtraHeaders["change-id"]; ok { + fmt.Fprintf(&payload, "change-id %s\n", string(v)) + } + + fmt.Fprintf(&payload, "\n%s", c.Message) + + return payload.String() +} -- 2.43.0