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

repo.go 12 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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 context
  5. import (
  6. "fmt"
  7. "path"
  8. "strings"
  9. "github.com/Unknwon/com"
  10. "gopkg.in/macaron.v1"
  11. "github.com/gogits/git-module"
  12. "github.com/gogits/gogs/models"
  13. "github.com/gogits/gogs/modules/log"
  14. "github.com/gogits/gogs/modules/setting"
  15. )
  16. type PullRequest struct {
  17. BaseRepo *models.Repository
  18. Allowed bool
  19. SameRepo bool
  20. HeadInfo string // [<user>:]<branch>
  21. }
  22. type Repository struct {
  23. AccessMode models.AccessMode
  24. IsWatching bool
  25. IsViewBranch bool
  26. IsViewTag bool
  27. IsViewCommit bool
  28. Repository *models.Repository
  29. Owner *models.User
  30. Commit *git.Commit
  31. Tag *git.Tag
  32. GitRepo *git.Repository
  33. BranchName string
  34. TagName string
  35. TreeName string
  36. CommitID string
  37. RepoLink string
  38. CloneLink models.CloneLink
  39. CommitsCount int64
  40. Mirror *models.Mirror
  41. PullRequest *PullRequest
  42. }
  43. // IsOwner returns true if current user is the owner of repository.
  44. func (r *Repository) IsOwner() bool {
  45. return r.AccessMode >= models.ACCESS_MODE_OWNER
  46. }
  47. // IsAdmin returns true if current user has admin or higher access of repository.
  48. func (r *Repository) IsAdmin() bool {
  49. return r.AccessMode >= models.ACCESS_MODE_ADMIN
  50. }
  51. // IsWriter returns true if current user has write or higher access of repository.
  52. func (r *Repository) IsWriter() bool {
  53. return r.AccessMode >= models.ACCESS_MODE_WRITE
  54. }
  55. // HasAccess returns true if the current user has at least read access for this repository
  56. func (r *Repository) HasAccess() bool {
  57. return r.AccessMode >= models.ACCESS_MODE_READ
  58. }
  59. func RetrieveBaseRepo(ctx *Context, repo *models.Repository) {
  60. // Non-fork repository will not return error in this method.
  61. if err := repo.GetBaseRepo(); err != nil {
  62. if models.IsErrRepoNotExist(err) {
  63. repo.IsFork = false
  64. repo.ForkID = 0
  65. return
  66. }
  67. ctx.Handle(500, "GetBaseRepo", err)
  68. return
  69. } else if err = repo.BaseRepo.GetOwner(); err != nil {
  70. ctx.Handle(500, "BaseRepo.GetOwner", err)
  71. return
  72. }
  73. }
  74. // composeGoGetImport returns go-get-import meta content.
  75. func composeGoGetImport(owner, repo string) string {
  76. return path.Join(setting.Domain, setting.AppSubUrl, owner, repo)
  77. }
  78. // earlyResponseForGoGetMeta responses appropriate go-get meta with status 200
  79. // if user does not have actual access to the requested repository,
  80. // or the owner or repository does not exist at all.
  81. // This is particular a workaround for "go get" command which does not respect
  82. // .netrc file.
  83. func earlyResponseForGoGetMeta(ctx *Context) {
  84. ctx.PlainText(200, []byte(com.Expand(`<meta name="go-import" content="{GoGetImport} git {CloneLink}">`,
  85. map[string]string{
  86. "GoGetImport": composeGoGetImport(ctx.Params(":username"), ctx.Params(":reponame")),
  87. "CloneLink": models.ComposeHTTPSCloneURL(ctx.Params(":username"), ctx.Params(":reponame")),
  88. })))
  89. }
  90. func RepoAssignment(args ...bool) macaron.Handler {
  91. return func(ctx *Context) {
  92. var (
  93. displayBare bool // To display bare page if it is a bare repo.
  94. )
  95. if len(args) >= 1 {
  96. displayBare = args[0]
  97. }
  98. var (
  99. owner *models.User
  100. err error
  101. )
  102. userName := ctx.Params(":username")
  103. repoName := ctx.Params(":reponame")
  104. refName := ctx.Params(":branchname")
  105. if len(refName) == 0 {
  106. refName = ctx.Params(":path")
  107. }
  108. // Check if the user is the same as the repository owner
  109. if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
  110. owner = ctx.User
  111. } else {
  112. owner, err = models.GetUserByName(userName)
  113. if err != nil {
  114. if models.IsErrUserNotExist(err) {
  115. if ctx.Query("go-get") == "1" {
  116. earlyResponseForGoGetMeta(ctx)
  117. return
  118. }
  119. ctx.Handle(404, "GetUserByName", err)
  120. } else {
  121. ctx.Handle(500, "GetUserByName", err)
  122. }
  123. return
  124. }
  125. }
  126. ctx.Repo.Owner = owner
  127. // Get repository.
  128. repo, err := models.GetRepositoryByName(owner.ID, repoName)
  129. if err != nil {
  130. if models.IsErrRepoNotExist(err) {
  131. if ctx.Query("go-get") == "1" {
  132. earlyResponseForGoGetMeta(ctx)
  133. return
  134. }
  135. ctx.Handle(404, "GetRepositoryByName", err)
  136. } else {
  137. ctx.Handle(500, "GetRepositoryByName", err)
  138. }
  139. return
  140. } else if err = repo.GetOwner(); err != nil {
  141. ctx.Handle(500, "GetOwner", err)
  142. return
  143. }
  144. // Admin has super access.
  145. if ctx.IsSigned && ctx.User.IsAdmin {
  146. ctx.Repo.AccessMode = models.ACCESS_MODE_OWNER
  147. } else {
  148. mode, err := models.AccessLevel(ctx.User, repo)
  149. if err != nil {
  150. ctx.Handle(500, "AccessLevel", err)
  151. return
  152. }
  153. ctx.Repo.AccessMode = mode
  154. }
  155. // Check access.
  156. if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
  157. if ctx.Query("go-get") == "1" {
  158. earlyResponseForGoGetMeta(ctx)
  159. return
  160. }
  161. ctx.Handle(404, "no access right", err)
  162. return
  163. }
  164. ctx.Data["HasAccess"] = true
  165. if repo.IsMirror {
  166. ctx.Repo.Mirror, err = models.GetMirror(repo.ID)
  167. if err != nil {
  168. ctx.Handle(500, "GetMirror", err)
  169. return
  170. }
  171. ctx.Data["MirrorEnablePrune"] = ctx.Repo.Mirror.EnablePrune
  172. ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval
  173. ctx.Data["Mirror"] = ctx.Repo.Mirror
  174. }
  175. ctx.Repo.Repository = repo
  176. ctx.Data["IsBareRepo"] = ctx.Repo.Repository.IsBare
  177. gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
  178. if err != nil {
  179. ctx.Handle(500, "RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
  180. return
  181. }
  182. ctx.Repo.GitRepo = gitRepo
  183. ctx.Repo.RepoLink = repo.Link()
  184. ctx.Data["RepoLink"] = ctx.Repo.RepoLink
  185. ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
  186. tags, err := ctx.Repo.GitRepo.GetTags()
  187. if err != nil {
  188. ctx.Handle(500, "GetTags", err)
  189. return
  190. }
  191. ctx.Data["Tags"] = tags
  192. ctx.Repo.Repository.NumTags = len(tags)
  193. ctx.Data["Title"] = owner.Name + "/" + repo.Name
  194. ctx.Data["Repository"] = repo
  195. ctx.Data["Owner"] = ctx.Repo.Repository.Owner
  196. ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
  197. ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
  198. ctx.Data["IsRepositoryWriter"] = ctx.Repo.IsWriter()
  199. ctx.Data["DisableSSH"] = setting.SSH.Disabled
  200. ctx.Data["CloneLink"] = repo.CloneLink()
  201. ctx.Data["WikiCloneLink"] = repo.WikiCloneLink()
  202. if ctx.IsSigned {
  203. ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID)
  204. ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)
  205. }
  206. // repo is bare and display enable
  207. if ctx.Repo.Repository.IsBare {
  208. log.Debug("Bare repository: %s", ctx.Repo.RepoLink)
  209. // NOTE: to prevent templating error
  210. ctx.Data["BranchName"] = ""
  211. if displayBare {
  212. if !ctx.Repo.IsAdmin() {
  213. ctx.Flash.Info(ctx.Tr("repo.repo_is_empty"), true)
  214. }
  215. ctx.HTML(200, "repo/bare")
  216. }
  217. return
  218. }
  219. ctx.Data["TagName"] = ctx.Repo.TagName
  220. brs, err := ctx.Repo.GitRepo.GetBranches()
  221. if err != nil {
  222. ctx.Handle(500, "GetBranches", err)
  223. return
  224. }
  225. ctx.Data["Branches"] = brs
  226. ctx.Data["BrancheCount"] = len(brs)
  227. // If not branch selected, try default one.
  228. // If default branch doesn't exists, fall back to some other branch.
  229. if len(ctx.Repo.BranchName) == 0 {
  230. if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
  231. ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
  232. } else if len(brs) > 0 {
  233. ctx.Repo.BranchName = brs[0]
  234. }
  235. }
  236. ctx.Data["BranchName"] = ctx.Repo.BranchName
  237. ctx.Data["CommitID"] = ctx.Repo.CommitID
  238. if repo.IsFork {
  239. RetrieveBaseRepo(ctx, repo)
  240. if ctx.Written() {
  241. return
  242. }
  243. }
  244. // People who have push access and propose a new pull request.
  245. if ctx.Repo.IsWriter() {
  246. // Pull request is allowed if this is a fork repository
  247. // and base repository accepts pull requests.
  248. if repo.BaseRepo != nil {
  249. if repo.BaseRepo.AllowsPulls() {
  250. ctx.Data["BaseRepo"] = repo.BaseRepo
  251. ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
  252. ctx.Repo.PullRequest.Allowed = true
  253. ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName
  254. }
  255. } else {
  256. // Or, this is repository accepts pull requests between branches.
  257. if repo.AllowsPulls() {
  258. ctx.Data["BaseRepo"] = repo
  259. ctx.Repo.PullRequest.BaseRepo = repo
  260. ctx.Repo.PullRequest.Allowed = true
  261. ctx.Repo.PullRequest.SameRepo = true
  262. ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName
  263. }
  264. }
  265. }
  266. ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
  267. if ctx.Query("go-get") == "1" {
  268. ctx.Data["GoGetImport"] = composeGoGetImport(owner.Name, repo.Name)
  269. prefix := setting.AppUrl + path.Join(owner.Name, repo.Name, "src", ctx.Repo.BranchName)
  270. ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
  271. ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
  272. }
  273. }
  274. }
  275. // RepoRef handles repository reference name including those contain `/`.
  276. func RepoRef() macaron.Handler {
  277. return func(ctx *Context) {
  278. // Empty repository does not have reference information.
  279. if ctx.Repo.Repository.IsBare {
  280. return
  281. }
  282. var (
  283. refName string
  284. err error
  285. )
  286. // For API calls.
  287. if ctx.Repo.GitRepo == nil {
  288. repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
  289. gitRepo, err := git.OpenRepository(repoPath)
  290. if err != nil {
  291. ctx.Handle(500, "RepoRef Invalid repo "+repoPath, err)
  292. return
  293. }
  294. ctx.Repo.GitRepo = gitRepo
  295. }
  296. // Get default branch.
  297. if len(ctx.Params("*")) == 0 {
  298. refName = ctx.Repo.Repository.DefaultBranch
  299. if !ctx.Repo.GitRepo.IsBranchExist(refName) {
  300. brs, err := ctx.Repo.GitRepo.GetBranches()
  301. if err != nil {
  302. ctx.Handle(500, "GetBranches", err)
  303. return
  304. }
  305. refName = brs[0]
  306. }
  307. ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
  308. if err != nil {
  309. ctx.Handle(500, "GetBranchCommit", err)
  310. return
  311. }
  312. ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
  313. ctx.Repo.IsViewBranch = true
  314. } else {
  315. hasMatched := false
  316. parts := strings.Split(ctx.Params("*"), "/")
  317. for i, part := range parts {
  318. refName = strings.TrimPrefix(refName+"/"+part, "/")
  319. if ctx.Repo.GitRepo.IsBranchExist(refName) ||
  320. ctx.Repo.GitRepo.IsTagExist(refName) {
  321. if i < len(parts)-1 {
  322. ctx.Repo.TreeName = strings.Join(parts[i+1:], "/")
  323. }
  324. hasMatched = true
  325. break
  326. }
  327. }
  328. if !hasMatched && len(parts[0]) == 40 {
  329. refName = parts[0]
  330. ctx.Repo.TreeName = strings.Join(parts[1:], "/")
  331. }
  332. if ctx.Repo.GitRepo.IsBranchExist(refName) {
  333. ctx.Repo.IsViewBranch = true
  334. ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
  335. if err != nil {
  336. ctx.Handle(500, "GetBranchCommit", err)
  337. return
  338. }
  339. ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
  340. } else if ctx.Repo.GitRepo.IsTagExist(refName) {
  341. ctx.Repo.IsViewTag = true
  342. ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
  343. if err != nil {
  344. ctx.Handle(500, "GetTagCommit", err)
  345. return
  346. }
  347. ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
  348. } else if len(refName) == 40 {
  349. ctx.Repo.IsViewCommit = true
  350. ctx.Repo.CommitID = refName
  351. ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
  352. if err != nil {
  353. ctx.Handle(404, "GetCommit", nil)
  354. return
  355. }
  356. } else {
  357. ctx.Handle(404, "RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
  358. return
  359. }
  360. }
  361. ctx.Repo.BranchName = refName
  362. ctx.Data["BranchName"] = ctx.Repo.BranchName
  363. ctx.Data["CommitID"] = ctx.Repo.CommitID
  364. ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
  365. ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
  366. ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
  367. ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
  368. if err != nil {
  369. ctx.Handle(500, "CommitsCount", err)
  370. return
  371. }
  372. ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
  373. }
  374. }
  375. func RequireRepoAdmin() macaron.Handler {
  376. return func(ctx *Context) {
  377. if !ctx.IsSigned || (!ctx.Repo.IsAdmin() && !ctx.User.IsAdmin) {
  378. ctx.Handle(404, ctx.Req.RequestURI, nil)
  379. return
  380. }
  381. }
  382. }
  383. func RequireRepoWriter() macaron.Handler {
  384. return func(ctx *Context) {
  385. if !ctx.IsSigned || (!ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
  386. ctx.Handle(404, ctx.Req.RequestURI, nil)
  387. return
  388. }
  389. }
  390. }
  391. // GitHookService checks if repository Git hooks service has been enabled.
  392. func GitHookService() macaron.Handler {
  393. return func(ctx *Context) {
  394. if !ctx.User.CanEditGitHook() {
  395. ctx.Handle(404, "GitHookService", nil)
  396. return
  397. }
  398. }
  399. }