summaryrefslogtreecommitdiffstats
path: root/models/repo/repo.go
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2021-12-10 09:27:50 +0800
committerGitHub <noreply@github.com>2021-12-10 09:27:50 +0800
commit719bddcd76610a63dadc8555760072957a11cf30 (patch)
tree0df26092fba7e3e21444fe493e6b349473b6b0cb /models/repo/repo.go
parentfb8166c6c6b652a0e6fa98681780a6a71090faf3 (diff)
downloadgitea-719bddcd76610a63dadc8555760072957a11cf30.tar.gz
gitea-719bddcd76610a63dadc8555760072957a11cf30.zip
Move repository model into models/repo (#17933)
* Some refactors related repository model * Move more methods out of repository * Move repository into models/repo * Fix test * Fix test * some improvements * Remove unnecessary function
Diffstat (limited to 'models/repo/repo.go')
-rw-r--r--models/repo/repo.go736
1 files changed, 736 insertions, 0 deletions
diff --git a/models/repo/repo.go b/models/repo/repo.go
new file mode 100644
index 0000000000..9353e813bc
--- /dev/null
+++ b/models/repo/repo.go
@@ -0,0 +1,736 @@
+// 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 repo
+
+import (
+ "context"
+ "fmt"
+ "html/template"
+ "net"
+ "net/url"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
+)
+
+// TrustModelType defines the types of trust model for this repository
+type TrustModelType int
+
+// kinds of TrustModel
+const (
+ DefaultTrustModel TrustModelType = iota // default trust model
+ CommitterTrustModel
+ CollaboratorTrustModel
+ CollaboratorCommitterTrustModel
+)
+
+// String converts a TrustModelType to a string
+func (t TrustModelType) String() string {
+ switch t {
+ case DefaultTrustModel:
+ return "default"
+ case CommitterTrustModel:
+ return "committer"
+ case CollaboratorTrustModel:
+ return "collaborator"
+ case CollaboratorCommitterTrustModel:
+ return "collaboratorcommitter"
+ }
+ return "default"
+}
+
+// ToTrustModel converts a string to a TrustModelType
+func ToTrustModel(model string) TrustModelType {
+ switch strings.ToLower(strings.TrimSpace(model)) {
+ case "default":
+ return DefaultTrustModel
+ case "collaborator":
+ return CollaboratorTrustModel
+ case "committer":
+ return CommitterTrustModel
+ case "collaboratorcommitter":
+ return CollaboratorCommitterTrustModel
+ }
+ return DefaultTrustModel
+}
+
+// RepositoryStatus defines the status of repository
+type RepositoryStatus int
+
+// all kinds of RepositoryStatus
+const (
+ RepositoryReady RepositoryStatus = iota // a normal repository
+ RepositoryBeingMigrated // repository is migrating
+ RepositoryPendingTransfer // repository pending in ownership transfer state
+ RepositoryBroken // repository is in a permanently broken state
+)
+
+// Repository represents a git repository.
+type Repository struct {
+ ID int64 `xorm:"pk autoincr"`
+ OwnerID int64 `xorm:"UNIQUE(s) index"`
+ OwnerName string
+ Owner *user_model.User `xorm:"-"`
+ LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
+ Name string `xorm:"INDEX NOT NULL"`
+ Description string `xorm:"TEXT"`
+ Website string `xorm:"VARCHAR(2048)"`
+ OriginalServiceType api.GitServiceType `xorm:"index"`
+ OriginalURL string `xorm:"VARCHAR(2048)"`
+ DefaultBranch string
+
+ NumWatches int
+ NumStars int
+ NumForks int
+ NumIssues int
+ NumClosedIssues int
+ NumOpenIssues int `xorm:"-"`
+ NumPulls int
+ NumClosedPulls int
+ NumOpenPulls int `xorm:"-"`
+ NumMilestones int `xorm:"NOT NULL DEFAULT 0"`
+ NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"`
+ NumOpenMilestones int `xorm:"-"`
+ NumProjects int `xorm:"NOT NULL DEFAULT 0"`
+ NumClosedProjects int `xorm:"NOT NULL DEFAULT 0"`
+ NumOpenProjects int `xorm:"-"`
+
+ IsPrivate bool `xorm:"INDEX"`
+ IsEmpty bool `xorm:"INDEX"`
+ IsArchived bool `xorm:"INDEX"`
+ IsMirror bool `xorm:"INDEX"`
+ *Mirror `xorm:"-"`
+ Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0"`
+
+ RenderingMetas map[string]string `xorm:"-"`
+ DocumentRenderingMetas map[string]string `xorm:"-"`
+ Units []*RepoUnit `xorm:"-"`
+ PrimaryLanguage *LanguageStat `xorm:"-"`
+
+ IsFork bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ ForkID int64 `xorm:"INDEX"`
+ BaseRepo *Repository `xorm:"-"`
+ IsTemplate bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ TemplateID int64 `xorm:"INDEX"`
+ Size int64 `xorm:"NOT NULL DEFAULT 0"`
+ CodeIndexerStatus *RepoIndexerStatus `xorm:"-"`
+ StatsIndexerStatus *RepoIndexerStatus `xorm:"-"`
+ IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"`
+ CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"`
+ Topics []string `xorm:"TEXT JSON"`
+
+ TrustModel TrustModelType
+
+ // Avatar: ID(10-20)-md5(32) - must fit into 64 symbols
+ Avatar string `xorm:"VARCHAR(64)"`
+
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+ UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+}
+
+func init() {
+ db.RegisterModel(new(Repository))
+}
+
+// SanitizedOriginalURL returns a sanitized OriginalURL
+func (repo *Repository) SanitizedOriginalURL() string {
+ if repo.OriginalURL == "" {
+ return ""
+ }
+ u, err := url.Parse(repo.OriginalURL)
+ if err != nil {
+ return ""
+ }
+ u.User = nil
+ return u.String()
+}
+
+// ColorFormat returns a colored string to represent this repo
+func (repo *Repository) ColorFormat(s fmt.State) {
+ log.ColorFprintf(s, "%d:%s/%s",
+ log.NewColoredIDValue(repo.ID),
+ repo.OwnerName,
+ repo.Name)
+}
+
+// IsBeingMigrated indicates that repository is being migrated
+func (repo *Repository) IsBeingMigrated() bool {
+ return repo.Status == RepositoryBeingMigrated
+}
+
+// IsBeingCreated indicates that repository is being migrated or forked
+func (repo *Repository) IsBeingCreated() bool {
+ return repo.IsBeingMigrated()
+}
+
+// IsBroken indicates that repository is broken
+func (repo *Repository) IsBroken() bool {
+ return repo.Status == RepositoryBroken
+}
+
+// AfterLoad is invoked from XORM after setting the values of all fields of this object.
+func (repo *Repository) AfterLoad() {
+ // FIXME: use models migration to solve all at once.
+ if len(repo.DefaultBranch) == 0 {
+ repo.DefaultBranch = setting.Repository.DefaultBranch
+ }
+
+ repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
+ repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls
+ repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
+ repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects
+}
+
+// MustOwner always returns a valid *user_model.User object to avoid
+// conceptually impossible error handling.
+// It creates a fake object that contains error details
+// when error occurs.
+func (repo *Repository) MustOwner() *user_model.User {
+ return repo.mustOwner(db.DefaultContext)
+}
+
+// FullName returns the repository full name
+func (repo *Repository) FullName() string {
+ return repo.OwnerName + "/" + repo.Name
+}
+
+// HTMLURL returns the repository HTML URL
+func (repo *Repository) HTMLURL() string {
+ return setting.AppURL + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
+}
+
+// CommitLink make link to by commit full ID
+// note: won't check whether it's an right id
+func (repo *Repository) CommitLink(commitID string) (result string) {
+ if commitID == "" || commitID == "0000000000000000000000000000000000000000" {
+ result = ""
+ } else {
+ result = repo.HTMLURL() + "/commit/" + url.PathEscape(commitID)
+ }
+ return
+}
+
+// APIURL returns the repository API URL
+func (repo *Repository) APIURL() string {
+ return setting.AppURL + "api/v1/repos/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
+}
+
+// GetCommitsCountCacheKey returns cache key used for commits count caching.
+func (repo *Repository) GetCommitsCountCacheKey(contextName string, isRef bool) string {
+ var prefix string
+ if isRef {
+ prefix = "ref"
+ } else {
+ prefix = "commit"
+ }
+ return fmt.Sprintf("commits-count-%d-%s-%s", repo.ID, prefix, contextName)
+}
+
+// LoadUnits loads repo units into repo.Units
+func (repo *Repository) LoadUnits(ctx context.Context) (err error) {
+ if repo.Units != nil {
+ return nil
+ }
+
+ repo.Units, err = getUnitsByRepoID(db.GetEngine(ctx), repo.ID)
+ log.Trace("repo.Units: %-+v", repo.Units)
+ return err
+}
+
+// UnitEnabled if this repository has the given unit enabled
+func (repo *Repository) UnitEnabled(tp unit.Type) bool {
+ if err := repo.LoadUnits(db.DefaultContext); err != nil {
+ log.Warn("Error loading repository (ID: %d) units: %s", repo.ID, err.Error())
+ }
+ for _, unit := range repo.Units {
+ if unit.Type == tp {
+ return true
+ }
+ }
+ return false
+}
+
+// MustGetUnit always returns a RepoUnit object
+func (repo *Repository) MustGetUnit(tp unit.Type) *RepoUnit {
+ ru, err := repo.GetUnit(tp)
+ if err == nil {
+ return ru
+ }
+
+ if tp == unit.TypeExternalWiki {
+ return &RepoUnit{
+ Type: tp,
+ Config: new(ExternalWikiConfig),
+ }
+ } else if tp == unit.TypeExternalTracker {
+ return &RepoUnit{
+ Type: tp,
+ Config: new(ExternalTrackerConfig),
+ }
+ } else if tp == unit.TypePullRequests {
+ return &RepoUnit{
+ Type: tp,
+ Config: new(PullRequestsConfig),
+ }
+ } else if tp == unit.TypeIssues {
+ return &RepoUnit{
+ Type: tp,
+ Config: new(IssuesConfig),
+ }
+ }
+ return &RepoUnit{
+ Type: tp,
+ Config: new(UnitConfig),
+ }
+}
+
+// GetUnit returns a RepoUnit object
+func (repo *Repository) GetUnit(tp unit.Type) (*RepoUnit, error) {
+ return repo.getUnit(db.DefaultContext, tp)
+}
+
+func (repo *Repository) getUnit(ctx context.Context, tp unit.Type) (*RepoUnit, error) {
+ if err := repo.LoadUnits(ctx); err != nil {
+ return nil, err
+ }
+ for _, unit := range repo.Units {
+ if unit.Type == tp {
+ return unit, nil
+ }
+ }
+ return nil, ErrUnitTypeNotExist{tp}
+}
+
+// GetOwner returns the repository owner
+func (repo *Repository) GetOwner(ctx context.Context) (err error) {
+ if repo.Owner != nil {
+ return nil
+ }
+
+ repo.Owner, err = user_model.GetUserByIDEngine(db.GetEngine(ctx), repo.OwnerID)
+ return err
+}
+
+func (repo *Repository) mustOwner(ctx context.Context) *user_model.User {
+ if err := repo.GetOwner(ctx); err != nil {
+ return &user_model.User{
+ Name: "error",
+ FullName: err.Error(),
+ }
+ }
+
+ return repo.Owner
+}
+
+// ComposeMetas composes a map of metas for properly rendering issue links and external issue trackers.
+func (repo *Repository) ComposeMetas() map[string]string {
+ if len(repo.RenderingMetas) == 0 {
+ metas := map[string]string{
+ "user": repo.OwnerName,
+ "repo": repo.Name,
+ "repoPath": repo.RepoPath(),
+ "mode": "comment",
+ }
+
+ unit, err := repo.GetUnit(unit.TypeExternalTracker)
+ if err == nil {
+ metas["format"] = unit.ExternalTrackerConfig().ExternalTrackerFormat
+ switch unit.ExternalTrackerConfig().ExternalTrackerStyle {
+ case markup.IssueNameStyleAlphanumeric:
+ metas["style"] = markup.IssueNameStyleAlphanumeric
+ default:
+ metas["style"] = markup.IssueNameStyleNumeric
+ }
+ }
+
+ repo.MustOwner()
+ if repo.Owner.IsOrganization() {
+ teams := make([]string, 0, 5)
+ _ = db.GetEngine(db.DefaultContext).Table("team_repo").
+ Join("INNER", "team", "team.id = team_repo.team_id").
+ Where("team_repo.repo_id = ?", repo.ID).
+ Select("team.lower_name").
+ OrderBy("team.lower_name").
+ Find(&teams)
+ metas["teams"] = "," + strings.Join(teams, ",") + ","
+ metas["org"] = strings.ToLower(repo.OwnerName)
+ }
+
+ repo.RenderingMetas = metas
+ }
+ return repo.RenderingMetas
+}
+
+// ComposeDocumentMetas composes a map of metas for properly rendering documents
+func (repo *Repository) ComposeDocumentMetas() map[string]string {
+ if len(repo.DocumentRenderingMetas) == 0 {
+ metas := map[string]string{}
+ for k, v := range repo.ComposeMetas() {
+ metas[k] = v
+ }
+ metas["mode"] = "document"
+ repo.DocumentRenderingMetas = metas
+ }
+ return repo.DocumentRenderingMetas
+}
+
+// GetBaseRepo populates repo.BaseRepo for a fork repository and
+// returns an error on failure (NOTE: no error is returned for
+// non-fork repositories, and BaseRepo will be left untouched)
+func (repo *Repository) GetBaseRepo() (err error) {
+ return repo.getBaseRepo(db.GetEngine(db.DefaultContext))
+}
+
+func (repo *Repository) getBaseRepo(e db.Engine) (err error) {
+ if !repo.IsFork {
+ return nil
+ }
+
+ repo.BaseRepo, err = getRepositoryByID(e, repo.ForkID)
+ return err
+}
+
+// IsGenerated returns whether _this_ repository was generated from a template
+func (repo *Repository) IsGenerated() bool {
+ return repo.TemplateID != 0
+}
+
+// RepoPath returns repository path by given user and repository name.
+func RepoPath(userName, repoName string) string { //revive:disable-line:exported
+ return filepath.Join(user_model.UserPath(userName), strings.ToLower(repoName)+".git")
+}
+
+// RepoPath returns the repository path
+func (repo *Repository) RepoPath() string {
+ return RepoPath(repo.OwnerName, repo.Name)
+}
+
+// GitConfigPath returns the path to a repository's git config/ directory
+func GitConfigPath(repoPath string) string {
+ return filepath.Join(repoPath, "config")
+}
+
+// GitConfigPath returns the repository git config path
+func (repo *Repository) GitConfigPath() string {
+ return GitConfigPath(repo.RepoPath())
+}
+
+// Link returns the repository link
+func (repo *Repository) Link() string {
+ return setting.AppSubURL + "/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
+}
+
+// ComposeCompareURL returns the repository comparison URL
+func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
+ return fmt.Sprintf("%s/%s/compare/%s...%s", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), util.PathEscapeSegments(oldCommitID), util.PathEscapeSegments(newCommitID))
+}
+
+// IsOwnedBy returns true when user owns this repository
+func (repo *Repository) IsOwnedBy(userID int64) bool {
+ return repo.OwnerID == userID
+}
+
+// CanCreateBranch returns true if repository meets the requirements for creating new branches.
+func (repo *Repository) CanCreateBranch() bool {
+ return !repo.IsMirror
+}
+
+// CanEnablePulls returns true if repository meets the requirements of accepting pulls.
+func (repo *Repository) CanEnablePulls() bool {
+ return !repo.IsMirror && !repo.IsEmpty
+}
+
+// AllowsPulls returns true if repository meets the requirements of accepting pulls and has them enabled.
+func (repo *Repository) AllowsPulls() bool {
+ return repo.CanEnablePulls() && repo.UnitEnabled(unit.TypePullRequests)
+}
+
+// CanEnableEditor returns true if repository meets the requirements of web editor.
+func (repo *Repository) CanEnableEditor() bool {
+ return !repo.IsMirror
+}
+
+// DescriptionHTML does special handles to description and return HTML string.
+func (repo *Repository) DescriptionHTML() template.HTML {
+ desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
+ URLPrefix: repo.HTMLURL(),
+ Metas: repo.ComposeMetas(),
+ }, repo.Description)
+ if err != nil {
+ log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err)
+ return template.HTML(markup.Sanitize(repo.Description))
+ }
+ return template.HTML(markup.Sanitize(string(desc)))
+}
+
+// CloneLink represents different types of clone URLs of repository.
+type CloneLink struct {
+ SSH string
+ HTTPS string
+ Git string
+}
+
+// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
+func ComposeHTTPSCloneURL(owner, repo string) string {
+ return fmt.Sprintf("%s%s/%s.git", setting.AppURL, url.PathEscape(owner), url.PathEscape(repo))
+}
+
+func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
+ repoName := repo.Name
+ if isWiki {
+ repoName += ".wiki"
+ }
+
+ sshUser := setting.RunUser
+ if setting.SSH.StartBuiltinServer {
+ sshUser = setting.SSH.BuiltinServerUser
+ }
+
+ cl := new(CloneLink)
+
+ // if we have a ipv6 literal we need to put brackets around it
+ // for the git cloning to work.
+ sshDomain := setting.SSH.Domain
+ ip := net.ParseIP(setting.SSH.Domain)
+ if ip != nil && ip.To4() == nil {
+ sshDomain = "[" + setting.SSH.Domain + "]"
+ }
+
+ if setting.SSH.Port != 22 {
+ cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)), url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
+ } else if setting.Repository.UseCompatSSHURI {
+ cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
+ } else {
+ cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
+ }
+ cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
+ return cl
+}
+
+// CloneLink returns clone URLs of repository.
+func (repo *Repository) CloneLink() (cl *CloneLink) {
+ return repo.cloneLink(false)
+}
+
+// GetOriginalURLHostname returns the hostname of a URL or the URL
+func (repo *Repository) GetOriginalURLHostname() string {
+ u, err := url.Parse(repo.OriginalURL)
+ if err != nil {
+ return repo.OriginalURL
+ }
+
+ return u.Host
+}
+
+// GetTrustModel will get the TrustModel for the repo or the default trust model
+func (repo *Repository) GetTrustModel() TrustModelType {
+ trustModel := repo.TrustModel
+ if trustModel == DefaultTrustModel {
+ trustModel = ToTrustModel(setting.Repository.Signing.DefaultTrustModel)
+ if trustModel == DefaultTrustModel {
+ return CollaboratorTrustModel
+ }
+ }
+ return trustModel
+}
+
+// GetRepositoryByOwnerAndName returns the repository by given ownername and reponame.
+func GetRepositoryByOwnerAndName(ownerName, repoName string) (*Repository, error) {
+ return GetRepositoryByOwnerAndNameCtx(db.DefaultContext, ownerName, repoName)
+}
+
+// __________ .__ __
+// \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
+// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
+// | | \ ___/| |_> > <_> )___ \| || | ( <_> ) | \/\___ |
+// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____|
+// \/ \/|__| \/ \/
+
+// ErrRepoNotExist represents a "RepoNotExist" kind of error.
+type ErrRepoNotExist struct {
+ ID int64
+ UID int64
+ OwnerName string
+ Name string
+}
+
+// IsErrRepoNotExist checks if an error is a ErrRepoNotExist.
+func IsErrRepoNotExist(err error) bool {
+ _, ok := err.(ErrRepoNotExist)
+ return ok
+}
+
+func (err ErrRepoNotExist) Error() string {
+ return fmt.Sprintf("repository does not exist [id: %d, uid: %d, owner_name: %s, name: %s]",
+ err.ID, err.UID, err.OwnerName, err.Name)
+}
+
+// GetRepositoryByOwnerAndNameCtx returns the repository by given owner name and repo name
+func GetRepositoryByOwnerAndNameCtx(ctx context.Context, ownerName, repoName string) (*Repository, error) {
+ var repo Repository
+ has, err := db.GetEngine(ctx).Table("repository").Select("repository.*").
+ Join("INNER", "`user`", "`user`.id = repository.owner_id").
+ Where("repository.lower_name = ?", strings.ToLower(repoName)).
+ And("`user`.lower_name = ?", strings.ToLower(ownerName)).
+ Get(&repo)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, ErrRepoNotExist{0, 0, ownerName, repoName}
+ }
+ return &repo, nil
+}
+
+// GetRepositoryByName returns the repository by given name under user if exists.
+func GetRepositoryByName(ownerID int64, name string) (*Repository, error) {
+ repo := &Repository{
+ OwnerID: ownerID,
+ LowerName: strings.ToLower(name),
+ }
+ has, err := db.GetEngine(db.DefaultContext).Get(repo)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, ErrRepoNotExist{0, ownerID, "", name}
+ }
+ return repo, err
+}
+
+func getRepositoryByID(e db.Engine, id int64) (*Repository, error) {
+ repo := new(Repository)
+ has, err := e.ID(id).Get(repo)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, ErrRepoNotExist{id, 0, "", ""}
+ }
+ return repo, nil
+}
+
+// GetRepositoryByID returns the repository by given id if exists.
+func GetRepositoryByID(id int64) (*Repository, error) {
+ return getRepositoryByID(db.GetEngine(db.DefaultContext), id)
+}
+
+// GetRepositoryByIDCtx returns the repository by given id if exists.
+func GetRepositoryByIDCtx(ctx context.Context, id int64) (*Repository, error) {
+ return getRepositoryByID(db.GetEngine(ctx), id)
+}
+
+// GetRepositoriesMapByIDs returns the repositories by given id slice.
+func GetRepositoriesMapByIDs(ids []int64) (map[int64]*Repository, error) {
+ repos := make(map[int64]*Repository, len(ids))
+ return repos, db.GetEngine(db.DefaultContext).In("id", ids).Find(&repos)
+}
+
+// IsRepositoryExistCtx returns true if the repository with given name under user has already existed.
+func IsRepositoryExistCtx(ctx context.Context, u *user_model.User, repoName string) (bool, error) {
+ has, err := db.GetEngine(ctx).Get(&Repository{
+ OwnerID: u.ID,
+ LowerName: strings.ToLower(repoName),
+ })
+ if err != nil {
+ return false, err
+ }
+ isDir, err := util.IsDir(RepoPath(u.Name, repoName))
+ return has && isDir, err
+}
+
+// IsRepositoryExist returns true if the repository with given name under user has already existed.
+func IsRepositoryExist(u *user_model.User, repoName string) (bool, error) {
+ return IsRepositoryExistCtx(db.DefaultContext, u, repoName)
+}
+
+// GetTemplateRepo populates repo.TemplateRepo for a generated repository and
+// returns an error on failure (NOTE: no error is returned for
+// non-generated repositories, and TemplateRepo will be left untouched)
+func GetTemplateRepo(repo *Repository) (*Repository, error) {
+ return getTemplateRepo(db.GetEngine(db.DefaultContext), repo)
+}
+
+func getTemplateRepo(e db.Engine, repo *Repository) (*Repository, error) {
+ if !repo.IsGenerated() {
+ return nil, nil
+ }
+
+ return getRepositoryByID(e, repo.TemplateID)
+}
+
+func countRepositories(userID int64, private bool) int64 {
+ sess := db.GetEngine(db.DefaultContext).Where("id > 0")
+
+ if userID > 0 {
+ sess.And("owner_id = ?", userID)
+ }
+ if !private {
+ sess.And("is_private=?", false)
+ }
+
+ count, err := sess.Count(new(Repository))
+ if err != nil {
+ log.Error("countRepositories: %v", err)
+ }
+ return count
+}
+
+// CountRepositories returns number of repositories.
+// Argument private only takes effect when it is false,
+// set it true to count all repositories.
+func CountRepositories(private bool) int64 {
+ return countRepositories(-1, private)
+}
+
+// CountUserRepositories returns number of repositories user owns.
+// Argument private only takes effect when it is false,
+// set it true to count all repositories.
+func CountUserRepositories(userID int64, private bool) int64 {
+ return countRepositories(userID, private)
+}
+
+// GetUserMirrorRepositories returns a list of mirror repositories of given user.
+func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
+ repos := make([]*Repository, 0, 10)
+ return repos, db.GetEngine(db.DefaultContext).
+ Where("owner_id = ?", userID).
+ And("is_mirror = ?", true).
+ Find(&repos)
+}
+
+func getRepositoryCount(e db.Engine, ownerID int64) (int64, error) {
+ return e.Count(&Repository{OwnerID: ownerID})
+}
+
+func getPublicRepositoryCount(e db.Engine, u *user_model.User) (int64, error) {
+ return e.Where("is_private = ?", false).Count(&Repository{OwnerID: u.ID})
+}
+
+func getPrivateRepositoryCount(e db.Engine, u *user_model.User) (int64, error) {
+ return e.Where("is_private = ?", true).Count(&Repository{OwnerID: u.ID})
+}
+
+// GetRepositoryCount returns the total number of repositories of user.
+func GetRepositoryCount(ctx context.Context, ownerID int64) (int64, error) {
+ return getRepositoryCount(db.GetEngine(ctx), ownerID)
+}
+
+// GetPublicRepositoryCount returns the total number of public repositories of user.
+func GetPublicRepositoryCount(u *user_model.User) (int64, error) {
+ return getPublicRepositoryCount(db.GetEngine(db.DefaultContext), u)
+}
+
+// GetPrivateRepositoryCount returns the total number of private repositories of user.
+func GetPrivateRepositoryCount(u *user_model.User) (int64, error) {
+ return getPrivateRepositoryCount(db.GetEngine(db.DefaultContext), u)
+}