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 14 kB

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