From 61306fa737f693c3b325d9a8da047ba0b939537e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 6 Jan 2017 09:51:15 +0800 Subject: [PATCH] Make releases faster than before and resolved #490 (#588) * make releases faster than before and resolved #490 * fix comment --- models/release.go | 17 +++-- routers/repo/release.go | 102 ++++++++++++--------------- vendor/code.gitea.io/git/MAINTAINERS | 1 + vendor/code.gitea.io/git/repo_tag.go | 82 +++++++++++++++++++++ vendor/code.gitea.io/git/tag.go | 25 ++++++- vendor/vendor.json | 6 +- 6 files changed, 169 insertions(+), 64 deletions(-) diff --git a/models/release.go b/models/release.go index 428b79e45..28ba6c1d6 100644 --- a/models/release.go +++ b/models/release.go @@ -22,12 +22,12 @@ import ( // Release represents a release of repository. type Release struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"index unique(n)"` Repo *Repository `xorm:"-"` PublisherID int64 - Publisher *User `xorm:"-"` - TagName string + Publisher *User `xorm:"-"` + TagName string `xorm:"index unique(n)"` LowerTagName string Target string Title string @@ -213,6 +213,15 @@ func GetReleasesByRepoID(repoID int64, page, pageSize int) (rels []*Release, err return rels, err } +// GetReleasesByRepoIDAndNames returns a list of releases of repository accroding repoID and tagNames. +func GetReleasesByRepoIDAndNames(repoID int64, tagNames []string) (rels []*Release, err error) { + err = x. + Desc("created_unix"). + In("tag_name", tagNames). + Find(&rels, Release{RepoID: repoID}) + return rels, err +} + type releaseSorter struct { rels []*Release } diff --git a/routers/repo/release.go b/routers/repo/release.go index 371ea3b59..c2e5aabf8 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -5,8 +5,10 @@ package repo import ( + "errors" "fmt" + "code.gitea.io/git" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" @@ -54,34 +56,55 @@ func Releases(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.releases") ctx.Data["PageIsReleaseList"] = true - rawTags, err := ctx.Repo.GitRepo.GetTags() + page := ctx.QueryInt("page") + if page <= 1 { + page = 1 + } + limit := ctx.QueryInt("limit") + if limit <= 0 { + limit = 10 + } + + rawTags, err := ctx.Repo.GitRepo.GetTagInfos(git.TagOption{}) if err != nil { ctx.Handle(500, "GetTags", err) return } - page := ctx.QueryInt("page") - if page <= 1 { - page = 1 + if len(rawTags) <= (page-1)*limit { + ctx.Handle(500, "Releases", errors.New("no more pages")) + return + } + + var tags []*git.Tag + if page*limit > len(rawTags) { + tags = rawTags[(page-1)*limit:] + } else { + tags = rawTags[(page-1)*limit : page*limit] + } + + var tagNames []string + for _, t := range tags { + tagNames = append(tagNames, t.Name) } - releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, page, 10) + + releases, err := models.GetReleasesByRepoIDAndNames(ctx.Repo.Repository.ID, tagNames) if err != nil { - ctx.Handle(500, "GetReleasesByRepoID", err) + ctx.Handle(500, "GetReleasesByRepoIDAndNames", err) return } // Temproray cache commits count of used branches to speed up. countCache := make(map[string]int64) - var cacheUsers = make(map[int64]*models.User) var ok bool - tags := make([]*models.Release, len(rawTags)) - for i, rawTag := range rawTags { - for j, r := range releases { - if r == nil || (r.IsDraft && !ctx.Repo.IsOwner()) { + releaseTags := make([]*models.Release, len(tags)) + for i, rawTag := range tags { + for _, r := range releases { + if r.IsDraft && !ctx.Repo.IsOwner() { continue } - if r.TagName == rawTag { + if r.TagName == rawTag.Name { if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok { r.Publisher, err = models.GetUserByID(r.PublisherID) if err != nil { @@ -101,64 +124,31 @@ func Releases(ctx *context.Context) { } r.Note = markdown.RenderString(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) - tags[i] = r - releases[j] = nil // Mark as used. + releaseTags[i] = r break } } - if tags[i] == nil { - commit, err := ctx.Repo.GitRepo.GetTagCommit(rawTag) - if err != nil { - ctx.Handle(500, "GetTagCommit", err) - return - } - - tags[i] = &models.Release{ - Title: rawTag, - TagName: rawTag, - Sha1: commit.ID.String(), + if releaseTags[i] == nil { + releaseTags[i] = &models.Release{ + Title: rawTag.Name, + TagName: rawTag.Name, + Sha1: rawTag.Object.String(), + Note: rawTag.Message, } - tags[i].NumCommits, err = commit.CommitsCount() + releaseTags[i].NumCommits, err = git.CommitsCount(ctx.Repo.GitRepo.Path, rawTag.Object.String()) if err != nil { ctx.Handle(500, "CommitsCount", err) return } - tags[i].NumCommitsBehind = ctx.Repo.CommitsCount - tags[i].NumCommits + releaseTags[i].NumCommitsBehind = ctx.Repo.CommitsCount - releaseTags[i].NumCommits } } - for _, r := range releases { - if r == nil { - continue - } - - if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok { - r.Publisher, err = models.GetUserByID(r.PublisherID) - if err != nil { - if models.IsErrUserNotExist(err) { - r.Publisher = models.NewGhostUser() - } else { - ctx.Handle(500, "GetUserByID", err) - return - } - } - cacheUsers[r.PublisherID] = r.Publisher - } - - if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil { - ctx.Handle(500, "calReleaseNumCommitsBehind", err) - return - } - - r.Note = markdown.RenderString(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) - tags = append(tags, r) - } - pager := paginater.New(ctx.Repo.Repository.NumTags, 10, page, 5) + pager := paginater.New(ctx.Repo.Repository.NumTags, limit, page, 5) ctx.Data["Page"] = pager - models.SortReleases(tags) - ctx.Data["Releases"] = tags + ctx.Data["Releases"] = releaseTags ctx.HTML(200, tplReleases) } diff --git a/vendor/code.gitea.io/git/MAINTAINERS b/vendor/code.gitea.io/git/MAINTAINERS index 18cf1ffd7..f405e412b 100644 --- a/vendor/code.gitea.io/git/MAINTAINERS +++ b/vendor/code.gitea.io/git/MAINTAINERS @@ -1,5 +1,6 @@ Alexey Makhov (@makhov) Andrey Nering (@andreynering) +Bo-Yi Wu (@appleboy) Kees de Vries (@Bwko) Kim Carlbäcker (@bkcsoft) LefsFlare (@LefsFlarey) diff --git a/vendor/code.gitea.io/git/repo_tag.go b/vendor/code.gitea.io/git/repo_tag.go index dfaa41f0c..33a833d3a 100644 --- a/vendor/code.gitea.io/git/repo_tag.go +++ b/vendor/code.gitea.io/git/repo_tag.go @@ -6,6 +6,7 @@ package git import ( "strings" + "time" "github.com/mcuadros/go-version" ) @@ -94,6 +95,87 @@ func (repo *Repository) GetTag(name string) (*Tag, error) { return tag, nil } +// TagOption describes tag options +type TagOption struct { +} + +// parseTag parse the line +// 2016-10-14 20:54:25 +0200 (tag: translation/20161014.01) d3b76dcf2 Dirk Baeumer dirkb@microsoft.com Merge in translations +func parseTag(line string, opt TagOption) (*Tag, error) { + line = strings.TrimSpace(line) + if len(line) < 40 { + return nil, nil + } + + var ( + err error + tag Tag + sig Signature + ) + sig.When, err = time.Parse("2006-01-02 15:04:05 -0700", line[0:25]) + if err != nil { + return nil, err + } + + left := strings.TrimSpace(line[25:]) + start := strings.Index(left, "(tag: ") + if start < 0 { + return nil, nil + } + end := strings.IndexByte(left[start+1:], ')') + if end < 0 { + return nil, nil + } + end = end + start + 1 + part := strings.IndexByte(left[start+6:end], ',') + if part > 0 { + tag.Name = strings.TrimSpace(left[start+6 : start+6+part]) + } else { + tag.Name = strings.TrimSpace(left[start+6 : end]) + } + next := strings.IndexByte(left[end+2:], ' ') + if next < 0 { + return nil, nil + } + tag.Object = MustIDFromString(strings.TrimSpace(left[end+2 : end+2+next])) + next = end + 2 + next + + emailStart := strings.IndexByte(left[next:], '<') + sig.Name = strings.TrimSpace(left[next:][:emailStart-1]) + emailEnd := strings.IndexByte(left[next:], '>') + sig.Email = strings.TrimSpace(left[next:][emailStart+1 : emailEnd]) + tag.Tagger = &sig + tag.Message = strings.TrimSpace(left[next+emailEnd+1:]) + return &tag, nil +} + +// GetTagInfos returns all tag infos of the repository. +func (repo *Repository) GetTagInfos(opt TagOption) ([]*Tag, error) { + cmd := NewCommand("log", "--tags", "--simplify-by-decoration", `--pretty=format:"%ci %d %H %cn<%ce> %s"`) + stdout, err := cmd.RunInDir(repo.Path) + if err != nil { + return nil, err + } + + tagSlices := strings.Split(stdout, "\n") + var tags []*Tag + for _, line := range tagSlices { + line := strings.Trim(line, `"`) + tag, err := parseTag(line, opt) + if err != nil { + return nil, err + } + if tag != nil { + tag.repo = repo + tags = append(tags, tag) + } + } + + sortTagsByTime(tags) + + return tags, nil +} + // GetTags returns all tags of the repository. func (repo *Repository) GetTags() ([]string, error) { cmd := NewCommand("tag", "-l") diff --git a/vendor/code.gitea.io/git/tag.go b/vendor/code.gitea.io/git/tag.go index f2a3d31d2..500fd2749 100644 --- a/vendor/code.gitea.io/git/tag.go +++ b/vendor/code.gitea.io/git/tag.go @@ -4,7 +4,10 @@ package git -import "bytes" +import ( + "bytes" + "sort" +) // Tag represents a Git tag. type Tag struct { @@ -64,3 +67,23 @@ l: } return tag, nil } + +type tagSorter []*Tag + +func (ts tagSorter) Len() int { + return len([]*Tag(ts)) +} + +func (ts tagSorter) Less(i, j int) bool { + return []*Tag(ts)[i].Tagger.When.After([]*Tag(ts)[j].Tagger.When) +} + +func (ts tagSorter) Swap(i, j int) { + []*Tag(ts)[i], []*Tag(ts)[j] = []*Tag(ts)[j], []*Tag(ts)[i] +} + +// sortTagsByTime +func sortTagsByTime(tags []*Tag) { + sorter := tagSorter(tags) + sort.Sort(sorter) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 190435071..d34874edd 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -3,10 +3,10 @@ "ignore": "test", "package": [ { - "checksumSHA1": "mIaKLz6373W+jDLjgE/Yzt/exeo=", + "checksumSHA1": "zK/6EifSPy/O5Vbx7CMWfnLHExI=", "path": "code.gitea.io/git", - "revision": "3d0fa331865619d2f3a7a0fcf23670a389310954", - "revisionTime": "2016-12-28T14:57:51Z" + "revision": "a3ee12b97af51eec1b7aa0525f6a39c97520817d", + "revisionTime": "2017-01-05T02:48:44Z" }, { "checksumSHA1": "BKj0haFTDebzdC2nACpoGzp3s8A=",