commit.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package git
  5. import (
  6. "bufio"
  7. "container/list"
  8. "fmt"
  9. "net/http"
  10. "strconv"
  11. "strings"
  12. "github.com/mcuadros/go-version"
  13. )
  14. // Commit represents a git commit.
  15. type Commit struct {
  16. Tree
  17. ID sha1 // The ID of this commit object
  18. Author *Signature
  19. Committer *Signature
  20. CommitMessage string
  21. parents []sha1 // SHA1 strings
  22. submoduleCache *objectCache
  23. }
  24. // Message returns the commit message. Same as retrieving CommitMessage directly.
  25. func (c *Commit) Message() string {
  26. return c.CommitMessage
  27. }
  28. // Summary returns first line of commit message.
  29. func (c *Commit) Summary() string {
  30. return strings.Split(c.CommitMessage, "\n")[0]
  31. }
  32. // ParentID returns oid of n-th parent (0-based index).
  33. // It returns nil if no such parent exists.
  34. func (c *Commit) ParentID(n int) (sha1, error) {
  35. if n >= len(c.parents) {
  36. return sha1{}, ErrNotExist{"", ""}
  37. }
  38. return c.parents[n], nil
  39. }
  40. // Parent returns n-th parent (0-based index) of the commit.
  41. func (c *Commit) Parent(n int) (*Commit, error) {
  42. id, err := c.ParentID(n)
  43. if err != nil {
  44. return nil, err
  45. }
  46. parent, err := c.repo.getCommit(id)
  47. if err != nil {
  48. return nil, err
  49. }
  50. return parent, nil
  51. }
  52. // ParentCount returns number of parents of the commit.
  53. // 0 if this is the root commit, otherwise 1,2, etc.
  54. func (c *Commit) ParentCount() int {
  55. return len(c.parents)
  56. }
  57. func isImageFile(data []byte) (string, bool) {
  58. contentType := http.DetectContentType(data)
  59. if strings.Index(contentType, "image/") != -1 {
  60. return contentType, true
  61. }
  62. return contentType, false
  63. }
  64. func (c *Commit) IsImageFile(name string) bool {
  65. blob, err := c.GetBlobByPath(name)
  66. if err != nil {
  67. return false
  68. }
  69. dataRc, err := blob.Data()
  70. if err != nil {
  71. return false
  72. }
  73. buf := make([]byte, 1024)
  74. n, _ := dataRc.Read(buf)
  75. buf = buf[:n]
  76. _, isImage := isImageFile(buf)
  77. return isImage
  78. }
  79. // GetCommitByPath return the commit of relative path object.
  80. func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) {
  81. return c.repo.getCommitByPathWithID(c.ID, relpath)
  82. }
  83. // AddAllChanges marks local changes to be ready for commit.
  84. func AddChanges(repoPath string, all bool, files ...string) error {
  85. cmd := NewCommand("add")
  86. if all {
  87. cmd.AddArguments("--all")
  88. }
  89. _, err := cmd.AddArguments(files...).RunInDir(repoPath)
  90. return err
  91. }
  92. type CommitChangesOptions struct {
  93. Committer *Signature
  94. Author *Signature
  95. Message string
  96. }
  97. // CommitChanges commits local changes with given committer, author and message.
  98. // If author is nil, it will be the same as committer.
  99. func CommitChanges(repoPath string, opts CommitChangesOptions) error {
  100. cmd := NewCommand()
  101. if opts.Committer != nil {
  102. cmd.AddEnvs("GIT_COMMITTER_NAME="+opts.Committer.Name, "GIT_COMMITTER_EMAIL="+opts.Committer.Email)
  103. }
  104. cmd.AddArguments("commit")
  105. if opts.Author == nil {
  106. opts.Author = opts.Committer
  107. }
  108. if opts.Author != nil {
  109. cmd.AddArguments(fmt.Sprintf("--author='%s <%s>'", opts.Author.Name, opts.Author.Email))
  110. }
  111. cmd.AddArguments("-m", opts.Message)
  112. _, err := cmd.RunInDir(repoPath)
  113. // No stderr but exit status 1 means nothing to commit.
  114. if err != nil && err.Error() == "exit status 1" {
  115. return nil
  116. }
  117. return err
  118. }
  119. func commitsCount(repoPath, revision, relpath string) (int64, error) {
  120. var cmd *Command
  121. isFallback := false
  122. if version.Compare(gitVersion, "1.8.0", "<") {
  123. isFallback = true
  124. cmd = NewCommand("log", "--pretty=format:''")
  125. } else {
  126. cmd = NewCommand("rev-list", "--count")
  127. }
  128. cmd.AddArguments(revision)
  129. if len(relpath) > 0 {
  130. cmd.AddArguments("--", relpath)
  131. }
  132. stdout, err := cmd.RunInDir(repoPath)
  133. if err != nil {
  134. return 0, err
  135. }
  136. if isFallback {
  137. return int64(strings.Count(stdout, "\n")) + 1, nil
  138. }
  139. return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
  140. }
  141. // CommitsCount returns number of total commits of until given revision.
  142. func CommitsCount(repoPath, revision string) (int64, error) {
  143. return commitsCount(repoPath, revision, "")
  144. }
  145. func (c *Commit) CommitsCount() (int64, error) {
  146. return CommitsCount(c.repo.Path, c.ID.String())
  147. }
  148. func (c *Commit) CommitsByRangeSize(page, size int) (*list.List, error) {
  149. return c.repo.CommitsByRangeSize(c.ID.String(), page, size)
  150. }
  151. func (c *Commit) CommitsByRange(page int) (*list.List, error) {
  152. return c.repo.CommitsByRange(c.ID.String(), page)
  153. }
  154. func (c *Commit) CommitsBefore() (*list.List, error) {
  155. return c.repo.getCommitsBefore(c.ID)
  156. }
  157. func (c *Commit) CommitsBeforeLimit(num int) (*list.List, error) {
  158. return c.repo.getCommitsBeforeLimit(c.ID, num)
  159. }
  160. func (c *Commit) CommitsBeforeUntil(commitID string) (*list.List, error) {
  161. endCommit, err := c.repo.GetCommit(commitID)
  162. if err != nil {
  163. return nil, err
  164. }
  165. return c.repo.CommitsBetween(c, endCommit)
  166. }
  167. func (c *Commit) SearchCommits(keyword string) (*list.List, error) {
  168. return c.repo.searchCommits(c.ID, keyword)
  169. }
  170. func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error) {
  171. return c.repo.getFilesChanged(pastCommit, c.ID.String())
  172. }
  173. func (c *Commit) GetSubModules() (*objectCache, error) {
  174. if c.submoduleCache != nil {
  175. return c.submoduleCache, nil
  176. }
  177. entry, err := c.GetTreeEntryByPath(".gitmodules")
  178. if err != nil {
  179. return nil, err
  180. }
  181. rd, err := entry.Blob().Data()
  182. if err != nil {
  183. return nil, err
  184. }
  185. scanner := bufio.NewScanner(rd)
  186. c.submoduleCache = newObjectCache()
  187. var ismodule bool
  188. var path string
  189. for scanner.Scan() {
  190. if strings.HasPrefix(scanner.Text(), "[submodule") {
  191. ismodule = true
  192. continue
  193. }
  194. if ismodule {
  195. fields := strings.Split(scanner.Text(), "=")
  196. k := strings.TrimSpace(fields[0])
  197. if k == "path" {
  198. path = strings.TrimSpace(fields[1])
  199. } else if k == "url" {
  200. c.submoduleCache.Set(path, &SubModule{path, strings.TrimSpace(fields[1])})
  201. ismodule = false
  202. }
  203. }
  204. }
  205. return c.submoduleCache, nil
  206. }
  207. func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
  208. modules, err := c.GetSubModules()
  209. if err != nil {
  210. return nil, err
  211. }
  212. module, has := modules.Get(entryname)
  213. if has {
  214. return module.(*SubModule), nil
  215. }
  216. return nil, nil
  217. }