gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package gitgraph
  4. import (
  5. "bytes"
  6. "context"
  7. "fmt"
  8. "strings"
  9. "time"
  10. asymkey_model "code.gitea.io/gitea/models/asymkey"
  11. "code.gitea.io/gitea/models/db"
  12. git_model "code.gitea.io/gitea/models/git"
  13. repo_model "code.gitea.io/gitea/models/repo"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/git"
  16. "code.gitea.io/gitea/modules/log"
  17. asymkey_service "code.gitea.io/gitea/services/asymkey"
  18. )
  19. // NewGraph creates a basic graph
  20. func NewGraph() *Graph {
  21. graph := &Graph{}
  22. graph.relationCommit = &Commit{
  23. Row: -1,
  24. Column: -1,
  25. }
  26. graph.Flows = map[int64]*Flow{}
  27. return graph
  28. }
  29. // Graph represents a collection of flows
  30. type Graph struct {
  31. Flows map[int64]*Flow
  32. Commits []*Commit
  33. MinRow int
  34. MinColumn int
  35. MaxRow int
  36. MaxColumn int
  37. relationCommit *Commit
  38. }
  39. // Width returns the width of the graph
  40. func (graph *Graph) Width() int {
  41. return graph.MaxColumn - graph.MinColumn + 1
  42. }
  43. // Height returns the height of the graph
  44. func (graph *Graph) Height() int {
  45. return graph.MaxRow - graph.MinRow + 1
  46. }
  47. // AddGlyph adds glyph to flows
  48. func (graph *Graph) AddGlyph(row, column int, flowID int64, color int, glyph byte) {
  49. flow, ok := graph.Flows[flowID]
  50. if !ok {
  51. flow = NewFlow(flowID, color, row, column)
  52. graph.Flows[flowID] = flow
  53. }
  54. flow.AddGlyph(row, column, glyph)
  55. if row < graph.MinRow {
  56. graph.MinRow = row
  57. }
  58. if row > graph.MaxRow {
  59. graph.MaxRow = row
  60. }
  61. if column < graph.MinColumn {
  62. graph.MinColumn = column
  63. }
  64. if column > graph.MaxColumn {
  65. graph.MaxColumn = column
  66. }
  67. }
  68. // AddCommit adds a commit at row, column on flowID with the provided data
  69. func (graph *Graph) AddCommit(row, column int, flowID int64, data []byte) error {
  70. commit, err := NewCommit(row, column, data)
  71. if err != nil {
  72. return err
  73. }
  74. commit.Flow = flowID
  75. graph.Commits = append(graph.Commits, commit)
  76. graph.Flows[flowID].Commits = append(graph.Flows[flowID].Commits, commit)
  77. return nil
  78. }
  79. // LoadAndProcessCommits will load the git.Commits for each commit in the graph,
  80. // the associate the commit with the user author, and check the commit verification
  81. // before finally retrieving the latest status
  82. func (graph *Graph) LoadAndProcessCommits(ctx context.Context, repository *repo_model.Repository, gitRepo *git.Repository) error {
  83. var err error
  84. var ok bool
  85. emails := map[string]*user_model.User{}
  86. keyMap := map[string]bool{}
  87. for _, c := range graph.Commits {
  88. if len(c.Rev) == 0 {
  89. continue
  90. }
  91. c.Commit, err = gitRepo.GetCommit(c.Rev)
  92. if err != nil {
  93. return fmt.Errorf("GetCommit: %s Error: %w", c.Rev, err)
  94. }
  95. if c.Commit.Author != nil {
  96. email := c.Commit.Author.Email
  97. if c.User, ok = emails[email]; !ok {
  98. c.User, _ = user_model.GetUserByEmail(ctx, email)
  99. emails[email] = c.User
  100. }
  101. }
  102. c.Verification = asymkey_service.ParseCommitWithSignature(ctx, c.Commit)
  103. _ = asymkey_model.CalculateTrustStatus(c.Verification, repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
  104. return repo_model.IsOwnerMemberCollaborator(ctx, repository, user.ID)
  105. }, &keyMap)
  106. statuses, err := git_model.GetLatestCommitStatus(ctx, repository.ID, c.Commit.ID.String(), db.ListOptionsAll)
  107. if err != nil {
  108. log.Error("GetLatestCommitStatus: %v", err)
  109. } else {
  110. c.Status = git_model.CalcCommitStatus(statuses)
  111. }
  112. }
  113. return nil
  114. }
  115. // NewFlow creates a new flow
  116. func NewFlow(flowID int64, color, row, column int) *Flow {
  117. return &Flow{
  118. ID: flowID,
  119. ColorNumber: color,
  120. MinRow: row,
  121. MinColumn: column,
  122. MaxRow: row,
  123. MaxColumn: column,
  124. }
  125. }
  126. // Flow represents a series of glyphs
  127. type Flow struct {
  128. ID int64
  129. ColorNumber int
  130. Glyphs []Glyph
  131. Commits []*Commit
  132. MinRow int
  133. MinColumn int
  134. MaxRow int
  135. MaxColumn int
  136. }
  137. // Color16 wraps the color numbers around mod 16
  138. func (flow *Flow) Color16() int {
  139. return flow.ColorNumber % 16
  140. }
  141. // AddGlyph adds glyph at row and column
  142. func (flow *Flow) AddGlyph(row, column int, glyph byte) {
  143. if row < flow.MinRow {
  144. flow.MinRow = row
  145. }
  146. if row > flow.MaxRow {
  147. flow.MaxRow = row
  148. }
  149. if column < flow.MinColumn {
  150. flow.MinColumn = column
  151. }
  152. if column > flow.MaxColumn {
  153. flow.MaxColumn = column
  154. }
  155. flow.Glyphs = append(flow.Glyphs, Glyph{
  156. row,
  157. column,
  158. glyph,
  159. })
  160. }
  161. // Glyph represents a coordinate and glyph
  162. type Glyph struct {
  163. Row int
  164. Column int
  165. Glyph byte
  166. }
  167. // RelationCommit represents an empty relation commit
  168. var RelationCommit = &Commit{
  169. Row: -1,
  170. }
  171. func parseGitTime(timeStr string) time.Time {
  172. t, err := time.Parse(time.RFC3339, timeStr)
  173. if err != nil {
  174. return time.Unix(0, 0)
  175. }
  176. return t
  177. }
  178. // NewCommit creates a new commit from a provided line
  179. func NewCommit(row, column int, line []byte) (*Commit, error) {
  180. data := bytes.SplitN(line, []byte("|"), 5)
  181. if len(data) < 5 {
  182. return nil, fmt.Errorf("malformed data section on line %d with commit: %s", row, string(line))
  183. }
  184. return &Commit{
  185. Row: row,
  186. Column: column,
  187. // 0 matches git log --pretty=format:%d => ref names, like the --decorate option of git-log(1)
  188. Refs: newRefsFromRefNames(data[0]),
  189. // 1 matches git log --pretty=format:%H => commit hash
  190. Rev: string(data[1]),
  191. // 2 matches git log --pretty=format:%ad => author date (format respects --date= option)
  192. Date: parseGitTime(string(data[2])),
  193. // 3 matches git log --pretty=format:%h => abbreviated commit hash
  194. ShortRev: string(data[3]),
  195. // 4 matches git log --pretty=format:%s => subject
  196. Subject: string(data[4]),
  197. }, nil
  198. }
  199. func newRefsFromRefNames(refNames []byte) []git.Reference {
  200. refBytes := bytes.Split(refNames, []byte{',', ' '})
  201. refs := make([]git.Reference, 0, len(refBytes))
  202. for _, refNameBytes := range refBytes {
  203. if len(refNameBytes) == 0 {
  204. continue
  205. }
  206. refName := string(refNameBytes)
  207. if after, ok := strings.CutPrefix(refName, "tag: "); ok {
  208. refName = after
  209. } else {
  210. refName = strings.TrimPrefix(refName, "HEAD -> ")
  211. }
  212. refs = append(refs, git.Reference{
  213. Name: refName,
  214. })
  215. }
  216. return refs
  217. }
  218. // Commit represents a commit at coordinate X, Y with the data
  219. type Commit struct {
  220. Commit *git.Commit
  221. User *user_model.User
  222. Verification *asymkey_model.CommitVerification
  223. Status *git_model.CommitStatus
  224. Flow int64
  225. Row int
  226. Column int
  227. Refs []git.Reference
  228. Rev string
  229. Date time.Time
  230. ShortRev string
  231. Subject string
  232. }
  233. // OnlyRelation returns whether this a relation only commit
  234. func (c *Commit) OnlyRelation() bool {
  235. return c.Row == -1
  236. }