diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 2f87b0d7bd..fc3fdc608f 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -217,6 +217,57 @@ func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error
 	return util.RemoveAll(repoPath)
 }
 
+type unadoptedRrepositories struct {
+	repositories []string
+	index        int
+	start        int
+	end          int
+}
+
+func (unadopted *unadoptedRrepositories) add(repository string) {
+	if unadopted.index >= unadopted.start && unadopted.index < unadopted.end {
+		unadopted.repositories = append(unadopted.repositories, repository)
+	}
+	unadopted.index++
+}
+
+func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unadopted *unadoptedRrepositories) error {
+	if len(repoNamesToCheck) == 0 {
+		return nil
+	}
+	ctxUser, err := user_model.GetUserByName(userName)
+	if err != nil {
+		if user_model.IsErrUserNotExist(err) {
+			log.Debug("Missing user: %s", userName)
+			return nil
+		}
+		return err
+	}
+	repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
+		Actor:   ctxUser,
+		Private: true,
+		ListOptions: db.ListOptions{
+			Page:     1,
+			PageSize: len(repoNamesToCheck),
+		}, LowerNames: repoNamesToCheck})
+	if err != nil {
+		return err
+	}
+	if len(repos) == len(repoNamesToCheck) {
+		return nil
+	}
+	repoNames := make(map[string]bool, len(repos))
+	for _, repo := range repos {
+		repoNames[repo.LowerName] = true
+	}
+	for _, repoName := range repoNamesToCheck {
+		if _, ok := repoNames[repoName]; !ok {
+			unadopted.add(filepath.Join(userName, repoName))
+		}
+	}
+	return nil
+}
+
 // ListUnadoptedRepositories lists all the unadopted repositories that match the provided query
 func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, int, error) {
 	globUser, _ := glob.Compile("*")
@@ -236,15 +287,17 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
 			}
 		}
 	}
+	var repoNamesToCheck []string
+
 	start := (opts.Page - 1) * opts.PageSize
-	end := start + opts.PageSize
+	unadopted := &unadoptedRrepositories{
+		repositories: make([]string, 0, opts.PageSize),
+		start:        start,
+		end:          start + opts.PageSize,
+		index:        0,
+	}
 
-	repoNamesToCheck := make([]string, 0, opts.PageSize)
-
-	repoNames := make([]string, 0, opts.PageSize)
-	var ctxUser *user_model.User
-
-	count := 0
+	var userName string
 
 	// We're going to iterate by pagesize.
 	root := filepath.Clean(setting.RepoRootPath)
@@ -258,51 +311,16 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
 
 		if !strings.ContainsRune(path[len(root)+1:], filepath.Separator) {
 			// Got a new user
-
-			// Clean up old repoNamesToCheck
-			if len(repoNamesToCheck) > 0 {
-				repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
-					Actor:   ctxUser,
-					Private: true,
-					ListOptions: db.ListOptions{
-						Page:     1,
-						PageSize: opts.PageSize,
-					}, LowerNames: repoNamesToCheck})
-				if err != nil {
-					return err
-				}
-				for _, name := range repoNamesToCheck {
-					found := false
-				repoLoopCatchup:
-					for i, repo := range repos {
-						if repo.LowerName == name {
-							found = true
-							repos = append(repos[:i], repos[i+1:]...)
-							break repoLoopCatchup
-						}
-					}
-					if !found {
-						if count >= start && count < end {
-							repoNames = append(repoNames, fmt.Sprintf("%s/%s", ctxUser.Name, name))
-						}
-						count++
-					}
-				}
-				repoNamesToCheck = repoNamesToCheck[:0]
+			if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil {
+				return err
 			}
+			repoNamesToCheck = repoNamesToCheck[:0]
 
 			if !globUser.Match(info.Name()) {
 				return filepath.SkipDir
 			}
 
-			ctxUser, err = user_model.GetUserByName(info.Name())
-			if err != nil {
-				if user_model.IsErrUserNotExist(err) {
-					log.Debug("Missing user: %s", info.Name())
-					return filepath.SkipDir
-				}
-				return err
-			}
+			userName = info.Name()
 			return nil
 		}
 
@@ -315,74 +333,16 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
 		if repo_model.IsUsableRepoName(name) != nil || strings.ToLower(name) != name || !globRepo.Match(name) {
 			return filepath.SkipDir
 		}
