diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index ee3dac3621..cbf8ae8732 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -248,6 +248,8 @@ var migrations = []Migration{
 	NewMigration("add changed_protected_files column for pull_request table", addChangedProtectedFilesPullRequestColumn),
 	// v156 -> v157
 	NewMigration("fix publisher ID for tag releases", fixPublisherIDforTagReleases),
+	// v157 -> v158
+	NewMigration("ensure repo topics are up-to-date", fixRepoTopics),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v157.go b/models/migrations/v157.go
new file mode 100644
index 0000000000..9689281c89
--- /dev/null
+++ b/models/migrations/v157.go
@@ -0,0 +1,68 @@
+// 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 migrations
+
+import (
+	"xorm.io/xorm"
+)
+
+func fixRepoTopics(x *xorm.Engine) error {
+
+	type Topic struct {
+		ID        int64  `xorm:"pk autoincr"`
+		Name      string `xorm:"UNIQUE VARCHAR(25)"`
+		RepoCount int
+	}
+
+	type RepoTopic struct {
+		RepoID  int64 `xorm:"pk"`
+		TopicID int64 `xorm:"pk"`
+	}
+
+	type Repository struct {
+		ID     int64    `xorm:"pk autoincr"`
+		Topics []string `xorm:"TEXT JSON"`
+	}
+
+	const batchSize = 100
+	sess := x.NewSession()
+	defer sess.Close()
+	repos := make([]*Repository, 0, batchSize)
+	topics := make([]string, 0, batchSize)
+	for start := 0; ; start += batchSize {
+		repos = repos[:0]
+
+		if err := sess.Begin(); err != nil {
+			return err
+		}
+
+		if err := sess.Limit(batchSize, start).Find(&repos); err != nil {
+			return err
+		}
+
+		if len(repos) == 0 {
+			break
+		}
+
+		for _, repo := range repos {
+			topics = topics[:0]
+			if err := sess.Select("name").Table("topic").
+				Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
+				Where("repo_topic.repo_id = ?", repo.ID).Desc("topic.repo_count").Find(&topics); err != nil {
+				return err
+			}
+			repo.Topics = topics
+			if _, err := sess.ID(repo.ID).Cols("topics").Update(repo); err != nil {
+				return err
+			}
+		}
+
+		if err := sess.Commit(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/models/topic.go b/models/topic.go
index 0075c1702d..6be4f10295 100644
--- a/models/topic.go
+++ b/models/topic.go
@@ -197,10 +197,13 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) {
 
 // GetRepoTopicByName retrives topic from name for a repo if it exist
 func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) {
+	return getRepoTopicByName(x, repoID, topicName)
+}
+func getRepoTopicByName(e Engine, repoID int64, topicName string) (*Topic, error) {
 	var cond = builder.NewCond()
 	var topic Topic
 	cond = cond.And(builder.Eq{"repo_topic.repo_id": repoID}).And(builder.Eq{"topic.name": topicName})
-	sess := x.Table("topic").Where(cond)
+	sess := e.Table("topic").Where(cond)
 	sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
 	has, err := sess.Get(&topic)
 	if has {
@@ -211,7 +214,13 @@ func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) {
 
 // AddTopic adds a topic name to a repository (if it does not already have it)
 func AddTopic(repoID int64, topicName string) (*Topic, error) {
-	topic, err := GetRepoTopicByName(repoID, topicName)
+	sess := x.NewSession()
+	defer sess.Close()
+	if err := sess.Begin(); err != nil {
+		return nil, err
+	}
+
+	topic, err := getRepoTopicByName(sess, repoID, topicName)
 	if err != nil {
 		return nil, err
 	}
@@ -220,7 +229,25 @@ func AddTopic(repoID int64, topicName string) (*Topic, error) {
 		return topic, nil
 	}
 
-	return addTopicByNameToRepo(x, repoID, topicName)
+	topic, err = addTopicByNameToRepo(sess, repoID, topicName)
+	if err != nil {
+		return nil, err
+	}
+
+	topicNames := make([]string, 0, 25)
+	if err := sess.Select("name").Table("topic").
+		Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
+		Where("repo_topic.repo_id = ?", repoID).Desc("topic.repo_count").Find(&topicNames); err != nil {
+		return nil, err
+	}
+
+	if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{
+		Topics: topicNames,
+	}); err != nil {
+		return nil, err
+	}
+
+	return topic, sess.Commit()
 }
 
 // DeleteTopic removes a topic name from a repository (if it has it)