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 c3cda7b73d0e8..0ed11cd21793d 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" @@ -443,9 +444,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) @@ -467,13 +468,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() diff --git a/models/repo/repo.go b/models/repo/repo.go index 5d5707d1acbe4..f02c55fc895a9 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -472,10 +472,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/git/command.go b/modules/git/command.go index 22cb275ab2dbd..e27bfd381623d 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -399,6 +399,10 @@ func IsErrorExitCode(err error, 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/git/commit.go b/modules/git/commit.go index 5f442b0e1aaca..c594ef3af7127 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -64,7 +64,7 @@ func (c *Commit) Parent(n int) (*Commit, error) { if err != nil { return nil, err } - parent, err := c.repo.getCommit(id) + parent, err := c.repo.GetCommitByObjectID(id) if err != nil { return nil, err } @@ -142,25 +142,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/git.go b/modules/git/git.go index e411269f7c50a..6afacaf117931 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -409,8 +409,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/ref.go b/modules/git/ref.go index ed801f20d5c34..e11853401dfee 100644 --- a/modules/git/ref.go +++ b/modules/git/ref.go @@ -50,7 +50,7 @@ type Reference struct { // Commit return the commit of the reference func (ref *Reference) Commit() (*Commit, error) { - return ref.repo.getCommit(ref.Object) + return ref.repo.GetCommitByObjectID(ref.Object) } // ShortName returns the short name of the reference diff --git a/modules/git/repo.go b/modules/git/repo.go index 4511e900e08a2..9019f00b11997 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" @@ -33,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 { @@ -63,32 +57,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) @@ -343,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.go b/modules/git/repo_branch.go index 552ae2bb8c6c1..12f88feb9a05e 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -14,17 +14,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_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_commit.go b/modules/git/repo_commit.go index 44273d2253ce8..847447aa9358c 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -31,7 +31,7 @@ func (repo *Repository) GetCommit(commitID string) (*Commit, error) { return nil, err } - return repo.getCommit(id) + return repo.GetCommitByObjectID(id) } // GetBranchCommit returns the last commit of given branch. @@ -68,7 +68,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com return nil, err } - return repo.getCommit(id) + return repo.GetCommitByObjectID(id) } // GetCommitByPath returns the last commit of relative path. @@ -266,7 +266,7 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) if err != nil { return nil, err } - commit, err := repo.getCommit(objectID) + commit, err := repo.GetCommitByObjectID(objectID) if err != nil { return nil, err } diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go index ae4c21aaa3d71..e171a7a380065 100644 --- a/modules/git/repo_commit_nogogit.go +++ b/modules/git/repo_commit_nogogit.go @@ -65,7 +65,7 @@ func (repo *Repository) IsCommitExist(name string) bool { return err == nil } -func (repo *Repository) getCommit(id ObjectID) (*Commit, error) { +func (repo *Repository) GetCommitByObjectID(id ObjectID) (*Commit, error) { wr, rd, cancel := repo.CatFileBatch(repo.Ctx) defer cancel() diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index 2026a4c9f5c1a..818ac8c95c9fb 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/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go index cbab39f8c576d..056d23491c147 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/git/tag.go b/modules/git/tag.go index f7666aa89b126..83881f0af0185 100644 --- a/modules/git/tag.go +++ b/modules/git/tag.go @@ -23,7 +23,7 @@ type Tag struct { // Commit return the commit of the tag reference func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) { - return gitRepo.getCommit(tag.Object) + return gitRepo.GetCommitByObjectID(tag.Object) } func parsePayloadSignature(data []byte, messageStart int) (payload, msg, sign string) { diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go index e13a4c82e1c25..1ebd2cd76fcab 100644 --- a/modules/gitrepo/branch.go +++ b/modules/gitrepo/branch.go @@ -28,22 +28,44 @@ 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 { + 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 { + cmd := git.NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name) + _, _, err := RunGitCmdStdString(repo, cmd, &RunOpts{IsWiki: true}) + 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) } // SetDefaultBranch sets default branch of repository. func SetDefaultBranch(ctx context.Context, repo Repository, name string) error { - _, _, err := git.NewCommand(ctx, "symbolic-ref", "HEAD"). - AddDynamicArguments(git.BranchPrefix + name). - RunStdString(&git.RunOpts{Dir: repoPath(repo)}) + cmd := git.NewCommand(ctx, "symbolic-ref", "HEAD"). + AddDynamicArguments(git.BranchPrefix + name) + _, _, err := RunGitCmdStdString(repo, cmd, &RunOpts{}) return err } // GetDefaultBranch gets default branch of repository. func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) { - return git.GetDefaultBranch(ctx, repoPath(repo)) + return curService.GetDefaultBranch(ctx, repoRelativePath(repo)) } func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) { - return git.GetDefaultBranch(ctx, wikiPath(repo)) + return curService.GetDefaultBranch(ctx, wikiRelativePath(repo)) } diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go new file mode 100644 index 0000000000000..871660ec941ea --- /dev/null +++ b/modules/gitrepo/command.go @@ -0,0 +1,74 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "bytes" + + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/util" +) + +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 RunGitCmdStdString(repo Repository, c *git.Command, opts *RunOpts) (stdout, stderr string, runErr git.RunStdError) { + 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) + } + // 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 == 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{ + IsWiki: opts.IsWiki, + 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, + }, + } + + 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.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 new file mode 100644 index 0000000000000..073bbb2e189f2 --- /dev/null +++ b/modules/gitrepo/format.go @@ -0,0 +1,40 @@ +// 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 + + 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 + } + + 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..78af43fdf542c --- /dev/null +++ b/modules/gitrepo/fsck.go @@ -0,0 +1,19 @@ +// 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 { + 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 d89f8f9c0c88c..91cc016455248 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -5,12 +5,11 @@ package gitrepo import ( "context" + "fmt" "io" - "path/filepath" "strings" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" ) type Repository interface { @@ -18,21 +17,27 @@ type Repository interface { GetOwnerName() string } -func repoPath(repo Repository) string { - return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git") +var _ Repository = (*SimpleRepository)(nil) + +type SimpleRepository struct { + OwnerName string + Name string +} + +func (r *SimpleRepository) GetName() string { + return r.Name } -func wikiPath(repo Repository) string { - return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git") +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. @@ -44,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 } } @@ -64,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 @@ -75,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 @@ -92,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 @@ -101,3 +106,31 @@ 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 curService.IsRepositoryExist(ctx, repoRelativePath(repo)) +} + +func RenameRepository(ctx context.Context, repo Repository, newName string) error { + 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 := 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 curService.RemoveDir(repoRelativePath(repo)) +} + +func ForkRepository(ctx context.Context, baseRepo, targetRepo Repository, singleBranch string) error { + return curService.ForkRepository(ctx, repoRelativePath(baseRepo), repoRelativePath(targetRepo), singleBranch) +} diff --git a/modules/repository/hooks.go b/modules/gitrepo/hooks.go similarity index 92% rename from modules/repository/hooks.go rename to modules/gitrepo/hooks.go index 95849789ab1f9..782c41496e767 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" @@ -105,8 +106,8 @@ done return hookNames, hookTpls, giteaHookTpls } -// CreateDelegateHooks creates all the hooks scripts for the repo -func CreateDelegateHooks(repoPath string) (err error) { +// createDelegateHooks creates all the hooks scripts for the repo +func createDelegateHooks(ctx context.Context, repoPath string) (err error) { hookNames, hookTpls, giteaHookTpls := getHookTemplates() hookDir := filepath.Join(repoPath, "hooks") @@ -169,11 +170,12 @@ func ensureExecutable(filename string) error { return os.Chmod(filename, mode) } -// CheckDelegateHooks checks the hooks scripts for the repo -func CheckDelegateHooks(repoPath string) ([]string, error) { +// checkDelegateHooks checks the hooks scripts for the repo +func checkDelegateHooks(ctx context.Context, repoPath string) ([]string, error) { hookNames, hookTpls, giteaHookTpls := getHookTemplates() hookDir := filepath.Join(repoPath, "hooks") + results := make([]string, 0, 10) for i, hookName := range hookNames { @@ -231,3 +233,10 @@ func CheckDelegateHooks(repoPath string) ([]string, error) { } return results, nil } + +func CreateDelegateHooks(ctx context.Context, repo Repository, isWiki bool) (err error) { + if !isWiki { + return curService.CreateDelegateHooks(ctx, repoRelativePath(repo)) + } + return curService.CreateDelegateHooks(ctx, wikiRelativePath(repo)) +} 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/repository.go b/modules/gitrepo/repository.go new file mode 100644 index 0000000000000..a2a9e35a8cd74 --- /dev/null +++ b/modules/gitrepo/repository.go @@ -0,0 +1,47 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "bufio" + "context" + "io" + "time" + + "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) + 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()) + GetCommitsFromIDs(commitIDs []string) []*git.Commit + CreateBundle(ctx context.Context, commit string, out io.Writer) error + CreateArchive(ctx context.Context, format git.ArchiveType, target io.Writer, usePrefix bool, commitID string) error + GetCodeActivityStats(fromTime time.Time, branch string) (*git.CodeActivityStats, error) + GetLanguageStats(commitID string) (map[string]int64, error) + GetBranchNames(skip, limit int) ([]string, int, error) + GetTagInfos(page, pageSize int) ([]*git.Tag, int, error) + GetTagCommitID(name string) (string, error) + WalkReferences(arg git.ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) + GetTagWithID(idStr, name string) (*git.Tag, error) + GetCommitByObjectID(id git.ObjectID) (*git.Commit, error) +} + +// 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 new file mode 100644 index 0000000000000..a3cc15ed7b894 --- /dev/null +++ b/modules/gitrepo/service.go @@ -0,0 +1,102 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + "fmt" + "path/filepath" + "time" + + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" +) + +type Service interface { + 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) + GetDefaultBranch(ctx context.Context, relativePath string) (string, error) +} + +var _ Service = &localServiceImpl{} + +type localServiceImpl struct { + repoRootDir string +} + +func (s *localServiceImpl) Run(c *git.Command, opts *git.RunOpts) error { + opts.Dir = s.absPath(opts.Dir) + return c.Run(opts) +} + +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) 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 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 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) +} + +func (s *localServiceImpl) GetDefaultBranch(ctx context.Context, relativePath string) (string, error) { + return git.GetDefaultBranch(ctx, s.absPath(relativePath)) +} diff --git a/modules/gitrepo/stats.go b/modules/gitrepo/stats.go new file mode 100644 index 0000000000000..a313a9f075f79 --- /dev/null +++ b/modules/gitrepo/stats.go @@ -0,0 +1,40 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +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) { + 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 &do, err + } + 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/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/modules/gitrepo/url.go b/modules/gitrepo/url.go new file mode 100644 index 0000000000000..53f4855754ab7 --- /dev/null +++ b/modules/gitrepo/url.go @@ -0,0 +1,19 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +func RepoGitURL(repo Repository) string { + return curService.GitURL(repoRelativePath(repo)) +} + +func WikiRepoGitURL(repo Repository) string { + return curService.GitURL(wikiRelativePath(repo)) +} + +func GetRepoOrWikiGitURL(repo Repository, isWiki bool) string { + if isWiki { + return WikiRepoGitURL(repo) + } + return RepoGitURL(repo) +} 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/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/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index c607d780ef968..618e040ae10b9 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" @@ -139,7 +140,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, &gitrepo.RunOpts{}) if err != nil { return err } diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index e4622fd66ef95..c287afcd197b8 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, &gitrepo.RunOpts{}) if err != nil { return nil, err } diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index 2905a540e56c1..7a541dc2ab0d9 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" + "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, &gitrepo.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(status.CommitSha, revision) - stdout, _, _ := hasAncestorCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, _ := gitrepo.RunGitCmdStdString(repo, hasAncestorCmd, &gitrepo.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, &gitrepo.RunOpts{}) if runErr != nil { return nil, runErr } @@ -101,7 +104,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, &gitrepo.RunOpts{}) if runErr != nil { // previous commit sha may have been removed by a force push, so // try rebuilding from scratch @@ -167,7 +170,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, &gitrepo.RunOpts{}) if err != nil { return nil, err } diff --git a/modules/indexer/stats/db.go b/modules/indexer/stats/db.go index 98a977c7008fe..ad3a52ab44439 100644 --- a/modules/indexer/stats/db.go +++ b/modules/indexer/stats/db.go @@ -46,13 +46,13 @@ func (db *DBIndexer) Index(id int64) error { defer gitRepo.Close() // Get latest commit for default branch - commitID, err := gitRepo.GetBranchCommitID(repo.DefaultBranch) + commitID, err := gitrepo.GetBranchCommitID(ctx, repo, 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/markup/html.go b/modules/markup/html.go index cef643bf18057..35ef87917aa95 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" @@ -1131,7 +1131,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 } @@ -1163,9 +1163,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 916e74fb6228d..b886b0269d861 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 d9b67e43af013..0ac0f20262684 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -31,9 +31,8 @@ const ( // these values should match the 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) { diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 005fcc278b973..452aff616ef87 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -15,7 +15,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" @@ -76,7 +76,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/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/branch.go b/modules/repository/branch.go index e448490f4ac18..793dfc8ab811d 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -10,7 +10,6 @@ import ( git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" @@ -35,7 +34,7 @@ func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) return SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doerID) } -func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, error) { +func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo gitrepo.GitRepository, doerID int64) (int64, error) { allBranches := container.Set[string]{} { branches, _, err := gitRepo.GetBranchNames(0, 0) 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/modules/repository/init.go b/modules/repository/init.go index 5f500c5233faa..57141d5ec7b8a 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -13,6 +13,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/label" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/options" @@ -120,25 +121,24 @@ func LoadRepoConfig() error { 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 cb926084baae4..4e00c2fcecadf 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -59,7 +59,7 @@ func SyncRepoTags(ctx context.Context, repoID int64) error { } // SyncReleasesWithTags synchronizes release table with repository tags -func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error { +func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo gitrepo.GitRepository) error { log.Debug("SyncReleasesWithTags: in Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name) // optimized procedure for pull-mirrors which saves a lot of time (in @@ -77,7 +77,7 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR } for page := 1; ; page++ { opts.Page = page - rels, err := db.Find[repo_model.Release](gitRepo.Ctx, opts) + rels, err := db.Find[repo_model.Release](ctx, opts) if err != nil { return fmt.Errorf("unable to GetReleasesByRepoID in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) } @@ -120,12 +120,12 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR } // PushUpdateAddTag must be called for any push actions to add tag -func PushUpdateAddTag(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error { +func PushUpdateAddTag(ctx context.Context, repo *repo_model.Repository, gitRepo gitrepo.GitRepository, tagName, sha1, refname string) error { tag, err := gitRepo.GetTagWithID(sha1, tagName) if err != nil { return fmt.Errorf("unable to GetTag: %w", err) } - commit, err := tag.Commit(gitRepo) + commit, err := gitRepo.GetCommitByObjectID(tag.Object) if err != nil { return fmt.Errorf("unable to get tag Commit: %w", err) } @@ -286,7 +286,7 @@ func (shortRelease) TableName() string { // upstream. Hence, after each sync we want the pull-mirror release set to be // identical to the upstream tag set. This is much more efficient for // repositories like https://github.com/vim/vim (with over 13000 tags). -func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error { +func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, gitRepo gitrepo.GitRepository) error { log.Trace("pullMirrorReleaseSync: rebuilding releases for pull-mirror Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name) tags, numTags, err := gitRepo.GetTagInfos(0, 0) if err != nil { diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 15aee8912d1ec..16a739c4b8c3a 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/routers/api/v1/misc/signing.go b/routers/api/v1/misc/signing.go index 24a46c1e704ca..01bb1e797b962 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/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 5e6b6a86586bc..1574cf046cb2a 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/init.go b/routers/init.go index aaf95920c2e6f..0b7051f9bde76 100644 --- a/routers/init.go +++ b/routers/init.go @@ -13,6 +13,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) diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 32ec3003e265c..9adf7aba2d2d5 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -17,6 +17,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" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/web" @@ -185,7 +186,10 @@ 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, &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/githttp.go b/routers/web/repo/githttp.go index 8fb6d930688f8..32944c85ac11f 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -25,6 +25,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "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,7 +91,6 @@ func httpBase(ctx *context.Context) *serviceHandler { isWiki := false unitType := unit.TypeCode - if strings.HasSuffix(reponame, ".wiki") { isWiki = true unitType = unit.TypeWiki @@ -458,13 +458,14 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) { var stderr bytes.Buffer cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.getRepoDir()) cmd.SetDescription(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.getRepoDir())) - if err := cmd.Run(&git.RunOpts{ - Dir: h.getRepoDir(), - Env: append(os.Environ(), h.environ...), - Stdout: ctx.Resp, - Stdin: reqBody, - Stderr: &stderr, - UseContextTimeout: true, + if err := gitrepo.RunGitCmd(h.repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Env: append(os.Environ(), h.environ...), + Stdout: ctx.Resp, + 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.getRepoDir(), err, stderr.String()) @@ -497,8 +498,9 @@ func getServiceType(ctx *context.Context) 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))) } @@ -528,7 +530,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.getRepoDir()}) + 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))) } @@ -539,7 +544,7 @@ func GetInfoRefs(ctx *context.Context) { _, _ = ctx.Resp.Write([]byte("0000")) _, _ = ctx.Resp.Write(refs) } else { - updateServerInfo(ctx, h.getRepoDir()) + updateServerInfo(ctx, h.repo) h.sendFile(ctx, "text/plain; charset=utf-8", "info/refs") } } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index e4f2e9a2bc24d..6ff983f71af9d 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -34,6 +34,7 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/emoji" "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" @@ -1929,7 +1930,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 { @@ -3034,7 +3035,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 a0a8e5410cf15..76178aedae289 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -230,7 +230,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, &gitrepo.RunOpts{}) if err == nil { parentCommit = strings.TrimSpace(parentCommit) } @@ -384,7 +385,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/actions/notifier.go b/services/actions/notifier.go index 6551da39e7268..d7602b79ae62f 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -524,7 +524,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 @@ -585,7 +585,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/agit/agit.go b/services/agit/agit.go index 52a70469e0c0f..3db7fddb93d2c 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -14,6 +14,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" "code.gitea.io/gitea/modules/setting" @@ -179,9 +180,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, &gitrepo.RunOpts{RunOpts: git.RunOpts{Env: os.Environ()}}) if err != nil { return nil, fmt.Errorf("failed to detect force push: %w", err) } else if len(output) > 0 { 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/context/repo.go b/services/context/repo.go index 56e9fada0e935..dff7b73fabf7c 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -55,7 +55,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 @@ -123,7 +123,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 { @@ -189,7 +189,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{ @@ -678,7 +678,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(ctx, ctx.Repo.Repository) diff --git a/services/doctor/heads.go b/services/doctor/heads.go index 41fca01d57137..223e7816baab8 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, &gitrepo.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, &gitrepo.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, &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/doctor/misc.go b/services/doctor/misc.go index 9300c3a25c9c6..5d692eecd8ed7 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) } @@ -92,18 +91,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/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 87691bf729617..58669d614825e 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_service.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{ RepoName: g.repoName, @@ -665,7 +669,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, &gitrepo.RunOpts{}) if err != nil { log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) return head, nil @@ -684,7 +689,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, &gitrepo.RunOpts{}) if err != nil { return "", err } @@ -701,13 +707,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, &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 - _, _, 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, &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 c9b924809819b..cac464b01ea4e 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -236,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, &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)) @@ -260,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, &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)) @@ -284,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, &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 21d5f08205ded..7715f46cc5801 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -31,40 +31,42 @@ 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, &gitrepo.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), gitrepo.RepoGitURL(repo))) } 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, gitrepo.RepoGitURL(repo))) } - _, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath}) + _, _, err = gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.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() 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.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: true}) 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(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 = cmd.RunStdString(&git.RunOpts{Dir: wikiPath}) + _, _, 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 } @@ -175,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, m.Repo.FullName())). - 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() @@ -205,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) } @@ -239,15 +244,15 @@ 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{ + cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", gitrepo.RepoGitURL(m.Repo))) + if err := gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ Timeout: timeout, - Dir: repoPath, Env: envs, Stdout: &stdoutBuilder, Stderr: &stderrBuilder, - }); err != nil { + }, + }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -261,19 +266,19 @@ 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() - if err = cmd. - SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())). - Run(&git.RunOpts{ + cmd.SetDescription(fmt.Sprintf("Mirror.runSync: %s", gitrepo.RepoGitURL(m.Repo))) + if err = gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ Timeout: timeout, - Dir: repoPath, Stdout: &stdoutBuilder, Stderr: &stderrBuilder, - }); err != nil { + }, + }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -336,14 +341,16 @@ 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{ + cmd := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). + SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", gitrepo.WikiRepoGitURL(m.Repo))) + if err := gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ Timeout: timeout, - Dir: wikiPath, Stdout: &stdoutBuilder, Stderr: &stderrBuilder, - }); err != nil { + }, + IsWiki: true, + }); err != nil { stdout := stdoutBuilder.String() stderr := stderrBuilder.String() @@ -357,20 +364,22 @@ 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() 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{ + git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). + SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", gitrepo.WikiRepoGitURL(m.Repo))) + if err = gitrepo.RunGitCmd(m.Repo, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ Timeout: timeout, - Dir: wikiPath, Stdout: &stdoutBuilder, Stderr: &stderrBuilder, - }); err != nil { + }, + IsWiki: true, + }); 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 21ba0afeffd6e..c65e44611b6d4 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -29,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 gitrepo.Repository, isWiki bool) error { cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) + url := gitrepo.RepoGitURL(repo) + if isWiki { + 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 := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil { + if _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: isWiki}); 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 := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: isWiki}); 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 := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: isWiki}); err != nil { return err } return nil } - if err := addRemoteAndConfig(addr, m.Repo.RepoPath()); 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.WikiPath()); err != nil { + if err := addRemoteAndConfig(wikiRemoteURL, m.Repo, true); err != nil { return err } } @@ -69,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, &gitrepo.RunOpts{}); err != nil { return err } if m.Repo.HasWiki() { - if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: m.Repo.WikiPath()}); 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 f4dd332b1440a..c7855e8f6a2eb 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -197,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, &gitrepo.RunOpts{}); err != nil { if strings.Contains(err.Error(), "exit status 1") { // prHeadRef is not an ancestor of the base branch return nil, nil @@ -225,9 +224,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) // 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, &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/commit_status.go b/services/pull/commit_status.go index aa1ad7cd665b8..c4b4709fe327d 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" @@ -134,7 +133,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 185a1895c9fb8..da197de7fed71 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -391,7 +391,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) @@ -553,7 +553,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, &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/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/release/release.go b/services/release/release.go index ba5fd1dd986eb..3d4af826bdb3d 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -318,9 +318,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, &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/adopt.go b/services/repository/adopt.go index b337eac38ac38..9642b3c764866 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -80,7 +80,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) } @@ -95,9 +95,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) } @@ -111,17 +111,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/archiver/archiver.go b/services/repository/archiver/archiver.go index 01c58f0ce402a..9c69e5f235ed5 100644 --- a/services/repository/archiver/archiver.go +++ b/services/repository/archiver/archiver.go @@ -216,7 +216,7 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver } defer gitRepo.Close() - go func(done chan error, w *io.PipeWriter, archiver *repo_model.RepoArchiver, gitRepo *git.Repository) { + go func(done chan error, w *io.PipeWriter, archiver *repo_model.RepoArchiver, gitRepo gitrepo.GitRepository) { defer func() { if r := recover(); r != nil { done <- fmt.Errorf("%v", r) diff --git a/services/repository/branch.go b/services/repository/branch.go index 229ac54f307d8..023dbed534af6 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -28,7 +28,6 @@ import ( "code.gitea.io/gitea/modules/timeutil" 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" ) @@ -204,7 +203,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 { diff --git a/services/repository/check.go b/services/repository/check.go index 5cdcc146797c9..be77695fc5dc3 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) @@ -88,9 +88,9 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du log.Trace("Running git gc on %-v", repo) command := git.NewCommand(ctx, "gc").AddArguments(args...). 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, &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) desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) diff --git a/services/repository/create.go b/services/repository/create.go index 971793bcc6e35..472889f83ea8e 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)). @@ -135,8 +136,8 @@ 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 { +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 +145,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 +153,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, opts); err != nil { return fmt.Errorf("prepareRepoCommit: %w", err) } @@ -252,10 +253,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 { @@ -266,15 +266,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) @@ -295,9 +295,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/delete.go b/services/repository/delete.go index 7c7dfe2dddb2b..803090cccf55b 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" @@ -288,12 +289,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/patch.go b/services/repository/files/patch.go index e5f7e2af965d9..d0ce7aae3e954 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 f029a9aefe96b..db16a1fb3c125 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -492,7 +492,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 diff --git a/services/repository/fork.go b/services/repository/fork.go index f074fd1082118..5a8b6e48898e3 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" @@ -99,23 +98,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") } } @@ -149,30 +144,22 @@ 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 { 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) } - 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/generate.go b/services/repository/generate.go index 9b09e271ab41a..2937c9325a67a 100644 --- a/services/repository/generate.go +++ b/services/repository/generate.go @@ -354,7 +354,7 @@ func generateRepository(ctx context.Context, doer, owner *user_model.User, templ } } - if err = repo_module.CheckInitRepository(ctx, owner.Name, generateRepo.Name, generateRepo.ObjectFormatName); err != nil { + if err = repo_module.CheckInitRepository(ctx, generateRepo, generateRepo.ObjectFormatName); err != nil { return generateRepo, err } @@ -362,9 +362,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, &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/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/repository/transfer.go b/services/repository/transfer.go index 83d303218825d..c929ad03fe1f0 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -17,6 +17,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" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/sync" @@ -81,7 +82,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) } @@ -306,19 +307,12 @@ func changeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo } } - newRepoPath := repo_model.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, repo_model.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/services/user/user.go b/services/user/user.go index 2287e36c716ac..bb4c7dbee7b35 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -18,6 +18,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" @@ -100,7 +101,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) @@ -109,7 +110,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) } diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 587caf62ff305..2a8e66363768a 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 diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index fdcc5feefa7ec..602431fe569d2 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -40,9 +40,11 @@ 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").AddDynamicArguments(git.BranchPrefix + repo.DefaultWikiBranch).RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil { + } + cmd := git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+repo.DefaultWikiBranch) + if _, _, err := gitrepo.RunGitCmdStdString(repo, cmd, &gitrepo.RunOpts{IsWiki: true}); err != nil { return fmt.Errorf("unable to set default wiki branch to %q: %w", repo.DefaultWikiBranch, err) } return nil @@ -96,7 +98,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model return fmt.Errorf("InitWiki: %w", err) } - hasDefaultBranch := git.IsBranchExist(ctx, repo.WikiPath(), repo.DefaultWikiBranch) + hasMasterBranch := gitrepo.IsWikiBranchExist(ctx, repo, repo.DefaultWikiBranch) basePath, err := repo_module.CreateTemporaryPath("update-wiki") if err != nil { diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go index 937f6a829c2cc..7f3120771ac9d 100644 --- a/tests/integration/api_repo_git_tags_test.go +++ b/tests/integration/api_repo_git_tags_test.go @@ -30,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, &gitrepo.RunOpts{}) + cmd = git.NewCommand(git.DefaultContext, "config", "user.email").AddDynamicArguments(user.Email) + _ = 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 daf411f452a44..df45af2f14b1d 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -285,26 +285,31 @@ 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, &gitrepo.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, - Stdin: stdin, - Stdout: &stdout, + cmd = git.NewCommand(git.DefaultContext, "hash-object", "-w", "--stdin") + err = gitrepo.RunGitCmd(repo1, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ + Stdin: stdin, + Stdout: &stdout, + }, }) 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, &gitrepo.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, &gitrepo.RunOpts{}) + assert.NoError(t, err) treeSha = strings.TrimSpace(treeSha) @@ -324,17 +329,19 @@ func TestCantMergeUnrelated(t *testing.T) { _, _ = messageBytes.WriteString("\n") stdout.Reset() - err = git.NewCommand(git.DefaultContext, "commit-tree").AddDynamicArguments(treeSha). - Run(&git.RunOpts{ + cmd = git.NewCommand(git.DefaultContext, "commit-tree").AddDynamicArguments(treeSha) + err = gitrepo.RunGitCmd(repo1, cmd, &gitrepo.RunOpts{ + RunOpts: git.RunOpts{ Env: env, - Dir: path, 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, &gitrepo.RunOpts{}) assert.NoError(t, err) testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")