@@ -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