From 1b4959c4e2d2a7f972082fcec6dd6624ffdf3f06 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 26 Jan 2024 14:38:45 +0800 Subject: [PATCH 01/17] Move git command run on a special repository to standalone methods to hide the implemention detail --- modules/git/command.go | 4 ++ modules/gitrepo/command.go | 39 +++++++++++++++++++ modules/indexer/code/bleve/bleve.go | 4 +- .../code/elasticsearch/elasticsearch.go | 4 +- modules/indexer/code/git.go | 13 ++++--- routers/private/hook_pre_receive.go | 4 +- services/agit/agit.go | 4 +- services/doctor/heads.go | 10 +++-- services/migrations/gitea_uploader.go | 13 +++++-- services/release/release.go | 7 ++-- services/repository/check.go | 3 +- tests/integration/api_repo_git_tags_test.go | 7 +++- 12 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 modules/gitrepo/command.go diff --git a/modules/git/command.go b/modules/git/command.go index 9305ef6f928e7..7d0438a1d07c9 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -388,6 +388,10 @@ func (r *runStdError) IsExitCode(code int) bool { return false } +func NewRunStdError(err error, stderr string) RunStdError { + return &runStdError{err: err, stderr: stderr} +} + // RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) { stdoutBytes, stderrBytes, err := c.RunStdBytes(opts) diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go new file mode 100644 index 0000000000000..bd26b2e63294a --- /dev/null +++ b/modules/gitrepo/command.go @@ -0,0 +1,39 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" +) + +// RunGitCmdStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). +func RunGitCmdStdString(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError) { + if opts.Dir != "" { + // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug + panic("dir field must be empty when using RunStdBytes") + } + opts.Dir = repo.RepoPath() + return c.RunStdString(opts) +} + +// RunGitCmdStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr). +func RunGitCmdStdBytes(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr []byte, runErr git.RunStdError) { + if opts.Dir != "" { + // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug + panic("dir field must be empty when using RunStdBytes") + } + opts.Dir = repo.RepoPath() + return c.RunStdBytes(opts) +} + +// Run runs the command with the RunOpts +func RunGitCmd(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) error { + if opts.Dir != "" { + // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug + panic("dir field must be empty when using RunStdBytes") + } + opts.Dir = repo.RepoPath() + return c.Run(opts) +} diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index 0bfd85cb3f30d..0dd26ff0a8803 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/indexer/code/internal" indexer_internal "code.gitea.io/gitea/modules/indexer/internal" inner_bleve "code.gitea.io/gitea/modules/indexer/internal/bleve" @@ -137,7 +138,8 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro var err error if !update.Sized { var stdout string - stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + cmd := git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha) + stdout, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) if err != nil { return err } diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index e7e3429a39af6..a1bb7a9217764 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/indexer/code/internal" indexer_internal "code.gitea.io/gitea/modules/indexer/internal" inner_elasticsearch "code.gitea.io/gitea/modules/indexer/internal/elasticsearch" @@ -94,7 +95,8 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro var err error if !update.Sized { var stdout string - stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + cmd := git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha) + stdout, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) if err != nil { return nil, err } diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index 76cd78e11e6bf..cad43ee400be2 100644 --- a/modules/indexer/code/git.go +++ b/modules/indexer/code/git.go @@ -10,13 +10,15 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + gitrepo "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/indexer/code/internal" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) { - stdout, _, err := git.NewCommand(ctx, "show-ref", "-s").AddDynamicArguments(git.BranchPrefix + repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + cmd := git.NewCommand(ctx, "show-ref", "-s").AddDynamicArguments(git.BranchPrefix + repo.DefaultBranch) + stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) if err != nil { return "", err } @@ -33,7 +35,7 @@ func getRepoChanges(ctx context.Context, repo *repo_model.Repository, revision s needGenesis := len(status.CommitSha) == 0 if !needGenesis { hasAncestorCmd := git.NewCommand(ctx, "merge-base").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) - stdout, _, _ := hasAncestorCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, _ := gitrepo.RunGitCmdStdString(repo, hasAncestorCmd, &git.RunOpts{}) needGenesis = len(stdout) == 0 } @@ -86,7 +88,8 @@ func parseGitLsTreeOutput(objectFormat git.ObjectFormat, stdout []byte) ([]inter // genesisChanges get changes to add repo to the indexer for the first time func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { var changes internal.RepoChanges - stdout, _, runErr := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()}) + cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision) + stdout, _, runErr := gitrepo.RunGitCmdStdBytes(repo, cmd, &git.RunOpts{}) if runErr != nil { return nil, runErr } @@ -103,7 +106,7 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s // nonGenesisChanges get changes since the previous indexer update func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { diffCmd := git.NewCommand(ctx, "diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) - stdout, _, runErr := diffCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, runErr := gitrepo.RunGitCmdStdString(repo, diffCmd, &git.RunOpts{}) if runErr != nil { // previous commit sha may have been removed by a force push, so // try rebuilding from scratch @@ -169,7 +172,7 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l").AddDynamicArguments(revision). AddDashesAndList(updatedFilenames...) - lsTreeStdout, _, err := cmd.RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()}) + lsTreeStdout, _, err := gitrepo.RunGitCmdStdBytes(repo, cmd, &git.RunOpts{}) if err != nil { return nil, err } diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 90d8287f06fd2..8be597bd8e1cc 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -18,6 +18,7 @@ import ( user_model "code.gitea.io/gitea/models/user" gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/web" @@ -185,7 +186,8 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r // 2. Disallow force pushes to protected branches if oldCommitID != objectFormat.EmptyObjectID().String() { - output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env}) + cmd := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID) + output, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{Env: ctx.env}) if err != nil { log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err) ctx.JSON(http.StatusInternalServerError, private.Response{ diff --git a/services/agit/agit.go b/services/agit/agit.go index bc68372570402..296162bd8e49d 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -13,6 +13,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" notify_service "code.gitea.io/gitea/services/notify" @@ -180,7 +181,8 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. } if !forcePush { - output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()}) + cmd := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]) + output, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{Env: os.Environ()}) if err != nil { return nil, fmt.Errorf("Fail to detect force push: %w", err) } else if len(output) > 0 { diff --git a/services/doctor/heads.go b/services/doctor/heads.go index 41fca01d57137..19e9163d25814 100644 --- a/services/doctor/heads.go +++ b/services/doctor/heads.go @@ -8,6 +8,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" ) @@ -18,9 +19,11 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) numReposUpdated := 0 err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { numRepos++ - _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + cmd := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch) + _, _, defaultBranchErr := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) - head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + cmd = git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD") + head, _, headErr := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) // what we expect: default branch is valid, and HEAD points to it if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch { @@ -46,7 +49,8 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) } // otherwise, let's try fixing HEAD - err := git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(&git.RunOpts{Dir: repo.RepoPath()}) + cmd = git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch) + err := gitrepo.RunGitCmd(repo, cmd, &git.RunOpts{}) if err != nil { logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) return nil diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 5a4392c66775f..4c08ea922149b 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -21,6 +21,7 @@ import ( user_model "code.gitea.io/gitea/models/user" base_module "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" @@ -640,7 +641,8 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head fetchArg = git.BranchPrefix + fetchArg } - _, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) + cmd := git.NewCommand(g.ctx, "fetch", "--no-tags").AddDashesAndList(remote, fetchArg) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) if err != nil { log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) return head, nil @@ -659,7 +661,8 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head pr.Head.SHA = headSha } - _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) + cmd := git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) if err != nil { return "", err } @@ -676,13 +679,15 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head // The SHA is empty log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName) } else { - _, _, err = git.NewCommand(g.ctx, "rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) + cmd := git.NewCommand(g.ctx, "rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) if err != nil { // Git update-ref remove bad references with a relative path log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitRefName()) } else { // set head information - _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) + cmd := git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) if err != nil { log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) } diff --git a/services/release/release.go b/services/release/release.go index f17682ae0f8f6..b4c90bea02e56 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -16,6 +16,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repository" @@ -319,9 +320,9 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re } } - if stdout, _, err := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName). - SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)). - RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") { + cmd := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName). + SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)) + if stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}); err != nil && !strings.Contains(err.Error(), "not found") { log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err) return fmt.Errorf("git tag -d: %w", err) } diff --git a/services/repository/check.go b/services/repository/check.go index b874ede51fdbc..f4a4662f1e120 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -14,6 +14,7 @@ import ( system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/util" @@ -90,7 +91,7 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())) var stdout string var err error - stdout, _, err = command.RunStdString(&git.RunOpts{Timeout: timeout, Dir: repo.RepoPath()}) + stdout, _, err = gitrepo.RunGitCmdStdString(repo, command, &git.RunOpts{Timeout: timeout}) if err != nil { log.Error("Repository garbage collection failed for %-v. Stdout: %s\nError: %v", repo, stdout, err) diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go index 2e8510ab1b3ae..973fadb65911f 100644 --- a/tests/integration/api_repo_git_tags_test.go +++ b/tests/integration/api_repo_git_tags_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/tests" @@ -29,8 +30,10 @@ func TestAPIGitTags(t *testing.T) { token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) // Set up git config for the tagger - _ = git.NewCommand(git.DefaultContext, "config", "user.name").AddDynamicArguments(user.Name).Run(&git.RunOpts{Dir: repo.RepoPath()}) - _ = git.NewCommand(git.DefaultContext, "config", "user.email").AddDynamicArguments(user.Email).Run(&git.RunOpts{Dir: repo.RepoPath()}) + cmd := git.NewCommand(git.DefaultContext, "config", "user.name").AddDynamicArguments(user.Name) + _ = gitrepo.RunGitCmd(repo, cmd, &git.RunOpts{}) + cmd = git.NewCommand(git.DefaultContext, "config", "user.email").AddDynamicArguments(user.Email) + _ = gitrepo.RunGitCmd(repo, cmd, &git.RunOpts{}) gitRepo, _ := git.OpenRepository(git.DefaultContext, repo.RepoPath()) defer gitRepo.Close() From 0d2b7f3773849fe35006ee79b76ca58d8be060c5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 26 Jan 2024 16:34:40 +0800 Subject: [PATCH 02/17] More refactoring --- modules/gitrepo/command.go | 22 +++++- modules/repository/generate.go | 7 +- services/migrations/gitea_uploader_test.go | 10 ++- services/mirror/mirror_pull.go | 82 +++++++++++----------- services/mirror/mirror_push.go | 23 +++--- services/pull/check.go | 12 ++-- services/pull/pull.go | 4 +- tests/integration/pull_merge_test.go | 34 +++++---- 8 files changed, 115 insertions(+), 79 deletions(-) diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go index bd26b2e63294a..49629cb9b0339 100644 --- a/modules/gitrepo/command.go +++ b/modules/gitrepo/command.go @@ -18,6 +18,16 @@ func RunGitCmdStdString(repo *repo_model.Repository, c *git.Command, opts *git.R return c.RunStdString(opts) } +// RunGitCmdStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). +func RunGitCmdStdStringWiki(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError) { + if opts.Dir != "" { + // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug + panic("dir field must be empty when using RunStdBytes") + } + opts.Dir = repo.WikiPath() + return c.RunStdString(opts) +} + // RunGitCmdStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr). func RunGitCmdStdBytes(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr []byte, runErr git.RunStdError) { if opts.Dir != "" { @@ -28,7 +38,7 @@ func RunGitCmdStdBytes(repo *repo_model.Repository, c *git.Command, opts *git.Ru return c.RunStdBytes(opts) } -// Run runs the command with the RunOpts +// RunGitCmd runs the command with the RunOpts func RunGitCmd(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) error { if opts.Dir != "" { // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug @@ -37,3 +47,13 @@ func RunGitCmd(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) e opts.Dir = repo.RepoPath() return c.Run(opts) } + +// RunGitCmdWiki runs the command with the RunOpts +func RunGitCmdWiki(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) error { + if opts.Dir != "" { + // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug + panic("dir field must be empty when using RunStdBytes") + } + opts.Dir = repo.WikiPath() + return c.Run(opts) +} diff --git a/modules/repository/generate.go b/modules/repository/generate.go index f8478b8c1852a..97b1f38aa2627 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -19,6 +19,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" @@ -366,9 +367,9 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ return generateRepo, fmt.Errorf("checkDaemonExportOK: %w", err) } - if stdout, _, err := git.NewCommand(ctx, "update-server-info"). - SetDescription(fmt.Sprintf("GenerateRepository(git update-server-info): %s", repoPath)). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + cmd := git.NewCommand(ctx, "update-server-info"). + SetDescription(fmt.Sprintf("GenerateRepository(git update-server-info): %s", repoPath)) + if stdout, _, err := gitrepo.RunGitCmdStdString(generateRepo, cmd, &git.RunOpts{}); err != nil { log.Error("GenerateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", generateRepo, stdout, err) return generateRepo, fmt.Errorf("error in GenerateRepository(git update-server-info): %w", err) } diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index 4ec0361dfb467..c947ee4a5f09e 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" @@ -235,7 +236,8 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) baseRef := "master" assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false, fromRepo.ObjectFormatName)) - err := git.NewCommand(git.DefaultContext, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(&git.RunOpts{Dir: fromRepo.RepoPath()}) + cmd := git.NewCommand(git.DefaultContext, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef) + err := gitrepo.RunGitCmd(fromRepo, cmd, &git.RunOpts{}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", fromRepo.RepoPath())), 0o644)) assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true)) @@ -259,7 +261,8 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { // fromRepo branch1 // headRef := "branch1" - _, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(headRef).RunStdString(&git.RunOpts{Dir: fromRepo.RepoPath()}) + cmd = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(headRef) + _, _, err = gitrepo.RunGitCmdStdString(fromRepo, cmd, &git.RunOpts{}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("SOMETHING"), 0o644)) assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true)) @@ -283,7 +286,8 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { assert.NoError(t, git.CloneWithArgs(git.DefaultContext, nil, fromRepo.RepoPath(), forkRepo.RepoPath(), git.CloneRepoOptions{ Branch: headRef, })) - _, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(forkHeadRef).RunStdString(&git.RunOpts{Dir: forkRepo.RepoPath()}) + cmd = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(forkHeadRef) + _, _, err = gitrepo.RunGitCmdStdString(forkRepo, cmd, &git.RunOpts{}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# branch2 %s", forkRepo.RepoPath())), 0o644)) assert.NoError(t, git.AddChanges(forkRepo.RepoPath(), true)) diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 6f03e14ab08bd..02b7d342a352d 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -13,6 +13,7 @@ import ( system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" @@ -30,29 +31,32 @@ const gitShortEmptySha = "0000000" // UpdateAddress writes new address to Git repository and database func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error { remoteName := m.GetRemoteName() - repoPath := m.GetRepository(ctx).RepoPath() + repo := m.GetRepository(ctx) + // Remove old remote - _, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) + cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName) + _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } - cmd := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr) + cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr) if strings.Contains(addr, "://") && strings.Contains(addr, "@") { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(addr), repoPath)) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(addr), repo.FullName())) } else { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repoPath)) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repo.FullName())) } - _, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath}) + _, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } if m.Repo.HasWiki() { - wikiPath := m.Repo.WikiPath() + wikiPath := m.Repo.FullName() + ".wiki.git" wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) // Remove old remote of wiki - _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: wikiPath}) + cmd = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName) + _, _, err = gitrepo.RunGitCmdStdStringWiki(repo, cmd, &git.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -63,7 +67,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } else { cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath)) } - _, _, err = cmd.RunStdString(&git.RunOpts{Dir: wikiPath}) + _, _, err = gitrepo.RunGitCmdStdStringWiki(repo, cmd, &git.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -238,15 +242,13 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutBuilder := strings.Builder{} stderrBuilder := strings.Builder{} - if err := cmd. - SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())). - Run(&git.RunOpts{ - Timeout: timeout, - Dir: repoPath, - Env: envs, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, - }); err != nil { + cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())) + if err := gitrepo.RunGitCmd(m.Repo, cmd, &git.RunOpts{ + Timeout: timeout, + Env: envs, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -265,14 +267,12 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo // Successful prune - reattempt mirror stderrBuilder.Reset() stdoutBuilder.Reset() - if err = cmd. - SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())). - Run(&git.RunOpts{ - Timeout: timeout, - Dir: repoPath, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, - }); err != nil { + cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())) + if err = gitrepo.RunGitCmd(m.Repo, cmd, &git.RunOpts{ + Timeout: timeout, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -335,14 +335,13 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo) stderrBuilder.Reset() stdoutBuilder.Reset() - if err := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). - SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). - Run(&git.RunOpts{ - Timeout: timeout, - Dir: wikiPath, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, - }); err != nil { + cmd := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). + SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())) + if err := gitrepo.RunGitCmdWiki(m.Repo, cmd, &git.RunOpts{ + Timeout: timeout, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -362,14 +361,13 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stderrBuilder.Reset() stdoutBuilder.Reset() - if err = git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). - SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). - Run(&git.RunOpts{ - Timeout: timeout, - Dir: wikiPath, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, - }); err != nil { + git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). + SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())) + if err = gitrepo.RunGitCmdWiki(m.Repo, cmd, &git.RunOpts{ + Timeout: timeout, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() stderrMessage = util.SanitizeCredentialURLs(stderr) diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index b117e79faca96..93baf1bc26bcc 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" @@ -28,33 +29,39 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) // AddPushMirrorRemote registers the push mirror remote. func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { - addRemoteAndConfig := func(addr, path string) error { + addRemoteAndConfig := func(addr string, repo *repo_model.Repository, isWiki bool, runStdString func(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError)) error { cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) + path := repo.FullName() + ".git" + if isWiki { + path = repo.FullName() + ".wiki.git" + } if strings.Contains(addr, "://") && strings.Contains(addr, "@") { cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path)) } else { cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path)) } - if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil { + if _, _, err := runStdString(repo, cmd, &git.RunOpts{}); err != nil { return err } - if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + cmd = git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*") + if _, _, err := runStdString(repo, cmd, &git.RunOpts{}); err != nil { return err } - if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + cmd = git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*") + if _, _, err := runStdString(repo, cmd, &git.RunOpts{}); err != nil { return err } return nil } - if err := addRemoteAndConfig(addr, m.Repo.RepoPath()); err != nil { + if err := addRemoteAndConfig(addr, m.Repo, false, gitrepo.RunGitCmdStdString); err != nil { return err } if m.Repo.HasWiki() { wikiRemoteURL := repository.WikiRemoteURL(ctx, addr) if len(wikiRemoteURL) > 0 { - if err := addRemoteAndConfig(wikiRemoteURL, m.Repo.WikiPath()); err != nil { + if err := addRemoteAndConfig(wikiRemoteURL, m.Repo, true, gitrepo.RunGitCmdStdStringWiki); err != nil { return err } } @@ -68,12 +75,12 @@ func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName) _ = m.GetRepository(ctx) - if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: m.Repo.RepoPath()}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(m.Repo, cmd, &git.RunOpts{}); err != nil { return err } if m.Repo.HasWiki() { - if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: m.Repo.WikiPath()}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdStringWiki(m.Repo, cmd, &git.RunOpts{}); err != nil { // The wiki remote may not exist log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err) } diff --git a/services/pull/check.go b/services/pull/check.go index ebe4c6d61b038..7a87dfc39a144 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" @@ -196,9 +197,8 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com prHeadRef := pr.GetGitRefName() // Check if the pull request is merged into BaseBranch - if _, _, err := git.NewCommand(ctx, "merge-base", "--is-ancestor"). - AddDynamicArguments(prHeadRef, pr.BaseBranch). - RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil { + cmd := git.NewCommand(ctx, "merge-base", "--is-ancestor").AddDynamicArguments(prHeadRef, pr.BaseBranch) + if _, _, err := gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &git.RunOpts{}); err != nil { if strings.Contains(err.Error(), "exit status 1") { // prHeadRef is not an ancestor of the base branch return nil, nil @@ -227,9 +227,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com } // Get the commit from BaseBranch where the pull request got merged - mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse"). - AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch). - RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) + cmd = git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse"). + AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch) + mergeCommit, _, err := gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &git.RunOpts{}) if err != nil { return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err) } else if len(mergeCommit) < objectFormat.FullLength() { diff --git a/services/pull/pull.go b/services/pull/pull.go index 930954bdfdfa1..b1e9ba6504a1a 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/container" gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -522,7 +523,8 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) { return err } - _, _, err = git.NewCommand(ctx, "update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID).RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) + cmd := git.NewCommand(ctx, "update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID) + _, _, err = gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &git.RunOpts{}) if err != nil { log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err) } diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 2aa6742a56ac0..781c75e1d6ede 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -25,6 +25,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" @@ -284,15 +285,15 @@ func TestCantMergeUnrelated(t *testing.T) { OwnerID: user1.ID, Name: "repo1", }) - path := repo_model.RepoPath(user1.Name, repo1.Name) - err := git.NewCommand(git.DefaultContext, "read-tree", "--empty").Run(&git.RunOpts{Dir: path}) + cmd := git.NewCommand(git.DefaultContext, "read-tree", "--empty") + err := gitrepo.RunGitCmd(repo1, cmd, &git.RunOpts{}) assert.NoError(t, err) stdin := bytes.NewBufferString("Unrelated File") var stdout strings.Builder - err = git.NewCommand(git.DefaultContext, "hash-object", "-w", "--stdin").Run(&git.RunOpts{ - Dir: path, + cmd = git.NewCommand(git.DefaultContext, "hash-object", "-w", "--stdin") + err = gitrepo.RunGitCmd(repo1, cmd, &git.RunOpts{ Stdin: stdin, Stdout: &stdout, }) @@ -300,10 +301,13 @@ func TestCantMergeUnrelated(t *testing.T) { assert.NoError(t, err) sha := strings.TrimSpace(stdout.String()) - _, _, err = git.NewCommand(git.DefaultContext, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments("100644", sha, "somewher-over-the-rainbow").RunStdString(&git.RunOpts{Dir: path}) + cmd = git.NewCommand(git.DefaultContext, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments("100644", sha, "somewher-over-the-rainbow") + _, _, err = gitrepo.RunGitCmdStdString(repo1, cmd, &git.RunOpts{}) assert.NoError(t, err) - treeSha, _, err := git.NewCommand(git.DefaultContext, "write-tree").RunStdString(&git.RunOpts{Dir: path}) + cmd = git.NewCommand(git.DefaultContext, "write-tree") + treeSha, _, err := gitrepo.RunGitCmdStdString(repo1, cmd, &git.RunOpts{}) + assert.NoError(t, err) treeSha = strings.TrimSpace(treeSha) @@ -323,17 +327,17 @@ func TestCantMergeUnrelated(t *testing.T) { _, _ = messageBytes.WriteString("\n") stdout.Reset() - err = git.NewCommand(git.DefaultContext, "commit-tree").AddDynamicArguments(treeSha). - Run(&git.RunOpts{ - Env: env, - Dir: path, - Stdin: messageBytes, - Stdout: &stdout, - }) + cmd = git.NewCommand(git.DefaultContext, "commit-tree").AddDynamicArguments(treeSha) + err = gitrepo.RunGitCmd(repo1, cmd, &git.RunOpts{ + Env: env, + Stdin: messageBytes, + Stdout: &stdout, + }) assert.NoError(t, err) commitSha := strings.TrimSpace(stdout.String()) - _, _, err = git.NewCommand(git.DefaultContext, "branch", "unrelated").AddDynamicArguments(commitSha).RunStdString(&git.RunOpts{Dir: path}) + cmd = git.NewCommand(git.DefaultContext, "branch", "unrelated").AddDynamicArguments(commitSha) + _, _, err = gitrepo.RunGitCmdStdString(repo1, cmd, &git.RunOpts{}) assert.NoError(t, err) testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") @@ -348,7 +352,7 @@ func TestCantMergeUnrelated(t *testing.T) { session.MakeRequest(t, req, http.StatusCreated) // Now this PR could be marked conflict - or at least a race may occur - so drop down to pure code at this point... - gitRepo, err := git.OpenRepository(git.DefaultContext, path) + gitRepo, err := git.OpenRepository(git.DefaultContext, repo1.RepoPath()) assert.NoError(t, err) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ HeadRepoID: repo1.ID, From 55abd3cf996ceeaeb9a55ee66b6a68d85ad8ff3b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 26 Jan 2024 16:42:36 +0800 Subject: [PATCH 03/17] More refactoring --- modules/gitrepo/url.go | 10 ++++++++++ routers/web/repo/pull.go | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 modules/gitrepo/url.go diff --git a/modules/gitrepo/url.go b/modules/gitrepo/url.go new file mode 100644 index 0000000000000..0118b8c92bc40 --- /dev/null +++ b/modules/gitrepo/url.go @@ -0,0 +1,10 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import repo_model "code.gitea.io/gitea/models/repo" + +func RepoGitURL(repo *repo_model.Repository) string { + return repo.RepoPath() +} diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index e36d7092af286..5589adfc6edfe 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -29,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" issue_template "code.gitea.io/gitea/modules/issue/template" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -438,7 +439,8 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri } if commitSHA != "" { // Get immediate parent of the first commit in the patch, grab history back - parentCommit, _, err = git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(&git.RunOpts{Dir: ctx.Repo.GitRepo.Path}) + cmd := git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA) + parentCommit, _, err = gitrepo.RunGitCmdStdString(ctx.Repo.Repository, cmd, &git.RunOpts{}) if err == nil { parentCommit = strings.TrimSpace(parentCommit) } From d30d9813a471b65468fe559a64b225dbe1d7b0e1 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 26 Jan 2024 17:42:21 +0800 Subject: [PATCH 04/17] Some refactoring --- modules/gitrepo/url.go | 4 ++++ services/mirror/mirror_pull.go | 19 +++++++++---------- services/mirror/mirror_push.go | 8 ++++---- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/modules/gitrepo/url.go b/modules/gitrepo/url.go index 0118b8c92bc40..114eb19813e9b 100644 --- a/modules/gitrepo/url.go +++ b/modules/gitrepo/url.go @@ -8,3 +8,7 @@ import repo_model "code.gitea.io/gitea/models/repo" func RepoGitURL(repo *repo_model.Repository) string { return repo.RepoPath() } + +func WikiRepoGitURL(repo *repo_model.Repository) string { + return repo.WikiPath() +} diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 02b7d342a352d..0948eea92bad2 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -42,9 +42,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr) if strings.Contains(addr, "://") && strings.Contains(addr, "@") { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(addr), repo.FullName())) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(addr), gitrepo.RepoGitURL(repo))) } else { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repo.FullName())) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, gitrepo.RepoGitURL(repo))) } _, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { @@ -52,7 +52,6 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } if m.Repo.HasWiki() { - wikiPath := m.Repo.FullName() + ".wiki.git" wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) // Remove old remote of wiki cmd = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName) @@ -63,9 +62,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath) if strings.Contains(wikiRemotePath, "://") && strings.Contains(wikiRemotePath, "@") { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(wikiRemotePath), wikiPath)) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(wikiRemotePath), gitrepo.WikiRepoGitURL(repo))) } else { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath)) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, gitrepo.WikiRepoGitURL(repo))) } _, _, err = gitrepo.RunGitCmdStdStringWiki(repo, cmd, &git.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { @@ -191,7 +190,7 @@ func pruneBrokenReferences(ctx context.Context, stderrBuilder.Reset() stdoutBuilder.Reset() pruneErr := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()). - SetDescription(fmt.Sprintf("Mirror.runSync %ssPrune references: %s ", wiki, m.Repo.FullName())). + SetDescription(fmt.Sprintf("Mirror.runSync %ssPrune references: %s ", wiki, gitrepo.RepoGitURL(m.Repo))). Run(&git.RunOpts{ Timeout: timeout, Dir: repoPath, @@ -242,7 +241,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutBuilder := strings.Builder{} stderrBuilder := strings.Builder{} - cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())) + cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", gitrepo.RepoGitURL(m.Repo))) if err := gitrepo.RunGitCmd(m.Repo, cmd, &git.RunOpts{ Timeout: timeout, Env: envs, @@ -267,7 +266,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo // Successful prune - reattempt mirror stderrBuilder.Reset() stdoutBuilder.Reset() - cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())) + cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", gitrepo.RepoGitURL(m.Repo))) if err = gitrepo.RunGitCmd(m.Repo, cmd, &git.RunOpts{ Timeout: timeout, Stdout: &stdoutBuilder, @@ -336,7 +335,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stderrBuilder.Reset() stdoutBuilder.Reset() cmd := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). - SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())) + SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", gitrepo.WikiRepoGitURL(m.Repo))) if err := gitrepo.RunGitCmdWiki(m.Repo, cmd, &git.RunOpts{ Timeout: timeout, Stdout: &stdoutBuilder, @@ -362,7 +361,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutBuilder.Reset() git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). - SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())) + SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", gitrepo.WikiRepoGitURL(m.Repo))) if err = gitrepo.RunGitCmdWiki(m.Repo, cmd, &git.RunOpts{ Timeout: timeout, Stdout: &stdoutBuilder, diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 93baf1bc26bcc..4b98aa6ff4144 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -31,14 +31,14 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(addr string, repo *repo_model.Repository, isWiki bool, runStdString func(repo *repo_model.Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError)) error { cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) - path := repo.FullName() + ".git" + url := gitrepo.RepoGitURL(repo) if isWiki { - path = repo.FullName() + ".wiki.git" + url = gitrepo.WikiRepoGitURL(repo) } if strings.Contains(addr, "://") && strings.Contains(addr, "@") { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path)) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), url)) } else { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path)) + cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, url)) } if _, _, err := runStdString(repo, cmd, &git.RunOpts{}); err != nil { return err From d573292170658d3ec5b7bbb71ad853bae949d337 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 13:26:12 +0800 Subject: [PATCH 05/17] Abstract for RepoPath --- models/repo/update.go | 16 +++++----------- modules/gitrepo/gitrepo.go | 22 ++++++++++++++++++++++ modules/{repository => gitrepo}/hooks.go | 21 ++++++++++++++++----- modules/gitrepo/url.go | 12 ++++++++++++ modules/indexer/stats/db.go | 10 +++++----- modules/migration/pullrequest.go | 6 +++--- modules/repository/generate.go | 2 +- modules/repository/init.go | 16 ++++++++-------- modules/repository/repo.go | 4 ++-- services/doctor/misc.go | 7 +++---- services/repository/adopt.go | 12 ++++++------ services/repository/create.go | 2 +- services/repository/delete.go | 6 +++--- services/repository/files/commit.go | 6 ++---- services/repository/fork.go | 2 +- services/repository/hooks.go | 5 ++--- services/wiki/wiki.go | 3 ++- 17 files changed, 94 insertions(+), 58 deletions(-) rename modules/{repository => gitrepo}/hooks.go (93%) create mode 100644 modules/gitrepo/url.go diff --git a/models/repo/update.go b/models/repo/update.go index 6ddf1a8905eb8..3c380e1a74334 100644 --- a/models/repo/update.go +++ b/models/repo/update.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" ) @@ -154,19 +155,12 @@ func ChangeRepositoryName(ctx context.Context, doer *user_model.User, repo *Repo return ErrRepoAlreadyExist{repo.Owner.Name, newRepoName} } - newRepoPath := RepoPath(repo.Owner.Name, newRepoName) - if err = util.Rename(repo.RepoPath(), newRepoPath); err != nil { - return fmt.Errorf("rename repository directory: %w", err) - } - - wikiPath := repo.WikiPath() - isExist, err := util.IsExist(wikiPath) - if err != nil { - log.Error("Unable to check if %s exists. Error: %v", wikiPath, err) + if err := gitrepo.RenameRepository(ctx, repo, newRepoName); err != nil { return err } - if isExist { - if err = util.Rename(wikiPath, WikiPath(repo.Owner.Name, newRepoName)); err != nil { + + if repo.HasWiki() { + if err = gitrepo.RenameWikiRepository(ctx, repo, newRepoName); err != nil { return fmt.Errorf("rename repository wiki: %w", err) } } diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go index d89f8f9c0c88c..9a38e91222cfd 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -5,12 +5,14 @@ package gitrepo import ( "context" + "fmt" "io" "path/filepath" "strings" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" ) type Repository interface { @@ -101,3 +103,23 @@ func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Rep gitRepo, err := git.OpenRepository(ctx, path) return gitRepo, gitRepo, err } + +func IsRepositoryExist(ctx context.Context, repo Repository) (bool, error) { + return util.IsExist(repoPath(repo)) +} + +func RenameRepository(ctx context.Context, repo Repository, newName string) error { + newRepoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(newName)+".git") + if err := util.Rename(repoPath(repo), newRepoPath); err != nil { + return fmt.Errorf("rename repository directory: %w", err) + } + return nil +} + +func RenameWikiRepository(ctx context.Context, repo Repository, newName string) error { + newWikiRepoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(newName)+".wiki.git") + if err := util.Rename(wikiPath(repo), newWikiRepoPath); err != nil { + return fmt.Errorf("rename repository wiki directory: %w", err) + } + return nil +} diff --git a/modules/repository/hooks.go b/modules/gitrepo/hooks.go similarity index 93% rename from modules/repository/hooks.go rename to modules/gitrepo/hooks.go index daab7c3091ebe..9f983247d40cd 100644 --- a/modules/repository/hooks.go +++ b/modules/gitrepo/hooks.go @@ -1,9 +1,10 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package repository +package gitrepo import ( + "context" "fmt" "os" "path/filepath" @@ -108,9 +109,14 @@ done } // CreateDelegateHooks creates all the hooks scripts for the repo -func CreateDelegateHooks(repoPath string) (err error) { +func CreateDelegateHooks(ctx context.Context, repo Repository, iswiki bool) (err error) { hookNames, hookTpls, giteaHookTpls := getHookTemplates() - hookDir := filepath.Join(repoPath, "hooks") + var hookDir string + if iswiki { + hookDir = filepath.Join(wikiPath(repo), "hooks") + } else { + hookDir = filepath.Join(repoPath(repo), "hooks") + } for i, hookName := range hookNames { oldHookPath := filepath.Join(hookDir, hookName) @@ -172,10 +178,15 @@ func ensureExecutable(filename string) error { } // CheckDelegateHooks checks the hooks scripts for the repo -func CheckDelegateHooks(repoPath string) ([]string, error) { +func CheckDelegateHooks(ctx context.Context, repo Repository, isWiki bool) ([]string, error) { hookNames, hookTpls, giteaHookTpls := getHookTemplates() - hookDir := filepath.Join(repoPath, "hooks") + var hookDir string + if isWiki { + hookDir = filepath.Join(wikiPath(repo), "hooks") + } else { + hookDir = filepath.Join(repoPath(repo), "hooks") + } results := make([]string, 0, 10) for i, hookName := range hookNames { diff --git a/modules/gitrepo/url.go b/modules/gitrepo/url.go new file mode 100644 index 0000000000000..83899c3f8775e --- /dev/null +++ b/modules/gitrepo/url.go @@ -0,0 +1,12 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +func RepoGitURL(repo Repository) string { + return repoPath(repo) +} + +func WikiRepoGitURL(repo Repository) string { + return repoPath(repo) +} diff --git a/modules/indexer/stats/db.go b/modules/indexer/stats/db.go index 98a977c7008fe..fd9dbdce6ff46 100644 --- a/modules/indexer/stats/db.go +++ b/modules/indexer/stats/db.go @@ -49,10 +49,10 @@ func (db *DBIndexer) Index(id int64) error { commitID, err := gitRepo.GetBranchCommitID(repo.DefaultBranch) if err != nil { if git.IsErrBranchNotExist(err) || git.IsErrNotExist(err) || setting.IsInTesting { - log.Debug("Unable to get commit ID for default branch %s in %s ... skipping this repository", repo.DefaultBranch, repo.RepoPath()) + log.Debug("Unable to get commit ID for default branch %s in %s ... skipping this repository", repo.DefaultBranch, gitrepo.RepoGitURL(repo)) return nil } - log.Error("Unable to get commit ID for default branch %s in %s. Error: %v", repo.DefaultBranch, repo.RepoPath(), err) + log.Error("Unable to get commit ID for default branch %s in %s. Error: %v", repo.DefaultBranch, gitrepo.RepoGitURL(repo), err) return err } @@ -65,17 +65,17 @@ func (db *DBIndexer) Index(id int64) error { stats, err := gitRepo.GetLanguageStats(commitID) if err != nil { if !setting.IsInTesting { - log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err) + log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, gitrepo.RepoGitURL(repo), err) } return err } err = repo_model.UpdateLanguageStats(ctx, repo, commitID, stats) if err != nil { - log.Error("Unable to update language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err) + log.Error("Unable to update language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, gitrepo.RepoGitURL(repo), err) return err } - log.Debug("DBIndexer completed language stats for ID %s for default branch %s in %s. stats count: %d", commitID, repo.DefaultBranch, repo.RepoPath(), len(stats)) + log.Debug("DBIndexer completed language stats for ID %s for default branch %s in %s. stats count: %d", commitID, repo.DefaultBranch, gitrepo.RepoGitURL(repo), len(stats)) return nil } diff --git a/modules/migration/pullrequest.go b/modules/migration/pullrequest.go index 4e7500f0d67f8..1435991bd2a8b 100644 --- a/modules/migration/pullrequest.go +++ b/modules/migration/pullrequest.go @@ -45,7 +45,7 @@ func (p *PullRequest) GetContext() DownloaderContext { return p.Context } // IsForkPullRequest returns true if the pull request from a forked repository but not the same repository func (p *PullRequest) IsForkPullRequest() bool { - return p.Head.RepoPath() != p.Base.RepoPath() + return p.Head.RepoFullName() != p.Base.RepoFullName() } // GetGitRefName returns pull request relative path to head @@ -62,8 +62,8 @@ type PullRequestBranch struct { OwnerName string `yaml:"owner_name"` } -// RepoPath returns pull request repo path -func (p PullRequestBranch) RepoPath() string { +// RepoFullName returns pull request repo full name +func (p PullRequestBranch) RepoFullName() string { return fmt.Sprintf("%s/%s", p.OwnerName, p.RepoName) } diff --git a/modules/repository/generate.go b/modules/repository/generate.go index b32c4e058e503..1d6cd50e42ff4 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -359,7 +359,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ } // FIXME - fix the hash - if err = CheckInitRepository(ctx, owner.Name, generateRepo.Name, git.Sha1ObjectFormat.Name()); err != nil { + if err = CheckInitRepository(ctx, generateRepo, git.Sha1ObjectFormat.Name()); err != nil { return generateRepo, err } diff --git a/modules/repository/init.go b/modules/repository/init.go index b90b234a73f80..9c8e003bcbc5f 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -16,6 +16,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/options" @@ -188,25 +189,24 @@ func InitRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi return nil } -func CheckInitRepository(ctx context.Context, owner, name, objectFormatName string) (err error) { +func CheckInitRepository(ctx context.Context, repo *repo_model.Repository, objectFormatName string) (err error) { // Somehow the directory could exist. - repoPath := repo_model.RepoPath(owner, name) - isExist, err := util.IsExist(repoPath) + isExist, err := gitrepo.IsRepositoryExist(ctx, repo) if err != nil { - log.Error("Unable to check if %s exists. Error: %v", repoPath, err) + log.Error("Unable to check if %s exists. Error: %v", repo.RepoPath(), err) return err } if isExist { return repo_model.ErrRepoFilesAlreadyExist{ - Uname: owner, - Name: name, + Uname: repo.OwnerName, + Name: repo.Name, } } // Init git bare new repository. - if err = git.InitRepository(ctx, repoPath, true, objectFormatName); err != nil { + if err = git.InitRepository(ctx, repo.RepoPath(), true, objectFormatName); err != nil { return fmt.Errorf("git.InitRepository: %w", err) - } else if err = CreateDelegateHooks(repoPath); err != nil { + } else if err = gitrepo.CreateDelegateHooks(ctx, repo, false); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) } return nil diff --git a/modules/repository/repo.go b/modules/repository/repo.go index fc3af040719ff..f9ea3ddc11996 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -262,11 +262,11 @@ func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error { // CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) { repoPath := repo.RepoPath() - if err := CreateDelegateHooks(repoPath); err != nil { + if err := gitrepo.CreateDelegateHooks(ctx, repo, false); err != nil { return repo, fmt.Errorf("createDelegateHooks: %w", err) } if repo.HasWiki() { - if err := CreateDelegateHooks(repo.WikiPath()); err != nil { + if err := gitrepo.CreateDelegateHooks(ctx, repo, true); err != nil { return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err) } } diff --git a/services/doctor/misc.go b/services/doctor/misc.go index 9300c3a25c9c6..08cd71dce3145 100644 --- a/services/doctor/misc.go +++ b/services/doctor/misc.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -50,14 +49,14 @@ func checkScriptType(ctx context.Context, logger log.Logger, autofix bool) error func checkHooks(ctx context.Context, logger log.Logger, autofix bool) error { if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { - results, err := repository.CheckDelegateHooks(repo.RepoPath()) + results, err := gitrepo.CheckDelegateHooks(ctx, repo, false) if err != nil { logger.Critical("Unable to check delegate hooks for repo %-v. ERROR: %v", repo, err) return fmt.Errorf("Unable to check delegate hooks for repo %-v. ERROR: %w", repo, err) } if len(results) > 0 && autofix { - logger.Warn("Regenerated hooks for %s", repo.FullName()) - if err := repository.CreateDelegateHooks(repo.RepoPath()); err != nil { + logger.Warn("Regenerated hooks for %s", gitrepo.RepoGitURL(repo)) + if err := gitrepo.CreateDelegateHooks(ctx, repo, false); err != nil { logger.Critical("Unable to recreate delegate hooks for %-v. ERROR: %v", repo, err) return fmt.Errorf("Unable to recreate delegate hooks for %-v. ERROR: %w", repo, err) } diff --git a/services/repository/adopt.go b/services/repository/adopt.go index bfb965063f33e..29517114a27ee 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -79,7 +79,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR return fmt.Errorf("getRepositoryByID: %w", err) } - if err := adoptRepository(ctx, repoPath, doer, repo, opts.DefaultBranch); err != nil { + if err := adoptRepository(ctx, doer, repo, opts.DefaultBranch); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) } @@ -110,17 +110,17 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR return repo, nil } -func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, defaultBranch string) (err error) { - isExist, err := util.IsExist(repoPath) +func adoptRepository(ctx context.Context, u *user_model.User, repo *repo_model.Repository, defaultBranch string) (err error) { + isExist, err := gitrepo.IsRepositoryExist(ctx, repo) if err != nil { - log.Error("Unable to check if %s exists. Error: %v", repoPath, err) + log.Error("Unable to check if %s exists. Error: %v", gitrepo.RepoGitURL(repo), err) return err } if !isExist { - return fmt.Errorf("adoptRepository: path does not already exist: %s", repoPath) + return fmt.Errorf("adoptRepository: path does not already exist: %s", gitrepo.RepoGitURL(repo)) } - if err := repo_module.CreateDelegateHooks(repoPath); err != nil { + if err := gitrepo.CreateDelegateHooks(ctx, repo, false); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) } diff --git a/services/repository/create.go b/services/repository/create.go index a648c0d816787..3d261b3d919c9 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -136,7 +136,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, // InitRepository initializes README and .gitignore if needed. func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) { - if err = repo_module.CheckInitRepository(ctx, repo.OwnerName, repo.Name, opts.ObjectFormatName); err != nil { + if err = repo_module.CheckInitRepository(ctx, repo, opts.ObjectFormatName); err != nil { return err } diff --git a/services/repository/delete.go b/services/repository/delete.go index 08d6800ee7660..426b4dbead8e0 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -24,6 +24,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" actions_module "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" @@ -286,12 +287,11 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID // we delete the file but the database rollback, the repository will be broken. // Remove repository files. - repoPath := repo.RepoPath() - system_model.RemoveAllWithNotice(ctx, "Delete repository files", repoPath) + system_model.RemoveAllWithNotice(ctx, "Delete repository files", gitrepo.RepoGitURL(repo)) // Remove wiki files if repo.HasWiki() { - system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath()) + system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", gitrepo.WikiRepoGitURL(repo)) } // Remove archives diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go index 16a15e06a7ccc..8634f7e9fc939 100644 --- a/services/repository/files/commit.go +++ b/services/repository/files/commit.go @@ -21,18 +21,16 @@ import ( // NOTE: All text-values will be trimmed from whitespaces. // Requires: Repo, Creator, SHA func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creator *user_model.User, sha string, status *git_model.CommitStatus) error { - repoPath := repo.RepoPath() - // confirm that commit is exist gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) if err != nil { - return fmt.Errorf("OpenRepository[%s]: %w", repoPath, err) + return fmt.Errorf("OpenRepository[%s]: %w", gitrepo.RepoGitURL(repo), err) } defer closer.Close() objectFormat, err := gitRepo.GetObjectFormat() if err != nil { - return fmt.Errorf("GetObjectFormat[%s]: %w", repoPath, err) + return fmt.Errorf("GetObjectFormat[%s]: %w", gitrepo.RepoGitURL(repo), err) } commit, err := gitRepo.GetCommit(sha) if err != nil { diff --git a/services/repository/fork.go b/services/repository/fork.go index f9c13a109eba7..72b94bbf5fa3a 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -164,7 +164,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork return fmt.Errorf("git update-server-info: %w", err) } - if err = repo_module.CreateDelegateHooks(repoPath); err != nil { + if err = gitrepo.CreateDelegateHooks(ctx, repo, false); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) } diff --git a/services/repository/hooks.go b/services/repository/hooks.go index 97e9e290a3e3d..97631124b4f6a 100644 --- a/services/repository/hooks.go +++ b/services/repository/hooks.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" - repo_module "code.gitea.io/gitea/modules/repository" "xorm.io/builder" ) @@ -32,11 +31,11 @@ func SyncRepositoryHooks(ctx context.Context) error { default: } - if err := repo_module.CreateDelegateHooks(repo.RepoPath()); err != nil { + if err := gitrepo.CreateDelegateHooks(ctx, repo, false); err != nil { return fmt.Errorf("SyncRepositoryHook: %w", err) } if repo.HasWiki() { - if err := repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { + if err := gitrepo.CreateDelegateHooks(ctx, repo, true); err != nil { return fmt.Errorf("SyncRepositoryHook: %w", err) } } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 50d52d3140fe8..4d4d61a62486b 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/sync" @@ -39,7 +40,7 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error { if err := git.InitRepository(ctx, repo.WikiPath(), true, repo.ObjectFormatName); err != nil { return fmt.Errorf("InitRepository: %w", err) - } else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { + } else if err = gitrepo.CreateDelegateHooks(ctx, repo, true); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) } else if _, _, err = git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil { return fmt.Errorf("unable to set default wiki branch to master: %w", err) From dbbb73c4d6e3901cd879b8e7577ab03e7ce23150 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 14:18:06 +0800 Subject: [PATCH 06/17] Merge repo and wiki functions --- modules/gitrepo/command.go | 37 +++------- modules/gitrepo/gitrepo.go | 7 ++ modules/gitrepo/url.go | 9 ++- modules/indexer/code/bleve/bleve.go | 2 +- .../code/elasticsearch/elasticsearch.go | 2 +- modules/indexer/code/git.go | 10 +-- modules/repository/generate.go | 2 +- routers/private/hook_pre_receive.go | 4 +- routers/web/repo/pull.go | 2 +- services/agit/agit.go | 2 +- services/doctor/heads.go | 6 +- services/doctor/mergebase.go | 16 ++-- services/migrations/gitea_uploader.go | 12 ++- services/migrations/gitea_uploader_test.go | 6 +- services/mirror/mirror_pull.go | 73 +++++++++++-------- services/mirror/mirror_push.go | 16 ++-- services/pull/check.go | 4 +- services/pull/pull.go | 2 +- services/release/release.go | 2 +- services/repository/check.go | 4 +- tests/integration/api_repo_git_tags_test.go | 4 +- tests/integration/pull_merge_test.go | 26 ++++--- 22 files changed, 137 insertions(+), 111 deletions(-) diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go index 8abcb2cf9709d..7fd2d25e4b6fd 100644 --- a/modules/gitrepo/command.go +++ b/modules/gitrepo/command.go @@ -7,52 +7,37 @@ import ( "code.gitea.io/gitea/modules/git" ) -// RunGitCmdStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). -func RunGitCmdStdString(repo Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError) { - if opts.Dir != "" { - // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug - panic("dir field must be empty when using RunStdBytes") - } - opts.Dir = repoPath(repo) - return c.RunStdString(opts) +type RunOpts struct { + git.RunOpts + IsWiki bool } // RunGitCmdStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). -func RunGitCmdStdStringWiki(repo Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError) { +func RunGitCmdStdString(repo Repository, c *git.Command, opts *RunOpts) (stdout, stderr string, runErr git.RunStdError) { if opts.Dir != "" { // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug panic("dir field must be empty when using RunStdBytes") } - opts.Dir = wikiPath(repo) - return c.RunStdString(opts) + opts.Dir = getPath(repo, opts.IsWiki) + return c.RunStdString(&opts.RunOpts) } // RunGitCmdStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr). -func RunGitCmdStdBytes(repo Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr []byte, runErr git.RunStdError) { +func RunGitCmdStdBytes(repo Repository, c *git.Command, opts *RunOpts) (stdout, stderr []byte, runErr git.RunStdError) { if opts.Dir != "" { // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug panic("dir field must be empty when using RunStdBytes") } opts.Dir = repoPath(repo) - return c.RunStdBytes(opts) + return c.RunStdBytes(&opts.RunOpts) } // RunGitCmd runs the command with the RunOpts -func RunGitCmd(repo Repository, c *git.Command, opts *git.RunOpts) error { - if opts.Dir != "" { - // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug - panic("dir field must be empty when using RunStdBytes") - } - opts.Dir = repoPath(repo) - return c.Run(opts) -} - -// RunGitCmdWiki runs the command with the RunOpts -func RunGitCmdWiki(repo Repository, c *git.Command, opts *git.RunOpts) error { +func RunGitCmd(repo Repository, c *git.Command, opts *RunOpts) error { if opts.Dir != "" { // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug panic("dir field must be empty when using RunStdBytes") } - opts.Dir = repoPath(repo) - return c.Run(opts) + opts.Dir = getPath(repo, opts.IsWiki) + return c.Run(&opts.RunOpts) } diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go index d89f8f9c0c88c..200317f74654a 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -26,6 +26,13 @@ func wikiPath(repo Repository) string { return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git") } +func getPath(repo Repository, isWiki bool) string { + if isWiki { + return wikiPath(repo) + } + return repoPath(repo) +} + // OpenRepository opens the repository at the given relative path with the provided context. func OpenRepository(ctx context.Context, repo Repository) (*git.Repository, error) { return git.OpenRepository(ctx, repoPath(repo)) diff --git a/modules/gitrepo/url.go b/modules/gitrepo/url.go index 83899c3f8775e..f5b09dce4f3e1 100644 --- a/modules/gitrepo/url.go +++ b/modules/gitrepo/url.go @@ -8,5 +8,12 @@ func RepoGitURL(repo Repository) string { } func WikiRepoGitURL(repo Repository) string { - return repoPath(repo) + return wikiPath(repo) +} + +func GetRepoOrWikiGitURL(repo Repository, isWiki bool) string { + if isWiki { + return WikiRepoGitURL(repo) + } + return RepoGitURL(repo) } diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index 339678d5a64fe..44aaf5e595510 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -139,7 +139,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro if !update.Sized { var stdout string cmd := git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha) - stdout, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) + stdout, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil { return err } diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index 490a85342e703..06d446c8f48a7 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -96,7 +96,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro if !update.Sized { var stdout string cmd := git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha) - stdout, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) + stdout, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil { return nil, err } diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index cad43ee400be2..9be507369d381 100644 --- a/modules/indexer/code/git.go +++ b/modules/indexer/code/git.go @@ -18,7 +18,7 @@ import ( func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) { cmd := git.NewCommand(ctx, "show-ref", "-s").AddDynamicArguments(git.BranchPrefix + repo.DefaultBranch) - stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) + stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil { return "", err } @@ -35,7 +35,7 @@ func getRepoChanges(ctx context.Context, repo *repo_model.Repository, revision s needGenesis := len(status.CommitSha) == 0 if !needGenesis { hasAncestorCmd := git.NewCommand(ctx, "merge-base").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) - stdout, _, _ := gitrepo.RunGitCmdStdString(repo, hasAncestorCmd, &git.RunOpts{}) + stdout, _, _ := gitrepo.RunGitCmdStdString(repo, hasAncestorCmd, &gitrepo.RunOpts{}) needGenesis = len(stdout) == 0 } @@ -89,7 +89,7 @@ func parseGitLsTreeOutput(objectFormat git.ObjectFormat, stdout []byte) ([]inter func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { var changes internal.RepoChanges cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision) - stdout, _, runErr := gitrepo.RunGitCmdStdBytes(repo, cmd, &git.RunOpts{}) + stdout, _, runErr := gitrepo.RunGitCmdStdBytes(repo, cmd, &gitrepo.RunOpts{}) if runErr != nil { return nil, runErr } @@ -106,7 +106,7 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s // nonGenesisChanges get changes since the previous indexer update func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { diffCmd := git.NewCommand(ctx, "diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) - stdout, _, runErr := gitrepo.RunGitCmdStdString(repo, diffCmd, &git.RunOpts{}) + stdout, _, runErr := gitrepo.RunGitCmdStdString(repo, diffCmd, &gitrepo.RunOpts{}) if runErr != nil { // previous commit sha may have been removed by a force push, so // try rebuilding from scratch @@ -172,7 +172,7 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l").AddDynamicArguments(revision). AddDashesAndList(updatedFilenames...) - lsTreeStdout, _, err := gitrepo.RunGitCmdStdBytes(repo, cmd, &git.RunOpts{}) + lsTreeStdout, _, err := gitrepo.RunGitCmdStdBytes(repo, cmd, &gitrepo.RunOpts{}) if err != nil { return nil, err } diff --git a/modules/repository/generate.go b/modules/repository/generate.go index 274a1c9116076..776da76a1c9d1 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -369,7 +369,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ cmd := git.NewCommand(ctx, "update-server-info"). SetDescription(fmt.Sprintf("GenerateRepository(git update-server-info): %s", repoPath)) - if stdout, _, err := gitrepo.RunGitCmdStdString(generateRepo, cmd, &git.RunOpts{}); err != nil { + if stdout, _, err := gitrepo.RunGitCmdStdString(generateRepo, cmd, &gitrepo.RunOpts{}); err != nil { log.Error("GenerateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", generateRepo, stdout, err) return generateRepo, fmt.Errorf("error in GenerateRepository(git update-server-info): %w", err) } diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 8be597bd8e1cc..d4a947a461500 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -187,7 +187,9 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r // 2. Disallow force pushes to protected branches if oldCommitID != objectFormat.EmptyObjectID().String() { cmd := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID) - output, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{Env: ctx.env}) + output, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{Env: ctx.env}, + }) if err != nil { log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err) ctx.JSON(http.StatusInternalServerError, private.Response{ diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 1a26d09064079..22b44740f625e 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -440,7 +440,7 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri if commitSHA != "" { // Get immediate parent of the first commit in the patch, grab history back cmd := git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA) - parentCommit, _, err = gitrepo.RunGitCmdStdString(ctx.Repo.Repository, cmd, &git.RunOpts{}) + parentCommit, _, err = gitrepo.RunGitCmdStdString(ctx.Repo.Repository, cmd, &gitrepo.RunOpts{}) if err == nil { parentCommit = strings.TrimSpace(parentCommit) } diff --git a/services/agit/agit.go b/services/agit/agit.go index 296162bd8e49d..9859e90249497 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -182,7 +182,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. if !forcePush { cmd := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]) - output, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{Env: os.Environ()}) + output, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{RunOpts: git.RunOpts{Env: os.Environ()}}) if err != nil { return nil, fmt.Errorf("Fail to detect force push: %w", err) } else if len(output) > 0 { diff --git a/services/doctor/heads.go b/services/doctor/heads.go index 19e9163d25814..223e7816baab8 100644 --- a/services/doctor/heads.go +++ b/services/doctor/heads.go @@ -20,10 +20,10 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { numRepos++ cmd := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch) - _, _, defaultBranchErr := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) + _, _, defaultBranchErr := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) cmd = git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD") - head, _, headErr := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) + head, _, headErr := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) // what we expect: default branch is valid, and HEAD points to it if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch { @@ -50,7 +50,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) // otherwise, let's try fixing HEAD cmd = git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch) - err := gitrepo.RunGitCmd(repo, cmd, &git.RunOpts{}) + err := gitrepo.RunGitCmd(repo, cmd, &gitrepo.RunOpts{}) if err != nil { logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) return nil diff --git a/services/doctor/mergebase.go b/services/doctor/mergebase.go index de460c419051a..d9c8b1b86dce7 100644 --- a/services/doctor/mergebase.go +++ b/services/doctor/mergebase.go @@ -12,6 +12,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "xorm.io/builder" @@ -36,23 +37,24 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro return iteratePRs(ctx, repo, func(repo *repo_model.Repository, pr *issues_model.PullRequest) error { numPRs++ pr.BaseRepo = repo - repoPath := repo.RepoPath() - oldMergeBase := pr.MergeBase if !pr.HasMerged { var err error - pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitRefName()).RunStdString(&git.RunOpts{Dir: repoPath}) + cmd := git.NewCommand(ctx, "merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitRefName()) + pr.MergeBase, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil { var err2 error - pr.MergeBase, _, err2 = git.NewCommand(ctx, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath}) + cmd = git.NewCommand(ctx, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch) + pr.MergeBase, _, err2 = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err2 != nil { logger.Warn("Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2) return nil } } } else { - parentsString, _, err := git.NewCommand(ctx, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath}) + cmd := git.NewCommand(ctx, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID) + parentsString, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil { logger.Warn("Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err) return nil @@ -64,8 +66,8 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro refs := append([]string{}, parents[1:]...) refs = append(refs, pr.GetGitRefName()) - cmd := git.NewCommand(ctx, "merge-base").AddDashesAndList(refs...) - pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath}) + cmd = git.NewCommand(ctx, "merge-base").AddDashesAndList(refs...) + pr.MergeBase, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil { logger.Warn("Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err) return nil diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 9734134d0a727..0489cb7e22d18 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -119,6 +119,10 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate } r.DefaultBranch = repo.DefaultBranch r.Description = repo.Description + if r.OwnerID != owner.ID { + return fmt.Errorf("owner id mismatch: %d != %d", r.OwnerID, owner.ID) + } + r.Owner = owner r, err = repo_module.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{ RepoName: g.repoName, @@ -642,7 +646,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head } cmd := git.NewCommand(g.ctx, "fetch", "--no-tags").AddDashesAndList(remote, fetchArg) - _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &gitrepo.RunOpts{}) if err != nil { log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) return head, nil @@ -662,7 +666,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head } cmd := git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA) - _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &gitrepo.RunOpts{}) if err != nil { return "", err } @@ -680,14 +684,14 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName) } else { cmd := git.NewCommand(g.ctx, "rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA) - _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &gitrepo.RunOpts{}) if err != nil { // Git update-ref remove bad references with a relative path log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitRefName()) } else { // set head information cmd := git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA) - _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(g.repo, cmd, &gitrepo.RunOpts{}) if err != nil { log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) } diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index d0007f1f0cdef..2cc6ecff80ddd 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -237,7 +237,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { baseRef := "master" assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false, fromRepo.ObjectFormatName)) cmd := git.NewCommand(git.DefaultContext, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef) - err := gitrepo.RunGitCmd(fromRepo, cmd, &git.RunOpts{}) + err := gitrepo.RunGitCmd(fromRepo, cmd, &gitrepo.RunOpts{}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", fromRepo.RepoPath())), 0o644)) assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true)) @@ -262,7 +262,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { // headRef := "branch1" cmd = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(headRef) - _, _, err = gitrepo.RunGitCmdStdString(fromRepo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(fromRepo, cmd, &gitrepo.RunOpts{}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("SOMETHING"), 0o644)) assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true)) @@ -287,7 +287,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { Branch: headRef, })) cmd = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(forkHeadRef) - _, _, err = gitrepo.RunGitCmdStdString(forkRepo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(forkRepo, cmd, &gitrepo.RunOpts{}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# branch2 %s", forkRepo.RepoPath())), 0o644)) assert.NoError(t, git.AddChanges(forkRepo.RepoPath(), true)) diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 4442c7b5dbb30..d988c1047160b 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -35,7 +35,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error // Remove old remote cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName) - _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) + _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -46,7 +46,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } else { cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, gitrepo.RepoGitURL(repo))) } - _, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -55,7 +55,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) // Remove old remote of wiki cmd = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName) - _, _, err = gitrepo.RunGitCmdStdStringWiki(repo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: true}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -66,7 +66,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } else { cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, gitrepo.WikiRepoGitURL(repo))) } - _, _, err = gitrepo.RunGitCmdStdStringWiki(repo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: true}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -177,26 +177,29 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult { func pruneBrokenReferences(ctx context.Context, m *repo_model.Mirror, - repoPath string, + repo *repo_model.Repository, timeout time.Duration, stdoutBuilder, stderrBuilder *strings.Builder, isWiki bool, ) error { wiki := "" + url := gitrepo.GetRepoOrWikiGitURL(m.Repo, isWiki) if isWiki { wiki = "Wiki " } stderrBuilder.Reset() stdoutBuilder.Reset() - pruneErr := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()). - SetDescription(fmt.Sprintf("Mirror.runSync %ssPrune references: %s ", wiki, gitrepo.RepoGitURL(m.Repo))). - Run(&git.RunOpts{ + cmd := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()). + SetDescription(fmt.Sprintf("Mirror.runSync %ssPrune references: %s ", wiki, url)) + pruneErr := gitrepo.RunGitCmd(repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ Timeout: timeout, - Dir: repoPath, Stdout: stdoutBuilder, Stderr: stderrBuilder, - }) + }, + IsWiki: isWiki, + }) if pruneErr != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -207,7 +210,7 @@ func pruneBrokenReferences(ctx context.Context, stdoutMessage := util.SanitizeCredentialURLs(stdout) log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr) - desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage) + desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, url, stderrMessage) if err := system_model.CreateRepositoryNotice(desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } @@ -242,11 +245,13 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutBuilder := strings.Builder{} stderrBuilder := strings.Builder{} cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", gitrepo.RepoGitURL(m.Repo))) - if err := gitrepo.RunGitCmd(m.Repo, cmd, &git.RunOpts{ - Timeout: timeout, - Env: envs, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, + if err := gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Timeout: timeout, + Env: envs, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }, }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -261,16 +266,18 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo err = nil // Attempt prune - pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, false) + pruneErr := pruneBrokenReferences(ctx, m, m.Repo, timeout, &stdoutBuilder, &stderrBuilder, false) if pruneErr == nil { // Successful prune - reattempt mirror stderrBuilder.Reset() stdoutBuilder.Reset() cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", gitrepo.RepoGitURL(m.Repo))) - if err = gitrepo.RunGitCmd(m.Repo, cmd, &git.RunOpts{ - Timeout: timeout, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, + if err = gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Timeout: timeout, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }, }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -336,10 +343,13 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutBuilder.Reset() cmd := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", gitrepo.WikiRepoGitURL(m.Repo))) - if err := gitrepo.RunGitCmdWiki(m.Repo, cmd, &git.RunOpts{ - Timeout: timeout, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, + if err := gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Timeout: timeout, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }, + IsWiki: true, }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -354,7 +364,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo err = nil // Attempt prune - pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, true) + pruneErr := pruneBrokenReferences(ctx, m, m.Repo, timeout, &stdoutBuilder, &stderrBuilder, true) if pruneErr == nil { // Successful prune - reattempt mirror stderrBuilder.Reset() @@ -362,10 +372,13 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", gitrepo.WikiRepoGitURL(m.Repo))) - if err = gitrepo.RunGitCmdWiki(m.Repo, cmd, &git.RunOpts{ - Timeout: timeout, - Stdout: &stdoutBuilder, - Stderr: &stderrBuilder, + if err = gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Timeout: timeout, + Stdout: &stdoutBuilder, + Stderr: &stderrBuilder, + }, + IsWiki: true, }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index f240a97d4a258..c65e44611b6d4 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -29,7 +29,7 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) // AddPushMirrorRemote registers the push mirror remote. func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { - addRemoteAndConfig := func(addr string, repo gitrepo.Repository, isWiki bool, runStdString func(repo gitrepo.Repository, c *git.Command, opts *git.RunOpts) (stdout, stderr string, runErr git.RunStdError)) error { + addRemoteAndConfig := func(addr string, repo gitrepo.Repository, isWiki bool) error { cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) url := gitrepo.RepoGitURL(repo) if isWiki { @@ -40,28 +40,28 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str } else { cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, url)) } - if _, _, err := runStdString(repo, cmd, &git.RunOpts{}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: isWiki}); err != nil { return err } cmd = git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*") - if _, _, err := runStdString(repo, cmd, &git.RunOpts{}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: isWiki}); err != nil { return err } cmd = git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*") - if _, _, err := runStdString(repo, cmd, &git.RunOpts{}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: isWiki}); err != nil { return err } return nil } - if err := addRemoteAndConfig(addr, m.Repo, false, gitrepo.RunGitCmdStdString); err != nil { + if err := addRemoteAndConfig(addr, m.Repo, false); err != nil { return err } if m.Repo.HasWiki() { wikiRemoteURL := repository.WikiRemoteURL(ctx, addr) if len(wikiRemoteURL) > 0 { - if err := addRemoteAndConfig(wikiRemoteURL, m.Repo, true, gitrepo.RunGitCmdStdStringWiki); err != nil { + if err := addRemoteAndConfig(wikiRemoteURL, m.Repo, true); err != nil { return err } } @@ -75,12 +75,12 @@ func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName) _ = m.GetRepository(ctx) - if _, _, err := gitrepo.RunGitCmdStdString(m.Repo, cmd, &git.RunOpts{}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(m.Repo, cmd, &gitrepo.RunOpts{}); err != nil { return err } if m.Repo.HasWiki() { - if _, _, err := gitrepo.RunGitCmdStdStringWiki(m.Repo, cmd, &git.RunOpts{}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(m.Repo, cmd, &gitrepo.RunOpts{IsWiki: true}); err != nil { // The wiki remote may not exist log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err) } diff --git a/services/pull/check.go b/services/pull/check.go index 81b3a92c94a57..090d63a89c7d3 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -198,7 +198,7 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com // Check if the pull request is merged into BaseBranch cmd := git.NewCommand(ctx, "merge-base", "--is-ancestor").AddDynamicArguments(prHeadRef, pr.BaseBranch) - if _, _, err := gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &git.RunOpts{}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &gitrepo.RunOpts{}); err != nil { if strings.Contains(err.Error(), "exit status 1") { // prHeadRef is not an ancestor of the base branch return nil, nil @@ -229,7 +229,7 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com // Get the commit from BaseBranch where the pull request got merged cmd = git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse"). AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch) - mergeCommit, _, err := gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &git.RunOpts{}) + mergeCommit, _, err := gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &gitrepo.RunOpts{}) if err != nil { return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err) } else if len(mergeCommit) < objectFormat.FullLength() { diff --git a/services/pull/pull.go b/services/pull/pull.go index 9056f309ffd4c..499ab75882f98 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -524,7 +524,7 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) { } cmd := git.NewCommand(ctx, "update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID) - _, _, err = gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(pr.BaseRepo, cmd, &gitrepo.RunOpts{}) if err != nil { log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err) } diff --git a/services/release/release.go b/services/release/release.go index 392cd50eb6208..4c5b546be0c4b 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -322,7 +322,7 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re cmd := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName). SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)) - if stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &git.RunOpts{}); err != nil && !strings.Contains(err.Error(), "not found") { + if stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}); err != nil && !strings.Contains(err.Error(), "not found") { log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err) return fmt.Errorf("git tag -d: %w", err) } diff --git a/services/repository/check.go b/services/repository/check.go index f4a4662f1e120..60af95706c4a6 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -91,7 +91,9 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())) var stdout string var err error - stdout, _, err = gitrepo.RunGitCmdStdString(repo, command, &git.RunOpts{Timeout: timeout}) + stdout, _, err = gitrepo.RunGitCmdStdString(repo, command, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{Timeout: timeout}, + }) if err != nil { log.Error("Repository garbage collection failed for %-v. Stdout: %s\nError: %v", repo, stdout, err) diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go index 9ab4594e5779f..7f3120771ac9d 100644 --- a/tests/integration/api_repo_git_tags_test.go +++ b/tests/integration/api_repo_git_tags_test.go @@ -31,9 +31,9 @@ func TestAPIGitTags(t *testing.T) { // Set up git config for the tagger cmd := git.NewCommand(git.DefaultContext, "config", "user.name").AddDynamicArguments(user.Name) - _ = gitrepo.RunGitCmd(repo, cmd, &git.RunOpts{}) + _ = gitrepo.RunGitCmd(repo, cmd, &gitrepo.RunOpts{}) cmd = git.NewCommand(git.DefaultContext, "config", "user.email").AddDynamicArguments(user.Email) - _ = gitrepo.RunGitCmd(repo, cmd, &git.RunOpts{}) + _ = gitrepo.RunGitCmd(repo, cmd, &gitrepo.RunOpts{}) gitRepo, _ := gitrepo.OpenRepository(git.DefaultContext, repo) defer gitRepo.Close() diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 9fe215d03820b..8371f83a515f4 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -287,26 +287,28 @@ func TestCantMergeUnrelated(t *testing.T) { }) cmd := git.NewCommand(git.DefaultContext, "read-tree", "--empty") - err := gitrepo.RunGitCmd(repo1, cmd, &git.RunOpts{}) + err := gitrepo.RunGitCmd(repo1, cmd, &gitrepo.RunOpts{}) assert.NoError(t, err) stdin := bytes.NewBufferString("Unrelated File") var stdout strings.Builder cmd = git.NewCommand(git.DefaultContext, "hash-object", "-w", "--stdin") - err = gitrepo.RunGitCmd(repo1, cmd, &git.RunOpts{ - Stdin: stdin, - Stdout: &stdout, + err = gitrepo.RunGitCmd(repo1, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Stdin: stdin, + Stdout: &stdout, + }, }) assert.NoError(t, err) sha := strings.TrimSpace(stdout.String()) cmd = git.NewCommand(git.DefaultContext, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments("100644", sha, "somewher-over-the-rainbow") - _, _, err = gitrepo.RunGitCmdStdString(repo1, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(repo1, cmd, &gitrepo.RunOpts{}) assert.NoError(t, err) cmd = git.NewCommand(git.DefaultContext, "write-tree") - treeSha, _, err := gitrepo.RunGitCmdStdString(repo1, cmd, &git.RunOpts{}) + treeSha, _, err := gitrepo.RunGitCmdStdString(repo1, cmd, &gitrepo.RunOpts{}) assert.NoError(t, err) treeSha = strings.TrimSpace(treeSha) @@ -328,16 +330,18 @@ func TestCantMergeUnrelated(t *testing.T) { stdout.Reset() cmd = git.NewCommand(git.DefaultContext, "commit-tree").AddDynamicArguments(treeSha) - err = gitrepo.RunGitCmd(repo1, cmd, &git.RunOpts{ - Env: env, - Stdin: messageBytes, - Stdout: &stdout, + err = gitrepo.RunGitCmd(repo1, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Env: env, + Stdin: messageBytes, + Stdout: &stdout, + }, }) assert.NoError(t, err) commitSha := strings.TrimSpace(stdout.String()) cmd = git.NewCommand(git.DefaultContext, "branch", "unrelated").AddDynamicArguments(commitSha) - _, _, err = gitrepo.RunGitCmdStdString(repo1, cmd, &git.RunOpts{}) + _, _, err = gitrepo.RunGitCmdStdString(repo1, cmd, &gitrepo.RunOpts{}) assert.NoError(t, err) testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") From d9cc981e0a74dc3e710bdd4fe7ca992ba5a827bb Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 14:53:52 +0800 Subject: [PATCH 07/17] Move more git command run to gitrepo package --- modules/repository/repo.go | 3 ++- services/doctor/misc.go | 11 ++++------- services/repository/adopt.go | 6 +++--- services/repository/create.go | 6 +++--- services/repository/fork.go | 6 +++--- services/wiki/wiki.go | 5 ++++- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/modules/repository/repo.go b/modules/repository/repo.go index fc3af040719ff..64973e6c2dcba 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -271,7 +271,8 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo } } - _, _, err := git.NewCommand(ctx, "remote", "rm", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) + cmd := git.NewCommand(ctx, "remote", "rm", "origin") + _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err) } diff --git a/services/doctor/misc.go b/services/doctor/misc.go index 9300c3a25c9c6..fff6c3895bfea 100644 --- a/services/doctor/misc.go +++ b/services/doctor/misc.go @@ -92,18 +92,15 @@ func checkEnablePushOptions(ctx context.Context, logger log.Logger, autofix bool if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { numRepos++ - r, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return err - } - defer r.Close() if autofix { - _, _, err := git.NewCommand(ctx, "config", "receive.advertisePushOptions", "true").RunStdString(&git.RunOpts{Dir: r.Path}) + cmd := git.NewCommand(ctx, "config", "receive.advertisePushOptions", "true") + _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) return err } - value, _, err := git.NewCommand(ctx, "config", "receive.advertisePushOptions").RunStdString(&git.RunOpts{Dir: r.Path}) + cmd := git.NewCommand(ctx, "config", "receive.advertisePushOptions") + value, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}) if err != nil { return err } diff --git a/services/repository/adopt.go b/services/repository/adopt.go index bfb965063f33e..e4ed649c1ff3f 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -94,9 +94,9 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR } } - if stdout, _, err := git.NewCommand(ctx, "update-server-info"). - SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + cmd := git.NewCommand(ctx, "update-server-info"). + SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", gitrepo.RepoGitURL(repo))) + if stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}); err != nil { log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) return fmt.Errorf("CreateRepository(git update-server-info): %w", err) } diff --git a/services/repository/create.go b/services/repository/create.go index a648c0d816787..86956e97e67c6 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -298,9 +298,9 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt return fmt.Errorf("checkDaemonExportOK: %w", err) } - if stdout, _, err := git.NewCommand(ctx, "update-server-info"). - SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + cmd := git.NewCommand(ctx, "update-server-info"). + SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", gitrepo.RepoGitURL(repo))) + if stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}); err != nil { log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) rollbackRepo = repo rollbackRepo.OwnerID = u.ID diff --git a/services/repository/fork.go b/services/repository/fork.go index f9c13a109eba7..f8c8a17f0a0c9 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -157,9 +157,9 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork return fmt.Errorf("checkDaemonExportOK: %w", err) } - if stdout, _, err := git.NewCommand(txCtx, "update-server-info"). - SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + cmd := git.NewCommand(txCtx, "update-server-info"). + SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", gitrepo.RepoGitURL(repo))) + if stdout, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{}); 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: %w", err) } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 50d52d3140fe8..25a75fd305cd2 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/sync" @@ -41,7 +42,9 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error { return fmt.Errorf("InitRepository: %w", err) } else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) - } else if _, _, err = git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil { + } + cmd := git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+DefaultBranch) + if _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: true}); err != nil { return fmt.Errorf("unable to set default wiki branch to master: %w", err) } return nil From a2f49e49220230de871b35f30bb67e754d882e25 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 15:42:59 +0800 Subject: [PATCH 08/17] Fix command --- modules/gitrepo/command.go | 2 +- modules/repository/repo.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go index 7fd2d25e4b6fd..83097d3255b83 100644 --- a/modules/gitrepo/command.go +++ b/modules/gitrepo/command.go @@ -28,7 +28,7 @@ func RunGitCmdStdBytes(repo Repository, c *git.Command, opts *RunOpts) (stdout, // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug panic("dir field must be empty when using RunStdBytes") } - opts.Dir = repoPath(repo) + opts.Dir = getPath(repo, opts.IsWiki) return c.RunStdBytes(&opts.RunOpts) } diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 64973e6c2dcba..19a36b899aaf0 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -245,13 +245,13 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, return repo, committer.Commit() } -// cleanUpMigrateGitConfig removes mirror info which prevents "push --all". +// cleanUpMigrateGitConfigWiki removes mirror info which prevents "push --all". // This also removes possible user credentials. -func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error { +func cleanUpMigrateGitConfigWiki(ctx context.Context, repo *repo_model.Repository) error { cmd := git.NewCommand(ctx, "remote", "rm", "origin") // if the origin does not exist - _, stderr, err := cmd.RunStdString(&git.RunOpts{ - Dir: repoPath, + _, stderr, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{ + IsWiki: true, }) if err != nil && !strings.HasPrefix(stderr, "fatal: No such remote") { return err @@ -278,7 +278,7 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo } if repo.HasWiki() { - if err := cleanUpMigrateGitConfig(ctx, repo.WikiPath()); err != nil { + if err := cleanUpMigrateGitConfigWiki(ctx, repo); err != nil { return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err) } } From 46a28b0821fd3aaf78357025082df9a844e89cf2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 16:13:05 +0800 Subject: [PATCH 09/17] More refactoring --- modules/context/repo.go | 2 +- routers/api/v1/misc/signing.go | 8 ++--- routers/web/repo/githttp.go | 50 +++++++++++++------------- services/asymkey/sign.go | 7 ++-- services/repository/files/patch.go | 2 +- services/repository/files/temp_repo.go | 2 +- services/repository/files/update.go | 2 +- 7 files changed, 37 insertions(+), 36 deletions(-) diff --git a/modules/context/repo.go b/modules/context/repo.go index 75ebfec705447..8c27e5f6f9d87 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -117,7 +117,7 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use requireSigned = protectedBranch.RequireSignedCommits } - sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName) + sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository, doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName) canCommit := r.CanEnableEditor(ctx, doer) && userCanPush if requireSigned { diff --git a/routers/api/v1/misc/signing.go b/routers/api/v1/misc/signing.go index 2ca9813e15ca0..0fa2ff4e8b71f 100644 --- a/routers/api/v1/misc/signing.go +++ b/routers/api/v1/misc/signing.go @@ -46,12 +46,12 @@ func SigningKey(ctx *context.APIContext) { // schema: // type: string - path := "" - if ctx.Repo != nil && ctx.Repo.Repository != nil { - path = ctx.Repo.Repository.RepoPath() + if ctx.Repo == nil || ctx.Repo.Repository == nil { + ctx.NotFound() + return } - content, err := asymkey_service.PublicSigningKey(ctx, path) + content, err := asymkey_service.PublicSigningKey(ctx, ctx.Repo.Repository) if err != nil { ctx.Error(http.StatusInternalServerError, "gpg export", err) return diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 6d3dd5a3fe629..564af74c329e2 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -11,7 +11,7 @@ import ( "fmt" "net/http" "os" - "path" + "path/filepath" "regexp" "strconv" "strings" @@ -26,6 +26,7 @@ import ( "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -90,11 +91,9 @@ func httpBase(ctx *context.Context) *serviceHandler { isWiki := false unitType := unit.TypeCode - var wikiRepoName string if strings.HasSuffix(reponame, ".wiki") { isWiki = true unitType = unit.TypeWiki - wikiRepoName = reponame reponame = reponame[:len(reponame)-5] } @@ -302,12 +301,7 @@ func httpBase(ctx *context.Context) *serviceHandler { r.URL.Path = strings.ToLower(r.URL.Path) // blue: In case some repo name has upper case name - dir := repo_model.RepoPath(username, reponame) - if isWiki { - dir = repo_model.RepoPath(username, wikiRepoName) - } - - return &serviceHandler{cfg, w, r, dir, cfg.Env} + return &serviceHandler{cfg, w, r, repo, isWiki, cfg.Env} } var ( @@ -362,7 +356,8 @@ type serviceHandler struct { cfg *serviceConfig w http.ResponseWriter r *http.Request - dir string + repo *repo_model.Repository + isWiki bool environ []string } @@ -400,7 +395,7 @@ func (h *serviceHandler) sendFile(contentType, file string) { h.w.WriteHeader(http.StatusBadRequest) return } - reqFile := path.Join(h.dir, file) + reqFile := filepath.Join(h.repo.RepoPath(), file) fi, err := os.Stat(reqFile) if os.IsNotExist(err) { @@ -471,18 +466,19 @@ func serviceRPC(h *serviceHandler, service string) { } var stderr bytes.Buffer - cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.dir) - cmd.SetDescription(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.dir)) - if err := cmd.Run(&git.RunOpts{ - Dir: h.dir, - Env: append(os.Environ(), h.environ...), - Stdout: h.w, - Stdin: reqBody, - Stderr: &stderr, - UseContextTimeout: true, + cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.repo.RepoPath()) + cmd.SetDescription(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.repo.RepoPath())) + if err := gitrepo.RunGitCmd(h.repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Env: append(os.Environ(), h.environ...), + Stdout: h.w, + Stdin: reqBody, + Stderr: &stderr, + UseContextTimeout: true, + }, }); err != nil { if err.Error() != "signal: killed" { - log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.dir, err, stderr.String()) + log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, gitrepo.RepoGitURL(h.repo), err, stderr.String()) } return } @@ -512,8 +508,9 @@ func getServiceType(r *http.Request) string { return strings.TrimPrefix(serviceType, "git-") } -func updateServerInfo(ctx gocontext.Context, dir string) []byte { - out, _, err := git.NewCommand(ctx, "update-server-info").RunStdBytes(&git.RunOpts{Dir: dir}) +func updateServerInfo(ctx gocontext.Context, repo *repo_model.Repository) []byte { + cmd := git.NewCommand(ctx, "update-server-info") + out, _, err := gitrepo.RunGitCmdStdBytes(repo, cmd, &gitrepo.RunOpts{}) if err != nil { log.Error(fmt.Sprintf("%v - %s", err, string(out))) } @@ -543,7 +540,10 @@ func GetInfoRefs(ctx *context.Context) { } h.environ = append(os.Environ(), h.environ...) - refs, _, err := cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").RunStdBytes(&git.RunOpts{Env: h.environ, Dir: h.dir}) + cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".") + refs, _, err := gitrepo.RunGitCmdStdBytes(h.repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{Env: h.environ}, + }) if err != nil { log.Error(fmt.Sprintf("%v - %s", err, string(refs))) } @@ -554,7 +554,7 @@ func GetInfoRefs(ctx *context.Context) { _, _ = h.w.Write([]byte("0000")) _, _ = h.w.Write(refs) } else { - updateServerInfo(ctx, h.dir) + updateServerInfo(ctx, h.repo) h.sendFile("text/plain; charset=utf-8", "info/refs") } } diff --git a/services/asymkey/sign.go b/services/asymkey/sign.go index 2f5d76a29334b..b8878dd4c72ef 100644 --- a/services/asymkey/sign.go +++ b/services/asymkey/sign.go @@ -114,7 +114,8 @@ func SigningKey(ctx context.Context, repoPath string) (string, *git.Signature) { } // PublicSigningKey gets the public signing key within a provided repository directory -func PublicSigningKey(ctx context.Context, repoPath string) (string, error) { +func PublicSigningKey(ctx context.Context, repo *repo_model.Repository) (string, error) { + repoPath := repo.RepoPath() signingKey, _ := SigningKey(ctx, repoPath) if signingKey == "" { return "", nil @@ -226,9 +227,9 @@ Loop: } // SignCRUDAction determines if we should sign a CRUD commit to this repository -func SignCRUDAction(ctx context.Context, repoPath string, u *user_model.User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { +func SignCRUDAction(ctx context.Context, repo *repo_model.Repository, u *user_model.User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions) - signingKey, sig := SigningKey(ctx, repoPath) + signingKey, sig := SigningKey(ctx, repo.RepoPath()) if signingKey == "" { return false, "", nil, &ErrWontSign{noKey} } diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index f6d5643dc91ce..86dbc55b79efe 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -80,7 +80,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode } } if protectedBranch != nil && protectedBranch.RequireSignedCommits { - _, _, _, err := asymkey_service.SignCRUDAction(ctx, repo.RepoPath(), doer, repo.RepoPath(), opts.OldBranch) + _, _, _, err := asymkey_service.SignCRUDAction(ctx, repo, doer, repo.RepoPath(), opts.OldBranch) if err != nil { if !asymkey_service.IsErrWontSign(err) { return err diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 9fcd335c55a6a..2d7eafad8a0b6 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -254,7 +254,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co var keyID string var signer *git.Signature if parent != "" { - sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent) + sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo, author, t.basePath, parent) } else { sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author) } diff --git a/services/repository/files/update.go b/services/repository/files/update.go index f223daf3a9fdf..0f754a0f8d735 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -488,7 +488,7 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do } } if protectedBranch.RequireSignedCommits { - _, _, _, err := asymkey_service.SignCRUDAction(ctx, repo.RepoPath(), doer, repo.RepoPath(), branchName) + _, _, _, err := asymkey_service.SignCRUDAction(ctx, repo, doer, repo.RepoPath(), branchName) if err != nil { if !asymkey_service.IsErrWontSign(err) { return err From e0355cd788c56d9ad7b0441a97e5458ac9be7389 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 20:12:46 +0800 Subject: [PATCH 10/17] More refactoring --- modules/gitrepo/gitrepo.go | 25 +++++++++++++++++++++++++ modules/gitrepo/stats.go | 19 +++++++++++++++++++ services/repository/branch.go | 3 +-- services/repository/create.go | 21 ++++++++++----------- services/repository/files/commit.go | 9 --------- services/repository/fork.go | 23 +++++------------------ 6 files changed, 60 insertions(+), 40 deletions(-) create mode 100644 modules/gitrepo/stats.go diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go index 9a38e91222cfd..fcaeddb08cb18 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -9,8 +9,10 @@ import ( "io" "path/filepath" "strings" + "time" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" ) @@ -20,6 +22,10 @@ type Repository interface { GetOwnerName() string } +func fullName(repo Repository) string { + return repo.GetOwnerName() + "/" + repo.GetName() +} + func repoPath(repo Repository) string { return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git") } @@ -123,3 +129,22 @@ func RenameWikiRepository(ctx context.Context, repo Repository, newName string) } return nil } + +func DeleteRepository(ctx context.Context, repo Repository) error { + return util.RemoveAll(repoPath(repo)) +} + +func ForkRepository(ctx context.Context, baseRepo, targetRepo Repository, singleBranch string) error { + cloneCmd := git.NewCommand(ctx, "clone", "--bare") + if singleBranch != "" { + cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(singleBranch) + } + + if stdout, _, err := cloneCmd.AddDynamicArguments(repoPath(baseRepo), repoPath(targetRepo)). + SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", fullName(baseRepo), fullName(targetRepo))). + RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil { + log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", targetRepo, baseRepo, stdout, err) + return fmt.Errorf("git clone: %w", err) + } + return nil +} diff --git a/modules/gitrepo/stats.go b/modules/gitrepo/stats.go new file mode 100644 index 0000000000000..b0d2c8f394768 --- /dev/null +++ b/modules/gitrepo/stats.go @@ -0,0 +1,19 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + + "code.gitea.io/gitea/modules/git" +) + +// CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch +func CountDivergingCommits(ctx context.Context, repo Repository, baseBranch, branch string) (*git.DivergeObject, error) { + divergence, err := git.GetDivergingCommits(ctx, repoPath(repo), baseBranch, branch) + if err != nil { + return nil, err + } + return &divergence, nil +} diff --git a/services/repository/branch.go b/services/repository/branch.go index e2e50297afacd..9ad8689ea3714 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -26,7 +26,6 @@ import ( "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" notify_service "code.gitea.io/gitea/services/notify" - files_service "code.gitea.io/gitea/services/repository/files" "xorm.io/builder" ) @@ -131,7 +130,7 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g // it's not default branch if repo.DefaultBranch != dbBranch.Name && !dbBranch.IsDeleted { var err error - divergence, err = files_service.CountDivergingCommits(ctx, repo, git.BranchPrefix+branchName) + divergence, err = gitrepo.CountDivergingCommits(ctx, repo, repo.DefaultBranch, git.BranchPrefix+branchName) if err != nil { log.Error("CountDivergingCommits: %v", err) } diff --git a/services/repository/create.go b/services/repository/create.go index 3d261b3d919c9..ddf8b206c8800 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -135,7 +135,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, } // InitRepository initializes README and .gitignore if needed. -func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) { +func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) { if err = repo_module.CheckInitRepository(ctx, repo, opts.ObjectFormatName); err != nil { return err } @@ -144,7 +144,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re if opts.AutoInit { tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name) if err != nil { - return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.RepoPath(), err) + return fmt.Errorf("Failed to create temp dir for repository %s: %w", gitrepo.RepoGitURL(repo), err) } defer func() { if err := util.RemoveAll(tmpDir); err != nil { @@ -152,7 +152,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re } }() - if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil { + if err = prepareRepoCommit(ctx, repo, tmpDir, repo.RepoPath(), opts); err != nil { return fmt.Errorf("prepareRepoCommit: %w", err) } @@ -255,10 +255,9 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt return nil } - repoPath := repo_model.RepoPath(u.Name, repo.Name) - isExist, err := util.IsExist(repoPath) + isExist, err := gitrepo.IsRepositoryExist(ctx, repo) if err != nil { - log.Error("Unable to check if %s exists. Error: %v", repoPath, err) + log.Error("Unable to check if %s exists. Error: %v", gitrepo.RepoGitURL(repo), err) return err } if isExist { @@ -269,15 +268,15 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt // // Previously Gitea would just delete and start afresh - this was naughty. // So we will now fail and delegate to other functionality to adopt or delete - log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath) + log.Error("Files already exist in %s and we are not going to adopt or delete.", gitrepo.RepoGitURL(repo)) return repo_model.ErrRepoFilesAlreadyExist{ Uname: u.Name, Name: repo.Name, } } - if err = initRepository(ctx, repoPath, doer, repo, opts); err != nil { - if err2 := util.RemoveAll(repoPath); err2 != nil { + if err = initRepository(ctx, doer, repo, opts); err != nil { + if err2 := gitrepo.DeleteRepository(ctx, repo); err2 != nil { log.Error("initRepository: %v", err) return fmt.Errorf( "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) @@ -299,8 +298,8 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt } if stdout, _, err := git.NewCommand(ctx, "update-server-info"). - SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", gitrepo.RepoGitURL(repo))). + RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil { log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) rollbackRepo = repo rollbackRepo.OwnerID = u.ID diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go index 8634f7e9fc939..937db472e60c9 100644 --- a/services/repository/files/commit.go +++ b/services/repository/files/commit.go @@ -60,15 +60,6 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato return nil } -// CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch -func CountDivergingCommits(ctx context.Context, repo *repo_model.Repository, branch string) (*git.DivergeObject, error) { - divergence, err := git.GetDivergingCommits(ctx, repo.RepoPath(), repo.DefaultBranch, branch) - if err != nil { - return nil, err - } - return &divergence, nil -} - // GetPayloadCommitVerification returns the verification information of a commit func GetPayloadCommitVerification(ctx context.Context, commit *git.Commit) *structs.PayloadCommitVerification { verification := &structs.PayloadCommitVerification{} diff --git a/services/repository/fork.go b/services/repository/fork.go index 72b94bbf5fa3a..1c532dd70e6f1 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "strings" - "time" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -91,23 +90,19 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork ObjectFormatName: opts.BaseRepo.ObjectFormatName, } - oldRepoPath := opts.BaseRepo.RepoPath() - needsRollback := false rollbackFn := func() { if !needsRollback { return } - repoPath := repo_model.RepoPath(owner.Name, repo.Name) - - if exists, _ := util.IsExist(repoPath); !exists { + if exists, _ := gitrepo.IsRepositoryExist(ctx, repo); !exists { return } // As the transaction will be failed and hence database changes will be destroyed we only need // to delete the related repository on the filesystem - if errDelete := util.RemoveAll(repoPath); errDelete != nil { + if errDelete := gitrepo.DeleteRepository(ctx, repo); errDelete != nil { log.Error("Failed to remove fork repo") } } @@ -141,16 +136,8 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork needsRollback = true - cloneCmd := git.NewCommand(txCtx, "clone", "--bare") - if opts.SingleBranch != "" { - cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch) - } - repoPath := repo_model.RepoPath(owner.Name, repo.Name) - if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repoPath). - SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())). - RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil { - log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err) - return fmt.Errorf("git clone: %w", err) + if err := gitrepo.ForkRepository(txCtx, opts.BaseRepo, repo, opts.SingleBranch); err != nil { + return err } if err := repo_module.CheckDaemonExportOK(txCtx, repo); err != nil { @@ -159,7 +146,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork if stdout, _, err := git.NewCommand(txCtx, "update-server-info"). SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + RunStdString(&git.RunOpts{Dir: repo.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: %w", err) } From 1a1c352cd52078022d8471eaa2ef3833f6e5d243 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 20:37:13 +0800 Subject: [PATCH 11/17] More refactoring --- modules/git/git.go | 5 ---- modules/git/repo.go | 27 -------------------- modules/gitrepo/format.go | 38 +++++++++++++++++++++++++++++ modules/gitrepo/fsck.go | 16 ++++++++++++ modules/indexer/code/git.go | 5 ++-- modules/repository/commits.go | 11 +++++---- modules/repository/commits_test.go | 2 +- routers/web/repo/branch.go | 3 ++- routers/web/repo/setting/webhook.go | 3 ++- services/actions/notifier.go | 4 +-- services/mirror/mirror_pull.go | 2 +- services/pull/pull.go | 2 +- services/release/release.go | 2 +- services/repository/check.go | 4 +-- services/repository/create.go | 5 ++-- services/webhook/notifier.go | 4 +-- 16 files changed, 80 insertions(+), 53 deletions(-) create mode 100644 modules/gitrepo/format.go create mode 100644 modules/gitrepo/fsck.go diff --git a/modules/git/git.go b/modules/git/git.go index 89c23ff2300d8..2a1ccab499b52 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -390,8 +390,3 @@ func configUnsetAll(key, value string) error { } return fmt.Errorf("failed to get git config %s, err: %w", key, err) } - -// Fsck verifies the connectivity and validity of the objects in the database -func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args TrustedCmdArgs) error { - return NewCommand(ctx, "fsck").AddArguments(args...).Run(&RunOpts{Timeout: timeout, Dir: repoPath}) -} diff --git a/modules/git/repo.go b/modules/git/repo.go index db99d285a86a6..028ca485f383b 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -7,7 +7,6 @@ package git import ( "bytes" "context" - "errors" "fmt" "io" "net/url" @@ -63,32 +62,6 @@ func IsRepoURLAccessible(ctx context.Context, url string) bool { return err == nil } -// GetObjectFormatOfRepo returns the hash type of repository at a given path -func GetObjectFormatOfRepo(ctx context.Context, repoPath string) (ObjectFormat, error) { - var stdout, stderr strings.Builder - - err := NewCommand(ctx, "hash-object", "--stdin").Run(&RunOpts{ - Dir: repoPath, - Stdout: &stdout, - Stderr: &stderr, - Stdin: &strings.Reader{}, - }) - if err != nil { - return nil, err - } - - if stderr.Len() > 0 { - return nil, errors.New(stderr.String()) - } - - h, err := NewIDFromString(strings.TrimRight(stdout.String(), "\n")) - if err != nil { - return nil, err - } - - return h.Type(), nil -} - // InitRepository initializes a new Git repository. func InitRepository(ctx context.Context, repoPath string, bare bool, objectFormatName string) error { err := os.MkdirAll(repoPath, os.ModePerm) diff --git a/modules/gitrepo/format.go b/modules/gitrepo/format.go new file mode 100644 index 0000000000000..4f0888b4f1f0d --- /dev/null +++ b/modules/gitrepo/format.go @@ -0,0 +1,38 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + "errors" + "strings" + + "code.gitea.io/gitea/modules/git" +) + +// GetObjectFormatOfRepo returns the hash type of repository at a given path +func GetObjectFormatOfRepo(ctx context.Context, repo Repository) (git.ObjectFormat, error) { + var stdout, stderr strings.Builder + + err := git.NewCommand(ctx, "hash-object", "--stdin").Run(&git.RunOpts{ + Dir: repoPath(repo), + Stdout: &stdout, + Stderr: &stderr, + Stdin: &strings.Reader{}, + }) + if err != nil { + return nil, err + } + + if stderr.Len() > 0 { + return nil, errors.New(stderr.String()) + } + + h, err := git.NewIDFromString(strings.TrimRight(stdout.String(), "\n")) + if err != nil { + return nil, err + } + + return h.Type(), nil +} diff --git a/modules/gitrepo/fsck.go b/modules/gitrepo/fsck.go new file mode 100644 index 0000000000000..d1e059a1d8121 --- /dev/null +++ b/modules/gitrepo/fsck.go @@ -0,0 +1,16 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + "time" + + "code.gitea.io/gitea/modules/git" +) + +// Fsck verifies the connectivity and validity of the objects in the database +func Fsck(ctx context.Context, repo Repository, timeout time.Duration, args git.TrustedCmdArgs) error { + return git.NewCommand(ctx, "fsck").AddArguments(args...).Run(&git.RunOpts{Timeout: timeout, Dir: repoPath(repo)}) +} diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index 76cd78e11e6bf..942861e8c05e0 100644 --- a/modules/indexer/code/git.go +++ b/modules/indexer/code/git.go @@ -10,6 +10,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/indexer/code/internal" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -92,7 +93,7 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s } var err error - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) + objectFormat, err := gitrepo.GetObjectFormatOfRepo(ctx, repo) if err != nil { return nil, err } @@ -174,7 +175,7 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio return nil, err } - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) + objectFormat, err := gitrepo.GetObjectFormatOfRepo(ctx, repo) if err != nil { return nil, err } diff --git a/modules/repository/commits.go b/modules/repository/commits.go index ede60429a13fb..a39642fe376b3 100644 --- a/modules/repository/commits.go +++ b/modules/repository/commits.go @@ -10,6 +10,7 @@ import ( "time" "code.gitea.io/gitea/models/avatars" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" @@ -43,7 +44,7 @@ func NewPushCommits() *PushCommits { } // toAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object. -func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) { +func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repo *repo_model.Repository, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) { var err error authorUsername := "" author, ok := emailUsers[commit.AuthorEmail] @@ -70,7 +71,7 @@ func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, emailUsers map[st committerUsername = committer.Name } - fileStatus, err := git.GetCommitFileStatus(ctx, repoPath, commit.Sha1) + fileStatus, err := git.GetCommitFileStatus(ctx, repo.RepoPath(), commit.Sha1) if err != nil { return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %w", commit.Sha1, err) } @@ -98,14 +99,14 @@ func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, emailUsers map[st // ToAPIPayloadCommits converts a PushCommits object to api.PayloadCommit format. // It returns all converted commits and, if provided, the head commit or an error otherwise. -func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLink string) ([]*api.PayloadCommit, *api.PayloadCommit, error) { +func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repo *repo_model.Repository, repoLink string) ([]*api.PayloadCommit, *api.PayloadCommit, error) { commits := make([]*api.PayloadCommit, len(pc.Commits)) var headCommit *api.PayloadCommit emailUsers := make(map[string]*user_model.User) for i, commit := range pc.Commits { - apiCommit, err := pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, commit) + apiCommit, err := pc.toAPIPayloadCommit(ctx, emailUsers, repo, repoLink, commit) if err != nil { return nil, nil, err } @@ -117,7 +118,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi } if pc.HeadCommit != nil && headCommit == nil { var err error - headCommit, err = pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, pc.HeadCommit) + headCommit, err = pc.toAPIPayloadCommit(ctx, emailUsers, repo, repoLink, pc.HeadCommit) if err != nil { return nil, nil, err } diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index 248673a907d47..228c2057bb3cf 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -52,7 +52,7 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { pushCommits.HeadCommit = &PushCommit{Sha1: "69554a6"} repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) - payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(git.DefaultContext, repo.RepoPath(), "/user2/repo16") + payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(git.DefaultContext, repo, "/user2/repo16") assert.NoError(t, err) assert.Len(t, payloadCommits, 3) assert.NotNil(t, headCommit) diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index c543160f42040..a31d611fc0b78 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -147,7 +148,7 @@ func RestoreBranchPost(ctx *context.Context) { return } - objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath()) + objectFormat, err := gitrepo.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository) if err != nil { log.Error("RestoreBranch: CreateBranch: %w", err) ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", deletedBranch.Name)) diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index ab3c70006f795..f9f41d0bd5bda 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -655,7 +656,7 @@ func TestWebhook(ctx *context.Context) { commit := ctx.Repo.Commit if commit == nil { ghost := user_model.NewGhostUser() - objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath()) + objectFormat, err := gitrepo.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository) if err != nil { ctx.Flash.Error("GetObjectFormatOfRepo: " + err.Error()) ctx.Status(http.StatusInternalServerError) diff --git a/services/actions/notifier.go b/services/actions/notifier.go index 0b4fed5db1282..39dfbff283d49 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -350,7 +350,7 @@ func (n *actionsNotifier) PushCommits(ctx context.Context, pusher *user_model.Us ctx = withMethod(ctx, "PushCommits") apiPusher := convert.ToUser(ctx, pusher, nil) - apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL()) + apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo, repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err) return @@ -412,7 +412,7 @@ func (n *actionsNotifier) SyncPushCommits(ctx context.Context, pusher *user_mode ctx = withMethod(ctx, "SyncPushCommits") apiPusher := convert.ToUser(ctx, pusher, nil) - apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL()) + apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo, repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err) return diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 3418cf90dfe38..33664562fe7a7 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -479,7 +479,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err) continue } - objectFormat, err := git.GetObjectFormatOfRepo(ctx, m.Repo.RepoPath()) + objectFormat, err := gitrepo.GetObjectFormatOfRepo(ctx, m.Repo) if err != nil { log.Error("SyncMirrors [repo: %-v]: unable to GetHashTypeOfRepo: %v", m.Repo, err) } diff --git a/services/pull/pull.go b/services/pull/pull.go index e1ea4357fc5d2..5c2b4a4a74ef3 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -329,7 +329,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, } if err == nil { for _, pr := range prs { - objectFormat, _ := git.GetObjectFormatOfRepo(ctx, pr.BaseRepo.RepoPath()) + objectFormat, _ := gitrepo.GetObjectFormatOfRepo(ctx, pr.BaseRepo) if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) if err != nil { diff --git a/services/release/release.go b/services/release/release.go index c1d13126f0b3b..c776e71c8904f 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -328,7 +328,7 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re } refName := git.RefNameFromTag(rel.TagName) - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) + objectFormat, err := gitrepo.GetObjectFormatOfRepo(ctx, repo) if err != nil { return err } diff --git a/services/repository/check.go b/services/repository/check.go index b874ede51fdbc..dcc16c434a8ec 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -14,6 +14,7 @@ import ( system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/util" @@ -48,8 +49,7 @@ func GitFsckRepos(ctx context.Context, timeout time.Duration, args git.TrustedCm // GitFsckRepo calls 'git fsck' to check an individual repository's health. func GitFsckRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args git.TrustedCmdArgs) error { log.Trace("Running health check on repository %-v", repo) - repoPath := repo.RepoPath() - if err := git.Fsck(ctx, repoPath, timeout, args); err != nil { + if err := gitrepo.Fsck(ctx, repo, timeout, args); err != nil { log.Warn("Failed to health check repository (%-v): %v", repo, err) if err = system_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil { log.Error("CreateRepositoryNotice: %v", err) diff --git a/services/repository/create.go b/services/repository/create.go index ddf8b206c8800..d5942e6c0764a 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -47,7 +47,7 @@ type CreateRepoOptions struct { ObjectFormatName string } -func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error { +func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir string, opts CreateRepoOptions) error { commitTimeStr := time.Now().Format(time.RFC3339) authorSig := repo.Owner.NewGitSig() @@ -61,6 +61,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, "GIT_COMMITTER_DATE="+commitTimeStr, ) + repoPath := repo.RepoPath() // Clone to temporary path and do the init commit. if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir). SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)). @@ -152,7 +153,7 @@ func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Re } }() - if err = prepareRepoCommit(ctx, repo, tmpDir, repo.RepoPath(), opts); err != nil { + if err = prepareRepoCommit(ctx, repo, tmpDir, opts); err != nil { return fmt.Errorf("prepareRepoCommit: %w", err) } diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 1ab14fd6a7e32..d9d3bc1dd59ef 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -587,7 +587,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m func (m *webhookNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { apiPusher := convert.ToUser(ctx, pusher, nil) - apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL()) + apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo, repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err) return @@ -829,7 +829,7 @@ func (m *webhookNotifier) DeleteRelease(ctx context.Context, doer *user_model.Us func (m *webhookNotifier) SyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { apiPusher := convert.ToUser(ctx, pusher, nil) - apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL()) + apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo, repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err) return From deeba9c5a87ff46a115700b05677ce54b78fc70a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 21:20:42 +0800 Subject: [PATCH 12/17] Some refactoring --- modules/git/repo_branch.go | 12 ------------ modules/git/repo_tag.go | 6 ------ modules/gitrepo/branch.go | 20 ++++++++++++++++++++ modules/gitrepo/tag.go | 15 +++++++++++++++ routers/api/v1/repo/branch.go | 4 ++-- routers/web/repo/issue.go | 5 +++-- routers/web/repo/pull.go | 2 +- services/pull/commit_status.go | 3 +-- services/pull/pull.go | 2 +- services/pull/temp_repo.go | 3 ++- services/wiki/wiki.go | 2 +- 11 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 modules/gitrepo/tag.go diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 979c5dec9175a..4d5f06dca45c2 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -5,7 +5,6 @@ package git import ( - "context" "errors" "fmt" "strings" @@ -14,17 +13,6 @@ import ( // BranchPrefix base dir of the branch information file store on git const BranchPrefix = "refs/heads/" -// IsReferenceExist returns true if given reference exists in the repository. -func IsReferenceExist(ctx context.Context, repoPath, name string) bool { - _, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath}) - return err == nil -} - -// IsBranchExist returns true if given branch exists in the repository. -func IsBranchExist(ctx context.Context, repoPath, name string) bool { - return IsReferenceExist(ctx, repoPath, BranchPrefix+name) -} - // Branch represents a Git branch. type Branch struct { Name string diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index af9a75b29ca9f..0262351a9ad6d 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -5,7 +5,6 @@ package git import ( - "context" "fmt" "io" "strings" @@ -17,11 +16,6 @@ import ( // TagPrefix tags prefix path on the repository const TagPrefix = "refs/tags/" -// IsTagExist returns true if given tag exists in the repository. -func IsTagExist(ctx context.Context, repoPath, name string) bool { - return IsReferenceExist(ctx, repoPath, TagPrefix+name) -} - // CreateTag create one tag in the repository func (repo *Repository) CreateTag(name, revision string) error { _, _, err := NewCommand(repo.Ctx, "tag").AddDashesAndList(name, revision).RunStdString(&RunOpts{Dir: repo.Path}) diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go index dcaf92668d158..dcd9c391eff82 100644 --- a/modules/gitrepo/branch.go +++ b/modules/gitrepo/branch.go @@ -30,3 +30,23 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str return gitRepo.GetBranchCommitID(branch) } + +// IsReferenceExist returns true if given reference exists in the repository. +func IsReferenceExist(ctx context.Context, repo Repository, name string) bool { + _, _, err := git.NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&git.RunOpts{Dir: repoPath(repo)}) + return err == nil +} + +func IsWikiReferenceExist(ctx context.Context, repo Repository, name string) bool { + _, _, err := git.NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&git.RunOpts{Dir: wikiPath(repo)}) + return err == nil +} + +// IsBranchExist returns true if given branch exists in the repository. +func IsBranchExist(ctx context.Context, repo Repository, name string) bool { + return IsReferenceExist(ctx, repo, git.BranchPrefix+name) +} + +func IsWikiBranchExist(ctx context.Context, repo Repository, name string) bool { + return IsWikiReferenceExist(ctx, repo, git.BranchPrefix+name) +} diff --git a/modules/gitrepo/tag.go b/modules/gitrepo/tag.go new file mode 100644 index 0000000000000..5c1625c0576a1 --- /dev/null +++ b/modules/gitrepo/tag.go @@ -0,0 +1,15 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + + "code.gitea.io/gitea/modules/git" +) + +// IsTagExist returns true if given tag exists in the repository. +func IsTagExist(ctx context.Context, repo Repository, name string) bool { + return IsReferenceExist(ctx, repo, git.TagPrefix+name) +} diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index bd02a8afc4340..21e4b05a8baaa 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -527,7 +527,7 @@ func CreateBranchProtection(ctx *context.APIContext) { isPlainRule := !git_model.IsRuleNameSpecial(ruleName) var isBranchExist bool if isPlainRule { - isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), ruleName) + isBranchExist = gitrepo.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository, ruleName) } protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, ruleName) @@ -910,7 +910,7 @@ func EditBranchProtection(ctx *context.APIContext) { isPlainRule := !git_model.IsRuleNameSpecial(bpName) var isBranchExist bool if isPlainRule { - isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName) + isBranchExist = gitrepo.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository, bpName) } if isBranchExist { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index c8c9924a9eb77..c58cef8d641e5 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -33,6 +33,7 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" issue_template "code.gitea.io/gitea/modules/issue/template" "code.gitea.io/gitea/modules/log" @@ -1928,7 +1929,7 @@ func ViewIssue(ctx *context.Context) { isPullBranchDeletable := canDelete && pull.HeadRepo != nil && - git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.HeadBranch) && + gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch) && (!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"]) if isPullBranchDeletable && pull.HasMerged { @@ -3025,7 +3026,7 @@ func NewComment(ctx *context.Context) { ctx.ServerError("Unable to load head repo", err) return } - if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok { + if ok := gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.BaseBranch); !ok { // todo localize ctx.JSONError("The origin branch is delete, cannot reopen.") return diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index b265cf47548bc..8b09ae54635be 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -593,7 +593,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pull.Flow == issues_model.PullRequestFlowGithub { headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch) } else { - headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName()) + headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitRefName()) } if headBranchExist { diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index b73816c7eb225..a602ddf106302 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -10,7 +10,6 @@ import ( "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" @@ -126,7 +125,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) { return "", errors.New("Head branch does not exist, can not merge") } - if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) { + if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitRefName()) { return "", errors.New("Head branch does not exist, can not merge") } diff --git a/services/pull/pull.go b/services/pull/pull.go index 5c2b4a4a74ef3..1182a75c899c6 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -380,7 +380,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, for _, pr := range prs { divergence, err := GetDiverging(ctx, pr) if err != nil { - if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { + if git_model.IsErrBranchNotExist(err) && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) { log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) } else { log.Error("GetDiverging: %v", err) diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index 36bdbde55c7a3..2001401f36887 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -15,6 +15,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" ) @@ -181,7 +182,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) if err := git.NewCommand(ctx, "fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch). Run(prCtx.RunOpts()); err != nil { cancel() - if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { + if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) { return nil, nil, git_model.ErrBranchNotExist{ BranchName: pr.HeadBranch, } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 4d4d61a62486b..6392d4ce8376c 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -96,7 +96,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model return fmt.Errorf("InitWiki: %w", err) } - hasMasterBranch := git.IsBranchExist(ctx, repo.WikiPath(), DefaultBranch) + hasMasterBranch := gitrepo.IsWikiBranchExist(ctx, repo, DefaultBranch) basePath, err := repo_module.CreateTemporaryPath("update-wiki") if err != nil { From 21a14fc1c1bc4dda559444fbf666a23ea6eb6cc2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Jan 2024 22:58:53 +0800 Subject: [PATCH 13/17] More refactoring --- models/repo_transfer.go | 3 ++- modules/gitrepo/user.go | 17 +++++++++++++++++ services/user/user.go | 5 +++-- 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 modules/gitrepo/user.go diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 630c243c8e958..0bab9da9c51be 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -14,6 +14,7 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -215,7 +216,7 @@ func TransferOwnership(ctx context.Context, doer *user_model.User, newOwnerName } if repoRenamed { - if err := util.Rename(repo_model.RepoPath(newOwnerName, repo.Name), repo_model.RepoPath(oldOwnerName, repo.Name)); err != nil { + if err := gitrepo.RenameDir(ctx, newOwnerName+"/"+repo.Name, oldOwnerName+"/"+repo.Name); err != nil { log.Critical("Unable to move repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name, repo_model.RepoPath(newOwnerName, repo.Name), repo_model.RepoPath(oldOwnerName, repo.Name), err) } diff --git a/modules/gitrepo/user.go b/modules/gitrepo/user.go new file mode 100644 index 0000000000000..1da9e6883e9d3 --- /dev/null +++ b/modules/gitrepo/user.go @@ -0,0 +1,17 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + "path/filepath" + "strings" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" +) + +func RenameDir(ctx context.Context, oldName, newName string) error { + return util.Rename(filepath.Join(setting.RepoRootPath, strings.ToLower(oldName)), filepath.Join(setting.RepoRootPath, strings.ToLower(newName))) +} diff --git a/services/user/user.go b/services/user/user.go index 8bf083192fa02..ae2097c0b8d5e 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -19,6 +19,7 @@ import ( system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/eventsource" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" @@ -103,7 +104,7 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string) err } // Do not fail if directory does not exist - if err = util.Rename(user_model.UserPath(oldUserName), user_model.UserPath(newUserName)); err != nil && !os.IsNotExist(err) { + if err = gitrepo.RenameDir(ctx, oldUserName, newUserName); err != nil && !os.IsNotExist(err) { u.Name = oldUserName u.LowerName = strings.ToLower(oldUserName) return fmt.Errorf("rename user directory: %w", err) @@ -112,7 +113,7 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string) err if err = committer.Commit(); err != nil { u.Name = oldUserName u.LowerName = strings.ToLower(oldUserName) - if err2 := util.Rename(user_model.UserPath(newUserName), user_model.UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { + if err2 := gitrepo.RenameDir(ctx, newUserName, oldUserName); err2 != nil && !os.IsNotExist(err2) { log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2) return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2) } From 889147e96edf2b503e209a4f41cd9ca84aa3451f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 1 Feb 2024 10:42:49 +0800 Subject: [PATCH 14/17] Don't use repoPath --- cmd/admin.go | 2 +- models/git/commit_status.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/admin.go b/cmd/admin.go index 6c9480e76eb7a..9b2eed34d233e 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -122,7 +122,7 @@ func runRepoSyncReleases(_ *cli.Context) error { } log.Trace("Processing next %d repos of %d", len(repos), count) for _, repo := range repos { - log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath()) + log.Trace("Synchronizing repo %s with path %s", repo.FullName(), gitrepo.RepoGitURL(repo)) gitRepo, err := gitrepo.OpenRepository(ctx, repo) if err != nil { log.Warn("OpenRepository: %v", err) diff --git a/models/git/commit_status.go b/models/git/commit_status.go index 1118b6cc8cb6b..5b8b323a34d01 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -18,6 +18,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -420,9 +421,9 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error { return fmt.Errorf("NewCommitStatus[nil, %s]: no repository specified", opts.SHA) } - repoPath := opts.Repo.RepoPath() + repoURL := gitrepo.RepoGitURL(opts.Repo) if opts.Creator == nil { - return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA) + return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoURL, opts.SHA) } ctx, committer, err := db.TxContext(ctx) @@ -444,13 +445,13 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error { opts.CommitStatus.CreatorID = opts.Creator.ID opts.CommitStatus.RepoID = opts.Repo.ID opts.CommitStatus.Index = idx - log.Debug("NewCommitStatus[%s, %s]: %d", repoPath, opts.SHA, opts.CommitStatus.Index) + log.Debug("NewCommitStatus[%s, %s]: %d", repoURL, opts.SHA, opts.CommitStatus.Index) opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context) // Insert new CommitStatus if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil { - return fmt.Errorf("insert CommitStatus[%s, %s]: %w", repoPath, opts.SHA, err) + return fmt.Errorf("insert CommitStatus[%s, %s]: %w", repoURL, opts.SHA, err) } return committer.Commit() From 57c939f6c97e4fdca4a6f05ec99d559be3871da2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 8 Feb 2024 10:41:49 +0800 Subject: [PATCH 15/17] add a serfvice interface and a local implementation --- modules/gitrepo/command.go | 58 +++++++++++++++++++++++++++----------- modules/gitrepo/init.go | 30 ++++++++++++++++++++ modules/gitrepo/service.go | 43 ++++++++++++++++++++++++++++ routers/init.go | 4 ++- 4 files changed, 118 insertions(+), 17 deletions(-) create mode 100644 modules/gitrepo/init.go create mode 100644 modules/gitrepo/service.go diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go index 83097d3255b83..65c43f03accc8 100644 --- a/modules/gitrepo/command.go +++ b/modules/gitrepo/command.go @@ -4,7 +4,10 @@ package gitrepo import ( + "bytes" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/util" ) type RunOpts struct { @@ -14,30 +17,53 @@ type RunOpts struct { // RunGitCmdStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). func RunGitCmdStdString(repo Repository, c *git.Command, opts *RunOpts) (stdout, stderr string, runErr git.RunStdError) { - if opts.Dir != "" { - // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug - panic("dir field must be empty when using RunStdBytes") + stdoutBytes, stderrBytes, err := RunGitCmdStdBytes(repo, c, opts) + stdout = util.UnsafeBytesToString(stdoutBytes) + stderr = util.UnsafeBytesToString(stderrBytes) + if err != nil { + return stdout, stderr, git.NewRunStdError(err, stderr) } - opts.Dir = getPath(repo, opts.IsWiki) - return c.RunStdString(&opts.RunOpts) + // even if there is no err, there could still be some stderr output, so we just return stdout/stderr as they are + return stdout, stderr, nil } // RunGitCmdStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr). func RunGitCmdStdBytes(repo Repository, c *git.Command, opts *RunOpts) (stdout, stderr []byte, runErr git.RunStdError) { - if opts.Dir != "" { - // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug - panic("dir field must be empty when using RunStdBytes") + if opts == nil { + opts = &RunOpts{} + } + if opts.Stdout != nil || opts.Stderr != nil { + // we must panic here, otherwise there would be bugs if developers set Stdin/Stderr by mistake, and it would be very difficult to debug + panic("stdout and stderr field must be nil when using RunStdBytes") + } + stdoutBuf := &bytes.Buffer{} + stderrBuf := &bytes.Buffer{} + + // We must not change the provided options as it could break future calls - therefore make a copy. + newOpts := &RunOpts{ + RunOpts: git.RunOpts{ + Env: opts.Env, + Timeout: opts.Timeout, + UseContextTimeout: opts.UseContextTimeout, + Dir: opts.Dir, + Stdout: stdoutBuf, + Stderr: stderrBuf, + Stdin: opts.Stdin, + PipelineFunc: opts.PipelineFunc, + }, + IsWiki: opts.IsWiki, } - opts.Dir = getPath(repo, opts.IsWiki) - return c.RunStdBytes(&opts.RunOpts) + + err := RunGitCmd(repo, c, newOpts) + stderr = stderrBuf.Bytes() + if err != nil { + return nil, stderr, git.NewRunStdError(err, util.UnsafeBytesToString(stderr)) + } + // even if there is no err, there could still be some stderr output + return stdoutBuf.Bytes(), stderr, nil } // RunGitCmd runs the command with the RunOpts func RunGitCmd(repo Repository, c *git.Command, opts *RunOpts) error { - if opts.Dir != "" { - // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug - panic("dir field must be empty when using RunStdBytes") - } - opts.Dir = getPath(repo, opts.IsWiki) - return c.Run(&opts.RunOpts) + return curService.Run(repo, c, opts) } diff --git a/modules/gitrepo/init.go b/modules/gitrepo/init.go new file mode 100644 index 0000000000000..11c21b08db6f9 --- /dev/null +++ b/modules/gitrepo/init.go @@ -0,0 +1,30 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/setting" +) + +var curService Service + +func Init(ctx context.Context) error { + curService = &localServiceImpl{ + repoRootDir: setting.RepoRootPath, + } + return nil +} + +// FIXME: +func VersionInfo() string { + return git.VersionInfo() +} + +// FIXME: +func HomeDir() string { + return setting.Git.HomePath +} diff --git a/modules/gitrepo/service.go b/modules/gitrepo/service.go new file mode 100644 index 0000000000000..238b660e3b6f1 --- /dev/null +++ b/modules/gitrepo/service.go @@ -0,0 +1,43 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "path/filepath" + "strings" + + "code.gitea.io/gitea/modules/git" +) + +type Service interface { + Run(Repository, *git.Command, *RunOpts) error +} + +var _ Service = &localServiceImpl{} + +type localServiceImpl struct { + repoRootDir string +} + +func (s *localServiceImpl) repoPath(repo Repository) string { + return filepath.Join(s.repoRootDir, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git") +} + +func (s *localServiceImpl) wikiPath(repo Repository) string { + return filepath.Join(s.repoRootDir, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git") +} + +func (s *localServiceImpl) Run(repo Repository, c *git.Command, opts *RunOpts) error { + if opts.Dir != "" { + // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug + panic("dir field must be empty when using RunStdBytes") + } + + if opts.IsWiki { + opts.Dir = s.wikiPath(repo) + } else { + opts.Dir = s.repoPath(repo) + } + return c.Run(&opts.RunOpts) +} diff --git a/routers/init.go b/routers/init.go index e0a7150ba3182..0d9ef633d74e4 100644 --- a/routers/init.go +++ b/routers/init.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -110,7 +111,8 @@ func InitWebInstallPage(ctx context.Context) { // InitWebInstalled is for global installed configuration. func InitWebInstalled(ctx context.Context) { mustInitCtx(ctx, git.InitFull) - log.Info("Git version: %s (home: %s)", git.VersionInfo(), git.HomeDir()) + mustInitCtx(ctx, gitrepo.Init) + log.Info("Git version: %s (home: %s)", gitrepo.VersionInfo(), gitrepo.HomeDir()) // Setup i18n translation.InitLocales(ctx) From 5e28cb347088537df825268d20e2005e0268b5e4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 10 Feb 2024 12:59:51 +0800 Subject: [PATCH 16/17] more abstract --- models/issues/pull.go | 7 +- models/repo/repo.go | 7 +- modules/context/repo.go | 6 +- modules/git/commit.go | 19 ------ modules/git/repo.go | 5 -- modules/gitrepo/branch.go | 8 ++- modules/gitrepo/command.go | 9 ++- modules/gitrepo/commit.go | 31 +++++++++ modules/gitrepo/format.go | 12 ++-- modules/gitrepo/fsck.go | 5 +- modules/gitrepo/gitrepo.go | 71 +++++++------------- modules/gitrepo/hooks.go | 23 ++----- modules/gitrepo/repository.go | 34 ++++++++++ modules/gitrepo/service.go | 84 +++++++++++++++++++----- modules/gitrepo/stats.go | 27 +++++++- modules/gitrepo/url.go | 4 +- modules/gitrepo/walk_nogogit.go | 4 +- modules/markup/html.go | 12 ++-- modules/markup/html_test.go | 5 +- modules/markup/markdown/markdown_test.go | 9 ++- modules/markup/renderer.go | 4 +- modules/templates/util_render_test.go | 15 ++--- services/repository/branch.go | 3 +- 23 files changed, 248 insertions(+), 156 deletions(-) create mode 100644 modules/gitrepo/commit.go create mode 100644 modules/gitrepo/repository.go diff --git a/models/issues/pull.go b/models/issues/pull.go index 2cb1e1b971e9c..bd10df1c94397 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -872,12 +872,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque } defer repo.Close() - branch, err := repo.GetDefaultBranch() - if err != nil { - return err - } - - commit, err := repo.GetBranchCommit(branch) + commit, err := repo.GetBranchCommit(pr.BaseRepo.DefaultBranch) if err != nil { return err } diff --git a/models/repo/repo.go b/models/repo/repo.go index 13493ba6e80e6..5eb0f68a25dc2 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -460,10 +460,9 @@ func (repo *Repository) MustOwner(ctx context.Context) *user_model.User { func (repo *Repository) ComposeMetas(ctx context.Context) map[string]string { if len(repo.RenderingMetas) == 0 { metas := map[string]string{ - "user": repo.OwnerName, - "repo": repo.Name, - "repoPath": repo.RepoPath(), - "mode": "comment", + "user": repo.OwnerName, + "repo": repo.Name, + "mode": "comment", } unit, err := repo.GetUnit(ctx, unit.TypeExternalTracker) diff --git a/modules/context/repo.go b/modules/context/repo.go index 8c27e5f6f9d87..436035275dd69 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -53,7 +53,7 @@ type Repository struct { Owner *user_model.User Commit *git.Commit Tag *git.Tag - GitRepo *git.Repository + GitRepo gitrepo.GitRepository RefName string BranchName string TagName string @@ -183,7 +183,7 @@ func (r *Repository) GetCommitGraphsCount(ctx context.Context, hidePRRefs bool, return cache.GetInt64(cacheKey, func() (int64, error) { if len(branches) == 0 { - return git.AllCommitsCount(ctx, r.Repository.RepoPath(), hidePRRefs, files...) + return gitrepo.AllCommitsCount(ctx, r.Repository, hidePRRefs, files...) } return git.CommitsCount(ctx, git.CommitsCountOptions{ @@ -692,7 +692,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { // If no branch is set in the request URL, try to guess a default one. if len(ctx.Repo.BranchName) == 0 { - if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) { + if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) { ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch } else { ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch() diff --git a/modules/git/commit.go b/modules/git/commit.go index 5d960e92f3384..abb022fb4611c 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -141,25 +141,6 @@ func CommitChangesWithArgs(repoPath string, args TrustedCmdArgs, opts CommitChan return err } -// AllCommitsCount returns count of all commits in repository -func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) { - cmd := NewCommand(ctx, "rev-list") - if hidePRRefs { - cmd.AddArguments("--exclude=" + PullPrefix + "*") - } - cmd.AddArguments("--all", "--count") - if len(files) > 0 { - cmd.AddDashesAndList(files...) - } - - stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) - if err != nil { - return 0, err - } - - return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64) -} - // CommitsCountOptions the options when counting commits type CommitsCountOptions struct { RepoPath string diff --git a/modules/git/repo.go b/modules/git/repo.go index 028ca485f383b..f31242704c003 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -32,11 +32,6 @@ type GPGSettings struct { const prettyLogFormat = `--pretty=format:%H` -// GetAllCommitsCount returns count of all commits in repository -func (repo *Repository) GetAllCommitsCount() (int64, error) { - return AllCommitsCount(repo.Ctx, repo.Path, false) -} - func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, error) { var commits []*Commit if len(logs) == 0 { diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go index dcd9c391eff82..c737abc3366d0 100644 --- a/modules/gitrepo/branch.go +++ b/modules/gitrepo/branch.go @@ -28,17 +28,19 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str } defer gitRepo.Close() - return gitRepo.GetBranchCommitID(branch) + return gitRepo.GetRefCommitID(git.BranchPrefix + branch) } // IsReferenceExist returns true if given reference exists in the repository. func IsReferenceExist(ctx context.Context, repo Repository, name string) bool { - _, _, err := git.NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&git.RunOpts{Dir: repoPath(repo)}) + cmd := git.NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name) + _, _, err := RunGitCmdStdString(repo, cmd, &RunOpts{}) return err == nil } func IsWikiReferenceExist(ctx context.Context, repo Repository, name string) bool { - _, _, err := git.NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&git.RunOpts{Dir: wikiPath(repo)}) + cmd := git.NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name) + _, _, err := RunGitCmdStdString(repo, cmd, &RunOpts{IsWiki: true}) return err == nil } diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go index 65c43f03accc8..871660ec941ea 100644 --- a/modules/gitrepo/command.go +++ b/modules/gitrepo/command.go @@ -41,6 +41,7 @@ func RunGitCmdStdBytes(repo Repository, c *git.Command, opts *RunOpts) (stdout, // We must not change the provided options as it could break future calls - therefore make a copy. newOpts := &RunOpts{ + IsWiki: opts.IsWiki, RunOpts: git.RunOpts{ Env: opts.Env, Timeout: opts.Timeout, @@ -51,7 +52,6 @@ func RunGitCmdStdBytes(repo Repository, c *git.Command, opts *RunOpts) (stdout, Stdin: opts.Stdin, PipelineFunc: opts.PipelineFunc, }, - IsWiki: opts.IsWiki, } err := RunGitCmd(repo, c, newOpts) @@ -65,5 +65,10 @@ func RunGitCmdStdBytes(repo Repository, c *git.Command, opts *RunOpts) (stdout, // RunGitCmd runs the command with the RunOpts func RunGitCmd(repo Repository, c *git.Command, opts *RunOpts) error { - return curService.Run(repo, c, opts) + if opts.IsWiki { + opts.Dir = wikiRelativePath(repo) + } else { + opts.Dir = repoRelativePath(repo) + } + return curService.Run(c, &opts.RunOpts) } diff --git a/modules/gitrepo/commit.go b/modules/gitrepo/commit.go new file mode 100644 index 0000000000000..647f56f08dcc5 --- /dev/null +++ b/modules/gitrepo/commit.go @@ -0,0 +1,31 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + "strconv" + "strings" + + "code.gitea.io/gitea/modules/git" +) + +// AllCommitsCount returns count of all commits in repository +func AllCommitsCount(ctx context.Context, repo Repository, hidePRRefs bool, files ...string) (int64, error) { + cmd := git.NewCommand(ctx, "rev-list") + if hidePRRefs { + cmd.AddArguments("--exclude=" + git.PullPrefix + "*") + } + cmd.AddArguments("--all", "--count") + if len(files) > 0 { + cmd.AddDashesAndList(files...) + } + + stdout, _, err := RunGitCmdStdString(repo, cmd, &RunOpts{}) + if err != nil { + return 0, err + } + + return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64) +} diff --git a/modules/gitrepo/format.go b/modules/gitrepo/format.go index 4f0888b4f1f0d..073bbb2e189f2 100644 --- a/modules/gitrepo/format.go +++ b/modules/gitrepo/format.go @@ -15,11 +15,13 @@ import ( func GetObjectFormatOfRepo(ctx context.Context, repo Repository) (git.ObjectFormat, error) { var stdout, stderr strings.Builder - err := git.NewCommand(ctx, "hash-object", "--stdin").Run(&git.RunOpts{ - Dir: repoPath(repo), - Stdout: &stdout, - Stderr: &stderr, - Stdin: &strings.Reader{}, + cmd := git.NewCommand(ctx, "hash-object", "--stdin") + err := RunGitCmd(repo, cmd, &RunOpts{ + RunOpts: git.RunOpts{ + Stdout: &stdout, + Stderr: &stderr, + Stdin: &strings.Reader{}, + }, }) if err != nil { return nil, err diff --git a/modules/gitrepo/fsck.go b/modules/gitrepo/fsck.go index d1e059a1d8121..78af43fdf542c 100644 --- a/modules/gitrepo/fsck.go +++ b/modules/gitrepo/fsck.go @@ -12,5 +12,8 @@ import ( // Fsck verifies the connectivity and validity of the objects in the database func Fsck(ctx context.Context, repo Repository, timeout time.Duration, args git.TrustedCmdArgs) error { - return git.NewCommand(ctx, "fsck").AddArguments(args...).Run(&git.RunOpts{Timeout: timeout, Dir: repoPath(repo)}) + cmd := git.NewCommand(ctx, "fsck").AddArguments(args...) + return RunGitCmd(repo, cmd, &RunOpts{ + RunOpts: git.RunOpts{Timeout: timeout}, + }) } diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go index ea8ea370e8350..91cc016455248 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -7,14 +7,9 @@ import ( "context" "fmt" "io" - "path/filepath" "strings" - "time" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" ) type Repository interface { @@ -22,32 +17,27 @@ type Repository interface { GetOwnerName() string } -func fullName(repo Repository) string { - return repo.GetOwnerName() + "/" + repo.GetName() -} +var _ Repository = (*SimpleRepository)(nil) -func repoPath(repo Repository) string { - return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git") +type SimpleRepository struct { + OwnerName string + Name string } -func wikiPath(repo Repository) string { - return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git") +func (r *SimpleRepository) GetName() string { + return r.Name } -func getPath(repo Repository, isWiki bool) string { - if isWiki { - return wikiPath(repo) - } - return repoPath(repo) +func (r *SimpleRepository) GetOwnerName() string { + return r.OwnerName } -// OpenRepository opens the repository at the given relative path with the provided context. -func OpenRepository(ctx context.Context, repo Repository) (*git.Repository, error) { - return git.OpenRepository(ctx, repoPath(repo)) +func repoRelativePath(repo Repository) string { + return strings.ToLower(repo.GetOwnerName()) + "/" + strings.ToLower(repo.GetName()) + ".git" } -func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository, error) { - return git.OpenRepository(ctx, wikiPath(repo)) +func wikiRelativePath(repo Repository) string { + return strings.ToLower(repo.GetOwnerName()) + "/" + strings.ToLower(repo.GetName()) + ".wiki.git" } // contextKey is a value for use with context.WithValue. @@ -59,14 +49,14 @@ type contextKey struct { var RepositoryContextKey = &contextKey{"repository"} // RepositoryFromContext attempts to get the repository from the context -func repositoryFromContext(ctx context.Context, repo Repository) *git.Repository { +func repositoryFromContext(ctx context.Context, repo Repository) GitRepository { value := ctx.Value(RepositoryContextKey) if value == nil { return nil } - if gitRepo, ok := value.(*git.Repository); ok && gitRepo != nil { - if gitRepo.Path == repoPath(repo) { + if gitRepo, ok := value.(GitRepository); ok && gitRepo != nil { + if gitRepo.GetRelativePath() == repoRelativePath(repo) { return gitRepo } } @@ -79,7 +69,7 @@ type nopCloser func() func (nopCloser) Close() error { return nil } // RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it -func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Repository, io.Closer, error) { +func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (GitRepository, io.Closer, error) { gitRepo := repositoryFromContext(ctx, repo) if gitRepo != nil { return gitRepo, nopCloser(nil), nil @@ -90,7 +80,7 @@ func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Rep } // repositoryFromContextPath attempts to get the repository from the context -func repositoryFromContextPath(ctx context.Context, path string) *git.Repository { +func repositoryFromContextPath(ctx context.Context, path string) GitRepository { value := ctx.Value(RepositoryContextKey) if value == nil { return nil @@ -107,7 +97,7 @@ func repositoryFromContextPath(ctx context.Context, path string) *git.Repository // RepositoryFromContextOrOpenPath attempts to get the repository from the context or just opens it // Deprecated: Use RepositoryFromContextOrOpen instead -func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Repository, io.Closer, error) { +func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (GitRepository, io.Closer, error) { gitRepo := repositoryFromContextPath(ctx, path) if gitRepo != nil { return gitRepo, nopCloser(nil), nil @@ -118,40 +108,29 @@ func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Rep } func IsRepositoryExist(ctx context.Context, repo Repository) (bool, error) { - return util.IsExist(repoPath(repo)) + return curService.IsRepositoryExist(ctx, repoRelativePath(repo)) } func RenameRepository(ctx context.Context, repo Repository, newName string) error { - newRepoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(newName)+".git") - if err := util.Rename(repoPath(repo), newRepoPath); err != nil { + newRepoPath := strings.ToLower(repo.GetOwnerName()) + "/" + strings.ToLower(newName) + ".git" + if err := curService.RenameDir(repoRelativePath(repo), newRepoPath); err != nil { return fmt.Errorf("rename repository directory: %w", err) } return nil } func RenameWikiRepository(ctx context.Context, repo Repository, newName string) error { - newWikiRepoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(newName)+".wiki.git") - if err := util.Rename(wikiPath(repo), newWikiRepoPath); err != nil { + newWikiRepoPath := strings.ToLower(repo.GetOwnerName()) + "/" + strings.ToLower(newName) + ".wiki.git" + if err := curService.RenameDir(wikiRelativePath(repo), newWikiRepoPath); err != nil { return fmt.Errorf("rename repository wiki directory: %w", err) } return nil } func DeleteRepository(ctx context.Context, repo Repository) error { - return util.RemoveAll(repoPath(repo)) + return curService.RemoveDir(repoRelativePath(repo)) } func ForkRepository(ctx context.Context, baseRepo, targetRepo Repository, singleBranch string) error { - cloneCmd := git.NewCommand(ctx, "clone", "--bare") - if singleBranch != "" { - cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(singleBranch) - } - - if stdout, _, err := cloneCmd.AddDynamicArguments(repoPath(baseRepo), repoPath(targetRepo)). - SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", fullName(baseRepo), fullName(targetRepo))). - RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil { - log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", targetRepo, baseRepo, stdout, err) - return fmt.Errorf("git clone: %w", err) - } - return nil + return curService.ForkRepository(ctx, repoRelativePath(baseRepo), repoRelativePath(targetRepo), singleBranch) } diff --git a/modules/gitrepo/hooks.go b/modules/gitrepo/hooks.go index 9f983247d40cd..24e27f9008dff 100644 --- a/modules/gitrepo/hooks.go +++ b/modules/gitrepo/hooks.go @@ -108,15 +108,10 @@ done return hookNames, hookTpls, giteaHookTpls } -// CreateDelegateHooks creates all the hooks scripts for the repo -func CreateDelegateHooks(ctx context.Context, repo Repository, iswiki bool) (err error) { +// createDelegateHooks creates all the hooks scripts for the repo +func createDelegateHooks(ctx context.Context, repoPath string) (err error) { hookNames, hookTpls, giteaHookTpls := getHookTemplates() - var hookDir string - if iswiki { - hookDir = filepath.Join(wikiPath(repo), "hooks") - } else { - hookDir = filepath.Join(repoPath(repo), "hooks") - } + hookDir := filepath.Join(repoPath, "hooks") for i, hookName := range hookNames { oldHookPath := filepath.Join(hookDir, hookName) @@ -177,16 +172,12 @@ func ensureExecutable(filename string) error { return os.Chmod(filename, mode) } -// CheckDelegateHooks checks the hooks scripts for the repo -func CheckDelegateHooks(ctx context.Context, repo Repository, isWiki bool) ([]string, error) { +// checkDelegateHooks checks the hooks scripts for the repo +func checkDelegateHooks(ctx context.Context, repoPath string) ([]string, error) { hookNames, hookTpls, giteaHookTpls := getHookTemplates() - var hookDir string - if isWiki { - hookDir = filepath.Join(wikiPath(repo), "hooks") - } else { - hookDir = filepath.Join(repoPath(repo), "hooks") - } + hookDir := filepath.Join(repoPath, "hooks") + results := make([]string, 0, 10) for i, hookName := range hookNames { diff --git a/modules/gitrepo/repository.go b/modules/gitrepo/repository.go new file mode 100644 index 0000000000000..d60c3173afc55 --- /dev/null +++ b/modules/gitrepo/repository.go @@ -0,0 +1,34 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + "io" + + "code.gitea.io/gitea/modules/git" +) + +type GitRepository interface { + io.Closer + GetBranches(skip, limit int) ([]*git.Branch, int, error) + GetRefCommitID(name string) (string, error) + IsObjectExist(sha string) bool + GetBranchCommit(branch string) (*git.Commit, error) + GetDefaultBranch() + GetObjectFormat() + IsBranchExist() + IsTagExist() + GetTagCommit() + GetCommit() +} + +// OpenRepository opens the repository at the given relative path with the provided context. +func OpenRepository(ctx context.Context, repo Repository) (GitRepository, error) { + return curService.OpenRepository(ctx, repoRelativePath(repo)) +} + +func OpenWikiRepository(ctx context.Context, repo Repository) (GitRepository, error) { + return curService.OpenRepository(ctx, wikiRelativePath(repo)) +} diff --git a/modules/gitrepo/service.go b/modules/gitrepo/service.go index 238b660e3b6f1..9e853aec98502 100644 --- a/modules/gitrepo/service.go +++ b/modules/gitrepo/service.go @@ -4,14 +4,27 @@ package gitrepo import ( + "context" + "fmt" "path/filepath" - "strings" + "time" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" ) type Service interface { - Run(Repository, *git.Command, *RunOpts) error + Run(cmd *git.Command, opts *git.RunOpts) error + OpenRepository(ctx context.Context, relativePath string) (GitRepository, error) + IsRepositoryExist(ctx context.Context, relativePath string) (bool, error) + RenameDir(oldRelativePath, newRelativePath string) error + RemoveDir(relativePath string) error + GitURL(relativePath string) string + ForkRepository(ctx context.Context, baseRelativePath, targetRelativePath, singleBranch string) error + CheckDelegateHooks(ctx context.Context, relativePath string) ([]string, error) + CreateDelegateHooks(ctx context.Context, relativePath string) (err error) + WalkReferences(ctx context.Context, relativePath string, walkfn func(sha1, refname string) error) (int, error) } var _ Service = &localServiceImpl{} @@ -20,24 +33,65 @@ type localServiceImpl struct { repoRootDir string } -func (s *localServiceImpl) repoPath(repo Repository) string { - return filepath.Join(s.repoRootDir, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git") +func (s *localServiceImpl) Run(c *git.Command, opts *git.RunOpts) error { + opts.Dir = s.absPath(opts.Dir) + return c.Run(opts) } -func (s *localServiceImpl) wikiPath(repo Repository) string { - return filepath.Join(s.repoRootDir, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git") +func (s *localServiceImpl) absPath(relativePaths ...string) string { + for _, p := range relativePaths { + if filepath.IsAbs(p) { + // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug + panic("dir field must be relative path") + } + } + path := append([]string{s.repoRootDir}, relativePaths...) + return filepath.Join(path...) +} + +func (s *localServiceImpl) OpenRepository(ctx context.Context, relativePath string) (GitRepository, error) { + return git.OpenRepository(ctx, s.absPath(relativePath)) +} + +func (s *localServiceImpl) IsRepositoryExist(ctx context.Context, relativePath string) (bool, error) { + return util.IsExist(s.absPath(relativePath)) +} + +func (s *localServiceImpl) RenameDir(oldRelativePath, newRelativePath string) error { + return util.Rename(s.absPath(oldRelativePath), s.absPath(newRelativePath)) +} + +func (s *localServiceImpl) RemoveDir(relativePath string) error { + return util.RemoveAll(s.absPath(relativePath)) } -func (s *localServiceImpl) Run(repo Repository, c *git.Command, opts *RunOpts) error { - if opts.Dir != "" { - // we must panic here, otherwise there would be bugs if developers set Dir by mistake, and it would be very difficult to debug - panic("dir field must be empty when using RunStdBytes") +func (s *localServiceImpl) GitURL(relativePath string) string { + return s.absPath(relativePath) +} + +func (s *localServiceImpl) ForkRepository(ctx context.Context, baseRelativePath, targetRelativePath, singleBranch string) error { + cloneCmd := git.NewCommand(ctx, "clone", "--bare") + if singleBranch != "" { + cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(singleBranch) } - if opts.IsWiki { - opts.Dir = s.wikiPath(repo) - } else { - opts.Dir = s.repoPath(repo) + if stdout, _, err := cloneCmd.AddDynamicArguments(s.absPath(baseRelativePath), s.absPath(targetRelativePath)). + SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", s.GitURL(baseRelativePath), s.GitURL(targetRelativePath))). + RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil { + log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", targetRelativePath, baseRelativePath, stdout, err) + return fmt.Errorf("git clone: %w", err) } - return c.Run(&opts.RunOpts) + return nil +} + +func (s *localServiceImpl) CheckDelegateHooks(ctx context.Context, relativePath string) ([]string, error) { + return checkDelegateHooks(ctx, s.absPath(relativePath)) +} + +func (s *localServiceImpl) CreateDelegateHooks(ctx context.Context, relativePath string) (err error) { + return createDelegateHooks(ctx, s.absPath(relativePath)) +} + +func (s *localServiceImpl) WalkReferences(ctx context.Context, relativePath string, walkfn func(sha1, refname string) error) (int, error) { + return git.WalkShowRef(ctx, s.absPath(relativePath), nil, 0, 0, walkfn) } diff --git a/modules/gitrepo/stats.go b/modules/gitrepo/stats.go index b0d2c8f394768..a313a9f075f79 100644 --- a/modules/gitrepo/stats.go +++ b/modules/gitrepo/stats.go @@ -5,15 +5,36 @@ package gitrepo import ( "context" + "fmt" + "strconv" + "strings" "code.gitea.io/gitea/modules/git" ) // CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch func CountDivergingCommits(ctx context.Context, repo Repository, baseBranch, branch string) (*git.DivergeObject, error) { - divergence, err := git.GetDivergingCommits(ctx, repoPath(repo), baseBranch, branch) + var do git.DivergeObject + cmd := git.NewCommand(ctx, "rev-list", "--count", "--left-right"). + AddDynamicArguments(baseBranch + "..." + branch) + stdout, _, err := RunGitCmdStdString(repo, cmd, &RunOpts{}) if err != nil { - return nil, err + return &do, err } - return &divergence, nil + left, right, found := strings.Cut(strings.Trim(stdout, "\n"), "\t") + if !found { + return &do, fmt.Errorf("git rev-list output is missing a tab: %q", stdout) + } + + behind, err1 := strconv.Atoi(left) + if err1 != nil { + return &do, err1 + } + do.Behind = behind + ahead, err1 := strconv.Atoi(right) + if err1 != nil { + return &do, err1 + } + do.Ahead = ahead + return &do, nil } diff --git a/modules/gitrepo/url.go b/modules/gitrepo/url.go index f5b09dce4f3e1..53f4855754ab7 100644 --- a/modules/gitrepo/url.go +++ b/modules/gitrepo/url.go @@ -4,11 +4,11 @@ package gitrepo func RepoGitURL(repo Repository) string { - return repoPath(repo) + return curService.GitURL(repoRelativePath(repo)) } func WikiRepoGitURL(repo Repository) string { - return wikiPath(repo) + return curService.GitURL(wikiRelativePath(repo)) } func GetRepoOrWikiGitURL(repo Repository, isWiki bool) string { diff --git a/modules/gitrepo/walk_nogogit.go b/modules/gitrepo/walk_nogogit.go index ff9555996dff5..a141ea46f56d0 100644 --- a/modules/gitrepo/walk_nogogit.go +++ b/modules/gitrepo/walk_nogogit.go @@ -7,11 +7,9 @@ package gitrepo import ( "context" - - "code.gitea.io/gitea/modules/git" ) // WalkReferences walks all the references from the repository func WalkReferences(ctx context.Context, repo Repository, walkfn func(sha1, refname string) error) (int, error) { - return git.WalkShowRef(ctx, repoPath(repo), nil, 0, 0, walkfn) + return curService.WalkReferences(ctx, repoRelativePath(repo), walkfn) } diff --git a/modules/markup/html.go b/modules/markup/html.go index 33dc1e9086aa0..56cb2db65c1ef 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/emoji" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup/common" "code.gitea.io/gitea/modules/references" @@ -1114,7 +1114,7 @@ func emojiProcessor(ctx *RenderContext, node *html.Node) { // hashCurrentPatternProcessor renders SHA1 strings to corresponding links that // are assumed to be in the same repository. func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) { - if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" || ctx.Metas["repoPath"] == "" { + if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" { return } @@ -1146,9 +1146,13 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) { if !inCache { if ctx.GitRepo == nil { var err error - ctx.GitRepo, err = git.OpenRepository(ctx.Ctx, ctx.Metas["repoPath"]) + gitRepo := &gitrepo.SimpleRepository{ + Name: ctx.Metas["repo"], + OwnerName: ctx.Metas["owner"], + } + ctx.GitRepo, err = gitrepo.OpenRepository(ctx.Ctx, gitRepo) if err != nil { - log.Error("unable to open repository: %s Error: %v", ctx.Metas["repoPath"], err) + log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(gitRepo), err) return } ctx.AddCancel(func() { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 89ecfc036b587..f37e7b3807695 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -23,9 +23,8 @@ import ( ) var localMetas = map[string]string{ - "user": "gogits", - "repo": "gogs", - "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/", + "user": "user13", + "repo": "repo11", } func TestMain(m *testing.M) { diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index bdf4011fa2486..abd9532f57745 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -28,9 +28,8 @@ const ( // these values should match the Repo const above var localMetas = map[string]string{ - "user": "gogits", - "repo": "gogs", - "repoPath": "../../../tests/gitea-repositories-meta/user13/repo11.git/", + "user": "user13", + "repo": "repo11", } func TestMain(m *testing.M) { @@ -572,7 +571,7 @@ foo: bar } func TestRenderLinks(t *testing.T) { - input := ` space @mention-user + input := ` space @mention-user /just/a/path.bin https://example.com/file.bin [local link](file.bin) @@ -593,7 +592,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit mail@domain.com @mention-user test #123 - space + space ` cases := []struct { Links markup.Links diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 5a7adcc553226..af58d0b5682ed 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -14,7 +14,7 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -73,7 +73,7 @@ type RenderContext struct { Links Links Metas map[string]string DefaultLink string - GitRepo *git.Repository + GitRepo gitrepo.GitRepository ShaExistCache map[string]bool cancelFn func() SidebarTocNode ast.Node diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 8648967d38611..63dc22d4a1c4a 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -17,7 +17,7 @@ import ( "github.com/stretchr/testify/assert" ) -const testInput = ` space @mention-user +const testInput = ` space @mention-user /just/a/path.bin https://example.com/file.bin [local link](file.bin) @@ -36,14 +36,13 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit mail@domain.com @mention-user test #123 - space + space ` var testMetas = map[string]string{ - "user": "user13", - "repo": "repo11", - "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/", - "mode": "comment", + "user": "user13", + "repo": "repo11", + "mode": "comment", } func TestMain(m *testing.M) { @@ -137,7 +136,7 @@ func TestRenderCommitMessageLinkSubject(t *testing.T) { } func TestRenderIssueTitle(t *testing.T) { - expected := ` space @mention-user + expected := ` space @mention-user /just/a/path.bin https://example.com/file.bin [local link](file.bin) @@ -156,7 +155,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit mail@domain.com @mention-user test #123 - space + space ` assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas)) } diff --git a/services/repository/branch.go b/services/repository/branch.go index 9ad8689ea3714..7e97a4d2de0ad 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -158,7 +158,8 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g pr.Issue.Repo = pr.BaseRepo if pr.HasMerged { - baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID] + gitRepo, ok := repoIDToGitRepo[pr.BaseRepoID] + var baseGitRepo gitrepo.GitRepository = gitRepo if !ok { baseGitRepo, err = gitrepo.OpenRepository(ctx, pr.BaseRepo) if err != nil { From 1aefb7c1973416a104767318d0d93d8c89adcf2d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Feb 2024 12:12:38 +0800 Subject: [PATCH 17/17] more interface for repository --- modules/git/repo.go | 7 +++++++ modules/git/repo_branch_gogit.go | 8 ++++---- modules/git/repo_branch_nogogit.go | 11 ++++++----- modules/git/repo_tag_nogogit.go | 3 ++- modules/gitrepo/repository.go | 14 ++++++++------ 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/modules/git/repo.go b/modules/git/repo.go index bd5f52c8314ae..c41ab34a20abf 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -311,3 +311,10 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io. _, err = io.Copy(out, fi) return err } + +// GetRelativePath FIXME +func (repo *Repository) GetRelativePath() string { + repoName := filepath.Base(repo.Path) + ownerName := filepath.Base(filepath.Dir(repo.Path)) + return ownerName + "/" + repoName +} diff --git a/modules/git/repo_branch_gogit.go b/modules/git/repo_branch_gogit.go index d1ec14d81155f..24c7a0a5391b0 100644 --- a/modules/git/repo_branch_gogit.go +++ b/modules/git/repo_branch_gogit.go @@ -26,16 +26,16 @@ func (repo *Repository) IsObjectExist(name string) bool { } // IsReferenceExist returns true if given reference exists in the repository. -func (repo *Repository) IsReferenceExist(name string) bool { +func (repo *Repository) IsReferenceExist(name string) (bool, error) { if name == "" { - return false + return false, nil } reference, err := repo.gogitRepo.Reference(plumbing.ReferenceName(name), true) if err != nil { - return false + return false, err } - return reference.Type() != plumbing.InvalidReference + return reference.Type() != plumbing.InvalidReference, nil } // IsBranchExist returns true if given branch exists in current repository. diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 470faebe25f79..fc780a56660e8 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -34,9 +34,9 @@ func (repo *Repository) IsObjectExist(name string) bool { } // IsReferenceExist returns true if given reference exists in the repository. -func (repo *Repository) IsReferenceExist(name string) bool { +func (repo *Repository) IsReferenceExist(name string) (bool, error) { if name == "" { - return false + return false, nil } wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx) @@ -44,10 +44,10 @@ func (repo *Repository) IsReferenceExist(name string) bool { _, err := wr.Write([]byte(name + "\n")) if err != nil { log.Debug("Error writing to CatFileBatchCheck %v", err) - return false + return false, err } _, _, _, err = ReadBatchLine(rd) - return err == nil + return err == nil, err } // IsBranchExist returns true if given branch exists in current repository. @@ -56,7 +56,8 @@ func (repo *Repository) IsBranchExist(name string) bool { return false } - return repo.IsReferenceExist(BranchPrefix + name) + exist, _ := repo.IsReferenceExist(BranchPrefix + name) + return exist } // GetBranchNames returns branches from the repository, skipping "skip" initial branches and diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go index 5d98fadd54366..c349ac959ab2e 100644 --- a/modules/git/repo_tag_nogogit.go +++ b/modules/git/repo_tag_nogogit.go @@ -19,7 +19,8 @@ func (repo *Repository) IsTagExist(name string) bool { return false } - return repo.IsReferenceExist(TagPrefix + name) + exist, _ := repo.IsReferenceExist(TagPrefix + name) + return exist } // GetTags returns all tags of the repository. diff --git a/modules/gitrepo/repository.go b/modules/gitrepo/repository.go index d60c3173afc55..18b0e7c436f03 100644 --- a/modules/gitrepo/repository.go +++ b/modules/gitrepo/repository.go @@ -4,6 +4,7 @@ package gitrepo import ( + "bufio" "context" "io" @@ -16,12 +17,13 @@ type GitRepository interface { GetRefCommitID(name string) (string, error) IsObjectExist(sha string) bool GetBranchCommit(branch string) (*git.Commit, error) - GetDefaultBranch() - GetObjectFormat() - IsBranchExist() - IsTagExist() - GetTagCommit() - GetCommit() + GetDefaultBranch() (string, error) + GetObjectFormat() (git.ObjectFormat, error) + IsReferenceExist(string) (bool, error) + GetCommit(string) (*git.Commit, error) + GetRelativePath() string + CatFileBatch(ctx context.Context) (git.WriteCloserError, *bufio.Reader, func()) + CatFileBatchCheck(ctx context.Context) (git.WriteCloserError, *bufio.Reader, func()) } // OpenRepository opens the repository at the given relative path with the provided context.