git-module

fork of https://git.lin.moe/gogs/git-module

git clone git://git.lin.moe/fork/git-module.git

  1package git
  2
  3import (
  4	"bytes"
  5	"context"
  6	"io"
  7	"net/http"
  8	"strings"
  9	"sync"
 10)
 11
 12// Commit contains information of a Git commit.
 13type Commit struct {
 14	// The hash of the commit.
 15	ID Oid
 16	//  The author of the commit.
 17	Author *Signature
 18	// The committer of the commit.
 19	Committer *Signature
 20	// The full commit message.
 21	Message string
 22
 23	parents []Oid
 24	*Tree
 25
 26	submodules    Submodules
 27	submodulesMu  sync.Mutex
 28	submodulesSet bool
 29}
 30
 31// Summary returns first line of commit message.
 32func (c *Commit) Summary() string {
 33	return strings.Split(c.Message, "\n")[0]
 34}
 35
 36// ParentsCount returns number of parents of the commit. It returns 0 if this is
 37// the root commit, otherwise returns 1, 2, etc.
 38func (c *Commit) ParentsCount() int {
 39	return len(c.parents)
 40}
 41
 42// 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, ErrParentNotExist
 47	}
 48	return c.parents[n], nil
 49}
 50
 51// Parent returns the n-th parent commit (0-based) of this commit. It returns
 52// 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, err
 57	}
 58
 59	return c.repo.CatFileCommit(ctx, id.String(), opts...)
 60}
 61
 62// 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}
 66
 67// CommitsByPage returns a paginated list of commits in the state of this
 68// 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}
 72
 73// SearchCommits searches commit message with given pattern. The returned list
 74// 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}
 78
 79// 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}
 83
 84// 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}
 88
 89// 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}
 93
 94// CommitsAfter returns a list of commits after given commit ID up to this
 95// 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}
 99
100// Ancestors returns a list of ancestors of this commit in reverse chronological
101// order.
102func (c *Commit) Ancestors(ctx context.Context, opts ...LogOptions) ([]*Commit, error) {
103	if c.ParentsCount() == 0 {
104		return []*Commit{}, nil
105	}
106
107	var opt LogOptions
108	if len(opts) > 0 {
109		opt = opts[0]
110	}
111
112	opt.Skip++
113
114	return c.repo.Log(ctx, c.ID.String(), opt)
115}
116
117type limitWriter struct {
118	W io.Writer
119	N int64
120}
121
122func (w *limitWriter) Write(p []byte) (int, error) {
123	if w.N <= 0 {
124		return len(p), nil
125	}
126
127	limit := int64(len(p))
128	if limit > w.N {
129		limit = w.N
130	}
131	n, err := w.W.Write(p[:limit])
132	w.N -= int64(n)
133
134	// Prevent "short write" error
135	return len(p), err
136}
137
138func (c *Commit) isImageFile(ctx context.Context, blob *Blob, err error) (bool, error) {
139	if err != nil {
140		if err == ErrNotBlob {
141			return false, nil
142		}
143		return false, err
144	}
145
146	buf := new(bytes.Buffer)
147	buf.Grow(512)
148	stdout := &limitWriter{
149		W: buf,
150		N: int64(buf.Cap()),
151	}
152
153	err = blob.Pipe(ctx, stdout)
154	if err != nil {
155		return false, err
156	}
157
158	return strings.Contains(http.DetectContentType(buf.Bytes()), "image/"), nil
159}
160
161// 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}
166
167// IsImageFileByIndex returns true if the blob of the commit is an image by
168// 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}