1
1
mirror of https://github.com/go-gitea/gitea synced 2024-06-02 01:15:48 +00:00
gitea/models/update.go
zeripath fb5af37b3e
Add Close() method to gogitRepository (#8901) (#8958)
Backport #8901 - Adjusted slightly for 1.9

In investigating #7947 it has become clear that the storage component of go-git repositories needs closing.

This PR adds this Close function and adds the Close functions as necessary.

In TransferOwnership the ctx.Repo.GitRepo is closed if it is open to help prevent the risk of multiple open files.

Fixes #7947
2019-11-13 18:51:33 +00:00

278 lines
7.0 KiB
Go

// Copyright 2014 The Gogs 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 models
import (
"container/list"
"fmt"
"strings"
"time"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
// env keys for git hooks need
const (
EnvRepoName = "GITEA_REPO_NAME"
EnvRepoUsername = "GITEA_REPO_USER_NAME"
EnvRepoIsWiki = "GITEA_REPO_IS_WIKI"
EnvPusherName = "GITEA_PUSHER_NAME"
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
EnvPusherID = "GITEA_PUSHER_ID"
)
// CommitToPushCommit transforms a git.Commit to PushCommit type.
func CommitToPushCommit(commit *git.Commit) *PushCommit {
return &PushCommit{
Sha1: commit.ID.String(),
Message: commit.Message(),
AuthorEmail: commit.Author.Email,
AuthorName: commit.Author.Name,
CommitterEmail: commit.Committer.Email,
CommitterName: commit.Committer.Name,
Timestamp: commit.Author.When,
}
}
// ListToPushCommits transforms a list.List to PushCommits type.
func ListToPushCommits(l *list.List) *PushCommits {
var commits []*PushCommit
var actEmail string
for e := l.Front(); e != nil; e = e.Next() {
commit := e.Value.(*git.Commit)
if actEmail == "" {
actEmail = commit.Committer.Email
}
commits = append(commits, CommitToPushCommit(commit))
}
return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*User)}
}
// PushUpdateOptions defines the push update options
type PushUpdateOptions struct {
PusherID int64
PusherName string
RepoUserName string
RepoName string
RefFullName string
OldCommitID string
NewCommitID string
}
// PushUpdate must be called for any push actions in order to
// generates necessary push action history feeds.
func PushUpdate(branch string, opt PushUpdateOptions) error {
repo, err := pushUpdate(opt)
if err != nil {
return err
}
pusher, err := GetUserByID(opt.PusherID)
if err != nil {
return err
}
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
go AddTestPullRequestTask(pusher, repo.ID, branch, true)
return nil
}
func pushUpdateDeleteTag(repo *Repository, tagName string) error {
rel, err := GetRelease(repo.ID, tagName)
if err != nil {
if IsErrReleaseNotExist(err) {
return nil
}
return fmt.Errorf("GetRelease: %v", err)
}
if rel.IsTag {
if _, err = x.ID(rel.ID).Delete(new(Release)); err != nil {
return fmt.Errorf("Delete: %v", err)
}
} else {
rel.IsDraft = true
rel.NumCommits = 0
rel.Sha1 = ""
if _, err = x.ID(rel.ID).AllCols().Update(rel); err != nil {
return fmt.Errorf("Update: %v", err)
}
}
return nil
}
func pushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string) error {
rel, err := GetRelease(repo.ID, tagName)
if err != nil && !IsErrReleaseNotExist(err) {
return fmt.Errorf("GetRelease: %v", err)
}
tag, err := gitRepo.GetTag(tagName)
if err != nil {
return fmt.Errorf("GetTag: %v", err)
}
commit, err := tag.Commit()
if err != nil {
return fmt.Errorf("Commit: %v", err)
}
sig := tag.Tagger
if sig == nil {
sig = commit.Author
}
if sig == nil {
sig = commit.Committer
}
var author *User
var createdAt = time.Unix(1, 0)
if sig != nil {
author, err = GetUserByEmail(sig.Email)
if err != nil && !IsErrUserNotExist(err) {
return fmt.Errorf("GetUserByEmail: %v", err)
}
createdAt = sig.When
}
commitsCount, err := commit.CommitsCount()
if err != nil {
return fmt.Errorf("CommitsCount: %v", err)
}
if rel == nil {
rel = &Release{
RepoID: repo.ID,
Title: "",
TagName: tagName,
LowerTagName: strings.ToLower(tagName),
Target: "",
Sha1: commit.ID.String(),
NumCommits: commitsCount,
Note: "",
IsDraft: false,
IsPrerelease: false,
IsTag: true,
CreatedUnix: util.TimeStamp(createdAt.Unix()),
}
if author != nil {
rel.PublisherID = author.ID
}
if _, err = x.InsertOne(rel); err != nil {
return fmt.Errorf("InsertOne: %v", err)
}
} else {
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = util.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
rel.IsDraft = false
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
}
if _, err = x.ID(rel.ID).AllCols().Update(rel); err != nil {
return fmt.Errorf("Update: %v", err)
}
}
return nil
}
func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) {
isNewRef := opts.OldCommitID == git.EmptySHA
isDelRef := opts.NewCommitID == git.EmptySHA
if isNewRef && isDelRef {
return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
}
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
_, err = git.NewCommand("update-server-info").RunInDir(repoPath)
if err != nil {
return nil, fmt.Errorf("Failed to call 'git update-server-info': %v", err)
}
owner, err := GetUserByName(opts.RepoUserName)
if err != nil {
return nil, fmt.Errorf("GetUserByName: %v", err)
}
repo, err = GetRepositoryByName(owner.ID, opts.RepoName)
if err != nil {
return nil, fmt.Errorf("GetRepositoryByName: %v", err)
}
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
return nil, fmt.Errorf("OpenRepository: %v", err)
}
defer gitRepo.Close()
if err = repo.UpdateSize(); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
var commits = &PushCommits{}
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
// If is tag reference
tagName := opts.RefFullName[len(git.TagPrefix):]
if isDelRef {
err = pushUpdateDeleteTag(repo, tagName)
if err != nil {
return nil, fmt.Errorf("pushUpdateDeleteTag: %v", err)
}
} else {
// Clear cache for tag commit count
cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
err = pushUpdateAddTag(repo, gitRepo, tagName)
if err != nil {
return nil, fmt.Errorf("pushUpdateAddTag: %v", err)
}
}
} else if !isDelRef {
// If is branch reference
// Clear cache for branch commit count
cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
if err != nil {
return nil, fmt.Errorf("gitRepo.GetCommit: %v", err)
}
// Push new branch.
var l *list.List
if isNewRef {
l, err = newCommit.CommitsBeforeLimit(10)
if err != nil {
return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
}
} else {
l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
if err != nil {
return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
}
}
commits = ListToPushCommits(l)
}
if err := CommitRepoAction(CommitRepoActionOptions{
PusherName: opts.PusherName,
RepoOwnerID: owner.ID,
RepoName: repo.Name,
RefFullName: opts.RefFullName,
OldCommitID: opts.OldCommitID,
NewCommitID: opts.NewCommitID,
Commits: commits,
}); err != nil {
return nil, fmt.Errorf("CommitRepoAction: %v", err)
}
return repo, nil
}