* Implementation of the feature to view repository size in admin panel * Move GetRepoSize to git module * Repository.RepoSize -> Repository.Size * RepoSize -> Size in template * Redo a few bits and pieces * Update size when syncing mirror or forking * Remove GetRepoSize * Changed fatal errors to error message * Copy migration code from Gogs * make fmttags/v1.2.0-rc1
| @@ -104,6 +104,8 @@ var migrations = []Migration{ | |||||
| NewMigration("generate and migrate repo and wiki Git hooks", generateAndMigrateGitHookChains), | NewMigration("generate and migrate repo and wiki Git hooks", generateAndMigrateGitHookChains), | ||||
| // v27 -> v28 | // v27 -> v28 | ||||
| NewMigration("change mirror interval from hours to time.Duration", convertIntervalToDuration), | NewMigration("change mirror interval from hours to time.Duration", convertIntervalToDuration), | ||||
| // v28 -> v29 | |||||
| NewMigration("add field for repo size", addRepoSize), | |||||
| } | } | ||||
| // Migrate database to current version | // Migrate database to current version | ||||
| @@ -0,0 +1,77 @@ | |||||
| // Copyright 2017 The Gogs Authors. All rights reserved. | |||||
| // Copyright 2017 Gitea. 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 ( | |||||
| "fmt" | |||||
| "path/filepath" | |||||
| "strings" | |||||
| "code.gitea.io/git" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "github.com/go-xorm/xorm" | |||||
| ) | |||||
| func addRepoSize(x *xorm.Engine) (err error) { | |||||
| log.Info("This migration could take up to minutes, please be patient.") | |||||
| type Repository struct { | |||||
| ID int64 | |||||
| OwnerID int64 | |||||
| Name string | |||||
| Size int64 | |||||
| } | |||||
| type User struct { | |||||
| ID int64 | |||||
| Name string | |||||
| } | |||||
| if err = x.Sync2(new(Repository)); err != nil { | |||||
| return fmt.Errorf("Sync2: %v", err) | |||||
| } | |||||
| // For the sake of SQLite3, we can't use x.Iterate here. | |||||
| offset := 0 | |||||
| for { | |||||
| repos := make([]*Repository, 0, 10) | |||||
| if err = x.Sql(fmt.Sprintf("SELECT * FROM `repository` ORDER BY id ASC LIMIT 10 OFFSET %d", offset)). | |||||
| Find(&repos); err != nil { | |||||
| return fmt.Errorf("select repos [offset: %d]: %v", offset, err) | |||||
| } | |||||
| log.Trace("Select [offset: %d, repos: %d]", offset, len(repos)) | |||||
| if len(repos) == 0 { | |||||
| break | |||||
| } | |||||
| offset += 10 | |||||
| for _, repo := range repos { | |||||
| if repo.Name == "." || repo.Name == ".." { | |||||
| continue | |||||
| } | |||||
| user := new(User) | |||||
| has, err := x.Where("id = ?", repo.OwnerID).Get(user) | |||||
| if err != nil { | |||||
| return fmt.Errorf("query owner of repository [repo_id: %d, owner_id: %d]: %v", repo.ID, repo.OwnerID, err) | |||||
| } else if !has { | |||||
| continue | |||||
| } | |||||
| repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git" | |||||
| countObject, err := git.GetRepoSize(repoPath) | |||||
| if err != nil { | |||||
| log.Warn("GetRepoSize: %v", err) | |||||
| continue | |||||
| } | |||||
| repo.Size = countObject.Size + countObject.SizePack | |||||
| if _, err = x.Id(repo.ID).Cols("size").Update(repo); err != nil { | |||||
| return fmt.Errorf("update size: %v", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -207,6 +207,7 @@ type Repository struct { | |||||
| IsFork bool `xorm:"INDEX NOT NULL DEFAULT false"` | IsFork bool `xorm:"INDEX NOT NULL DEFAULT false"` | ||||
| ForkID int64 `xorm:"INDEX"` | ForkID int64 `xorm:"INDEX"` | ||||
| BaseRepo *Repository `xorm:"-"` | BaseRepo *Repository `xorm:"-"` | ||||
| Size int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| Created time.Time `xorm:"-"` | Created time.Time `xorm:"-"` | ||||
| CreatedUnix int64 `xorm:"INDEX"` | CreatedUnix int64 `xorm:"INDEX"` | ||||
| @@ -546,6 +547,18 @@ func (repo *Repository) IsOwnedBy(userID int64) bool { | |||||
| return repo.OwnerID == userID | return repo.OwnerID == userID | ||||
| } | } | ||||
| // UpdateSize updates the repository size, calculating it using git.GetRepoSize | |||||
| func (repo *Repository) UpdateSize() error { | |||||
| repoInfoSize, err := git.GetRepoSize(repo.RepoPath()) | |||||
| if err != nil { | |||||
| return fmt.Errorf("UpdateSize: %v", err) | |||||
| } | |||||
| repo.Size = repoInfoSize.Size + repoInfoSize.SizePack | |||||
| _, err = x.ID(repo.ID).Cols("size").Update(repo) | |||||
| return err | |||||
| } | |||||
| // CanBeForked returns true if repository meets the requirements of being forked. | // CanBeForked returns true if repository meets the requirements of being forked. | ||||
| func (repo *Repository) CanBeForked() bool { | func (repo *Repository) CanBeForked() bool { | ||||
| return !repo.IsBare | return !repo.IsBare | ||||
| @@ -810,6 +823,10 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) { | |||||
| } | } | ||||
| } | } | ||||
| if err = repo.UpdateSize(); err != nil { | |||||
| log.Error(4, "Failed to update size for repository: %v", err) | |||||
| } | |||||
| if opts.IsMirror { | if opts.IsMirror { | ||||
| if _, err = x.InsertOne(&Mirror{ | if _, err = x.InsertOne(&Mirror{ | ||||
| RepoID: repo.ID, | RepoID: repo.ID, | ||||
| @@ -1464,6 +1481,10 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e | |||||
| return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err) | return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err) | ||||
| } | } | ||||
| } | } | ||||
| if err = repo.UpdateSize(); err != nil { | |||||
| log.Error(4, "Failed to update size for repository: %v", err) | |||||
| } | |||||
| } | } | ||||
| return nil | return nil | ||||
| @@ -2171,6 +2192,10 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| if err = repo.UpdateSize(); err != nil { | |||||
| log.Error(4, "Failed to update size for repository: %v", err) | |||||
| } | |||||
| // Copy LFS meta objects in new session | // Copy LFS meta objects in new session | ||||
| sess2 := x.NewSession() | sess2 := x.NewSession() | ||||
| defer sessionRelease(sess2) | defer sessionRelease(sess2) | ||||
| @@ -147,6 +147,11 @@ func (m *Mirror) runSync() bool { | |||||
| } | } | ||||
| return false | return false | ||||
| } | } | ||||
| if err := m.Repo.UpdateSize(); err != nil { | |||||
| log.Error(4, "Failed to update size for mirror repository: %v", err) | |||||
| } | |||||
| if m.Repo.HasWiki() { | if m.Repo.HasWiki() { | ||||
| if _, stderr, err := process.GetManager().ExecDir( | if _, stderr, err := process.GetManager().ExecDir( | ||||
| timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath), | timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath), | ||||
| @@ -101,6 +101,10 @@ func PushUpdate(opts PushUpdateOptions) (err error) { | |||||
| return fmt.Errorf("GetRepositoryByName: %v", err) | return fmt.Errorf("GetRepositoryByName: %v", err) | ||||
| } | } | ||||
| if err = repo.UpdateSize(); err != nil { | |||||
| log.Error(4, "Failed to update size for repository: %v", err) | |||||
| } | |||||
| // Push tags. | // Push tags. | ||||
| if strings.HasPrefix(opts.RefFullName, git.TagPrefix) { | if strings.HasPrefix(opts.RefFullName, git.TagPrefix) { | ||||
| if err := CommitRepoAction(CommitRepoActionOptions{ | if err := CommitRepoAction(CommitRepoActionOptions{ | ||||
| @@ -82,6 +82,9 @@ func NewFuncMap() []template.FuncMap { | |||||
| "DateFmtShort": func(t time.Time) string { | "DateFmtShort": func(t time.Time) string { | ||||
| return t.Format("Jan 02, 2006") | return t.Format("Jan 02, 2006") | ||||
| }, | }, | ||||
| "SizeFmt": func(s int64) string { | |||||
| return base.FileSize(s) | |||||
| }, | |||||
| "List": List, | "List": List, | ||||
| "SubStr": func(str string, start, length int) string { | "SubStr": func(str string, start, length int) string { | ||||
| if len(str) == 0 { | if len(str) == 0 { | ||||
| @@ -1119,6 +1119,7 @@ repos.private = Private | |||||
| repos.watches = Watches | repos.watches = Watches | ||||
| repos.stars = Stars | repos.stars = Stars | ||||
| repos.issues = Issues | repos.issues = Issues | ||||
| repos.size = Size | |||||
| auths.auth_manage_panel = Authentication Manage Panel | auths.auth_manage_panel = Authentication Manage Panel | ||||
| auths.new = Add New Source | auths.new = Add New Source | ||||
| @@ -20,6 +20,7 @@ | |||||
| <th>{{.i18n.Tr "admin.repos.watches"}}</th> | <th>{{.i18n.Tr "admin.repos.watches"}}</th> | ||||
| <th>{{.i18n.Tr "admin.repos.stars"}}</th> | <th>{{.i18n.Tr "admin.repos.stars"}}</th> | ||||
| <th>{{.i18n.Tr "admin.repos.issues"}}</th> | <th>{{.i18n.Tr "admin.repos.issues"}}</th> | ||||
| <th>{{.i18n.Tr "admin.repos.size"}}</th> | |||||
| <th>{{.i18n.Tr "admin.users.created"}}</th> | <th>{{.i18n.Tr "admin.users.created"}}</th> | ||||
| <th>{{.i18n.Tr "admin.notices.op"}}</th> | <th>{{.i18n.Tr "admin.notices.op"}}</th> | ||||
| </tr> | </tr> | ||||
| @@ -34,6 +35,7 @@ | |||||
| <td>{{.NumWatches}}</td> | <td>{{.NumWatches}}</td> | ||||
| <td>{{.NumStars}}</td> | <td>{{.NumStars}}</td> | ||||
| <td>{{.NumIssues}}</td> | <td>{{.NumIssues}}</td> | ||||
| <td>{{SizeFmt .Size}}</td> | |||||
| <td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td> | <td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td> | ||||
| <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Current}}" data-id="{{.ID}}"><i class="trash icon text red"></i></a></td> | <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Current}}" data-id="{{.ID}}"><i class="trash icon text red"></i></a></td> | ||||
| </tr> | </tr> | ||||