@@ -25,6 +25,7 @@ import (
2525 "code.gitea.io/gitea/modules/cache"
2626 "code.gitea.io/gitea/modules/git"
2727 "code.gitea.io/gitea/modules/gitrepo"
28+ "code.gitea.io/gitea/modules/httplib"
2829 code_indexer "code.gitea.io/gitea/modules/indexer/code"
2930 "code.gitea.io/gitea/modules/log"
3031 "code.gitea.io/gitea/modules/optional"
@@ -306,11 +307,9 @@ func RetrieveTemplateRepo(ctx *Context, repo *repo_model.Repository) {
306307}
307308
308309// ComposeGoGetImport returns go-get-import meta content.
309- func ComposeGoGetImport (owner , repo string ) string {
310- /// setting.AppUrl is guaranteed to be parse as url
311- appURL , _ := url .Parse (setting .AppURL )
312-
313- return path .Join (appURL .Host , setting .AppSubURL , url .PathEscape (owner ), url .PathEscape (repo ))
310+ func ComposeGoGetImport (ctx context.Context , owner , repo string ) string {
311+ curAppURL , _ := url .Parse (httplib .GuessCurrentAppURL (ctx ))
312+ return path .Join (curAppURL .Host , setting .AppSubURL , url .PathEscape (owner ), url .PathEscape (repo ))
314313}
315314
316315// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
@@ -332,7 +331,7 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
332331 } else {
333332 cloneURL = repo_model .ComposeHTTPSCloneURL (username , reponame )
334333 }
335- goImportContent := fmt .Sprintf ("%s git %s" , ComposeGoGetImport (username , reponame ), cloneURL )
334+ goImportContent := fmt .Sprintf ("%s git %s" , ComposeGoGetImport (ctx , username , reponame ), cloneURL )
336335 htmlMeta := fmt .Sprintf (`<meta name="go-import" content="%s">` , html .EscapeString (goImportContent ))
337336 ctx .PlainText (http .StatusOK , htmlMeta )
338337}
@@ -744,7 +743,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
744743 }
745744
746745 if ctx .FormString ("go-get" ) == "1" {
747- ctx .Data ["GoGetImport" ] = ComposeGoGetImport (owner .Name , repo .Name )
746+ ctx .Data ["GoGetImport" ] = ComposeGoGetImport (ctx , owner .Name , repo .Name )
748747 fullURLPrefix := repo .HTMLURL () + "/src/branch/" + util .PathEscapeSegments (ctx .Repo .BranchName )
749748 ctx .Data ["GoDocDirectory" ] = fullURLPrefix + "{/dir}"
750749 ctx .Data ["GoDocFile" ] = fullURLPrefix + "{/dir}/{file}#L{line}"
@@ -756,19 +755,11 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
756755type RepoRefType int
757756
758757const (
759- // RepoRefLegacy unknown type, make educated guess and redirect.
760- // for backward compatibility with previous URL scheme
761- RepoRefLegacy RepoRefType = iota
762- // RepoRefAny is for usage where educated guess is needed
763- // but redirect can not be made
764- RepoRefAny
765- // RepoRefBranch branch
758+ // RepoRefUnknown is for legacy support, makes the code to "guess" the ref type
759+ RepoRefUnknown RepoRefType = iota
766760 RepoRefBranch
767- // RepoRefTag tag
768761 RepoRefTag
769- // RepoRefCommit commit
770762 RepoRefCommit
771- // RepoRefBlob blob
772763 RepoRefBlob
773764)
774765
@@ -781,22 +772,6 @@ func RepoRef() func(*Context) context.CancelFunc {
781772 return RepoRefByType (RepoRefBranch )
782773}
783774
784- // RefTypeIncludesBranches returns true if ref type can be a branch
785- func (rt RepoRefType ) RefTypeIncludesBranches () bool {
786- if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefBranch {
787- return true
788- }
789- return false
790- }
791-
792- // RefTypeIncludesTags returns true if ref type can be a tag
793- func (rt RepoRefType ) RefTypeIncludesTags () bool {
794- if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefTag {
795- return true
796- }
797- return false
798- }
799-
800775func getRefNameFromPath (repo * Repository , path string , isExist func (string ) bool ) string {
801776 refName := ""
802777 parts := strings .Split (path , "/" )
@@ -810,28 +785,50 @@ func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool
810785 return ""
811786}
812787
788+ func isStringLikelyCommitID (objFmt git.ObjectFormat , s string , minLength ... int ) bool {
789+ minLen := util .OptionalArg (minLength , objFmt .FullLength ())
790+ if len (s ) < minLen || len (s ) > objFmt .FullLength () {
791+ return false
792+ }
793+ for _ , c := range s {
794+ isHex := (c >= '0' && c <= '9' ) || (c >= 'a' && c <= 'f' )
795+ if ! isHex {
796+ return false
797+ }
798+ }
799+ return true
800+ }
801+
802+ func getRefNameLegacy (ctx * Base , repo * Repository , optionalExtraRef ... string ) (string , RepoRefType ) {
803+ extraRef := util .OptionalArg (optionalExtraRef )
804+ reqPath := ctx .PathParam ("*" )
805+ reqPath = path .Join (extraRef , reqPath )
806+
807+ if refName := getRefName (ctx , repo , RepoRefBranch ); refName != "" {
808+ return refName , RepoRefBranch
809+ }
810+ if refName := getRefName (ctx , repo , RepoRefTag ); refName != "" {
811+ return refName , RepoRefTag
812+ }
813+
814+ // For legacy support only full commit sha
815+ parts := strings .Split (reqPath , "/" )
816+ if isStringLikelyCommitID (git .ObjectFormatFromName (repo .Repository .ObjectFormatName ), parts [0 ]) {
817+ // FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
818+ repo .TreePath = strings .Join (parts [1 :], "/" )
819+ return parts [0 ], RepoRefCommit
820+ }
821+
822+ if refName := getRefName (ctx , repo , RepoRefBlob ); len (refName ) > 0 {
823+ return refName , RepoRefBlob
824+ }
825+ repo .TreePath = reqPath
826+ return repo .Repository .DefaultBranch , RepoRefBranch
827+ }
828+
813829func getRefName (ctx * Base , repo * Repository , pathType RepoRefType ) string {
814830 path := ctx .PathParam ("*" )
815831 switch pathType {
816- case RepoRefLegacy , RepoRefAny :
817- if refName := getRefName (ctx , repo , RepoRefBranch ); len (refName ) > 0 {
818- return refName
819- }
820- if refName := getRefName (ctx , repo , RepoRefTag ); len (refName ) > 0 {
821- return refName
822- }
823- // For legacy and API support only full commit sha
824- parts := strings .Split (path , "/" )
825-
826- if len (parts ) > 0 && len (parts [0 ]) == git .ObjectFormatFromName (repo .Repository .ObjectFormatName ).FullLength () {
827- repo .TreePath = strings .Join (parts [1 :], "/" )
828- return parts [0 ]
829- }
830- if refName := getRefName (ctx , repo , RepoRefBlob ); len (refName ) > 0 {
831- return refName
832- }
833- repo .TreePath = path
834- return repo .Repository .DefaultBranch
835832 case RepoRefBranch :
836833 ref := getRefNameFromPath (repo , path , repo .GitRepo .IsBranchExist )
837834 if len (ref ) == 0 {
@@ -866,13 +863,13 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
866863 return getRefNameFromPath (repo , path , repo .GitRepo .IsTagExist )
867864 case RepoRefCommit :
868865 parts := strings .Split (path , "/" )
869-
870- if len ( parts ) > 0 && len ( parts [ 0 ]) >= 7 && len ( parts [ 0 ]) <= repo . GetObjectFormat (). FullLength () {
866+ if isStringLikelyCommitID ( repo . GetObjectFormat (), parts [ 0 ], 7 ) {
867+ // FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
871868 repo .TreePath = strings .Join (parts [1 :], "/" )
872869 return parts [0 ]
873870 }
874871
875- if len ( parts ) > 0 && parts [0 ] == headRefName {
872+ if parts [0 ] == headRefName {
876873 // HEAD ref points to last default branch commit
877874 commit , err := repo .GitRepo .GetBranchCommit (repo .Repository .DefaultBranch )
878875 if err != nil {
@@ -888,15 +885,21 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
888885 }
889886 return path
890887 default :
891- log . Error ("Unrecognized path type: %v" , path )
888+ panic ( fmt . Sprintf ("Unrecognized path type: %v" , pathType ) )
892889 }
893890 return ""
894891}
895892
893+ type RepoRefByTypeOptions struct {
894+ IgnoreNotExistErr bool
895+ }
896+
896897// RepoRefByType handles repository reference name for a specific type
897898// of repository reference
898- func RepoRefByType (refType RepoRefType , ignoreNotExistErr ... bool ) func (* Context ) context.CancelFunc {
899+ func RepoRefByType (detectRefType RepoRefType , opts ... RepoRefByTypeOptions ) func (* Context ) context.CancelFunc {
900+ opt := util .OptionalArg (opts )
899901 return func (ctx * Context ) (cancel context.CancelFunc ) {
902+ refType := detectRefType
900903 // Empty repository does not have reference information.
901904 if ctx .Repo .Repository .IsEmpty {
902905 // assume the user is viewing the (non-existent) default branch
@@ -956,7 +959,12 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
956959 }
957960 ctx .Repo .IsViewBranch = true
958961 } else {
959- refName = getRefName (ctx .Base , ctx .Repo , refType )
962+ guessLegacyPath := refType == RepoRefUnknown
963+ if guessLegacyPath {
964+ refName , refType = getRefNameLegacy (ctx .Base , ctx .Repo )
965+ } else {
966+ refName = getRefName (ctx .Base , ctx .Repo , refType )
967+ }
960968 ctx .Repo .RefName = refName
961969 isRenamedBranch , has := ctx .Data ["IsRenamedBranch" ].(bool )
962970 if isRenamedBranch && has {
@@ -967,7 +975,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
967975 return cancel
968976 }
969977
970- if refType . RefTypeIncludesBranches () && ctx .Repo .GitRepo .IsBranchExist (refName ) {
978+ if refType == RepoRefBranch && ctx .Repo .GitRepo .IsBranchExist (refName ) {
971979 ctx .Repo .IsViewBranch = true
972980 ctx .Repo .BranchName = refName
973981
@@ -977,7 +985,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
977985 return cancel
978986 }
979987 ctx .Repo .CommitID = ctx .Repo .Commit .ID .String ()
980- } else if refType . RefTypeIncludesTags () && ctx .Repo .GitRepo .IsTagExist (refName ) {
988+ } else if refType == RepoRefTag && ctx .Repo .GitRepo .IsTagExist (refName ) {
981989 ctx .Repo .IsViewTag = true
982990 ctx .Repo .TagName = refName
983991
@@ -991,7 +999,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
991999 return cancel
9921000 }
9931001 ctx .Repo .CommitID = ctx .Repo .Commit .ID .String ()
994- } else if len ( refName ) >= 7 && len ( refName ) <= ctx .Repo .GetObjectFormat (). FullLength ( ) {
1002+ } else if isStringLikelyCommitID ( ctx .Repo .GetObjectFormat (), refName , 7 ) {
9951003 ctx .Repo .IsViewCommit = true
9961004 ctx .Repo .CommitID = refName
9971005
@@ -1002,18 +1010,18 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
10021010 }
10031011 // If short commit ID add canonical link header
10041012 if len (refName ) < ctx .Repo .GetObjectFormat ().FullLength () {
1005- ctx . RespHeader (). Set ( "Link" , fmt . Sprintf ( "<%s>; rel= \" canonical \" " ,
1006- util . URLJoin ( setting . AppURL , strings . Replace ( ctx .Req . URL . RequestURI (), util . PathEscapeSegments ( refName ), url . PathEscape ( ctx . Repo . Commit . ID . String ()), 1 )) ))
1013+ canonicalURL := util . URLJoin ( httplib . GuessCurrentAppURL ( ctx ), strings . Replace ( ctx . Req . URL . RequestURI (), util . PathEscapeSegments ( refName ), url . PathEscape ( ctx . Repo . Commit . ID . String ()), 1 ))
1014+ ctx .RespHeader (). Set ( "Link" , fmt . Sprintf ( `<%s>; rel="canonical"` , canonicalURL ))
10071015 }
10081016 } else {
1009- if len ( ignoreNotExistErr ) > 0 && ignoreNotExistErr [ 0 ] {
1017+ if opt . IgnoreNotExistErr {
10101018 return cancel
10111019 }
10121020 ctx .NotFound ("RepoRef invalid repo" , fmt .Errorf ("branch or tag not exist: %s" , refName ))
10131021 return cancel
10141022 }
10151023
1016- if refType == RepoRefLegacy {
1024+ if guessLegacyPath {
10171025 // redirect from old URL scheme to new URL scheme
10181026 prefix := strings .TrimPrefix (setting .AppSubURL + strings .ToLower (strings .TrimSuffix (ctx .Req .URL .Path , ctx .PathParam ("*" ))), strings .ToLower (ctx .Repo .RepoLink ))
10191027 redirect := path .Join (
0 commit comments