2014-03-15 20:03:23 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-09-12 09:48:13 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2014-03-15 20:03:23 +04:00
2016-03-11 19:56:52 +03:00
package context
2014-03-15 20:03:23 +04:00
import (
2021-05-06 02:30:25 +03:00
"context"
2024-02-15 00:48:45 +03:00
"errors"
2014-03-20 08:12:33 +04:00
"fmt"
2022-04-01 11:47:50 +03:00
"html"
2021-12-15 09:59:57 +03:00
"net/http"
2019-01-31 00:04:19 +03:00
"net/url"
2015-10-31 19:04:04 +03:00
"path"
2014-03-17 12:47:42 +04:00
"strings"
2014-03-16 10:28:24 +04:00
2022-05-20 17:08:52 +03:00
"code.gitea.io/gitea/models/db"
2022-06-12 18:51:54 +03:00
git_model "code.gitea.io/gitea/models/git"
2022-06-13 12:37:59 +03:00
issues_model "code.gitea.io/gitea/models/issues"
2022-05-11 13:09:36 +03:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 22:57:58 +03:00
unit_model "code.gitea.io/gitea/models/unit"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2017-10-26 04:37:33 +03:00
"code.gitea.io/gitea/modules/cache"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
"code.gitea.io/gitea/modules/gitrepo"
2024-11-05 09:35:54 +03:00
"code.gitea.io/gitea/modules/httplib"
2022-01-27 11:30:51 +03:00
code_indexer "code.gitea.io/gitea/modules/indexer/code"
2017-10-30 05:04:25 +03:00
"code.gitea.io/gitea/modules/log"
2024-02-23 05:18:33 +03:00
"code.gitea.io/gitea/modules/optional"
2022-06-06 11:01:49 +03:00
repo_module "code.gitea.io/gitea/modules/repository"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2020-11-25 23:07:39 +03:00
"code.gitea.io/gitea/modules/util"
2021-12-10 11:14:24 +03:00
asymkey_service "code.gitea.io/gitea/services/asymkey"
2017-10-26 04:37:33 +03:00
2019-10-16 00:24:16 +03:00
"github.com/editorconfig/editorconfig-core-go/v2"
2014-03-15 20:03:23 +04:00
)
2021-07-08 14:38:13 +03:00
// PullRequest contains information to make a pull request
2016-03-14 00:37:44 +03:00
type PullRequest struct {
2024-12-12 11:10:09 +03:00
BaseRepo * repo_model . Repository
Allowed bool // it only used by the web tmpl: "PullRequestCtx.Allowed"
SameRepo bool // it only used by the web tmpl: "PullRequestCtx.SameRepo"
2016-03-14 00:37:44 +03:00
}
2017-03-15 03:52:01 +03:00
// Repository contains information to operate a repository
2016-03-14 00:37:44 +03:00
type Repository struct {
2022-05-11 13:09:36 +03:00
access_model . Permission
2016-03-14 00:37:44 +03:00
IsWatching bool
IsViewBranch bool
IsViewTag bool
IsViewCommit bool
2021-12-10 04:27:50 +03:00
Repository * repo_model . Repository
2021-11-24 12:49:20 +03:00
Owner * user_model . User
2016-03-14 00:37:44 +03:00
Commit * git . Commit
Tag * git . Tag
GitRepo * git . Repository
2021-11-18 02:50:17 +03:00
RefName string
2016-03-14 00:37:44 +03:00
BranchName string
TagName string
2016-08-25 07:35:03 +03:00
TreePath string
2016-03-14 00:37:44 +03:00
CommitID string
RepoLink string
2021-12-10 04:27:50 +03:00
CloneLink repo_model . CloneLink
2016-03-14 00:37:44 +03:00
CommitsCount int64
2016-08-30 12:08:38 +03:00
PullRequest * PullRequest
2016-03-14 00:37:44 +03:00
}
2022-05-11 13:09:36 +03:00
// CanWriteToBranch checks if the branch is writable by the user
2023-07-22 17:14:27 +03:00
func ( r * Repository ) CanWriteToBranch ( ctx context . Context , user * user_model . User , branch string ) bool {
return issues_model . CanMaintainerWriteToBranch ( ctx , r . Permission , branch , user )
2022-05-11 13:09:36 +03:00
}
2016-08-30 12:08:38 +03:00
// CanEnableEditor returns true if repository is editable and user has proper access level.
2023-07-22 17:14:27 +03:00
func ( r * Repository ) CanEnableEditor ( ctx context . Context , user * user_model . User ) bool {
return r . IsViewBranch && r . CanWriteToBranch ( ctx , user , r . BranchName ) && r . Repository . CanEnableEditor ( ) && ! r . Repository . IsArchived
2016-08-30 12:08:38 +03:00
}
2017-10-15 22:59:24 +03:00
// CanCreateBranch returns true if repository is editable and user has proper access level.
func ( r * Repository ) CanCreateBranch ( ) bool {
2021-11-09 22:57:58 +03:00
return r . Permission . CanWrite ( unit_model . TypeCode ) && r . Repository . CanCreateBranch ( )
2017-10-15 22:59:24 +03:00
}
2024-02-24 09:55:19 +03:00
func ( r * Repository ) GetObjectFormat ( ) git . ObjectFormat {
return git . ObjectFormatFromName ( r . Repository . ObjectFormatName )
}
2019-01-23 21:58:38 +03:00
// RepoMustNotBeArchived checks if a repo is archived
2021-01-26 18:36:53 +03:00
func RepoMustNotBeArchived ( ) func ( ctx * Context ) {
2019-01-23 21:58:38 +03:00
return func ( ctx * Context ) {
if ctx . Repo . Repository . IsArchived {
2024-02-15 00:48:45 +03:00
ctx . NotFound ( "IsArchived" , errors . New ( ctx . Locale . TrString ( "repo.archive.title" ) ) )
2019-01-23 21:58:38 +03:00
}
}
}
2020-01-15 11:32:57 +03:00
// CanCommitToBranchResults represents the results of CanCommitToBranch
type CanCommitToBranchResults struct {
CanCommitToBranch bool
EditorEnabled bool
UserCanPush bool
RequireSigned bool
WillSign bool
SigningKey string
WontSignReason string
}
2017-05-02 03:49:55 +03:00
// CanCommitToBranch returns true if repository is editable and user has proper access level
2022-08-25 05:31:57 +03:00
//
// and branch is not protected for push
2022-01-20 02:26:57 +03:00
func ( r * Repository ) CanCommitToBranch ( ctx context . Context , doer * user_model . User ) ( CanCommitToBranchResults , error ) {
2023-01-16 11:00:22 +03:00
protectedBranch , err := git_model . GetFirstMatchProtectedBranchRule ( ctx , r . Repository . ID , r . BranchName )
2017-05-02 03:49:55 +03:00
if err != nil {
2020-01-15 11:32:57 +03:00
return CanCommitToBranchResults { } , err
2017-05-02 03:49:55 +03:00
}
2020-01-15 11:32:57 +03:00
userCanPush := true
requireSigned := false
if protectedBranch != nil {
2023-01-16 11:00:22 +03:00
protectedBranch . Repo = r . Repository
userCanPush = protectedBranch . CanUserPush ( ctx , doer )
2020-01-15 11:32:57 +03:00
requireSigned = protectedBranch . RequireSignedCommits
}
2022-01-20 02:26:57 +03:00
sign , keyID , _ , err := asymkey_service . SignCRUDAction ( ctx , r . Repository . RepoPath ( ) , doer , r . Repository . RepoPath ( ) , git . BranchPrefix + r . BranchName )
2020-01-15 11:32:57 +03:00
2023-07-22 17:14:27 +03:00
canCommit := r . CanEnableEditor ( ctx , doer ) && userCanPush
2020-01-15 11:32:57 +03:00
if requireSigned {
canCommit = canCommit && sign
}
wontSignReason := ""
if err != nil {
2021-12-10 11:14:24 +03:00
if asymkey_service . IsErrWontSign ( err ) {
wontSignReason = string ( err . ( * asymkey_service . ErrWontSign ) . Reason )
2020-01-15 11:32:57 +03:00
err = nil
} else {
wontSignReason = "error"
}
}
return CanCommitToBranchResults {
CanCommitToBranch : canCommit ,
2023-07-22 17:14:27 +03:00
EditorEnabled : r . CanEnableEditor ( ctx , doer ) ,
2020-01-15 11:32:57 +03:00
UserCanPush : userCanPush ,
RequireSigned : requireSigned ,
WillSign : sign ,
SigningKey : keyID ,
WontSignReason : wontSignReason ,
} , err
2017-05-02 03:49:55 +03:00
}
2017-09-12 09:48:13 +03:00
// CanUseTimetracker returns whether or not a user can use the timetracker.
2023-10-14 11:37:24 +03:00
func ( r * Repository ) CanUseTimetracker ( ctx context . Context , issue * issues_model . Issue , user * user_model . User ) bool {
2017-09-12 09:48:13 +03:00
// Checking for following:
// 1. Is timetracker enabled
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
2023-10-14 11:37:24 +03:00
isAssigned , _ := issues_model . IsUserAssignedToIssue ( ctx , issue , user )
return r . Repository . IsTimetrackerEnabled ( ctx ) && ( ! r . Repository . AllowOnlyContributorsToTrackTime ( ctx ) ||
2020-01-20 15:00:32 +03:00
r . Permission . CanWriteIssuesOrPulls ( issue . IsPull ) || issue . IsPoster ( user . ID ) || isAssigned )
2017-09-12 09:48:13 +03:00
}
2018-07-18 00:23:58 +03:00
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
2023-10-14 11:37:24 +03:00
func ( r * Repository ) CanCreateIssueDependencies ( ctx context . Context , user * user_model . User , isPull bool ) bool {
return r . Repository . IsDependenciesEnabled ( ctx ) && r . Permission . CanWriteIssuesOrPulls ( isPull )
2018-07-18 00:23:58 +03:00
}
2017-10-26 04:37:33 +03:00
// GetCommitsCount returns cached commit count for current view
func ( r * Repository ) GetCommitsCount ( ) ( int64 , error ) {
2023-04-19 16:40:42 +03:00
if r . Commit == nil {
return 0 , nil
}
2017-10-26 04:37:33 +03:00
var contextName string
if r . IsViewBranch {
contextName = r . BranchName
} else if r . IsViewTag {
contextName = r . TagName
} else {
contextName = r . CommitID
}
return cache . GetInt64 ( r . Repository . GetCommitsCountCacheKey ( contextName , r . IsViewBranch || r . IsViewTag ) , func ( ) ( int64 , error ) {
return r . Commit . CommitsCount ( )
} )
}
2020-11-08 20:21:54 +03:00
// GetCommitGraphsCount returns cached commit count for current view
2022-01-20 02:26:57 +03:00
func ( r * Repository ) GetCommitGraphsCount ( ctx context . Context , hidePRRefs bool , branches , files [ ] string ) ( int64 , error ) {
2020-11-08 20:21:54 +03:00
cacheKey := fmt . Sprintf ( "commits-count-%d-graph-%t-%s-%s" , r . Repository . ID , hidePRRefs , branches , files )
return cache . GetInt64 ( cacheKey , func ( ) ( int64 , error ) {
if len ( branches ) == 0 {
2022-01-20 02:26:57 +03:00
return git . AllCommitsCount ( ctx , r . Repository . RepoPath ( ) , hidePRRefs , files ... )
2020-11-08 20:21:54 +03:00
}
2023-05-08 10:10:53 +03:00
return git . CommitsCount ( ctx ,
git . CommitsCountOptions {
RepoPath : r . Repository . RepoPath ( ) ,
Revision : branches ,
RelPath : files ,
} )
2020-11-08 20:21:54 +03:00
} )
}
2017-10-30 05:04:25 +03:00
// BranchNameSubURL sub-URL for the BranchName field
func ( r * Repository ) BranchNameSubURL ( ) string {
switch {
case r . IsViewBranch :
2021-11-16 21:18:25 +03:00
return "branch/" + util . PathEscapeSegments ( r . BranchName )
2017-10-30 05:04:25 +03:00
case r . IsViewTag :
2021-11-18 02:50:17 +03:00
return "tag/" + util . PathEscapeSegments ( r . TagName )
2017-10-30 05:04:25 +03:00
case r . IsViewCommit :
2021-11-18 02:50:17 +03:00
return "commit/" + util . PathEscapeSegments ( r . CommitID )
2017-10-30 05:04:25 +03:00
}
2019-04-02 10:48:31 +03:00
log . Error ( "Unknown view type for repo: %v" , r )
2017-10-30 05:04:25 +03:00
return ""
}
2019-04-17 19:06:35 +03:00
// FileExists returns true if a file exists in the given repo branch
2021-12-20 07:41:31 +03:00
func ( r * Repository ) FileExists ( path , branch string ) ( bool , error ) {
2019-04-17 19:06:35 +03:00
if branch == "" {
branch = r . Repository . DefaultBranch
}
commit , err := r . GitRepo . GetBranchCommit ( branch )
if err != nil {
return false , err
}
if _ , err := commit . GetTreeEntryByPath ( path ) ; err != nil {
return false , err
}
return true , nil
}
2016-08-12 03:07:09 +03:00
// GetEditorconfig returns the .editorconfig definition if found in the
// HEAD of the default repo branch.
2023-04-06 23:01:20 +03:00
func ( r * Repository ) GetEditorconfig ( optCommit ... * git . Commit ) ( cfg * editorconfig . Editorconfig , warning , err error ) {
2019-10-13 16:23:14 +03:00
if r . GitRepo == nil {
2023-04-06 23:01:20 +03:00
return nil , nil , nil
2019-10-13 16:23:14 +03:00
}
2023-04-06 23:01:20 +03:00
var commit * git . Commit
2022-04-21 18:17:57 +03:00
if len ( optCommit ) != 0 {
commit = optCommit [ 0 ]
} else {
commit , err = r . GitRepo . GetBranchCommit ( r . Repository . DefaultBranch )
if err != nil {
2023-04-06 23:01:20 +03:00
return nil , nil , err
2022-04-21 18:17:57 +03:00
}
2016-08-12 03:07:09 +03:00
}
treeEntry , err := commit . GetTreeEntryByPath ( ".editorconfig" )
if err != nil {
2023-04-06 23:01:20 +03:00
return nil , nil , err
2016-08-12 03:07:09 +03:00
}
2017-11-29 04:50:39 +03:00
if treeEntry . Blob ( ) . Size ( ) >= setting . UI . MaxDisplayFileSize {
2023-04-06 23:01:20 +03:00
return nil , nil , git . ErrNotExist { ID : "" , RelPath : ".editorconfig" }
2017-11-29 04:50:39 +03:00
}
2019-04-19 15:17:27 +03:00
reader , err := treeEntry . Blob ( ) . DataAsync ( )
2016-08-12 03:07:09 +03:00
if err != nil {
2023-04-06 23:01:20 +03:00
return nil , nil , err
2016-08-12 03:07:09 +03:00
}
2019-04-19 15:17:27 +03:00
defer reader . Close ( )
2023-04-06 23:01:20 +03:00
return editorconfig . ParseGraceful ( reader )
2016-08-12 03:07:09 +03:00
}
2016-11-25 09:51:01 +03:00
// RetrieveBaseRepo retrieves base repository
2021-12-10 04:27:50 +03:00
func RetrieveBaseRepo ( ctx * Context , repo * repo_model . Repository ) {
2015-09-01 18:43:53 +03:00
// Non-fork repository will not return error in this method.
2022-12-03 05:48:26 +03:00
if err := repo . GetBaseRepo ( ctx ) ; err != nil {
2021-12-10 04:27:50 +03:00
if repo_model . IsErrRepoNotExist ( err ) {
2015-09-01 18:43:53 +03:00
repo . IsFork = false
repo . ForkID = 0
return
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBaseRepo" , err )
2015-09-01 18:43:53 +03:00
return
2023-02-18 15:11:03 +03:00
} else if err = repo . BaseRepo . LoadOwner ( ctx ) ; err != nil {
ctx . ServerError ( "BaseRepo.LoadOwner" , err )
2015-09-01 18:43:53 +03:00
return
}
}
2019-11-11 18:15:29 +03:00
// RetrieveTemplateRepo retrieves template repository used to generate this repository
2021-12-10 04:27:50 +03:00
func RetrieveTemplateRepo ( ctx * Context , repo * repo_model . Repository ) {
2019-11-11 18:15:29 +03:00
// Non-generated repository will not return error in this method.
2022-05-20 17:08:52 +03:00
templateRepo , err := repo_model . GetTemplateRepo ( ctx , repo )
2021-12-10 04:27:50 +03:00
if err != nil {
if repo_model . IsErrRepoNotExist ( err ) {
2019-11-11 18:15:29 +03:00
repo . TemplateID = 0
return
}
ctx . ServerError ( "GetTemplateRepo" , err )
return
2023-02-18 15:11:03 +03:00
} else if err = templateRepo . LoadOwner ( ctx ) ; err != nil {
ctx . ServerError ( "TemplateRepo.LoadOwner" , err )
2019-11-11 18:15:29 +03:00
return
}
2022-05-11 13:09:36 +03:00
perm , err := access_model . GetUserRepoPermission ( ctx , templateRepo , ctx . Doer )
2019-11-20 22:44:35 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2021-11-09 22:57:58 +03:00
if ! perm . CanRead ( unit_model . TypeCode ) {
2019-11-11 18:15:29 +03:00
repo . TemplateID = 0
}
}
2017-09-23 16:24:24 +03:00
// ComposeGoGetImport returns go-get-import meta content.
2024-11-05 09:35:54 +03:00
func ComposeGoGetImport ( ctx context . Context , owner , repo string ) string {
curAppURL , _ := url . Parse ( httplib . GuessCurrentAppURL ( ctx ) )
return path . Join ( curAppURL . Host , setting . AppSubURL , url . PathEscape ( owner ) , url . PathEscape ( repo ) )
2016-08-08 00:29:16 +03:00
}
2017-09-23 16:24:24 +03:00
// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
2016-08-08 00:29:16 +03:00
// if user does not have actual access to the requested repository,
// or the owner or repository does not exist at all.
// This is particular a workaround for "go get" command which does not respect
// .netrc file.
2017-09-23 16:24:24 +03:00
func EarlyResponseForGoGetMeta ( ctx * Context ) {
2024-12-24 16:47:45 +03:00
username := ctx . PathParam ( "username" )
reponame := strings . TrimSuffix ( ctx . PathParam ( "reponame" ) , ".git" )
2019-09-06 16:44:59 +03:00
if username == "" || reponame == "" {
2021-12-15 09:59:57 +03:00
ctx . PlainText ( http . StatusBadRequest , "invalid repository path" )
2019-09-06 16:44:59 +03:00
return
}
2023-05-12 12:44:37 +03:00
var cloneURL string
if setting . Repository . GoGetCloneURLProtocol == "ssh" {
cloneURL = repo_model . ComposeSSHCloneURL ( username , reponame )
} else {
cloneURL = repo_model . ComposeHTTPSCloneURL ( username , reponame )
}
2024-11-05 09:35:54 +03:00
goImportContent := fmt . Sprintf ( "%s git %s" , ComposeGoGetImport ( ctx , username , reponame ) , cloneURL )
2022-04-01 11:47:50 +03:00
htmlMeta := fmt . Sprintf ( ` <meta name="go-import" content="%s"> ` , html . EscapeString ( goImportContent ) )
ctx . PlainText ( http . StatusOK , htmlMeta )
2016-08-08 00:29:16 +03:00
}
2017-02-05 17:35:03 +03:00
// RedirectToRepo redirect to a differently-named repository
2023-05-21 04:50:53 +03:00
func RedirectToRepo ( ctx * Base , redirectRepoID int64 ) {
2024-12-24 16:47:45 +03:00
ownerName := ctx . PathParam ( "username" )
previousRepoName := ctx . PathParam ( "reponame" )
2017-02-05 17:35:03 +03:00
2022-12-03 05:48:26 +03:00
repo , err := repo_model . GetRepositoryByID ( ctx , redirectRepoID )
2017-02-05 17:35:03 +03:00
if err != nil {
2023-05-21 04:50:53 +03:00
log . Error ( "GetRepositoryByID: %v" , err )
ctx . Error ( http . StatusInternalServerError , "GetRepositoryByID" )
2017-02-05 17:35:03 +03:00
return
}
redirectPath := strings . Replace (
2021-11-16 21:18:25 +03:00
ctx . Req . URL . EscapedPath ( ) ,
url . PathEscape ( ownerName ) + "/" + url . PathEscape ( previousRepoName ) ,
url . PathEscape ( repo . OwnerName ) + "/" + url . PathEscape ( repo . Name ) ,
2017-02-05 17:35:03 +03:00
1 ,
)
2019-04-25 08:51:40 +03:00
if ctx . Req . URL . RawQuery != "" {
redirectPath += "?" + ctx . Req . URL . RawQuery
}
2022-03-23 07:54:07 +03:00
ctx . Redirect ( path . Join ( setting . AppSubURL , redirectPath ) , http . StatusTemporaryRedirect )
2017-02-05 17:35:03 +03:00
}
2017-09-19 14:44:49 +03:00
2021-12-10 04:27:50 +03:00
func repoAssignment ( ctx * Context , repo * repo_model . Repository ) {
2018-11-28 14:26:14 +03:00
var err error
2023-02-18 15:11:03 +03:00
if err = repo . LoadOwner ( ctx ) ; err != nil {
ctx . ServerError ( "LoadOwner" , err )
2019-03-05 23:15:24 +03:00
return
}
2022-05-11 13:09:36 +03:00
ctx . Repo . Permission , err = access_model . GetUserRepoPermission ( ctx , repo , ctx . Doer )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
2017-10-05 10:32:25 +03:00
}
2024-10-11 22:08:19 +03:00
if ! ctx . Repo . Permission . HasAnyUnitAccessOrEveryoneAccess ( ) && ! canWriteAsMaintainer ( ctx ) {
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2017-10-05 10:32:25 +03:00
EarlyResponseForGoGetMeta ( ctx )
return
}
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "no access right" , nil )
2017-10-05 10:32:25 +03:00
return
}
2018-11-28 14:26:14 +03:00
ctx . Data [ "Permission" ] = & ctx . Repo . Permission
2017-10-05 10:32:25 +03:00
if repo . IsMirror {
2023-05-15 22:02:10 +03:00
pullMirror , err := repo_model . GetMirrorByRepoID ( ctx , repo . ID )
2022-06-13 19:12:59 +03:00
if err == nil {
2023-05-15 22:02:10 +03:00
ctx . Data [ "PullMirror" ] = pullMirror
2022-06-13 19:12:59 +03:00
} else if err != repo_model . ErrMirrorNotExist {
ctx . ServerError ( "GetMirrorByRepoID" , err )
return
2022-05-03 16:55:17 +03:00
}
2017-10-05 10:32:25 +03:00
}
2021-12-10 04:27:50 +03:00
2017-10-05 10:32:25 +03:00
ctx . Repo . Repository = repo
ctx . Data [ "RepoName" ] = ctx . Repo . Repository . Name
2019-01-18 03:01:04 +03:00
ctx . Data [ "IsEmptyRepo" ] = ctx . Repo . Repository . IsEmpty
2017-10-05 10:32:25 +03:00
}
2021-01-29 18:35:30 +03:00
// RepoAssignment returns a middleware to handle repository assignment
2024-12-24 06:43:57 +03:00
func RepoAssignment ( ctx * Context ) {
2022-04-21 18:17:57 +03:00
if _ , repoAssignmentOnce := ctx . Data [ "repoAssignmentExecuted" ] ; repoAssignmentOnce {
2024-12-12 11:10:09 +03:00
// FIXME: it should panic in dev/test modes to have a clear behavior
2024-12-24 06:43:57 +03:00
if ! setting . IsProd || setting . IsInTesting {
panic ( "RepoAssignment should not be executed twice" )
}
return
2022-04-21 18:17:57 +03:00
}
ctx . Data [ "repoAssignmentExecuted" ] = true
2021-04-10 03:26:08 +03:00
var (
2021-11-24 12:49:20 +03:00
owner * user_model . User
2021-04-10 03:26:08 +03:00
err error
)
2024-12-24 16:47:45 +03:00
userName := ctx . PathParam ( "username" )
repoName := ctx . PathParam ( "reponame" )
2021-04-10 03:26:08 +03:00
repoName = strings . TrimSuffix ( repoName , ".git" )
2023-04-23 02:38:25 +03:00
if setting . Other . EnableFeed {
2022-11-21 08:14:58 +03:00
repoName = strings . TrimSuffix ( repoName , ".rss" )
repoName = strings . TrimSuffix ( repoName , ".atom" )
}
2021-04-10 03:26:08 +03:00
// Check if the user is the same as the repository owner
2022-03-22 10:03:22 +03:00
if ctx . IsSigned && ctx . Doer . LowerName == strings . ToLower ( userName ) {
owner = ctx . Doer
2021-04-10 03:26:08 +03:00
} else {
2022-05-20 17:08:52 +03:00
owner , err = user_model . GetUserByName ( ctx , userName )
2021-04-10 03:26:08 +03:00
if err != nil {
2021-11-24 12:49:20 +03:00
if user_model . IsErrUserNotExist ( err ) {
2022-10-11 14:54:44 +03:00
// go-get does not support redirects
// https://github.com/golang/go/issues/19760
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2021-04-10 03:26:08 +03:00
EarlyResponseForGoGetMeta ( ctx )
2024-12-24 06:43:57 +03:00
return
2021-01-26 18:36:53 +03:00
}
2022-10-11 14:54:44 +03:00
2023-09-25 16:17:37 +03:00
if redirectUserID , err := user_model . LookupUserRedirect ( ctx , userName ) ; err == nil {
2023-05-21 04:50:53 +03:00
RedirectToUser ( ctx . Base , userName , redirectUserID )
2022-10-11 14:54:44 +03:00
} else if user_model . IsErrUserRedirectNotExist ( err ) {
ctx . NotFound ( "GetUserByName" , nil )
} else {
ctx . ServerError ( "LookupUserRedirect" , err )
}
2021-04-10 03:26:08 +03:00
} else {
ctx . ServerError ( "GetUserByName" , err )
2021-01-26 18:36:53 +03:00
}
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
}
ctx . Repo . Owner = owner
2022-03-26 12:04:22 +03:00
ctx . ContextUser = owner
2023-09-06 09:38:14 +03:00
ctx . Data [ "ContextUser" ] = ctx . ContextUser
2021-04-10 03:26:08 +03:00
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
2014-03-30 06:09:59 +04:00
2022-03-23 16:29:18 +03:00
// redirect link to wiki
if strings . HasSuffix ( repoName , ".wiki" ) {
// ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added
// Now we happen to know that all of our paths are: /:username/:reponame/whatever_else
2024-12-24 16:47:45 +03:00
originalRepoName := ctx . PathParam ( "reponame" )
2022-03-23 16:29:18 +03:00
redirectRepoName := strings . TrimSuffix ( repoName , ".wiki" )
redirectRepoName += originalRepoName [ len ( redirectRepoName ) + 5 : ]
redirectPath := strings . Replace (
ctx . Req . URL . EscapedPath ( ) ,
url . PathEscape ( userName ) + "/" + url . PathEscape ( originalRepoName ) ,
url . PathEscape ( userName ) + "/" + url . PathEscape ( redirectRepoName ) + "/wiki" ,
1 ,
)
if ctx . Req . URL . RawQuery != "" {
redirectPath += "?" + ctx . Req . URL . RawQuery
}
ctx . Redirect ( path . Join ( setting . AppSubURL , redirectPath ) )
2024-12-24 06:43:57 +03:00
return
2022-03-23 16:29:18 +03:00
}
2021-04-10 03:26:08 +03:00
// Get repository.
2023-10-11 07:24:07 +03:00
repo , err := repo_model . GetRepositoryByName ( ctx , owner . ID , repoName )
2021-04-10 03:26:08 +03:00
if err != nil {
2021-12-10 04:27:50 +03:00
if repo_model . IsErrRepoNotExist ( err ) {
2023-10-14 11:37:24 +03:00
redirectRepoID , err := repo_model . LookupRedirect ( ctx , owner . ID , repoName )
2021-04-10 03:26:08 +03:00
if err == nil {
2023-05-21 04:50:53 +03:00
RedirectToRepo ( ctx . Base , redirectRepoID )
2021-12-12 18:48:20 +03:00
} else if repo_model . IsErrRedirectNotExist ( err ) {
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2021-04-10 03:26:08 +03:00
EarlyResponseForGoGetMeta ( ctx )
2024-12-24 06:43:57 +03:00
return
2014-03-15 20:03:23 +04:00
}
2021-04-10 03:26:08 +03:00
ctx . NotFound ( "GetRepositoryByName" , nil )
} else {
ctx . ServerError ( "LookupRepoRedirect" , err )
2014-03-15 20:03:23 +04:00
}
2021-04-10 03:26:08 +03:00
} else {
ctx . ServerError ( "GetRepositoryByName" , err )
}
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
repo . Owner = owner
2014-03-15 20:03:23 +04:00
2021-04-10 03:26:08 +03:00
repoAssignment ( ctx , repo )
if ctx . Written ( ) {
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
2014-04-12 05:47:39 +04:00
2021-04-10 03:26:08 +03:00
ctx . Repo . RepoLink = repo . Link ( )
ctx . Data [ "RepoLink" ] = ctx . Repo . RepoLink
ctx . Data [ "RepoRelPath" ] = ctx . Repo . Owner . Name + "/" + ctx . Repo . Repository . Name
2014-03-30 09:30:17 +04:00
2023-04-23 02:38:25 +03:00
if setting . Other . EnableFeed {
2023-04-07 11:48:23 +03:00
ctx . Data [ "EnableFeed" ] = true
ctx . Data [ "FeedURL" ] = ctx . Repo . RepoLink
}
2022-12-10 05:46:31 +03:00
unit , err := ctx . Repo . Repository . GetUnit ( ctx , unit_model . TypeExternalTracker )
2021-04-10 03:26:08 +03:00
if err == nil {
ctx . Data [ "RepoExternalIssuesLink" ] = unit . ExternalTrackerConfig ( ) . ExternalTrackerURL
}
2014-03-30 07:38:41 +04:00
2024-01-15 05:19:25 +03:00
ctx . Data [ "NumTags" ] , err = db . Count [ repo_model . Release ] ( ctx , repo_model . FindReleasesOptions {
2022-10-03 15:05:53 +03:00
IncludeDrafts : true ,
IncludeTags : true ,
2024-02-28 08:39:12 +03:00
HasSha1 : optional . Some ( true ) , // only draft releases which are created with existing tags
2024-01-15 05:19:25 +03:00
RepoID : ctx . Repo . Repository . ID ,
2021-04-10 03:26:08 +03:00
} )
if err != nil {
ctx . ServerError ( "GetReleaseCountByRepoID" , err )
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
2024-01-15 05:19:25 +03:00
ctx . Data [ "NumReleases" ] , err = db . Count [ repo_model . Release ] ( ctx , repo_model . FindReleasesOptions {
2023-09-28 16:21:47 +03:00
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
IncludeDrafts : ctx . Repo . CanWrite ( unit_model . TypeReleases ) ,
2024-01-15 05:19:25 +03:00
RepoID : ctx . Repo . Repository . ID ,
2023-09-28 16:21:47 +03:00
} )
2021-04-10 03:26:08 +03:00
if err != nil {
ctx . ServerError ( "GetReleaseCountByRepoID" , err )
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
2019-02-20 02:09:47 +03:00
2021-04-10 03:26:08 +03:00
ctx . Data [ "Title" ] = owner . Name + "/" + repo . Name
ctx . Data [ "Repository" ] = repo
ctx . Data [ "Owner" ] = ctx . Repo . Repository . Owner
ctx . Data [ "IsRepositoryOwner" ] = ctx . Repo . IsOwner ( )
ctx . Data [ "IsRepositoryAdmin" ] = ctx . Repo . IsAdmin ( )
ctx . Data [ "RepoOwnerIsOrganization" ] = repo . Owner . IsOrganization ( )
2021-11-09 22:57:58 +03:00
ctx . Data [ "CanWriteCode" ] = ctx . Repo . CanWrite ( unit_model . TypeCode )
ctx . Data [ "CanWriteIssues" ] = ctx . Repo . CanWrite ( unit_model . TypeIssues )
ctx . Data [ "CanWritePulls" ] = ctx . Repo . CanWrite ( unit_model . TypePullRequests )
2023-11-21 11:27:33 +03:00
ctx . Data [ "CanWriteActions" ] = ctx . Repo . CanWrite ( unit_model . TypeActions )
2014-04-14 05:00:12 +04:00
2023-09-14 20:09:32 +03:00
canSignedUserFork , err := repo_module . CanUserForkRepo ( ctx , ctx . Doer , ctx . Repo . Repository )
2021-12-13 04:59:09 +03:00
if err != nil {
ctx . ServerError ( "CanUserForkRepo" , err )
2024-12-24 06:43:57 +03:00
return
2021-12-13 04:59:09 +03:00
}
ctx . Data [ "CanSignedUserFork" ] = canSignedUserFork
2022-06-06 11:01:49 +03:00
userAndOrgForks , err := repo_model . GetForksByUserAndOrgs ( ctx , ctx . Doer , ctx . Repo . Repository )
2021-12-13 04:59:09 +03:00
if err != nil {
ctx . ServerError ( "GetForksByUserAndOrgs" , err )
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
2021-12-13 04:59:09 +03:00
ctx . Data [ "UserAndOrgForks" ] = userAndOrgForks
// canSignedUserFork is true if the current user doesn't have a fork of this repo yet or
// if he owns an org that doesn't have a fork of this repo yet
// If multiple forks are available or if the user can fork to another account, but there is already a fork: open selection dialog
ctx . Data [ "ShowForkModal" ] = len ( userAndOrgForks ) > 1 || ( canSignedUserFork && len ( userAndOrgForks ) > 0 )
2017-10-15 18:06:07 +03:00
2022-03-29 06:21:30 +03:00
ctx . Data [ "RepoCloneLink" ] = repo . CloneLink ( )
cloneButtonShowHTTPS := ! setting . Repository . DisableHTTPGit
cloneButtonShowSSH := ! setting . SSH . Disabled && ( ctx . IsSigned || setting . SSH . ExposeAnonymous )
if ! cloneButtonShowHTTPS && ! cloneButtonShowSSH {
// We have to show at least one link, so we just show the HTTPS
cloneButtonShowHTTPS = true
}
ctx . Data [ "CloneButtonShowHTTPS" ] = cloneButtonShowHTTPS
ctx . Data [ "CloneButtonShowSSH" ] = cloneButtonShowSSH
ctx . Data [ "CloneButtonOriginLink" ] = ctx . Data [ "RepoCloneLink" ] // it may be rewritten to the WikiCloneLink by the router middleware
2021-04-10 03:26:08 +03:00
ctx . Data [ "RepoSearchEnabled" ] = setting . Indexer . RepoIndexerEnabled
2022-01-27 11:30:51 +03:00
if setting . Indexer . RepoIndexerEnabled {
Refactor indexer (#25174)
Refactor `modules/indexer` to make it more maintainable. And it can be
easier to support more features. I'm trying to solve some of issue
searching, this is a precursor to making functional changes.
Current supported engines and the index versions:
| engines | issues | code |
| - | - | - |
| db | Just a wrapper for database queries, doesn't need version | - |
| bleve | The version of index is **2** | The version of index is **6**
|
| elasticsearch | The old index has no version, will be treated as
version **0** in this PR | The version of index is **1** |
| meilisearch | The old index has no version, will be treated as version
**0** in this PR | - |
## Changes
### Split
Splited it into mutiple packages
```text
indexer
├── internal
│ ├── bleve
│ ├── db
│ ├── elasticsearch
│ └── meilisearch
├── code
│ ├── bleve
│ ├── elasticsearch
│ └── internal
└── issues
├── bleve
├── db
├── elasticsearch
├── internal
└── meilisearch
```
- `indexer/interanal`: Internal shared package for indexer.
- `indexer/interanal/[engine]`: Internal shared package for each engine
(bleve/db/elasticsearch/meilisearch).
- `indexer/code`: Implementations for code indexer.
- `indexer/code/internal`: Internal shared package for code indexer.
- `indexer/code/[engine]`: Implementation via each engine for code
indexer.
- `indexer/issues`: Implementations for issues indexer.
### Deduplication
- Combine `Init/Ping/Close` for code indexer and issues indexer.
- ~Combine `issues.indexerHolder` and `code.wrappedIndexer` to
`internal.IndexHolder`.~ Remove it, use dummy indexer instead when the
indexer is not ready.
- Duplicate two copies of creating ES clients.
- Duplicate two copies of `indexerID()`.
### Enhancement
- [x] Support index version for elasticsearch issues indexer, the old
index without version will be treated as version 0.
- [x] Fix spell of `elastic_search/ElasticSearch`, it should be
`Elasticsearch`.
- [x] Improve versioning of ES index. We don't need `Aliases`:
- Gitea does't need aliases for "Zero Downtime" because it never delete
old indexes.
- The old code of issues indexer uses the orignal name to create issue
index, so it's tricky to convert it to an alias.
- [x] Support index version for meilisearch issues indexer, the old
index without version will be treated as version 0.
- [x] Do "ping" only when `Ping` has been called, don't ping
periodically and cache the status.
- [x] Support the context parameter whenever possible.
- [x] Fix outdated example config.
- [x] Give up the requeue logic of issues indexer: When indexing fails,
call Ping to check if it was caused by the engine being unavailable, and
only requeue the task if the engine is unavailable.
- It is fragile and tricky, could cause data losing (It did happen when
I was doing some tests for this PR). And it works for ES only.
- Just always requeue the failed task, if it caused by bad data, it's a
bug of Gitea which should be fixed.
---------
Co-authored-by: Giteabot <teabot@gitea.io>
2023-06-23 15:37:56 +03:00
ctx . Data [ "CodeIndexerUnavailable" ] = ! code_indexer . IsAvailable ( ctx )
2022-01-27 11:30:51 +03:00
}
2014-03-30 07:38:41 +04:00
2021-04-10 03:26:08 +03:00
if ctx . IsSigned {
2023-09-15 09:13:19 +03:00
ctx . Data [ "IsWatchingRepo" ] = repo_model . IsWatching ( ctx , ctx . Doer . ID , repo . ID )
2022-05-20 17:08:52 +03:00
ctx . Data [ "IsStaringRepo" ] = repo_model . IsStaring ( ctx , ctx . Doer . ID , repo . ID )
2021-04-10 03:26:08 +03:00
}
2015-10-03 02:58:36 +03:00
2021-04-10 03:26:08 +03:00
if repo . IsFork {
RetrieveBaseRepo ( ctx , repo )
if ctx . Written ( ) {
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
}
2019-04-15 23:48:35 +03:00
2021-04-10 03:26:08 +03:00
if repo . IsGenerated ( ) {
RetrieveTemplateRepo ( ctx , repo )
if ctx . Written ( ) {
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
}
2014-03-15 20:03:23 +04:00
2024-10-02 07:37:16 +03:00
isHomeOrSettings := ctx . Link == ctx . Repo . RepoLink ||
ctx . Link == ctx . Repo . RepoLink + "/settings" ||
strings . HasPrefix ( ctx . Link , ctx . Repo . RepoLink + "/settings/" ) ||
ctx . Link == ctx . Repo . RepoLink + "/-/migrate/status"
2021-11-23 01:32:16 +03:00
2021-04-10 03:26:08 +03:00
// Disable everything when the repo is being created
2021-11-23 01:32:16 +03:00
if ctx . Repo . Repository . IsBeingCreated ( ) || ctx . Repo . Repository . IsBroken ( ) {
2021-04-10 03:26:08 +03:00
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
2021-11-23 01:32:16 +03:00
if ! isHomeOrSettings {
ctx . Redirect ( ctx . Repo . RepoLink )
}
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
2019-11-13 10:01:19 +03:00
2024-12-24 06:43:57 +03:00
if ctx . Repo . GitRepo != nil {
if ! setting . IsProd || setting . IsInTesting {
panic ( "RepoAssignment: GitRepo should be nil" )
}
_ = ctx . Repo . GitRepo . Close ( )
ctx . Repo . GitRepo = nil
}
2024-12-31 07:22:09 +03:00
ctx . Repo . GitRepo , err = gitrepo . RepositoryFromRequestContextOrOpen ( ctx , repo )
2021-04-10 03:26:08 +03:00
if err != nil {
2021-11-23 01:32:16 +03:00
if strings . Contains ( err . Error ( ) , "repository does not exist" ) || strings . Contains ( err . Error ( ) , "no such file or directory" ) {
log . Error ( "Repository %-v has a broken repository on the file system: %s Error: %v" , ctx . Repo . Repository , ctx . Repo . Repository . RepoPath ( ) , err )
2023-04-19 16:40:42 +03:00
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
2021-11-23 01:32:16 +03:00
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
// Only allow access to base of repo or settings
if ! isHomeOrSettings {
ctx . Redirect ( ctx . Repo . RepoLink )
}
2024-12-24 06:43:57 +03:00
return
2021-11-23 01:32:16 +03:00
}
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
ctx . ServerError ( "RepoAssignment Invalid repo " + repo . FullName ( ) , err )
2024-12-24 06:43:57 +03:00
return
2021-05-06 02:30:25 +03:00
}
2019-10-13 16:23:14 +03:00
2021-04-10 03:26:08 +03:00
// Stop at this point when the repo is empty.
if ctx . Repo . Repository . IsEmpty {
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
2016-03-07 07:57:46 +03:00
2023-06-29 13:03:20 +03:00
branchOpts := git_model . FindBranchOptions {
RepoID : ctx . Repo . Repository . ID ,
2024-02-23 05:18:33 +03:00
IsDeletedBranch : optional . Some ( false ) ,
2023-12-11 11:56:48 +03:00
ListOptions : db . ListOptionsAll ,
2023-06-29 13:03:20 +03:00
}
2023-12-11 11:56:48 +03:00
branchesTotal , err := db . Count [ git_model . Branch ] ( ctx , branchOpts )
2023-06-29 13:03:20 +03:00
if err != nil {
ctx . ServerError ( "CountBranches" , err )
2024-12-24 06:43:57 +03:00
return
2023-06-29 13:03:20 +03:00
}
2023-07-21 14:20:04 +03:00
// non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
2023-06-29 13:03:20 +03:00
if branchesTotal == 0 { // fallback to do a sync immediately
branchesTotal , err = repo_module . SyncRepoBranches ( ctx , ctx . Repo . Repository . ID , 0 )
if err != nil {
ctx . ServerError ( "SyncRepoBranches" , err )
2024-12-24 06:43:57 +03:00
return
2023-06-29 13:03:20 +03:00
}
}
ctx . Data [ "BranchesCount" ] = branchesTotal
2021-03-01 03:47:30 +03:00
2023-07-21 14:20:04 +03:00
// If no branch is set in the request URL, try to guess a default one.
2021-04-10 03:26:08 +03:00
if len ( ctx . Repo . BranchName ) == 0 {
2024-12-24 06:43:57 +03:00
if len ( ctx . Repo . Repository . DefaultBranch ) > 0 && ctx . Repo . GitRepo . IsBranchExist ( ctx . Repo . Repository . DefaultBranch ) {
2021-04-10 03:26:08 +03:00
ctx . Repo . BranchName = ctx . Repo . Repository . DefaultBranch
2023-07-21 14:20:04 +03:00
} else {
2024-03-08 10:30:10 +03:00
ctx . Repo . BranchName , _ = gitrepo . GetDefaultBranch ( ctx , ctx . Repo . Repository )
2023-07-21 14:20:04 +03:00
if ctx . Repo . BranchName == "" {
// If it still can't get a default branch, fall back to default branch from setting.
// Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
ctx . Repo . BranchName = setting . Repository . DefaultBranch
}
2021-04-10 03:26:08 +03:00
}
2021-11-18 02:50:17 +03:00
ctx . Repo . RefName = ctx . Repo . BranchName
2021-04-10 03:26:08 +03:00
}
ctx . Data [ "BranchName" ] = ctx . Repo . BranchName
// People who have push access or have forked repository can propose a new pull request.
2021-11-22 18:21:55 +03:00
canPush := ctx . Repo . CanWrite ( unit_model . TypeCode ) ||
2023-09-14 20:09:32 +03:00
( ctx . IsSigned && repo_model . HasForkedRepo ( ctx , ctx . Doer . ID , ctx . Repo . Repository . ID ) )
2021-04-10 03:26:08 +03:00
canCompare := false
// Pull request is allowed if this is a fork repository
// and base repository accepts pull requests.
2023-10-11 07:24:07 +03:00
if repo . BaseRepo != nil && repo . BaseRepo . AllowsPulls ( ctx ) {
2021-04-10 03:26:08 +03:00
canCompare = true
ctx . Data [ "BaseRepo" ] = repo . BaseRepo
ctx . Repo . PullRequest . BaseRepo = repo . BaseRepo
ctx . Repo . PullRequest . Allowed = canPush
2023-10-11 07:24:07 +03:00
} else if repo . AllowsPulls ( ctx ) {
2021-04-10 03:26:08 +03:00
// Or, this is repository accepts pull requests between branches.
canCompare = true
ctx . Data [ "BaseRepo" ] = repo
ctx . Repo . PullRequest . BaseRepo = repo
ctx . Repo . PullRequest . Allowed = canPush
ctx . Repo . PullRequest . SameRepo = true
}
ctx . Data [ "CanCompareOrPull" ] = canCompare
ctx . Data [ "PullRequestCtx" ] = ctx . Repo . PullRequest
2021-12-10 04:27:50 +03:00
if ctx . Repo . Repository . Status == repo_model . RepositoryPendingTransfer {
2024-12-18 06:44:16 +03:00
repoTransfer , err := repo_model . GetPendingRepositoryTransfer ( ctx , ctx . Repo . Repository )
2021-04-10 03:26:08 +03:00
if err != nil {
ctx . ServerError ( "GetPendingRepositoryTransfer" , err )
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
2021-03-01 03:47:30 +03:00
2022-12-10 05:46:31 +03:00
if err := repoTransfer . LoadAttributes ( ctx ) ; err != nil {
2021-04-10 03:26:08 +03:00
ctx . ServerError ( "LoadRecipient" , err )
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
ctx . Data [ "RepoTransfer" ] = repoTransfer
2022-03-22 10:03:22 +03:00
if ctx . Doer != nil {
2023-09-16 17:39:12 +03:00
ctx . Data [ "CanUserAcceptTransfer" ] = repoTransfer . CanUserAcceptTransfer ( ctx , ctx . Doer )
2021-04-10 03:26:08 +03:00
}
}
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2024-11-05 09:35:54 +03:00
ctx . Data [ "GoGetImport" ] = ComposeGoGetImport ( ctx , owner . Name , repo . Name )
2023-02-11 09:34:11 +03:00
fullURLPrefix := repo . HTMLURL ( ) + "/src/branch/" + util . PathEscapeSegments ( ctx . Repo . BranchName )
ctx . Data [ "GoDocDirectory" ] = fullURLPrefix + "{/dir}"
ctx . Data [ "GoDocFile" ] = fullURLPrefix + "{/dir}/{file}#L{line}"
2014-03-15 20:03:23 +04:00
}
}
2014-05-06 03:58:13 +04:00
2017-10-30 05:04:25 +03:00
// RepoRefType type of repo reference
type RepoRefType int
const (
2024-11-05 09:35:54 +03:00
// RepoRefUnknown is for legacy support, makes the code to "guess" the ref type
RepoRefUnknown RepoRefType = iota
2017-10-30 05:04:25 +03:00
RepoRefBranch
RepoRefTag
RepoRefCommit
2018-11-18 21:45:40 +03:00
RepoRefBlob
2017-10-30 05:04:25 +03:00
)
2023-10-03 10:37:06 +03:00
const headRefName = "HEAD"
2017-10-30 05:04:25 +03:00
// RepoRef handles repository reference names when the ref name is not
// explicitly given
2024-12-24 06:43:57 +03:00
func RepoRef ( ) func ( * Context ) {
2017-10-30 05:04:25 +03:00
// since no ref name is explicitly specified, ok to just use branch
return RepoRefByType ( RepoRefBranch )
}
2024-04-29 11:47:56 +03:00
func getRefNameFromPath ( repo * Repository , path string , isExist func ( string ) bool ) string {
2017-10-30 05:04:25 +03:00
refName := ""
parts := strings . Split ( path , "/" )
for i , part := range parts {
refName = strings . TrimPrefix ( refName + "/" + part , "/" )
if isExist ( refName ) {
2023-05-21 04:50:53 +03:00
repo . TreePath = strings . Join ( parts [ i + 1 : ] , "/" )
2017-10-30 05:04:25 +03:00
return refName
}
}
return ""
}
2024-11-05 09:35:54 +03:00
func getRefNameLegacy ( ctx * Base , repo * Repository , optionalExtraRef ... string ) ( string , RepoRefType ) {
extraRef := util . OptionalArg ( optionalExtraRef )
reqPath := ctx . PathParam ( "*" )
reqPath = path . Join ( extraRef , reqPath )
if refName := getRefName ( ctx , repo , RepoRefBranch ) ; refName != "" {
return refName , RepoRefBranch
}
if refName := getRefName ( ctx , repo , RepoRefTag ) ; refName != "" {
return refName , RepoRefTag
}
// For legacy support only full commit sha
parts := strings . Split ( reqPath , "/" )
2024-12-12 11:10:09 +03:00
if git . IsStringLikelyCommitID ( git . ObjectFormatFromName ( repo . Repository . ObjectFormatName ) , parts [ 0 ] ) {
2024-11-05 09:35:54 +03:00
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
return parts [ 0 ] , RepoRefCommit
}
if refName := getRefName ( ctx , repo , RepoRefBlob ) ; len ( refName ) > 0 {
return refName , RepoRefBlob
}
repo . TreePath = reqPath
return repo . Repository . DefaultBranch , RepoRefBranch
}
2023-05-21 04:50:53 +03:00
func getRefName ( ctx * Base , repo * Repository , pathType RepoRefType ) string {
2024-06-19 01:32:45 +03:00
path := ctx . PathParam ( "*" )
2017-10-30 05:04:25 +03:00
switch pathType {
case RepoRefBranch :
2024-04-29 11:47:56 +03:00
ref := getRefNameFromPath ( repo , path , repo . GitRepo . IsBranchExist )
2021-10-08 20:03:04 +03:00
if len ( ref ) == 0 {
2023-10-03 10:37:06 +03:00
// check if ref is HEAD
parts := strings . Split ( path , "/" )
if parts [ 0 ] == headRefName {
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
return repo . Repository . DefaultBranch
}
2021-10-08 20:03:04 +03:00
// maybe it's a renamed branch
2024-04-29 11:47:56 +03:00
return getRefNameFromPath ( repo , path , func ( s string ) bool {
2023-05-21 04:50:53 +03:00
b , exist , err := git_model . FindRenamedBranch ( ctx , repo . Repository . ID , s )
2021-10-08 20:03:04 +03:00
if err != nil {
2023-11-08 16:50:20 +03:00
log . Error ( "FindRenamedBranch: %v" , err )
2021-10-08 20:03:04 +03:00
return false
}
if ! exist {
return false
}
ctx . Data [ "IsRenamedBranch" ] = true
ctx . Data [ "RenamedBranchName" ] = b . To
return true
} )
}
return ref
2017-10-30 05:04:25 +03:00
case RepoRefTag :
2024-04-29 11:47:56 +03:00
return getRefNameFromPath ( repo , path , repo . GitRepo . IsTagExist )
2017-10-30 05:04:25 +03:00
case RepoRefCommit :
parts := strings . Split ( path , "/" )
2024-12-12 11:10:09 +03:00
if git . IsStringLikelyCommitID ( repo . GetObjectFormat ( ) , parts [ 0 ] , 7 ) {
2024-11-05 09:35:54 +03:00
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
2023-05-21 04:50:53 +03:00
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
2017-10-30 05:04:25 +03:00
return parts [ 0 ]
}
2023-10-03 10:37:06 +03:00
2024-11-05 09:35:54 +03:00
if parts [ 0 ] == headRefName {
2023-10-03 10:37:06 +03:00
// HEAD ref points to last default branch commit
commit , err := repo . GitRepo . GetBranchCommit ( repo . Repository . DefaultBranch )
if err != nil {
return ""
}
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
return commit . ID . String ( )
}
2018-11-18 21:45:40 +03:00
case RepoRefBlob :
2023-05-21 04:50:53 +03:00
_ , err := repo . GitRepo . GetBlob ( path )
2018-11-18 21:45:40 +03:00
if err != nil {
return ""
}
return path
2017-10-30 05:04:25 +03:00
default :
2024-11-05 09:35:54 +03:00
panic ( fmt . Sprintf ( "Unrecognized path type: %v" , pathType ) )
2017-10-30 05:04:25 +03:00
}
return ""
}
2024-11-05 09:35:54 +03:00
type RepoRefByTypeOptions struct {
IgnoreNotExistErr bool
}
2017-10-30 05:04:25 +03:00
// RepoRefByType handles repository reference name for a specific type
// of repository reference
2024-12-24 06:43:57 +03:00
func RepoRefByType ( detectRefType RepoRefType , opts ... RepoRefByTypeOptions ) func ( * Context ) {
2024-11-05 09:35:54 +03:00
opt := util . OptionalArg ( opts )
2024-12-24 06:43:57 +03:00
return func ( ctx * Context ) {
2024-11-05 09:35:54 +03:00
refType := detectRefType
2021-04-10 03:26:08 +03:00
// Empty repository does not have reference information.
if ctx . Repo . Repository . IsEmpty {
2023-04-19 16:40:42 +03:00
// assume the user is viewing the (non-existent) default branch
ctx . Repo . IsViewBranch = true
ctx . Repo . BranchName = ctx . Repo . Repository . DefaultBranch
ctx . Data [ "TreePath" ] = ""
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
var (
refName string
err error
)
if ctx . Repo . GitRepo == nil {
2024-12-31 07:22:09 +03:00
ctx . Repo . GitRepo , err = gitrepo . RepositoryFromRequestContextOrOpen ( ctx , ctx . Repo . Repository )
2021-04-10 03:26:08 +03:00
if err != nil {
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-27 23:09:51 +03:00
ctx . ServerError ( fmt . Sprintf ( "Open Repository %v failed" , ctx . Repo . Repository . FullName ( ) ) , err )
2024-12-24 06:43:57 +03:00
return
2021-05-06 02:30:25 +03:00
}
2021-04-10 03:26:08 +03:00
}
2015-12-05 01:20:23 +03:00
2021-04-10 03:26:08 +03:00
// Get default branch.
2024-06-19 01:32:45 +03:00
if len ( ctx . PathParam ( "*" ) ) == 0 {
2021-04-10 03:26:08 +03:00
refName = ctx . Repo . Repository . DefaultBranch
if ! ctx . Repo . GitRepo . IsBranchExist ( refName ) {
2023-06-29 13:03:20 +03:00
brs , _ , err := ctx . Repo . GitRepo . GetBranches ( 0 , 1 )
2023-04-19 16:40:42 +03:00
if err == nil && len ( brs ) != 0 {
2023-06-29 13:03:20 +03:00
refName = brs [ 0 ] . Name
2021-04-10 03:26:08 +03:00
} else if len ( brs ) == 0 {
2023-04-19 16:40:42 +03:00
log . Error ( "No branches in non-empty repository %s" , ctx . Repo . GitRepo . Path )
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
} else {
log . Error ( "GetBranches error: %v" , err )
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
}
2021-11-18 02:50:17 +03:00
ctx . Repo . RefName = refName
ctx . Repo . BranchName = refName
2021-04-10 03:26:08 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( refName )
2023-04-19 16:40:42 +03:00
if err == nil {
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
} else if strings . Contains ( err . Error ( ) , "fatal: not a git repository" ) || strings . Contains ( err . Error ( ) , "object does not exist" ) {
// if the repository is broken, we can continue to the handler code, to show "Settings -> Delete Repository" for end users
log . Error ( "GetBranchCommit: %v" , err )
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
} else {
2021-04-10 03:26:08 +03:00
ctx . ServerError ( "GetBranchCommit" , err )
2024-12-24 06:43:57 +03:00
return
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . Repo . IsViewBranch = true
} else {
2024-11-05 09:35:54 +03:00
guessLegacyPath := refType == RepoRefUnknown
if guessLegacyPath {
refName , refType = getRefNameLegacy ( ctx . Base , ctx . Repo )
} else {
refName = getRefName ( ctx . Base , ctx . Repo , refType )
}
2021-11-18 02:50:17 +03:00
ctx . Repo . RefName = refName
2021-10-08 20:03:04 +03:00
isRenamedBranch , has := ctx . Data [ "IsRenamedBranch" ] . ( bool )
if isRenamedBranch && has {
renamedBranchName := ctx . Data [ "RenamedBranchName" ] . ( string )
ctx . Flash . Info ( ctx . Tr ( "repo.branch.renamed" , refName , renamedBranchName ) )
2021-11-16 21:18:25 +03:00
link := setting . AppSubURL + strings . Replace ( ctx . Req . URL . EscapedPath ( ) , util . PathEscapeSegments ( refName ) , util . PathEscapeSegments ( renamedBranchName ) , 1 )
2021-10-08 20:03:04 +03:00
ctx . Redirect ( link )
2024-12-24 06:43:57 +03:00
return
2021-10-08 20:03:04 +03:00
}
2024-11-05 09:35:54 +03:00
if refType == RepoRefBranch && ctx . Repo . GitRepo . IsBranchExist ( refName ) {
2021-04-10 03:26:08 +03:00
ctx . Repo . IsViewBranch = true
2021-11-18 02:50:17 +03:00
ctx . Repo . BranchName = refName
2015-12-05 01:20:23 +03:00
2015-12-10 04:46:05 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( refName )
2015-12-05 01:20:23 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranchCommit" , err )
2024-12-24 06:43:57 +03:00
return
2015-12-05 01:20:23 +03:00
}
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
2024-11-05 09:35:54 +03:00
} else if refType == RepoRefTag && ctx . Repo . GitRepo . IsTagExist ( refName ) {
2021-04-10 03:26:08 +03:00
ctx . Repo . IsViewTag = true
2021-11-18 02:50:17 +03:00
ctx . Repo . TagName = refName
2021-04-10 03:26:08 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetTagCommit ( refName )
if err != nil {
2022-06-18 20:08:34 +03:00
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "GetTagCommit" , err )
2024-12-24 06:43:57 +03:00
return
2022-06-18 20:08:34 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . ServerError ( "GetTagCommit" , err )
2024-12-24 06:43:57 +03:00
return
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
2024-12-12 11:10:09 +03:00
} else if git . IsStringLikelyCommitID ( ctx . Repo . GetObjectFormat ( ) , refName , 7 ) {
2021-04-10 03:26:08 +03:00
ctx . Repo . IsViewCommit = true
ctx . Repo . CommitID = refName
2015-12-05 01:20:23 +03:00
2021-04-10 03:26:08 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetCommit ( refName )
if err != nil {
ctx . NotFound ( "GetCommit" , err )
2024-12-24 06:43:57 +03:00
return
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
// If short commit ID add canonical link header
2024-02-24 09:55:19 +03:00
if len ( refName ) < ctx . Repo . GetObjectFormat ( ) . FullLength ( ) {
2024-11-05 09:35:54 +03:00
canonicalURL := util . URLJoin ( httplib . GuessCurrentAppURL ( ctx ) , strings . Replace ( ctx . Req . URL . RequestURI ( ) , util . PathEscapeSegments ( refName ) , url . PathEscape ( ctx . Repo . Commit . ID . String ( ) ) , 1 ) )
ctx . RespHeader ( ) . Set ( "Link" , fmt . Sprintf ( ` <%s>; rel="canonical" ` , canonicalURL ) )
2021-04-10 03:26:08 +03:00
}
} else {
2024-11-05 09:35:54 +03:00
if opt . IgnoreNotExistErr {
2024-12-24 06:43:57 +03:00
return
2021-05-06 06:12:50 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . NotFound ( "RepoRef invalid repo" , fmt . Errorf ( "branch or tag not exist: %s" , refName ) )
2024-12-24 06:43:57 +03:00
return
2015-12-05 01:20:23 +03:00
}
2017-10-30 05:04:25 +03:00
2024-11-05 09:35:54 +03:00
if guessLegacyPath {
2021-04-10 03:26:08 +03:00
// redirect from old URL scheme to new URL scheme
2024-06-19 01:32:45 +03:00
prefix := strings . TrimPrefix ( setting . AppSubURL + strings . ToLower ( strings . TrimSuffix ( ctx . Req . URL . Path , ctx . PathParam ( "*" ) ) ) , strings . ToLower ( ctx . Repo . RepoLink ) )
2024-06-18 02:28:47 +03:00
redirect := path . Join (
2021-11-16 21:18:25 +03:00
ctx . Repo . RepoLink ,
util . PathEscapeSegments ( prefix ) ,
2021-04-10 03:26:08 +03:00
ctx . Repo . BranchNameSubURL ( ) ,
2024-06-18 02:28:47 +03:00
util . PathEscapeSegments ( ctx . Repo . TreePath ) )
ctx . Redirect ( redirect )
2024-12-24 06:43:57 +03:00
return
2017-10-30 05:04:25 +03:00
}
2021-04-10 03:26:08 +03:00
}
2015-12-05 01:20:23 +03:00
2021-04-10 03:26:08 +03:00
ctx . Data [ "BranchName" ] = ctx . Repo . BranchName
2022-08-26 16:07:06 +03:00
ctx . Data [ "RefName" ] = ctx . Repo . RefName
2021-04-10 03:26:08 +03:00
ctx . Data [ "BranchNameSubURL" ] = ctx . Repo . BranchNameSubURL ( )
2021-11-18 02:50:17 +03:00
ctx . Data [ "TagName" ] = ctx . Repo . TagName
2021-04-10 03:26:08 +03:00
ctx . Data [ "CommitID" ] = ctx . Repo . CommitID
ctx . Data [ "TreePath" ] = ctx . Repo . TreePath
ctx . Data [ "IsViewBranch" ] = ctx . Repo . IsViewBranch
ctx . Data [ "IsViewTag" ] = ctx . Repo . IsViewTag
ctx . Data [ "IsViewCommit" ] = ctx . Repo . IsViewCommit
2024-12-06 17:29:04 +03:00
ctx . Data [ "CanCreateBranch" ] = ctx . Repo . CanCreateBranch ( ) // only used by the branch selector dropdown: AllowCreateNewRef
2021-04-10 03:26:08 +03:00
ctx . Repo . CommitsCount , err = ctx . Repo . GetCommitsCount ( )
if err != nil {
ctx . ServerError ( "GetCommitsCount" , err )
2024-12-24 06:43:57 +03:00
return
2021-04-10 03:26:08 +03:00
}
ctx . Data [ "CommitsCount" ] = ctx . Repo . CommitsCount
2022-07-25 18:39:42 +03:00
ctx . Repo . GitRepo . LastCommitCache = git . NewLastCommitCache ( ctx . Repo . CommitsCount , ctx . Repo . Repository . FullName ( ) , ctx . Repo . GitRepo , cache . GetCache ( ) )
2015-12-05 01:20:23 +03:00
}
}
2014-12-07 04:22:48 +03:00
// GitHookService checks if repository Git hooks service has been enabled.
2021-01-26 18:36:53 +03:00
func GitHookService ( ) func ( ctx * Context ) {
2014-10-07 01:50:00 +04:00
return func ( ctx * Context ) {
2022-03-22 10:03:22 +03:00
if ! ctx . Doer . CanEditGitHook ( ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GitHookService" , nil )
2014-10-07 01:50:00 +04:00
return
}
}
}
2024-10-11 22:08:19 +03:00
// canWriteAsMaintainer check if the doer can write to a branch as a maintainer
func canWriteAsMaintainer ( ctx * Context ) bool {
branchName := getRefNameFromPath ( ctx . Repo , ctx . PathParam ( "*" ) , func ( branchName string ) bool {
return issues_model . CanMaintainerWriteToBranch ( ctx , ctx . Repo . Permission , branchName , ctx . Doer )
} )
return len ( branchName ) > 0
}