From dabfe1bcc02d4e2fee208051cf755258d48cf06f Mon Sep 17 00:00:00 2001
From: a1012112796 <1012112796@qq.com>
Date: Fri, 15 Aug 2025 11:51:37 +0800
Subject: [PATCH 1/4] support subdirectory config for go-get since go 1.25
ref: https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86
Signed-off-by: a1012112796 <1012112796@qq.com>
---
 models/repo/repo.go                  |  6 +++++
 models/repo/repo_unit.go             | 24 +++++++++++++++++-
 modules/structs/repo.go              |  2 ++
 options/locale/locale_en-US.ini      |  3 +++
 routers/api/v1/repo/repo.go          |  8 ++++++
 routers/web/goget.go                 | 18 +++++++++++++-
 routers/web/repo/setting/setting.go  |  4 ++-
 services/context/repo.go             |  7 ++++++
 services/forms/repo_form.go          |  2 ++
 templates/repo/settings/options.tmpl | 14 ++++++++++-
 templates/swagger/v1_json.tmpl       |  5 ++++
 tests/integration/goget_test.go      | 37 ++++++++++++++++++++++++++++
 12 files changed, 126 insertions(+), 4 deletions(-)
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 2403b3b40bafe..4708adec4195a 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -468,6 +468,12 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
 			Type:   tp,
 			Config: cfg,
 		}
+	case unit.TypePackages:
+		cfg := new(PackagesConfig)
+		return &RepoUnit{
+			Type:   tp,
+			Config: cfg,
+		}
 	}
 
 	return &RepoUnit{
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index a5207bc22a805..f8677854f3215 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -264,9 +264,11 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
 			r.Config = new(IssuesConfig)
 		case unit.TypeActions:
 			r.Config = new(ActionsConfig)
+		case unit.TypePackages:
+			r.Config = new(PackagesConfig)
 		case unit.TypeProjects:
 			r.Config = new(ProjectsConfig)
-		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypePackages:
+		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki:
 			fallthrough
 		default:
 			r.Config = new(UnitConfig)
@@ -319,6 +321,26 @@ func (r *RepoUnit) ProjectsConfig() *ProjectsConfig {
 	return r.Config.(*ProjectsConfig)
 }
 
+// PackagesConfig returns config for unit.PackagesConfig
+func (r *RepoUnit) PackagesConfig() *PackagesConfig {
+	return r.Config.(*PackagesConfig)
+}
+
+// PackagesConfig describes package config
+type PackagesConfig struct {
+	GoModuleSubDir string
+}
+
+// FromDB fills up a PackagesConfig from serialized format.
+func (cfg *PackagesConfig) FromDB(bs []byte) error {
+	return json.UnmarshalHandleDoubleEncode(bs, &cfg)
+}
+
+// ToDB exports a PackagesConfig to a serialized format.
+func (cfg *PackagesConfig) ToDB() ([]byte, error) {
+	return json.Marshal(cfg)
+}
+
 func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err error) {
 	var tmpUnits []*RepoUnit
 	if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil {
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 404718def0f81..67e7da926f74e 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -227,6 +227,8 @@ type EditRepoOption struct {
 	MirrorInterval *string `json:"mirror_interval,omitempty"`
 	// enable prune - remove obsolete remote-tracking references when mirroring
 	EnablePrune *bool `json:"enable_prune,omitempty"`
+	// set the subdirectory for Go modules
+	GoModuleSubDir *string `json:"go_module_sub_dir,omitempty"`
 }
 
 // GenerateRepoOption options when creating a repository using a template
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index d7e73a0cfbb08..b020c1be7c3ac 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -2629,6 +2629,9 @@ settings.rename_branch_from=old branch name
 settings.rename_branch_to=new branch name
 settings.rename_branch=Rename branch
 
+settings.go_module_sub_dir = Golang module subdirectory
+settings.go_module_sub_dir_desc = The subdirectory in repository redirect for go module (which is supported since go 1.25, see %[2]s for more detail)
+
 diff.browse_source = Browse Source
 diff.parent = parent
 diff.commit = commit
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index e69b7729a0fb0..0d737d4e97560 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -994,9 +994,17 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
 
 	if opts.HasPackages != nil && !unit_model.TypePackages.UnitGlobalDisabled() {
 		if *opts.HasPackages {
+			unit := repo.MustGetUnit(ctx, unit_model.TypePackages)
+			cfg := unit.PackagesConfig()
+
+			if opts.GoModuleSubDir != nil {
+				cfg.GoModuleSubDir = *opts.GoModuleSubDir
+			}
+
 			units = append(units, repo_model.RepoUnit{
 				RepoID: repo.ID,
 				Type:   unit_model.TypePackages,
+				Config: cfg,
 			})
 		} else {
 			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
diff --git a/routers/web/goget.go b/routers/web/goget.go
index 67e0bee866c12..c5ed7c871b32f 100644
--- a/routers/web/goget.go
+++ b/routers/web/goget.go
@@ -56,7 +56,18 @@ func goGet(ctx *context.Context) {
 	if err == nil && len(repo.DefaultBranch) > 0 {
 		branchName = repo.DefaultBranch
 	}
-	prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
+
+	subDir := ""
+	if repo != nil {
+		subDir = context.GetGoModuleSubDirConfig(ctx, repo)
+	}
+
+	var prefix string
+	if len(subDir) > 0 {
+		prefix = setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName), subDir)
+	} else {
+		prefix = setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
+	}
 
 	appURL, _ := url.Parse(setting.AppURL)
 
@@ -73,6 +84,11 @@ func goGet(ctx *context.Context) {
 	} else {
 		cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, ownerName, repoName)
 	}
+
+	if len(subDir) > 0 {
+		cloneURL += " " + util.PathEscapeSegments(subDir)
+	}
+
 	goImportContent := fmt.Sprintf("%s git %s", goGetImport, cloneURL /*CloneLink*/)
 	goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/)
 	goGetCli := fmt.Sprintf("go get %s%s", insecure, goGetImport)
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 0865d9d7c0f59..bba445f741f50 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -608,7 +608,9 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 	}
 
 	if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
