diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2020-01-12 20:11:17 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-12 20:11:17 +0800 |
commit | b465d0d78793da6e67890a7cb9d3ae1b807c53ca (patch) | |
tree | 1cdfb83699ad2fd31d5835bb2ac1e1b579eaf784 /modules/repository | |
parent | 5765212c6dbcaeb27779707af3ca57775e535bd9 (diff) | |
download | gitea-b465d0d78793da6e67890a7cb9d3ae1b807c53ca.tar.gz gitea-b465d0d78793da6e67890a7cb9d3ae1b807c53ca.zip |
Move create/fork repository from models to modules/repository (#9489)
* Move create/fork repository from models to modules/repository
* fix wrong reference
* fix test
* fix test
* fix lint
* Fix DBContext
* remove duplicated TestMain
* fix lint
* fix conflicts
Diffstat (limited to 'modules/repository')
-rw-r--r-- | modules/repository/create.go | 77 | ||||
-rw-r--r-- | modules/repository/create_test.go | 145 | ||||
-rw-r--r-- | modules/repository/fork.go | 87 | ||||
-rw-r--r-- | modules/repository/fork_test.go | 25 | ||||
-rw-r--r-- | modules/repository/generate.go | 230 | ||||
-rw-r--r-- | modules/repository/init.go | 214 | ||||
-rw-r--r-- | modules/repository/repo.go | 2 |
7 files changed, 779 insertions, 1 deletions
diff --git a/modules/repository/create.go b/modules/repository/create.go new file mode 100644 index 0000000000..dc96b856d9 --- /dev/null +++ b/modules/repository/create.go @@ -0,0 +1,77 @@ +// Copyright 2019 The Gitea 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 repository + +import ( + "fmt" + "os" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +// CreateRepository creates a repository for the user/organization. +func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *models.Repository, err error) { + if !doer.IsAdmin && !u.CanCreateRepo() { + return nil, models.ErrReachLimitOfRepo{ + Limit: u.MaxRepoCreation, + } + } + + repo := &models.Repository{ + OwnerID: u.ID, + Owner: u, + OwnerName: u.Name, + Name: opts.Name, + LowerName: strings.ToLower(opts.Name), + Description: opts.Description, + OriginalURL: opts.OriginalURL, + OriginalServiceType: opts.GitServiceType, + IsPrivate: opts.IsPrivate, + IsFsckEnabled: !opts.IsMirror, + CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, + Status: opts.Status, + IsEmpty: !opts.AutoInit, + } + + err = models.WithTx(func(ctx models.DBContext) error { + if err = models.CreateRepository(ctx, doer, u, repo); err != nil { + return err + } + + // No need for init mirror. + if !opts.IsMirror { + repoPath := models.RepoPath(u.Name, repo.Name) + if err = initRepository(ctx, repoPath, u, repo, opts); err != nil { + if err2 := os.RemoveAll(repoPath); err2 != nil { + log.Error("initRepository: %v", err) + return fmt.Errorf( + "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) + } + return fmt.Errorf("initRepository: %v", err) + } + + // Initialize Issue Labels if selected + if len(opts.IssueLabels) > 0 { + if err = models.InitalizeLabels(ctx, repo.ID, opts.IssueLabels); err != nil { + return fmt.Errorf("initalizeLabels: %v", err) + } + } + + if stdout, err := git.NewCommand("update-server-info"). + SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)). + RunInDir(repoPath); err != nil { + log.Error("CreateRepitory(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) + return fmt.Errorf("CreateRepository(git update-server-info): %v", err) + } + } + return nil + }) + + return repo, err +} diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go new file mode 100644 index 0000000000..53c0b0f305 --- /dev/null +++ b/modules/repository/create_test.go @@ -0,0 +1,145 @@ +// Copyright 2019 The Gitea 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 repository + +import ( + "fmt" + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestIncludesAllRepositoriesTeams(t *testing.T) { + assert.NoError(t, models.PrepareTestDatabase()) + + testTeamRepositories := func(teamID int64, repoIds []int64) { + team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team) + assert.NoError(t, team.GetRepositories(), "%s: GetRepositories", team.Name) + assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) + assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name) + for i, rid := range repoIds { + if rid > 0 { + assert.True(t, team.HasRepository(rid), "%s: HasRepository(%d) %d", rid, i) + } + } + } + + // Get an admin user. + user, err := models.GetUserByID(1) + assert.NoError(t, err, "GetUserByID") + + // Create org. + org := &models.User{ + Name: "All repo", + IsActive: true, + Type: models.UserTypeOrganization, + Visibility: structs.VisibleTypePublic, + } + assert.NoError(t, models.CreateOrganization(org, user), "CreateOrganization") + + // Check Owner team. + ownerTeam, err := org.GetOwnerTeam() + assert.NoError(t, err, "GetOwnerTeam") + assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories") + + // Create repos. + repoIds := make([]int64, 0) + for i := 0; i < 3; i++ { + r, err := CreateRepository(user, org, models.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}) + assert.NoError(t, err, "CreateRepository %d", i) + if r != nil { + repoIds = append(repoIds, r.ID) + } + } + // Get fresh copy of Owner team after creating repos. + ownerTeam, err = org.GetOwnerTeam() + assert.NoError(t, err, "GetOwnerTeam") + + // Create teams and check repositories. + teams := []*models.Team{ + ownerTeam, + { + OrgID: org.ID, + Name: "team one", + Authorize: models.AccessModeRead, + IncludesAllRepositories: true, + }, + { + OrgID: org.ID, + Name: "team 2", + Authorize: models.AccessModeRead, + IncludesAllRepositories: false, + }, + { + OrgID: org.ID, + Name: "team three", + Authorize: models.AccessModeWrite, + IncludesAllRepositories: true, + }, + { + OrgID: org.ID, + Name: "team 4", + Authorize: models.AccessModeWrite, + IncludesAllRepositories: false, + }, + } + teamRepos := [][]int64{ + repoIds, + repoIds, + {}, + repoIds, + {}, + } + for i, team := range teams { + if i > 0 { // first team is Owner. + assert.NoError(t, models.NewTeam(team), "%s: NewTeam", team.Name) + } + testTeamRepositories(team.ID, teamRepos[i]) + } + + // Update teams and check repositories. + teams[3].IncludesAllRepositories = false + teams[4].IncludesAllRepositories = true + teamRepos[4] = repoIds + for i, team := range teams { + assert.NoError(t, models.UpdateTeam(team, false, true), "%s: UpdateTeam", team.Name) + testTeamRepositories(team.ID, teamRepos[i]) + } + + // Create repo and check teams repositories. + org.Teams = nil // Reset teams to allow their reloading. + r, err := CreateRepository(user, org, models.CreateRepoOptions{Name: "repo-last"}) + assert.NoError(t, err, "CreateRepository last") + if r != nil { + repoIds = append(repoIds, r.ID) + } + teamRepos[0] = repoIds + teamRepos[1] = repoIds + teamRepos[4] = repoIds + for i, team := range teams { + testTeamRepositories(team.ID, teamRepos[i]) + } + + // Remove repo and check teams repositories. + assert.NoError(t, models.DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository") + teamRepos[0] = repoIds[1:] + teamRepos[1] = repoIds[1:] + teamRepos[3] = repoIds[1:3] + teamRepos[4] = repoIds[1:] + for i, team := range teams { + testTeamRepositories(team.ID, teamRepos[i]) + } + + // Wipe created items. + for i, rid := range repoIds { + if i > 0 { // first repo already deleted. + assert.NoError(t, models.DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i) + } + } + assert.NoError(t, models.DeleteOrganization(org), "DeleteOrganization") +} diff --git a/modules/repository/fork.go b/modules/repository/fork.go new file mode 100644 index 0000000000..8953ce9ba4 --- /dev/null +++ b/modules/repository/fork.go @@ -0,0 +1,87 @@ +// Copyright 2019 The Gitea 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 repository + +import ( + "fmt" + "strings" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" +) + +// ForkRepository forks a repository +func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, desc string) (_ *models.Repository, err error) { + forkedRepo, err := oldRepo.GetUserFork(owner.ID) + if err != nil { + return nil, err + } + if forkedRepo != nil { + return nil, models.ErrForkAlreadyExist{ + Uname: owner.Name, + RepoName: oldRepo.FullName(), + ForkName: forkedRepo.FullName(), + } + } + + repo := &models.Repository{ + OwnerID: owner.ID, + Owner: owner, + OwnerName: owner.Name, + Name: name, + LowerName: strings.ToLower(name), + Description: desc, + DefaultBranch: oldRepo.DefaultBranch, + IsPrivate: oldRepo.IsPrivate, + IsEmpty: oldRepo.IsEmpty, + IsFork: true, + ForkID: oldRepo.ID, + } + + oldRepoPath := oldRepo.RepoPath() + + err = models.WithTx(func(ctx models.DBContext) error { + if err = models.CreateRepository(ctx, doer, owner, repo); err != nil { + return err + } + + if err = models.IncrementRepoForkNum(ctx, oldRepo.ID); err != nil { + return err + } + + repoPath := models.RepoPath(owner.Name, repo.Name) + if stdout, err := git.NewCommand( + "clone", "--bare", oldRepoPath, repoPath). + SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", oldRepo.FullName(), repo.FullName())). + RunInDirTimeout(10*time.Minute, ""); err != nil { + log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, oldRepo, stdout, err) + return fmt.Errorf("git clone: %v", err) + } + + if stdout, err := git.NewCommand("update-server-info"). + SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())). + RunInDir(repoPath); err != nil { + log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err) + return fmt.Errorf("git update-server-info: %v", err) + } + + if err = models.CreateDelegateHooks(repoPath); err != nil { + return fmt.Errorf("createDelegateHooks: %v", err) + } + return nil + }) + if err != nil { + return nil, err + } + + ctx := models.DefaultDBContext() + if err = repo.UpdateSize(ctx); err != nil { + log.Error("Failed to update size for repository: %v", err) + } + + return repo, models.CopyLFS(ctx, repo, oldRepo) +} diff --git a/modules/repository/fork_test.go b/modules/repository/fork_test.go new file mode 100644 index 0000000000..cb3526bccf --- /dev/null +++ b/modules/repository/fork_test.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Gitea 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 repository + +import ( + "testing" + + "code.gitea.io/gitea/models" + "github.com/stretchr/testify/assert" +) + +func TestForkRepository(t *testing.T) { + assert.NoError(t, models.PrepareTestDatabase()) + + // user 13 has already forked repo10 + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 13}).(*models.User) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository) + + fork, err := ForkRepository(user, user, repo, "test", "test") + assert.Nil(t, fork) + assert.Error(t, err) + assert.True(t, models.IsErrForkAlreadyExist(err)) +} diff --git a/modules/repository/generate.go b/modules/repository/generate.go new file mode 100644 index 0000000000..96ce25e59f --- /dev/null +++ b/modules/repository/generate.go @@ -0,0 +1,230 @@ +// Copyright 2019 The Gitea 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 repository + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" +) + +func generateExpansion(src string, templateRepo, generateRepo *models.Repository) string { + return os.Expand(src, func(key string) string { + switch key { + case "REPO_NAME": + return generateRepo.Name + case "TEMPLATE_NAME": + return templateRepo.Name + case "REPO_DESCRIPTION": + return generateRepo.Description + case "TEMPLATE_DESCRIPTION": + return templateRepo.Description + case "REPO_OWNER": + return generateRepo.OwnerName + case "TEMPLATE_OWNER": + return templateRepo.OwnerName + case "REPO_LINK": + return generateRepo.Link() + case "TEMPLATE_LINK": + return templateRepo.Link() + case "REPO_HTTPS_URL": + return generateRepo.CloneLink().HTTPS + case "TEMPLATE_HTTPS_URL": + return templateRepo.CloneLink().HTTPS + case "REPO_SSH_URL": + return generateRepo.CloneLink().SSH + case "TEMPLATE_SSH_URL": + return templateRepo.CloneLink().SSH + default: + return key + } + }) +} + +func checkGiteaTemplate(tmpDir string) (*models.GiteaTemplate, error) { + gtPath := filepath.Join(tmpDir, ".gitea", "template") + if _, err := os.Stat(gtPath); os.IsNotExist(err) { + return nil, nil + } else if err != nil { + return nil, err + } + + content, err := ioutil.ReadFile(gtPath) + if err != nil { + return nil, err + } + + gt := &models.GiteaTemplate{ + Path: gtPath, + Content: content, + } + + return gt, nil +} + +func generateRepoCommit(repo, templateRepo, generateRepo *models.Repository, tmpDir string) error { + commitTimeStr := time.Now().Format(time.RFC3339) + authorSig := repo.Owner.NewGitSig() + + // Because this may call hooks we should pass in the environment + env := append(os.Environ(), + "GIT_AUTHOR_NAME="+authorSig.Name, + "GIT_AUTHOR_EMAIL="+authorSig.Email, + "GIT_AUTHOR_DATE="+commitTimeStr, + "GIT_COMMITTER_NAME="+authorSig.Name, + "GIT_COMMITTER_EMAIL="+authorSig.Email, + "GIT_COMMITTER_DATE="+commitTimeStr, + ) + + // Clone to temporary path and do the init commit. + templateRepoPath := templateRepo.RepoPath() + if err := git.Clone(templateRepoPath, tmpDir, git.CloneRepoOptions{ + Depth: 1, + }); err != nil { + return fmt.Errorf("git clone: %v", err) + } + + if err := os.RemoveAll(path.Join(tmpDir, ".git")); err != nil { + return fmt.Errorf("remove git dir: %v", err) + } + + // Variable expansion + gt, err := checkGiteaTemplate(tmpDir) + if err != nil { + return fmt.Errorf("checkGiteaTemplate: %v", err) + } + + if err := os.Remove(gt.Path); err != nil { + return fmt.Errorf("remove .giteatemplate: %v", err) + } + + // Avoid walking tree if there are no globs + if len(gt.Globs()) > 0 { + tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/" + if err := filepath.Walk(tmpDirSlash, func(path string, info os.FileInfo, walkErr error) error { + if walkErr != nil { + return walkErr + } + + if info.IsDir() { + return nil + } + + base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash) + for _, g := range gt.Globs() { + if g.Match(base) { + content, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + if err := ioutil.WriteFile(path, + []byte(generateExpansion(string(content), templateRepo, generateRepo)), + 0644); err != nil { + return err + } + break + } + } + return nil + }); err != nil { + return err + } + } + + if err := git.InitRepository(tmpDir, false); err != nil { + return err + } + + repoPath := repo.RepoPath() + if stdout, err := git.NewCommand("remote", "add", "origin", repoPath). + SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)). + RunInDirWithEnv(tmpDir, env); err != nil { + log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err) + return fmt.Errorf("git remote add: %v", err) + } + + return initRepoCommit(tmpDir, repo, repo.Owner) +} + +func generateGitContent(ctx models.DBContext, repo, templateRepo, generateRepo *models.Repository) (err error) { + tmpDir, err := ioutil.TempDir(os.TempDir(), "gitea-"+repo.Name) + if err != nil { + return fmt.Errorf("Failed to create temp dir for repository %s: %v", repo.RepoPath(), err) + } + + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + log.Error("RemoveAll: %v", err) + } + }() + + if err = generateRepoCommit(repo, templateRepo, generateRepo, tmpDir); err != nil { + return fmt.Errorf("generateRepoCommit: %v", err) + } + + // re-fetch repo + if repo, err = models.GetRepositoryByIDCtx(ctx, repo.ID); err != nil { + return fmt.Errorf("getRepositoryByID: %v", err) + } + + repo.DefaultBranch = "master" + if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil { + return fmt.Errorf("updateRepository: %v", err) + } + + return nil +} + +// GenerateGitContent generates git content from a template repository +func GenerateGitContent(ctx models.DBContext, templateRepo, generateRepo *models.Repository) error { + if err := generateGitContent(ctx, generateRepo, templateRepo, generateRepo); err != nil { + return err + } + + if err := generateRepo.UpdateSize(ctx); err != nil { + return fmt.Errorf("failed to update size for repository: %v", err) + } + + if err := models.CopyLFS(ctx, generateRepo, templateRepo); err != nil { + return fmt.Errorf("failed to copy LFS: %v", err) + } + return nil +} + +// GenerateRepository generates a repository from a template +func GenerateRepository(ctx models.DBContext, doer, owner *models.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) { + generateRepo := &models.Repository{ + OwnerID: owner.ID, + Owner: owner, + OwnerName: owner.Name, + Name: opts.Name, + LowerName: strings.ToLower(opts.Name), + Description: opts.Description, + IsPrivate: opts.Private, + IsEmpty: !opts.GitContent || templateRepo.IsEmpty, + IsFsckEnabled: templateRepo.IsFsckEnabled, + TemplateID: templateRepo.ID, + } + + if err = models.CreateRepository(ctx, doer, owner, generateRepo); err != nil { + return nil, err + } + + repoPath := models.RepoPath(owner.Name, generateRepo.Name) + if err = checkInitRepository(repoPath); err != nil { + return generateRepo, err + } + + return generateRepo, nil +} diff --git a/modules/repository/init.go b/modules/repository/init.go new file mode 100644 index 0000000000..a65b335174 --- /dev/null +++ b/modules/repository/init.go @@ -0,0 +1,214 @@ +// Copyright 2019 The Gitea 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 repository + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + + "github.com/mcuadros/go-version" + "github.com/unknwon/com" +) + +func prepareRepoCommit(ctx models.DBContext, repo *models.Repository, tmpDir, repoPath string, opts models.CreateRepoOptions) error { + commitTimeStr := time.Now().Format(time.RFC3339) + authorSig := repo.Owner.NewGitSig() + + // Because this may call hooks we should pass in the environment + env := append(os.Environ(), + "GIT_AUTHOR_NAME="+authorSig.Name, + "GIT_AUTHOR_EMAIL="+authorSig.Email, + "GIT_AUTHOR_DATE="+commitTimeStr, + "GIT_COMMITTER_NAME="+authorSig.Name, + "GIT_COMMITTER_EMAIL="+authorSig.Email, + "GIT_COMMITTER_DATE="+commitTimeStr, + ) + + // Clone to temporary path and do the init commit. + if stdout, err := git.NewCommand("clone", repoPath, tmpDir). + SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)). + RunInDirWithEnv("", env); err != nil { + log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err) + return fmt.Errorf("git clone: %v", err) + } + + // README + data, err := models.GetRepoInitFile("readme", opts.Readme) + if err != nil { + return fmt.Errorf("GetRepoInitFile[%s]: %v", opts.Readme, err) + } + + cloneLink := repo.CloneLink() + match := map[string]string{ + "Name": repo.Name, + "Description": repo.Description, + "CloneURL.SSH": cloneLink.SSH, + "CloneURL.HTTPS": cloneLink.HTTPS, + } + if err = ioutil.WriteFile(filepath.Join(tmpDir, "README.md"), + []byte(com.Expand(string(data), match)), 0644); err != nil { + return fmt.Errorf("write README.md: %v", err) + } + + // .gitignore + if len(opts.Gitignores) > 0 { + var buf bytes.Buffer + names := strings.Split(opts.Gitignores, ",") + for _, name := range names { + data, err = models.GetRepoInitFile("gitignore", name) + if err != nil { + return fmt.Errorf("GetRepoInitFile[%s]: %v", name, err) + } + buf.WriteString("# ---> " + name + "\n") + buf.Write(data) + buf.WriteString("\n") + } + + if buf.Len() > 0 { + if err = ioutil.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0644); err != nil { + return fmt.Errorf("write .gitignore: %v", err) + } + } + } + + // LICENSE + if len(opts.License) > 0 { + data, err = models.GetRepoInitFile("license", opts.License) + if err != nil { + return fmt.Errorf("GetRepoInitFile[%s]: %v", opts.License, err) + } + + if err = ioutil.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0644); err != nil { + return fmt.Errorf("write LICENSE: %v", err) + } + } + + return nil +} + +// initRepoCommit temporarily changes with work directory. +func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User) (err error) { + commitTimeStr := time.Now().Format(time.RFC3339) + + sig := u.NewGitSig() + // Because this may call hooks we should pass in the environment + env := append(os.Environ(), + "GIT_AUTHOR_NAME="+sig.Name, + "GIT_AUTHOR_EMAIL="+sig.Email, + "GIT_AUTHOR_DATE="+commitTimeStr, + "GIT_COMMITTER_NAME="+sig.Name, + "GIT_COMMITTER_EMAIL="+sig.Email, + "GIT_COMMITTER_DATE="+commitTimeStr, + ) + + if stdout, err := git.NewCommand("add", "--all"). + SetDescription(fmt.Sprintf("initRepoCommit (git add): %s", tmpPath)). + RunInDir(tmpPath); err != nil { + log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err) + return fmt.Errorf("git add --all: %v", err) + } + + binVersion, err := git.BinVersion() + if err != nil { + return fmt.Errorf("Unable to get git version: %v", err) + } + + args := []string{ + "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), + "-m", "Initial commit", + } + + if version.Compare(binVersion, "1.7.9", ">=") { + sign, keyID := models.SignInitialCommit(tmpPath, u) + if sign { + args = append(args, "-S"+keyID) + } else if version.Compare(binVersion, "2.0.0", ">=") { + args = append(args, "--no-gpg-sign") + } + } + + if stdout, err := git.NewCommand(args...). + SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)). + RunInDirWithEnv(tmpPath, env); err != nil { + log.Error("Failed to commit: %v: Stdout: %s\nError: %v", args, stdout, err) + return fmt.Errorf("git commit: %v", err) + } + + if stdout, err := git.NewCommand("push", "origin", "master"). + SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)). + RunInDirWithEnv(tmpPath, models.InternalPushingEnvironment(u, repo)); err != nil { + log.Error("Failed to push back to master: Stdout: %s\nError: %v", stdout, err) + return fmt.Errorf("git push: %v", err) + } + + return nil +} + +func checkInitRepository(repoPath string) (err error) { + // Somehow the directory could exist. + if com.IsExist(repoPath) { + return fmt.Errorf("checkInitRepository: path already exists: %s", repoPath) + } + + // Init git bare new repository. + if err = git.InitRepository(repoPath, true); err != nil { + return fmt.Errorf("git.InitRepository: %v", err) + } else if err = models.CreateDelegateHooks(repoPath); err != nil { + return fmt.Errorf("createDelegateHooks: %v", err) + } + return nil +} + +// InitRepository initializes README and .gitignore if needed. +func initRepository(ctx models.DBContext, repoPath string, u *models.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) { + if err = checkInitRepository(repoPath); err != nil { + return err + } + + // Initialize repository according to user's choice. + if opts.AutoInit { + tmpDir, err := ioutil.TempDir(os.TempDir(), "gitea-"+repo.Name) + if err != nil { + return fmt.Errorf("Failed to create temp dir for repository %s: %v", repo.RepoPath(), err) + } + + defer os.RemoveAll(tmpDir) + + if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil { + return fmt.Errorf("prepareRepoCommit: %v", err) + } + + // Apply changes and commit. + if err = initRepoCommit(tmpDir, repo, u); err != nil { + return fmt.Errorf("initRepoCommit: %v", err) + } + } + + // Re-fetch the repository from database before updating it (else it would + // override changes that were done earlier with sql) + if repo, err = models.GetRepositoryByIDCtx(ctx, repo.ID); err != nil { + return fmt.Errorf("getRepositoryByID: %v", err) + } + + if !opts.AutoInit { + repo.IsEmpty = true + } + + repo.DefaultBranch = "master" + if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil { + return fmt.Errorf("updateRepository: %v", err) + } + + return nil +} diff --git a/modules/repository/repo.go b/modules/repository/repo.go index b0b118e038..bb8cceeadc 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -116,7 +116,7 @@ func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opt } } - if err = repo.UpdateSize(); err != nil { + if err = repo.UpdateSize(models.DefaultDBContext()); err != nil { log.Error("Failed to update size for repository: %v", err) } |