-		if count < end {
-			repoNamesToCheck = append(repoNamesToCheck, name)
-			if len(repoNamesToCheck) >= opts.PageSize {
-				repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
-					Actor:   ctxUser,
-					Private: true,
-					ListOptions: db.ListOptions{
-						Page:     1,
-						PageSize: opts.PageSize,
-					}, LowerNames: repoNamesToCheck})
-				if err != nil {
-					return err
-				}
-				for _, name := range repoNamesToCheck {
-					found := false
-				repoLoop:
-					for i, repo := range repos {
-						if repo.LowerName == name {
-							found = true
-							repos = append(repos[:i], repos[i+1:]...)
-							break repoLoop
-						}
-					}
-					if !found {
-						if count >= start && count < end {
-							repoNames = append(repoNames, fmt.Sprintf("%s/%s", ctxUser.Name, name))
-						}
-						count++
-					}
-				}
-				repoNamesToCheck = repoNamesToCheck[:0]
-			}
-			return filepath.SkipDir
-		}
-		count++
+
+		repoNamesToCheck = append(repoNamesToCheck, name)
 		return filepath.SkipDir
 	}); err != nil {
 		return nil, 0, err
 	}
 
-	if len(repoNamesToCheck) > 0 {
-		repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
-			Actor:   ctxUser,
-			Private: true,
-			ListOptions: db.ListOptions{
-				Page:     1,
-				PageSize: opts.PageSize,
-			}, LowerNames: repoNamesToCheck})
-		if err != nil {
-			return nil, 0, err
-		}
-		for _, name := range repoNamesToCheck {
-			found := false
-		repoLoop:
-			for i, repo := range repos {
-				if repo.LowerName == name {
-					found = true
-					repos = append(repos[:i], repos[i+1:]...)
-					break repoLoop
-				}
-			}
-			if !found {
-				if count >= start && count < end {
-					repoNames = append(repoNames, fmt.Sprintf("%s/%s", ctxUser.Name, name))
-				}
-				count++
-			}
-		}
+	if err := checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil {
+		return nil, 0, err
 	}
-	return repoNames, count, nil
+
+	return unadopted.repositories, unadopted.index, nil
 }
diff --git a/services/repository/adopt_test.go b/services/repository/adopt_test.go
new file mode 100644
index 0000000000..2053151106
--- /dev/null
+++ b/services/repository/adopt_test.go
@@ -0,0 +1,86 @@
+// 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 repository
+
+import (
+	"os"
+	"path"
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unittest"
+	"code.gitea.io/gitea/modules/setting"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCheckUnadoptedRepositories_Add(t *testing.T) {
+	start := 10
+	end := 20
+	unadopted := &unadoptedRrepositories{
+		start: start,
+		end:   end,
+		index: 0,
+	}
+
+	total := 30
+	for i := 0; i < total; i++ {
+		unadopted.add("something")
+	}
+
+	assert.Equal(t, total, unadopted.index)
+	assert.Equal(t, end-start, len(unadopted.repositories))
+}
+
+func TestCheckUnadoptedRepositories(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	//
+	// Non existent user
+	//
+	unadopted := &unadoptedRrepositories{start: 0, end: 100}
+	err := checkUnadoptedRepositories("notauser", []string{"repo"}, unadopted)
+	assert.NoError(t, err)
+	assert.Equal(t, 0, len(unadopted.repositories))
+	//
+	// Unadopted repository is returned
+	// Existing (adopted) repository is not returned
+	//
+	userName := "user2"
+	repoName := "repo2"
+	unadoptedRepoName := "unadopted"
+	unadopted = &unadoptedRrepositories{start: 0, end: 100}
+	err = checkUnadoptedRepositories(userName, []string{repoName, unadoptedRepoName}, unadopted)
+	assert.NoError(t, err)
+	assert.Equal(t, []string{path.Join(userName, unadoptedRepoName)}, unadopted.repositories)
+	//
+	// Existing (adopted) repository is not returned
+	//
+	unadopted = &unadoptedRrepositories{start: 0, end: 100}
+	err = checkUnadoptedRepositories(userName, []string{repoName}, unadopted)
+	assert.NoError(t, err)
+	assert.Equal(t, 0, len(unadopted.repositories))
+	assert.Equal(t, 0, unadopted.index)
+}
+
+func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	username := "user2"
+	unadoptedList := []string{path.Join(username, "unadopted1"), path.Join(username, "unadopted2")}
+	for _, unadopted := range unadoptedList {
+		_ = os.Mkdir(path.Join(setting.RepoRootPath, unadopted+".git"), 0755)
+	}
+
+	opts := db.ListOptions{Page: 1, PageSize: 1}
+	repoNames, count, err := ListUnadoptedRepositories("", &opts)
+	assert.NoError(t, err)
+	assert.Equal(t, 2, count)
+	assert.Equal(t, unadoptedList[0], repoNames[0])
+
+	opts = db.ListOptions{Page: 2, PageSize: 1}
+	repoNames, count, err = ListUnadoptedRepositories("", &opts)
+	assert.NoError(t, err)
+	assert.Equal(t, 2, count)
+	assert.Equal(t, unadoptedList[1], repoNames[0])
+}