mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-11 06:47:45 +00:00
Creating a repository with an initial commit, via `auto_init` or a template with git content, pushed that commit through receive-pack and three server-side hooks that do nothing for this internal push except refresh server info. Fetching it into the bare repository instead makes such repository creation about 3.7x faster locally (median 790ms to 214ms). E2E tests that create initialized repositories get about 0.2-0.3s faster each. 1. Run `update-server-info` after template content generation, which the push's `post-receive` hook did before. 2. Remove the now unused `InternalPushingEnvironment` and `PushOptions.LocalRefName`.
195 lines
5.8 KiB
Go
195 lines
5.8 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitea.dev/models/db"
|
|
git_model "gitea.dev/models/git"
|
|
issues_model "gitea.dev/models/issues"
|
|
repo_model "gitea.dev/models/repo"
|
|
user_model "gitea.dev/models/user"
|
|
"gitea.dev/modules/git"
|
|
"gitea.dev/modules/log"
|
|
notify_service "gitea.dev/services/notify"
|
|
)
|
|
|
|
// GenerateIssueLabels generates issue labels from a template repository
|
|
func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
|
|
templateLabels, err := issues_model.GetLabelsByRepoID(ctx, templateRepo.ID, "", db.ListOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Prevent insert being called with an empty slice which would result in
|
|
// err "no element on slice when insert".
|
|
if len(templateLabels) == 0 {
|
|
return nil
|
|
}
|
|
|
|
newLabels := make([]*issues_model.Label, 0, len(templateLabels))
|
|
for _, templateLabel := range templateLabels {
|
|
newLabels = append(newLabels, &issues_model.Label{
|
|
RepoID: generateRepo.ID,
|
|
Name: templateLabel.Name,
|
|
Exclusive: templateLabel.Exclusive,
|
|
ExclusiveOrder: templateLabel.ExclusiveOrder,
|
|
Description: templateLabel.Description,
|
|
Color: templateLabel.Color,
|
|
})
|
|
}
|
|
return db.Insert(ctx, newLabels)
|
|
}
|
|
|
|
func GenerateProtectedBranch(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
|
|
templateBranches, err := git_model.FindRepoProtectedBranchRules(ctx, templateRepo.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Prevent insert being called with an empty slice which would result in
|
|
// err "no element on slice when insert".
|
|
if len(templateBranches) == 0 {
|
|
return nil
|
|
}
|
|
|
|
newBranches := make([]*git_model.ProtectedBranch, 0, len(templateBranches))
|
|
for _, templateBranch := range templateBranches {
|
|
templateBranch.ID = 0
|
|
templateBranch.RepoID = generateRepo.ID
|
|
templateBranch.UpdatedUnix = 0
|
|
templateBranch.CreatedUnix = 0
|
|
newBranches = append(newBranches, templateBranch)
|
|
}
|
|
return db.Insert(ctx, newBranches)
|
|
}
|
|
|
|
// GenerateRepository generates a repository from a template
|
|
func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
|
|
if !doer.CanCreateRepoIn(owner) {
|
|
return nil, repo_model.ErrReachLimitOfRepo{
|
|
Limit: owner.MaxRepoCreation,
|
|
}
|
|
}
|
|
|
|
generateRepo := &repo_model.Repository{
|
|
OwnerID: owner.ID,
|
|
Owner: owner,
|
|
OwnerName: owner.Name,
|
|
Name: opts.Name,
|
|
LowerName: strings.ToLower(opts.Name),
|
|
Description: opts.Description,
|
|
DefaultBranch: opts.DefaultBranch,
|
|
IsPrivate: opts.Private,
|
|
IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
|
|
IsFsckEnabled: templateRepo.IsFsckEnabled,
|
|
TemplateID: templateRepo.ID,
|
|
TrustModel: templateRepo.TrustModel,
|
|
ObjectFormatName: templateRepo.ObjectFormatName,
|
|
Status: repo_model.RepositoryBeingMigrated,
|
|
}
|
|
|
|
// 1 - Create the repository in the database
|
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
|
return createRepositoryInDB(ctx, doer, owner, generateRepo, false)
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// last - clean up the repository if something goes wrong
|
|
defer func() {
|
|
if err != nil {
|
|
// we can not use `ctx` because it may be canceled or timed out
|
|
cleanupRepository(generateRepo)
|
|
}
|
|
}()
|
|
|
|
// 2 - check whether the repository with the same storage exists
|
|
isExist, err := git.IsRepositoryExist(ctx, generateRepo)
|
|
if err != nil {
|
|
log.Error("Unable to check if %s exists. Error: %v", generateRepo.FullName(), err)
|
|
return nil, err
|
|
}
|
|
if isExist {
|
|
// Don't return directly, we need err in defer to cleanupRepository
|
|
err = repo_model.ErrRepoFilesAlreadyExist{
|
|
Uname: generateRepo.OwnerName,
|
|
Name: generateRepo.Name,
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// 3 -Init git bare new repository.
|
|
if err = git.InitRepository(ctx, generateRepo, generateRepo.ObjectFormatName, ""); err != nil {
|
|
return nil, fmt.Errorf("git.InitRepository: %w", err)
|
|
} else if err = git.CreateDelegateHooks(ctx, generateRepo); err != nil {
|
|
return nil, fmt.Errorf("createDelegateHooks: %w", err)
|
|
}
|
|
|
|
// 4 - generate the repository contents according to the template
|
|
// Git Content
|
|
if opts.GitContent && !templateRepo.IsEmpty {
|
|
if err = GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Topics
|
|
if opts.Topics {
|
|
if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Git Hooks
|
|
if opts.GitHooks {
|
|
if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Webhooks
|
|
if opts.Webhooks {
|
|
if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Avatar
|
|
if opts.Avatar && len(templateRepo.Avatar) > 0 {
|
|
if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Issue Labels
|
|
if opts.IssueLabels {
|
|
if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if opts.ProtectedBranch {
|
|
if err = GenerateProtectedBranch(ctx, templateRepo, generateRepo); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// 5 - Update the git repository
|
|
if err = updateGitRepoAfterCreate(ctx, generateRepo); err != nil {
|
|
return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
|
|
}
|
|
|
|
// 6 - update repository status to be ready
|
|
generateRepo.Status = repo_model.RepositoryReady
|
|
if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, generateRepo, "status"); err != nil {
|
|
return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
|
|
}
|
|
|
|
notify_service.CreateRepository(ctx, doer, owner, generateRepo)
|
|
|
|
return generateRepo, nil
|
|
}
|