@ -25,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httplib"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/optional"
@ -306,11 +307,9 @@ func RetrieveTemplateRepo(ctx *Context, repo *repo_model.Repository) {
}
}
// ComposeGoGetImport returns go-get-import meta content.
// ComposeGoGetImport returns go-get-import meta content.
func ComposeGoGetImport ( owner , repo string ) string {
func ComposeGoGetImport ( ctx context . Context , owner , repo string ) string {
/// setting.AppUrl is guaranteed to be parse as url
curAppURL , _ := url . Parse ( httplib . GuessCurrentAppURL ( ctx ) )
appURL , _ := url . Parse ( setting . AppURL )
return path . Join ( curAppURL . Host , setting . AppSubURL , url . PathEscape ( owner ) , url . PathEscape ( repo ) )
return path . Join ( appURL . Host , setting . AppSubURL , url . PathEscape ( owner ) , url . PathEscape ( repo ) )
}
}
// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
@ -332,7 +331,7 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
} else {
} else {
cloneURL = repo_model . ComposeHTTPSCloneURL ( username , reponame )
cloneURL = repo_model . ComposeHTTPSCloneURL ( username , reponame )
}
}
goImportContent := fmt . Sprintf ( "%s git %s" , ComposeGoGetImport ( username , reponame ) , cloneURL )
goImportContent := fmt . Sprintf ( "%s git %s" , ComposeGoGetImport ( ctx , username , reponame ) , cloneURL )
htmlMeta := fmt . Sprintf ( ` <meta name="go-import" content="%s"> ` , html . EscapeString ( goImportContent ) )
htmlMeta := fmt . Sprintf ( ` <meta name="go-import" content="%s"> ` , html . EscapeString ( goImportContent ) )
ctx . PlainText ( http . StatusOK , htmlMeta )
ctx . PlainText ( http . StatusOK , htmlMeta )
}
}
@ -744,7 +743,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
}
}
if ctx . FormString ( "go-get" ) == "1" {
if ctx . FormString ( "go-get" ) == "1" {
ctx . Data [ "GoGetImport" ] = ComposeGoGetImport ( owner . Name , repo . Name )
ctx . Data [ "GoGetImport" ] = ComposeGoGetImport ( ctx , owner . Name , repo . Name )
fullURLPrefix := repo . HTMLURL ( ) + "/src/branch/" + util . PathEscapeSegments ( ctx . Repo . BranchName )
fullURLPrefix := repo . HTMLURL ( ) + "/src/branch/" + util . PathEscapeSegments ( ctx . Repo . BranchName )
ctx . Data [ "GoDocDirectory" ] = fullURLPrefix + "{/dir}"
ctx . Data [ "GoDocDirectory" ] = fullURLPrefix + "{/dir}"
ctx . Data [ "GoDocFile" ] = fullURLPrefix + "{/dir}/{file}#L{line}"
ctx . Data [ "GoDocFile" ] = fullURLPrefix + "{/dir}/{file}#L{line}"
@ -756,19 +755,11 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
type RepoRefType int
type RepoRefType int
const (
const (
// RepoRefLegacy unknown type, make educated guess and redirect.
// RepoRefUnknown is for legacy support, makes the code to "guess" the ref type
// for backward compatibility with previous URL scheme
RepoRefUnknown RepoRefType = iota
RepoRefLegacy RepoRefType = iota
// RepoRefAny is for usage where educated guess is needed
// but redirect can not be made
RepoRefAny
// RepoRefBranch branch
RepoRefBranch
RepoRefBranch
// RepoRefTag tag
RepoRefTag
RepoRefTag
// RepoRefCommit commit
RepoRefCommit
RepoRefCommit
// RepoRefBlob blob
RepoRefBlob
RepoRefBlob
)
)
@ -781,22 +772,6 @@ func RepoRef() func(*Context) context.CancelFunc {
return RepoRefByType ( RepoRefBranch )
return RepoRefByType ( RepoRefBranch )
}
}
// RefTypeIncludesBranches returns true if ref type can be a branch
func ( rt RepoRefType ) RefTypeIncludesBranches ( ) bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefBranch {
return true
}
return false
}
// RefTypeIncludesTags returns true if ref type can be a tag
func ( rt RepoRefType ) RefTypeIncludesTags ( ) bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefTag {
return true
}
return false
}
func getRefNameFromPath ( repo * Repository , path string , isExist func ( string ) bool ) string {
func getRefNameFromPath ( repo * Repository , path string , isExist func ( string ) bool ) string {
refName := ""
refName := ""
parts := strings . Split ( path , "/" )
parts := strings . Split ( path , "/" )
@ -810,28 +785,50 @@ func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool
return ""
return ""
}
}
func getRefName ( ctx * Base , repo * Repository , pathType RepoRefType ) string {
func isStringLikelyCommitID ( objFmt git . ObjectFormat , s string , minLength ... int ) bool {
path := ctx . PathParam ( "*" )
minLen := util . OptionalArg ( minLength , objFmt . FullLength ( ) )
switch pathType {
if len ( s ) < minLen || len ( s ) > objFmt . FullLength ( ) {
case RepoRefLegacy , RepoRefAny :
return false
if refName := getRefName ( ctx , repo , RepoRefBranch ) ; len ( refName ) > 0 {
return refName
}
}
if refName := getRefName ( ctx , repo , RepoRefTag ) ; len ( refName ) > 0 {
for _ , c := range s {
return refName
isHex := ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' )
if ! isHex {
return false
}
}
return true
}
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 and API support only full commit sha
parts := strings . Split ( path , "/" )
if len ( parts ) > 0 && len ( parts [ 0 ] ) == git . ObjectFormatFromName ( repo . Repository . ObjectFormatName ) . FullLength ( ) {
// For legacy support only full commit sha
parts := strings . Split ( reqPath , "/" )
if isStringLikelyCommitID ( git . ObjectFormatFromName ( repo . Repository . ObjectFormatName ) , parts [ 0 ] ) {
// 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 : ] , "/" )
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
return parts [ 0 ]
return parts [ 0 ] , RepoRefCommit
}
}
if refName := getRefName ( ctx , repo , RepoRefBlob ) ; len ( refName ) > 0 {
if refName := getRefName ( ctx , repo , RepoRefBlob ) ; len ( refName ) > 0 {
return refName
return refName , RepoRefBlob
}
}
repo . TreePath = path
repo . TreePath = reqPath
return repo . Repository . DefaultBranch
return repo . Repository . DefaultBranch , RepoRefBranch
}
func getRefName ( ctx * Base , repo * Repository , pathType RepoRefType ) string {
path := ctx . PathParam ( "*" )
switch pathType {
case RepoRefBranch :
case RepoRefBranch :
ref := getRefNameFromPath ( repo , path , repo . GitRepo . IsBranchExist )
ref := getRefNameFromPath ( repo , path , repo . GitRepo . IsBranchExist )
if len ( ref ) == 0 {
if len ( ref ) == 0 {
@ -866,13 +863,13 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
return getRefNameFromPath ( repo , path , repo . GitRepo . IsTagExist )
return getRefNameFromPath ( repo , path , repo . GitRepo . IsTagExist )
case RepoRefCommit :
case RepoRefCommit :
parts := strings . Split ( path , "/" )
parts := strings . Split ( path , "/" )
if isStringLikelyCommitID ( repo . GetObjectFormat ( ) , parts [ 0 ] , 7 ) {
if len ( parts ) > 0 && len ( parts [ 0 ] ) >= 7 && len ( parts [ 0 ] ) <= repo . GetObjectFormat ( ) . FullLength ( ) {
// 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 : ] , "/" )
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
return parts [ 0 ]
return parts [ 0 ]
}
}
if len ( parts ) > 0 && parts [ 0 ] == headRefName {
if parts [ 0 ] == headRefName {
// HEAD ref points to last default branch commit
// HEAD ref points to last default branch commit
commit , err := repo . GitRepo . GetBranchCommit ( repo . Repository . DefaultBranch )
commit , err := repo . GitRepo . GetBranchCommit ( repo . Repository . DefaultBranch )
if err != nil {
if err != nil {
@ -888,15 +885,21 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
}
}
return path
return path
default :
default :
log . Error ( "Unrecognized path type: %v" , path )
panic ( fmt . Sprintf ( "Unrecognized path type: %v" , pathType ) )
}
}
return ""
return ""
}
}
type RepoRefByTypeOptions struct {
IgnoreNotExistErr bool
}
// RepoRefByType handles repository reference name for a specific type
// RepoRefByType handles repository reference name for a specific type
// of repository reference
// of repository reference
func RepoRefByType ( refType RepoRefType , ignoreNotExistErr ... bool ) func ( * Context ) context . CancelFunc {
func RepoRefByType ( detectRefType RepoRefType , opts ... RepoRefByTypeOptions ) func ( * Context ) context . CancelFunc {
opt := util . OptionalArg ( opts )
return func ( ctx * Context ) ( cancel context . CancelFunc ) {
return func ( ctx * Context ) ( cancel context . CancelFunc ) {
refType := detectRefType
// Empty repository does not have reference information.
// Empty repository does not have reference information.
if ctx . Repo . Repository . IsEmpty {
if ctx . Repo . Repository . IsEmpty {
// assume the user is viewing the (non-existent) default branch
// assume the user is viewing the (non-existent) default branch
@ -955,8 +958,13 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return cancel
return cancel
}
}
ctx . Repo . IsViewBranch = true
ctx . Repo . IsViewBranch = true
} else {
guessLegacyPath := refType == RepoRefUnknown
if guessLegacyPath {
refName , refType = getRefNameLegacy ( ctx . Base , ctx . Repo )
} else {
} else {
refName = getRefName ( ctx . Base , ctx . Repo , refType )
refName = getRefName ( ctx . Base , ctx . Repo , refType )
}
ctx . Repo . RefName = refName
ctx . Repo . RefName = refName
isRenamedBranch , has := ctx . Data [ "IsRenamedBranch" ] . ( bool )
isRenamedBranch , has := ctx . Data [ "IsRenamedBranch" ] . ( bool )
if isRenamedBranch && has {
if isRenamedBranch && has {
@ -967,7 +975,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return cancel
return cancel
}
}
if refType . RefTypeIncludesBranches ( ) && ctx . Repo . GitRepo . IsBranchExist ( refName ) {
if refType == RepoRefBranch && ctx . Repo . GitRepo . IsBranchExist ( refName ) {
ctx . Repo . IsViewBranch = true
ctx . Repo . IsViewBranch = true
ctx . Repo . BranchName = refName
ctx . Repo . BranchName = refName
@ -977,7 +985,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return cancel
return cancel
}
}
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
} else if refType . RefTypeIncludesTags ( ) && ctx . Repo . GitRepo . IsTagExist ( refName ) {
} else if refType == RepoRefTag && ctx . Repo . GitRepo . IsTagExist ( refName ) {
ctx . Repo . IsViewTag = true
ctx . Repo . IsViewTag = true
ctx . Repo . TagName = refName
ctx . Repo . TagName = refName
@ -991,7 +999,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return cancel
return cancel
}
}
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
} else if len ( refName ) >= 7 && len ( refName ) <= ctx . Repo . GetObjectFormat ( ) . FullLength ( ) {
} else if isStringLikelyCommitID ( ctx . Repo . GetObjectFormat ( ) , refName , 7 ) {
ctx . Repo . IsViewCommit = true
ctx . Repo . IsViewCommit = true
ctx . Repo . CommitID = refName
ctx . Repo . CommitID = refName
@ -1002,18 +1010,18 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
}
}
// If short commit ID add canonical link header
// If short commit ID add canonical link header
if len ( refName ) < ctx . Repo . GetObjectFormat ( ) . FullLength ( ) {
if len ( refName ) < ctx . Repo . GetObjectFormat ( ) . FullLength ( ) {
ctx . RespHeader ( ) . Set ( "Link" , fmt . Sprintf ( "<%s>; rel=\"canonical\"" ,
canonicalURL := util . URLJoin ( httplib . GuessCurrentAppURL ( ctx ) , strings . Replace ( ctx . Req . URL . RequestURI ( ) , util . PathEscapeSegments ( refName ) , url . PathEscape ( ctx . Repo . Commit . ID . String ( ) ) , 1 ) )
util . URLJoin ( se tting . AppURL , strings . Replace ( ctx . Req . URL . RequestURI ( ) , util . PathEscapeSegments ( refName ) , url . PathEscape ( ctx . Repo . Commit . ID . String ( ) ) , 1 ) ) ) )
c tx . RespHeader ( ) . Set ( "Link" , fmt . Sprintf ( ` <%s>; rel="canonical" ` , canonicalURL ) )
}
}
} else {
} else {
if len ( ignoreNotExistErr ) > 0 && ignoreNotExistErr [ 0 ] {
if opt . IgnoreNotExistErr {
return cancel
return cancel
}
}
ctx . NotFound ( "RepoRef invalid repo" , fmt . Errorf ( "branch or tag not exist: %s" , refName ) )
ctx . NotFound ( "RepoRef invalid repo" , fmt . Errorf ( "branch or tag not exist: %s" , refName ) )
return cancel
return cancel
}
}
if refType == RepoRefLegacy {
if guessLegacyPath {
// redirect from old URL scheme to new URL scheme
// redirect from old URL scheme to new URL scheme
prefix := strings . TrimPrefix ( setting . AppSubURL + strings . ToLower ( strings . TrimSuffix ( ctx . Req . URL . Path , ctx . PathParam ( "*" ) ) ) , strings . ToLower ( ctx . Repo . RepoLink ) )
prefix := strings . TrimPrefix ( setting . AppSubURL + strings . ToLower ( strings . TrimSuffix ( ctx . Req . URL . Path , ctx . PathParam ( "*" ) ) ) , strings . ToLower ( ctx . Repo . RepoLink ) )
redirect := path . Join (
redirect := path . Join (