-		units = append(units, newRepoUnit(repo, unit_model.TypePackages, nil))
+		units = append(units, newRepoUnit(repo, unit_model.TypePackages, &repo_model.PackagesConfig{
+			GoModuleSubDir: form.GoModuleSubDir,
+		}))
 	} else if !unit_model.TypePackages.UnitGlobalDisabled() {
 		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
 	}
diff --git a/services/context/repo.go b/services/context/repo.go
index afc6de9b1666d..adfd438c0e498 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -344,6 +344,13 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
 	ctx.PlainText(http.StatusOK, htmlMeta)
 }
 
+// GetGoModuleSubDirConfig retrieves the subdirectory configuration for a Go module.
+func GetGoModuleSubDirConfig(ctx *Context, repo *repo_model.Repository) string {
+	pkgCfg := repo.MustGetUnit(ctx, unit_model.TypePackages).PackagesConfig()
+
+	return strings.TrimSpace(pkgCfg.GoModuleSubDir)
+}
+
 // RedirectToRepo redirect to a differently-named repository
 func RedirectToRepo(ctx *Base, redirectRepoID int64) {
 	ownerName := ctx.PathParam("username")
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index cb267f891ccb7..89639a6d5d38e 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -150,6 +150,8 @@ type RepoSettingForm struct {
 
 	EnableActions bool
 
+	GoModuleSubDir string
+
 	IsArchived bool
 
 	// Signing Settings
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index fc42056e0a2bb..36aa90933cf17 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -499,16 +499,28 @@
 					
 				
 
+				
 				{{$isPackagesEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypePackages}}
 				{{$isPackagesGlobalDisabled := ctx.Consts.RepoUnitTypePackages.UnitGlobalDisabled}}
+				{{$pkgUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypePackages}}
 				
 
+				
+					
+						
+						
+						
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
+					
+				
-					
-						
-						
-						
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
-					
-				
+					
+				
+
+				
+					
+						
+						
+						
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
+					
+				
 					
From 26f9a9147bf3dc9b365d0b586fd43eee47e1b272 Mon Sep 17 00:00:00 2001
From: a1012112796 <1012112796@qq.com>
Date: Sun, 17 Aug 2025 11:43:23 +0800
Subject: [PATCH 3/4] Revert "maybe add a special unit named as `misc` to sore
 these special repo level configs"
This reverts commit 7d8595bad7e7388c94f1c1c46dcf2cb03260dd2d.
---
 models/repo/repo.go                  |  4 ++--
 models/repo/repo_unit.go             | 24 ++++++++++++------------
 models/unit/unit.go                  | 26 --------------------------
 options/locale/locale_en-US.ini      |  1 -
 routers/api/v1/repo/repo.go          | 24 ++++++++----------------
 routers/web/repo/setting/setting.go  |  8 +++-----
 services/context/context.go          |  1 -
 services/context/repo.go             |  4 ++--
 templates/repo/settings/options.tmpl | 28 +++++++++++++---------------
 9 files changed, 40 insertions(+), 80 deletions(-)
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 1818553c7b1aa..4708adec4195a 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -468,8 +468,8 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
 			Type:   tp,
 			Config: cfg,
 		}
-	case unit.TypeMisc:
-		cfg := new(MiscConfig)
+	case unit.TypePackages:
+		cfg := new(PackagesConfig)
 		return &RepoUnit{
 			Type:   tp,
 			Config: cfg,
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index 27966611eec10..f8677854f3215 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -264,11 +264,11 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
 			r.Config = new(IssuesConfig)
 		case unit.TypeActions:
 			r.Config = new(ActionsConfig)
+		case unit.TypePackages:
+			r.Config = new(PackagesConfig)
 		case unit.TypeProjects:
 			r.Config = new(ProjectsConfig)
-		case unit.TypeMisc:
-			r.Config = new(MiscConfig)
-		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypePackages:
+		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki:
 			fallthrough
 		default:
 			r.Config = new(UnitConfig)
@@ -321,23 +321,23 @@ func (r *RepoUnit) ProjectsConfig() *ProjectsConfig {
 	return r.Config.(*ProjectsConfig)
 }
 
-// MiscConfig returns config for unit.MiscConfig
-func (r *RepoUnit) MiscConfig() *MiscConfig {
-	return r.Config.(*MiscConfig)
+// PackagesConfig returns config for unit.PackagesConfig
+func (r *RepoUnit) PackagesConfig() *PackagesConfig {
+	return r.Config.(*PackagesConfig)
 }
 
-// MiscConfig describes repo misc config
-type MiscConfig struct {
+// PackagesConfig describes package config
+type PackagesConfig struct {
 	GoModuleSubDir string
 }
 
-// FromDB fills up a MiscConfig from serialized format.
-func (cfg *MiscConfig) FromDB(bs []byte) error {
+// FromDB fills up a PackagesConfig from serialized format.
+func (cfg *PackagesConfig) FromDB(bs []byte) error {
 	return json.UnmarshalHandleDoubleEncode(bs, &cfg)
 }
 
-// ToDB exports a MiscConfig to a serialized format.
-func (cfg *MiscConfig) ToDB() ([]byte, error) {
+// ToDB exports a PackagesConfig to a serialized format.
+func (cfg *PackagesConfig) ToDB() ([]byte, error) {
 	return json.Marshal(cfg)
 }
 
diff --git a/models/unit/unit.go b/models/unit/unit.go
index bf13491836782..c0560678ca9aa 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -14,7 +14,6 @@ import (
 	"code.gitea.io/gitea/modules/container"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/util"
 )
 
 // Type is Unit's Type
@@ -35,10 +34,6 @@ const (
 	TypePackages        // 9 Packages
 	TypeActions         // 10 Actions
 
-	// misc is a special uint to store special configs for a repo which is not related to any unit,
-	// it should be considered as enabled anyway
-	TypeMisc Type = 999 // 999 misc
-
 	// FIXME: TEAM-UNIT-PERMISSION: the team unit "admin" permission's design is not right, when a new unit is added in the future,
 	// admin team won't inherit the correct admin permission for the new unit, need to have a complete fix before adding any new unit.
 )
@@ -70,7 +65,6 @@ var (
 		TypeProjects,
 		TypePackages,
 		TypeActions,
-		TypeMisc,
 	}
 
 	// DefaultRepoUnits contains the default unit types
@@ -83,14 +77,12 @@ var (
 		TypeProjects,
 		TypePackages,
 		TypeActions,
-		TypeMisc,
 	}
 
 	// ForkRepoUnits contains the default unit types for forks
 	DefaultForkRepoUnits = []Type{
 		TypeCode,
 		TypePullRequests,
-		TypeMisc,
 	}
 
 	// DefaultMirrorRepoUnits contains the default unit types for mirrors
@@ -101,7 +93,6 @@ var (
 		TypeWiki,
 		TypeProjects,
 		TypePackages,
-		TypeMisc,
 	}
 
 	// DefaultTemplateRepoUnits contains the default unit types for templates
@@ -113,14 +104,12 @@ var (
 		TypeWiki,
 		TypeProjects,
 		TypePackages,
-		TypeMisc,
 	}
 
 	// NotAllowedDefaultRepoUnits contains units that can't be default
 	NotAllowedDefaultRepoUnits = []Type{
 		TypeExternalWiki,
 		TypeExternalTracker,
-		TypeMisc,
 	}
 
 	disabledRepoUnitsAtomic atomic.Pointer[[]Type] // the units that have been globally disabled
@@ -174,11 +163,6 @@ func LoadUnitConfig() error {
 	if len(invalidKeys) > 0 {
 		log.Warn("Invalid keys in disabled repo units: %s", strings.Join(invalidKeys, ", "))
 	}
-	if slices.Contains(disabledRepoUnits, TypeMisc) {
-		log.Warn("Misc unit should not be disabled")
-		disabledRepoUnits = util.SliceRemoveAll(disabledRepoUnits, TypeMisc)
-	}
-
 	DisabledRepoUnitsSet(disabledRepoUnits)
 
 	setDefaultRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultRepoUnits...)
@@ -344,15 +328,6 @@ var (
 		perm.AccessModeOwner,
 	}
 
-	UnitMisc = Unit{
-		TypeMisc,
-		"repo.misc",
-		"/misc",
-		"misc.unit.desc",
-		999,
-		perm.AccessModeOwner,
-	}
-
 	// Units contains all the units
 	Units = map[Type]Unit{
 		TypeCode:            UnitCode,
@@ -365,7 +340,6 @@ var (
 		TypeProjects:        UnitProjects,
 		TypePackages:        UnitPackages,
 		TypeActions:         UnitActions,
-		TypeMisc:            UnitMisc,
 	}
 )
 
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 74769e88bd24a..b020c1be7c3ac 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -2629,7 +2629,6 @@ settings.rename_branch_from=old branch name
 settings.rename_branch_to=new branch name
 settings.rename_branch=Rename branch
 
-misc = Misc
 settings.go_module_sub_dir = Golang module subdirectory
 settings.go_module_sub_dir_desc = The subdirectory in repository redirect for go module (which is supported since go 1.25, see 
%[2]s for more detail)
 
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index fc89a4d58a48f..0d737d4e97560 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -994,9 +994,17 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
 
 	if opts.HasPackages != nil && !unit_model.TypePackages.UnitGlobalDisabled() {
 		if *opts.HasPackages {
+			unit := repo.MustGetUnit(ctx, unit_model.TypePackages)
+			cfg := unit.PackagesConfig()
+
+			if opts.GoModuleSubDir != nil {
+				cfg.GoModuleSubDir = *opts.GoModuleSubDir
+			}
+
 			units = append(units, repo_model.RepoUnit{
 				RepoID: repo.ID,
 				Type:   unit_model.TypePackages,
+				Config: cfg,
 			})
 		} else {
 			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
@@ -1014,22 +1022,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
 		}
 	}
 
-	miscCfg := repo.MustGetUnit(ctx, unit_model.TypeMisc).MiscConfig()
-	misCfgUpdated := false
-
-	if opts.GoModuleSubDir != nil {
-		miscCfg.GoModuleSubDir = *opts.GoModuleSubDir
-		misCfgUpdated = true
-	}
-
-	if misCfgUpdated {
-		units = append(units, repo_model.RepoUnit{
-			RepoID: repo.ID,
-			Type:   unit_model.TypeMisc,
-			Config: miscCfg,
-		})
-	}
-
 	if len(units)+len(deleteUnitTypes) > 0 {
 		if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
 			ctx.APIErrorInternal(err)
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 5a23a75aff207..bba445f741f50 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -608,7 +608,9 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 	}
 
 	if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
-		units = append(units, newRepoUnit(repo, unit_model.TypePackages, nil))
+		units = append(units, newRepoUnit(repo, unit_model.TypePackages, &repo_model.PackagesConfig{
+			GoModuleSubDir: form.GoModuleSubDir,
+		}))
 	} else if !unit_model.TypePackages.UnitGlobalDisabled() {
 		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
 	}
@@ -638,10 +640,6 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
 	}
 
-	miscCfg := repo.MustGetUnit(ctx, unit_model.TypeMisc).MiscConfig()
-	miscCfg.GoModuleSubDir = form.GoModuleSubDir
-	units = append(units, newRepoUnit(repo, unit_model.TypeMisc, miscCfg))
-
 	if len(units) == 0 {
 		ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit"))
 		ctx.Redirect(ctx.Repo.RepoLink + "/settings")
diff --git a/services/context/context.go b/services/context/context.go
index 05d1b988f3d16..32ec260aab931 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -119,7 +119,6 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
 		"RepoUnitTypeProjects":        unit.TypeProjects,
 		"RepoUnitTypePackages":        unit.TypePackages,
 		"RepoUnitTypeActions":         unit.TypeActions,
-		"RepoUnitTypeMisc":            unit.TypeMisc,
 	}
 	return tmplCtx
 }
diff --git a/services/context/repo.go b/services/context/repo.go
index 41ca476d1fd82..adfd438c0e498 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -346,9 +346,9 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
 
 // GetGoModuleSubDirConfig retrieves the subdirectory configuration for a Go module.
 func GetGoModuleSubDirConfig(ctx *Context, repo *repo_model.Repository) string {
-	miscCfg := repo.MustGetUnit(ctx, unit_model.TypeMisc).MiscConfig()
+	pkgCfg := repo.MustGetUnit(ctx, unit_model.TypePackages).PackagesConfig()
 
-	return strings.TrimSpace(miscCfg.GoModuleSubDir)
+	return strings.TrimSpace(pkgCfg.GoModuleSubDir)
 }
 
 // RedirectToRepo redirect to a differently-named repository
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index d2f249aaf1ae7..36aa90933cf17 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -499,16 +499,28 @@
 					
 
 				
 
+				
 				{{$isPackagesEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypePackages}}
 				{{$isPackagesGlobalDisabled := ctx.Consts.RepoUnitTypePackages.UnitGlobalDisabled}}
+				{{$pkgUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypePackages}}
 				
 
+				
+					
+						
+						
+						
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
+					
+				
-					
-				
-
-				
-					
-						
-						
-						
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
-					
-				
 					
From 1b09fcb70e172cc320183a0a7c2d799f6000f97d Mon Sep 17 00:00:00 2001
From: a1012112796 <1012112796@qq.com>
Date: Sun, 17 Aug 2025 12:37:26 +0800
Subject: [PATCH 4/4] use unitCode
---
 models/repo/repo.go                  |  4 +--
 models/repo/repo_unit.go             | 45 +++++++++++++---------------
 routers/api/v1/repo/repo.go          | 16 +++++-----
 routers/web/repo/setting/setting.go  |  8 ++---
 services/context/repo.go             |  4 +--
 templates/repo/settings/options.tmpl | 28 ++++++++---------
 6 files changed, 49 insertions(+), 56 deletions(-)
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 4708adec4195a..fbb42b5e2db1e 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -468,8 +468,8 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
 			Type:   tp,
 			Config: cfg,
 		}
-	case unit.TypePackages:
-		cfg := new(PackagesConfig)
+	case unit.TypeCode:
+		cfg := new(CodeConfig)
 		return &RepoUnit{
 			Type:   tp,
 			Config: cfg,
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index f8677854f3215..4250560e5dac3 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -264,11 +264,11 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
 			r.Config = new(IssuesConfig)
 		case unit.TypeActions:
 			r.Config = new(ActionsConfig)
-		case unit.TypePackages:
-			r.Config = new(PackagesConfig)
 		case unit.TypeProjects:
 			r.Config = new(ProjectsConfig)
-		case unit.TypeCode, unit.TypeReleases, unit.TypeWiki:
+		case unit.TypeCode:
+			r.Config = new(CodeConfig)
+		case unit.TypeReleases, unit.TypeWiki, unit.TypePackages:
 			fallthrough
 		default:
 			r.Config = new(UnitConfig)
@@ -281,9 +281,24 @@ func (r *RepoUnit) Unit() unit.Unit {
 	return unit.Units[r.Type]
 }
 
+// CodeConfig describes code config
+type CodeConfig struct {
+	GoModuleSubDir string
+}
+
+// FromDB fills up a CodeConfig from serialized format.
+func (cfg *CodeConfig) FromDB(bs []byte) error {
+	return json.UnmarshalHandleDoubleEncode(bs, &cfg)
+}
+
+// ToDB exports a CodeConfig to a serialized format.
+func (cfg *CodeConfig) ToDB() ([]byte, error) {
+	return json.Marshal(cfg)
+}
+
 // CodeConfig returns config for unit.TypeCode
-func (r *RepoUnit) CodeConfig() *UnitConfig {
-	return r.Config.(*UnitConfig)
+func (r *RepoUnit) CodeConfig() *CodeConfig {
+	return r.Config.(*CodeConfig)
 }
 
 // PullRequestsConfig returns config for unit.TypePullRequests
@@ -321,26 +336,6 @@ func (r *RepoUnit) ProjectsConfig() *ProjectsConfig {
 	return r.Config.(*ProjectsConfig)
 }
 
-// PackagesConfig returns config for unit.PackagesConfig
-func (r *RepoUnit) PackagesConfig() *PackagesConfig {
-	return r.Config.(*PackagesConfig)
-}
-
-// PackagesConfig describes package config
-type PackagesConfig struct {
-	GoModuleSubDir string
-}
-
-// FromDB fills up a PackagesConfig from serialized format.
-func (cfg *PackagesConfig) FromDB(bs []byte) error {
-	return json.UnmarshalHandleDoubleEncode(bs, &cfg)
-}
-
-// ToDB exports a PackagesConfig to a serialized format.
-func (cfg *PackagesConfig) ToDB() ([]byte, error) {
-	return json.Marshal(cfg)
-}
-
 func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err error) {
 	var tmpUnits []*RepoUnit
 	if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil {
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 0d737d4e97560..8a11d37a8108a 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -871,10 +871,16 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
 
 	if opts.HasCode != nil && !unit_model.TypeCode.UnitGlobalDisabled() {
 		if *opts.HasCode {
+			cfg := &repo_model.CodeConfig{}
+
+			if opts.GoModuleSubDir != nil {
+				cfg.GoModuleSubDir = *opts.GoModuleSubDir
+			}
+
 			units = append(units, repo_model.RepoUnit{
 				RepoID: repo.ID,
 				Type:   unit_model.TypeCode,
-				Config: &repo_model.UnitConfig{},
+				Config: cfg,
 			})
 		} else {
 			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
@@ -994,17 +1000,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
 
 	if opts.HasPackages != nil && !unit_model.TypePackages.UnitGlobalDisabled() {
 		if *opts.HasPackages {
-			unit := repo.MustGetUnit(ctx, unit_model.TypePackages)
-			cfg := unit.PackagesConfig()
-
-			if opts.GoModuleSubDir != nil {
-				cfg.GoModuleSubDir = *opts.GoModuleSubDir
-			}
-
 			units = append(units, repo_model.RepoUnit{
 				RepoID: repo.ID,
 				Type:   unit_model.TypePackages,
-				Config: cfg,
 			})
 		} else {
 			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index bba445f741f50..8c69078ab06ff 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -524,7 +524,9 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 	}
 
 	if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
-		units = append(units, newRepoUnit(repo, unit_model.TypeCode, nil))
+		units = append(units, newRepoUnit(repo, unit_model.TypeCode, &repo_model.CodeConfig{
+			GoModuleSubDir: form.GoModuleSubDir,
+		}))
 	} else if !unit_model.TypeCode.UnitGlobalDisabled() {
 		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
 	}
@@ -608,9 +610,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
 	}
 
 	if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
-		units = append(units, newRepoUnit(repo, unit_model.TypePackages, &repo_model.PackagesConfig{
-			GoModuleSubDir: form.GoModuleSubDir,
-		}))
+		units = append(units, newRepoUnit(repo, unit_model.TypePackages, nil))
 	} else if !unit_model.TypePackages.UnitGlobalDisabled() {
 		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
 	}
diff --git a/services/context/repo.go b/services/context/repo.go
index adfd438c0e498..71f2e168f8b5f 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -346,9 +346,9 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
 
 // GetGoModuleSubDirConfig retrieves the subdirectory configuration for a Go module.
 func GetGoModuleSubDirConfig(ctx *Context, repo *repo_model.Repository) string {
-	pkgCfg := repo.MustGetUnit(ctx, unit_model.TypePackages).PackagesConfig()
+	codeCfg := repo.MustGetUnit(ctx, unit_model.TypeCode).CodeConfig()
 
-	return strings.TrimSpace(pkgCfg.GoModuleSubDir)
+	return strings.TrimSpace(codeCfg.GoModuleSubDir)
 }
 
 // RedirectToRepo redirect to a differently-named repository
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 36aa90933cf17..c048d0f01f2a7 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -304,14 +304,26 @@
 
 				{{$isCodeEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeCode}}
 				{{$isCodeGlobalDisabled := ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled}}
+				{{$codeUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeCode}}
+
 				
 
+				
+					
+						
+						
+						
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
+					
+				
+
+				
+
 				{{$isInternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeWiki}}
 				{{$isExternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalWiki}}
 				{{$isWikiEnabled := or $isInternalWikiEnabled $isExternalWikiEnabled}}
@@ -499,28 +511,16 @@
 					
 
 				
 
-				
 				{{$isPackagesEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypePackages}}
 				{{$isPackagesGlobalDisabled := ctx.Consts.RepoUnitTypePackages.UnitGlobalDisabled}}
-				{{$pkgUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypePackages}}
 				
 
-				
-					
-						
-						
-						
{{ctx.Locale.Tr "repo.settings.go_module_sub_dir_desc" "https://github.com/golang/go/commit/835e36fc7f631f74233edfd4ab43b6b56833db86" "github.com/golang/go@835e36fc7f"}}
-					
-