From 1d8543e7db58d7c4973758e47f005c4d8bd7d7a3 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 25 Aug 2022 10:31:57 +0800
Subject: [PATCH] Move some files into models' sub packages (#20262)

* Move some files into models' sub packages

* Move functions

* merge main branch

* Fix check

* fix check

* Fix some tests

* Fix lint

* Fix lint

* Revert lint changes

* Fix error comments

* Fix lint

Co-authored-by: 6543 <6543@obermui.de>
---
 cmd/admin.go                                  |  35 ++-
 integrations/api_notification_test.go         |  12 +-
 integrations/api_releases_test.go             |   5 +-
 integrations/api_token_test.go                |   8 +-
 integrations/api_user_heatmap_test.go         |   8 +-
 integrations/eventsource_test.go              |   6 +-
 integrations/mirror_pull_test.go              |  15 +-
 integrations/mirror_push_test.go              |   3 +-
 integrations/privateactivity_test.go          |   6 +-
 integrations/pull_merge_test.go               |   3 +-
 integrations/pull_update_test.go              |   4 +-
 integrations/repo_tag_test.go                 |   4 +-
 models/{ => activities}/action.go             |  40 +--
 models/{ => activities}/action_list.go        |   2 +-
 models/{ => activities}/action_test.go        |  57 ++--
 models/activities/main_test.go                |  20 ++
 models/{ => activities}/notification.go       |   7 +-
 models/{ => activities}/notification_test.go  |  49 +--
 models/{ => activities}/repo_activity.go      |   6 +-
 models/{ => activities}/statistic.go          |   4 +-
 models/{ => activities}/user_heatmap.go       |   4 +-
 models/{ => activities}/user_heatmap_test.go  |   7 +-
 models/admin/main_test.go                     |   7 +-
 models/admin/notice_test.go                   |  73 ++---
 models/{ => admin}/task.go                    |   2 +-
 models/auth/main_test.go                      |  14 +-
 models/auth/oauth2_test.go                    |  69 ++---
 models/auth/source_test.go                    |  11 +-
 models/{ => auth}/token.go                    |  35 ++-
 models/{ => auth}/token_test.go               |  51 ++--
 models/auth/webauthn_test.go                  |  25 +-
 models/consistency.go                         |  56 ----
 models/error.go                               | 203 -------------
 models/main_test.go                           |   3 +-
 models/migrate.go                             |   5 +-
 models/migrate_test.go                        |   2 +-
 models/org.go                                 |  66 ----
 models/org_team.go                            |  34 +--
 models/org_team_test.go                       |  19 --
 models/organization/mini_org.go               |  78 +++++
 models/organization/org_repo.go               |  18 ++
 models/organization/team.go                   |   6 +
 models/perm/access/access_test.go             | 249 ----------------
 models/perm/access/repo_permission.go         |  14 +
 models/repo.go                                | 169 +----------
 models/{ => repo}/release.go                  |  78 +++--
 models/repo/repo.go                           |  14 +
 models/{ => repo}/upload.go                   |  24 +-
 models/repo/wiki.go                           |  46 +++
 models/repo_collaboration.go                  |  37 ---
 models/repo_collaboration_test.go             |  16 -
 models/repo_transfer.go                       |   4 +-
 models/user.go                                |   5 +-
 models/user/user.go                           |  10 +
 models/user/user_update.go                    |  16 +
 modules/context/repo.go                       |   7 +-
 modules/convert/notification.go               |  18 +-
 modules/convert/release.go                    |   5 +-
 modules/convert/repository.go                 |   2 +-
 modules/doctor/dbconsistency.go               |  10 +-
 modules/doctor/usertype.go                    |   6 +-
 modules/eventsource/manager_run.go            |   4 +-
 modules/metrics/collector.go                  |   4 +-
 modules/notification/action/action.go         | 106 +++----
 modules/notification/action/action_test.go    |   8 +-
 modules/notification/base/notifier.go         |   7 +-
 modules/notification/base/null.go             |   7 +-
 modules/notification/mail/mail.go             |  42 +--
 modules/notification/notification.go          |   7 +-
 modules/notification/ui/ui.go                 |   6 +-
 modules/notification/webhook/webhook.go       |   9 +-
 modules/repository/collaborator.go            |  43 +++
 modules/repository/collaborator_test.go       | 281 ++++++++++++++++++
 modules/repository/create.go                  | 153 +++++++++-
 modules/repository/create_test.go             |   7 +-
 modules/repository/generate.go                |   3 +-
 modules/repository/init.go                    |   5 +-
 modules/repository/repo.go                    |  19 +-
 modules/templates/helper.go                   |  34 +--
 routers/api/packages/nuget/auth.go            |   8 +-
 routers/api/v1/admin/adopt.go                 |   4 +-
 routers/api/v1/notify/notifications.go        |  18 +-
 routers/api/v1/notify/repo.go                 |  26 +-
 routers/api/v1/notify/threads.go              |  10 +-
 routers/api/v1/notify/user.go                 |  14 +-
 routers/api/v1/org/team.go                    |   5 +-
 routers/api/v1/repo/collaborators.go          |   3 +-
 routers/api/v1/repo/migrate.go                |   2 +-
 routers/api/v1/repo/pull.go                   |   3 +-
 routers/api/v1/repo/release.go                |  35 +--
 routers/api/v1/repo/release_attachment.go     |   9 +-
 routers/api/v1/repo/release_tags.go           |   9 +-
 routers/api/v1/repo/repo.go                   |   3 +-
 routers/api/v1/repo/tag.go                    |   5 +-
 routers/api/v1/repo/teams.go                  |   3 +-
 routers/api/v1/repo/wiki.go                   |   8 +-
 routers/api/v1/swagger/user.go                |   4 +-
 routers/api/v1/user/app.go                    |  37 ++-
 routers/api/v1/user/user.go                   |   4 +-
 routers/web/admin/admin.go                    |   6 +-
 routers/web/admin/repos.go                    |   4 +-
 routers/web/auth/oauth.go                     |   4 +-
 routers/web/feed/convert.go                   |  80 ++---
 routers/web/feed/profile.go                   |   4 +-
 routers/web/feed/repo.go                      |   4 +-
 routers/web/org/teams.go                      |   3 +-
 routers/web/repo/activity.go                  |   8 +-
 routers/web/repo/compare.go                   |   5 +-
 routers/web/repo/editor.go                    |   5 +-
 routers/web/repo/issue.go                     |   4 +-
 routers/web/repo/pull.go                      |   3 +-
 routers/web/repo/release.go                   |  43 +--
 routers/web/repo/release_test.go              |   4 +-
 routers/web/repo/repo.go                      |   7 +-
 routers/web/repo/setting.go                   |  11 +-
 routers/web/repo/view.go                      |   9 +-
 routers/web/repo/wiki.go                      |  10 +-
 routers/web/user/home.go                      |   8 +-
 routers/web/user/notification.go              |  30 +-
 routers/web/user/profile.go                   |   6 +-
 routers/web/user/setting/adopt.go             |   4 +-
 routers/web/user/setting/applications.go      |  17 +-
 routers/web/user/setting/security/security.go |  13 +-
 routers/web/user/task.go                      |  10 +-
 services/auth/basic.go                        |   8 +-
 services/auth/oauth2.go                       |  13 +-
 services/cron/tasks_extended.go               |   4 +-
 services/issue/commit.go                      |   9 +-
 services/issue/commit_test.go                 |  18 +-
 services/issue/issue.go                       |   6 +-
 services/mailer/mail.go                       |  34 +--
 services/mailer/mail_comment.go               |   6 +-
 services/mailer/mail_issue.go                 |  17 +-
 services/mailer/mail_release.go               |   5 +-
 services/mailer/mail_test.go                  |  44 +--
 services/migrations/gitea_uploader.go         |   8 +-
 services/migrations/gitea_uploader_test.go    |  15 +-
 services/org/repo.go                          |  28 ++
 services/org/repo_test.go                     |  34 +++
 services/release/release.go                   |  26 +-
 services/release/release_test.go              |  47 ++-
 services/repository/adopt.go                  |   7 +-
 services/repository/files/upload.go           |   7 +-
 services/repository/fork.go                   |  22 +-
 services/repository/fork_test.go              |   3 +-
 services/repository/push.go                   |  13 +-
 services/repository/repository.go             |   6 +-
 services/repository/transfer.go               |   5 +-
 services/repository/transfer_test.go          |   6 +-
 services/task/migrate.go                      |   7 +-
 services/task/task.go                         |  16 +-
 services/wiki/wiki.go                         |   7 +-
 services/wiki/wiki_test.go                    |   9 +-
 templates/swagger/v1_json.tmpl                |   2 +-
 154 files changed, 1763 insertions(+), 1738 deletions(-)
 rename models/{ => activities}/action.go (95%)
 rename models/{ => activities}/action_list.go (99%)
 rename models/{ => activities}/action_test.go (76%)
 create mode 100644 models/activities/main_test.go
 rename models/{ => activities}/notification.go (98%)
 rename models/{ => activities}/notification_test.go (52%)
 rename models/{ => activities}/repo_activity.go (98%)
 rename models/{ => activities}/statistic.go (97%)
 rename models/{ => activities}/user_heatmap.go (97%)
 rename models/{ => activities}/user_heatmap_test.go (90%)
 rename models/{ => admin}/task.go (99%)
 rename models/{ => auth}/token.go (86%)
 rename models/{ => auth}/token_test.go (62%)
 delete mode 100644 models/consistency.go
 create mode 100644 models/organization/mini_org.go
 create mode 100644 models/organization/org_repo.go
 rename models/{ => repo}/release.go (85%)
 rename models/{ => repo}/upload.go (88%)
 create mode 100644 models/user/user_update.go
 create mode 100644 modules/repository/collaborator.go
 create mode 100644 modules/repository/collaborator_test.go
 create mode 100644 services/org/repo.go
 create mode 100644 services/org/repo_test.go

