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.

commit.go 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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 repo
  5. import (
  6. "container/list"
  7. "path"
  8. "strings"
  9. "code.gitea.io/git"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/base"
  12. "code.gitea.io/gitea/modules/context"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/setting"
  15. "github.com/Unknwon/paginater"
  16. )
  17. const (
  18. tplCommits base.TplName = "repo/commits"
  19. tplGraph base.TplName = "repo/graph"
  20. tplDiff base.TplName = "repo/diff/page"
  21. )
  22. // RefCommits render commits page
  23. func RefCommits(ctx *context.Context) {
  24. switch {
  25. case len(ctx.Repo.TreePath) == 0:
  26. Commits(ctx)
  27. case ctx.Repo.TreePath == "search":
  28. SearchCommits(ctx)
  29. default:
  30. FileHistory(ctx)
  31. }
  32. }
  33. func renderIssueLinks(oldCommits *list.List, repoLink string) *list.List {
  34. newCommits := list.New()
  35. for e := oldCommits.Front(); e != nil; e = e.Next() {
  36. c := e.Value.(*git.Commit)
  37. newCommits.PushBack(c)
  38. }
  39. return newCommits
  40. }
  41. // Commits render branch's commits
  42. func Commits(ctx *context.Context) {
  43. ctx.Data["PageIsCommits"] = true
  44. if ctx.Repo.Commit == nil {
  45. ctx.Handle(404, "Commit not found", nil)
  46. return
  47. }
  48. ctx.Data["PageIsViewCode"] = true
  49. commitsCount, err := ctx.Repo.GetCommitsCount()
  50. if err != nil {
  51. ctx.Handle(500, "GetCommitsCount", err)
  52. return
  53. }
  54. page := ctx.QueryInt("page")
  55. if page <= 1 {
  56. page = 1
  57. }
  58. ctx.Data["Page"] = paginater.New(int(commitsCount), git.CommitsRangeSize, page, 5)
  59. // Both `git log branchName` and `git log commitId` work.
  60. commits, err := ctx.Repo.Commit.CommitsByRange(page)
  61. if err != nil {
  62. ctx.Handle(500, "CommitsByRange", err)
  63. return
  64. }
  65. commits = renderIssueLinks(commits, ctx.Repo.RepoLink)
  66. commits = models.ValidateCommitsWithEmails(commits)
  67. commits = models.ParseCommitsWithSignature(commits)
  68. commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
  69. ctx.Data["Commits"] = commits
  70. ctx.Data["Username"] = ctx.Repo.Owner.Name
  71. ctx.Data["Reponame"] = ctx.Repo.Repository.Name
  72. ctx.Data["CommitCount"] = commitsCount
  73. ctx.Data["Branch"] = ctx.Repo.BranchName
  74. ctx.HTML(200, tplCommits)
  75. }
  76. // Graph render commit graph - show commits from all branches.
  77. func Graph(ctx *context.Context) {
  78. ctx.Data["PageIsCommits"] = true
  79. ctx.Data["PageIsViewCode"] = true
  80. commitsCount, err := ctx.Repo.GetCommitsCount()
  81. if err != nil {
  82. ctx.Handle(500, "GetCommitsCount", err)
  83. return
  84. }
  85. graph, err := models.GetCommitGraph(ctx.Repo.GitRepo)
  86. if err != nil {
  87. ctx.Handle(500, "GetCommitGraph", err)
  88. return
  89. }
  90. ctx.Data["Graph"] = graph
  91. ctx.Data["Username"] = ctx.Repo.Owner.Name
  92. ctx.Data["Reponame"] = ctx.Repo.Repository.Name
  93. ctx.Data["CommitCount"] = commitsCount
  94. ctx.Data["Branch"] = ctx.Repo.BranchName
  95. ctx.Data["RequireGitGraph"] = true
  96. ctx.HTML(200, tplGraph)
  97. }
  98. // SearchCommits render commits filtered by keyword
  99. func SearchCommits(ctx *context.Context) {
  100. ctx.Data["PageIsCommits"] = true
  101. ctx.Data["PageIsViewCode"] = true
  102. keyword := strings.Trim(ctx.Query("q"), " ")
  103. if len(keyword) == 0 {
  104. ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchNameSubURL())
  105. return
  106. }
  107. all := ctx.QueryBool("all")
  108. commits, err := ctx.Repo.Commit.SearchCommits(keyword, all)
  109. if err != nil {
  110. ctx.Handle(500, "SearchCommits", err)
  111. return
  112. }
  113. commits = renderIssueLinks(commits, ctx.Repo.RepoLink)
  114. commits = models.ValidateCommitsWithEmails(commits)
  115. commits = models.ParseCommitsWithSignature(commits)
  116. commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
  117. ctx.Data["Commits"] = commits
  118. ctx.Data["Keyword"] = keyword
  119. if all {
  120. ctx.Data["All"] = "checked"
  121. }
  122. ctx.Data["Username"] = ctx.Repo.Owner.Name
  123. ctx.Data["Reponame"] = ctx.Repo.Repository.Name
  124. ctx.Data["CommitCount"] = commits.Len()
  125. ctx.Data["Branch"] = ctx.Repo.BranchName
  126. ctx.HTML(200, tplCommits)
  127. }
  128. // FileHistory show a file's reversions
  129. func FileHistory(ctx *context.Context) {
  130. ctx.Data["IsRepoToolbarCommits"] = true
  131. fileName := ctx.Repo.TreePath
  132. if len(fileName) == 0 {
  133. Commits(ctx)
  134. return
  135. }
  136. branchName := ctx.Repo.BranchName
  137. commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(branchName, fileName)
  138. if err != nil {
  139. ctx.Handle(500, "FileCommitsCount", err)
  140. return
  141. } else if commitsCount == 0 {
  142. ctx.Handle(404, "FileCommitsCount", nil)
  143. return
  144. }
  145. page := ctx.QueryInt("page")
  146. if page <= 1 {
  147. page = 1
  148. }
  149. ctx.Data["Page"] = paginater.New(int(commitsCount), git.CommitsRangeSize, page, 5)
  150. commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(branchName, fileName, page)
  151. if err != nil {
  152. ctx.Handle(500, "CommitsByFileAndRange", err)
  153. return
  154. }
  155. commits = renderIssueLinks(commits, ctx.Repo.RepoLink)
  156. commits = models.ValidateCommitsWithEmails(commits)
  157. commits = models.ParseCommitsWithSignature(commits)
  158. commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
  159. ctx.Data["Commits"] = commits
  160. ctx.Data["Username"] = ctx.Repo.Owner.Name
  161. ctx.Data["Reponame"] = ctx.Repo.Repository.Name
  162. ctx.Data["FileName"] = fileName
  163. ctx.Data["CommitCount"] = commitsCount
  164. ctx.Data["Branch"] = branchName
  165. ctx.HTML(200, tplCommits)
  166. }
  167. // Diff show different from current commit to previous commit
  168. func Diff(ctx *context.Context) {
  169. ctx.Data["PageIsDiff"] = true
  170. ctx.Data["RequireHighlightJS"] = true
  171. userName := ctx.Repo.Owner.Name
  172. repoName := ctx.Repo.Repository.Name
  173. commitID := ctx.Params(":sha")
  174. commit, err := ctx.Repo.GitRepo.GetCommit(commitID)
  175. if err != nil {
  176. if git.IsErrNotExist(err) {
  177. ctx.Handle(404, "Repo.GitRepo.GetCommit", err)
  178. } else {
  179. ctx.Handle(500, "Repo.GitRepo.GetCommit", err)
  180. }
  181. return
  182. }
  183. if len(commitID) != 40 {
  184. commitID = commit.ID.String()
  185. }
  186. statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository, ctx.Repo.Commit.ID.String(), 0)
  187. if err != nil {
  188. log.Error(3, "GetLatestCommitStatus: %v", err)
  189. }
  190. ctx.Data["CommitStatus"] = models.CalcCommitStatus(statuses)
  191. diff, err := models.GetDiffCommit(models.RepoPath(userName, repoName),
  192. commitID, setting.Git.MaxGitDiffLines,
  193. setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
  194. if err != nil {
  195. ctx.Handle(404, "GetDiffCommit", err)
  196. return
  197. }
  198. parents := make([]string, commit.ParentCount())
  199. for i := 0; i < commit.ParentCount(); i++ {
  200. sha, err := commit.ParentID(i)
  201. parents[i] = sha.String()
  202. if err != nil {
  203. ctx.Handle(404, "repo.Diff", err)
  204. return
  205. }
  206. }
  207. ctx.Data["CommitID"] = commitID
  208. ctx.Data["Username"] = userName
  209. ctx.Data["Reponame"] = repoName
  210. ctx.Data["IsImageFile"] = commit.IsImageFile
  211. ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
  212. ctx.Data["Commit"] = commit
  213. ctx.Data["Verification"] = models.ParseCommitWithSignature(commit)
  214. ctx.Data["Author"] = models.ValidateCommitWithEmail(commit)
  215. ctx.Data["Diff"] = diff
  216. ctx.Data["Parents"] = parents
  217. ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
  218. ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", commitID)
  219. if commit.ParentCount() > 0 {
  220. ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", parents[0])
  221. }
  222. ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", commitID)
  223. ctx.HTML(200, tplDiff)
  224. }
  225. // RawDiff dumps diff results of repository in given commit ID to io.Writer
  226. func RawDiff(ctx *context.Context) {
  227. if err := models.GetRawDiff(
  228. models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name),
  229. ctx.Params(":sha"),
  230. models.RawDiffType(ctx.Params(":ext")),
  231. ctx.Resp,
  232. ); err != nil {
  233. ctx.Handle(500, "GetRawDiff", err)
  234. return
  235. }
  236. }
  237. // CompareDiff show different from one commit to another commit
  238. func CompareDiff(ctx *context.Context) {
  239. ctx.Data["IsRepoToolbarCommits"] = true
  240. ctx.Data["IsDiffCompare"] = true
  241. userName := ctx.Repo.Owner.Name
  242. repoName := ctx.Repo.Repository.Name
  243. beforeCommitID := ctx.Params(":before")
  244. afterCommitID := ctx.Params(":after")
  245. commit, err := ctx.Repo.GitRepo.GetCommit(afterCommitID)
  246. if err != nil {
  247. ctx.Handle(404, "GetCommit", err)
  248. return
  249. }
  250. diff, err := models.GetDiffRange(models.RepoPath(userName, repoName), beforeCommitID,
  251. afterCommitID, setting.Git.MaxGitDiffLines,
  252. setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
  253. if err != nil {
  254. ctx.Handle(404, "GetDiffRange", err)
  255. return
  256. }
  257. commits, err := commit.CommitsBeforeUntil(beforeCommitID)
  258. if err != nil {
  259. ctx.Handle(500, "CommitsBeforeUntil", err)
  260. return
  261. }
  262. commits = models.ValidateCommitsWithEmails(commits)
  263. commits = models.ParseCommitsWithSignature(commits)
  264. commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
  265. ctx.Data["CommitRepoLink"] = ctx.Repo.RepoLink
  266. ctx.Data["Commits"] = commits
  267. ctx.Data["CommitCount"] = commits.Len()
  268. ctx.Data["BeforeCommitID"] = beforeCommitID
  269. ctx.Data["AfterCommitID"] = afterCommitID
  270. ctx.Data["Username"] = userName
  271. ctx.Data["Reponame"] = repoName
  272. ctx.Data["IsImageFile"] = commit.IsImageFile
  273. ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID) + " · " + userName + "/" + repoName
  274. ctx.Data["Commit"] = commit
  275. ctx.Data["Diff"] = diff
  276. ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
  277. ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", afterCommitID)
  278. ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", beforeCommitID)
  279. ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", afterCommitID)
  280. ctx.Data["RequireHighlightJS"] = true
  281. ctx.HTML(200, tplDiff)
  282. }