diff --git a/models/release.go b/models/release.go
index 428b79e45f..28ba6c1d68 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 371ea3b597..c2e5aabf8f 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
 	}
-	releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, page, 10)
+
+	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.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
+		if releaseTags[i] == nil {
+			releaseTags[i] = &models.Release{
+				Title:   rawTag.Name,
+				TagName: rawTag.Name,
+				Sha1:    rawTag.Object.String(),
+				Note:    rawTag.Message,
 			}
 
-			tags[i] = &models.Release{
-				Title:   rawTag,
-				TagName: rawTag,
-				Sha1:    commit.ID.String(),
-			}
-
-			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 18cf1ffd77..f405e412bd 100644
--- a/vendor/code.gitea.io/git/MAINTAINERS
+++ b/vendor/code.gitea.io/git/MAINTAINERS
@@ -1,5 +1,6 @@
 Alexey Makhov <amakhov@avito.ru> (@makhov)
 Andrey Nering <andrey.nering@gmail.com> (@andreynering)
+Bo-Yi Wu <appleboy.tw@gmail.com> (@appleboy)
 Kees de Vries <bouwko@gmail.com> (@Bwko)
 Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
 LefsFlare <nobody@nobody.tld> (@LefsFlarey)
diff --git a/vendor/code.gitea.io/git/repo_tag.go b/vendor/code.gitea.io/git/repo_tag.go
index dfaa41f0cf..33a833d3aa 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 f2a3d31d27..500fd27491 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 1904350712..d34874edd6 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=",