diff --git a/cmd/admin.go b/cmd/admin.go
index 6c2a8626c4..2cf63d384a 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -13,9 +13,8 @@ import (
 	"strings"
 	"text/tabwriter"
 
-	"code.gitea.io/gitea/models"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
-	"code.gitea.io/gitea/models/auth"
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -593,12 +592,12 @@ func runCreateUser(c *cli.Context) error {
 	}
 
 	if c.Bool("access-token") {
-		t := &models.AccessToken{
+		t := &auth_model.AccessToken{
 			Name: "gitea-admin",
 			UID:  u.ID,
 		}
 
-		if err := models.NewAccessToken(t); err != nil {
+		if err := auth_model.NewAccessToken(t); err != nil {
 			return err
 		}
 
@@ -700,12 +699,12 @@ func runGenerateAccessToken(c *cli.Context) error {
 		return err
 	}
 
-	t := &models.AccessToken{
+	t := &auth_model.AccessToken{
 		Name: c.String("token-name"),
 		UID:  user.ID,
 	}
 
-	if err := models.NewAccessToken(t); err != nil {
+	if err := auth_model.NewAccessToken(t); err != nil {
 		return err
 	}
 
@@ -779,9 +778,9 @@ func runRepoSyncReleases(_ *cli.Context) error {
 }
 
 func getReleaseCount(id int64) (int64, error) {
-	return models.GetReleaseCountByRepoID(
+	return repo_model.GetReleaseCountByRepoID(
 		id,
-		models.FindReleasesOptions{
+		repo_model.FindReleasesOptions{
 			IncludeTags: true,
 		},
 	)
@@ -844,8 +843,8 @@ func runAddOauth(c *cli.Context) error {
 		return err
 	}
 
-	return auth.CreateSource(&auth.Source{
-		Type:     auth.OAuth2,
+	return auth_model.CreateSource(&auth_model.Source{
+		Type:     auth_model.OAuth2,
 		Name:     c.String("name"),
 		IsActive: true,
 		Cfg:      parseOAuth2Config(c),
@@ -864,7 +863,7 @@ func runUpdateOauth(c *cli.Context) error {
 		return err
 	}
 
-	source, err := auth.GetSourceByID(c.Int64("id"))
+	source, err := auth_model.GetSourceByID(c.Int64("id"))
 	if err != nil {
 		return err
 	}
@@ -944,7 +943,7 @@ func runUpdateOauth(c *cli.Context) error {
 	oAuth2Config.CustomURLMapping = customURLMapping
 	source.Cfg = oAuth2Config
 
-	return auth.UpdateSource(source)
+	return auth_model.UpdateSource(source)
 }
 
 func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
@@ -1015,8 +1014,8 @@ func runAddSMTP(c *cli.Context) error {
 		smtpConfig.Auth = "PLAIN"
 	}
 
-	return auth.CreateSource(&auth.Source{
-		Type:     auth.SMTP,
+	return auth_model.CreateSource(&auth_model.Source{
+		Type:     auth_model.SMTP,
 		Name:     c.String("name"),
 		IsActive: active,
 		Cfg:      &smtpConfig,
@@ -1035,7 +1034,7 @@ func runUpdateSMTP(c *cli.Context) error {
 		return err
 	}
 
-	source, err := auth.GetSourceByID(c.Int64("id"))
+	source, err := auth_model.GetSourceByID(c.Int64("id"))
 	if err != nil {
 		return err
 	}
@@ -1056,7 +1055,7 @@ func runUpdateSMTP(c *cli.Context) error {
 
 	source.Cfg = smtpConfig
 
-	return auth.UpdateSource(source)
+	return auth_model.UpdateSource(source)
 }
 
 func runListAuth(c *cli.Context) error {
@@ -1067,7 +1066,7 @@ func runListAuth(c *cli.Context) error {
 		return err
 	}
 
-	authSources, err := auth.Sources()
+	authSources, err := auth_model.Sources()
 	if err != nil {
 		return err
 	}
@@ -1105,7 +1104,7 @@ func runDeleteAuth(c *cli.Context) error {
 		return err
 	}
 
-	source, err := auth.GetSourceByID(c.Int64("id"))
+	source, err := auth_model.GetSourceByID(c.Int64("id"))
 	if err != nil {
 		return err
 	}
diff --git a/integrations/api_notification_test.go b/integrations/api_notification_test.go
index ad6a8af0a5..bd28f49851 100644
--- a/integrations/api_notification_test.go
+++ b/integrations/api_notification_test.go
@@ -9,7 +9,7 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -23,7 +23,7 @@ func TestAPINotification(t *testing.T) {
 
 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
-	thread5 := unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5})
+	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5})
 	assert.NoError(t, thread5.LoadAttributes())
 	session := loginUser(t, user2.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -126,9 +126,9 @@ func TestAPINotification(t *testing.T) {
 	req = NewRequest(t, "PATCH", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token))
 	session.MakeRequest(t, req, http.StatusResetContent)
 
-	assert.Equal(t, models.NotificationStatusUnread, thread5.Status)
-	thread5 = unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5})
-	assert.Equal(t, models.NotificationStatusRead, thread5.Status)
+	assert.Equal(t, activities_model.NotificationStatusUnread, thread5.Status)
+	thread5 = unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5})
+	assert.Equal(t, activities_model.NotificationStatusRead, thread5.Status)
 
 	// -- check notifications --
 	req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/new?token=%s", token))
@@ -141,7 +141,7 @@ func TestAPINotificationPUT(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	thread5 := unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5})
+	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5})
 	assert.NoError(t, thread5.LoadAttributes())
 	session := loginUser(t, user2.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go
index 9561e3f488..ef9390390d 100644
--- a/integrations/api_releases_test.go
+++ b/integrations/api_releases_test.go
@@ -10,7 +10,6 @@ import (
 	"net/url"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -84,7 +83,7 @@ func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string,
 
 	var newRelease api.Release
 	DecodeJSON(t, resp, &newRelease)
-	rel := &models.Release{
+	rel := &repo_model.Release{
 		ID:      newRelease.ID,
 		TagName: newRelease.TagName,
 		Title:   newRelease.Title,
@@ -138,7 +137,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) {
 	resp = session.MakeRequest(t, req, http.StatusOK)
 
 	DecodeJSON(t, resp, &newRelease)
-	rel := &models.Release{
+	rel := &repo_model.Release{
 		ID:      newRelease.ID,
 		TagName: newRelease.TagName,
 		Title:   newRelease.Title,
diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go
index 17e8fb5e25..6ba4c26a79 100644
--- a/integrations/api_token_test.go
+++ b/integrations/api_token_test.go
@@ -8,7 +8,7 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
@@ -27,7 +27,7 @@ func TestAPICreateAndDeleteToken(t *testing.T) {
 
 	var newAccessToken api.AccessToken
 	DecodeJSON(t, resp, &newAccessToken)
-	unittest.AssertExistsAndLoadBean(t, &models.AccessToken{
+	unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{
 		ID:    newAccessToken.ID,
 		Name:  newAccessToken.Name,
 		Token: newAccessToken.Token,
@@ -38,7 +38,7 @@ func TestAPICreateAndDeleteToken(t *testing.T) {
 	req = AddBasicAuthHeader(req, user.Name)
 	MakeRequest(t, req, http.StatusNoContent)
 
-	unittest.AssertNotExistsBean(t, &models.AccessToken{ID: newAccessToken.ID})
+	unittest.AssertNotExistsBean(t, &auth_model.AccessToken{ID: newAccessToken.ID})
 
 	req = NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{
 		"name": "test-key-2",
@@ -51,7 +51,7 @@ func TestAPICreateAndDeleteToken(t *testing.T) {
 	req = AddBasicAuthHeader(req, user.Name)
 	MakeRequest(t, req, http.StatusNoContent)
 
-	unittest.AssertNotExistsBean(t, &models.AccessToken{ID: newAccessToken.ID})
+	unittest.AssertNotExistsBean(t, &auth_model.AccessToken{ID: newAccessToken.ID})
 }
 
 // TestAPIDeleteMissingToken ensures that error is thrown when token not found
diff --git a/integrations/api_user_heatmap_test.go b/integrations/api_user_heatmap_test.go
index 62e70d4c3d..6930507645 100644
--- a/integrations/api_user_heatmap_test.go
+++ b/integrations/api_user_heatmap_test.go
@@ -10,7 +10,7 @@ import (
 	"testing"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/timeutil"
 
 	"github.com/stretchr/testify/assert"
@@ -29,10 +29,10 @@ func TestUserHeatmap(t *testing.T) {
 	urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap?token=%s", normalUsername, token)
 	req := NewRequest(t, "GET", urlStr)
 	resp := MakeRequest(t, req, http.StatusOK)
-	var heatmap []*models.UserHeatmapData
+	var heatmap []*activities_model.UserHeatmapData
 	DecodeJSON(t, resp, &heatmap)
-	var dummyheatmap []*models.UserHeatmapData
-	dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1603227600, Contributions: 1})
+	var dummyheatmap []*activities_model.UserHeatmapData
+	dummyheatmap = append(dummyheatmap, &activities_model.UserHeatmapData{Timestamp: 1603227600, Contributions: 1})
 
 	assert.Equal(t, dummyheatmap, heatmap)
 }
diff --git a/integrations/eventsource_test.go b/integrations/eventsource_test.go
index e2465e9e56..4bb607cde4 100644
--- a/integrations/eventsource_test.go
+++ b/integrations/eventsource_test.go
@@ -10,7 +10,7 @@ import (
 	"testing"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -42,7 +42,7 @@ func TestEventSourceManagerRun(t *testing.T) {
 				if !ok {
 					return false
 				}
-				data, ok := event.Data.(models.UserIDCount)
+				data, ok := event.Data.(activities_model.UserIDCount)
 				if !ok {
 					return false
 				}
@@ -55,7 +55,7 @@ func TestEventSourceManagerRun(t *testing.T) {
 
 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
-	thread5 := unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5})
+	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5})
 	assert.NoError(t, thread5.LoadAttributes())
 	session := loginUser(t, user2.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/mirror_pull_test.go b/integrations/mirror_pull_test.go
index 3a45fa7cc2..dcba17be49 100644
--- a/integrations/mirror_pull_test.go
+++ b/integrations/mirror_pull_test.go
@@ -8,7 +8,6 @@ import (
 	"context"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -38,7 +37,7 @@ func TestMirrorPull(t *testing.T) {
 		Releases:    false,
 	}
 
-	mirrorRepo, err := repository.CreateRepository(user, user, models.CreateRepoOptions{
+	mirrorRepo, err := repository.CreateRepository(user, user, repository.CreateRepoOptions{
 		Name:        opts.RepoName,
 		Description: opts.Description,
 		IsPrivate:   opts.Private,
@@ -57,11 +56,11 @@ func TestMirrorPull(t *testing.T) {
 	assert.NoError(t, err)
 	defer gitRepo.Close()
 
-	findOptions := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true}
-	initCount, err := models.GetReleaseCountByRepoID(mirror.ID, findOptions)
+	findOptions := repo_model.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true}
+	initCount, err := repo_model.GetReleaseCountByRepoID(mirror.ID, findOptions)
 	assert.NoError(t, err)
 
-	assert.NoError(t, release_service.CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, release_service.CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -81,18 +80,18 @@ func TestMirrorPull(t *testing.T) {
 	ok := mirror_service.SyncPullMirror(ctx, mirror.ID)
 	assert.True(t, ok)
 
-	count, err := models.GetReleaseCountByRepoID(mirror.ID, findOptions)
+	count, err := repo_model.GetReleaseCountByRepoID(mirror.ID, findOptions)
 	assert.NoError(t, err)
 	assert.EqualValues(t, initCount+1, count)
 
-	release, err := models.GetRelease(repo.ID, "v0.2")
+	release, err := repo_model.GetRelease(repo.ID, "v0.2")
 	assert.NoError(t, err)
 	assert.NoError(t, release_service.DeleteReleaseByID(ctx, release.ID, user, true))
 
 	ok = mirror_service.SyncPullMirror(ctx, mirror.ID)
 	assert.True(t, ok)
 
-	count, err = models.GetReleaseCountByRepoID(mirror.ID, findOptions)
+	count, err = repo_model.GetReleaseCountByRepoID(mirror.ID, findOptions)
 	assert.NoError(t, err)
 	assert.EqualValues(t, initCount, count)
 }
diff --git a/integrations/mirror_push_test.go b/integrations/mirror_push_test.go
index 5a226c5a92..1af23d7837 100644
--- a/integrations/mirror_push_test.go
+++ b/integrations/mirror_push_test.go
@@ -12,7 +12,6 @@ import (
 	"strconv"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -39,7 +38,7 @@ func testMirrorPush(t *testing.T, u *url.URL) {
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 	srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 
-	mirrorRepo, err := repository.CreateRepository(user, user, models.CreateRepoOptions{
+	mirrorRepo, err := repository.CreateRepository(user, user, repository.CreateRepoOptions{
 		Name: "test-push-mirror",
 	})
 	assert.NoError(t, err)
diff --git a/integrations/privateactivity_test.go b/integrations/privateactivity_test.go
index 8e6a538c59..d91a1ddc30 100644
--- a/integrations/privateactivity_test.go
+++ b/integrations/privateactivity_test.go
@@ -9,7 +9,7 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -117,7 +117,7 @@ func testPrivateActivityHelperHasHeatmapContentFromPublic(t *testing.T) bool {
 	req := NewRequestf(t, "GET", "/api/v1/users/%s/heatmap", privateActivityTestUser)
 	resp := MakeRequest(t, req, http.StatusOK)
 
-	var items []*models.UserHeatmapData
+	var items []*activities_model.UserHeatmapData
 	DecodeJSON(t, resp, &items)
 
 	return len(items) != 0
@@ -129,7 +129,7 @@ func testPrivateActivityHelperHasHeatmapContentFromSession(t *testing.T, session
 	req := NewRequestf(t, "GET", "/api/v1/users/%s/heatmap?token=%s", privateActivityTestUser, token)
 	resp := session.MakeRequest(t, req, http.StatusOK)
 
-	var items []*models.UserHeatmapData
+	var items []*activities_model.UserHeatmapData
 	DecodeJSON(t, resp, &items)
 
 	return len(items) != 0
diff --git a/integrations/pull_merge_test.go b/integrations/pull_merge_test.go
index 13c2ff973a..1ae1ec6576 100644
--- a/integrations/pull_merge_test.go
+++ b/integrations/pull_merge_test.go
@@ -25,6 +25,7 @@ import (
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/modules/translation"
@@ -355,7 +356,7 @@ func TestConflictChecking(t *testing.T) {
 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// Create new clean repo to test conflict checking.
-		baseRepo, err := repo_service.CreateRepository(user, user, models.CreateRepoOptions{
+		baseRepo, err := repo_service.CreateRepository(user, user, repo_module.CreateRepoOptions{
 			Name:          "conflict-checking",
 			Description:   "Tempo repo",
 			AutoInit:      true,
diff --git a/integrations/pull_update_test.go b/integrations/pull_update_test.go
index 056d06189a..475382c043 100644
--- a/integrations/pull_update_test.go
+++ b/integrations/pull_update_test.go
@@ -10,12 +10,12 @@ import (
 	"testing"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	pull_service "code.gitea.io/gitea/services/pull"
 	repo_service "code.gitea.io/gitea/services/repository"
 	files_service "code.gitea.io/gitea/services/repository/files"
@@ -80,7 +80,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) {
 }
 
 func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_model.PullRequest {
-	baseRepo, err := repo_service.CreateRepository(actor, actor, models.CreateRepoOptions{
+	baseRepo, err := repo_service.CreateRepository(actor, actor, repo_module.CreateRepoOptions{
 		Name:        "repo-pr-update",
 		Description: "repo-tmp-pr-update description",
 		AutoInit:    true,
diff --git a/integrations/repo_tag_test.go b/integrations/repo_tag_test.go
index 1b3e30106b..8bb7c9f32a 100644
--- a/integrations/repo_tag_test.go
+++ b/integrations/repo_tag_test.go
@@ -77,14 +77,14 @@ func TestCreateNewTagProtected(t *testing.T) {
 	})
 
 	// Cleanup
-	releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{
+	releases, err := repo_model.GetReleasesByRepoID(repo.ID, repo_model.FindReleasesOptions{
 		IncludeTags: true,
 		TagNames:    []string{"v-1", "v-1.1"},
 	})
 	assert.NoError(t, err)
 
 	for _, release := range releases {
-		err = models.DeleteReleaseByID(release.ID)
+		err = repo_model.DeleteReleaseByID(release.ID)
 		assert.NoError(t, err)
 	}
 
diff --git a/models/action.go b/models/activities/action.go
similarity index 95%
rename from models/action.go
rename to models/activities/action.go
index 14e021389a..78a519e9a7 100644
--- a/models/action.go
+++ b/models/activities/action.go
@@ -3,7 +3,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package activities
 
 import (
 	"context"
@@ -211,21 +211,6 @@ func (a *Action) GetRepoLink() string {
 	return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
 }
 
-// GetRepositoryFromMatch returns a *repo_model.Repository from a username and repo strings
-func GetRepositoryFromMatch(ownerName, repoName string) (*repo_model.Repository, error) {
-	var err error
-	refRepo, err := repo_model.GetRepositoryByOwnerAndName(ownerName, repoName)
-	if err != nil {
-		if repo_model.IsErrRepoNotExist(err) {
-			log.Warn("Repository referenced in commit but does not exist: %v", err)
-			return nil, err
-		}
-		log.Error("repo_model.GetRepositoryByOwnerAndName: %v", err)
-		return nil, err
-	}
-	return refRepo, nil
-}
-
 // GetCommentLink returns link to action comment.
 func (a *Action) GetCommentLink() string {
 	return a.getCommentLink(db.DefaultContext)
@@ -372,7 +357,8 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) {
 	return actions, nil
 }
 
-func activityReadable(user, doer *user_model.User) bool {
+// ActivityReadable return whether doer can read activities of user
+func ActivityReadable(user, doer *user_model.User) bool {
 	return !user.KeepActivityPrivate ||
 		doer != nil && (doer.IsAdmin || user.ID == doer.ID)
 }
@@ -602,3 +588,23 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error {
 		Delete(&Action{})
 	return err
 }
+
+// CountActionCreatedUnixString count actions where created_unix is an empty string
+func CountActionCreatedUnixString() (int64, error) {
+	if setting.Database.UseSQLite3 {
+		return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
+	}
+	return 0, nil
+}
+
+// FixActionCreatedUnixString set created_unix to zero if it is an empty string
+func FixActionCreatedUnixString() (int64, error) {
+	if setting.Database.UseSQLite3 {
+		res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
+		if err != nil {
+			return 0, err
+		}
+		return res.RowsAffected()
+	}
+	return 0, nil
+}
diff --git a/models/action_list.go b/models/activities/action_list.go
similarity index 99%
rename from models/action_list.go
rename to models/activities/action_list.go
index d585ef0fc2..16fb4bac8c 100644
--- a/models/action_list.go
+++ b/models/activities/action_list.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package activities
 
 import (
 	"context"
diff --git a/models/action_test.go b/models/activities/action_test.go
similarity index 76%
rename from models/action_test.go
rename to models/activities/action_test.go
index 5c61736a61..83fd9ee38d 100644
--- a/models/action_test.go
+++ b/models/activities/action_test.go
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package activities_test
 
 import (
 	"path"
 	"testing"
 
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -21,7 +22,7 @@ func TestAction_GetRepoPath(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{})
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-	action := &Action{RepoID: repo.ID}
+	action := &activities_model.Action{RepoID: repo.ID}
 	assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath())
 }
 
@@ -29,7 +30,7 @@ func TestAction_GetRepoLink(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{})
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-	action := &Action{RepoID: repo.ID}
+	action := &activities_model.Action{RepoID: repo.ID}
 	setting.AppSubURL = "/suburl"
 	expected := path.Join(setting.AppSubURL, owner.Name, repo.Name)
 	assert.Equal(t, expected, action.GetRepoLink())
@@ -40,7 +41,7 @@ func TestGetFeeds(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
-	actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedUser:   user,
 		Actor:           user,
 		IncludePrivate:  true,
@@ -53,7 +54,7 @@ func TestGetFeeds(t *testing.T) {
 		assert.EqualValues(t, user.ID, actions[0].UserID)
 	}
 
-	actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedUser:   user,
 		Actor:           user,
 		IncludePrivate:  false,
@@ -70,7 +71,7 @@ func TestGetFeedsForRepos(t *testing.T) {
 	pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8})
 
 	// private repo & no login
-	actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedRepo:  privRepo,
 		IncludePrivate: true,
 	})
@@ -78,7 +79,7 @@ func TestGetFeedsForRepos(t *testing.T) {
 	assert.Len(t, actions, 0)
 
 	// public repo & no login
-	actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedRepo:  pubRepo,
 		IncludePrivate: true,
 	})
@@ -86,7 +87,7 @@ func TestGetFeedsForRepos(t *testing.T) {
 	assert.Len(t, actions, 1)
 
 	// private repo and login
-	actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedRepo:  privRepo,
 		IncludePrivate: true,
 		Actor:          user,
@@ -95,7 +96,7 @@ func TestGetFeedsForRepos(t *testing.T) {
 	assert.Len(t, actions, 1)
 
 	// public repo & login
-	actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedRepo:  pubRepo,
 		IncludePrivate: true,
 		Actor:          user,
@@ -110,7 +111,7 @@ func TestGetFeeds2(t *testing.T) {
 	org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
-	actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedUser:   org,
 		Actor:           user,
 		IncludePrivate:  true,
@@ -124,7 +125,7 @@ func TestGetFeeds2(t *testing.T) {
 		assert.EqualValues(t, org.ID, actions[0].UserID)
 	}
 
-	actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedUser:   org,
 		Actor:           user,
 		IncludePrivate:  false,
@@ -171,40 +172,40 @@ func TestActivityReadable(t *testing.T) {
 		result: true,
 	}}
 	for _, test := range tt {
-		assert.Equal(t, test.result, activityReadable(test.user, test.doer), test.desc)
+		assert.Equal(t, test.result, activities_model.ActivityReadable(test.user, test.doer), test.desc)
 	}
 }
 
 func TestNotifyWatchers(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	action := &Action{
+	action := &activities_model.Action{
 		ActUserID: 8,
 		RepoID:    1,
-		OpType:    ActionStarRepo,
+		OpType:    activities_model.ActionStarRepo,
 	}
-	assert.NoError(t, NotifyWatchers(action))
+	assert.NoError(t, activities_model.NotifyWatchers(action))
 
 	// One watchers are inactive, thus action is only created for user 8, 1, 4, 11
-	unittest.AssertExistsAndLoadBean(t, &Action{
+	unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
 		ActUserID: action.ActUserID,
 		UserID:    8,
 		RepoID:    action.RepoID,
 		OpType:    action.OpType,
 	})
-	unittest.AssertExistsAndLoadBean(t, &Action{
+	unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
 		ActUserID: action.ActUserID,
 		UserID:    1,
 		RepoID:    action.RepoID,
 		OpType:    action.OpType,
 	})
-	unittest.AssertExistsAndLoadBean(t, &Action{
+	unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
 		ActUserID: action.ActUserID,
 		UserID:    4,
 		RepoID:    action.RepoID,
 		OpType:    action.OpType,
 	})
-	unittest.AssertExistsAndLoadBean(t, &Action{
+	unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
 		ActUserID: action.ActUserID,
 		UserID:    11,
 		RepoID:    action.RepoID,
@@ -215,12 +216,12 @@ func TestNotifyWatchers(t *testing.T) {
 func TestGetFeedsCorrupted(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	unittest.AssertExistsAndLoadBean(t, &Action{
+	unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
 		ID:     8,
 		RepoID: 1700,
 	})
 
-	actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+	actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 		RequestedUser:  user,
 		Actor:          user,
 		IncludePrivate: true,
@@ -235,12 +236,12 @@ func TestConsistencyUpdateAction(t *testing.T) {
 	}
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	id := 8
-	unittest.AssertExistsAndLoadBean(t, &Action{
+	unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
 		ID: int64(id),
 	})
 	_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
 	assert.NoError(t, err)
-	actions := make([]*Action, 0, 1)
+	actions := make([]*activities_model.Action, 0, 1)
 	//
 	// XORM returns an error when created_unix is a string
 	//
@@ -251,17 +252,17 @@ func TestConsistencyUpdateAction(t *testing.T) {
 	//
 	// Get rid of incorrectly set created_unix
 	//
-	count, err := CountActionCreatedUnixString()
+	count, err := activities_model.CountActionCreatedUnixString()
 	assert.NoError(t, err)
 	assert.EqualValues(t, 1, count)
-	count, err = FixActionCreatedUnixString()
+	count, err = activities_model.FixActionCreatedUnixString()
 	assert.NoError(t, err)
 	assert.EqualValues(t, 1, count)
 
-	count, err = CountActionCreatedUnixString()
+	count, err = activities_model.CountActionCreatedUnixString()
 	assert.NoError(t, err)
 	assert.EqualValues(t, 0, count)
-	count, err = FixActionCreatedUnixString()
+	count, err = activities_model.FixActionCreatedUnixString()
 	assert.NoError(t, err)
 	assert.EqualValues(t, 0, count)
 
@@ -269,5 +270,5 @@ func TestConsistencyUpdateAction(t *testing.T) {
 	// XORM must be happy now
 	//
 	assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
-	unittest.CheckConsistencyFor(t, &Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
diff --git a/models/activities/main_test.go b/models/activities/main_test.go
new file mode 100644
index 0000000000..0a87f47600
--- /dev/null
+++ b/models/activities/main_test.go
@@ -0,0 +1,20 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package activities_test
+
+import (
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models/unittest"
+
+	_ "code.gitea.io/gitea/models"
+)
+
+func TestMain(m *testing.M) {
+	unittest.MainTest(m, &unittest.TestOptions{
+		GiteaRootPath: filepath.Join("..", ".."),
+	})
+}
diff --git a/models/notification.go b/models/activities/notification.go
similarity index 98%
rename from models/notification.go
rename to models/activities/notification.go
index fdc4ffad13..88776db42b 100644
--- a/models/notification.go
+++ b/models/activities/notification.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package activities
 
 import (
 	"context"
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/organization"
+	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -267,10 +268,10 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
 
 			return err
 		}
-		if issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
+		if issue.IsPull && !access_model.CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
 			continue
 		}
-		if !issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
+		if !issue.IsPull && !access_model.CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
 			continue
 		}
 
diff --git a/models/notification_test.go b/models/activities/notification_test.go
similarity index 52%
rename from models/notification_test.go
rename to models/activities/notification_test.go
index 340fb4337a..4ee16af076 100644
--- a/models/notification_test.go
+++ b/models/activities/notification_test.go
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package activities_test
 
 import (
 	"testing"
 
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/unittest"
@@ -19,22 +20,22 @@ func TestCreateOrUpdateIssueNotifications(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
 
-	assert.NoError(t, CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0))
+	assert.NoError(t, activities_model.CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0))
 
 	// User 9 is inactive, thus notifications for user 1 and 4 are created
-	notf := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 1, IssueID: issue.ID})
-	assert.Equal(t, NotificationStatusUnread, notf.Status)
+	notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{UserID: 1, IssueID: issue.ID})
+	assert.Equal(t, activities_model.NotificationStatusUnread, notf.Status)
 	unittest.CheckConsistencyFor(t, &issues_model.Issue{ID: issue.ID})
 
-	notf = unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 4, IssueID: issue.ID})
-	assert.Equal(t, NotificationStatusUnread, notf.Status)
+	notf = unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{UserID: 4, IssueID: issue.ID})
+	assert.Equal(t, activities_model.NotificationStatusUnread, notf.Status)
 }
 
 func TestNotificationsForUser(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread}
-	notfs, err := NotificationsForUser(db.DefaultContext, user, statuses, 1, 10)
+	statuses := []activities_model.NotificationStatus{activities_model.NotificationStatusRead, activities_model.NotificationStatusUnread}
+	notfs, err := activities_model.NotificationsForUser(db.DefaultContext, user, statuses, 1, 10)
 	assert.NoError(t, err)
 	if assert.Len(t, notfs, 3) {
 		assert.EqualValues(t, 5, notfs[0].ID)
@@ -48,7 +49,7 @@ func TestNotificationsForUser(t *testing.T) {
 
 func TestNotification_GetRepo(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	notf := unittest.AssertExistsAndLoadBean(t, &Notification{RepoID: 1})
+	notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{RepoID: 1})
 	repo, err := notf.GetRepo()
 	assert.NoError(t, err)
 	assert.Equal(t, repo, notf.Repository)
@@ -57,7 +58,7 @@ func TestNotification_GetRepo(t *testing.T) {
 
 func TestNotification_GetIssue(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	notf := unittest.AssertExistsAndLoadBean(t, &Notification{RepoID: 1})
+	notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{RepoID: 1})
 	issue, err := notf.GetIssue()
 	assert.NoError(t, err)
 	assert.Equal(t, issue, notf.Issue)
@@ -67,11 +68,11 @@ func TestNotification_GetIssue(t *testing.T) {
 func TestGetNotificationCount(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	cnt, err := GetNotificationCount(db.DefaultContext, user, NotificationStatusRead)
+	cnt, err := activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusRead)
 	assert.NoError(t, err)
 	assert.EqualValues(t, 0, cnt)
 
-	cnt, err = GetNotificationCount(db.DefaultContext, user, NotificationStatusUnread)
+	cnt, err = activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusUnread)
 	assert.NoError(t, err)
 	assert.EqualValues(t, 1, cnt)
 }
@@ -80,15 +81,15 @@ func TestSetNotificationStatus(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 	notf := unittest.AssertExistsAndLoadBean(t,
-		&Notification{UserID: user.ID, Status: NotificationStatusRead})
-	_, err := SetNotificationStatus(notf.ID, user, NotificationStatusPinned)
+		&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
+	_, err := activities_model.SetNotificationStatus(notf.ID, user, activities_model.NotificationStatusPinned)
 	assert.NoError(t, err)
 	unittest.AssertExistsAndLoadBean(t,
-		&Notification{ID: notf.ID, Status: NotificationStatusPinned})
+		&activities_model.Notification{ID: notf.ID, Status: activities_model.NotificationStatusPinned})
 
-	_, err = SetNotificationStatus(1, user, NotificationStatusRead)
+	_, err = activities_model.SetNotificationStatus(1, user, activities_model.NotificationStatusRead)
 	assert.Error(t, err)
-	_, err = SetNotificationStatus(unittest.NonexistentID, user, NotificationStatusRead)
+	_, err = activities_model.SetNotificationStatus(unittest.NonexistentID, user, activities_model.NotificationStatusRead)
 	assert.Error(t, err)
 }
 
@@ -96,16 +97,16 @@ func TestUpdateNotificationStatuses(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 	notfUnread := unittest.AssertExistsAndLoadBean(t,
-		&Notification{UserID: user.ID, Status: NotificationStatusUnread})
+		&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusUnread})
 	notfRead := unittest.AssertExistsAndLoadBean(t,
-		&Notification{UserID: user.ID, Status: NotificationStatusRead})
+		&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
 	notfPinned := unittest.AssertExistsAndLoadBean(t,
-		&Notification{UserID: user.ID, Status: NotificationStatusPinned})
-	assert.NoError(t, UpdateNotificationStatuses(user, NotificationStatusUnread, NotificationStatusRead))
+		&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusPinned})
+	assert.NoError(t, activities_model.UpdateNotificationStatuses(user, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead))
 	unittest.AssertExistsAndLoadBean(t,
-		&Notification{ID: notfUnread.ID, Status: NotificationStatusRead})
+		&activities_model.Notification{ID: notfUnread.ID, Status: activities_model.NotificationStatusRead})
 	unittest.AssertExistsAndLoadBean(t,
-		&Notification{ID: notfRead.ID, Status: NotificationStatusRead})
+		&activities_model.Notification{ID: notfRead.ID, Status: activities_model.NotificationStatusRead})
 	unittest.AssertExistsAndLoadBean(t,
-		&Notification{ID: notfPinned.ID, Status: NotificationStatusPinned})
+		&activities_model.Notification{ID: notfPinned.ID, Status: activities_model.NotificationStatusPinned})
 }
diff --git a/models/repo_activity.go b/models/activities/repo_activity.go
similarity index 98%
rename from models/repo_activity.go
rename to models/activities/repo_activity.go
index 6a3636ab07..684ceee272 100644
--- a/models/repo_activity.go
+++ b/models/activities/repo_activity.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package activities
 
 import (
 	"context"
@@ -39,7 +39,7 @@ type ActivityStats struct {
 	ClosedIssues                issues_model.IssueList
 	ClosedIssueAuthorCount      int64
 	UnresolvedIssues            issues_model.IssueList
-	PublishedReleases           []*Release
+	PublishedReleases           []*repo_model.Release
 	PublishedReleaseAuthorCount int64
 	Code                        *git.CodeActivityStats
 }
@@ -344,7 +344,7 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error
 	// Published releases list
 	sess := releasesForActivityStatement(repoID, fromTime)
 	sess.OrderBy("release.created_unix DESC")
-	stats.PublishedReleases = make([]*Release, 0)
+	stats.PublishedReleases = make([]*repo_model.Release, 0)
 	if err = sess.Find(&stats.PublishedReleases); err != nil {
 		return err
 	}
diff --git a/models/statistic.go b/models/activities/statistic.go
similarity index 97%
rename from models/statistic.go
rename to models/activities/statistic.go
index ec094b5f5b..ea785a3ee2 100644
--- a/models/statistic.go
+++ b/models/activities/statistic.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package activities
 
 import (
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -101,7 +101,7 @@ func GetStatistic() (stats Statistic) {
 	stats.Counter.Oauth = 0
 	stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
 	stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
-	stats.Counter.Release, _ = e.Count(new(Release))
+	stats.Counter.Release, _ = e.Count(new(repo_model.Release))
 	stats.Counter.AuthSource = auth.CountSources()
 	stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
 	stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone))
diff --git a/models/user_heatmap.go b/models/activities/user_heatmap.go
similarity index 97%
rename from models/user_heatmap.go
rename to models/activities/user_heatmap.go
index e908837ae8..6e76be6c6b 100644
--- a/models/user_heatmap.go
+++ b/models/activities/user_heatmap.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.package models
 
-package models
+package activities
 
 import (
 	"code.gitea.io/gitea/models/db"
@@ -31,7 +31,7 @@ func GetUserHeatmapDataByUserTeam(user *user_model.User, team *organization.Team
 func getUserHeatmapData(user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
 	hdata := make([]*UserHeatmapData, 0)
 
-	if !activityReadable(user, doer) {
+	if !ActivityReadable(user, doer) {
 		return hdata, nil
 	}
 
diff --git a/models/user_heatmap_test.go b/models/activities/user_heatmap_test.go
similarity index 90%
rename from models/user_heatmap_test.go
rename to models/activities/user_heatmap_test.go
index 1ff7bc6ed2..a8a240f790 100644
--- a/models/user_heatmap_test.go
+++ b/models/activities/user_heatmap_test.go
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.package models
 
-package models
+package activities_test
 
 import (
 	"fmt"
 	"testing"
 	"time"
 
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -73,7 +74,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
 		}
 
 		// get the action for comparison
-		actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+		actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
 			RequestedUser:   user,
 			Actor:           doer,
 			IncludePrivate:  true,
@@ -83,7 +84,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
 		assert.NoError(t, err)
 
 		// Get the heatmap and compare
-		heatmap, err := GetUserHeatmapDataByUser(user, doer)
+		heatmap, err := activities_model.GetUserHeatmapDataByUser(user, doer)
 		var contributions int
 		for _, hm := range heatmap {
 			contributions += int(hm.Contributions)
diff --git a/models/admin/main_test.go b/models/admin/main_test.go
index 693b70fbf7..23277fe37c 100644
--- a/models/admin/main_test.go
+++ b/models/admin/main_test.go
@@ -2,18 +2,21 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package admin
+package admin_test
 
 import (
 	"path/filepath"
 	"testing"
 
 	"code.gitea.io/gitea/models/unittest"
+
+	_ "code.gitea.io/gitea/models"
+	_ "code.gitea.io/gitea/models/activities"
+	_ "code.gitea.io/gitea/models/perm/access"
 )
 
 func TestMain(m *testing.M) {
 	unittest.MainTest(m, &unittest.TestOptions{
 		GiteaRootPath: filepath.Join("..", ".."),
-		FixtureFiles:  []string{"notice.yml"},
 	})
 }
diff --git a/models/admin/notice_test.go b/models/admin/notice_test.go
index b4613db8e7..f3767392d1 100644
--- a/models/admin/notice_test.go
+++ b/models/admin/notice_test.go
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package admin
+package admin_test
 
 import (
 	"testing"
 
+	"code.gitea.io/gitea/models/admin"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unittest"
 
@@ -14,8 +15,8 @@ import (
 )
 
 func TestNotice_TrStr(t *testing.T) {
-	notice := &Notice{
-		Type:        NoticeRepository,
+	notice := &admin.Notice{
+		Type:        admin.NoticeRepository,
 		Description: "test description",
 	}
 	assert.Equal(t, "admin.notices.type_1", notice.TrStr())
@@ -24,24 +25,24 @@ func TestNotice_TrStr(t *testing.T) {
 func TestCreateNotice(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	noticeBean := &Notice{
-		Type:        NoticeRepository,
+	noticeBean := &admin.Notice{
+		Type:        admin.NoticeRepository,
 		Description: "test description",
 	}
 	unittest.AssertNotExistsBean(t, noticeBean)
-	assert.NoError(t, CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
+	assert.NoError(t, admin.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
 	unittest.AssertExistsAndLoadBean(t, noticeBean)
 }
 
 func TestCreateRepositoryNotice(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	noticeBean := &Notice{
-		Type:        NoticeRepository,
+	noticeBean := &admin.Notice{
+		Type:        admin.NoticeRepository,
 		Description: "test description",
 	}
 	unittest.AssertNotExistsBean(t, noticeBean)
-	assert.NoError(t, CreateRepositoryNotice(noticeBean.Description))
+	assert.NoError(t, admin.CreateRepositoryNotice(noticeBean.Description))
 	unittest.AssertExistsAndLoadBean(t, noticeBean)
 }
 
@@ -49,20 +50,20 @@ func TestCreateRepositoryNotice(t *testing.T) {
 
 func TestCountNotices(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	assert.Equal(t, int64(3), CountNotices())
+	assert.Equal(t, int64(3), admin.CountNotices())
 }
 
 func TestNotices(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	notices, err := Notices(1, 2)
+	notices, err := admin.Notices(1, 2)
 	assert.NoError(t, err)
 	if assert.Len(t, notices, 2) {
 		assert.Equal(t, int64(3), notices[0].ID)
 		assert.Equal(t, int64(2), notices[1].ID)
 	}
 
-	notices, err = Notices(2, 2)
+	notices, err = admin.Notices(2, 2)
 	assert.NoError(t, err)
 	if assert.Len(t, notices, 1) {
 		assert.Equal(t, int64(1), notices[0].ID)
@@ -72,45 +73,45 @@ func TestNotices(t *testing.T) {
 func TestDeleteNotice(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
-	assert.NoError(t, DeleteNotice(3))
-	unittest.AssertNotExistsBean(t, &Notice{ID: 3})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
+	assert.NoError(t, admin.DeleteNotice(3))
+	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
 }
 
 func TestDeleteNotices(t *testing.T) {
 	// delete a non-empty range
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
-	assert.NoError(t, DeleteNotices(1, 2))
-	unittest.AssertNotExistsBean(t, &Notice{ID: 1})
-	unittest.AssertNotExistsBean(t, &Notice{ID: 2})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
+	assert.NoError(t, admin.DeleteNotices(1, 2))
+	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
+	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 2})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 }
 
 func TestDeleteNotices2(t *testing.T) {
 	// delete an empty range
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
-	assert.NoError(t, DeleteNotices(3, 2))
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
+	assert.NoError(t, admin.DeleteNotices(3, 2))
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
 }
 
 func TestDeleteNoticesByIDs(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
-	assert.NoError(t, DeleteNoticesByIDs([]int64{1, 3}))
-	unittest.AssertNotExistsBean(t, &Notice{ID: 1})
-	unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
-	unittest.AssertNotExistsBean(t, &Notice{ID: 3})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
+	assert.NoError(t, admin.DeleteNoticesByIDs([]int64{1, 3}))
+	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
+	unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
+	unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
 }
diff --git a/models/task.go b/models/admin/task.go
similarity index 99%
rename from models/task.go
rename to models/admin/task.go
index 67f04d9562..07eb61decc 100644
--- a/models/task.go
+++ b/models/admin/task.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package admin
 
 import (
 	"context"
diff --git a/models/auth/main_test.go b/models/auth/main_test.go
index ccbdd4e81c..5d52e963b8 100644
--- a/models/auth/main_test.go
+++ b/models/auth/main_test.go
@@ -2,24 +2,22 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package auth
+package auth_test
 
 import (
 	"path/filepath"
 	"testing"
 
 	"code.gitea.io/gitea/models/unittest"
+
+	_ "code.gitea.io/gitea/models"
+	_ "code.gitea.io/gitea/models/activities"
+	_ "code.gitea.io/gitea/models/auth"
+	_ "code.gitea.io/gitea/models/perm/access"
 )
 
 func TestMain(m *testing.M) {
 	unittest.MainTest(m, &unittest.TestOptions{
 		GiteaRootPath: filepath.Join("..", ".."),
-		FixtureFiles: []string{
-			"login_source.yml",
-			"oauth2_application.yml",
-			"oauth2_authorization_code.yml",
-			"oauth2_grant.yml",
-			"webauthn_credential.yml",
-		},
 	})
 }
diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go
index 2a74f39998..3b2ba8c8f1 100644
--- a/models/auth/oauth2_test.go
+++ b/models/auth/oauth2_test.go
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package auth
+package auth_test
 
 import (
 	"testing"
 
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unittest"
 
@@ -17,23 +18,23 @@ import (
 
 func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
+	app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
 	secret, err := app.GenerateClientSecret()
 	assert.NoError(t, err)
 	assert.True(t, len(secret) > 0)
-	unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
+	unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
 }
 
 func BenchmarkOAuth2Application_GenerateClientSecret(b *testing.B) {
 	assert.NoError(b, unittest.PrepareTestDatabase())
-	app := unittest.AssertExistsAndLoadBean(b, &OAuth2Application{ID: 1})
+	app := unittest.AssertExistsAndLoadBean(b, &auth_model.OAuth2Application{ID: 1})
 	for i := 0; i < b.N; i++ {
 		_, _ = app.GenerateClientSecret()
 	}
 }
 
 func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
-	app := &OAuth2Application{
+	app := &auth_model.OAuth2Application{
 		RedirectURIs: []string{"a", "b", "c"},
 	}
 	assert.True(t, app.ContainsRedirectURI("a"))
@@ -44,7 +45,7 @@ func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
 
 func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
+	app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
 	secret, err := app.GenerateClientSecret()
 	assert.NoError(t, err)
 	assert.True(t, app.ValidateClientSecret([]byte(secret)))
@@ -53,31 +54,31 @@ func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
 
 func TestGetOAuth2ApplicationByClientID(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	app, err := GetOAuth2ApplicationByClientID(db.DefaultContext, "da7da3ba-9a13-4167-856f-3899de0b0138")
+	app, err := auth_model.GetOAuth2ApplicationByClientID(db.DefaultContext, "da7da3ba-9a13-4167-856f-3899de0b0138")
 	assert.NoError(t, err)
 	assert.Equal(t, "da7da3ba-9a13-4167-856f-3899de0b0138", app.ClientID)
 
-	app, err = GetOAuth2ApplicationByClientID(db.DefaultContext, "invalid client id")
+	app, err = auth_model.GetOAuth2ApplicationByClientID(db.DefaultContext, "invalid client id")
 	assert.Error(t, err)
 	assert.Nil(t, app)
 }
 
 func TestCreateOAuth2Application(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	app, err := CreateOAuth2Application(db.DefaultContext, CreateOAuth2ApplicationOptions{Name: "newapp", UserID: 1})
+	app, err := auth_model.CreateOAuth2Application(db.DefaultContext, auth_model.CreateOAuth2ApplicationOptions{Name: "newapp", UserID: 1})
 	assert.NoError(t, err)
 	assert.Equal(t, "newapp", app.Name)
 	assert.Len(t, app.ClientID, 36)
-	unittest.AssertExistsAndLoadBean(t, &OAuth2Application{Name: "newapp"})
+	unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{Name: "newapp"})
 }
 
 func TestOAuth2Application_TableName(t *testing.T) {
-	assert.Equal(t, "oauth2_application", new(OAuth2Application).TableName())
+	assert.Equal(t, "oauth2_application", new(auth_model.OAuth2Application).TableName())
 }
 
 func TestOAuth2Application_GetGrantByUserID(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
+	app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
 	grant, err := app.GetGrantByUserID(db.DefaultContext, 1)
 	assert.NoError(t, err)
 	assert.Equal(t, int64(1), grant.UserID)
@@ -89,7 +90,7 @@ func TestOAuth2Application_GetGrantByUserID(t *testing.T) {
 
 func TestOAuth2Application_CreateGrant(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
+	app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
 	grant, err := app.CreateGrant(db.DefaultContext, 2, "")
 	assert.NoError(t, err)
 	assert.NotNil(t, grant)
@@ -102,26 +103,26 @@ func TestOAuth2Application_CreateGrant(t *testing.T) {
 
 func TestGetOAuth2GrantByID(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	grant, err := GetOAuth2GrantByID(db.DefaultContext, 1)
+	grant, err := auth_model.GetOAuth2GrantByID(db.DefaultContext, 1)
 	assert.NoError(t, err)
 	assert.Equal(t, int64(1), grant.ID)
 
-	grant, err = GetOAuth2GrantByID(db.DefaultContext, 34923458)
+	grant, err = auth_model.GetOAuth2GrantByID(db.DefaultContext, 34923458)
 	assert.NoError(t, err)
 	assert.Nil(t, grant)
 }
 
 func TestOAuth2Grant_IncreaseCounter(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Counter: 1})
+	grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 1})
 	assert.NoError(t, grant.IncreaseCounter(db.DefaultContext))
 	assert.Equal(t, int64(2), grant.Counter)
-	unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Counter: 2})
+	unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 2})
 }
 
 func TestOAuth2Grant_ScopeContains(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Scope: "openid profile"})
+	grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Scope: "openid profile"})
 	assert.True(t, grant.ScopeContains("openid"))
 	assert.True(t, grant.ScopeContains("profile"))
 	assert.False(t, grant.ScopeContains("profil"))
@@ -130,7 +131,7 @@ func TestOAuth2Grant_ScopeContains(t *testing.T) {
 
 func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1})
+	grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1})
 	code, err := grant.GenerateNewAuthorizationCode(db.DefaultContext, "https://example2.com/callback", "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg", "S256")
 	assert.NoError(t, err)
 	assert.NotNil(t, code)
@@ -138,46 +139,46 @@ func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
 }
 
 func TestOAuth2Grant_TableName(t *testing.T) {
-	assert.Equal(t, "oauth2_grant", new(OAuth2Grant).TableName())
+	assert.Equal(t, "oauth2_grant", new(auth_model.OAuth2Grant).TableName())
 }
 
 func TestGetOAuth2GrantsByUserID(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	result, err := GetOAuth2GrantsByUserID(db.DefaultContext, 1)
+	result, err := auth_model.GetOAuth2GrantsByUserID(db.DefaultContext, 1)
 	assert.NoError(t, err)
 	assert.Len(t, result, 1)
 	assert.Equal(t, int64(1), result[0].ID)
 	assert.Equal(t, result[0].ApplicationID, result[0].Application.ID)
 
-	result, err = GetOAuth2GrantsByUserID(db.DefaultContext, 34134)
+	result, err = auth_model.GetOAuth2GrantsByUserID(db.DefaultContext, 34134)
 	assert.NoError(t, err)
 	assert.Empty(t, result)
 }
 
 func TestRevokeOAuth2Grant(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	assert.NoError(t, RevokeOAuth2Grant(db.DefaultContext, 1, 1))
-	unittest.AssertNotExistsBean(t, &OAuth2Grant{ID: 1, UserID: 1})
+	assert.NoError(t, auth_model.RevokeOAuth2Grant(db.DefaultContext, 1, 1))
+	unittest.AssertNotExistsBean(t, &auth_model.OAuth2Grant{ID: 1, UserID: 1})
 }
 
 //////////////////// Authorization Code
 
 func TestGetOAuth2AuthorizationByCode(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	code, err := GetOAuth2AuthorizationByCode(db.DefaultContext, "authcode")
+	code, err := auth_model.GetOAuth2AuthorizationByCode(db.DefaultContext, "authcode")
 	assert.NoError(t, err)
 	assert.NotNil(t, code)
 	assert.Equal(t, "authcode", code.Code)
 	assert.Equal(t, int64(1), code.ID)
 
-	code, err = GetOAuth2AuthorizationByCode(db.DefaultContext, "does not exist")
+	code, err = auth_model.GetOAuth2AuthorizationByCode(db.DefaultContext, "does not exist")
 	assert.NoError(t, err)
 	assert.Nil(t, code)
 }
 
 func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
 	// test plain
-	code := &OAuth2AuthorizationCode{
+	code := &auth_model.OAuth2AuthorizationCode{
 		CodeChallengeMethod: "plain",
 		CodeChallenge:       "test123",
 	}
@@ -185,7 +186,7 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
 	assert.False(t, code.ValidateCodeChallenge("ierwgjoergjio"))
 
 	// test S256
-	code = &OAuth2AuthorizationCode{
+	code = &auth_model.OAuth2AuthorizationCode{
 		CodeChallengeMethod: "S256",
 		CodeChallenge:       "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg",
 	}
@@ -193,14 +194,14 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
 	assert.False(t, code.ValidateCodeChallenge("wiogjerogorewngoenrgoiuenorg"))
 
 	// test unknown
-	code = &OAuth2AuthorizationCode{
+	code = &auth_model.OAuth2AuthorizationCode{
 		CodeChallengeMethod: "monkey",
 		CodeChallenge:       "foiwgjioriogeiogjerger",
 	}
 	assert.False(t, code.ValidateCodeChallenge("foiwgjioriogeiogjerger"))
 
 	// test no code challenge
-	code = &OAuth2AuthorizationCode{
+	code = &auth_model.OAuth2AuthorizationCode{
 		CodeChallengeMethod: "",
 		CodeChallenge:       "foierjiogerogerg",
 	}
@@ -208,7 +209,7 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
 }
 
 func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
-	code := &OAuth2AuthorizationCode{
+	code := &auth_model.OAuth2AuthorizationCode{
 		RedirectURI: "https://example.com/callback",
 		Code:        "thecode",
 	}
@@ -224,11 +225,11 @@ func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
 
 func TestOAuth2AuthorizationCode_Invalidate(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	code := unittest.AssertExistsAndLoadBean(t, &OAuth2AuthorizationCode{Code: "authcode"})
+	code := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"})
 	assert.NoError(t, code.Invalidate(db.DefaultContext))
-	unittest.AssertNotExistsBean(t, &OAuth2AuthorizationCode{Code: "authcode"})
+	unittest.AssertNotExistsBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"})
 }
 
 func TestOAuth2AuthorizationCode_TableName(t *testing.T) {
-	assert.Equal(t, "oauth2_authorization_code", new(OAuth2AuthorizationCode).TableName())
+	assert.Equal(t, "oauth2_authorization_code", new(auth_model.OAuth2AuthorizationCode).TableName())
 }
diff --git a/models/auth/source_test.go b/models/auth/source_test.go
index 6a8e286910..67e96ee19e 100644
--- a/models/auth/source_test.go
+++ b/models/auth/source_test.go
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package auth
+package auth_test
 
 import (
 	"strings"
 	"testing"
 
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/modules/json"
@@ -37,13 +38,13 @@ func (source *TestSource) ToDB() ([]byte, error) {
 func TestDumpAuthSource(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	authSourceSchema, err := db.TableInfo(new(Source))
+	authSourceSchema, err := db.TableInfo(new(auth_model.Source))
 	assert.NoError(t, err)
 
-	RegisterTypeConfig(OAuth2, new(TestSource))
+	auth_model.RegisterTypeConfig(auth_model.OAuth2, new(TestSource))
 
-	CreateSource(&Source{
-		Type:     OAuth2,
+	auth_model.CreateSource(&auth_model.Source{
+		Type:     auth_model.OAuth2,
 		Name:     "TestSource",
 		IsActive: false,
 		Cfg: &TestSource{
diff --git a/models/token.go b/models/auth/token.go
similarity index 86%
rename from models/token.go
rename to models/auth/token.go
index b89514309c..01654f2901 100644
--- a/models/token.go
+++ b/models/auth/token.go
@@ -3,14 +3,13 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package auth
 
 import (
 	"crypto/subtle"
 	"fmt"
 	"time"
 
-	"code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/setting"
@@ -21,6 +20,34 @@ import (
 	lru "github.com/hashicorp/golang-lru"
 )
 
+// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
+type ErrAccessTokenNotExist struct {
+	Token string
+}
+
+// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
+func IsErrAccessTokenNotExist(err error) bool {
+	_, ok := err.(ErrAccessTokenNotExist)
+	return ok
+}
+
+func (err ErrAccessTokenNotExist) Error() string {
+	return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
+}
+
+// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
+type ErrAccessTokenEmpty struct{}
+
+// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
+func IsErrAccessTokenEmpty(err error) bool {
+	_, ok := err.(ErrAccessTokenEmpty)
+	return ok
+}
+
+func (err ErrAccessTokenEmpty) Error() string {
+	return "access token is empty"
+}
+
 var successfulAccessTokenCache *lru.Cache
 
 // AccessToken represents a personal access token.
@@ -68,7 +95,7 @@ func NewAccessToken(t *AccessToken) error {
 	}
 	t.TokenSalt = salt
 	t.Token = base.EncodeSha1(gouuid.New().String())
-	t.TokenHash = auth.HashToken(t.Token, t.TokenSalt)
+	t.TokenHash = HashToken(t.Token, t.TokenSalt)
 	t.TokenLastEight = t.Token[len(t.Token)-8:]
 	_, err = db.GetEngine(db.DefaultContext).Insert(t)
 	return err
@@ -130,7 +157,7 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
 	}
 
 	for _, t := range tokens {
-		tempHash := auth.HashToken(token, t.TokenSalt)
+		tempHash := HashToken(token, t.TokenSalt)
 		if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
 			if successfulAccessTokenCache != nil {
 				successfulAccessTokenCache.Add(token, t.ID)
diff --git a/models/token_test.go b/models/auth/token_test.go
similarity index 62%
rename from models/token_test.go
rename to models/auth/token_test.go
index 007148870a..b27ff13406 100644
--- a/models/token_test.go
+++ b/models/auth/token_test.go
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package auth_test
 
 import (
 	"testing"
 
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/unittest"
 
 	"github.com/stretchr/testify/assert"
@@ -14,77 +15,77 @@ import (
 
 func TestNewAccessToken(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	token := &AccessToken{
+	token := &auth_model.AccessToken{
 		UID:  3,
 		Name: "Token C",
 	}
-	assert.NoError(t, NewAccessToken(token))
+	assert.NoError(t, auth_model.NewAccessToken(token))
 	unittest.AssertExistsAndLoadBean(t, token)
 
-	invalidToken := &AccessToken{
+	invalidToken := &auth_model.AccessToken{
 		ID:   token.ID, // duplicate
 		UID:  2,
 		Name: "Token F",
 	}
-	assert.Error(t, NewAccessToken(invalidToken))
+	assert.Error(t, auth_model.NewAccessToken(invalidToken))
 }
 
 func TestAccessTokenByNameExists(t *testing.T) {
 	name := "Token Gitea"
 
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	token := &AccessToken{
+	token := &auth_model.AccessToken{
 		UID:  3,
 		Name: name,
 	}
 
 	// Check to make sure it doesn't exists already
-	exist, err := AccessTokenByNameExists(token)
+	exist, err := auth_model.AccessTokenByNameExists(token)
 	assert.NoError(t, err)
 	assert.False(t, exist)
 
 	// Save it to the database
-	assert.NoError(t, NewAccessToken(token))
+	assert.NoError(t, auth_model.NewAccessToken(token))
 	unittest.AssertExistsAndLoadBean(t, token)
 
 	// This token must be found by name in the DB now
-	exist, err = AccessTokenByNameExists(token)
+	exist, err = auth_model.AccessTokenByNameExists(token)
 	assert.NoError(t, err)
 	assert.True(t, exist)
 
-	user4Token := &AccessToken{
+	user4Token := &auth_model.AccessToken{
 		UID:  4,
 		Name: name,
 	}
 
 	// Name matches but different user ID, this shouldn't exists in the
 	// database
-	exist, err = AccessTokenByNameExists(user4Token)
+	exist, err = auth_model.AccessTokenByNameExists(user4Token)
 	assert.NoError(t, err)
 	assert.False(t, exist)
 }
 
 func TestGetAccessTokenBySHA(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")
+	token, err := auth_model.GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")
 	assert.NoError(t, err)
 	assert.Equal(t, int64(1), token.UID)
 	assert.Equal(t, "Token A", token.Name)
 	assert.Equal(t, "2b3668e11cb82d3af8c6e4524fc7841297668f5008d1626f0ad3417e9fa39af84c268248b78c481daa7e5dc437784003494f", token.TokenHash)
 	assert.Equal(t, "e4efbf36", token.TokenLastEight)
 
-	_, err = GetAccessTokenBySHA("notahash")
+	_, err = auth_model.GetAccessTokenBySHA("notahash")
 	assert.Error(t, err)
-	assert.True(t, IsErrAccessTokenNotExist(err))
+	assert.True(t, auth_model.IsErrAccessTokenNotExist(err))
 
-	_, err = GetAccessTokenBySHA("")
+	_, err = auth_model.GetAccessTokenBySHA("")
 	assert.Error(t, err)
-	assert.True(t, IsErrAccessTokenEmpty(err))
+	assert.True(t, auth_model.IsErrAccessTokenEmpty(err))
 }
 
 func TestListAccessTokens(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	tokens, err := ListAccessTokens(ListAccessTokensOptions{UserID: 1})
+	tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 1})
 	assert.NoError(t, err)
 	if assert.Len(t, tokens, 2) {
 		assert.Equal(t, int64(1), tokens[0].UID)
@@ -93,39 +94,39 @@ func TestListAccessTokens(t *testing.T) {
 		assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B")
 	}
 
-	tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 2})
+	tokens, err = auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 2})
 	assert.NoError(t, err)
 	if assert.Len(t, tokens, 1) {
 		assert.Equal(t, int64(2), tokens[0].UID)
 		assert.Equal(t, "Token A", tokens[0].Name)
 	}
 
-	tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 100})
+	tokens, err = auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 100})
 	assert.NoError(t, err)
 	assert.Empty(t, tokens)
 }
 
 func TestUpdateAccessToken(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
+	token, err := auth_model.GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
 	assert.NoError(t, err)
 	token.Name = "Token Z"
 
-	assert.NoError(t, UpdateAccessToken(token))
+	assert.NoError(t, auth_model.UpdateAccessToken(token))
 	unittest.AssertExistsAndLoadBean(t, token)
 }
 
 func TestDeleteAccessTokenByID(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
+	token, err := auth_model.GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
 	assert.NoError(t, err)
 	assert.Equal(t, int64(1), token.UID)
 
-	assert.NoError(t, DeleteAccessTokenByID(token.ID, 1))
+	assert.NoError(t, auth_model.DeleteAccessTokenByID(token.ID, 1))
 	unittest.AssertNotExistsBean(t, token)
 
-	err = DeleteAccessTokenByID(100, 100)
+	err = auth_model.DeleteAccessTokenByID(100, 100)
 	assert.Error(t, err)
-	assert.True(t, IsErrAccessTokenNotExist(err))
+	assert.True(t, auth_model.IsErrAccessTokenNotExist(err))
 }
diff --git a/models/auth/webauthn_test.go b/models/auth/webauthn_test.go
index edbb7ecd5b..29344376cc 100644
--- a/models/auth/webauthn_test.go
+++ b/models/auth/webauthn_test.go
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package auth
+package auth_test
 
 import (
 	"testing"
 
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/unittest"
 
 	"github.com/duo-labs/webauthn/webauthn"
@@ -16,51 +17,51 @@ import (
 func TestGetWebAuthnCredentialByID(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	res, err := GetWebAuthnCredentialByID(1)
+	res, err := auth_model.GetWebAuthnCredentialByID(1)
 	assert.NoError(t, err)
 	assert.Equal(t, "WebAuthn credential", res.Name)
 
-	_, err = GetWebAuthnCredentialByID(342432)
+	_, err = auth_model.GetWebAuthnCredentialByID(342432)
 	assert.Error(t, err)
-	assert.True(t, IsErrWebAuthnCredentialNotExist(err))
+	assert.True(t, auth_model.IsErrWebAuthnCredentialNotExist(err))
 }
 
 func TestGetWebAuthnCredentialsByUID(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	res, err := GetWebAuthnCredentialsByUID(32)
+	res, err := auth_model.GetWebAuthnCredentialsByUID(32)
 	assert.NoError(t, err)
 	assert.Len(t, res, 1)
 	assert.Equal(t, "WebAuthn credential", res[0].Name)
 }
 
 func TestWebAuthnCredential_TableName(t *testing.T) {
-	assert.Equal(t, "webauthn_credential", WebAuthnCredential{}.TableName())
+	assert.Equal(t, "webauthn_credential", auth_model.WebAuthnCredential{}.TableName())
 }
 
 func TestWebAuthnCredential_UpdateSignCount(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	cred := unittest.AssertExistsAndLoadBean(t, &WebAuthnCredential{ID: 1})
+	cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
 	cred.SignCount = 1
 	assert.NoError(t, cred.UpdateSignCount())
-	unittest.AssertExistsIf(t, true, &WebAuthnCredential{ID: 1, SignCount: 1})
+	unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
 }
 
 func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	cred := unittest.AssertExistsAndLoadBean(t, &WebAuthnCredential{ID: 1})
+	cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
 	cred.SignCount = 0xffffffff
 	assert.NoError(t, cred.UpdateSignCount())
-	unittest.AssertExistsIf(t, true, &WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
+	unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
 }
 
 func TestCreateCredential(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
+	res, err := auth_model.CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
 	assert.NoError(t, err)
 	assert.Equal(t, "WebAuthn Created Credential", res.Name)
 	assert.Equal(t, []byte("Test"), res.CredentialID)
 
-	unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
+	unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
 }
diff --git a/models/consistency.go b/models/consistency.go
deleted file mode 100644
index 18ed9195fc..0000000000
--- a/models/consistency.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
-	"code.gitea.io/gitea/models/db"
-	repo_model "code.gitea.io/gitea/models/repo"
-	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/setting"
-
-	"xorm.io/builder"
-)
-
-// CountNullArchivedRepository counts the number of repositories with is_archived is null
-func CountNullArchivedRepository() (int64, error) {
-	return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(repo_model.Repository))
-}
-
-// FixNullArchivedRepository sets is_archived to false where it is null
-func FixNullArchivedRepository() (int64, error) {
-	return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&repo_model.Repository{
-		IsArchived: false,
-	})
-}
-
-// CountWrongUserType count OrgUser who have wrong type
-func CountWrongUserType() (int64, error) {
-	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(user_model.User))
-}
-
-// FixWrongUserType fix OrgUser who have wrong type
-func FixWrongUserType() (int64, error) {
-	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&user_model.User{Type: 1})
-}
-
-// CountActionCreatedUnixString count actions where created_unix is an empty string
-func CountActionCreatedUnixString() (int64, error) {
-	if setting.Database.UseSQLite3 {
-		return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
-	}
-	return 0, nil
-}
-
-// FixActionCreatedUnixString set created_unix to zero if it is an empty string
-func FixActionCreatedUnixString() (int64, error) {
-	if setting.Database.UseSQLite3 {
-		res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
-		if err != nil {
-			return 0, err
-		}
-		return res.RowsAffected()
-	}
-	return 0, nil
-}
diff --git a/models/error.go b/models/error.go
index 3c617904f8..873ed0ceaa 100644
--- a/models/error.go
+++ b/models/error.go
@@ -57,93 +57,6 @@ func (err ErrUserOwnPackages) Error() string {
 	return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
 }
 
-//  __      __.__ __   .__
-// /  \    /  \__|  | _|__|
-// \   \/\/   /  |  |/ /  |
-//  \        /|  |    <|  |
-//   \__/\  / |__|__|_ \__|
-//        \/          \/
-
-// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
-type ErrWikiAlreadyExist struct {
-	Title string
-}
-
-// IsErrWikiAlreadyExist checks if an error is an ErrWikiAlreadyExist.
-func IsErrWikiAlreadyExist(err error) bool {
-	_, ok := err.(ErrWikiAlreadyExist)
-	return ok
-}
-
-func (err ErrWikiAlreadyExist) Error() string {
-	return fmt.Sprintf("wiki page already exists [title: %s]", err.Title)
-}
-
-// ErrWikiReservedName represents a reserved name error.
-type ErrWikiReservedName struct {
-	Title string
-}
-
-// IsErrWikiReservedName checks if an error is an ErrWikiReservedName.
-func IsErrWikiReservedName(err error) bool {
-	_, ok := err.(ErrWikiReservedName)
-	return ok
-}
-
-func (err ErrWikiReservedName) Error() string {
-	return fmt.Sprintf("wiki title is reserved: %s", err.Title)
-}
-
-// ErrWikiInvalidFileName represents an invalid wiki file name.
-type ErrWikiInvalidFileName struct {
-	FileName string
-}
-
-// IsErrWikiInvalidFileName checks if an error is an ErrWikiInvalidFileName.
-func IsErrWikiInvalidFileName(err error) bool {
-	_, ok := err.(ErrWikiInvalidFileName)
-	return ok
-}
-
-func (err ErrWikiInvalidFileName) Error() string {
-	return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
-}
-
-//    _____                                   ___________     __
-//   /  _  \   ____  ____  ____   ______ _____\__    ___/___ |  | __ ____   ____
-//  /  /_\  \_/ ___\/ ___\/ __ \ /  ___//  ___/ |    | /  _ \|  |/ // __ \ /    \
-// /    |    \  \__\  \__\  ___/ \___ \ \___ \  |    |(  <_> )    <\  ___/|   |  \
-// \____|__  /\___  >___  >___  >____  >____  > |____| \____/|__|_ \\___  >___|  /
-//         \/     \/    \/    \/     \/     \/                    \/    \/     \/
-
-// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
-type ErrAccessTokenNotExist struct {
-	Token string
-}
-
-// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
-func IsErrAccessTokenNotExist(err error) bool {
-	_, ok := err.(ErrAccessTokenNotExist)
-	return ok
-}
-
-func (err ErrAccessTokenNotExist) Error() string {
-	return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
-}
-
-// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
-type ErrAccessTokenEmpty struct{}
-
-// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
-func IsErrAccessTokenEmpty(err error) bool {
-	_, ok := err.(ErrAccessTokenEmpty)
-	return ok
-}
-
-func (err ErrAccessTokenEmpty) Error() string {
-	return "access token is empty"
-}
-
 // ErrNoPendingRepoTransfer is an error type for repositories without a pending
 // transfer request
 type ErrNoPendingRepoTransfer struct {
@@ -178,23 +91,6 @@ func (err ErrRepoTransferInProgress) Error() string {
 	return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
 }
 
-// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
-type ErrForkAlreadyExist struct {
-	Uname    string
-	RepoName string
-	ForkName string
-}
-
-// IsErrForkAlreadyExist checks if an error is an ErrForkAlreadyExist.
-func IsErrForkAlreadyExist(err error) bool {
-	_, ok := err.(ErrForkAlreadyExist)
-	return ok
-}
-
-func (err ErrForkAlreadyExist) Error() string {
-	return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName)
-}
-
 // ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
 type ErrInvalidCloneAddr struct {
 	Host               string
@@ -243,37 +139,6 @@ func (err ErrUpdateTaskNotExist) Error() string {
 	return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
 }
 
-// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
-type ErrReleaseAlreadyExist struct {
-	TagName string
-}
-
-// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
-func IsErrReleaseAlreadyExist(err error) bool {
-	_, ok := err.(ErrReleaseAlreadyExist)
-	return ok
-}
-
-func (err ErrReleaseAlreadyExist) Error() string {
-	return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
-}
-
-// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
-type ErrReleaseNotExist struct {
-	ID      int64
-	TagName string
-}
-
-// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
-func IsErrReleaseNotExist(err error) bool {
-	_, ok := err.(ErrReleaseNotExist)
-	return ok
-}
-
-func (err ErrReleaseNotExist) Error() string {
-	return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
-}
-
 // ErrInvalidTagName represents a "InvalidTagName" kind of error.
 type ErrInvalidTagName struct {
 	TagName string
@@ -657,71 +522,3 @@ func (err ErrPullRequestHasMerged) Error() string {
 	return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
 		err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
 }
-
-//  _________ __                                __         .__
-//  /   _____//  |_  ____ ________  _  _______ _/  |_  ____ |  |__
-//  \_____  \\   __\/  _ \\____ \ \/ \/ /\__  \\   __\/ ___\|  |  \
-//  /        \|  | (  <_> )  |_> >     /  / __ \|  | \  \___|   Y  \
-//  /_______  /|__|  \____/|   __/ \/\_/  (____  /__|  \___  >___|  /
-// \/             |__|                \/          \/     \/
-
-// ErrStopwatchNotExist represents a "Stopwatch Not Exist" kind of error.
-type ErrStopwatchNotExist struct {
-	ID int64
-}
-
-// IsErrStopwatchNotExist checks if an error is a ErrStopwatchNotExist.
-func IsErrStopwatchNotExist(err error) bool {
-	_, ok := err.(ErrStopwatchNotExist)
-	return ok
-}
-
-func (err ErrStopwatchNotExist) Error() string {
-	return fmt.Sprintf("stopwatch does not exist [id: %d]", err.ID)
-}
-
-// ___________                     __              .______________.__
-// \__    ___/___________    ____ |  | __ ____   __| _/\__    ___/|__| _____   ____
-// |    |  \_  __ \__  \ _/ ___\|  |/ // __ \ / __ |   |    |   |  |/     \_/ __ \
-// |    |   |  | \// __ \\  \___|    <\  ___// /_/ |   |    |   |  |  Y Y  \  ___/
-// |____|   |__|  (____  /\___  >__|_ \\___  >____ |   |____|   |__|__|_|  /\___  >
-// \/     \/     \/    \/     \/                     \/     \/
-
-// ErrTrackedTimeNotExist represents a "TrackedTime Not Exist" kind of error.
-type ErrTrackedTimeNotExist struct {
-	ID int64
-}
-
-// IsErrTrackedTimeNotExist checks if an error is a ErrTrackedTimeNotExist.
-func IsErrTrackedTimeNotExist(err error) bool {
-	_, ok := err.(ErrTrackedTimeNotExist)
-	return ok
-}
-
-func (err ErrTrackedTimeNotExist) Error() string {
-	return fmt.Sprintf("tracked time does not exist [id: %d]", err.ID)
-}
-
-//  ____ ___        .__                    .___
-// |    |   \______ |  |   _________     __| _/
-// |    |   /\____ \|  |  /  _ \__  \   / __ |
-// |    |  / |  |_> >  |_(  <_> ) __ \_/ /_/ |
-// |______/  |   __/|____/\____(____  /\____ |
-//           |__|                   \/      \/
-//
-
-// ErrUploadNotExist represents a "UploadNotExist" kind of error.
-type ErrUploadNotExist struct {
-	ID   int64
-	UUID string
-}
-
-// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
-func IsErrUploadNotExist(err error) bool {
-	_, ok := err.(ErrUploadNotExist)
-	return ok
-}
-
-func (err ErrUploadNotExist) Error() string {
-	return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
-}
diff --git a/models/main_test.go b/models/main_test.go
index bb2fedc15a..49b6e3e560 100644
--- a/models/main_test.go
+++ b/models/main_test.go
@@ -7,6 +7,7 @@ package models
 import (
 	"testing"
 
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -28,7 +29,7 @@ func TestFixturesAreConsistent(t *testing.T) {
 		&user_model.User{},
 		&repo_model.Repository{},
 		&organization.Team{},
-		&Action{})
+		&activities_model.Action{})
 }
 
 func TestMain(m *testing.M) {
diff --git a/models/migrate.go b/models/migrate.go
index 0af3891cb8..f6bceaa019 100644
--- a/models/migrate.go
+++ b/models/migrate.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/structs"
 )
 
@@ -154,7 +155,7 @@ func InsertPullRequests(prs ...*issues_model.PullRequest) error {
 }
 
 // InsertReleases migrates release
-func InsertReleases(rels ...*Release) error {
+func InsertReleases(rels ...*repo_model.Release) error {
 	ctx, committer, err := db.TxContext()
 	if err != nil {
 		return err
@@ -191,7 +192,7 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us
 		return err
 	}
 
-	if err := UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil {
+	if err := repo_model.UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil {
 		return err
 	}
 
diff --git a/models/migrate_test.go b/models/migrate_test.go
index 627fb1ae73..bc7729673a 100644
--- a/models/migrate_test.go
+++ b/models/migrate_test.go
@@ -149,7 +149,7 @@ func TestMigrate_InsertReleases(t *testing.T) {
 	a := &repo_model.Attachment{
 		UUID: "a0eebc91-9c0c-4ef7-bb6e-6bb9bd380a12",
 	}
-	r := &Release{
+	r := &repo_model.Release{
 		Attachments: []*repo_model.Attachment{a},
 	}
 
diff --git a/models/org.go b/models/org.go
index efcb7183e7..53be80c3df 100644
--- a/models/org.go
+++ b/models/org.go
@@ -8,79 +8,13 @@ package models
 import (
 	"context"
 	"fmt"
-	"strings"
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
-	"code.gitea.io/gitea/models/unit"
-	user_model "code.gitea.io/gitea/models/user"
-
-	"xorm.io/builder"
 )
 
-// MinimalOrg represents a simple orgnization with only needed columns
-type MinimalOrg = organization.Organization
-
-// GetUserOrgsList returns one user's all orgs list
-func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
-	schema, err := db.TableInfo(new(user_model.User))
-	if err != nil {
-		return nil, err
-	}
-
-	outputCols := []string{
-		"id",
-		"name",
-		"full_name",
-		"visibility",
-		"avatar",
-		"avatar_email",
-		"use_custom_avatar",
-	}
-
-	groupByCols := &strings.Builder{}
-	for _, col := range outputCols {
-		fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
-	}
-	groupByStr := groupByCols.String()
-	groupByStr = groupByStr[0 : len(groupByStr)-1]
-
-	sess := db.GetEngine(db.DefaultContext)
-	sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count").
-		Table("user").
-		Join("INNER", "team", "`team`.org_id = `user`.id").
-		Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
-		Join("LEFT", builder.
-			Select("id as repo_id, owner_id as repo_owner_id").
-			From("repository").
-			Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
-		Where("`team_user`.uid = ?", user.ID).
-		GroupBy(groupByStr)
-
-	type OrgCount struct {
-		organization.Organization `xorm:"extends"`
-		OrgCount                  int
-	}
-
-	orgCounts := make([]*OrgCount, 0, 10)
-
-	if err := sess.
-		Asc("`user`.name").
-		Find(&orgCounts); err != nil {
-		return nil, err
-	}
-
-	orgs := make([]*MinimalOrg, len(orgCounts))
-	for i, orgCount := range orgCounts {
-		orgCount.Organization.NumRepos = orgCount.OrgCount
-		orgs[i] = &orgCount.Organization
-	}
-
-	return orgs, nil
-}
-
 func removeOrgUser(ctx context.Context, orgID, userID int64) error {
 	ou := new(organization.OrgUser)
 
diff --git a/models/org_team.go b/models/org_team.go
index 5d29e33337..61ddd2a047 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -25,12 +25,12 @@ import (
 	"xorm.io/builder"
 )
 
-func addRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
+func AddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
 	if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
 		return err
 	}
 
-	if _, err = db.GetEngine(ctx).Incr("num_repos").ID(t.ID).Update(new(organization.Team)); err != nil {
+	if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
 		return fmt.Errorf("update team: %v", err)
 	}
 
@@ -58,16 +58,15 @@ func addRepository(ctx context.Context, t *organization.Team, repo *repo_model.R
 // addAllRepositories adds all repositories to the team.
 // If the team already has some repositories they will be left unchanged.
 func addAllRepositories(ctx context.Context, t *organization.Team) error {
-	var orgRepos []repo_model.Repository
-	e := db.GetEngine(ctx)
-	if err := e.Where("owner_id = ?", t.OrgID).Find(&orgRepos); err != nil {
+	orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID)
+	if err != nil {
 		return fmt.Errorf("get org repos: %v", err)
 	}
 
 	for _, repo := range orgRepos {
 		if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
-			if err := addRepository(ctx, t, &repo); err != nil {
-				return fmt.Errorf("addRepository: %v", err)
+			if err := AddRepository(ctx, t, repo); err != nil {
+				return fmt.Errorf("AddRepository: %v", err)
 			}
 		}
 	}
@@ -90,27 +89,6 @@ func AddAllRepositories(t *organization.Team) (err error) {
 	return committer.Commit()
 }
 
-// AddRepository adds new repository to team of organization.
-func AddRepository(t *organization.Team, repo *repo_model.Repository) (err error) {
-	if repo.OwnerID != t.OrgID {
-		return errors.New("Repository does not belong to organization")
-	} else if HasRepository(t, repo.ID) {
-		return nil
-	}
-
-	ctx, committer, err := db.TxContext()
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err = addRepository(ctx, t, repo); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
 // RemoveAllRepositories removes all repositories from team and recalculates access
 func RemoveAllRepositories(t *organization.Team) (err error) {
 	if t.IncludesAllRepositories {
diff --git a/models/org_team_test.go b/models/org_team_test.go
index a0de6d73f1..a600d07c0c 100644
--- a/models/org_team_test.go
+++ b/models/org_team_test.go
@@ -68,25 +68,6 @@ func TestTeam_HasRepository(t *testing.T) {
 	test(2, 5, false)
 }
 
-func TestTeam_AddRepository(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	testSuccess := func(teamID, repoID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
-		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
-		assert.NoError(t, AddRepository(team, repo))
-		unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
-		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
-	}
-	testSuccess(2, 3)
-	testSuccess(2, 5)
-
-	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
-	assert.Error(t, AddRepository(team, repo))
-	unittest.CheckConsistencyFor(t, &organization.Team{ID: 1}, &repo_model.Repository{ID: 1})
-}
-
 func TestTeam_RemoveRepository(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
diff --git a/models/organization/mini_org.go b/models/organization/mini_org.go
new file mode 100644
index 0000000000..36cf948e65
--- /dev/null
+++ b/models/organization/mini_org.go
@@ -0,0 +1,78 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package organization
+
+import (
+	"fmt"
+	"strings"
+
+	"code.gitea.io/gitea/models/db"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
+
+	"xorm.io/builder"
+)
+
+// MinimalOrg represents a simple organization with only the needed columns
+type MinimalOrg = Organization
+
+// GetUserOrgsList returns all organizations the given user has access to
+func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
+	schema, err := db.TableInfo(new(user_model.User))
+	if err != nil {
+		return nil, err
+	}
+
+	outputCols := []string{
+		"id",
+		"name",
+		"full_name",
+		"visibility",
+		"avatar",
+		"avatar_email",
+		"use_custom_avatar",
+	}
+
+	groupByCols := &strings.Builder{}
+	for _, col := range outputCols {
+		fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
+	}
+	groupByStr := groupByCols.String()
+	groupByStr = groupByStr[0 : len(groupByStr)-1]
+
+	sess := db.GetEngine(db.DefaultContext)
+	sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count").
+		Table("user").
+		Join("INNER", "team", "`team`.org_id = `user`.id").
+		Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
+		Join("LEFT", builder.
+			Select("id as repo_id, owner_id as repo_owner_id").
+			From("repository").
+			Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
+		Where("`team_user`.uid = ?", user.ID).
+		GroupBy(groupByStr)
+
+	type OrgCount struct {
+		Organization `xorm:"extends"`
+		OrgCount     int
+	}
+
+	orgCounts := make([]*OrgCount, 0, 10)
+
+	if err := sess.
+		Asc("`user`.name").
+		Find(&orgCounts); err != nil {
+		return nil, err
+	}
+
+	orgs := make([]*MinimalOrg, len(orgCounts))
+	for i, orgCount := range orgCounts {
+		orgCount.Organization.NumRepos = orgCount.OrgCount
+		orgs[i] = &orgCount.Organization
+	}
+
+	return orgs, nil
+}
diff --git a/models/organization/org_repo.go b/models/organization/org_repo.go
new file mode 100644
index 0000000000..364374f71b
--- /dev/null
+++ b/models/organization/org_repo.go
@@ -0,0 +1,18 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package organization
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	repo_model "code.gitea.io/gitea/models/repo"
+)
+
+// GetOrgRepositories get repos belonging to the given organization
+func GetOrgRepositories(ctx context.Context, orgID int64) ([]*repo_model.Repository, error) {
+	var orgRepos []*repo_model.Repository
+	return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
+}
diff --git a/models/organization/team.go b/models/organization/team.go
index 6787b9e0fa..2d5ee17272 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -359,3 +359,9 @@ func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams []*Te
 		OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
 		Find(&teams)
 }
+
+// IncrTeamRepoNum increases the number of repos for the given team by 1
+func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
+	_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
+	return err
+}
diff --git a/models/perm/access/access_test.go b/models/perm/access/access_test.go
index 8e75792e0c..7f58be4f39 100644
--- a/models/perm/access/access_test.go
+++ b/models/perm/access/access_test.go
@@ -7,13 +7,10 @@ package access_test
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/models/organization"
 	perm_model "code.gitea.io/gitea/models/perm"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
-	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 
@@ -128,249 +125,3 @@ func TestRepository_RecalculateAccesses2(t *testing.T) {
 	assert.NoError(t, err)
 	assert.False(t, has)
 }
-
-func TestRepoPermissionPublicNonOrgRepo(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	// public non-organization repo
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
-	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-	// plain user
-	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.False(t, perm.CanWrite(unit.Type))
-	}
-
-	// change to collaborator
-	assert.NoError(t, models.AddCollaborator(repo, user))
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	// collaborator
-	collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, collaborator)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	// owner
-	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	// admin
-	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-}
-
-func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	// private non-organization repo
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
-	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-	// plain user
-	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
-	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.False(t, perm.CanRead(unit.Type))
-		assert.False(t, perm.CanWrite(unit.Type))
-	}
-
-	// change to collaborator to default write access
-	assert.NoError(t, models.AddCollaborator(repo, user))
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.False(t, perm.CanWrite(unit.Type))
-	}
-
-	// owner
-	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	// admin
-	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-}
-
-func TestRepoPermissionPublicOrgRepo(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	// public organization repo
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32})
-	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-	// plain user
-	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
-	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.False(t, perm.CanWrite(unit.Type))
-	}
-
-	// change to collaborator to default write access
-	assert.NoError(t, models.AddCollaborator(repo, user))
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.False(t, perm.CanWrite(unit.Type))
-	}
-
-	// org member team owner
-	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	// org member team tester
-	member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, member)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-	}
-	assert.True(t, perm.CanWrite(unit.TypeIssues))
-	assert.False(t, perm.CanWrite(unit.TypeCode))
-
-	// admin
-	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-}
-
-func TestRepoPermissionPrivateOrgRepo(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	// private organization repo
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24})
-	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-	// plain user
-	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
-	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.False(t, perm.CanRead(unit.Type))
-		assert.False(t, perm.CanWrite(unit.Type))
-	}
-
-	// change to collaborator to default write access
-	assert.NoError(t, models.AddCollaborator(repo, user))
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.False(t, perm.CanWrite(unit.Type))
-	}
-
-	// org member team owner
-	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	// update team information and then check permission
-	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
-	err = organization.UpdateTeamUnits(team, nil)
-	assert.NoError(t, err)
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-
-	// org member team tester
-	tester := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, tester)
-	assert.NoError(t, err)
-	assert.True(t, perm.CanWrite(unit.TypeIssues))
-	assert.False(t, perm.CanWrite(unit.TypeCode))
-	assert.False(t, perm.CanRead(unit.TypeCode))
-
-	// org member team reviewer
-	reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, reviewer)
-	assert.NoError(t, err)
-	assert.False(t, perm.CanRead(unit.TypeIssues))
-	assert.False(t, perm.CanWrite(unit.TypeCode))
-	assert.True(t, perm.CanRead(unit.TypeCode))
-
-	// admin
-	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-	assert.NoError(t, err)
-	for _, unit := range repo.Units {
-		assert.True(t, perm.CanRead(unit.Type))
-		assert.True(t, perm.CanWrite(unit.Type))
-	}
-}
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index 99919c70bf..93e3bdd6d8 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -430,3 +430,17 @@ func IsRepoReader(ctx context.Context, repo *repo_model.Repository, userID int64
 	}
 	return db.GetEngine(ctx).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm_model.AccessModeRead).Get(&Access{})
 }
+
+// CheckRepoUnitUser check whether user could visit the unit of this repository
+func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
+	if user != nil && user.IsAdmin {
+		return true
+	}
+	perm, err := GetUserRepoPermission(ctx, repo, user)
+	if err != nil {
+		log.Error("GetUserRepoPermission: %w", err)
+		return false
+	}
+
+	return perm.CanRead(unitType)
+}
diff --git a/models/repo.go b/models/repo.go
index 66ef514739..35af6c1ef4 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -12,13 +12,13 @@ import (
 
 	_ "image/jpeg" // Needed for jpeg support
 
+	activities_model "code.gitea.io/gitea/models/activities"
 	admin_model "code.gitea.io/gitea/models/admin"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/organization"
-	"code.gitea.io/gitea/models/perm"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	project_model "code.gitea.io/gitea/models/project"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -27,10 +27,7 @@ import (
 	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/log"
-	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/storage"
-	api "code.gitea.io/gitea/modules/structs"
-	"code.gitea.io/gitea/modules/util"
 
 	"xorm.io/builder"
 )
@@ -40,162 +37,6 @@ func NewRepoContext() {
 	unit.LoadUnitConfig()
 }
 
-// CheckRepoUnitUser check whether user could visit the unit of this repository
-func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
-	if user != nil && user.IsAdmin {
-		return true
-	}
-	perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
-	if err != nil {
-		log.Error("GetUserRepoPermission(): %v", err)
-		return false
-	}
-
-	return perm.CanRead(unitType)
-}
-
-// CreateRepoOptions contains the create repository options
-type CreateRepoOptions struct {
-	Name           string
-	Description    string
-	OriginalURL    string
-	GitServiceType api.GitServiceType
-	Gitignores     string
-	IssueLabels    string
-	License        string
-	Readme         string
-	DefaultBranch  string
-	IsPrivate      bool
-	IsMirror       bool
-	IsTemplate     bool
-	AutoInit       bool
-	Status         repo_model.RepositoryStatus
-	TrustModel     repo_model.TrustModelType
-	MirrorInterval string
-}
-
-// CreateRepository creates a repository for the user/organization.
-func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt bool) (err error) {
-	if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
-		return err
-	}
-
-	has, err := repo_model.IsRepositoryExist(ctx, u, repo.Name)
-	if err != nil {
-		return fmt.Errorf("IsRepositoryExist: %v", err)
-	} else if has {
-		return repo_model.ErrRepoAlreadyExist{
-			Uname: u.Name,
-			Name:  repo.Name,
-		}
-	}
-
-	repoPath := repo_model.RepoPath(u.Name, repo.Name)
-	isExist, err := util.IsExist(repoPath)
-	if err != nil {
-		log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
-		return err
-	}
-	if !overwriteOrAdopt && isExist {
-		log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
-		return repo_model.ErrRepoFilesAlreadyExist{
-			Uname: u.Name,
-			Name:  repo.Name,
-		}
-	}
-
-	if err = db.Insert(ctx, repo); err != nil {
-		return err
-	}
-	if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
-		return err
-	}
-
-	// insert units for repo
-	units := make([]repo_model.RepoUnit, 0, len(unit.DefaultRepoUnits))
-	for _, tp := range unit.DefaultRepoUnits {
-		if tp == unit.TypeIssues {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   tp,
-				Config: &repo_model.IssuesConfig{
-					EnableTimetracker:                setting.Service.DefaultEnableTimetracking,
-					AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
-					EnableDependencies:               setting.Service.DefaultEnableDependencies,
-				},
-			})
-		} else if tp == unit.TypePullRequests {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   tp,
-				Config: &repo_model.PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true},
-			})
-		} else {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   tp,
-			})
-		}
-	}
-
-	if err = db.Insert(ctx, units); err != nil {
-		return err
-	}
-
-	// Remember visibility preference.
-	u.LastRepoVisibility = repo.IsPrivate
-	if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil {
-		return fmt.Errorf("updateUser: %v", err)
-	}
-
-	if _, err = db.GetEngine(ctx).Incr("num_repos").ID(u.ID).Update(new(user_model.User)); err != nil {
-		return fmt.Errorf("increment user total_repos: %v", err)
-	}
-	u.NumRepos++
-
-	// Give access to all members in teams with access to all repositories.
-	if u.IsOrganization() {
-		teams, err := organization.FindOrgTeams(ctx, u.ID)
-		if err != nil {
-			return fmt.Errorf("loadTeams: %v", err)
-		}
-		for _, t := range teams {
-			if t.IncludesAllRepositories {
-				if err := addRepository(ctx, t, repo); err != nil {
-					return fmt.Errorf("addRepository: %v", err)
-				}
-			}
-		}
-
-		if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
-			return fmt.Errorf("IsUserRepoAdminCtx: %v", err)
-		} else if !isAdmin {
-			// Make creator repo admin if it wasn't assigned automatically
-			if err = addCollaborator(ctx, repo, doer); err != nil {
-				return fmt.Errorf("AddCollaborator: %v", err)
-			}
-			if err = repo_model.ChangeCollaborationAccessModeCtx(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil {
-				return fmt.Errorf("ChangeCollaborationAccessMode: %v", err)
-			}
-		}
-	} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
-		// Organization automatically called this in addRepository method.
-		return fmt.Errorf("recalculateAccesses: %v", err)
-	}
-
-	if setting.Service.AutoWatchNewRepos {
-		if err = repo_model.WatchRepo(ctx, doer.ID, repo.ID, true); err != nil {
-			return fmt.Errorf("watchRepo: %v", err)
-		}
-	}
-
-	if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
-		return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
-	}
-
-	return nil
-}
-
 // DeleteRepository deletes a repository for a user or organization.
 // make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
 func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
@@ -279,7 +120,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
 
 	if err := db.DeleteBeans(ctx,
 		&access_model.Access{RepoID: repo.ID},
-		&Action{RepoID: repo.ID},
+		&activities_model.Action{RepoID: repo.ID},
 		&repo_model.Collaboration{RepoID: repoID},
 		&issues_model.Comment{RefRepoID: repoID},
 		&git_model.CommitStatus{RepoID: repoID},
@@ -289,16 +130,16 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
 		&repo_model.LanguageStat{RepoID: repoID},
 		&issues_model.Milestone{RepoID: repoID},
 		&repo_model.Mirror{RepoID: repoID},
-		&Notification{RepoID: repoID},
+		&activities_model.Notification{RepoID: repoID},
 		&git_model.ProtectedBranch{RepoID: repoID},
 		&git_model.ProtectedTag{RepoID: repoID},
 		&repo_model.PushMirror{RepoID: repoID},
-		&Release{RepoID: repoID},
+		&repo_model.Release{RepoID: repoID},
 		&repo_model.RepoIndexerStatus{RepoID: repoID},
 		&repo_model.Redirect{RedirectRepoID: repoID},
 		&repo_model.RepoUnit{RepoID: repoID},
 		&repo_model.Star{RepoID: repoID},
-		&Task{RepoID: repoID},
+		&admin_model.Task{RepoID: repoID},
 		&repo_model.Watch{RepoID: repoID},
 		&webhook.Webhook{RepoID: repoID},
 	); err != nil {
diff --git a/models/release.go b/models/repo/release.go
similarity index 85%
rename from models/release.go
rename to models/repo/release.go
index b169920f2f..9a4de26c68 100644
--- a/models/release.go
+++ b/models/repo/release.go
@@ -3,7 +3,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package repo
 
 import (
 	"context"
@@ -14,7 +14,6 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
-	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
@@ -23,14 +22,45 @@ import (
 	"xorm.io/builder"
 )
 
+// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
+type ErrReleaseAlreadyExist struct {
+	TagName string
+}
+
+// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
+func IsErrReleaseAlreadyExist(err error) bool {
+	_, ok := err.(ErrReleaseAlreadyExist)
+	return ok
+}
+
+func (err ErrReleaseAlreadyExist) Error() string {
+	return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
+}
+
+// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
+type ErrReleaseNotExist struct {
+	ID      int64
+	TagName string
+}
+
+// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
+func IsErrReleaseNotExist(err error) bool {
+	_, ok := err.(ErrReleaseNotExist)
+	return ok
+}
+
+func (err ErrReleaseNotExist) Error() string {
+	return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
+}
+
 // Release represents a release of repository.
 type Release struct {
-	ID               int64                  `xorm:"pk autoincr"`
-	RepoID           int64                  `xorm:"INDEX UNIQUE(n)"`
-	Repo             *repo_model.Repository `xorm:"-"`
-	PublisherID      int64                  `xorm:"INDEX"`
-	Publisher        *user_model.User       `xorm:"-"`
-	TagName          string                 `xorm:"INDEX UNIQUE(n)"`
+	ID               int64            `xorm:"pk autoincr"`
+	RepoID           int64            `xorm:"INDEX UNIQUE(n)"`
+	Repo             *Repository      `xorm:"-"`
+	PublisherID      int64            `xorm:"INDEX"`
+	Publisher        *user_model.User `xorm:"-"`
+	TagName          string           `xorm:"INDEX UNIQUE(n)"`
 	OriginalAuthor   string
 	OriginalAuthorID int64 `xorm:"index"`
 	LowerTagName     string
@@ -38,14 +68,14 @@ type Release struct {
 	Title            string
 	Sha1             string `xorm:"VARCHAR(40)"`
 	NumCommits       int64
-	NumCommitsBehind int64                    `xorm:"-"`
-	Note             string                   `xorm:"TEXT"`
-	RenderedNote     string                   `xorm:"-"`
-	IsDraft          bool                     `xorm:"NOT NULL DEFAULT false"`
-	IsPrerelease     bool                     `xorm:"NOT NULL DEFAULT false"`
-	IsTag            bool                     `xorm:"NOT NULL DEFAULT false"`
-	Attachments      []*repo_model.Attachment `xorm:"-"`
-	CreatedUnix      timeutil.TimeStamp       `xorm:"INDEX"`
+	NumCommitsBehind int64              `xorm:"-"`
+	Note             string             `xorm:"TEXT"`
+	RenderedNote     string             `xorm:"-"`
+	IsDraft          bool               `xorm:"NOT NULL DEFAULT false"`
+	IsPrerelease     bool               `xorm:"NOT NULL DEFAULT false"`
+	IsTag            bool               `xorm:"NOT NULL DEFAULT false"`
+	Attachments      []*Attachment      `xorm:"-"`
+	CreatedUnix      timeutil.TimeStamp `xorm:"INDEX"`
 }
 
 func init() {
@@ -55,7 +85,7 @@ func init() {
 func (r *Release) loadAttributes(ctx context.Context) error {
 	var err error
 	if r.Repo == nil {
-		r.Repo, err = repo_model.GetRepositoryByIDCtx(ctx, r.RepoID)
+		r.Repo, err = GetRepositoryByIDCtx(ctx, r.RepoID)
 		if err != nil {
 			return err
 		}
@@ -116,7 +146,7 @@ func UpdateRelease(ctx context.Context, rel *Release) error {
 // AddReleaseAttachments adds a release attachments
 func AddReleaseAttachments(ctx context.Context, releaseID int64, attachmentUUIDs []string) (err error) {
 	// Check attachments
-	attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, attachmentUUIDs)
+	attachments, err := GetAttachmentsByUUIDs(ctx, attachmentUUIDs)
 	if err != nil {
 		return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %v", attachmentUUIDs, err)
 	}
@@ -279,9 +309,9 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
 
 	// Sort
 	sortedRels := releaseMetaSearch{ID: make([]int64, len(rels)), Rel: make([]*Release, len(rels))}
-	var attachments []*repo_model.Attachment
+	var attachments []*Attachment
 	for index, element := range rels {
-		element.Attachments = []*repo_model.Attachment{}
+		element.Attachments = []*Attachment{}
 		sortedRels.ID[index] = element.ID
 		sortedRels.Rel[index] = element
 	}
@@ -291,7 +321,7 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
 	err = db.GetEngine(ctx).
 		Asc("release_id", "name").
 		In("release_id", sortedRels.ID).
-		Find(&attachments, repo_model.Attachment{})
+		Find(&attachments, Attachment{})
 	if err != nil {
 		return err
 	}
@@ -354,7 +384,7 @@ func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, origi
 }
 
 // PushUpdateDeleteTagsContext updates a number of delete tags with context
-func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repository, tags []string) error {
+func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []string) error {
 	if len(tags) == 0 {
 		return nil
 	}
@@ -384,7 +414,7 @@ func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repositor
 }
 
 // PushUpdateDeleteTag must be called for any push actions to delete tag
-func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
+func PushUpdateDeleteTag(repo *Repository, tagName string) error {
 	rel, err := GetRelease(repo.ID, tagName)
 	if err != nil {
 		if IsErrReleaseNotExist(err) {
@@ -409,7 +439,7 @@ func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
 }
 
 // SaveOrUpdateTag must be called for any push actions to add tag
-func SaveOrUpdateTag(repo *repo_model.Repository, newRel *Release) error {
+func SaveOrUpdateTag(repo *Repository, newRel *Release) error {
 	rel, err := GetRelease(repo.ID, newRel.TagName)
 	if err != nil && !IsErrReleaseNotExist(err) {
 		return fmt.Errorf("GetRelease: %v", err)
diff --git a/models/repo/repo.go b/models/repo/repo.go
index bb2a7468ff..9a582c8fe1 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -23,6 +23,8 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
+
+	"xorm.io/builder"
 )
 
 // ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo.
@@ -784,3 +786,15 @@ func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed
 	}
 	return nil
 }
+
+// CountNullArchivedRepository counts the number of repositories with is_archived is null
+func CountNullArchivedRepository() (int64, error) {
+	return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(Repository))
+}
+
+// FixNullArchivedRepository sets is_archived to false where it is null
+func FixNullArchivedRepository() (int64, error) {
+	return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&Repository{
+		IsArchived: false,
+	})
+}
diff --git a/models/upload.go b/models/repo/upload.go
similarity index 88%
rename from models/upload.go
rename to models/repo/upload.go
index 4a64ff34e3..24544910b1 100644
--- a/models/upload.go
+++ b/models/repo/upload.go
@@ -3,7 +3,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package models
+package repo
 
 import (
 	"fmt"
@@ -20,13 +20,21 @@ import (
 	gouuid "github.com/google/uuid"
 )
 
-//  ____ ___        .__                    .___ ___________.___.__
-// |    |   \______ |  |   _________     __| _/ \_   _____/|   |  |   ____   ______
-// |    |   /\____ \|  |  /  _ \__  \   / __ |   |    __)  |   |  | _/ __ \ /  ___/
-// |    |  / |  |_> >  |_(  <_> ) __ \_/ /_/ |   |     \   |   |  |_\  ___/ \___ \
-// |______/  |   __/|____/\____(____  /\____ |   \___  /   |___|____/\___  >____  >
-//           |__|                   \/      \/       \/                  \/     \/
-//
+// ErrUploadNotExist represents a "UploadNotExist" kind of error.
+type ErrUploadNotExist struct {
+	ID   int64
+	UUID string
+}
+
+// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
+func IsErrUploadNotExist(err error) bool {
+	_, ok := err.(ErrUploadNotExist)
+	return ok
+}
+
+func (err ErrUploadNotExist) Error() string {
+	return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
+}
 
 // Upload represent a uploaded file to a repo to be deleted when moved
 type Upload struct {
diff --git a/models/repo/wiki.go b/models/repo/wiki.go
index abf0155cad..72ec7c394b 100644
--- a/models/repo/wiki.go
+++ b/models/repo/wiki.go
@@ -6,6 +6,7 @@
 package repo
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -14,6 +15,51 @@ import (
 	"code.gitea.io/gitea/modules/util"
 )
 
+// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
+type ErrWikiAlreadyExist struct {
+	Title string
+}
+
+// IsErrWikiAlreadyExist checks if an error is an ErrWikiAlreadyExist.
+func IsErrWikiAlreadyExist(err error) bool {
+	_, ok := err.(ErrWikiAlreadyExist)
+	return ok
+}
+
+func (err ErrWikiAlreadyExist) Error() string {
+	return fmt.Sprintf("wiki page already exists [title: %s]", err.Title)
+}
+
+// ErrWikiReservedName represents a reserved name error.
+type ErrWikiReservedName struct {
+	Title string
+}
+
+// IsErrWikiReservedName checks if an error is an ErrWikiReservedName.
+func IsErrWikiReservedName(err error) bool {
+	_, ok := err.(ErrWikiReservedName)
+	return ok
+}
+
+func (err ErrWikiReservedName) Error() string {
+	return fmt.Sprintf("wiki title is reserved: %s", err.Title)
+}
+
+// ErrWikiInvalidFileName represents an invalid wiki file name.
+type ErrWikiInvalidFileName struct {
+	FileName string
+}
+
+// IsErrWikiInvalidFileName checks if an error is an ErrWikiInvalidFileName.
+func IsErrWikiInvalidFileName(err error) bool {
+	_, ok := err.(ErrWikiInvalidFileName)
+	return ok
+}
+
+func (err ErrWikiInvalidFileName) Error() string {
+	return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
+}
+
 // WikiCloneLink returns clone URLs of repository wiki.
 func (repo *Repository) WikiCloneLink() *CloneLink {
 	return repo.cloneLink(true)
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index c8866421bd..05df2f29aa 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -11,7 +11,6 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
-	"code.gitea.io/gitea/models/perm"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -19,42 +18,6 @@ import (
 	"xorm.io/builder"
 )
 
-func addCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error {
-	collaboration := &repo_model.Collaboration{
-		RepoID: repo.ID,
-		UserID: u.ID,
-	}
-
-	has, err := db.GetByBean(ctx, collaboration)
-	if err != nil {
-		return err
-	} else if has {
-		return nil
-	}
-	collaboration.Mode = perm.AccessModeWrite
-
-	if err = db.Insert(ctx, collaboration); err != nil {
-		return err
-	}
-
-	return access_model.RecalculateUserAccess(ctx, repo, u.ID)
-}
-
-// AddCollaborator adds new collaboration to a repository with default access mode.
-func AddCollaborator(repo *repo_model.Repository, u *user_model.User) error {
-	ctx, committer, err := db.TxContext()
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err := addCollaborator(ctx, repo, u); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
 // DeleteCollaboration removes collaboration relation between the user and repository.
 func DeleteCollaboration(repo *repo_model.Repository, uid int64) (err error) {
 	collaboration := &repo_model.Collaboration{
diff --git a/models/repo_collaboration_test.go b/models/repo_collaboration_test.go
index 72b354f1a5..77034b65d2 100644
--- a/models/repo_collaboration_test.go
+++ b/models/repo_collaboration_test.go
@@ -10,26 +10,10 @@ import (
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
-	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
 
-func TestRepository_AddCollaborator(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	testSuccess := func(repoID, userID int64) {
-		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
-		assert.NoError(t, repo.GetOwner(db.DefaultContext))
-		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
-		assert.NoError(t, AddCollaborator(repo, user))
-		unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID})
-	}
-	testSuccess(1, 4)
-	testSuccess(1, 4)
-	testSuccess(3, 4)
-}
-
 func TestRepository_DeleteCollaboration(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
diff --git a/models/repo_transfer.go b/models/repo_transfer.go
index 7d07fb252c..636d49b989 100644
--- a/models/repo_transfer.go
+++ b/models/repo_transfer.go
@@ -327,8 +327,8 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
 		}
 		for _, t := range teams {
 			if t.IncludesAllRepositories {
-				if err := addRepository(ctx, t, repo); err != nil {
-					return fmt.Errorf("addRepository: %v", err)
+				if err := AddRepository(ctx, t, repo); err != nil {
+					return fmt.Errorf("AddRepository: %v", err)
 				}
 			}
 		}
diff --git a/models/user.go b/models/user.go
index 4afbb9bea5..fceb5aabec 100644
--- a/models/user.go
+++ b/models/user.go
@@ -12,6 +12,7 @@ import (
 
 	_ "image/jpeg" // Needed for jpeg support
 
+	activities_model "code.gitea.io/gitea/models/activities"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
@@ -70,14 +71,14 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
 	// ***** END: Follow *****
 
 	if err = db.DeleteBeans(ctx,
-		&AccessToken{UID: u.ID},
+		&auth_model.AccessToken{UID: u.ID},
 		&repo_model.Collaboration{UserID: u.ID},
 		&access_model.Access{UserID: u.ID},
 		&repo_model.Watch{UserID: u.ID},
 		&repo_model.Star{UID: u.ID},
 		&user_model.Follow{UserID: u.ID},
 		&user_model.Follow{FollowID: u.ID},
-		&Action{UserID: u.ID},
+		&activities_model.Action{UserID: u.ID},
 		&issues_model.IssueUser{UID: u.ID},
 		&user_model.EmailAddress{UID: u.ID},
 		&user_model.UserOpenID{UID: u.ID},
diff --git a/models/user/user.go b/models/user/user.go
index 91eeeb8962..f1df335eb6 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -1317,6 +1317,16 @@ func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
 	return false
 }
 
+// CountWrongUserType count OrgUser who have wrong type
+func CountWrongUserType() (int64, error) {
+	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(User))
+}
+
+// FixWrongUserType fix OrgUser who have wrong type
+func FixWrongUserType() (int64, error) {
+	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&User{Type: 1})
+}
+
 func GetOrderByName() string {
 	if setting.UI.DefaultShowFullName {
 		return "full_name, name"
diff --git a/models/user/user_update.go b/models/user/user_update.go
new file mode 100644
index 0000000000..9c9dc09bb2
--- /dev/null
+++ b/models/user/user_update.go
@@ -0,0 +1,16 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package user
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+)
+
+func IncrUserRepoNum(ctx context.Context, userID int64) error {
+	_, err := db.GetEngine(ctx).Incr("num_repos").ID(userID).Update(new(User))
+	return err
+}
diff --git a/modules/context/repo.go b/modules/context/repo.go
index ea40542069..1e0f6461a2 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -118,7 +118,8 @@ type CanCommitToBranchResults struct {
 }
 
 // CanCommitToBranch returns true if repository is editable and user has proper access level
-//   and branch is not protected for push
+//
+// and branch is not protected for push
 func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.User) (CanCommitToBranchResults, error) {
 	protectedBranch, err := git_model.GetProtectedBranchBy(ctx, r.Repository.ID, r.BranchName)
 	if err != nil {
@@ -523,14 +524,14 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 		ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
 	}
 
-	ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
+	ctx.Data["NumTags"], err = repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{
 		IncludeTags: true,
 	})
 	if err != nil {
 		ctx.ServerError("GetReleaseCountByRepoID", err)
 		return
 	}
-	ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{})
+	ctx.Data["NumReleases"], err = repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{})
 	if err != nil {
 		ctx.ServerError("GetReleaseCountByRepoID", err)
 		return
diff --git a/modules/convert/notification.go b/modules/convert/notification.go
index 1efba5745c..55f782f8f6 100644
--- a/modules/convert/notification.go
+++ b/modules/convert/notification.go
@@ -7,17 +7,17 @@ package convert
 import (
 	"net/url"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/perm"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
 // ToNotificationThread convert a Notification to api.NotificationThread
-func ToNotificationThread(n *models.Notification) *api.NotificationThread {
+func ToNotificationThread(n *activities_model.Notification) *api.NotificationThread {
 	result := &api.NotificationThread{
 		ID:        n.ID,
-		Unread:    !(n.Status == models.NotificationStatusRead || n.Status == models.NotificationStatusPinned),
-		Pinned:    n.Status == models.NotificationStatusPinned,
+		Unread:    !(n.Status == activities_model.NotificationStatusRead || n.Status == activities_model.NotificationStatusPinned),
+		Pinned:    n.Status == activities_model.NotificationStatusPinned,
 		UpdatedAt: n.UpdatedUnix.AsTime(),
 		URL:       n.APIURL(),
 	}
@@ -34,7 +34,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
 
 	// handle Subject
 	switch n.Source {
-	case models.NotificationSourceIssue:
+	case activities_model.NotificationSourceIssue:
 		result.Subject = &api.NotificationSubject{Type: api.NotifySubjectIssue}
 		if n.Issue != nil {
 			result.Subject.Title = n.Issue.Title
@@ -47,7 +47,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
 				result.Subject.LatestCommentHTMLURL = comment.HTMLURL()
 			}
 		}
-	case models.NotificationSourcePullRequest:
+	case activities_model.NotificationSourcePullRequest:
 		result.Subject = &api.NotificationSubject{Type: api.NotifySubjectPull}
 		if n.Issue != nil {
 			result.Subject.Title = n.Issue.Title
@@ -65,7 +65,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
 				result.Subject.State = "merged"
 			}
 		}
-	case models.NotificationSourceCommit:
+	case activities_model.NotificationSourceCommit:
 		url := n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
 		result.Subject = &api.NotificationSubject{
 			Type:    api.NotifySubjectCommit,
@@ -73,7 +73,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
 			URL:     url,
 			HTMLURL: url,
 		}
-	case models.NotificationSourceRepository:
+	case activities_model.NotificationSourceRepository:
 		result.Subject = &api.NotificationSubject{
 			Type:  api.NotifySubjectRepository,
 			Title: n.Repository.FullName(),
@@ -87,7 +87,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
 }
 
 // ToNotifications convert list of Notification to api.NotificationThread list
-func ToNotifications(nl models.NotificationList) []*api.NotificationThread {
+func ToNotifications(nl activities_model.NotificationList) []*api.NotificationThread {
 	result := make([]*api.NotificationThread, 0, len(nl))
 	for _, n := range nl {
 		result = append(result, ToNotificationThread(n))
diff --git a/modules/convert/release.go b/modules/convert/release.go
index 955d3ff05f..5fc95dab72 100644
--- a/modules/convert/release.go
+++ b/modules/convert/release.go
@@ -5,13 +5,12 @@
 package convert
 
 import (
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
-// ToRelease convert a models.Release to api.Release
-func ToRelease(r *models.Release) *api.Release {
+// ToRelease convert a repo_model.Release to api.Release
+func ToRelease(r *repo_model.Release) *api.Release {
 	assets := make([]*api.Attachment, 0)
 	for _, att := range r.Attachments {
 		assets = append(assets, ToReleaseAttachment(att))
diff --git a/modules/convert/repository.go b/modules/convert/repository.go
index d333c124b5..f853163848 100644
--- a/modules/convert/repository.go
+++ b/modules/convert/repository.go
@@ -102,7 +102,7 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo
 		return nil
 	}
 
-	numReleases, _ := models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false})
+	numReleases, _ := repo_model.GetReleaseCountByRepoID(repo.ID, repo_model.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false})
 
 	mirrorInterval := ""
 	var mirrorUpdated time.Time
diff --git a/modules/doctor/dbconsistency.go b/modules/doctor/dbconsistency.go
index f8b62e898a..7ae349908e 100644
--- a/modules/doctor/dbconsistency.go
+++ b/modules/doctor/dbconsistency.go
@@ -7,7 +7,7 @@ package doctor
 import (
 	"context"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/migrations"
@@ -121,8 +121,8 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
 		// find null archived repositories
 		{
 			Name:         "Repositories with is_archived IS NULL",
-			Counter:      models.CountNullArchivedRepository,
-			Fixer:        models.FixNullArchivedRepository,
+			Counter:      repo_model.CountNullArchivedRepository,
+			Fixer:        repo_model.FixNullArchivedRepository,
 			FixedMessage: "Fixed",
 		},
 		// find label comments with empty labels
@@ -148,8 +148,8 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
 		},
 		{
 			Name:         "Action with created_unix set as an empty string",
-			Counter:      models.CountActionCreatedUnixString,
-			Fixer:        models.FixActionCreatedUnixString,
+			Counter:      activities_model.CountActionCreatedUnixString,
+			Fixer:        activities_model.FixActionCreatedUnixString,
 			FixedMessage: "Set to zero",
 		},
 	}
diff --git a/modules/doctor/usertype.go b/modules/doctor/usertype.go
index 34e12afe72..166e38bd24 100644
--- a/modules/doctor/usertype.go
+++ b/modules/doctor/usertype.go
@@ -7,19 +7,19 @@ package doctor
 import (
 	"context"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 )
 
 func checkUserType(ctx context.Context, logger log.Logger, autofix bool) error {
-	count, err := models.CountWrongUserType()
+	count, err := user_model.CountWrongUserType()
 	if err != nil {
 		logger.Critical("Error: %v whilst counting wrong user types")
 		return err
 	}
 	if count > 0 {
 		if autofix {
-			if count, err = models.FixWrongUserType(); err != nil {
+			if count, err = user_model.FixWrongUserType(); err != nil {
 				logger.Critical("Error: %v whilst fixing wrong user types")
 				return err
 			}
diff --git a/modules/eventsource/manager_run.go b/modules/eventsource/manager_run.go
index 06d48454fd..44e878fd4e 100644
--- a/modules/eventsource/manager_run.go
+++ b/modules/eventsource/manager_run.go
@@ -8,7 +8,7 @@ import (
 	"context"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/graceful"
@@ -72,7 +72,7 @@ loop:
 
 			now := timeutil.TimeStampNow().Add(-2)
 
-			uidCounts, err := models.GetUIDsAndNotificationCounts(then, now)
+			uidCounts, err := activities_model.GetUIDsAndNotificationCounts(then, now)
 			if err != nil {
 				log.Error("Unable to get UIDcounts: %v", err)
 			}
diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go
index 069633a565..dcd80b05a9 100755
--- a/modules/metrics/collector.go
+++ b/modules/metrics/collector.go
@@ -5,7 +5,7 @@
 package metrics
 
 import (
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 
 	"github.com/prometheus/client_golang/prometheus"
 )
@@ -225,7 +225,7 @@ func (c Collector) Describe(ch chan<- *prometheus.Desc) {
 
 // Collect returns the metrics with values
 func (c Collector) Collect(ch chan<- prometheus.Metric) {
-	stats := models.GetStatistic()
+	stats := activities_model.GetStatistic()
 
 	ch <- prometheus.MustNewConstMetric(
 		c.Accesses,
diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go
index e438f41485..d3ff8b156e 100644
--- a/modules/notification/action/action.go
+++ b/modules/notification/action/action.go
@@ -9,7 +9,7 @@ import (
 	"path"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -45,10 +45,10 @@ func (a *actionNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*u
 	}
 	repo := issue.Repo
 
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: issue.Poster.ID,
 		ActUser:   issue.Poster,
-		OpType:    models.ActionCreateIssue,
+		OpType:    activities_model.ActionCreateIssue,
 		Content:   fmt.Sprintf("%d|%s", issue.Index, issue.Title),
 		RepoID:    repo.ID,
 		Repo:      repo,
@@ -62,7 +62,7 @@ func (a *actionNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*u
 func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) {
 	// Compose comment action, could be plain comment, close or reopen issue/pull request.
 	// This object will be used to notify watchers in the end of function.
-	act := &models.Action{
+	act := &activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
 		Content:   fmt.Sprintf("%d|%s", issue.Index, ""),
@@ -74,19 +74,19 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *i
 	}
 	// Check comment type.
 	if closeOrReopen {
-		act.OpType = models.ActionCloseIssue
+		act.OpType = activities_model.ActionCloseIssue
 		if issue.IsPull {
-			act.OpType = models.ActionClosePullRequest
+			act.OpType = activities_model.ActionClosePullRequest
 		}
 	} else {
-		act.OpType = models.ActionReopenIssue
+		act.OpType = activities_model.ActionReopenIssue
 		if issue.IsPull {
-			act.OpType = models.ActionReopenPullRequest
+			act.OpType = activities_model.ActionReopenPullRequest
 		}
 	}
 
 	// Notify watchers for whatever action comes in, ignore if no action type.
-	if err := models.NotifyWatchers(act); err != nil {
+	if err := activities_model.NotifyWatchers(act); err != nil {
 		log.Error("NotifyWatchers: %v", err)
 	}
 }
@@ -95,7 +95,7 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *i
 func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository,
 	issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
 ) {
-	act := &models.Action{
+	act := &activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
 		RepoID:    issue.Repo.ID,
@@ -116,13 +116,13 @@ func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *r
 	act.Content = fmt.Sprintf("%d|%s", issue.Index, truncatedContent)
 
 	if issue.IsPull {
-		act.OpType = models.ActionCommentPull
+		act.OpType = activities_model.ActionCommentPull
 	} else {
-		act.OpType = models.ActionCommentIssue
+		act.OpType = activities_model.ActionCommentIssue
 	}
 
 	// Notify watchers for whatever action comes in, ignore if no action type.
-	if err := models.NotifyWatchers(act); err != nil {
+	if err := activities_model.NotifyWatchers(act); err != nil {
 		log.Error("NotifyWatchers: %v", err)
 	}
 }
@@ -141,10 +141,10 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, me
 		return
 	}
 
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: pull.Issue.Poster.ID,
 		ActUser:   pull.Issue.Poster,
-		OpType:    models.ActionCreatePullRequest,
+		OpType:    activities_model.ActionCreatePullRequest,
 		Content:   fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
 		RepoID:    pull.Issue.Repo.ID,
 		Repo:      pull.Issue.Repo,
@@ -155,10 +155,10 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, me
 }
 
 func (a *actionNotifier) NotifyRenameRepository(doer *user_model.User, repo *repo_model.Repository, oldRepoName string) {
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
-		OpType:    models.ActionRenameRepo,
+		OpType:    activities_model.ActionRenameRepo,
 		RepoID:    repo.ID,
 		Repo:      repo,
 		IsPrivate: repo.IsPrivate,
@@ -169,10 +169,10 @@ func (a *actionNotifier) NotifyRenameRepository(doer *user_model.User, repo *rep
 }
 
 func (a *actionNotifier) NotifyTransferRepository(doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) {
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
-		OpType:    models.ActionTransferRepo,
+		OpType:    activities_model.ActionTransferRepo,
 		RepoID:    repo.ID,
 		Repo:      repo,
 		IsPrivate: repo.IsPrivate,
@@ -183,10 +183,10 @@ func (a *actionNotifier) NotifyTransferRepository(doer *user_model.User, repo *r
 }
 
 func (a *actionNotifier) NotifyCreateRepository(doer, u *user_model.User, repo *repo_model.Repository) {
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
-		OpType:    models.ActionCreateRepo,
+		OpType:    activities_model.ActionCreateRepo,
 		RepoID:    repo.ID,
 		Repo:      repo,
 		IsPrivate: repo.IsPrivate,
@@ -196,10 +196,10 @@ func (a *actionNotifier) NotifyCreateRepository(doer, u *user_model.User, repo *
 }
 
 func (a *actionNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, repo *repo_model.Repository) {
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
-		OpType:    models.ActionCreateRepo,
+		OpType:    activities_model.ActionCreateRepo,
 		RepoID:    repo.ID,
 		Repo:      repo,
 		IsPrivate: repo.IsPrivate,
@@ -221,15 +221,15 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r
 		return
 	}
 
-	actions := make([]*models.Action, 0, 10)
+	actions := make([]*activities_model.Action, 0, 10)
 	for _, lines := range review.CodeComments {
 		for _, comments := range lines {
 			for _, comm := range comments {
-				actions = append(actions, &models.Action{
+				actions = append(actions, &activities_model.Action{
 					ActUserID: review.Reviewer.ID,
 					ActUser:   review.Reviewer,
 					Content:   fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
-					OpType:    models.ActionCommentPull,
+					OpType:    activities_model.ActionCommentPull,
 					RepoID:    review.Issue.RepoID,
 					Repo:      review.Issue.Repo,
 					IsPrivate: review.Issue.Repo.IsPrivate,
@@ -241,7 +241,7 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r
 	}
 
 	if review.Type != issues_model.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
-		action := &models.Action{
+		action := &activities_model.Action{
 			ActUserID: review.Reviewer.ID,
 			ActUser:   review.Reviewer,
 			Content:   fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
@@ -254,26 +254,26 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r
 
 		switch review.Type {
 		case issues_model.ReviewTypeApprove:
-			action.OpType = models.ActionApprovePullRequest
+			action.OpType = activities_model.ActionApprovePullRequest
 		case issues_model.ReviewTypeReject:
-			action.OpType = models.ActionRejectPullRequest
+			action.OpType = activities_model.ActionRejectPullRequest
 		default:
-			action.OpType = models.ActionCommentPull
+			action.OpType = activities_model.ActionCommentPull
 		}
 
 		actions = append(actions, action)
 	}
 
-	if err := models.NotifyWatchersActions(actions); err != nil {
+	if err := activities_model.NotifyWatchersActions(actions); err != nil {
 		log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
 	}
 }
 
 func (*actionNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer *user_model.User) {
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
-		OpType:    models.ActionMergePullRequest,
+		OpType:    activities_model.ActionMergePullRequest,
 		Content:   fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
 		RepoID:    pr.Issue.Repo.ID,
 		Repo:      pr.Issue.Repo,
@@ -288,10 +288,10 @@ func (*actionNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *i
 	if len(review.OriginalAuthor) > 0 {
 		reviewerName = review.OriginalAuthor
 	}
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
-		OpType:    models.ActionPullReviewDismissed,
+		OpType:    activities_model.ActionPullReviewDismissed,
 		Content:   fmt.Sprintf("%d|%s|%s", review.Issue.Index, reviewerName, comment.Content),
 		RepoID:    review.Issue.Repo.ID,
 		Repo:      review.Issue.Repo,
@@ -310,19 +310,19 @@ func (a *actionNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_m
 		return
 	}
 
-	opType := models.ActionCommitRepo
+	opType := activities_model.ActionCommitRepo
 
 	// Check it's tag push or branch.
 	if opts.IsTag() {
-		opType = models.ActionPushTag
+		opType = activities_model.ActionPushTag
 		if opts.IsDelRef() {
-			opType = models.ActionDeleteTag
+			opType = activities_model.ActionDeleteTag
 		}
 	} else if opts.IsDelRef() {
-		opType = models.ActionDeleteBranch
+		opType = activities_model.ActionDeleteBranch
 	}
 
-	if err = models.NotifyWatchers(&models.Action{
+	if err = activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: pusher.ID,
 		ActUser:   pusher,
 		OpType:    opType,
@@ -337,12 +337,12 @@ func (a *actionNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_m
 }
 
 func (a *actionNotifier) NotifyCreateRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
-	opType := models.ActionCommitRepo
+	opType := activities_model.ActionCommitRepo
 	if refType == "tag" {
 		// has sent same action in `NotifyPushCommits`, so skip it.
 		return
 	}
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
 		OpType:    opType,
@@ -356,12 +356,12 @@ func (a *actionNotifier) NotifyCreateRef(doer *user_model.User, repo *repo_model
 }
 
 func (a *actionNotifier) NotifyDeleteRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
-	opType := models.ActionDeleteBranch
+	opType := activities_model.ActionDeleteBranch
 	if refType == "tag" {
 		// has sent same action in `NotifyPushCommits`, so skip it.
 		return
 	}
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
 		OpType:    opType,
@@ -381,10 +381,10 @@ func (a *actionNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *re
 		return
 	}
 
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: repo.OwnerID,
 		ActUser:   repo.MustOwner(),
-		OpType:    models.ActionMirrorSyncPush,
+		OpType:    activities_model.ActionMirrorSyncPush,
 		RepoID:    repo.ID,
 		Repo:      repo,
 		IsPrivate: repo.IsPrivate,
@@ -396,10 +396,10 @@ func (a *actionNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *re
 }
 
 func (a *actionNotifier) NotifySyncCreateRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: repo.OwnerID,
 		ActUser:   repo.MustOwner(),
-		OpType:    models.ActionMirrorSyncCreate,
+		OpType:    activities_model.ActionMirrorSyncCreate,
 		RepoID:    repo.ID,
 		Repo:      repo,
 		IsPrivate: repo.IsPrivate,
@@ -410,10 +410,10 @@ func (a *actionNotifier) NotifySyncCreateRef(doer *user_model.User, repo *repo_m
 }
 
 func (a *actionNotifier) NotifySyncDeleteRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: repo.OwnerID,
 		ActUser:   repo.MustOwner(),
-		OpType:    models.ActionMirrorSyncDelete,
+		OpType:    activities_model.ActionMirrorSyncDelete,
 		RepoID:    repo.ID,
 		Repo:      repo,
 		IsPrivate: repo.IsPrivate,
@@ -423,15 +423,15 @@ func (a *actionNotifier) NotifySyncDeleteRef(doer *user_model.User, repo *repo_m
 	}
 }
 
-func (a *actionNotifier) NotifyNewRelease(rel *models.Release) {
+func (a *actionNotifier) NotifyNewRelease(rel *repo_model.Release) {
 	if err := rel.LoadAttributes(); err != nil {
 		log.Error("NotifyNewRelease: %v", err)
 		return
 	}
-	if err := models.NotifyWatchers(&models.Action{
+	if err := activities_model.NotifyWatchers(&activities_model.Action{
 		ActUserID: rel.PublisherID,
 		ActUser:   rel.Publisher,
-		OpType:    models.ActionPublishRelease,
+		OpType:    activities_model.ActionPublishRelease,
 		RepoID:    rel.RepoID,
 		Repo:      rel.Repo,
 		IsPrivate: rel.Repo.IsPrivate,
diff --git a/modules/notification/action/action_test.go b/modules/notification/action/action_test.go
index f6de0d6759..79a938d6cd 100644
--- a/modules/notification/action/action_test.go
+++ b/modules/notification/action/action_test.go
@@ -9,7 +9,7 @@ import (
 	"strings"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -35,8 +35,8 @@ func TestRenameRepoAction(t *testing.T) {
 	repo.Name = newRepoName
 	repo.LowerName = strings.ToLower(newRepoName)
 
-	actionBean := &models.Action{
-		OpType:    models.ActionRenameRepo,
+	actionBean := &activities_model.Action{
+		OpType:    activities_model.ActionRenameRepo,
 		ActUserID: user.ID,
 		ActUser:   user,
 		RepoID:    repo.ID,
@@ -49,5 +49,5 @@ func TestRenameRepoAction(t *testing.T) {
 	NewNotifier().NotifyRenameRepository(user, repo, oldRepoName)
 
 	unittest.AssertExistsAndLoadBean(t, actionBean)
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go
index 31fa8f5f18..541731ddbf 100644
--- a/modules/notification/base/notifier.go
+++ b/modules/notification/base/notifier.go
@@ -5,7 +5,6 @@
 package base
 
 import (
-	"code.gitea.io/gitea/models"
 	issues_model "code.gitea.io/gitea/models/issues"
 	packages_model "code.gitea.io/gitea/models/packages"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -46,9 +45,9 @@ type Notifier interface {
 		issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User)
 	NotifyUpdateComment(*user_model.User, *issues_model.Comment, string)
 	NotifyDeleteComment(*user_model.User, *issues_model.Comment)
-	NotifyNewRelease(rel *models.Release)
-	NotifyUpdateRelease(doer *user_model.User, rel *models.Release)
-	NotifyDeleteRelease(doer *user_model.User, rel *models.Release)
+	NotifyNewRelease(rel *repo_model.Release)
+	NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release)
+	NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release)
 	NotifyPushCommits(pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
 	NotifyCreateRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string)
 	NotifyDeleteRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName string)
diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go
index d336f09301..4f382b3d01 100644
--- a/modules/notification/base/null.go
+++ b/modules/notification/base/null.go
@@ -5,7 +5,6 @@
 package base
 
 import (
-	"code.gitea.io/gitea/models"
 	issues_model "code.gitea.io/gitea/models/issues"
 	packages_model "code.gitea.io/gitea/models/packages"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -80,15 +79,15 @@ func (*NullNotifier) NotifyDeleteComment(doer *user_model.User, c *issues_model.
 }
 
 // NotifyNewRelease places a place holder function
-func (*NullNotifier) NotifyNewRelease(rel *models.Release) {
+func (*NullNotifier) NotifyNewRelease(rel *repo_model.Release) {
 }
 
 // NotifyUpdateRelease places a place holder function
-func (*NullNotifier) NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
+func (*NullNotifier) NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release) {
 }
 
 // NotifyDeleteRelease places a place holder function
-func (*NullNotifier) NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
+func (*NullNotifier) NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release) {
 }
 
 // NotifyIssueChangeMilestone places a place holder function
diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go
index 5085656c14..100b4eb36f 100644
--- a/modules/notification/mail/mail.go
+++ b/modules/notification/mail/mail.go
@@ -7,7 +7,7 @@ package mail
 import (
 	"fmt"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -35,15 +35,15 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *rep
 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyCreateIssueComment Issue[%d] #%d in [%d]", issue.ID, issue.Index, issue.RepoID))
 	defer finished()
 
-	var act models.ActionType
+	var act activities_model.ActionType
 	if comment.Type == issues_model.CommentTypeClose {
-		act = models.ActionCloseIssue
+		act = activities_model.ActionCloseIssue
 	} else if comment.Type == issues_model.CommentTypeReopen {
-		act = models.ActionReopenIssue
+		act = activities_model.ActionReopenIssue
 	} else if comment.Type == issues_model.CommentTypeComment {
-		act = models.ActionCommentIssue
+		act = activities_model.ActionCommentIssue
 	} else if comment.Type == issues_model.CommentTypeCode {
-		act = models.ActionCommentIssue
+		act = activities_model.ActionCommentIssue
 	} else if comment.Type == issues_model.CommentTypePullRequestPush {
 		act = 0
 	}
@@ -54,24 +54,24 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *rep
 }
 
 func (m *mailNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) {
-	if err := mailer.MailParticipants(issue, issue.Poster, models.ActionCreateIssue, mentions); err != nil {
+	if err := mailer.MailParticipants(issue, issue.Poster, activities_model.ActionCreateIssue, mentions); err != nil {
 		log.Error("MailParticipants: %v", err)
 	}
 }
 
 func (m *mailNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) {
-	var actionType models.ActionType
+	var actionType activities_model.ActionType
 	if issue.IsPull {
 		if isClosed {
-			actionType = models.ActionClosePullRequest
+			actionType = activities_model.ActionClosePullRequest
 		} else {
-			actionType = models.ActionReopenPullRequest
+			actionType = activities_model.ActionReopenPullRequest
 		}
 	} else {
 		if isClosed {
-			actionType = models.ActionCloseIssue
+			actionType = activities_model.ActionCloseIssue
 		} else {
-			actionType = models.ActionReopenIssue
+			actionType = activities_model.ActionReopenIssue
 		}
 	}
 
@@ -86,14 +86,14 @@ func (m *mailNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *issu
 		return
 	}
 	if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() {
-		if err := mailer.MailParticipants(issue, doer, models.ActionPullRequestReadyForReview, nil); err != nil {
+		if err := mailer.MailParticipants(issue, doer, activities_model.ActionPullRequestReadyForReview, nil); err != nil {
 			log.Error("MailParticipants: %v", err)
 		}
 	}
 }
 
 func (m *mailNotifier) NotifyNewPullRequest(pr *issues_model.PullRequest, mentions []*user_model.User) {
-	if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil {
+	if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, activities_model.ActionCreatePullRequest, mentions); err != nil {
 		log.Error("MailParticipants: %v", err)
 	}
 }
@@ -102,13 +102,13 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r *
 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestReview Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID))
 	defer finished()
 
-	var act models.ActionType
+	var act activities_model.ActionType
 	if comment.Type == issues_model.CommentTypeClose {
-		act = models.ActionCloseIssue
+		act = activities_model.ActionCloseIssue
 	} else if comment.Type == issues_model.CommentTypeReopen {
-		act = models.ActionReopenIssue
+		act = activities_model.ActionReopenIssue
 	} else if comment.Type == issues_model.CommentTypeComment {
-		act = models.ActionCommentPull
+		act = activities_model.ActionCommentPull
 	}
 	if err := mailer.MailParticipantsComment(ctx, comment, act, pr.Issue, mentions); err != nil {
 		log.Error("MailParticipantsComment: %v", err)
@@ -148,7 +148,7 @@ func (m *mailNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer
 		log.Error("pr.LoadIssue: %v", err)
 		return
 	}
-	if err := mailer.MailParticipants(pr.Issue, doer, models.ActionMergePullRequest, nil); err != nil {
+	if err := mailer.MailParticipants(pr.Issue, doer, activities_model.ActionMergePullRequest, nil); err != nil {
 		log.Error("MailParticipants: %v", err)
 	}
 }
@@ -184,12 +184,12 @@ func (m *mailNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *i
 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRevieweDismiss Review[%d] in Issue[%d]", review.ID, review.IssueID))
 	defer finished()
 
-	if err := mailer.MailParticipantsComment(ctx, comment, models.ActionPullReviewDismissed, review.Issue, nil); err != nil {
+	if err := mailer.MailParticipantsComment(ctx, comment, activities_model.ActionPullReviewDismissed, review.Issue, nil); err != nil {
 		log.Error("MailParticipantsComment: %v", err)
 	}
 }
 
-func (m *mailNotifier) NotifyNewRelease(rel *models.Release) {
+func (m *mailNotifier) NotifyNewRelease(rel *repo_model.Release) {
 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyNewRelease rel[%d]%s in [%d]", rel.ID, rel.Title, rel.RepoID))
 	defer finished()
 
diff --git a/modules/notification/notification.go b/modules/notification/notification.go
index bdfed90b78..713cc72aac 100644
--- a/modules/notification/notification.go
+++ b/modules/notification/notification.go
@@ -5,7 +5,6 @@
 package notification
 
 import (
-	"code.gitea.io/gitea/models"
 	issues_model "code.gitea.io/gitea/models/issues"
 	packages_model "code.gitea.io/gitea/models/packages"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -142,21 +141,21 @@ func NotifyDeleteComment(doer *user_model.User, c *issues_model.Comment) {
 }
 
 // NotifyNewRelease notifies new release to notifiers
-func NotifyNewRelease(rel *models.Release) {
+func NotifyNewRelease(rel *repo_model.Release) {
 	for _, notifier := range notifiers {
 		notifier.NotifyNewRelease(rel)
 	}
 }
 
 // NotifyUpdateRelease notifies update release to notifiers
-func NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
+func NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release) {
 	for _, notifier := range notifiers {
 		notifier.NotifyUpdateRelease(doer, rel)
 	}
 }
 
 // NotifyDeleteRelease notifies delete release to notifiers
-func NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
+func NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release) {
 	for _, notifier := range notifiers {
 		notifier.NotifyDeleteRelease(doer, rel)
 	}
diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go
index 74866a3363..5e5196a70a 100644
--- a/modules/notification/ui/ui.go
+++ b/modules/notification/ui/ui.go
@@ -5,7 +5,7 @@
 package ui
 
 import (
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -42,7 +42,7 @@ func NewNotifier() base.Notifier {
 func (ns *notificationService) handle(data ...queue.Data) []queue.Data {
 	for _, datum := range data {
 		opts := datum.(issueNotificationOpts)
-		if err := models.CreateOrUpdateIssueNotifications(opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil {
+		if err := activities_model.CreateOrUpdateIssueNotifications(opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil {
 			log.Error("Was unable to create issue notification: %v", err)
 		}
 	}
@@ -237,7 +237,7 @@ func (ns *notificationService) NotifyPullReviewRequest(doer *user_model.User, is
 }
 
 func (ns *notificationService) NotifyRepoPendingTransfer(doer, newOwner *user_model.User, repo *repo_model.Repository) {
-	if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil {
+	if err := activities_model.CreateRepoTransferNotification(doer, newOwner, repo); err != nil {
 		log.Error("NotifyRepoPendingTransfer: %v", err)
 	}
 }
diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go
index be71d18fda..ed3b53e3e7 100644
--- a/modules/notification/webhook/webhook.go
+++ b/modules/notification/webhook/webhook.go
@@ -7,7 +7,6 @@ package webhook
 import (
 	"fmt"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	packages_model "code.gitea.io/gitea/models/packages"
@@ -797,7 +796,7 @@ func (m *webhookNotifier) NotifyDeleteRef(pusher *user_model.User, repo *repo_mo
 	}
 }
 
-func sendReleaseHook(doer *user_model.User, rel *models.Release, action api.HookReleaseAction) {
+func sendReleaseHook(doer *user_model.User, rel *repo_model.Release, action api.HookReleaseAction) {
 	if err := rel.LoadAttributes(); err != nil {
 		log.Error("LoadAttributes: %v", err)
 		return
@@ -814,15 +813,15 @@ func sendReleaseHook(doer *user_model.User, rel *models.Release, action api.Hook
 	}
 }
 
-func (m *webhookNotifier) NotifyNewRelease(rel *models.Release) {
+func (m *webhookNotifier) NotifyNewRelease(rel *repo_model.Release) {
 	sendReleaseHook(rel.Publisher, rel, api.HookReleasePublished)
 }
 
-func (m *webhookNotifier) NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
+func (m *webhookNotifier) NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release) {
 	sendReleaseHook(doer, rel, api.HookReleaseUpdated)
 }
 
-func (m *webhookNotifier) NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
+func (m *webhookNotifier) NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release) {
 	sendReleaseHook(doer, rel, api.HookReleaseDeleted)
 }
 
diff --git a/modules/repository/collaborator.go b/modules/repository/collaborator.go
new file mode 100644
index 0000000000..9d20a25890
--- /dev/null
+++ b/modules/repository/collaborator.go
@@ -0,0 +1,43 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repository
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/perm"
+	access_model "code.gitea.io/gitea/models/perm/access"
+	repo_model "code.gitea.io/gitea/models/repo"
+	user_model "code.gitea.io/gitea/models/user"
+)
+
+func addCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error {
+	collaboration := &repo_model.Collaboration{
+		RepoID: repo.ID,
+		UserID: u.ID,
+	}
+
+	has, err := db.GetByBean(ctx, collaboration)
+	if err != nil {
+		return err
+	} else if has {
+		return nil
+	}
+	collaboration.Mode = perm.AccessModeWrite
+
+	if err = db.Insert(ctx, collaboration); err != nil {
+		return err
+	}
+
+	return access_model.RecalculateUserAccess(ctx, repo, u.ID)
+}
+
+// AddCollaborator adds new collaboration to a repository with default access mode.
+func AddCollaborator(repo *repo_model.Repository, u *user_model.User) error {
+	return db.WithTx(func(ctx context.Context) error {
+		return addCollaborator(ctx, repo, u)
+	})
+}
diff --git a/modules/repository/collaborator_test.go b/modules/repository/collaborator_test.go
new file mode 100644
index 0000000000..1b927aa3b6
--- /dev/null
+++ b/modules/repository/collaborator_test.go
@@ -0,0 +1,281 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repository
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
+	perm_model "code.gitea.io/gitea/models/perm"
+	access_model "code.gitea.io/gitea/models/perm/access"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRepository_AddCollaborator(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	testSuccess := func(repoID, userID int64) {
+		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
+		assert.NoError(t, repo.GetOwner(db.DefaultContext))
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
+		assert.NoError(t, AddCollaborator(repo, user))
+		unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID})
+	}
+	testSuccess(1, 4)
+	testSuccess(1, 4)
+	testSuccess(3, 4)
+}
+
+func TestRepoPermissionPublicNonOrgRepo(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	// public non-organization repo
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
+
+	// plain user
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.False(t, perm.CanWrite(unit.Type))
+	}
+
+	// change to collaborator
+	assert.NoError(t, AddCollaborator(repo, user))
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	// collaborator
+	collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, collaborator)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	// owner
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	// admin
+	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+}
+
+func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	// private non-organization repo
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
+
+	// plain user
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.False(t, perm.CanRead(unit.Type))
+		assert.False(t, perm.CanWrite(unit.Type))
+	}
+
+	// change to collaborator to default write access
+	assert.NoError(t, AddCollaborator(repo, user))
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.False(t, perm.CanWrite(unit.Type))
+	}
+
+	// owner
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	// admin
+	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+}
+
+func TestRepoPermissionPublicOrgRepo(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	// public organization repo
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32})
+	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
+
+	// plain user
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
+	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.False(t, perm.CanWrite(unit.Type))
+	}
+
+	// change to collaborator to default write access
+	assert.NoError(t, AddCollaborator(repo, user))
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.False(t, perm.CanWrite(unit.Type))
+	}
+
+	// org member team owner
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	// org member team tester
+	member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, member)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+	}
+	assert.True(t, perm.CanWrite(unit.TypeIssues))
+	assert.False(t, perm.CanWrite(unit.TypeCode))
+
+	// admin
+	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+}
+
+func TestRepoPermissionPrivateOrgRepo(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	// private organization repo
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24})
+	assert.NoError(t, repo.LoadUnits(db.DefaultContext))
+
+	// plain user
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
+	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.False(t, perm.CanRead(unit.Type))
+		assert.False(t, perm.CanWrite(unit.Type))
+	}
+
+	// change to collaborator to default write access
+	assert.NoError(t, AddCollaborator(repo, user))
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.False(t, perm.CanWrite(unit.Type))
+	}
+
+	// org member team owner
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	// update team information and then check permission
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
+	err = organization.UpdateTeamUnits(team, nil)
+	assert.NoError(t, err)
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+
+	// org member team tester
+	tester := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, tester)
+	assert.NoError(t, err)
+	assert.True(t, perm.CanWrite(unit.TypeIssues))
+	assert.False(t, perm.CanWrite(unit.TypeCode))
+	assert.False(t, perm.CanRead(unit.TypeCode))
+
+	// org member team reviewer
+	reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, reviewer)
+	assert.NoError(t, err)
+	assert.False(t, perm.CanRead(unit.TypeIssues))
+	assert.False(t, perm.CanWrite(unit.TypeCode))
+	assert.True(t, perm.CanRead(unit.TypeCode))
+
+	// admin
+	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
+	assert.NoError(t, err)
+	for _, unit := range repo.Units {
+		assert.True(t, perm.CanRead(unit.Type))
+		assert.True(t, perm.CanWrite(unit.Type))
+	}
+}
diff --git a/modules/repository/create.go b/modules/repository/create.go
index 9204d7e422..7a25323def 100644
--- a/modules/repository/create.go
+++ b/modules/repository/create.go
@@ -13,11 +13,16 @@ import (
 	"unicode/utf8"
 
 	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
+	"code.gitea.io/gitea/models/organization"
+	"code.gitea.io/gitea/models/perm"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -25,8 +30,150 @@ import (
 	"code.gitea.io/gitea/modules/util"
 )
 
+// CreateRepositoryByExample creates a repository for the user/organization.
+func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt bool) (err error) {
+	if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
+		return err
+	}
+
+	has, err := repo_model.IsRepositoryExist(ctx, u, repo.Name)
+	if err != nil {
+		return fmt.Errorf("IsRepositoryExist: %v", err)
+	} else if has {
+		return repo_model.ErrRepoAlreadyExist{
+			Uname: u.Name,
+			Name:  repo.Name,
+		}
+	}
+
+	repoPath := repo_model.RepoPath(u.Name, repo.Name)
+	isExist, err := util.IsExist(repoPath)
+	if err != nil {
+		log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
+		return err
+	}
+	if !overwriteOrAdopt && isExist {
+		log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
+		return repo_model.ErrRepoFilesAlreadyExist{
+			Uname: u.Name,
+			Name:  repo.Name,
+		}
+	}
+
+	if err = db.Insert(ctx, repo); err != nil {
+		return err
+	}
+	if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
+		return err
+	}
+
+	// insert units for repo
+	units := make([]repo_model.RepoUnit, 0, len(unit.DefaultRepoUnits))
+	for _, tp := range unit.DefaultRepoUnits {
+		if tp == unit.TypeIssues {
+			units = append(units, repo_model.RepoUnit{
+				RepoID: repo.ID,
+				Type:   tp,
+				Config: &repo_model.IssuesConfig{
+					EnableTimetracker:                setting.Service.DefaultEnableTimetracking,
+					AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
+					EnableDependencies:               setting.Service.DefaultEnableDependencies,
+				},
+			})
+		} else if tp == unit.TypePullRequests {
+			units = append(units, repo_model.RepoUnit{
+				RepoID: repo.ID,
+				Type:   tp,
+				Config: &repo_model.PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true},
+			})
+		} else {
+			units = append(units, repo_model.RepoUnit{
+				RepoID: repo.ID,
+				Type:   tp,
+			})
+		}
+	}
+
+	if err = db.Insert(ctx, units); err != nil {
+		return err
+	}
+
+	// Remember visibility preference.
+	u.LastRepoVisibility = repo.IsPrivate
+	if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil {
+		return fmt.Errorf("UpdateUserCols: %v", err)
+	}
+
+	if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil {
+		return fmt.Errorf("IncrUserRepoNum: %v", err)
+	}
+	u.NumRepos++
+
+	// Give access to all members in teams with access to all repositories.
+	if u.IsOrganization() {
+		teams, err := organization.FindOrgTeams(ctx, u.ID)
+		if err != nil {
+			return fmt.Errorf("FindOrgTeams: %v", err)
+		}
+		for _, t := range teams {
+			if t.IncludesAllRepositories {
+				if err := models.AddRepository(ctx, t, repo); err != nil {
+					return fmt.Errorf("AddRepository: %v", err)
+				}
+			}
+		}
+
+		if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
+			return fmt.Errorf("IsUserRepoAdmin: %v", err)
+		} else if !isAdmin {
+			// Make creator repo admin if it wasn't assigned automatically
+			if err = addCollaborator(ctx, repo, doer); err != nil {
+				return fmt.Errorf("addCollaborator: %v", err)
+			}
+			if err = repo_model.ChangeCollaborationAccessModeCtx(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil {
+				return fmt.Errorf("ChangeCollaborationAccessModeCtx: %v", err)
+			}
+		}
+	} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
+		// Organization automatically called this in AddRepository method.
+		return fmt.Errorf("RecalculateAccesses: %v", err)
+	}
+
+	if setting.Service.AutoWatchNewRepos {
+		if err = repo_model.WatchRepo(ctx, doer.ID, repo.ID, true); err != nil {
+			return fmt.Errorf("WatchRepo: %v", err)
+		}
+	}
+
+	if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
+		return fmt.Errorf("CopyDefaultWebhooksToRepo: %v", err)
+	}
+
+	return nil
+}
+
+// CreateRepoOptions contains the create repository options
+type CreateRepoOptions struct {
+	Name           string
+	Description    string
+	OriginalURL    string
+	GitServiceType api.GitServiceType
+	Gitignores     string
+	IssueLabels    string
+	License        string
+	Readme         string
+	DefaultBranch  string
+	IsPrivate      bool
+	IsMirror       bool
+	IsTemplate     bool
+	AutoInit       bool
+	Status         repo_model.RepositoryStatus
+	TrustModel     repo_model.TrustModelType
+	MirrorInterval string
+}
+
 // CreateRepository creates a repository for the user/organization.
-func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) {
+func CreateRepository(doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
 	if !doer.IsAdmin && !u.CanCreateRepo() {
 		return nil, repo_model.ErrReachLimitOfRepo{
 			Limit: u.MaxRepoCreation,
@@ -66,7 +213,7 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (
 	var rollbackRepo *repo_model.Repository
 
 	if err := db.WithTx(func(ctx context.Context) error {
-		if err := models.CreateRepository(ctx, doer, u, repo, false); err != nil {
+		if err := CreateRepositoryByExample(ctx, doer, u, repo, false); err != nil {
 			return err
 		}
 
@@ -220,7 +367,7 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili
 
 		// If repo has become private, we need to set its actions to private.
 		if repo.IsPrivate {
-			_, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&models.Action{
+			_, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{
 				IsPrivate: true,
 			})
 			if err != nil {
diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go
index 39f8b11356..3040782845 100644
--- a/modules/repository/create_test.go
+++ b/modules/repository/create_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
@@ -56,7 +57,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	// Create repos.
 	repoIds := make([]int64, 0)
 	for i := 0; i < 3; i++ {
-		r, err := CreateRepository(user, org.AsUser(), models.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
+		r, err := CreateRepository(user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
 		assert.NoError(t, err, "CreateRepository %d", i)
 		if r != nil {
 			repoIds = append(repoIds, r.ID)
@@ -118,7 +119,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	}
 
 	// Create repo and check teams repositories.
-	r, err := CreateRepository(user, org.AsUser(), models.CreateRepoOptions{Name: "repo-last"})
+	r, err := CreateRepository(user, org.AsUser(), CreateRepoOptions{Name: "repo-last"})
 	assert.NoError(t, err, "CreateRepository last")
 	if r != nil {
 		repoIds = append(repoIds, r.ID)
@@ -162,7 +163,7 @@ func TestUpdateRepositoryVisibilityChanged(t *testing.T) {
 	assert.NoError(t, err)
 
 	// Check visibility of action has become private
-	act := models.Action{}
+	act := activities_model.Action{}
 	_, err = db.GetEngine(db.DefaultContext).ID(3).Get(&act)
 
 	assert.NoError(t, err)
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index 8f7b4c885d..4d76d33993 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -15,7 +15,6 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	git_model "code.gitea.io/gitea/models/git"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -321,7 +320,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ
 		TrustModel:    templateRepo.TrustModel,
 	}
 
-	if err = models.CreateRepository(ctx, doer, owner, generateRepo, false); err != nil {
+	if err = CreateRepositoryByExample(ctx, doer, owner, generateRepo, false); err != nil {
 		return nil, err
 	}
 
diff --git a/modules/repository/init.go b/modules/repository/init.go
index e984697cda..37ed0748b4 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -15,7 +15,6 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -214,7 +213,7 @@ func LoadRepoConfig() {
 	Licenses = sortedLicenses
 }
 
-func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts models.CreateRepoOptions) error {
+func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
 	commitTimeStr := time.Now().Format(time.RFC3339)
 	authorSig := repo.Owner.NewGitSig()
 
@@ -387,7 +386,7 @@ func checkInitRepository(ctx context.Context, owner, name string) (err error) {
 }
 
 // InitRepository initializes README and .gitignore if needed.
-func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts models.CreateRepoOptions) (err error) {
+func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
 	if err = checkInitRepository(ctx, repo.OwnerName, repo.Name); err != nil {
 		return err
 	}
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index 436045146a..48c3edf60f 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -13,7 +13,6 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	"code.gitea.io/gitea/models/organization"
@@ -31,8 +30,8 @@ import (
 )
 
 /*
-	GitHub, GitLab, Gogs: *.wiki.git
-	BitBucket: *.git/wiki
+GitHub, GitLab, Gogs: *.wiki.git
+BitBucket: *.git/wiki
 */
 var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"}
 
@@ -277,14 +276,14 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
 	}
 
 	existingRelTags := make(map[string]struct{})
-	opts := models.FindReleasesOptions{
+	opts := repo_model.FindReleasesOptions{
 		IncludeDrafts: true,
 		IncludeTags:   true,
 		ListOptions:   db.ListOptions{PageSize: 50},
 	}
 	for page := 1; ; page++ {
 		opts.Page = page
-		rels, err := models.GetReleasesByRepoID(repo.ID, opts)
+		rels, err := repo_model.GetReleasesByRepoID(repo.ID, opts)
 		if err != nil {
 			return fmt.Errorf("unable to GetReleasesByRepoID in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
 		}
@@ -300,7 +299,7 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
 				return fmt.Errorf("unable to GetTagCommitID for %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
 			}
 			if git.IsErrNotExist(err) || commitID != rel.Sha1 {
-				if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
+				if err := repo_model.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
 					return fmt.Errorf("unable to PushUpdateDeleteTag: %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
 				}
 			} else {
@@ -359,7 +358,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
 		return fmt.Errorf("unable to get CommitsCount: %w", err)
 	}
 
-	rel := models.Release{
+	rel := repo_model.Release{
 		RepoID:       repo.ID,
 		TagName:      tagName,
 		LowerTagName: strings.ToLower(tagName),
@@ -372,7 +371,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
 		rel.PublisherID = author.ID
 	}
 
-	return models.SaveOrUpdateTag(repo, &rel)
+	return repo_model.SaveOrUpdateTag(repo, &rel)
 }
 
 // StoreMissingLfsObjectsInRepository downloads missing LFS objects
@@ -489,14 +488,14 @@ func pullMirrorReleaseSync(repo *repo_model.Repository, gitRepo *git.Repository)
 		//
 		// clear out existing releases
 		//
-		if _, err := db.DeleteByBean(ctx, &models.Release{RepoID: repo.ID}); err != nil {
+		if _, err := db.DeleteByBean(ctx, &repo_model.Release{RepoID: repo.ID}); err != nil {
 			return fmt.Errorf("unable to clear releases for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
 		}
 		//
 		// make release set identical to upstream tags
 		//
 		for _, tag := range tags {
-			release := models.Release{
+			release := repo_model.Release{
 				RepoID:       repo.ID,
 				TagName:      tag.Name,
 				LowerTagName: strings.ToLower(tag.Name),
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 10b7022ea6..48b62403a0 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -24,7 +24,7 @@ import (
 	"time"
 	"unicode"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/avatars"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/organization"
@@ -655,7 +655,7 @@ func Avatar(item interface{}, others ...interface{}) template.HTML {
 }
 
 // AvatarByAction renders user avatars from action. args: action, size (int), class (string)
-func AvatarByAction(action *models.Action, others ...interface{}) template.HTML {
+func AvatarByAction(action *activities_model.Action, others ...interface{}) template.HTML {
 	action.LoadActUser()
 	return Avatar(action.ActUser, others...)
 }
@@ -854,7 +854,7 @@ func IsMultilineCommitMessage(msg string) bool {
 
 // Actioner describes an action
 type Actioner interface {
-	GetOpType() models.ActionType
+	GetOpType() activities_model.ActionType
 	GetActUserName() string
 	GetRepoUserName() string
 	GetRepoName() string
@@ -867,33 +867,33 @@ type Actioner interface {
 }
 
 // ActionIcon accepts an action operation type and returns an icon class name.
-func ActionIcon(opType models.ActionType) string {
+func ActionIcon(opType activities_model.ActionType) string {
 	switch opType {
-	case models.ActionCreateRepo, models.ActionTransferRepo, models.ActionRenameRepo:
+	case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
 		return "repo"
-	case models.ActionCommitRepo, models.ActionPushTag, models.ActionDeleteTag, models.ActionDeleteBranch:
+	case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionDeleteBranch:
 		return "git-commit"
-	case models.ActionCreateIssue:
+	case activities_model.ActionCreateIssue:
 		return "issue-opened"
-	case models.ActionCreatePullRequest:
+	case activities_model.ActionCreatePullRequest:
 		return "git-pull-request"
-	case models.ActionCommentIssue, models.ActionCommentPull:
+	case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
 		return "comment-discussion"
-	case models.ActionMergePullRequest:
+	case activities_model.ActionMergePullRequest:
 		return "git-merge"
-	case models.ActionCloseIssue, models.ActionClosePullRequest:
+	case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
 		return "issue-closed"
-	case models.ActionReopenIssue, models.ActionReopenPullRequest:
+	case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
 		return "issue-reopened"
-	case models.ActionMirrorSyncPush, models.ActionMirrorSyncCreate, models.ActionMirrorSyncDelete:
+	case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
 		return "mirror"
-	case models.ActionApprovePullRequest:
+	case activities_model.ActionApprovePullRequest:
 		return "check"
-	case models.ActionRejectPullRequest:
+	case activities_model.ActionRejectPullRequest:
 		return "diff"
-	case models.ActionPublishRelease:
+	case activities_model.ActionPublishRelease:
 		return "tag"
-	case models.ActionPullReviewDismissed:
+	case activities_model.ActionPullReviewDismissed:
 		return "x"
 	default:
 		return "question"
diff --git a/routers/api/packages/nuget/auth.go b/routers/api/packages/nuget/auth.go
index 26a5b90189..1dad452648 100644
--- a/routers/api/packages/nuget/auth.go
+++ b/routers/api/packages/nuget/auth.go
@@ -7,7 +7,7 @@ package nuget
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	auth_model "code.gitea.io/gitea/models/auth"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/timeutil"
@@ -22,9 +22,9 @@ func (a *Auth) Name() string {
 
 // https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#request-parameters
 func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) *user_model.User {
-	token, err := models.GetAccessTokenBySHA(req.Header.Get("X-NuGet-ApiKey"))
+	token, err := auth_model.GetAccessTokenBySHA(req.Header.Get("X-NuGet-ApiKey"))
 	if err != nil {
-		if !(models.IsErrAccessTokenNotExist(err) || models.IsErrAccessTokenEmpty(err)) {
+		if !(auth_model.IsErrAccessTokenNotExist(err) || auth_model.IsErrAccessTokenEmpty(err)) {
 			log.Error("GetAccessTokenBySHA: %v", err)
 		}
 		return nil
@@ -37,7 +37,7 @@ func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataS
 	}
 
 	token.UpdatedUnix = timeutil.TimeStampNow()
-	if err := models.UpdateAccessToken(token); err != nil {
+	if err := auth_model.UpdateAccessToken(token); err != nil {
 		log.Error("UpdateAccessToken:  %v", err)
 	}
 
diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go
index 8f11ab67f0..48222f89eb 100644
--- a/routers/api/v1/admin/adopt.go
+++ b/routers/api/v1/admin/adopt.go
@@ -7,10 +7,10 @@ package admin
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/routers/api/v1/utils"
 	repo_service "code.gitea.io/gitea/services/repository"
@@ -110,7 +110,7 @@ func AdoptRepository(ctx *context.APIContext) {
 		ctx.NotFound()
 		return
 	}
-	if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, models.CreateRepoOptions{
+	if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
 		Name:      repoName,
 		IsPrivate: true,
 	}); err != nil {
diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go
index 44cb4ae769..9948f90a12 100644
--- a/routers/api/v1/notify/notifications.go
+++ b/routers/api/v1/notify/notifications.go
@@ -8,7 +8,7 @@ import (
 	"net/http"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/context"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/routers/api/v1/utils"
@@ -22,16 +22,16 @@ func NewAvailable(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/NotificationCount"
-	ctx.JSON(http.StatusOK, api.NotificationCount{New: models.CountUnread(ctx, ctx.Doer.ID)})
+	ctx.JSON(http.StatusOK, api.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)})
 }
 
-func getFindNotificationOptions(ctx *context.APIContext) *models.FindNotificationOptions {
+func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {
 	before, since, err := context.GetQueryBeforeSince(ctx.Context)
 	if err != nil {
 		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 		return nil
 	}
-	opts := &models.FindNotificationOptions{
+	opts := &activities_model.FindNotificationOptions{
 		ListOptions:       utils.GetListOptions(ctx),
 		UserID:            ctx.Doer.ID,
 		UpdatedBeforeUnix: before,
@@ -50,17 +50,17 @@ func getFindNotificationOptions(ctx *context.APIContext) *models.FindNotificatio
 	return opts
 }
 
-func subjectToSource(value []string) (result []models.NotificationSource) {
+func subjectToSource(value []string) (result []activities_model.NotificationSource) {
 	for _, v := range value {
 		switch strings.ToLower(v) {
 		case "issue":
-			result = append(result, models.NotificationSourceIssue)
+			result = append(result, activities_model.NotificationSourceIssue)
 		case "pull":
-			result = append(result, models.NotificationSourcePullRequest)
+			result = append(result, activities_model.NotificationSourcePullRequest)
 		case "commit":
-			result = append(result, models.NotificationSourceCommit)
+			result = append(result, activities_model.NotificationSourceCommit)
 		case "repository":
-			result = append(result, models.NotificationSourceRepository)
+			result = append(result, activities_model.NotificationSourceRepository)
 		}
 	}
 	return result
diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go
index 4e9dd806de..f8e1fb0865 100644
--- a/routers/api/v1/notify/repo.go
+++ b/routers/api/v1/notify/repo.go
@@ -9,31 +9,31 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/structs"
 )
 
-func statusStringToNotificationStatus(status string) models.NotificationStatus {
+func statusStringToNotificationStatus(status string) activities_model.NotificationStatus {
 	switch strings.ToLower(strings.TrimSpace(status)) {
 	case "unread":
-		return models.NotificationStatusUnread
+		return activities_model.NotificationStatusUnread
 	case "read":
-		return models.NotificationStatusRead
+		return activities_model.NotificationStatusRead
 	case "pinned":
-		return models.NotificationStatusPinned
+		return activities_model.NotificationStatusPinned
 	default:
 		return 0
 	}
 }
 
-func statusStringsToNotificationStatuses(statuses, defaultStatuses []string) []models.NotificationStatus {
+func statusStringsToNotificationStatuses(statuses, defaultStatuses []string) []activities_model.NotificationStatus {
 	if len(statuses) == 0 {
 		statuses = defaultStatuses
 	}
-	results := make([]models.NotificationStatus, 0, len(statuses))
+	results := make([]activities_model.NotificationStatus, 0, len(statuses))
 	for _, status := range statuses {
 		notificationStatus := statusStringToNotificationStatus(status)
 		if notificationStatus > 0 {
@@ -109,13 +109,13 @@ func ListRepoNotifications(ctx *context.APIContext) {
 	}
 	opts.RepoID = ctx.Repo.Repository.ID
 
-	totalCount, err := models.CountNotifications(opts)
+	totalCount, err := activities_model.CountNotifications(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
 	}
 
-	nl, err := models.GetNotifications(ctx, opts)
+	nl, err := activities_model.GetNotifications(ctx, opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -192,7 +192,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
 		}
 	}
 
-	opts := &models.FindNotificationOptions{
+	opts := &activities_model.FindNotificationOptions{
 		UserID:            ctx.Doer.ID,
 		RepoID:            ctx.Repo.Repository.ID,
 		UpdatedBeforeUnix: lastRead,
@@ -203,7 +203,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
 		opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
 		log.Error("%v", opts.Status)
 	}
-	nl, err := models.GetNotifications(ctx, opts)
+	nl, err := activities_model.GetNotifications(ctx, opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -211,13 +211,13 @@ func ReadRepoNotifications(ctx *context.APIContext) {
 
 	targetStatus := statusStringToNotificationStatus(ctx.FormString("to-status"))
 	if targetStatus == 0 {
-		targetStatus = models.NotificationStatusRead
+		targetStatus = activities_model.NotificationStatusRead
 	}
 
 	changed := make([]*structs.NotificationThread, 0, len(nl))
 
 	for _, n := range nl {
-		notif, err := models.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
+		notif, err := activities_model.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
 		if err != nil {
 			ctx.InternalServerError(err)
 			return
diff --git a/routers/api/v1/notify/threads.go b/routers/api/v1/notify/threads.go
index 7d8d34504f..44a1d30a55 100644
--- a/routers/api/v1/notify/threads.go
+++ b/routers/api/v1/notify/threads.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/modules/context"
@@ -86,10 +86,10 @@ func ReadThread(ctx *context.APIContext) {
 
 	targetStatus := statusStringToNotificationStatus(ctx.FormString("to-status"))
 	if targetStatus == 0 {
-		targetStatus = models.NotificationStatusRead
+		targetStatus = activities_model.NotificationStatusRead
 	}
 
-	notif, err := models.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
+	notif, err := activities_model.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -101,8 +101,8 @@ func ReadThread(ctx *context.APIContext) {
 	ctx.JSON(http.StatusResetContent, convert.ToNotificationThread(notif))
 }
 
-func getThread(ctx *context.APIContext) *models.Notification {
-	n, err := models.GetNotificationByID(ctx.ParamsInt64(":id"))
+func getThread(ctx *context.APIContext) *activities_model.Notification {
+	n, err := activities_model.GetNotificationByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if db.IsErrNotExist(err) {
 			ctx.Error(http.StatusNotFound, "GetNotificationByID", err)
diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go
index b923307783..1b6706e16f 100644
--- a/routers/api/v1/notify/user.go
+++ b/routers/api/v1/notify/user.go
@@ -8,7 +8,7 @@ import (
 	"net/http"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/structs"
@@ -69,13 +69,13 @@ func ListNotifications(ctx *context.APIContext) {
 		return
 	}
 
-	totalCount, err := models.CountNotifications(opts)
+	totalCount, err := activities_model.CountNotifications(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
 	}
 
-	nl, err := models.GetNotifications(ctx, opts)
+	nl, err := activities_model.GetNotifications(ctx, opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -140,7 +140,7 @@ func ReadNotifications(ctx *context.APIContext) {
 			lastRead = tmpLastRead.Unix()
 		}
 	}
-	opts := &models.FindNotificationOptions{
+	opts := &activities_model.FindNotificationOptions{
 		UserID:            ctx.Doer.ID,
 		UpdatedBeforeUnix: lastRead,
 	}
@@ -148,7 +148,7 @@ func ReadNotifications(ctx *context.APIContext) {
 		statuses := ctx.FormStrings("status-types")
 		opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
 	}
-	nl, err := models.GetNotifications(ctx, opts)
+	nl, err := activities_model.GetNotifications(ctx, opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -156,13 +156,13 @@ func ReadNotifications(ctx *context.APIContext) {
 
 	targetStatus := statusStringToNotificationStatus(ctx.FormString("to-status"))
 	if targetStatus == 0 {
-		targetStatus = models.NotificationStatusRead
+		targetStatus = activities_model.NotificationStatusRead
 	}
 
 	changed := make([]*structs.NotificationThread, 0, len(nl))
 
 	for _, n := range nl {
-		notif, err := models.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
+		notif, err := activities_model.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
 		if err != nil {
 			ctx.InternalServerError(err)
 			return
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index db37bac57e..c891d0e122 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -22,6 +22,7 @@ import (
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/routers/api/v1/user"
 	"code.gitea.io/gitea/routers/api/v1/utils"
+	org_service "code.gitea.io/gitea/services/org"
 )
 
 // ListTeams list all the teams of an organization
@@ -656,8 +657,8 @@ func AddTeamRepository(ctx *context.APIContext) {
 		ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
 		return
 	}
-	if err := models.AddRepository(ctx.Org.Team, repo); err != nil {
-		ctx.Error(http.StatusInternalServerError, "AddRepository", err)
+	if err := org_service.TeamAddRepository(ctx.Org.Team, repo); err != nil {
+		ctx.Error(http.StatusInternalServerError, "TeamAddRepository", err)
 		return
 	}
 	ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index aa425e5828..0e6236216c 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -16,6 +16,7 @@ import (
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/routers/api/v1/utils"
@@ -174,7 +175,7 @@ func AddCollaborator(ctx *context.APIContext) {
 		return
 	}
 
-	if err := models.AddCollaborator(ctx.Repo.Repository, collaborator); err != nil {
+	if err := repo_module.AddCollaborator(ctx.Repo.Repository, collaborator); err != nil {
 		ctx.Error(http.StatusInternalServerError, "AddCollaborator", err)
 		return
 	}
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index f868c53951..ed371d787c 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -170,7 +170,7 @@ func Migrate(ctx *context.APIContext) {
 		opts.Releases = false
 	}
 
-	repo, err := repo_module.CreateRepository(ctx.Doer, repoOwner, models.CreateRepoOptions{
+	repo, err := repo_module.CreateRepository(ctx.Doer, repoOwner, repo_module.CreateRepoOptions{
 		Name:           opts.RepoName,
 		Description:    opts.Description,
 		OriginalURL:    form.CloneAddr,
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 50d2c9484f..dda05203df 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	issues_model "code.gitea.io/gitea/models/issues"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	pull_model "code.gitea.io/gitea/models/pull"
@@ -753,7 +754,7 @@ func MergePullRequest(ctx *context.APIContext) {
 
 	if ctx.IsSigned {
 		// Update issue-user.
-		if err = models.SetIssueReadBy(ctx, pr.Issue.ID, ctx.Doer.ID); err != nil {
+		if err = activities_model.SetIssueReadBy(ctx, pr.Issue.ID, ctx.Doer.ID); err != nil {
 			ctx.Error(http.StatusInternalServerError, "ReadBy", err)
 			return
 		}
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index 80009f78e9..acc9696e1b 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/perm"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -49,12 +50,12 @@ func GetRelease(ctx *context.APIContext) {
 	//     "$ref": "#/responses/notFound"
 
 	id := ctx.ParamsInt64(":id")
-	release, err := models.GetReleaseByID(ctx, id)
-	if err != nil && !models.IsErrReleaseNotExist(err) {
+	release, err := repo_model.GetReleaseByID(ctx, id)
+	if err != nil && !repo_model.IsErrReleaseNotExist(err) {
 		ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
 		return
 	}
-	if err != nil && models.IsErrReleaseNotExist(err) ||
+	if err != nil && repo_model.IsErrReleaseNotExist(err) ||
 		release.IsTag || release.RepoID != ctx.Repo.Repository.ID {
 		ctx.NotFound()
 		return
@@ -114,7 +115,7 @@ func ListReleases(ctx *context.APIContext) {
 		listOptions.PageSize = ctx.FormInt("per_page")
 	}
 
-	opts := models.FindReleasesOptions{
+	opts := repo_model.FindReleasesOptions{
 		ListOptions:   listOptions,
 		IncludeDrafts: ctx.Repo.AccessMode >= perm.AccessModeWrite || ctx.Repo.UnitAccessMode(unit.TypeReleases) >= perm.AccessModeWrite,
 		IncludeTags:   false,
@@ -122,7 +123,7 @@ func ListReleases(ctx *context.APIContext) {
 		IsPreRelease:  ctx.FormOptionalBool("pre-release"),
 	}
 
-	releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
+	releases, err := repo_model.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err)
 		return
@@ -136,7 +137,7 @@ func ListReleases(ctx *context.APIContext) {
 		rels[i] = convert.ToRelease(release)
 	}
 
-	filteredCount, err := models.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
+	filteredCount, err := repo_model.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -179,9 +180,9 @@ func CreateRelease(ctx *context.APIContext) {
 	//   "409":
 	//     "$ref": "#/responses/error"
 	form := web.GetForm(ctx).(*api.CreateReleaseOption)
-	rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName)
+	rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, form.TagName)
 	if err != nil {
-		if !models.IsErrReleaseNotExist(err) {
+		if !repo_model.IsErrReleaseNotExist(err) {
 			ctx.Error(http.StatusInternalServerError, "GetRelease", err)
 			return
 		}
@@ -189,7 +190,7 @@ func CreateRelease(ctx *context.APIContext) {
 		if len(form.Target) == 0 {
 			form.Target = ctx.Repo.Repository.DefaultBranch
 		}
-		rel = &models.Release{
+		rel = &repo_model.Release{
 			RepoID:       ctx.Repo.Repository.ID,
 			PublisherID:  ctx.Doer.ID,
 			Publisher:    ctx.Doer,
@@ -203,7 +204,7 @@ func CreateRelease(ctx *context.APIContext) {
 			Repo:         ctx.Repo.Repository,
 		}
 		if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil {
-			if models.IsErrReleaseAlreadyExist(err) {
+			if repo_model.IsErrReleaseAlreadyExist(err) {
 				ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
 			} else {
 				ctx.Error(http.StatusInternalServerError, "CreateRelease", err)
@@ -272,12 +273,12 @@ func EditRelease(ctx *context.APIContext) {
 
 	form := web.GetForm(ctx).(*api.EditReleaseOption)
 	id := ctx.ParamsInt64(":id")
-	rel, err := models.GetReleaseByID(ctx, id)
-	if err != nil && !models.IsErrReleaseNotExist(err) {
+	rel, err := repo_model.GetReleaseByID(ctx, id)
+	if err != nil && !repo_model.IsErrReleaseNotExist(err) {
 		ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
 		return
 	}
-	if err != nil && models.IsErrReleaseNotExist(err) ||
+	if err != nil && repo_model.IsErrReleaseNotExist(err) ||
 		rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
 		ctx.NotFound()
 		return
@@ -307,7 +308,7 @@ func EditRelease(ctx *context.APIContext) {
 	}
 
 	// reload data from database
-	rel, err = models.GetReleaseByID(ctx, id)
+	rel, err = repo_model.GetReleaseByID(ctx, id)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
 		return
@@ -350,12 +351,12 @@ func DeleteRelease(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 
 	id := ctx.ParamsInt64(":id")
-	rel, err := models.GetReleaseByID(ctx, id)
-	if err != nil && !models.IsErrReleaseNotExist(err) {
+	rel, err := repo_model.GetReleaseByID(ctx, id)
+	if err != nil && !repo_model.IsErrReleaseNotExist(err) {
 		ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
 		return
 	}
-	if err != nil && models.IsErrReleaseNotExist(err) ||
+	if err != nil && repo_model.IsErrReleaseNotExist(err) ||
 		rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
 		ctx.NotFound()
 		return
diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go
index 7b63af34c8..a469877c13 100644
--- a/routers/api/v1/repo/release_attachment.go
+++ b/routers/api/v1/repo/release_attachment.go
@@ -7,7 +7,6 @@ package repo
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -102,9 +101,9 @@ func ListReleaseAttachments(ctx *context.APIContext) {
 	//     "$ref": "#/responses/AttachmentList"
 
 	releaseID := ctx.ParamsInt64(":id")
-	release, err := models.GetReleaseByID(ctx, releaseID)
+	release, err := repo_model.GetReleaseByID(ctx, releaseID)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound()
 			return
 		}
@@ -172,9 +171,9 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
 
 	// Check if release exists an load release
 	releaseID := ctx.ParamsInt64(":id")
-	release, err := models.GetReleaseByID(ctx, releaseID)
+	release, err := repo_model.GetReleaseByID(ctx, releaseID)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound()
 			return
 		}
diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index 73dee73e1a..2cb97c58a0 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -8,6 +8,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	releaseservice "code.gitea.io/gitea/services/release"
@@ -44,9 +45,9 @@ func GetReleaseByTag(ctx *context.APIContext) {
 
 	tag := ctx.Params(":tag")
 
-	release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
+	release, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tag)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound()
 			return
 		}
@@ -97,9 +98,9 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
 
 	tag := ctx.Params(":tag")
 
-	release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
+	release, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tag)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound()
 			return
 		}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index cdd1f7d5c4..d28b56cab6 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -11,7 +11,6 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
@@ -232,7 +231,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
 	if opt.AutoInit && opt.Readme == "" {
 		opt.Readme = "Default"
 	}
-	repo, err := repo_service.CreateRepository(ctx.Doer, owner, models.CreateRepoOptions{
+	repo, err := repo_service.CreateRepository(ctx.Doer, owner, repo_module.CreateRepoOptions{
 		Name:          opt.Name,
 		Description:   opt.Description,
 		IssueLabels:   opt.IssueLabels,
diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go
index 433d823c7e..f0f8503996 100644
--- a/routers/api/v1/repo/tag.go
+++ b/routers/api/v1/repo/tag.go
@@ -10,6 +10,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -249,9 +250,9 @@ func DeleteTag(ctx *context.APIContext) {
 	//     "$ref": "#/responses/conflict"
 	tagName := ctx.Params("*")
 
-	tag, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
+	tag, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound()
 			return
 		}
diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go
index 47c69d722b..f485f2086e 100644
--- a/routers/api/v1/repo/teams.go
+++ b/routers/api/v1/repo/teams.go
@@ -12,6 +12,7 @@ import (
 	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
+	org_service "code.gitea.io/gitea/services/org"
 )
 
 // ListTeams list a repository's teams
@@ -199,7 +200,7 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
 			ctx.Error(http.StatusUnprocessableEntity, "alreadyAdded", fmt.Errorf("team '%s' is already added to repo", team.Name))
 			return
 		}
-		err = models.AddRepository(team, ctx.Repo.Repository)
+		err = org_service.TeamAddRepository(team, ctx.Repo.Repository)
 	} else {
 		if !repoHasTeam {
 			ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name))
diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go
index d423bddbbd..82542d1ab8 100644
--- a/routers/api/v1/repo/wiki.go
+++ b/routers/api/v1/repo/wiki.go
@@ -10,7 +10,7 @@ import (
 	"net/http"
 	"net/url"
 
-	"code.gitea.io/gitea/models"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
@@ -72,9 +72,9 @@ func NewWikiPage(ctx *context.APIContext) {
 	form.ContentBase64 = string(content)
 
 	if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.ContentBase64, form.Message); err != nil {
-		if models.IsErrWikiReservedName(err) {
+		if repo_model.IsErrWikiReservedName(err) {
 			ctx.Error(http.StatusBadRequest, "IsErrWikiReservedName", err)
-		} else if models.IsErrWikiAlreadyExist(err) {
+		} else if repo_model.IsErrWikiAlreadyExist(err) {
 			ctx.Error(http.StatusBadRequest, "IsErrWikiAlreadyExists", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "AddWikiPage", err)
@@ -314,7 +314,7 @@ func ListWikiPages(ctx *context.APIContext) {
 		}
 		wikiName, err := wiki_service.FilenameToName(entry.Name())
 		if err != nil {
-			if models.IsErrWikiInvalidFileName(err) {
+			if repo_model.IsErrWikiInvalidFileName(err) {
 				continue
 			}
 			ctx.Error(http.StatusInternalServerError, "WikiFilenameToName", err)
diff --git a/routers/api/v1/swagger/user.go b/routers/api/v1/swagger/user.go
index a4d5201236..857bdc2a14 100644
--- a/routers/api/v1/swagger/user.go
+++ b/routers/api/v1/swagger/user.go
@@ -5,7 +5,7 @@
 package swagger
 
 import (
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
@@ -40,7 +40,7 @@ type swaggerModelEditUserOption struct {
 // swagger:response UserHeatmapData
 type swaggerResponseUserHeatmapData struct {
 	// in:body
-	Body []models.UserHeatmapData `json:"body"`
+	Body []activities_model.UserHeatmapData `json:"body"`
 }
 
 // UserSettings
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index 0d2e8401cc..a94db79239 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -11,8 +11,7 @@ import (
 	"net/http"
 	"strconv"
 
-	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/models/auth"
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -45,14 +44,14 @@ func ListAccessTokens(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/AccessTokenList"
 
-	opts := models.ListAccessTokensOptions{UserID: ctx.Doer.ID, ListOptions: utils.GetListOptions(ctx)}
+	opts := auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID, ListOptions: utils.GetListOptions(ctx)}
 
-	count, err := models.CountAccessTokens(opts)
+	count, err := auth_model.CountAccessTokens(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
 	}
-	tokens, err := models.ListAccessTokens(opts)
+	tokens, err := auth_model.ListAccessTokens(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -98,12 +97,12 @@ func CreateAccessToken(ctx *context.APIContext) {
 
 	form := web.GetForm(ctx).(*api.CreateAccessTokenOption)
 
-	t := &models.AccessToken{
+	t := &auth_model.AccessToken{
 		UID:  ctx.Doer.ID,
 		Name: form.Name,
 	}
 
-	exist, err := models.AccessTokenByNameExists(t)
+	exist, err := auth_model.AccessTokenByNameExists(t)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -113,7 +112,7 @@ func CreateAccessToken(ctx *context.APIContext) {
 		return
 	}
 
-	if err := models.NewAccessToken(t); err != nil {
+	if err := auth_model.NewAccessToken(t); err != nil {
 		ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
 		return
 	}
@@ -155,7 +154,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
 	tokenID, _ := strconv.ParseInt(token, 0, 64)
 
 	if tokenID == 0 {
-		tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{
+		tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{
 			Name:   token,
 			UserID: ctx.Doer.ID,
 		})
@@ -180,8 +179,8 @@ func DeleteAccessToken(ctx *context.APIContext) {
 		return
 	}
 
-	if err := models.DeleteAccessTokenByID(tokenID, ctx.Doer.ID); err != nil {
-		if models.IsErrAccessTokenNotExist(err) {
+	if err := auth_model.DeleteAccessTokenByID(tokenID, ctx.Doer.ID); err != nil {
+		if auth_model.IsErrAccessTokenNotExist(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "DeleteAccessTokenByID", err)
@@ -213,7 +212,7 @@ func CreateOauth2Application(ctx *context.APIContext) {
 
 	data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
 
-	app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
+	app, err := auth_model.CreateOAuth2Application(ctx, auth_model.CreateOAuth2ApplicationOptions{
 		Name:         data.Name,
 		UserID:       ctx.Doer.ID,
 		RedirectURIs: data.RedirectURIs,
@@ -252,7 +251,7 @@ func ListOauth2Applications(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/OAuth2ApplicationList"
 
-	apps, total, err := auth.ListOAuth2Applications(ctx.Doer.ID, utils.GetListOptions(ctx))
+	apps, total, err := auth_model.ListOAuth2Applications(ctx.Doer.ID, utils.GetListOptions(ctx))
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err)
 		return
@@ -288,8 +287,8 @@ func DeleteOauth2Application(ctx *context.APIContext) {
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	appID := ctx.ParamsInt64(":id")
-	if err := auth.DeleteOAuth2Application(appID, ctx.Doer.ID); err != nil {
-		if auth.IsErrOAuthApplicationNotFound(err) {
+	if err := auth_model.DeleteOAuth2Application(appID, ctx.Doer.ID); err != nil {
+		if auth_model.IsErrOAuthApplicationNotFound(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err)
@@ -320,9 +319,9 @@ func GetOauth2Application(ctx *context.APIContext) {
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	appID := ctx.ParamsInt64(":id")
-	app, err := auth.GetOAuth2ApplicationByID(ctx, appID)
+	app, err := auth_model.GetOAuth2ApplicationByID(ctx, appID)
 	if err != nil {
-		if auth.IsErrOauthClientIDInvalid(err) || auth.IsErrOAuthApplicationNotFound(err) {
+		if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetOauth2ApplicationByID", err)
@@ -363,14 +362,14 @@ func UpdateOauth2Application(ctx *context.APIContext) {
 
 	data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
 
-	app, err := auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{
+	app, err := auth_model.UpdateOAuth2Application(auth_model.UpdateOAuth2ApplicationOptions{
 		Name:         data.Name,
 		UserID:       ctx.Doer.ID,
 		ID:           appID,
 		RedirectURIs: data.RedirectURIs,
 	})
 	if err != nil {
-		if auth.IsErrOauthClientIDInvalid(err) || auth.IsErrOAuthApplicationNotFound(err) {
+		if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "UpdateOauth2ApplicationByID", err)
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index 2a3cb15c0f..69197aef23 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -8,7 +8,7 @@ package user
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -139,7 +139,7 @@ func GetUserHeatmapData(ctx *context.APIContext) {
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
-	heatmap, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
+	heatmap, err := activities_model.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByUser", err)
 		return
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index c773034c53..17471ef8a6 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -15,7 +15,7 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
@@ -128,7 +128,7 @@ func Dashboard(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("admin.dashboard")
 	ctx.Data["PageIsAdmin"] = true
 	ctx.Data["PageIsAdminDashboard"] = true
-	ctx.Data["Stats"] = models.GetStatistic()
+	ctx.Data["Stats"] = activities_model.GetStatistic()
 	ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate()
 	ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion()
 	// FIXME: update periodically
@@ -144,7 +144,7 @@ func DashboardPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("admin.dashboard")
 	ctx.Data["PageIsAdmin"] = true
 	ctx.Data["PageIsAdminDashboard"] = true
-	ctx.Data["Stats"] = models.GetStatistic()
+	ctx.Data["Stats"] = activities_model.GetStatistic()
 	updateSystemStatus()
 	ctx.Data["SysStatus"] = sysStatus
 
diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go
index 8ce981ec20..17b00975ec 100644
--- a/routers/web/admin/repos.go
+++ b/routers/web/admin/repos.go
@@ -9,13 +9,13 @@ import (
 	"net/url"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/routers/web/explore"
@@ -148,7 +148,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
 	if has || !isDir {
 		// Fallthrough to failure mode
 	} else if action == "adopt" {
-		if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, models.CreateRepoOptions{
+		if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
 			Name:      dirSplit[1],
 			IsPrivate: true,
 		}); err != nil {
diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 714ae96fa4..b400fdac8c 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -15,8 +15,8 @@ import (
 	"net/url"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/auth"
+	org_model "code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -296,7 +296,7 @@ func InfoOAuth(ctx *context.Context) {
 // returns a list of "org" and "org:team" strings,
 // that the given user is a part of.
 func getOAuthGroupsForUser(user *user_model.User) ([]string, error) {
-	orgs, err := models.GetUserOrgsList(user)
+	orgs, err := org_model.GetUserOrgsList(user)
 	if err != nil {
 		return nil, fmt.Errorf("GetUserOrgList: %v", err)
 	}
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index 978469d125..e16c54b8d8 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -12,7 +12,7 @@ import (
 	"strconv"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/markup"
 	"code.gitea.io/gitea/modules/markup/markdown"
@@ -23,33 +23,33 @@ import (
 	"github.com/gorilla/feeds"
 )
 
-func toBranchLink(act *models.Action) string {
+func toBranchLink(act *activities_model.Action) string {
 	return act.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
 }
 
-func toTagLink(act *models.Action) string {
+func toTagLink(act *activities_model.Action) string {
 	return act.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
 }
 
-func toIssueLink(act *models.Action) string {
+func toIssueLink(act *activities_model.Action) string {
 	return act.GetRepoLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
 }
 
-func toPullLink(act *models.Action) string {
+func toPullLink(act *activities_model.Action) string {
 	return act.GetRepoLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
 }
 
-func toSrcLink(act *models.Action) string {
+func toSrcLink(act *activities_model.Action) string {
 	return act.GetRepoLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
 }
 
-func toReleaseLink(act *models.Action) string {
+func toReleaseLink(act *activities_model.Action) string {
 	return act.GetRepoLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
 }
 
 // renderMarkdown creates a minimal markdown render context from an action.
 // If rendering fails, the original markdown text is returned
-func renderMarkdown(ctx *context.Context, act *models.Action, content string) string {
+func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) string {
 	markdownCtx := &markup.RenderContext{
 		Ctx:       ctx,
 		URLPrefix: act.GetRepoLink(),
@@ -67,7 +67,7 @@ func renderMarkdown(ctx *context.Context, act *models.Action, content string) st
 }
 
 // feedActionsToFeedItems convert gitea's Action feed to feeds Item
-func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (items []*feeds.Item, err error) {
+func feedActionsToFeedItems(ctx *context.Context, actions activities_model.ActionList) (items []*feeds.Item, err error) {
 	for _, act := range actions {
 		act.LoadActUser()
 
@@ -78,110 +78,110 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
 		// title
 		title = act.ActUser.DisplayName() + " "
 		switch act.OpType {
-		case models.ActionCreateRepo:
+		case activities_model.ActionCreateRepo:
 			title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
 			link.Href = act.GetRepoLink()
-		case models.ActionRenameRepo:
+		case activities_model.ActionRenameRepo:
 			title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
 			link.Href = act.GetRepoLink()
-		case models.ActionCommitRepo:
+		case activities_model.ActionCommitRepo:
 			link.Href = toBranchLink(act)
 			if len(act.Content) != 0 {
 				title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
 			} else {
 				title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
 			}
-		case models.ActionCreateIssue:
+		case activities_model.ActionCreateIssue:
 			link.Href = toIssueLink(act)
 			title += ctx.TrHTMLEscapeArgs("action.create_issue", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionCreatePullRequest:
+		case activities_model.ActionCreatePullRequest:
 			link.Href = toPullLink(act)
 			title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionTransferRepo:
+		case activities_model.ActionTransferRepo:
 			link.Href = act.GetRepoLink()
 			title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
-		case models.ActionPushTag:
+		case activities_model.ActionPushTag:
 			link.Href = toTagLink(act)
 			title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoLink(), link.Href, act.GetTag(), act.ShortRepoPath())
-		case models.ActionCommentIssue:
+		case activities_model.ActionCommentIssue:
 			issueLink := toIssueLink(act)
 			if link.Href == "#" {
 				link.Href = issueLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.comment_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionMergePullRequest:
+		case activities_model.ActionMergePullRequest:
 			pullLink := toPullLink(act)
 			if link.Href == "#" {
 				link.Href = pullLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.merge_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionCloseIssue:
+		case activities_model.ActionCloseIssue:
 			issueLink := toIssueLink(act)
 			if link.Href == "#" {
 				link.Href = issueLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.close_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionReopenIssue:
+		case activities_model.ActionReopenIssue:
 			issueLink := toIssueLink(act)
 			if link.Href == "#" {
 				link.Href = issueLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.reopen_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionClosePullRequest:
+		case activities_model.ActionClosePullRequest:
 			pullLink := toPullLink(act)
 			if link.Href == "#" {
 				link.Href = pullLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.close_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionReopenPullRequest:
+		case activities_model.ActionReopenPullRequest:
 			pullLink := toPullLink(act)
 			if link.Href == "#" {
 				link.Href = pullLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionDeleteTag:
+		case activities_model.ActionDeleteTag:
 			link.Href = act.GetRepoLink()
 			title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoLink(), act.GetTag(), act.ShortRepoPath())
-		case models.ActionDeleteBranch:
+		case activities_model.ActionDeleteBranch:
 			link.Href = act.GetRepoLink()
 			title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
-		case models.ActionMirrorSyncPush:
+		case activities_model.ActionMirrorSyncPush:
 			srcLink := toSrcLink(act)
 			if link.Href == "#" {
 				link.Href = srcLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
-		case models.ActionMirrorSyncCreate:
+		case activities_model.ActionMirrorSyncCreate:
 			srcLink := toSrcLink(act)
 			if link.Href == "#" {
 				link.Href = srcLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
-		case models.ActionMirrorSyncDelete:
+		case activities_model.ActionMirrorSyncDelete:
 			link.Href = act.GetRepoLink()
 			title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoLink(), act.GetBranch(), act.ShortRepoPath())
-		case models.ActionApprovePullRequest:
+		case activities_model.ActionApprovePullRequest:
 			pullLink := toPullLink(act)
 			title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionRejectPullRequest:
+		case activities_model.ActionRejectPullRequest:
 			pullLink := toPullLink(act)
 			title += ctx.TrHTMLEscapeArgs("action.reject_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionCommentPull:
+		case activities_model.ActionCommentPull:
 			pullLink := toPullLink(act)
 			title += ctx.TrHTMLEscapeArgs("action.comment_pull", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
-		case models.ActionPublishRelease:
+		case activities_model.ActionPublishRelease:
 			releaseLink := toReleaseLink(act)
 			if link.Href == "#" {
 				link.Href = releaseLink
 			}
 			title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoLink(), releaseLink, act.ShortRepoPath(), act.Content)
-		case models.ActionPullReviewDismissed:
+		case activities_model.ActionPullReviewDismissed:
 			pullLink := toPullLink(act)
 			title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
-		case models.ActionStarRepo:
+		case activities_model.ActionStarRepo:
 			link.Href = act.GetRepoLink()
 			title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
-		case models.ActionWatchRepo:
+		case activities_model.ActionWatchRepo:
 			link.Href = act.GetRepoLink()
 			title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
 		default:
@@ -191,7 +191,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
 		// description & content
 		{
 			switch act.OpType {
-			case models.ActionCommitRepo, models.ActionMirrorSyncPush:
+			case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
 				push := templates.ActionContent2Commits(act)
 				repoLink := act.GetRepoLink()
 
@@ -212,20 +212,20 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
 					link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), push.Commits[0].Sha1)}
 				}
 
-			case models.ActionCreateIssue, models.ActionCreatePullRequest:
+			case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
 				desc = strings.Join(act.GetIssueInfos(), "#")
 				content = renderMarkdown(ctx, act, act.GetIssueContent())
-			case models.ActionCommentIssue, models.ActionApprovePullRequest, models.ActionRejectPullRequest, models.ActionCommentPull:
+			case activities_model.ActionCommentIssue, activities_model.ActionApprovePullRequest, activities_model.ActionRejectPullRequest, activities_model.ActionCommentPull:
 				desc = act.GetIssueTitle()
 				comment := act.GetIssueInfos()[1]
 				if len(comment) != 0 {
 					desc += "\n\n" + renderMarkdown(ctx, act, comment)
 				}
-			case models.ActionMergePullRequest:
+			case activities_model.ActionMergePullRequest:
 				desc = act.GetIssueInfos()[1]
-			case models.ActionCloseIssue, models.ActionReopenIssue, models.ActionClosePullRequest, models.ActionReopenPullRequest:
+			case activities_model.ActionCloseIssue, activities_model.ActionReopenIssue, activities_model.ActionClosePullRequest, activities_model.ActionReopenPullRequest:
 				desc = act.GetIssueTitle()
-			case models.ActionPullReviewDismissed:
+			case activities_model.ActionPullReviewDismissed:
 				desc = ctx.Tr("action.review_dismissed_reason") + "\n\n" + act.GetIssueInfos()[2]
 			}
 		}
diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go
index 61a39755f5..c467f7412a 100644
--- a/routers/web/feed/profile.go
+++ b/routers/web/feed/profile.go
@@ -8,7 +8,7 @@ import (
 	"net/http"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/context"
 
 	"github.com/gorilla/feeds"
@@ -26,7 +26,7 @@ func ShowUserFeedAtom(ctx *context.Context) {
 
 // showUserFeed show user activity as RSS / Atom feed
 func showUserFeed(ctx *context.Context, formatType string) {
-	actions, err := models.GetFeeds(ctx, models.GetFeedsOptions{
+	actions, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
 		RequestedUser:   ctx.ContextUser,
 		Actor:           ctx.Doer,
 		IncludePrivate:  false,
diff --git a/routers/web/feed/repo.go b/routers/web/feed/repo.go
index ac856195b9..027f90872f 100644
--- a/routers/web/feed/repo.go
+++ b/routers/web/feed/repo.go
@@ -7,7 +7,7 @@ package feed
 import (
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/context"
 
@@ -16,7 +16,7 @@ import (
 
 // ShowRepoFeed shows user activity on the repo as RSS / Atom feed
 func ShowRepoFeed(ctx *context.Context, repo *repo_model.Repository, formatType string) {
-	actions, err := models.GetFeeds(ctx, models.GetFeedsOptions{
+	actions, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
 		RequestedRepo:  repo,
 		Actor:          ctx.Doer,
 		IncludePrivate: true,
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index a3c3acb4f4..13c88565c4 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -26,6 +26,7 @@ import (
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/routers/utils"
 	"code.gitea.io/gitea/services/forms"
+	org_service "code.gitea.io/gitea/services/org"
 )
 
 const (
@@ -194,7 +195,7 @@ func TeamsRepoAction(ctx *context.Context) {
 			ctx.ServerError("GetRepositoryByName", err)
 			return
 		}
-		err = models.AddRepository(ctx.Org.Team, repo)
+		err = org_service.TeamAddRepository(ctx.Org.Team, repo)
 	case "remove":
 		err = models.RemoveRepository(ctx.Org.Team, ctx.FormInt64("repoid"))
 	case "addall":
diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go
index b2f25ebe72..7f2ed8cb26 100644
--- a/routers/web/repo/activity.go
+++ b/routers/web/repo/activity.go
@@ -8,7 +8,7 @@ import (
 	"net/http"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -52,7 +52,7 @@ func Activity(ctx *context.Context) {
 	ctx.Data["PeriodText"] = ctx.Tr("repo.activity.period." + ctx.Data["Period"].(string))
 
 	var err error
-	if ctx.Data["Activity"], err = models.GetActivityStats(ctx, ctx.Repo.Repository, timeFrom,
+	if ctx.Data["Activity"], err = activities_model.GetActivityStats(ctx, ctx.Repo.Repository, timeFrom,
 		ctx.Repo.CanRead(unit.TypeReleases),
 		ctx.Repo.CanRead(unit.TypeIssues),
 		ctx.Repo.CanRead(unit.TypePullRequests),
@@ -61,7 +61,7 @@ func Activity(ctx *context.Context) {
 		return
 	}
 
-	if ctx.PageData["repoActivityTopAuthors"], err = models.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10); err != nil {
+	if ctx.PageData["repoActivityTopAuthors"], err = activities_model.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10); err != nil {
 		ctx.ServerError("GetActivityStatsTopAuthors", err)
 		return
 	}
@@ -94,7 +94,7 @@ func ActivityAuthors(ctx *context.Context) {
 	}
 
 	var err error
-	authors, err := models.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10)
+	authors, err := activities_model.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10)
 	if err != nil {
 		ctx.ServerError("GetActivityStatsTopAuthors", err)
 		return
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 8ed794b45c..f34d3a5203 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -17,7 +17,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
 	access_model "code.gitea.io/gitea/models/perm/access"
@@ -459,7 +458,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
 	if rootRepo != nil &&
 		rootRepo.ID != ci.HeadRepo.ID &&
 		rootRepo.ID != baseRepo.ID {
-		canRead := models.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode)
+		canRead := access_model.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode)
 		if canRead {
 			ctx.Data["RootRepo"] = rootRepo
 			if !fileOnly {
@@ -484,7 +483,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
 		ownForkRepo.ID != ci.HeadRepo.ID &&
 		ownForkRepo.ID != baseRepo.ID &&
 		(rootRepo == nil || ownForkRepo.ID != rootRepo.ID) {
-		canRead := models.CheckRepoUnitUser(ctx, ownForkRepo, ctx.Doer, unit.TypeCode)
+		canRead := access_model.CheckRepoUnitUser(ctx, ownForkRepo, ctx.Doer, unit.TypeCode)
 		if canRead {
 			ctx.Data["OwnForkRepo"] = ownForkRepo
 			if !fileOnly {
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index b510fd504d..e8fc020450 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	git_model "code.gitea.io/gitea/models/git"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/charset"
@@ -763,7 +764,7 @@ func UploadFileToServer(ctx *context.Context) {
 		return
 	}
 
-	upload, err := models.NewUpload(name, buf, file)
+	upload, err := repo_model.NewUpload(name, buf, file)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, fmt.Sprintf("NewUpload: %v", err))
 		return
@@ -783,7 +784,7 @@ func RemoveUploadFileFromServer(ctx *context.Context) {
 		return
 	}
 
-	if err := models.DeleteUploadByUUID(form.File); err != nil {
+	if err := repo_model.DeleteUploadByUUID(form.File); err != nil {
 		ctx.Error(http.StatusInternalServerError, fmt.Sprintf("DeleteUploadByUUID: %v", err))
 		return
 	}
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index ad25a94e13..3f14416e48 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -19,7 +19,7 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
@@ -1313,7 +1313,7 @@ func ViewIssue(ctx *context.Context) {
 
 	if ctx.IsSigned {
 		// Update issue-user.
-		if err = models.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
+		if err = activities_model.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
 			ctx.ServerError("ReadBy", err)
 			return
 		}
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 7c140a4e59..9b2c7c02cb 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -17,6 +17,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
@@ -295,7 +296,7 @@ func checkPullInfo(ctx *context.Context) *issues_model.Issue {
 
 	if ctx.IsSigned {
 		// Update issue-user.
-		if err = models.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
+		if err = activities_model.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
 			ctx.ServerError("ReadBy", err)
 			return nil
 		}
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index ab87c3e238..935813051a 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
@@ -33,7 +34,7 @@ const (
 )
 
 // calReleaseNumCommitsBehind calculates given release has how many commits behind release target.
-func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *models.Release, countCache map[string]int64) error {
+func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model.Release, countCache map[string]int64) error {
 	// Fast return if release target is same as default branch.
 	if repoCtx.BranchName == release.Target {
 		release.NumCommitsBehind = repoCtx.CommitsCount - release.NumCommits
@@ -115,25 +116,25 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
 	writeAccess := ctx.Repo.CanWrite(unit.TypeReleases)
 	ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
 
-	opts := models.FindReleasesOptions{
+	opts := repo_model.FindReleasesOptions{
 		ListOptions:   listOptions,
 		IncludeDrafts: writeAccess && !isTagList,
 		IncludeTags:   isTagList,
 	}
 
-	releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
+	releases, err := repo_model.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
 	if err != nil {
 		ctx.ServerError("GetReleasesByRepoID", err)
 		return
 	}
 
-	count, err := models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, opts)
+	count, err := repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, opts)
 	if err != nil {
 		ctx.ServerError("GetReleaseCountByRepoID", err)
 		return
 	}
 
-	if err = models.GetReleaseAttachments(ctx, releases...); err != nil {
+	if err = repo_model.GetReleaseAttachments(ctx, releases...); err != nil {
 		ctx.ServerError("GetReleaseAttachments", err)
 		return
 	}
@@ -199,9 +200,9 @@ func SingleRelease(ctx *context.Context) {
 	writeAccess := ctx.Repo.CanWrite(unit.TypeReleases)
 	ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
 
-	release, err := models.GetRelease(ctx.Repo.Repository.ID, ctx.Params("*"))
+	release, err := repo_model.GetRelease(ctx.Repo.Repository.ID, ctx.Params("*"))
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound("GetRelease", err)
 			return
 		}
@@ -209,7 +210,7 @@ func SingleRelease(ctx *context.Context) {
 		return
 	}
 
-	err = models.GetReleaseAttachments(ctx, release)
+	err = repo_model.GetReleaseAttachments(ctx, release)
 	if err != nil {
 		ctx.ServerError("GetReleaseAttachments", err)
 		return
@@ -241,15 +242,15 @@ func SingleRelease(ctx *context.Context) {
 		return
 	}
 
-	ctx.Data["Releases"] = []*models.Release{release}
+	ctx.Data["Releases"] = []*repo_model.Release{release}
 	ctx.HTML(http.StatusOK, tplReleases)
 }
 
 // LatestRelease redirects to the latest release
 func LatestRelease(ctx *context.Context) {
-	release, err := models.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
+	release, err := repo_model.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound("LatestRelease", err)
 			return
 		}
@@ -272,8 +273,8 @@ func NewRelease(ctx *context.Context) {
 	ctx.Data["RequireTribute"] = true
 	ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
 	if tagName := ctx.FormString("tag"); len(tagName) > 0 {
-		rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
-		if err != nil && !models.IsErrReleaseNotExist(err) {
+		rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
+		if err != nil && !repo_model.IsErrReleaseNotExist(err) {
 			ctx.ServerError("GetRelease", err)
 			return
 		}
@@ -321,9 +322,9 @@ func NewReleasePost(ctx *context.Context) {
 		attachmentUUIDs = form.Files
 	}
 
-	rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName)
+	rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, form.TagName)
 	if err != nil {
-		if !models.IsErrReleaseNotExist(err) {
+		if !repo_model.IsErrReleaseNotExist(err) {
 			ctx.ServerError("GetRelease", err)
 			return
 		}
@@ -363,7 +364,7 @@ func NewReleasePost(ctx *context.Context) {
 			return
 		}
 
-		rel = &models.Release{
+		rel = &repo_model.Release{
 			RepoID:       ctx.Repo.Repository.ID,
 			Repo:         ctx.Repo.Repository,
 			PublisherID:  ctx.Doer.ID,
@@ -380,7 +381,7 @@ func NewReleasePost(ctx *context.Context) {
 		if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil {
 			ctx.Data["Err_TagName"] = true
 			switch {
-			case models.IsErrReleaseAlreadyExist(err):
+			case repo_model.IsErrReleaseAlreadyExist(err):
 				ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
 			case models.IsErrInvalidTagName(err):
 				ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
@@ -427,9 +428,9 @@ func EditRelease(ctx *context.Context) {
 	upload.AddUploadContext(ctx, "release")
 
 	tagName := ctx.Params("*")
-	rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
+	rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound("GetRelease", err)
 		} else {
 			ctx.ServerError("GetRelease", err)
@@ -463,9 +464,9 @@ func EditReleasePost(ctx *context.Context) {
 	ctx.Data["RequireTribute"] = true
 
 	tagName := ctx.Params("*")
-	rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
+	rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
 	if err != nil {
-		if models.IsErrReleaseNotExist(err) {
+		if repo_model.IsErrReleaseNotExist(err) {
 			ctx.NotFound("GetRelease", err)
 		} else {
 			ctx.ServerError("GetRelease", err)
diff --git a/routers/web/repo/release_test.go b/routers/web/repo/release_test.go
index 33cf54cdc9..16371fc860 100644
--- a/routers/web/repo/release_test.go
+++ b/routers/web/repo/release_test.go
@@ -7,7 +7,7 @@ package repo
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/modules/web"
@@ -52,7 +52,7 @@ func TestNewReleasePost(t *testing.T) {
 		test.LoadGitRepo(t, ctx)
 		web.SetForm(ctx, &testCase.Form)
 		NewReleasePost(ctx)
-		unittest.AssertExistsAndLoadBean(t, &models.Release{
+		unittest.AssertExistsAndLoadBean(t, &repo_model.Release{
 			RepoID:      1,
 			PublisherID: 2,
 			TagName:     testCase.Form.TagName,
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index c2c79e4a0d..fe95d1e478 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -15,6 +15,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
+	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -152,7 +153,7 @@ func Create(ctx *context.Context) {
 	templateID := ctx.FormInt64("template_id")
 	if templateID > 0 {
 		templateRepo, err := repo_model.GetRepositoryByID(templateID)
-		if err == nil && models.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
+		if err == nil && access_model.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
 			ctx.Data["repo_template"] = templateID
 			ctx.Data["repo_template_name"] = templateRepo.Name
 		}
@@ -257,7 +258,7 @@ func CreatePost(ctx *context.Context) {
 			return
 		}
 	} else {
-		repo, err = repo_service.CreateRepository(ctx.Doer, ctxUser, models.CreateRepoOptions{
+		repo, err = repo_service.CreateRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
 			Name:          form.RepoName,
 			Description:   form.Description,
 			Gitignores:    form.Gitignores,
@@ -358,7 +359,7 @@ func RedirectDownload(ctx *context.Context) {
 	)
 	tagNames := []string{vTag}
 	curRepo := ctx.Repo.Repository
-	releases, err := models.GetReleasesByRepoIDAndNames(ctx, curRepo.ID, tagNames)
+	releases, err := repo_model.GetReleasesByRepoIDAndNames(ctx, curRepo.ID, tagNames)
 	if err != nil {
 		if repo_model.IsErrAttachmentNotExist(err) {
 			ctx.Error(http.StatusNotFound)
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index 2a04dc06a3..267940c8d2 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -30,7 +30,7 @@ import (
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/log"
 	mirror_module "code.gitea.io/gitea/modules/mirror"
-	"code.gitea.io/gitea/modules/repository"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/typesniffer"
@@ -43,6 +43,7 @@ import (
 	"code.gitea.io/gitea/services/mailer"
 	"code.gitea.io/gitea/services/migrations"
 	mirror_service "code.gitea.io/gitea/services/mirror"
+	org_service "code.gitea.io/gitea/services/org"
 	repo_service "code.gitea.io/gitea/services/repository"
 	wiki_service "code.gitea.io/gitea/services/wiki"
 )
@@ -607,7 +608,7 @@ func SettingsPost(ctx *context.Context) {
 		}
 		repo.IsMirror = false
 
-		if _, err := repository.CleanUpMigrateInfo(ctx, repo); err != nil {
+		if _, err := repo_module.CleanUpMigrateInfo(ctx, repo); err != nil {
 			ctx.ServerError("CleanUpMigrateInfo", err)
 			return
 		} else if err = repo_model.DeleteMirrorByRepoID(ctx.Repo.Repository.ID); err != nil {
@@ -916,7 +917,7 @@ func CollaborationPost(ctx *context.Context) {
 		return
 	}
 
-	if err = models.AddCollaborator(ctx.Repo.Repository, u); err != nil {
+	if err = repo_module.AddCollaborator(ctx.Repo.Repository, u); err != nil {
 		ctx.ServerError("AddCollaborator", err)
 		return
 	}
@@ -989,8 +990,8 @@ func AddTeamPost(ctx *context.Context) {
 		return
 	}
 
-	if err = models.AddRepository(team, ctx.Repo.Repository); err != nil {
-		ctx.ServerError("team.AddRepository", err)
+	if err = org_service.TeamAddRepository(team, ctx.Repo.Repository); err != nil {
+		ctx.ServerError("TeamAddRepository", err)
 		return
 	}
 
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 72ffda7e01..24f559fe45 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -18,7 +18,8 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
+	admin_model "code.gitea.io/gitea/models/admin"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
@@ -666,9 +667,9 @@ func safeURL(address string) string {
 func checkHomeCodeViewable(ctx *context.Context) {
 	if len(ctx.Repo.Units) > 0 {
 		if ctx.Repo.Repository.IsBeingCreated() {
-			task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
+			task, err := admin_model.GetMigratingTask(ctx.Repo.Repository.ID)
 			if err != nil {
-				if models.IsErrTaskDoesNotExist(err) {
+				if admin_model.IsErrTaskDoesNotExist(err) {
 					ctx.Data["Repo"] = ctx.Repo
 					ctx.Data["CloneAddr"] = ""
 					ctx.Data["Failed"] = true
@@ -694,7 +695,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
 
 		if ctx.IsSigned {
 			// Set repo notification-status read if unread
-			if err := models.SetRepoReadBy(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID); err != nil {
+			if err := activities_model.SetRepoReadBy(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID); err != nil {
 				ctx.ServerError("ReadBy", err)
 				return
 			}
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index 4cd5856ea6..0c161568bd 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -15,8 +15,8 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	git_model "code.gitea.io/gitea/models/git"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/charset"
@@ -164,7 +164,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
 		}
 		wikiName, err := wiki_service.FilenameToName(entry.Name())
 		if err != nil {
-			if models.IsErrWikiInvalidFileName(err) {
+			if repo_model.IsErrWikiInvalidFileName(err) {
 				continue
 			}
 			if wikiRepo != nil {
@@ -588,7 +588,7 @@ func WikiPages(ctx *context.Context) {
 		}
 		wikiName, err := wiki_service.FilenameToName(entry.Name())
 		if err != nil {
-			if models.IsErrWikiInvalidFileName(err) {
+			if repo_model.IsErrWikiInvalidFileName(err) {
 				continue
 			}
 			ctx.ServerError("WikiFilenameToName", err)
@@ -693,10 +693,10 @@ func NewWikiPost(ctx *context.Context) {
 	}
 
 	if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.Content, form.Message); err != nil {
-		if models.IsErrWikiReservedName(err) {
+		if repo_model.IsErrWikiReservedName(err) {
 			ctx.Data["Err_Title"] = true
 			ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form)
-		} else if models.IsErrWikiAlreadyExist(err) {
+		} else if repo_model.IsErrWikiAlreadyExist(err) {
 			ctx.Data["Err_Title"] = true
 			ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form)
 		} else {
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index c4aa749ce2..837caedc84 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -14,7 +14,7 @@ import (
 	"strconv"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
@@ -56,7 +56,7 @@ func getDashboardContextUser(ctx *context.Context) *user_model.User {
 	}
 	ctx.Data["ContextUser"] = ctxUser
 
-	orgs, err := models.GetUserOrgsList(ctx.Doer)
+	orgs, err := organization.GetUserOrgsList(ctx.Doer)
 	if err != nil {
 		ctx.ServerError("GetUserOrgsList", err)
 		return nil
@@ -91,7 +91,7 @@ func Dashboard(ctx *context.Context) {
 	}
 
 	if setting.Service.EnableUserHeatmap {
-		data, err := models.GetUserHeatmapDataByUserTeam(ctxUser, ctx.Org.Team, ctx.Doer)
+		data, err := activities_model.GetUserHeatmapDataByUserTeam(ctxUser, ctx.Org.Team, ctx.Doer)
 		if err != nil {
 			ctx.ServerError("GetUserHeatmapDataByUserTeam", err)
 			return
@@ -100,7 +100,7 @@ func Dashboard(ctx *context.Context) {
 	}
 
 	var err error
-	ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
+	ctx.Data["Feeds"], err = activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
 		RequestedUser:   ctxUser,
 		RequestedTeam:   ctx.Org.Team,
 		Actor:           ctx.Doer,
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index 9e658bcb14..5e8142cec7 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -12,7 +12,7 @@ import (
 	"net/url"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
@@ -36,7 +36,7 @@ func GetNotificationCount(c *context.Context) {
 	}
 
 	c.Data["NotificationUnreadCount"] = func() int64 {
-		count, err := models.GetNotificationCount(c, c.Doer, models.NotificationStatusUnread)
+		count, err := activities_model.GetNotificationCount(c, c.Doer, activities_model.NotificationStatusUnread)
 		if err != nil {
 			if err != goctx.Canceled {
 				log.Error("Unable to GetNotificationCount for user:%-v: %v", c.Doer, err)
@@ -65,7 +65,7 @@ func Notifications(c *context.Context) {
 func getNotifications(c *context.Context) {
 	var (
 		keyword = c.FormTrim("q")
-		status  models.NotificationStatus
+		status  activities_model.NotificationStatus
 		page    = c.FormInt("page")
 		perPage = c.FormInt("perPage")
 	)
@@ -78,12 +78,12 @@ func getNotifications(c *context.Context) {
 
 	switch keyword {
 	case "read":
-		status = models.NotificationStatusRead
+		status = activities_model.NotificationStatusRead
 	default:
-		status = models.NotificationStatusUnread
+		status = activities_model.NotificationStatusUnread
 	}
 
-	total, err := models.GetNotificationCount(c, c.Doer, status)
+	total, err := activities_model.GetNotificationCount(c, c.Doer, status)
 	if err != nil {
 		c.ServerError("ErrGetNotificationCount", err)
 		return
@@ -96,8 +96,8 @@ func getNotifications(c *context.Context) {
 		return
 	}
 
-	statuses := []models.NotificationStatus{status, models.NotificationStatusPinned}
-	notifications, err := models.NotificationsForUser(c, c.Doer, statuses, page, perPage)
+	statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned}
+	notifications, err := activities_model.NotificationsForUser(c, c.Doer, statuses, page, perPage)
 	if err != nil {
 		c.ServerError("ErrNotificationsForUser", err)
 		return
@@ -151,22 +151,22 @@ func NotificationStatusPost(c *context.Context) {
 	var (
 		notificationID = c.FormInt64("notification_id")
 		statusStr      = c.FormString("status")
-		status         models.NotificationStatus
+		status         activities_model.NotificationStatus
 	)
 
 	switch statusStr {
 	case "read":
-		status = models.NotificationStatusRead
+		status = activities_model.NotificationStatusRead
 	case "unread":
-		status = models.NotificationStatusUnread
+		status = activities_model.NotificationStatusUnread
 	case "pinned":
-		status = models.NotificationStatusPinned
+		status = activities_model.NotificationStatusPinned
 	default:
 		c.ServerError("InvalidNotificationStatus", errors.New("Invalid notification status"))
 		return
 	}
 
-	if _, err := models.SetNotificationStatus(notificationID, c.Doer, status); err != nil {
+	if _, err := activities_model.SetNotificationStatus(notificationID, c.Doer, status); err != nil {
 		c.ServerError("SetNotificationStatus", err)
 		return
 	}
@@ -188,7 +188,7 @@ func NotificationStatusPost(c *context.Context) {
 
 // NotificationPurgePost is a route for 'purging' the list of notifications - marking all unread as read
 func NotificationPurgePost(c *context.Context) {
-	err := models.UpdateNotificationStatuses(c.Doer, models.NotificationStatusUnread, models.NotificationStatusRead)
+	err := activities_model.UpdateNotificationStatuses(c.Doer, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead)
 	if err != nil {
 		c.ServerError("ErrUpdateNotificationStatuses", err)
 		return
@@ -199,5 +199,5 @@ func NotificationPurgePost(c *context.Context) {
 
 // NewAvailable returns the notification counts
 func NewAvailable(ctx *context.Context) {
-	ctx.JSON(http.StatusOK, structs.NotificationCount{New: models.CountUnread(ctx, ctx.Doer.ID)})
+	ctx.JSON(http.StatusOK, structs.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)})
 }
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index c804be3c5f..a3452fd692 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -10,7 +10,7 @@ import (
 	"net/http"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
 	project_model "code.gitea.io/gitea/models/project"
@@ -69,7 +69,7 @@ func Profile(ctx *context.Context) {
 	ctx.Data["IsFollowing"] = isFollowing
 
 	if setting.Service.EnableUserHeatmap {
-		data, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
+		data, err := activities_model.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
 		if err != nil {
 			ctx.ServerError("GetUserHeatmapDataByUser", err)
 			return
@@ -188,7 +188,7 @@ func Profile(ctx *context.Context) {
 
 		total = int(count)
 	case "activity":
-		ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
+		ctx.Data["Feeds"], err = activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
 			RequestedUser:   ctx.ContextUser,
 			Actor:           ctx.Doer,
 			IncludePrivate:  showPrivate,
diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go
index c7139f8bb1..a92aa6e989 100644
--- a/routers/web/user/setting/adopt.go
+++ b/routers/web/user/setting/adopt.go
@@ -7,10 +7,10 @@ package setting
 import (
 	"path/filepath"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
 	repo_service "code.gitea.io/gitea/services/repository"
@@ -46,7 +46,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
 	if has || !isDir {
 		// Fallthrough to failure mode
 	} else if action == "adopt" && allowAdopt {
-		if _, err := repo_service.AdoptRepository(ctxUser, ctxUser, models.CreateRepoOptions{
+		if _, err := repo_service.AdoptRepository(ctxUser, ctxUser, repo_module.CreateRepoOptions{
 			Name:      dir,
 			IsPrivate: true,
 		}); err != nil {
diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go
index 4ffec47801..e9572a07a6 100644
--- a/routers/web/user/setting/applications.go
+++ b/routers/web/user/setting/applications.go
@@ -8,8 +8,7 @@ package setting
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/models/auth"
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
@@ -44,12 +43,12 @@ func ApplicationsPost(ctx *context.Context) {
 		return
 	}
 
-	t := &models.AccessToken{
+	t := &auth_model.AccessToken{
 		UID:  ctx.Doer.ID,
 		Name: form.Name,
 	}
 
-	exist, err := models.AccessTokenByNameExists(t)
+	exist, err := auth_model.AccessTokenByNameExists(t)
 	if err != nil {
 		ctx.ServerError("AccessTokenByNameExists", err)
 		return
@@ -60,7 +59,7 @@ func ApplicationsPost(ctx *context.Context) {
 		return
 	}
 
-	if err := models.NewAccessToken(t); err != nil {
+	if err := auth_model.NewAccessToken(t); err != nil {
 		ctx.ServerError("NewAccessToken", err)
 		return
 	}
@@ -73,7 +72,7 @@ func ApplicationsPost(ctx *context.Context) {
 
 // DeleteApplication response for delete user access token
 func DeleteApplication(ctx *context.Context) {
-	if err := models.DeleteAccessTokenByID(ctx.FormInt64("id"), ctx.Doer.ID); err != nil {
+	if err := auth_model.DeleteAccessTokenByID(ctx.FormInt64("id"), ctx.Doer.ID); err != nil {
 		ctx.Flash.Error("DeleteAccessTokenByID: " + err.Error())
 	} else {
 		ctx.Flash.Success(ctx.Tr("settings.delete_token_success"))
@@ -85,7 +84,7 @@ func DeleteApplication(ctx *context.Context) {
 }
 
 func loadApplicationsData(ctx *context.Context) {
-	tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.Doer.ID})
+	tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
 	if err != nil {
 		ctx.ServerError("ListAccessTokens", err)
 		return
@@ -93,12 +92,12 @@ func loadApplicationsData(ctx *context.Context) {
 	ctx.Data["Tokens"] = tokens
 	ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
 	if setting.OAuth2.Enable {
-		ctx.Data["Applications"], err = auth.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID)
+		ctx.Data["Applications"], err = auth_model.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID)
 		if err != nil {
 			ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
 			return
 		}
-		ctx.Data["Grants"], err = auth.GetOAuth2GrantsByUserID(ctx, ctx.Doer.ID)
+		ctx.Data["Grants"], err = auth_model.GetOAuth2GrantsByUserID(ctx, ctx.Doer.ID)
 		if err != nil {
 			ctx.ServerError("GetOAuth2GrantsByUserID", err)
 			return
diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go
index 218cf57ab7..57ea24eeb1 100644
--- a/routers/web/user/setting/security/security.go
+++ b/routers/web/user/setting/security/security.go
@@ -8,8 +8,7 @@ package security
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/models/auth"
+	auth_model "code.gitea.io/gitea/models/auth"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -56,21 +55,21 @@ func DeleteAccountLink(ctx *context.Context) {
 }
 
 func loadSecurityData(ctx *context.Context) {
-	enrolled, err := auth.HasTwoFactorByUID(ctx.Doer.ID)
+	enrolled, err := auth_model.HasTwoFactorByUID(ctx.Doer.ID)
 	if err != nil {
 		ctx.ServerError("SettingsTwoFactor", err)
 		return
 	}
 	ctx.Data["TOTPEnrolled"] = enrolled
 
-	credentials, err := auth.GetWebAuthnCredentialsByUID(ctx.Doer.ID)
+	credentials, err := auth_model.GetWebAuthnCredentialsByUID(ctx.Doer.ID)
 	if err != nil {
 		ctx.ServerError("GetWebAuthnCredentialsByUID", err)
 		return
 	}
 	ctx.Data["WebAuthnCredentials"] = credentials
 
-	tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.Doer.ID})
+	tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
 	if err != nil {
 		ctx.ServerError("ListAccessTokens", err)
 		return
@@ -84,9 +83,9 @@ func loadSecurityData(ctx *context.Context) {
 	}
 
 	// map the provider display name with the AuthSource
-	sources := make(map[*auth.Source]string)
+	sources := make(map[*auth_model.Source]string)
 	for _, externalAccount := range accountLinks {
-		if authSource, err := auth.GetSourceByID(externalAccount.LoginSourceID); err == nil {
+		if authSource, err := auth_model.GetSourceByID(externalAccount.LoginSourceID); err == nil {
 			var providerDisplayName string
 
 			type DisplayNamed interface {
diff --git a/routers/web/user/task.go b/routers/web/user/task.go
index fd561cdd4c..7f5ef792ad 100644
--- a/routers/web/user/task.go
+++ b/routers/web/user/task.go
@@ -8,16 +8,16 @@ import (
 	"net/http"
 	"strconv"
 
-	"code.gitea.io/gitea/models"
+	admin_model "code.gitea.io/gitea/models/admin"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/json"
 )
 
 // TaskStatus returns task's status
 func TaskStatus(ctx *context.Context) {
-	task, opts, err := models.GetMigratingTaskByID(ctx.ParamsInt64("task"), ctx.Doer.ID)
+	task, opts, err := admin_model.GetMigratingTaskByID(ctx.ParamsInt64("task"), ctx.Doer.ID)
 	if err != nil {
-		if models.IsErrTaskDoesNotExist(err) {
+		if admin_model.IsErrTaskDoesNotExist(err) {
 			ctx.JSON(http.StatusNotFound, map[string]interface{}{
 				"error": "task `" + strconv.FormatInt(ctx.ParamsInt64("task"), 10) + "` does not exist",
 			})
@@ -33,9 +33,9 @@ func TaskStatus(ctx *context.Context) {
 
 	if task.Message != "" && task.Message[0] == '{' {
 		// assume message is actually a translatable string
-		var translatableMessage models.TranslatableMessage
+		var translatableMessage admin_model.TranslatableMessage
 		if err := json.Unmarshal([]byte(message), &translatableMessage); err != nil {
-			translatableMessage = models.TranslatableMessage{
+			translatableMessage = admin_model.TranslatableMessage{
 				Format: "migrate.migrating_failed.error",
 				Args:   []interface{}{task.Message},
 			}
diff --git a/services/auth/basic.go b/services/auth/basic.go
index 1869662e92..9b32ad29af 100644
--- a/services/auth/basic.go
+++ b/services/auth/basic.go
@@ -9,7 +9,7 @@ import (
 	"net/http"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	auth_model "code.gitea.io/gitea/models/auth"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/log"
@@ -85,7 +85,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
 		return u
 	}
 
-	token, err := models.GetAccessTokenBySHA(authToken)
+	token, err := auth_model.GetAccessTokenBySHA(authToken)
 	if err == nil {
 		log.Trace("Basic Authorization: Valid AccessToken for user[%d]", uid)
 		u, err := user_model.GetUserByID(token.UID)
@@ -95,13 +95,13 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
 		}
 
 		token.UpdatedUnix = timeutil.TimeStampNow()
-		if err = models.UpdateAccessToken(token); err != nil {
+		if err = auth_model.UpdateAccessToken(token); err != nil {
 			log.Error("UpdateAccessToken:  %v", err)
 		}
 
 		store.GetData()["IsApiToken"] = true
 		return u
-	} else if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
+	} else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
 		log.Error("GetAccessTokenBySha: %v", err)
 	}
 
diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go
index 68638a0806..8f038d6104 100644
--- a/services/auth/oauth2.go
+++ b/services/auth/oauth2.go
@@ -10,8 +10,7 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/models/auth"
+	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
@@ -37,8 +36,8 @@ func CheckOAuthAccessToken(accessToken string) int64 {
 		log.Trace("oauth2.ParseToken: %v", err)
 		return 0
 	}
-	var grant *auth.OAuth2Grant
-	if grant, err = auth.GetOAuth2GrantByID(db.DefaultContext, token.GrantID); err != nil || grant == nil {
+	var grant *auth_model.OAuth2Grant
+	if grant, err = auth_model.GetOAuth2GrantByID(db.DefaultContext, token.GrantID); err != nil || grant == nil {
 		return 0
 	}
 	if token.Type != oauth2.TypeAccessToken {
@@ -91,15 +90,15 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
 		}
 		return uid
 	}
-	t, err := models.GetAccessTokenBySHA(tokenSHA)
+	t, err := auth_model.GetAccessTokenBySHA(tokenSHA)
 	if err != nil {
-		if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
+		if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
 			log.Error("GetAccessTokenBySHA: %v", err)
 		}
 		return 0
 	}
 	t.UpdatedUnix = timeutil.TimeStampNow()
-	if err = models.UpdateAccessToken(t); err != nil {
+	if err = auth_model.UpdateAccessToken(t); err != nil {
 		log.Error("UpdateAccessToken: %v", err)
 	}
 	store.GetData()["IsApiToken"] = true
diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go
index 41bd5c4420..c3455ec327 100644
--- a/services/cron/tasks_extended.go
+++ b/services/cron/tasks_extended.go
@@ -8,7 +8,7 @@ import (
 	"context"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/admin"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
@@ -134,7 +134,7 @@ func registerDeleteOldActions() {
 		OlderThan: 365 * 24 * time.Hour,
 	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		olderThanConfig := config.(*OlderThanConfig)
-		return models.DeleteOldActions(olderThanConfig.OlderThan)
+		return activities_model.DeleteOldActions(olderThanConfig.OlderThan)
 	})
 }
 
diff --git a/services/issue/commit.go b/services/issue/commit.go
index 1053a81162..0d04de81bc 100644
--- a/services/issue/commit.go
+++ b/services/issue/commit.go
@@ -13,12 +13,12 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/references"
 	"code.gitea.io/gitea/modules/repository"
 )
@@ -119,8 +119,13 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm
 
 			// issue is from another repo
 			if len(ref.Owner) > 0 && len(ref.Name) > 0 {
-				refRepo, err = models.GetRepositoryFromMatch(ref.Owner, ref.Name)
+				refRepo, err = repo_model.GetRepositoryByOwnerAndName(ref.Owner, ref.Name)
 				if err != nil {
+					if repo_model.IsErrRepoNotExist(err) {
+						log.Warn("Repository referenced in commit but does not exist: %v", err)
+					} else {
+						log.Error("repo_model.GetRepositoryByOwnerAndName: %v", err)
+					}
 					continue
 				}
 			} else {
diff --git a/services/issue/commit_test.go b/services/issue/commit_test.go
index f9ad0302ff..8469bf1ac1 100644
--- a/services/issue/commit_test.go
+++ b/services/issue/commit_test.go
@@ -7,7 +7,7 @@ package issue
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -64,7 +64,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
 	unittest.AssertExistsAndLoadBean(t, commentBean)
 	unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 
 	// Test that push to a non-default branch closes no issue.
 	pushCommits = []*repository.PushCommit{
@@ -91,7 +91,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, "non-existing-branch"))
 	unittest.AssertExistsAndLoadBean(t, commentBean)
 	unittest.AssertNotExistsBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 
 	pushCommits = []*repository.PushCommit{
 		{
@@ -117,7 +117,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
 	unittest.AssertExistsAndLoadBean(t, commentBean)
 	unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
 
 func TestUpdateIssuesCommit_Colon(t *testing.T) {
@@ -142,7 +142,7 @@ func TestUpdateIssuesCommit_Colon(t *testing.T) {
 	unittest.AssertNotExistsBean(t, &issues_model.Issue{RepoID: repo.ID, Index: 2}, "is_closed=1")
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
 	unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
 
 func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
@@ -176,7 +176,7 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, "non-existing-branch"))
 	unittest.AssertExistsAndLoadBean(t, commentBean)
 	unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
 
 func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
@@ -211,7 +211,7 @@ func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
 	unittest.AssertExistsAndLoadBean(t, commentBean)
 	unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
 
 func TestUpdateIssuesCommit_AnotherRepo_FullAddress(t *testing.T) {
@@ -246,7 +246,7 @@ func TestUpdateIssuesCommit_AnotherRepo_FullAddress(t *testing.T) {
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
 	unittest.AssertExistsAndLoadBean(t, commentBean)
 	unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
 
 func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
@@ -297,5 +297,5 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
 	unittest.AssertNotExistsBean(t, commentBean)
 	unittest.AssertNotExistsBean(t, commentBean2)
 	unittest.AssertNotExistsBean(t, issueBean, "is_closed=1")
-	unittest.CheckConsistencyFor(t, &models.Action{})
+	unittest.CheckConsistencyFor(t, &activities_model.Action{})
 }
diff --git a/services/issue/issue.go b/services/issue/issue.go
index 7131829b03..bbd0278792 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -7,7 +7,7 @@ package issue
 import (
 	"fmt"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	admin_model "code.gitea.io/gitea/models/admin"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
@@ -224,7 +224,7 @@ func deleteIssue(issue *issues_model.Issue) error {
 		return err
 	}
 
-	if err := models.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil {
+	if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil {
 		return err
 	}
 
@@ -245,7 +245,7 @@ func deleteIssue(issue *issues_model.Issue) error {
 		&issues_model.IssueDependency{},
 		&issues_model.IssueAssignees{},
 		&issues_model.IssueUser{},
-		&models.Notification{},
+		&activities_model.Notification{},
 		&issues_model.Reaction{},
 		&issues_model.IssueWatch{},
 		&issues_model.Stopwatch{},
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 738a207ce8..a5bfa496f9 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -17,7 +17,7 @@ import (
 	texttmpl "text/template"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -302,7 +302,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
 
 	// Make sure to compose independent messages to avoid leaking user emails
 	msgID := createReference(ctx.Issue, ctx.Comment, ctx.ActionType)
-	reference := createReference(ctx.Issue, nil, models.ActionType(0))
+	reference := createReference(ctx.Issue, nil, activities_model.ActionType(0))
 
 	msgs := make([]*Message, 0, len(recipients))
 	for _, recipient := range recipients {
@@ -323,7 +323,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
 	return msgs, nil
 }
 
-func createReference(issue *issues_model.Issue, comment *issues_model.Comment, actionType models.ActionType) string {
+func createReference(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string {
 	var path string
 	if issue.IsPull {
 		path = "pulls"
@@ -336,13 +336,13 @@ func createReference(issue *issues_model.Issue, comment *issues_model.Comment, a
 		extra = fmt.Sprintf("/comment/%d", comment.ID)
 	} else {
 		switch actionType {
-		case models.ActionCloseIssue, models.ActionClosePullRequest:
+		case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
 			extra = fmt.Sprintf("/close/%d", time.Now().UnixNano()/1e6)
-		case models.ActionReopenIssue, models.ActionReopenPullRequest:
+		case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
 			extra = fmt.Sprintf("/reopen/%d", time.Now().UnixNano()/1e6)
-		case models.ActionMergePullRequest:
+		case activities_model.ActionMergePullRequest:
 			extra = fmt.Sprintf("/merge/%d", time.Now().UnixNano()/1e6)
-		case models.ActionPullRequestReadyForReview:
+		case activities_model.ActionPullRequestReadyForReview:
 			extra = fmt.Sprintf("/ready/%d", time.Now().UnixNano()/1e6)
 		}
 	}
@@ -420,7 +420,7 @@ func SendIssueAssignedMail(issue *issues_model.Issue, doer *user_model.User, con
 			Context:    context.TODO(), // TODO: use a correct context
 			Issue:      issue,
 			Doer:       doer,
-			ActionType: models.ActionType(0),
+			ActionType: activities_model.ActionType(0),
 			Content:    content,
 			Comment:    comment,
 		}, lang, tos, false, "issue assigned")
@@ -433,8 +433,8 @@ func SendIssueAssignedMail(issue *issues_model.Issue, doer *user_model.User, con
 }
 
 // actionToTemplate returns the type and name of the action facing the user
-// (slightly different from models.ActionType) and the name of the template to use (based on availability)
-func actionToTemplate(issue *issues_model.Issue, actionType models.ActionType,
+// (slightly different from activities_model.ActionType) and the name of the template to use (based on availability)
+func actionToTemplate(issue *issues_model.Issue, actionType activities_model.ActionType,
 	commentType issues_model.CommentType, reviewType issues_model.ReviewType,
 ) (typeName, name, template string) {
 	if issue.IsPull {
@@ -443,19 +443,19 @@ func actionToTemplate(issue *issues_model.Issue, actionType models.ActionType,
 		typeName = "issue"
 	}
 	switch actionType {
-	case models.ActionCreateIssue, models.ActionCreatePullRequest:
+	case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
 		name = "new"
-	case models.ActionCommentIssue, models.ActionCommentPull:
+	case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
 		name = "comment"
-	case models.ActionCloseIssue, models.ActionClosePullRequest:
+	case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
 		name = "close"
-	case models.ActionReopenIssue, models.ActionReopenPullRequest:
+	case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
 		name = "reopen"
-	case models.ActionMergePullRequest:
+	case activities_model.ActionMergePullRequest:
 		name = "merge"
-	case models.ActionPullReviewDismissed:
+	case activities_model.ActionPullReviewDismissed:
 		name = "review_dismissed"
-	case models.ActionPullRequestReadyForReview:
+	case activities_model.ActionPullRequestReadyForReview:
 		name = "ready_for_review"
 	default:
 		switch commentType {
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index 95d11ae8a1..2dab673b4e 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -7,7 +7,7 @@ package mailer
 import (
 	"context"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	issues_model "code.gitea.io/gitea/models/issues"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
@@ -15,7 +15,7 @@ import (
 )
 
 // MailParticipantsComment sends new comment emails to repository watchers and mentioned people.
-func MailParticipantsComment(ctx context.Context, c *issues_model.Comment, opType models.ActionType, issue *issues_model.Issue, mentions []*user_model.User) error {
+func MailParticipantsComment(ctx context.Context, c *issues_model.Comment, opType activities_model.ActionType, issue *issues_model.Issue, mentions []*user_model.User) error {
 	if setting.MailService == nil {
 		// No mail service configured
 		return nil
@@ -53,7 +53,7 @@ func MailMentionsComment(ctx context.Context, pr *issues_model.PullRequest, c *i
 			Context:    ctx,
 			Issue:      pr.Issue,
 			Doer:       c.Poster,
-			ActionType: models.ActionCommentPull,
+			ActionType: activities_model.ActionCommentPull,
 			Content:    c.Content,
 			Comment:    c,
 		}, mentions, visited, true); err != nil {
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index b4827e83a7..ec6ddcf14e 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -8,8 +8,9 @@ import (
 	"context"
 	"fmt"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	issues_model "code.gitea.io/gitea/models/issues"
+	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -25,7 +26,7 @@ type mailCommentContext struct {
 	context.Context
 	Issue      *issues_model.Issue
 	Doer       *user_model.User
-	ActionType models.ActionType
+	ActionType activities_model.ActionType
 	Content    string
 	Comment    *issues_model.Comment
 }
@@ -80,7 +81,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
 
 	// =========== Repo watchers ===========
 	// Make repo watchers last, since it's likely the list with the most users
-	if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress() && ctx.ActionType != models.ActionCreatePullRequest) {
+	if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress() && ctx.ActionType != activities_model.ActionCreatePullRequest) {
 		ids, err = repo_model.GetRepoWatchersIDs(ctx, ctx.Issue.RepoID)
 		if err != nil {
 			return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err)
@@ -149,7 +150,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
 		visited[user.ID] = true
 
 		// test if this user is allowed to see the issue/pull
-		if !models.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
+		if !access_model.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
 			continue
 		}
 
@@ -175,16 +176,16 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
 
 // MailParticipants sends new issue thread created emails to repository watchers
 // and mentioned people.
-func MailParticipants(issue *issues_model.Issue, doer *user_model.User, opType models.ActionType, mentions []*user_model.User) error {
+func MailParticipants(issue *issues_model.Issue, doer *user_model.User, opType activities_model.ActionType, mentions []*user_model.User) error {
 	if setting.MailService == nil {
 		// No mail service configured
 		return nil
 	}
 
 	content := issue.Content
-	if opType == models.ActionCloseIssue || opType == models.ActionClosePullRequest ||
-		opType == models.ActionReopenIssue || opType == models.ActionReopenPullRequest ||
-		opType == models.ActionMergePullRequest {
+	if opType == activities_model.ActionCloseIssue || opType == activities_model.ActionClosePullRequest ||
+		opType == activities_model.ActionReopenIssue || opType == activities_model.ActionReopenPullRequest ||
+		opType == activities_model.ActionMergePullRequest {
 		content = ""
 	}
 	if err := mailIssueCommentToParticipants(
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index dd9f78612c..7c44f93929 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -8,7 +8,6 @@ import (
 	"bytes"
 	"context"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
@@ -25,7 +24,7 @@ const (
 )
 
 // MailNewRelease send new release notify to all all repo watchers.
-func MailNewRelease(ctx context.Context, rel *models.Release) {
+func MailNewRelease(ctx context.Context, rel *repo_model.Release) {
 	if setting.MailService == nil {
 		// No mail service configured
 		return
@@ -55,7 +54,7 @@ func MailNewRelease(ctx context.Context, rel *models.Release) {
 	}
 }
 
-func mailNewRelease(ctx context.Context, lang string, tos []string, rel *models.Release) {
+func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_model.Release) {
 	locale := translation.NewLocale(lang)
 
 	var err error
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index 4302b2c74b..acb1f69961 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -13,7 +13,7 @@ import (
 	"testing"
 	texttmpl "text/template"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -73,7 +73,7 @@ func TestComposeIssueCommentMessage(t *testing.T) {
 	recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
 	msgs, err := composeIssueCommentMessages(&mailCommentContext{
 		Context: context.TODO(), // TODO: use a correct context
-		Issue:   issue, Doer: doer, ActionType: models.ActionCommentIssue,
+		Issue:   issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
 		Content: "test body", Comment: comment,
 	}, "en-US", recipients, false, "issue comment")
 	assert.NoError(t, err)
@@ -102,7 +102,7 @@ func TestComposeIssueMessage(t *testing.T) {
 	recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
 	msgs, err := composeIssueCommentMessages(&mailCommentContext{
 		Context: context.TODO(), // TODO: use a correct context
-		Issue:   issue, Doer: doer, ActionType: models.ActionCreateIssue,
+		Issue:   issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
 		Content: "test body",
 	}, "en-US", recipients, false, "issue create")
 	assert.NoError(t, err)
@@ -147,14 +147,14 @@ func TestTemplateSelection(t *testing.T) {
 
 	msg := testComposeIssueCommentMessage(t, &mailCommentContext{
 		Context: context.TODO(), // TODO: use a correct context
-		Issue:   issue, Doer: doer, ActionType: models.ActionCreateIssue,
+		Issue:   issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
 		Content: "test body",
 	}, recipients, false, "TestTemplateSelection")
 	expect(t, msg, "issue/new/subject", "issue/new/body")
 
 	msg = testComposeIssueCommentMessage(t, &mailCommentContext{
 		Context: context.TODO(), // TODO: use a correct context
-		Issue:   issue, Doer: doer, ActionType: models.ActionCommentIssue,
+		Issue:   issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
 		Content: "test body", Comment: comment,
 	}, recipients, false, "TestTemplateSelection")
 	expect(t, msg, "issue/default/subject", "issue/default/body")
@@ -163,14 +163,14 @@ func TestTemplateSelection(t *testing.T) {
 	comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
 	msg = testComposeIssueCommentMessage(t, &mailCommentContext{
 		Context: context.TODO(), // TODO: use a correct context
-		Issue:   pull, Doer: doer, ActionType: models.ActionCommentPull,
+		Issue:   pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
 		Content: "test body", Comment: comment,
 	}, recipients, false, "TestTemplateSelection")
 	expect(t, msg, "pull/comment/subject", "pull/comment/body")
 
 	msg = testComposeIssueCommentMessage(t, &mailCommentContext{
 		Context: context.TODO(), // TODO: use a correct context
-		Issue:   issue, Doer: doer, ActionType: models.ActionCloseIssue,
+		Issue:   issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
 		Content: "test body", Comment: comment,
 	}, recipients, false, "TestTemplateSelection")
 	expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body")
@@ -181,7 +181,7 @@ func TestTemplateServices(t *testing.T) {
 	assert.NoError(t, issue.LoadRepo(db.DefaultContext))
 
 	expect := func(t *testing.T, issue *issues_model.Issue, comment *issues_model.Comment, doer *user_model.User,
-		actionType models.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
+		actionType activities_model.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
 	) {
 		subjectTemplates = texttmpl.Must(texttmpl.New("issue/default").Parse(tplSubject))
 		bodyTemplates = template.Must(template.New("issue/default").Parse(tplBody))
@@ -202,19 +202,19 @@ func TestTemplateServices(t *testing.T) {
 		assert.Contains(t, wholemsg, "\r\n"+expBody+"\r\n")
 	}
 
-	expect(t, issue, comment, doer, models.ActionCommentIssue, false,
+	expect(t, issue, comment, doer, activities_model.ActionCommentIssue, false,
 		"{{.SubjectPrefix}}[{{.Repo}}]: @{{.Doer.Name}} commented on #{{.Issue.Index}} - {{.Issue.Title}}",
 		"//{{.ActionType}},{{.ActionName}},{{if .IsMention}}norender{{end}}//",
 		"Re: [user2/repo1]: @user2 commented on #1 - issue1",
 		"//issue,comment,//")
 
-	expect(t, issue, comment, doer, models.ActionCommentIssue, true,
+	expect(t, issue, comment, doer, activities_model.ActionCommentIssue, true,
 		"{{if .IsMention}}must render{{end}}",
 		"//subject is: {{.Subject}}//",
 		"must render",
 		"//subject is: must render//")
 
-	expect(t, issue, comment, doer, models.ActionCommentIssue, true,
+	expect(t, issue, comment, doer, activities_model.ActionCommentIssue, true,
 		"{{.FallbackSubject}}",
 		"//{{.SubjectPrefix}}//",
 		"Re: [user2/repo1] issue1 (#1)",
@@ -266,7 +266,7 @@ func Test_createReference(t *testing.T) {
 	type args struct {
 		issue      *issues_model.Issue
 		comment    *issues_model.Comment
-		actionType models.ActionType
+		actionType activities_model.ActionType
 	}
 	tests := []struct {
 		name   string
@@ -278,7 +278,7 @@ func Test_createReference(t *testing.T) {
 			name: "Open Issue",
 			args: args{
 				issue:      issue,
-				actionType: models.ActionCreateIssue,
+				actionType: activities_model.ActionCreateIssue,
 			},
 			prefix: fmt.Sprintf("%s/issues/%d@%s", issue.Repo.FullName(), issue.Index, setting.Domain),
 		},
@@ -286,7 +286,7 @@ func Test_createReference(t *testing.T) {
 			name: "Open Pull",
 			args: args{
 				issue:      pullIssue,
-				actionType: models.ActionCreatePullRequest,
+				actionType: activities_model.ActionCreatePullRequest,
 			},
 			prefix: fmt.Sprintf("%s/pulls/%d@%s", issue.Repo.FullName(), issue.Index, setting.Domain),
 		},
@@ -295,7 +295,7 @@ func Test_createReference(t *testing.T) {
 			args: args{
 				issue:      issue,
 				comment:    comment,
-				actionType: models.ActionCommentIssue,
+				actionType: activities_model.ActionCommentIssue,
 			},
 			prefix: fmt.Sprintf("%s/issues/%d/comment/%d@%s", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
 		},
@@ -304,7 +304,7 @@ func Test_createReference(t *testing.T) {
 			args: args{
 				issue:      pullIssue,
 				comment:    comment,
-				actionType: models.ActionCommentPull,
+				actionType: activities_model.ActionCommentPull,
 			},
 			prefix: fmt.Sprintf("%s/pulls/%d/comment/%d@%s", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
 		},
@@ -312,7 +312,7 @@ func Test_createReference(t *testing.T) {
 			name: "Close Issue",
 			args: args{
 				issue:      issue,
-				actionType: models.ActionCloseIssue,
+				actionType: activities_model.ActionCloseIssue,
 			},
 			prefix: fmt.Sprintf("%s/issues/%d/close/", issue.Repo.FullName(), issue.Index),
 		},
@@ -320,7 +320,7 @@ func Test_createReference(t *testing.T) {
 			name: "Close Pull",
 			args: args{
 				issue:      pullIssue,
-				actionType: models.ActionClosePullRequest,
+				actionType: activities_model.ActionClosePullRequest,
 			},
 			prefix: fmt.Sprintf("%s/pulls/%d/close/", issue.Repo.FullName(), issue.Index),
 		},
@@ -328,7 +328,7 @@ func Test_createReference(t *testing.T) {
 			name: "Reopen Issue",
 			args: args{
 				issue:      issue,
-				actionType: models.ActionReopenIssue,
+				actionType: activities_model.ActionReopenIssue,
 			},
 			prefix: fmt.Sprintf("%s/issues/%d/reopen/", issue.Repo.FullName(), issue.Index),
 		},
@@ -336,7 +336,7 @@ func Test_createReference(t *testing.T) {
 			name: "Reopen Pull",
 			args: args{
 				issue:      pullIssue,
-				actionType: models.ActionReopenPullRequest,
+				actionType: activities_model.ActionReopenPullRequest,
 			},
 			prefix: fmt.Sprintf("%s/pulls/%d/reopen/", issue.Repo.FullName(), issue.Index),
 		},
@@ -344,7 +344,7 @@ func Test_createReference(t *testing.T) {
 			name: "Merge Pull",
 			args: args{
 				issue:      pullIssue,
-				actionType: models.ActionMergePullRequest,
+				actionType: activities_model.ActionMergePullRequest,
 			},
 			prefix: fmt.Sprintf("%s/pulls/%d/merge/", issue.Repo.FullName(), issue.Index),
 		},
@@ -352,7 +352,7 @@ func Test_createReference(t *testing.T) {
 			name: "Ready Pull",
 			args: args{
 				issue:      pullIssue,
-				actionType: models.ActionPullRequestReadyForReview,
+				actionType: activities_model.ActionPullRequestReadyForReview,
 			},
 			prefix: fmt.Sprintf("%s/pulls/%d/ready/", issue.Repo.FullName(), issue.Index),
 		},
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index c7a6f9b02f..79b22bef78 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -83,7 +83,7 @@ func (g *GiteaLocalUploader) MaxBatchInsertSize(tp string) int {
 	case "label":
 		return db.MaxBatchInsertSize(new(issues_model.Label))
 	case "release":
-		return db.MaxBatchInsertSize(new(models.Release))
+		return db.MaxBatchInsertSize(new(repo_model.Release))
 	case "pullrequest":
 		return db.MaxBatchInsertSize(new(issues_model.PullRequest))
 	}
@@ -99,7 +99,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
 
 	var r *repo_model.Repository
 	if opts.MigrateToRepoID <= 0 {
-		r, err = repo_module.CreateRepository(g.doer, owner, models.CreateRepoOptions{
+		r, err = repo_module.CreateRepository(g.doer, owner, repo_module.CreateRepoOptions{
 			Name:           g.repoName,
 			Description:    repo.Description,
 			OriginalURL:    repo.OriginalURL,
@@ -237,7 +237,7 @@ func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
 
 // CreateReleases creates releases
 func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
-	rels := make([]*models.Release, 0, len(releases))
+	rels := make([]*repo_model.Release, 0, len(releases))
 	for _, release := range releases {
 		if release.Created.IsZero() {
 			if !release.Published.IsZero() {
@@ -247,7 +247,7 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
 			}
 		}
 
-		rel := models.Release{
+		rel := repo_model.Release{
 			RepoID:       g.repo.ID,
 			TagName:      release.TagName,
 			LowerTagName: strings.ToLower(release.TagName),
diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go
index 1f1c4bde6f..715dbf7a5c 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -15,7 +15,6 @@ import (
 	"testing"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -85,7 +84,7 @@ func TestGiteaUploadRepo(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Len(t, labels, 12)
 
-	releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{
+	releases, err := repo_model.GetReleasesByRepoID(repo.ID, repo_model.FindReleasesOptions{
 		ListOptions: db.ListOptions{
 			PageSize: 10,
 			Page:     0,
@@ -95,7 +94,7 @@ func TestGiteaUploadRepo(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Len(t, releases, 8)
 
-	releases, err = models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{
+	releases, err = repo_model.GetReleasesByRepoID(repo.ID, repo_model.FindReleasesOptions{
 		ListOptions: db.ListOptions{
 			PageSize: 10,
 			Page:     0,
@@ -146,7 +145,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
 	// The externalID does not match any existing user, everything
 	// belongs to the doer
 	//
-	target := models.Release{}
+	target := repo_model.Release{}
 	uploader.userMap = make(map[int64]int64)
 	err := uploader.remapUser(&source, &target)
 	assert.NoError(t, err)
@@ -157,7 +156,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
 	// everything belongs to the doer
 	//
 	source.PublisherID = user.ID
-	target = models.Release{}
+	target = repo_model.Release{}
 	uploader.userMap = make(map[int64]int64)
 	err = uploader.remapUser(&source, &target)
 	assert.NoError(t, err)
@@ -168,7 +167,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
 	// belongs to the existing user
 	//
 	source.PublisherName = user.Name
-	target = models.Release{}
+	target = repo_model.Release{}
 	uploader.userMap = make(map[int64]int64)
 	err = uploader.remapUser(&source, &target)
 	assert.NoError(t, err)
@@ -197,7 +196,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
 	// by the doer
 	//
 	uploader.userMap = make(map[int64]int64)
-	target := models.Release{}
+	target := repo_model.Release{}
 	err := uploader.remapUser(&source, &target)
 	assert.NoError(t, err)
 	assert.EqualValues(t, doer.ID, target.GetUserID())
@@ -220,7 +219,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
 	// the migrated data
 	//
 	uploader.userMap = make(map[int64]int64)
-	target = models.Release{}
+	target = repo_model.Release{}
 	err = uploader.remapUser(&source, &target)
 	assert.NoError(t, err)
 	assert.EqualValues(t, linkedUser.ID, target.GetUserID())
diff --git a/services/org/repo.go b/services/org/repo.go
new file mode 100644
index 0000000000..769419d45b
--- /dev/null
+++ b/services/org/repo.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package org
+
+import (
+	"context"
+	"errors"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
+	repo_model "code.gitea.io/gitea/models/repo"
+)
+
+// TeamAddRepository adds new repository to team of organization.
+func TeamAddRepository(t *organization.Team, repo *repo_model.Repository) (err error) {
+	if repo.OwnerID != t.OrgID {
+		return errors.New("repository does not belong to organization")
+	} else if models.HasRepository(t, repo.ID) {
+		return nil
+	}
+
+	return db.WithTx(func(ctx context.Context) error {
+		return models.AddRepository(ctx, t, repo)
+	})
+}
diff --git a/services/org/repo_test.go b/services/org/repo_test.go
new file mode 100644
index 0000000000..21158bfa21
--- /dev/null
+++ b/services/org/repo_test.go
@@ -0,0 +1,34 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package org
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/organization"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unittest"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestTeam_AddRepository(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	testSuccess := func(teamID, repoID int64) {
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
+		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
+		assert.NoError(t, TeamAddRepository(team, repo))
+		unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
+		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
+	}
+	testSuccess(2, 3)
+	testSuccess(2, 5)
+
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+	assert.Error(t, TeamAddRepository(team, repo))
+	unittest.CheckConsistencyFor(t, &organization.Team{ID: 1}, &repo_model.Repository{ID: 1})
+}
diff --git a/services/release/release.go b/services/release/release.go
index 6fa966de1f..e2e8c13699 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -23,7 +23,7 @@ import (
 	"code.gitea.io/gitea/modules/timeutil"
 )
 
-func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool, error) {
+func createTag(gitRepo *git.Repository, rel *repo_model.Release, msg string) (bool, error) {
 	var created bool
 	// Only actual create when publish.
 	if !rel.IsDraft {
@@ -112,12 +112,12 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool,
 }
 
 // CreateRelease creates a new release of repository.
-func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs []string, msg string) error {
-	has, err := models.IsReleaseExist(gitRepo.Ctx, rel.RepoID, rel.TagName)
+func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, attachmentUUIDs []string, msg string) error {
+	has, err := repo_model.IsReleaseExist(gitRepo.Ctx, rel.RepoID, rel.TagName)
 	if err != nil {
 		return err
 	} else if has {
-		return models.ErrReleaseAlreadyExist{
+		return repo_model.ErrReleaseAlreadyExist{
 			TagName: rel.TagName,
 		}
 	}
@@ -131,7 +131,7 @@ func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs
 		return err
 	}
 
-	if err = models.AddReleaseAttachments(gitRepo.Ctx, rel.ID, attachmentUUIDs); err != nil {
+	if err = repo_model.AddReleaseAttachments(gitRepo.Ctx, rel.ID, attachmentUUIDs); err != nil {
 		return err
 	}
 
@@ -144,7 +144,7 @@ func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs
 
 // CreateNewTag creates a new repository tag
 func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error {
-	has, err := models.IsReleaseExist(ctx, repo.ID, tagName)
+	has, err := repo_model.IsReleaseExist(ctx, repo.ID, tagName)
 	if err != nil {
 		return err
 	} else if has {
@@ -159,7 +159,7 @@ func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.R
 	}
 	defer closer.Close()
 
-	rel := &models.Release{
+	rel := &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  doer.ID,
@@ -182,7 +182,7 @@ func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.R
 // addAttachmentUUIDs accept a slice of new created attachments' uuids which will be reassigned release_id as the created release
 // delAttachmentUUIDs accept a slice of attachments' uuids which will be deleted from the release
 // editAttachments accept a map of attachment uuid to new attachment name which will be updated with attachments.
-func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.Release,
+func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_model.Release,
 	addAttachmentUUIDs, delAttachmentUUIDs []string, editAttachments map[string]string,
 ) (err error) {
 	if rel.ID == 0 {
@@ -200,11 +200,11 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R
 	}
 	defer committer.Close()
 
-	if err = models.UpdateRelease(ctx, rel); err != nil {
+	if err = repo_model.UpdateRelease(ctx, rel); err != nil {
 		return err
 	}
 
-	if err = models.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil {
+	if err = repo_model.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil {
 		return fmt.Errorf("AddReleaseAttachments: %v", err)
 	}
 
@@ -283,7 +283,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R
 
 // DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
 func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, delTag bool) error {
-	rel, err := models.GetReleaseByID(ctx, id)
+	rel, err := repo_model.GetReleaseByID(ctx, id)
 	if err != nil {
 		return fmt.Errorf("GetReleaseByID: %v", err)
 	}
@@ -324,13 +324,13 @@ func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, del
 			}, repository.NewPushCommits())
 		notification.NotifyDeleteRef(doer, repo, "tag", git.TagPrefix+rel.TagName)
 
-		if err := models.DeleteReleaseByID(id); err != nil {
+		if err := repo_model.DeleteReleaseByID(id); err != nil {
 			return fmt.Errorf("DeleteReleaseByID: %v", err)
 		}
 	} else {
 		rel.IsTag = true
 
-		if err = models.UpdateRelease(ctx, rel); err != nil {
+		if err = repo_model.UpdateRelease(ctx, rel); err != nil {
 			return fmt.Errorf("Update: %v", err)
 		}
 	}
diff --git a/services/release/release_test.go b/services/release/release_test.go
index d1a9298b69..c0cafb5fcc 100644
--- a/services/release/release_test.go
+++ b/services/release/release_test.go
@@ -10,7 +10,6 @@ import (
 	"testing"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -38,7 +37,7 @@ func TestRelease_Create(t *testing.T) {
 	assert.NoError(t, err)
 	defer gitRepo.Close()
 
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -52,7 +51,7 @@ func TestRelease_Create(t *testing.T) {
 		IsTag:        false,
 	}, nil, ""))
 
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -66,7 +65,7 @@ func TestRelease_Create(t *testing.T) {
 		IsTag:        false,
 	}, nil, ""))
 
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -80,7 +79,7 @@ func TestRelease_Create(t *testing.T) {
 		IsTag:        false,
 	}, nil, ""))
 
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -94,7 +93,7 @@ func TestRelease_Create(t *testing.T) {
 		IsTag:        false,
 	}, nil, ""))
 
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -115,7 +114,7 @@ func TestRelease_Create(t *testing.T) {
 	}, strings.NewReader("testtest"))
 	assert.NoError(t, err)
 
-	release := models.Release{
+	release := repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -143,7 +142,7 @@ func TestRelease_Update(t *testing.T) {
 	defer gitRepo.Close()
 
 	// Test a changed release
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -156,18 +155,18 @@ func TestRelease_Update(t *testing.T) {
 		IsPrerelease: false,
 		IsTag:        false,
 	}, nil, ""))
-	release, err := models.GetRelease(repo.ID, "v1.1.1")
+	release, err := repo_model.GetRelease(repo.ID, "v1.1.1")
 	assert.NoError(t, err)
 	releaseCreatedUnix := release.CreatedUnix
 	time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
 	release.Note = "Changed note"
 	assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
-	release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+	release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
 	assert.NoError(t, err)
 	assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
 
 	// Test a changed draft
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -180,18 +179,18 @@ func TestRelease_Update(t *testing.T) {
 		IsPrerelease: false,
 		IsTag:        false,
 	}, nil, ""))
-	release, err = models.GetRelease(repo.ID, "v1.2.1")
+	release, err = repo_model.GetRelease(repo.ID, "v1.2.1")
 	assert.NoError(t, err)
 	releaseCreatedUnix = release.CreatedUnix
 	time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
 	release.Title = "Changed title"
 	assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
-	release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+	release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
 	assert.NoError(t, err)
 	assert.Less(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
 
 	// Test a changed pre-release
-	assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+	assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -204,19 +203,19 @@ func TestRelease_Update(t *testing.T) {
 		IsPrerelease: true,
 		IsTag:        false,
 	}, nil, ""))
-	release, err = models.GetRelease(repo.ID, "v1.3.1")
+	release, err = repo_model.GetRelease(repo.ID, "v1.3.1")
 	assert.NoError(t, err)
 	releaseCreatedUnix = release.CreatedUnix
 	time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
 	release.Title = "Changed title"
 	release.Note = "Changed note"
 	assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
-	release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+	release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
 	assert.NoError(t, err)
 	assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
 
 	// Test create release
-	release = &models.Release{
+	release = &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -236,7 +235,7 @@ func TestRelease_Update(t *testing.T) {
 	tagName := release.TagName
 
 	assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
-	release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+	release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
 	assert.NoError(t, err)
 	assert.Equal(t, tagName, release.TagName)
 
@@ -249,7 +248,7 @@ func TestRelease_Update(t *testing.T) {
 	assert.NoError(t, err)
 
 	assert.NoError(t, UpdateRelease(user, gitRepo, release, []string{attach.UUID}, nil, nil))
-	assert.NoError(t, models.GetReleaseAttachments(db.DefaultContext, release))
+	assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
 	assert.Len(t, release.Attachments, 1)
 	assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
 	assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
@@ -260,7 +259,7 @@ func TestRelease_Update(t *testing.T) {
 		attach.UUID: "test2.txt",
 	}))
 	release.Attachments = nil
-	assert.NoError(t, models.GetReleaseAttachments(db.DefaultContext, release))
+	assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
 	assert.Len(t, release.Attachments, 1)
 	assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
 	assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
@@ -269,7 +268,7 @@ func TestRelease_Update(t *testing.T) {
 	// delete the attachment
 	assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, []string{attach.UUID}, nil))
 	release.Attachments = nil
-	assert.NoError(t, models.GetReleaseAttachments(db.DefaultContext, release))
+	assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
 	assert.Empty(t, release.Attachments)
 }
 
@@ -285,7 +284,7 @@ func TestRelease_createTag(t *testing.T) {
 	defer gitRepo.Close()
 
 	// Test a changed release
-	release := &models.Release{
+	release := &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -309,7 +308,7 @@ func TestRelease_createTag(t *testing.T) {
 	assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
 
 	// Test a changed draft
-	release = &models.Release{
+	release = &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
@@ -332,7 +331,7 @@ func TestRelease_createTag(t *testing.T) {
 	assert.Less(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
 
 	// Test a changed pre-release
-	release = &models.Release{
+	release = &repo_model.Release{
 		RepoID:       repo.ID,
 		Repo:         repo,
 		PublisherID:  user.ID,
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 6d6611c705..74876d8e76 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -11,7 +11,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -26,7 +25,7 @@ import (
 )
 
 // AdoptRepository adopts pre-existing repository files for the user/organization.
-func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) {
+func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) {
 	if !doer.IsAdmin && !u.CanCreateRepo() {
 		return nil, repo_model.ErrReachLimitOfRepo{
 			Limit: u.MaxRepoCreation,
@@ -67,7 +66,7 @@ func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*
 			}
 		}
 
-		if err := models.CreateRepository(ctx, doer, u, repo, true); err != nil {
+		if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, true); err != nil {
 			return err
 		}
 		if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
@@ -100,7 +99,7 @@ func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*
 	return repo, nil
 }
 
-func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts models.CreateRepoOptions) (err error) {
+func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts repo_module.CreateRepoOptions) (err error) {
 	isExist, err := util.IsExist(repoPath)
 	if err != nil {
 		log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go
index ffc1f4efe9..327a2e121c 100644
--- a/services/repository/files/upload.go
+++ b/services/repository/files/upload.go
@@ -11,7 +11,6 @@ import (
 	"path"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	git_model "code.gitea.io/gitea/models/git"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -32,7 +31,7 @@ type UploadRepoFileOptions struct {
 }
 
 type uploadInfo struct {
-	upload        *models.Upload
+	upload        *repo_model.Upload
 	lfsMetaObject *git_model.LFSMetaObject
 }
 
@@ -56,7 +55,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 		return nil
 	}
 
-	uploads, err := models.GetUploadsByUUIDs(opts.Files)
+	uploads, err := repo_model.GetUploadsByUUIDs(opts.Files)
 	if err != nil {
 		return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err)
 	}
@@ -157,7 +156,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 		return err
 	}
 
-	return models.DeleteUploads(uploads...)
+	return repo_model.DeleteUploads(uploads...)
 }
 
 func copyUploadedLFSFileIntoRepository(info *uploadInfo, filename2attribute2info map[string]map[string]string, t *TemporaryUploadRepository, treePath string) error {
diff --git a/services/repository/fork.go b/services/repository/fork.go
index b274585ed5..96c391e715 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -23,6 +22,23 @@ import (
 	"code.gitea.io/gitea/modules/util"
 )
 
+// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
+type ErrForkAlreadyExist struct {
+	Uname    string
+	RepoName string
+	ForkName string
+}
+
+// IsErrForkAlreadyExist checks if an error is an ErrForkAlreadyExist.
+func IsErrForkAlreadyExist(err error) bool {
+	_, ok := err.(ErrForkAlreadyExist)
+	return ok
+}
+
+func (err ErrForkAlreadyExist) Error() string {
+	return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName)
+}
+
 // ForkRepoOptions contains the fork repository options
 type ForkRepoOptions struct {
 	BaseRepo    *repo_model.Repository
@@ -37,7 +53,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
 		return nil, err
 	}
 	if forkedRepo != nil {
-		return nil, models.ErrForkAlreadyExist{
+		return nil, ErrForkAlreadyExist{
 			Uname:    owner.Name,
 			RepoName: opts.BaseRepo.FullName(),
 			ForkName: forkedRepo.FullName(),
@@ -93,7 +109,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
 	}()
 
 	err = db.WithTx(func(txCtx context.Context) error {
-		if err = models.CreateRepository(txCtx, doer, owner, repo, false); err != nil {
+		if err = repo_module.CreateRepositoryByExample(txCtx, doer, owner, repo, false); err != nil {
 			return err
 		}
 
diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go
index 376c5c06d9..d4ba507351 100644
--- a/services/repository/fork_test.go
+++ b/services/repository/fork_test.go
@@ -7,7 +7,6 @@ package repository
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -30,5 +29,5 @@ func TestForkRepository(t *testing.T) {
 	})
 	assert.Nil(t, fork)
 	assert.Error(t, err)
-	assert.True(t, models.IsErrForkAlreadyExist(err))
+	assert.True(t, IsErrForkAlreadyExist(err))
 }
diff --git a/services/repository/push.go b/services/repository/push.go
index 65ac7b660c..f3f505aa00 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -11,7 +11,6 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -292,7 +291,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 // PushUpdateAddDeleteTags updates a number of added and delete tags
 func PushUpdateAddDeleteTags(repo *repo_model.Repository, gitRepo *git.Repository, addTags, delTags []string) error {
 	return db.WithTx(func(ctx context.Context) error {
-		if err := models.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil {
+		if err := repo_model.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil {
 			return err
 		}
 		return pushUpdateAddTags(ctx, repo, gitRepo, addTags)
@@ -310,16 +309,16 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
 		lowerTags = append(lowerTags, strings.ToLower(tag))
 	}
 
-	releases, err := models.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags)
+	releases, err := repo_model.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags)
 	if err != nil {
 		return fmt.Errorf("GetReleasesByRepoIDAndNames: %v", err)
 	}
-	relMap := make(map[string]*models.Release)
+	relMap := make(map[string]*repo_model.Release)
 	for _, rel := range releases {
 		relMap[rel.LowerTagName] = rel
 	}
 
-	newReleases := make([]*models.Release, 0, len(lowerTags)-len(relMap))
+	newReleases := make([]*repo_model.Release, 0, len(lowerTags)-len(relMap))
 
 	emailToUser := make(map[string]*user_model.User)
 
@@ -366,7 +365,7 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
 		rel, has := relMap[lowerTag]
 
 		if !has {
-			rel = &models.Release{
+			rel = &repo_model.Release{
 				RepoID:       repo.ID,
 				Title:        "",
 				TagName:      tags[i],
@@ -393,7 +392,7 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
 			if rel.IsTag && author != nil {
 				rel.PublisherID = author.ID
 			}
-			if err = models.UpdateRelease(ctx, rel); err != nil {
+			if err = repo_model.UpdateRelease(ctx, rel); err != nil {
 				return fmt.Errorf("Update: %v", err)
 			}
 		}
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 4bde6879a6..d530358360 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -25,7 +25,7 @@ import (
 )
 
 // CreateRepository creates a repository for the user/organization.
-func CreateRepository(doer, owner *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) {
+func CreateRepository(doer, owner *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) {
 	repo, err := repo_module.CreateRepository(doer, owner, opts)
 	if err != nil {
 		// No need to rollback here we should do this in CreateRepository...
@@ -69,7 +69,7 @@ func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_mo
 		}
 	}
 
-	repo, err := CreateRepository(authUser, owner, models.CreateRepoOptions{
+	repo, err := CreateRepository(authUser, owner, repo_module.CreateRepoOptions{
 		Name:      repoName,
 		IsPrivate: setting.Repository.DefaultPushCreatePrivate,
 	})
@@ -117,7 +117,7 @@ func LinkedRepository(a *repo_model.Attachment) (*repo_model.Repository, unit.Ty
 		}
 		return repo, unitType, err
 	} else if a.ReleaseID != 0 {
-		rel, err := models.GetReleaseByID(db.DefaultContext, a.ReleaseID)
+		rel, err := repo_model.GetReleaseByID(db.DefaultContext, a.ReleaseID)
 		if err != nil {
 			return nil, unit.TypeReleases, err
 		}
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index ae15383240..a0f4a7685c 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -16,6 +16,7 @@ import (
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification"
+	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/sync"
 )
 
@@ -49,7 +50,7 @@ func TransferOwnership(doer, newOwner *user_model.User, repo *repo_model.Reposit
 	}
 
 	for _, team := range teams {
-		if err := models.AddRepository(team, newRepo); err != nil {
+		if err := models.AddRepository(db.DefaultContext, team, newRepo); err != nil {
 			return err
 		}
 	}
@@ -111,7 +112,7 @@ func StartRepositoryTransfer(doer, newOwner *user_model.User, repo *repo_model.R
 		return err
 	}
 	if !hasAccess {
-		if err := models.AddCollaborator(repo, newOwner); err != nil {
+		if err := repo_module.AddCollaborator(repo, newOwner); err != nil {
 			return err
 		}
 		if err := repo_model.ChangeCollaborationAccessMode(repo, newOwner.ID, perm.AccessModeRead); err != nil {
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index 3c929f2f7b..bf2a0ce0a2 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -8,7 +8,7 @@ import (
 	"sync"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
 	access_model "code.gitea.io/gitea/models/perm/access"
@@ -49,8 +49,8 @@ func TestTransferOwnership(t *testing.T) {
 	exist, err = util.IsExist(repo_model.RepoPath("user2", "repo3"))
 	assert.NoError(t, err)
 	assert.True(t, exist)
-	unittest.AssertExistsAndLoadBean(t, &models.Action{
-		OpType:    models.ActionTransferRepo,
+	unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
+		OpType:    activities_model.ActionTransferRepo,
 		ActUserID: 2,
 		RepoID:    3,
 		Content:   "user3/repo3",
diff --git a/services/task/migrate.go b/services/task/migrate.go
index 651681ef65..775cbf6128 100644
--- a/services/task/migrate.go
+++ b/services/task/migrate.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	admin_model "code.gitea.io/gitea/models/admin"
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -40,7 +41,7 @@ func handleCreateError(owner *user_model.User, err error) error {
 	}
 }
 
-func runMigrateTask(t *models.Task) (err error) {
+func runMigrateTask(t *admin_model.Task) (err error) {
 	defer func() {
 		if e := recover(); e != nil {
 			err = fmt.Errorf("PANIC whilst trying to do migrate task: %v", e)
@@ -48,7 +49,7 @@ func runMigrateTask(t *models.Task) (err error) {
 		}
 
 		if err == nil {
-			err = models.FinishMigrateTask(t)
+			err = admin_model.FinishMigrateTask(t)
 			if err == nil {
 				notification.NotifyMigrateRepository(t.Doer, t.Owner, t.Repo)
 				return
@@ -110,7 +111,7 @@ func runMigrateTask(t *models.Task) (err error) {
 	}
 
 	t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
-		message := models.TranslatableMessage{
+		message := admin_model.TranslatableMessage{
 			Format: format,
 			Args:   args,
 		}
diff --git a/services/task/task.go b/services/task/task.go
index 9deb0286c5..138dc88a04 100644
--- a/services/task/task.go
+++ b/services/task/task.go
@@ -7,7 +7,7 @@ package task
 import (
 	"fmt"
 
-	"code.gitea.io/gitea/models"
+	admin_model "code.gitea.io/gitea/models/admin"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/graceful"
@@ -27,7 +27,7 @@ import (
 var taskQueue queue.Queue
 
 // Run a task
-func Run(t *models.Task) error {
+func Run(t *admin_model.Task) error {
 	switch t.Type {
 	case structs.TaskTypeMigrateRepo:
 		return runMigrateTask(t)
@@ -38,7 +38,7 @@ func Run(t *models.Task) error {
 
 // Init will start the service to get all unfinished tasks and run them
 func Init() error {
-	taskQueue = queue.CreateQueue("task", handle, &models.Task{})
+	taskQueue = queue.CreateQueue("task", handle, &admin_model.Task{})
 
 	if taskQueue == nil {
 		return fmt.Errorf("Unable to create Task Queue")
@@ -51,7 +51,7 @@ func Init() error {
 
 func handle(data ...queue.Data) []queue.Data {
 	for _, datum := range data {
-		task := datum.(*models.Task)
+		task := datum.(*admin_model.Task)
 		if err := Run(task); err != nil {
 			log.Error("Run task failed: %v", err)
 		}
@@ -70,7 +70,7 @@ func MigrateRepository(doer, u *user_model.User, opts base.MigrateOptions) error
 }
 
 // CreateMigrateTask creates a migrate task
-func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*models.Task, error) {
+func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*admin_model.Task, error) {
 	// encrypt credentials for persistence
 	var err error
 	opts.CloneAddrEncrypted, err = secret.EncryptSecret(setting.SecretKey, opts.CloneAddr)
@@ -93,7 +93,7 @@ func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*mod
 		return nil, err
 	}
 
-	task := &models.Task{
+	task := &admin_model.Task{
 		DoerID:         doer.ID,
 		OwnerID:        u.ID,
 		Type:           structs.TaskTypeMigrateRepo,
@@ -101,11 +101,11 @@ func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*mod
 		PayloadContent: string(bs),
 	}
 
-	if err := models.CreateTask(task); err != nil {
+	if err := admin_model.CreateTask(task); err != nil {
 		return nil, err
 	}
 
-	repo, err := repo_module.CreateRepository(doer, u, models.CreateRepoOptions{
+	repo, err := repo_module.CreateRepository(doer, u, repo_module.CreateRepoOptions{
 		Name:           opts.RepoName,
 		Description:    opts.Description,
 		OriginalURL:    opts.OriginalURL,
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 43e35eed69..ac5283bbeb 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -12,7 +12,6 @@ import (
 	"os"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	admin_model "code.gitea.io/gitea/models/admin"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -33,7 +32,7 @@ var (
 
 func nameAllowed(name string) error {
 	if util.IsStringInSlice(name, reservedWikiNames) {
-		return models.ErrWikiReservedName{
+		return repo_model.ErrWikiReservedName{
 			Title: name,
 		}
 	}
@@ -59,7 +58,7 @@ func NameToFilename(name string) string {
 // FilenameToName converts a wiki filename to its corresponding page name.
 func FilenameToName(filename string) (string, error) {
 	if !strings.HasSuffix(filename, ".md") {
-		return "", models.ErrWikiInvalidFileName{
+		return "", repo_model.ErrWikiInvalidFileName{
 			FileName: filename,
 		}
 	}
@@ -178,7 +177,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
 
 	if isNew {
 		if isWikiExist {
-			return models.ErrWikiAlreadyExist{
+			return repo_model.ErrWikiAlreadyExist{
 				Title: newWikiPath,
 			}
 		}
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index 1852ddbcb3..938a7d6acd 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -9,7 +9,6 @@ import (
 	"path/filepath"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -90,11 +89,11 @@ func TestWikiFilenameToName(t *testing.T) {
 	} {
 		_, err := FilenameToName(badFilename)
 		assert.Error(t, err)
-		assert.True(t, models.IsErrWikiInvalidFileName(err))
+		assert.True(t, repo_model.IsErrWikiInvalidFileName(err))
 	}
 	_, err := FilenameToName("badescaping%%.md")
 	assert.Error(t, err)
-	assert.False(t, models.IsErrWikiInvalidFileName(err))
+	assert.False(t, repo_model.IsErrWikiInvalidFileName(err))
 }
 
 func TestWikiNameToFilenameToName(t *testing.T) {
@@ -157,7 +156,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
 		// test for already-existing wiki name
 		err := AddWikiPage(git.DefaultContext, doer, repo, "Home", wikiContent, commitMsg)
 		assert.Error(t, err)
-		assert.True(t, models.IsErrWikiAlreadyExist(err))
+		assert.True(t, repo_model.IsErrWikiAlreadyExist(err))
 	})
 
 	t.Run("check wiki reserved name", func(t *testing.T) {
@@ -165,7 +164,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
 		// test for reserved wiki name
 		err := AddWikiPage(git.DefaultContext, doer, repo, "_edit", wikiContent, commitMsg)
 		assert.Error(t, err)
-		assert.True(t, models.IsErrWikiReservedName(err))
+		assert.True(t, repo_model.IsErrWikiReservedName(err))
 	})
 }
 
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 19f16b5c1c..8a8ca9a924 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -18828,7 +18828,7 @@
           "$ref": "#/definitions/TimeStamp"
         }
       },
-      "x-go-package": "code.gitea.io/gitea/models"
+      "x-go-package": "code.gitea.io/gitea/models/activities"
     },
     "UserSettings": {
       "description": "UserSettings represents user settings",