You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo_commit.go 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright 2014 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. "bytes"
  7. "container/list"
  8. "errors"
  9. "strings"
  10. "sync"
  11. "github.com/Unknwon/com"
  12. )
  13. func (repo *Repository) getCommitIdOfRef(refpath string) (string, error) {
  14. stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--verify", refpath)
  15. if err != nil {
  16. return "", errors.New(stderr)
  17. }
  18. return strings.Split(stdout, " ")[0], nil
  19. }
  20. func (repo *Repository) GetCommitIdOfBranch(branchName string) (string, error) {
  21. return repo.getCommitIdOfRef("refs/heads/" + branchName)
  22. }
  23. // get branch's last commit or a special commit by id string
  24. func (repo *Repository) GetCommitOfBranch(branchName string) (*Commit, error) {
  25. commitId, err := repo.GetCommitIdOfBranch(branchName)
  26. if err != nil {
  27. return nil, err
  28. }
  29. return repo.GetCommit(commitId)
  30. }
  31. // Parse commit information from the (uncompressed) raw
  32. // data from the commit object.
  33. // \n\n separate headers from message
  34. func parseCommitData(data []byte) (*Commit, error) {
  35. commit := new(Commit)
  36. commit.parents = make([]sha1, 0, 1)
  37. // we now have the contents of the commit object. Let's investigate...
  38. nextline := 0
  39. l:
  40. for {
  41. eol := bytes.IndexByte(data[nextline:], '\n')
  42. switch {
  43. case eol > 0:
  44. line := data[nextline : nextline+eol]
  45. spacepos := bytes.IndexByte(line, ' ')
  46. reftype := line[:spacepos]
  47. switch string(reftype) {
  48. case "tree":
  49. id, err := NewIdFromString(string(line[spacepos+1:]))
  50. if err != nil {
  51. return nil, err
  52. }
  53. commit.Tree.Id = id
  54. case "parent":
  55. // A commit can have one or more parents
  56. oid, err := NewIdFromString(string(line[spacepos+1:]))
  57. if err != nil {
  58. return nil, err
  59. }
  60. commit.parents = append(commit.parents, oid)
  61. case "author":
  62. sig, err := newSignatureFromCommitline(line[spacepos+1:])
  63. if err != nil {
  64. return nil, err
  65. }
  66. commit.Author = sig
  67. case "committer":
  68. sig, err := newSignatureFromCommitline(line[spacepos+1:])
  69. if err != nil {
  70. return nil, err
  71. }
  72. commit.Committer = sig
  73. }
  74. nextline += eol + 1
  75. case eol == 0:
  76. commit.CommitMessage = string(data[nextline+1:])
  77. break l
  78. default:
  79. break l
  80. }
  81. }
  82. return commit, nil
  83. }
  84. func (repo *Repository) getCommit(id sha1) (*Commit, error) {
  85. if repo.commitCache != nil {
  86. if c, ok := repo.commitCache[id]; ok {
  87. return c, nil
  88. }
  89. } else {
  90. repo.commitCache = make(map[sha1]*Commit, 10)
  91. }
  92. data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String())
  93. if err != nil {
  94. return nil, errors.New(string(bytErr))
  95. }
  96. commit, err := parseCommitData(data)
  97. if err != nil {
  98. return nil, err
  99. }
  100. commit.repo = repo
  101. commit.Id = id
  102. repo.commitCache[id] = commit
  103. return commit, nil
  104. }
  105. // Find the commit object in the repository.
  106. func (repo *Repository) GetCommit(commitId string) (*Commit, error) {
  107. id, err := NewIdFromString(commitId)
  108. if err != nil {
  109. return nil, err
  110. }
  111. return repo.getCommit(id)
  112. }
  113. func (repo *Repository) commitsCount(id sha1) (int, error) {
  114. stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", id.String())
  115. if err != nil {
  116. return 0, errors.New(stderr)
  117. }
  118. return com.StrTo(strings.TrimSpace(stdout)).Int()
  119. }
  120. // used only for single tree, (]
  121. func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) {
  122. l := list.New()
  123. if last == nil || last.ParentCount() == 0 {
  124. return l, nil
  125. }
  126. var err error
  127. cur := last
  128. for {
  129. if cur.Id.Equal(before.Id) {
  130. break
  131. }
  132. l.PushBack(cur)
  133. if cur.ParentCount() == 0 {
  134. break
  135. }
  136. cur, err = cur.Parent(0)
  137. if err != nil {
  138. return nil, err
  139. }
  140. }
  141. return l, nil
  142. }
  143. func (repo *Repository) commitsBefore(lock *sync.Mutex, l *list.List, parent *list.Element, id sha1, limit int) error {
  144. commit, err := repo.getCommit(id)
  145. if err != nil {
  146. return err
  147. }
  148. var e *list.Element
  149. if parent == nil {
  150. e = l.PushBack(commit)
  151. } else {
  152. var in = parent
  153. for {
  154. if in == nil {
  155. break
  156. } else if in.Value.(*Commit).Id.Equal(commit.Id) {
  157. return nil
  158. } else {
  159. if in.Next() == nil {
  160. break
  161. }
  162. if in.Value.(*Commit).Committer.When.Equal(commit.Committer.When) {
  163. break
  164. }
  165. if in.Value.(*Commit).Committer.When.After(commit.Committer.When) &&
  166. in.Next().Value.(*Commit).Committer.When.Before(commit.Committer.When) {
  167. break
  168. }
  169. }
  170. in = in.Next()
  171. }
  172. e = l.InsertAfter(commit, in)
  173. }
  174. var pr = parent
  175. if commit.ParentCount() > 1 {
  176. pr = e
  177. }
  178. for i := 0; i < commit.ParentCount(); i++ {
  179. id, err := commit.ParentId(i)
  180. if err != nil {
  181. return err
  182. }
  183. err = repo.commitsBefore(lock, l, pr, id, 0)
  184. if err != nil {
  185. return err
  186. }
  187. }
  188. return nil
  189. }
  190. func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) {
  191. l := list.New()
  192. lock := new(sync.Mutex)
  193. err := repo.commitsBefore(lock, l, nil, id, 0)
  194. return l, err
  195. }
  196. func (repo *Repository) getCommitOfRelPath(id sha1, relPath string) (*Commit, error) {
  197. stdout, _, err := com.ExecCmdDir(repo.Path, "git", "log", "-1", prettyLogFormat, id.String(), "--", relPath)
  198. if err != nil {
  199. return nil, err
  200. }
  201. id, err = NewIdFromString(string(stdout))
  202. if err != nil {
  203. return nil, err
  204. }
  205. return repo.getCommit(id)
  206. }