git.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 models
  5. import (
  6. "container/list"
  7. "fmt"
  8. "path"
  9. "strings"
  10. "github.com/Unknwon/com"
  11. "github.com/gogits/git"
  12. )
  13. // RepoFile represents a file object in git repository.
  14. type RepoFile struct {
  15. *git.TreeEntry
  16. Path string
  17. Size int64
  18. Repo *git.Repository
  19. Commit *git.Commit
  20. }
  21. // LookupBlob returns the content of an object.
  22. func (file *RepoFile) LookupBlob() (*git.Blob, error) {
  23. if file.Repo == nil {
  24. return nil, ErrRepoFileNotLoaded
  25. }
  26. return file.Repo.LookupBlob(file.Id)
  27. }
  28. // GetBranches returns all branches of given repository.
  29. func GetBranches(userName, reposName string) ([]string, error) {
  30. repo, err := git.OpenRepository(RepoPath(userName, reposName))
  31. if err != nil {
  32. return nil, err
  33. }
  34. refs, err := repo.AllReferences()
  35. if err != nil {
  36. return nil, err
  37. }
  38. brs := make([]string, len(refs))
  39. for i, ref := range refs {
  40. brs[i] = ref.Name
  41. }
  42. return brs, nil
  43. }
  44. func GetTargetFile(userName, reposName, branchName, commitId, rpath string) (*RepoFile, error) {
  45. repo, err := git.OpenRepository(RepoPath(userName, reposName))
  46. if err != nil {
  47. return nil, err
  48. }
  49. commit, err := repo.GetCommit(branchName, commitId)
  50. if err != nil {
  51. return nil, err
  52. }
  53. parts := strings.Split(path.Clean(rpath), "/")
  54. var entry *git.TreeEntry
  55. tree := commit.Tree
  56. for i, part := range parts {
  57. if i == len(parts)-1 {
  58. entry = tree.EntryByName(part)
  59. if entry == nil {
  60. return nil, ErrRepoFileNotExist
  61. }
  62. } else {
  63. tree, err = repo.SubTree(tree, part)
  64. if err != nil {
  65. return nil, err
  66. }
  67. }
  68. }
  69. size, err := repo.ObjectSize(entry.Id)
  70. if err != nil {
  71. return nil, err
  72. }
  73. repoFile := &RepoFile{
  74. entry,
  75. rpath,
  76. size,
  77. repo,
  78. commit,
  79. }
  80. return repoFile, nil
  81. }
  82. // GetReposFiles returns a list of file object in given directory of repository.
  83. func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]*RepoFile, error) {
  84. repo, err := git.OpenRepository(RepoPath(userName, reposName))
  85. if err != nil {
  86. return nil, err
  87. }
  88. commit, err := repo.GetCommit(branchName, commitId)
  89. if err != nil {
  90. return nil, err
  91. }
  92. var repodirs []*RepoFile
  93. var repofiles []*RepoFile
  94. commit.Tree.Walk(func(dirname string, entry *git.TreeEntry) int {
  95. if dirname == rpath {
  96. // TODO: size get method shoule be improved
  97. size, err := repo.ObjectSize(entry.Id)
  98. if err != nil {
  99. return 0
  100. }
  101. var cm = commit
  102. var i int
  103. for {
  104. i = i + 1
  105. //fmt.Println(".....", i, cm.Id(), cm.ParentCount())
  106. if cm.ParentCount() == 0 {
  107. break
  108. } else if cm.ParentCount() == 1 {
  109. pt, _ := repo.SubTree(cm.Parent(0).Tree, dirname)
  110. if pt == nil {
  111. break
  112. }
  113. pEntry := pt.EntryByName(entry.Name)
  114. if pEntry == nil || !pEntry.Id.Equal(entry.Id) {
  115. break
  116. } else {
  117. cm = cm.Parent(0)
  118. }
  119. } else {
  120. var emptyCnt = 0
  121. var sameIdcnt = 0
  122. var lastSameCm *git.Commit
  123. //fmt.Println(".....", cm.ParentCount())
  124. for i := 0; i < cm.ParentCount(); i++ {
  125. //fmt.Println("parent", i, cm.Parent(i).Id())
  126. p := cm.Parent(i)
  127. pt, _ := repo.SubTree(p.Tree, dirname)
  128. var pEntry *git.TreeEntry
  129. if pt != nil {
  130. pEntry = pt.EntryByName(entry.Name)
  131. }
  132. //fmt.Println("pEntry", pEntry)
  133. if pEntry == nil {
  134. emptyCnt = emptyCnt + 1
  135. if emptyCnt+sameIdcnt == cm.ParentCount() {
  136. if lastSameCm == nil {
  137. goto loop
  138. } else {
  139. cm = lastSameCm
  140. break
  141. }
  142. }
  143. } else {
  144. //fmt.Println(i, "pEntry", pEntry.Id, "entry", entry.Id)
  145. if !pEntry.Id.Equal(entry.Id) {
  146. goto loop
  147. } else {
  148. lastSameCm = cm.Parent(i)
  149. sameIdcnt = sameIdcnt + 1
  150. if emptyCnt+sameIdcnt == cm.ParentCount() {
  151. // TODO: now follow the first parent commit?
  152. cm = lastSameCm
  153. //fmt.Println("sameId...")
  154. break
  155. }
  156. }
  157. }
  158. }
  159. }
  160. }
  161. loop:
  162. rp := &RepoFile{
  163. entry,
  164. path.Join(dirname, entry.Name),
  165. size,
  166. repo,
  167. cm,
  168. }
  169. if entry.IsFile() {
  170. repofiles = append(repofiles, rp)
  171. } else if entry.IsDir() {
  172. repodirs = append(repodirs, rp)
  173. }
  174. }
  175. return 0
  176. })
  177. return append(repodirs, repofiles...), nil
  178. }
  179. func GetCommit(userName, repoName, branchname, commitid string) (*git.Commit, error) {
  180. repo, err := git.OpenRepository(RepoPath(userName, repoName))
  181. if err != nil {
  182. return nil, err
  183. }
  184. return repo.GetCommit(branchname, commitid)
  185. }
  186. // GetCommits returns all commits of given branch of repository.
  187. func GetCommits(userName, reposName, branchname string) (*list.List, error) {
  188. repo, err := git.OpenRepository(RepoPath(userName, reposName))
  189. if err != nil {
  190. return nil, err
  191. }
  192. r, err := repo.LookupReference(fmt.Sprintf("refs/heads/%s", branchname))
  193. if err != nil {
  194. return nil, err
  195. }
  196. return r.AllCommits()
  197. }
  198. type DiffFile struct {
  199. Name string
  200. Addition, Deletion int
  201. Type string
  202. Content []string
  203. }
  204. type Diff struct {
  205. NumFiles int // Number of file has been changed.
  206. TotalAddition, TotalDeletion int
  207. Files []*DiffFile
  208. }
  209. func GetDiff(repoPath, commitid string) (*Diff, error) {
  210. stdout, _, err := com.ExecCmdDir(repoPath, "git", "show", commitid)
  211. if err != nil {
  212. return nil, err
  213. }
  214. // Sperate parts by file.
  215. startIndex := strings.Index(stdout, "diff --git ") + 12
  216. // First part is commit information.
  217. // Check if it's a merge.
  218. mergeIndex := strings.Index(stdout[:startIndex], "merge")
  219. if mergeIndex > -1 {
  220. mergeCommit := strings.SplitN(strings.Split(stdout[:startIndex], "\n")[1], "", 3)[2]
  221. return GetDiff(repoPath, mergeCommit)
  222. }
  223. parts := strings.Split(stdout[startIndex:], "diff --git ")
  224. diff := &Diff{NumFiles: len(parts)}
  225. diff.Files = make([]*DiffFile, 0, diff.NumFiles)
  226. for _, part := range parts {
  227. infos := strings.SplitN(part, "\n", 6)
  228. maxIndex := len(infos) - 1
  229. infos[maxIndex] = strings.TrimSuffix(strings.TrimSuffix(infos[maxIndex], "\n"), "\n\\ No newline at end of file")
  230. file := &DiffFile{
  231. Name: strings.TrimPrefix(strings.Split(infos[0], " ")[0], "a/"),
  232. Content: strings.Split(infos[maxIndex], "\n"),
  233. }
  234. diff.Files = append(diff.Files, file)
  235. }
  236. return diff, nil
  237. }