1package git23import (4 "bytes"5 "context"6 "io"7 "net/http"8 "strings"9 "sync"10)1112// Commit contains information of a Git commit.13type Commit struct {14 // The hash of the commit.15 ID Oid16 // The author of the commit.17 Author *Signature18 // The committer of the commit.19 Committer *Signature20 // The full commit message.21 Message string2223 parents []Oid24 *Tree2526 submodules Submodules27 submodulesMu sync.Mutex28 submodulesSet bool29}3031// Summary returns first line of commit message.32func (c *Commit) Summary() string {33 return strings.Split(c.Message, "\n")[0]34}3536// ParentsCount returns number of parents of the commit. It returns 0 if this is37// the root commit, otherwise returns 1, 2, etc.38func (c *Commit) ParentsCount() int {39 return len(c.parents)40}4142// ParentID returns the Oid of the n-th parent (0-based) of this commit.43// It returns an ErrParentNotExist if no such parent exists.44func (c *Commit) ParentID(n int) (Oid, error) {45 if n >= len(c.parents) {46 return nil, ErrParentNotExist47 }48 return c.parents[n], nil49}5051// Parent returns the n-th parent commit (0-based) of this commit. It returns52// ErrRevisionNotExist if no such parent exists.53func (c *Commit) Parent(ctx context.Context, n int, opts ...CatFileCommitOptions) (*Commit, error) {54 id, err := c.ParentID(n)55 if err != nil {56 return nil, err57 }5859 return c.repo.CatFileCommit(ctx, id.String(), opts...)60}6162// CommitByPath returns the commit of the path in the state of this commit.63func (c *Commit) CommitByPath(ctx context.Context, opts ...CommitByRevisionOptions) (*Commit, error) {64 return c.repo.CommitByRevision(ctx, c.ID.String(), opts...)65}6667// CommitsByPage returns a paginated list of commits in the state of this68// commit. The returned list is in reverse chronological order.69func (c *Commit) CommitsByPage(ctx context.Context, page, size int, opts ...CommitsByPageOptions) ([]*Commit, error) {70 return c.repo.CommitsByPage(ctx, c.ID.String(), page, size, opts...)71}7273// SearchCommits searches commit message with given pattern. The returned list74// is in reverse chronological order.75func (c *Commit) SearchCommits(ctx context.Context, pattern string, opts ...SearchCommitsOptions) ([]*Commit, error) {76 return c.repo.SearchCommits(ctx, c.ID.String(), pattern, opts...)77}7879// ShowNameStatus returns name status of the commit.80func (c *Commit) ShowNameStatus(ctx context.Context, opts ...ShowNameStatusOptions) (*NameStatus, error) {81 return c.repo.ShowNameStatus(ctx, c.ID.String(), opts...)82}8384// CommitsCount returns number of total commits up to this commit.85func (c *Commit) CommitsCount(ctx context.Context, opts ...RevListCountOptions) (int64, error) {86 return c.repo.RevListCount(ctx, []string{c.ID.String()}, opts...)87}8889// FilesChangedAfter returns a list of files changed after given commit ID.90func (c *Commit) FilesChangedAfter(ctx context.Context, after string, opts ...DiffNameOnlyOptions) ([]string, error) {91 return c.repo.DiffNameOnly(ctx, after, c.ID.String(), opts...)92}9394// CommitsAfter returns a list of commits after given commit ID up to this95// commit. The returned list is in reverse chronological order.96func (c *Commit) CommitsAfter(ctx context.Context, after string, opts ...RevListOptions) ([]*Commit, error) {97 return c.repo.RevList(ctx, []string{after + "..." + c.ID.String()}, opts...)98}99100// Ancestors returns a list of ancestors of this commit in reverse chronological101// order.102func (c *Commit) Ancestors(ctx context.Context, opts ...LogOptions) ([]*Commit, error) {103 if c.ParentsCount() == 0 {104 return []*Commit{}, nil105 }106107 var opt LogOptions108 if len(opts) > 0 {109 opt = opts[0]110 }111112 opt.Skip++113114 return c.repo.Log(ctx, c.ID.String(), opt)115}116117type limitWriter struct {118 W io.Writer119 N int64120}121122func (w *limitWriter) Write(p []byte) (int, error) {123 if w.N <= 0 {124 return len(p), nil125 }126127 limit := int64(len(p))128 if limit > w.N {129 limit = w.N130 }131 n, err := w.W.Write(p[:limit])132 w.N -= int64(n)133134 // Prevent "short write" error135 return len(p), err136}137138func (c *Commit) isImageFile(ctx context.Context, blob *Blob, err error) (bool, error) {139 if err != nil {140 if err == ErrNotBlob {141 return false, nil142 }143 return false, err144 }145146 buf := new(bytes.Buffer)147 buf.Grow(512)148 stdout := &limitWriter{149 W: buf,150 N: int64(buf.Cap()),151 }152153 err = blob.Pipe(ctx, stdout)154 if err != nil {155 return false, err156 }157158 return strings.Contains(http.DetectContentType(buf.Bytes()), "image/"), nil159}160161// IsImageFile returns true if the blob of the commit is an image by subpath.162func (c *Commit) IsImageFile(ctx context.Context, subpath string) (bool, error) {163 blob, err := c.Blob(ctx, subpath)164 return c.isImageFile(ctx, blob, err)165}166167// IsImageFileByIndex returns true if the blob of the commit is an image by168// index.169func (c *Commit) IsImageFileByIndex(ctx context.Context, index string) (bool, error) {170 blob, err := c.BlobByIndex(ctx, index)171 return c.isImageFile(ctx, blob, err)172}