refactor: use WithRepo instead of WithDir for most git operations, clean up model migrations (#38555)

by the way, remove some unnecessary "models" imports from model
migration package, fix migration test model init bug
(modelmigration/migrationtest/tests.go)
This commit is contained in:
wxiaoguang
2026-07-21 20:18:00 +08:00
committed by GitHub
parent 8731ea4bbb
commit 91eb14c944
45 changed files with 211 additions and 217 deletions

View File

@@ -0,0 +1,13 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package base
import (
repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git/gitcmd"
)
func LocalCodeGitRepo(ownerName, repoName string) gitcmd.RepositoryFacade {
return repo_model.CodeRepoByName(ownerName, repoName)
}

View File

@@ -58,7 +58,11 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (base.EngineMigra
}
}
db.ResetModels()
if len(syncModels) > 0 {
for _, syncModel := range syncModels {
db.RegisterModel(syncModel)
}
if err := x.Sync(syncModels...); err != nil {
t.Errorf("error during sync: %v", err)
return x, deferFn

View File

@@ -7,7 +7,6 @@ import (
"context"
"fmt"
"math"
"path/filepath"
"strings"
"time"
@@ -76,24 +75,23 @@ func FixMergeBase(ctx context.Context, x base.EngineMigration) error {
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
continue
}
userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName))
repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git")
gitRepo := base.LocalCodeGitRepo(baseRepo.OwnerName, baseRepo.Name)
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
if !pr.HasMerged {
var err error
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).WithDir(repoPath).RunStdString(ctx)
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).WithRepo(gitRepo).RunStdString(ctx)
if err != nil {
var err2 error
pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).WithDir(repoPath).RunStdString(ctx)
pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).WithRepo(gitRepo).RunStdString(ctx)
if err2 != nil {
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
continue
}
}
} else {
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithRepo(gitRepo).RunStdString(ctx)
if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
@@ -107,7 +105,7 @@ func FixMergeBase(ctx context.Context, x base.EngineMigration) error {
refs = append(refs, gitRefName)
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
pr.MergeBase, _, err = cmd.WithRepo(gitRepo).RunStdString(ctx)
if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue

View File

@@ -7,7 +7,6 @@ import (
"context"
"fmt"
"math"
"path/filepath"
"strings"
"time"
@@ -74,12 +73,11 @@ func RefixMergeBase(ctx context.Context, x base.EngineMigration) error {
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
continue
}
userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName))
repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git")
gitRepo := base.LocalCodeGitRepo(baseRepo.OwnerName, baseRepo.Name)
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithRepo(gitRepo).RunStdString(ctx)
if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
@@ -94,7 +92,7 @@ func RefixMergeBase(ctx context.Context, x base.EngineMigration) error {
refs = append(refs, gitRefName)
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
pr.MergeBase, _, err = cmd.WithRepo(gitRepo).RunStdString(ctx)
if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue

View File

@@ -9,7 +9,6 @@ import (
"time"
"gitea.dev/modelmigration/base"
repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git"
"gitea.dev/modules/graceful"
"gitea.dev/modules/log"
@@ -83,7 +82,7 @@ func AddCommitDivergenceToPulls(x base.EngineMigration) error {
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
continue
}
repoStore := repo_model.CodeRepoByName(baseRepo.OwnerName, baseRepo.Name)
repoStore := base.LocalCodeGitRepo(baseRepo.OwnerName, baseRepo.Name)
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
divergence, err := git.GetDivergingCommits(graceful.GetManager().HammerContext(), repoStore, pr.BaseBranch, gitRefName)
if err != nil {

View File

@@ -9,7 +9,6 @@ import (
"strings"
"gitea.dev/modelmigration/base"
repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git"
"gitea.dev/modules/log"
)
@@ -98,7 +97,7 @@ func FixPublisherIDforTagReleases(ctx context.Context, x base.EngineMigration) e
return err
}
}
gitRepo, err = git.OpenRepository(repo_model.CodeRepoByName(repo.OwnerName, repo.Name))
gitRepo, err = git.OpenRepository(base.LocalCodeGitRepo(repo.OwnerName, repo.Name))
if err != nil {
log.Error("Error whilst opening git repo for [%d]%s/%s. Error: %v", repo.ID, repo.OwnerName, repo.Name, err)
return err

View File

@@ -5,18 +5,17 @@ package v1_17
import (
"gitea.dev/modelmigration/base"
"gitea.dev/models/pull"
"gitea.dev/modules/timeutil"
)
func AddReviewViewedFiles(x base.EngineMigration) error {
type ReviewState struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user)"`
PullID int64 `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"`
CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"`
UpdatedFiles map[string]pull.ViewedState `xorm:"NOT NULL LONGTEXT JSON"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user)"`
PullID int64 `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"`
CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"`
UpdatedFiles map[string]uint8 `xorm:"NOT NULL LONGTEXT JSON"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
return x.Sync(new(ReviewState))

View File

@@ -7,15 +7,13 @@ import (
"time"
"gitea.dev/modelmigration/base"
"gitea.dev/models/repo"
"gitea.dev/modules/timeutil"
)
func AddSyncOnCommitColForPushMirror(x base.EngineMigration) error {
type PushMirror struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
Repo *repo.Repository `xorm:"-"`
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
RemoteName string
SyncOnCommit bool `xorm:"NOT NULL DEFAULT true"`

View File

@@ -7,7 +7,6 @@ import (
"context"
"gitea.dev/modelmigration/base"
repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git"
"gitea.dev/modules/setting"
)
@@ -159,7 +158,7 @@ func migratePushMirrors(x base.EngineMigration) error {
func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
ctx := context.Background()
repo := repo_model.CodeRepoByName(ownerName, repoName)
repo := base.LocalCodeGitRepo(ownerName, repoName)
if exist, _ := git.IsRepositoryExist(ctx, repo); !exist {
return "", nil
}

View File

@@ -5,12 +5,11 @@ package v1_22
import (
"gitea.dev/modelmigration/base"
"gitea.dev/models/perm"
)
func AddRepoUnitEveryoneAccessMode(x base.EngineMigration) error {
type RepoUnit struct { //revive:disable-line:exported
EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
EveryoneAccessMode int `xorm:"NOT NULL DEFAULT 0"`
}
return x.Sync(&RepoUnit{})
}

View File

@@ -5,14 +5,13 @@ package v1_24
import (
"gitea.dev/modelmigration/base"
"gitea.dev/models/perm"
"xorm.io/xorm"
)
func AddRepoUnitAnonymousAccessMode(x base.EngineMigration) error {
type RepoUnit struct { //revive:disable-line:exported
AnonymousAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
AnonymousAccessMode int `xorm:"NOT NULL DEFAULT 0"`
}
_, err := x.SyncWithOptions(xorm.SyncOptions{
IgnoreConstrains: true,

View File

@@ -11,10 +11,6 @@ import (
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
_ "gitea.dev/models/actions"
_ "gitea.dev/models/git"
_ "gitea.dev/models/repo"
"github.com/stretchr/testify/require"
)

View File

@@ -8,7 +8,6 @@ import (
"fmt"
"gitea.dev/modelmigration/base"
repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git"
)
@@ -86,7 +85,7 @@ func FixReleaseSha1OnReleaseTable(ctx context.Context, x base.EngineMigration) e
userCache[repo.OwnerID] = user
}
gitRepo, err = git.OpenRepository(repo_model.CodeRepoByName(user.Name, repo.Name))
gitRepo, err = git.OpenRepository(base.LocalCodeGitRepo(user.Name, repo.Name))
if err != nil {
return err
}

View File

@@ -116,6 +116,10 @@ func RegisterModel(bean any, initFunc ...func() error) {
}
}
func ResetModels() {
registeredModels, registeredInitFuncs = nil, nil
}
// SyncAllTables sync the schemas of all tables, is required by unit test code
func SyncAllTables() error {
_, err := xormEngine.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{

15
models/init.go Normal file
View File

@@ -0,0 +1,15 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package models
import (
"context"
"gitea.dev/models/unit"
)
// Init initialize model
func Init(ctx context.Context) error {
return unit.LoadUnitConfig()
}

View File

@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package models
package repostats
import (
"testing"

View File

@@ -2,7 +2,7 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package models
package repostats
import (
"context"
@@ -12,7 +12,6 @@ import (
"gitea.dev/models/db"
issues_model "gitea.dev/models/issues"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/log"
@@ -21,11 +20,6 @@ import (
"xorm.io/builder"
)
// Init initialize model
func Init(ctx context.Context) error {
return unit.LoadUnitConfig()
}
type repoChecker struct {
querySQL func(ctx context.Context) ([]int64, error)
correctSQL func(ctx context.Context, id int64) error

View File

@@ -1,7 +1,7 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package models
package repostats
import (
"testing"

View File

@@ -42,30 +42,34 @@ func CreateBundle(ctx context.Context, repo RepositoryFacade, commit string, out
// git update-ref refs/bundle/temp-{timestamp} {commit}
// git bundle create - refs/bundle/temp-{timestamp}
// git update-ref -d refs/bundle/temp-{timestamp}
tmp, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-bundle")
tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-bundle")
if err != nil {
return err
}
defer cleanup()
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(gitcmd.RepoLocalPath(repo), "objects"))
_, _, err = gitcmd.NewCommand("init", "--bare").WithDir(tmp).WithEnv(env).RunStdString(ctx)
gitTmpCmd := func() *gitcmd.Command {
return gitcmd.NewCommand().WithDir(tmpDir).WithEnv(env)
}
_, _, err = gitTmpCmd().AddArguments("init", "--bare").RunStdString(ctx)
if err != nil {
return err
}
_, _, err = gitcmd.NewCommand("reset", "--soft").AddDynamicArguments(commit).WithDir(tmp).WithEnv(env).RunStdString(ctx)
_, _, err = gitTmpCmd().AddArguments("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx)
if err != nil {
return err
}
_, _, err = gitcmd.NewCommand("branch", "-m", "bundle").WithDir(tmp).WithEnv(env).RunStdString(ctx)
_, _, err = gitTmpCmd().AddArguments("branch", "-m", "bundle").RunStdString(ctx)
if err != nil {
return err
}
tmpFile := filepath.Join(tmp, "bundle")
_, _, err = gitcmd.NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").WithDir(tmp).WithEnv(env).RunStdString(ctx)
tmpFile := filepath.Join(tmpDir, "bundle")
_, _, err = gitTmpCmd().AddArguments("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx)
if err != nil {
return err
}

View File

@@ -6,8 +6,10 @@ package git
import (
"context"
"io"
"os"
"gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/util"
)
type BufferedReader interface {
@@ -49,8 +51,11 @@ type CatFileBatchCloser interface {
// The CatFileBatch and the readers create by it should only be used in the same goroutine.
func NewBatch(ctx context.Context, repo RepositoryFacade) (CatFileBatchCloser, error) {
repoPath := gitcmd.RepoLocalPath(repo)
if DefaultFeatures().SupportCatFileBatchCommand {
return newCatFileBatchCommand(ctx, repoPath)
if _, err := os.Stat(repoPath); err != nil {
return nil, util.NewNotExistErrorf("repo %q doesn't exist", repo.LogString())
}
return newCatFileBatchLegacy(ctx, repoPath)
if DefaultFeatures().SupportCatFileBatchCommand {
return newCatFileBatchCommand(ctx, repo), nil
}
return newCatFileBatchLegacy(ctx, repo), nil
}

View File

@@ -5,38 +5,32 @@ package git
import (
"context"
"os"
"path/filepath"
"strings"
"gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/setting"
"gitea.dev/modules/util"
)
// catFileBatchCommand implements the CatFileBatch interface using the "cat-file --batch-command" command
// for git version >= 2.36
// ref: https://git-scm.com/docs/git-cat-file#Documentation/git-cat-file.txt---batch-command
type catFileBatchCommand struct {
ctx context.Context
repoPath string
batch *catFileBatchCommunicator
ctx context.Context
repo RepositoryFacade
batch *catFileBatchCommunicator
}
var _ CatFileBatch = (*catFileBatchCommand)(nil)
func newCatFileBatchCommand(ctx context.Context, repoPath string) (*catFileBatchCommand, error) {
if _, err := os.Stat(repoPath); err != nil {
return nil, util.NewNotExistErrorf("repo %q doesn't exist", filepath.Base(repoPath))
}
return &catFileBatchCommand{ctx: ctx, repoPath: repoPath}, nil
func newCatFileBatchCommand(ctx context.Context, repo RepositoryFacade) *catFileBatchCommand {
return &catFileBatchCommand{ctx: ctx, repo: repo}
}
func (b *catFileBatchCommand) getBatch() *catFileBatchCommunicator {
if b.batch != nil {
return b.batch
}
b.batch = newCatFileBatch(b.ctx, b.repoPath, gitcmd.NewCommand("cat-file", "--batch-command"))
b.batch = newCatFileBatch(b.ctx, b.repo, gitcmd.NewCommand("cat-file", "--batch-command"))
return b.batch
}

View File

@@ -6,13 +6,10 @@ package git
import (
"context"
"io"
"os"
"path/filepath"
"strings"
"gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/setting"
"gitea.dev/modules/util"
)
// catFileBatchLegacy implements the CatFileBatch interface using the "cat-file --batch" command and "cat-file --batch-check" command
@@ -21,25 +18,22 @@ import (
// ref: https://git-scm.com/docs/git-cat-file#Documentation/git-cat-file.txt---batch
type catFileBatchLegacy struct {
ctx context.Context
repoPath string
repo RepositoryFacade
batchContent *catFileBatchCommunicator
batchCheck *catFileBatchCommunicator
}
var _ CatFileBatchCloser = (*catFileBatchLegacy)(nil)
func newCatFileBatchLegacy(ctx context.Context, repoPath string) (*catFileBatchLegacy, error) {
if _, err := os.Stat(repoPath); err != nil {
return nil, util.NewNotExistErrorf("repo %q doesn't exist", filepath.Base(repoPath))
}
return &catFileBatchLegacy{ctx: ctx, repoPath: repoPath}, nil
func newCatFileBatchLegacy(ctx context.Context, repo RepositoryFacade) *catFileBatchLegacy {
return &catFileBatchLegacy{ctx: ctx, repo: repo}
}
func (b *catFileBatchLegacy) getBatchContent() *catFileBatchCommunicator {
if b.batchContent != nil {
return b.batchContent
}
b.batchContent = newCatFileBatch(b.ctx, b.repoPath, gitcmd.NewCommand("cat-file", "--batch"))
b.batchContent = newCatFileBatch(b.ctx, b.repo, gitcmd.NewCommand("cat-file", "--batch"))
return b.batchContent
}
@@ -47,7 +41,7 @@ func (b *catFileBatchLegacy) getBatchCheck() *catFileBatchCommunicator {
if b.batchCheck != nil {
return b.batchCheck
}
b.batchCheck = newCatFileBatch(b.ctx, b.repoPath, gitcmd.NewCommand("cat-file", "--batch-check"))
b.batchCheck = newCatFileBatch(b.ctx, b.repo, gitcmd.NewCommand("cat-file", "--batch-check"))
return b.batchCheck
}

View File

@@ -34,7 +34,7 @@ func (b *catFileBatchCommunicator) Close(err ...error) {
}
// newCatFileBatch opens git cat-file --batch/--batch-check/--batch-command command and prepares the stdin/stdout pipes for communication.
func newCatFileBatch(ctx context.Context, repoPath string, cmdCatFile *gitcmd.Command) *catFileBatchCommunicator {
func newCatFileBatch(ctx context.Context, repo RepositoryFacade, cmdCatFile *gitcmd.Command) *catFileBatchCommunicator {
ctx, ctxCancel := context.WithCancelCause(ctx)
stdinWriter, stdoutReader, stdPipeClose := cmdCatFile.MakeStdinStdoutPipe()
ret := &catFileBatchCommunicator{
@@ -47,7 +47,7 @@ func newCatFileBatch(ctx context.Context, repoPath string, cmdCatFile *gitcmd.Co
stdPipeClose()
}))
err := cmdCatFile.WithDir(repoPath).StartWithStderr(ctx)
err := cmdCatFile.WithRepo(repo).StartWithStderr(ctx)
if err != nil {
log.Error("Unable to start git command %v: %v", cmdCatFile.LogString(), err)
// ideally here it should return the error, but it would require refactoring all callers
@@ -59,7 +59,7 @@ func newCatFileBatch(ctx context.Context, repoPath string, cmdCatFile *gitcmd.Co
go func() {
err := cmdCatFile.WaitWithStderr()
if err != nil && !errors.Is(err, context.Canceled) {
log.Error("cat-file --batch command failed in repo %s, error: %v", repoPath, err)
log.Error("cat-file --batch command failed in repo %s, error: %v", repo.LogString(), err)
}
ret.Close(err)
}()

View File

@@ -35,7 +35,14 @@ type Command struct {
args []string
preErrors []error
configArgs []string
opts runOpts
// Dir is the working dir for the git command, however:
// FIXME: this could be incorrect in many cases, for example:
// * /some/path/.git
// * /some/path/.git/gitea-data/data/repositories/user/repo.git
// If "user/repo.git" is invalid/broken, then running git command in it will use "/some/path/.git", and produce unexpected results
// The correct approach is to use `--git-dir" global argument or "GIT_DIR=..." environment variable.
gitDir string
cmd *exec.Cmd
@@ -44,10 +51,15 @@ type Command struct {
cmdFinished process.FinishedFunc
cmdStartTime time.Time
pipelineFunc func(Context) error
parentPipeFiles []*os.File
parentPipeReaders []*os.File
childrenPipeFiles []*os.File
cmdEnv []string
cmdTimeout time.Duration
// only os.Pipe and in-memory buffers can work with Stdin safely, see https://github.com/golang/go/issues/77227 if the command would exit unexpectedly
cmdStdin io.Reader
cmdStdout io.Writer
@@ -204,23 +216,6 @@ func ToTrustedCmdArgs(args []string) TrustedCmdArgs {
return ret
}
type runOpts struct {
// TODO: this struct should be removed, the fields can be just merged into the command
Env []string
Timeout time.Duration
// Dir is the working dir for the git command, however:
// FIXME: this could be incorrect in many cases, for example:
// * /some/path/.git
// * /some/path/.git/gitea-data/data/repositories/user/repo.git
// If "user/repo.git" is invalid/broken, then running git command in it will use "/some/path/.git", and produce unexpected results
// The correct approach is to use `--git-dir" global argument or "GIT_DIR=..." environment variable.
Dir string
PipelineFunc func(Context) error
}
func commonBaseEnvs() []string {
envs := []string{
// Make Gitea use internal git config only, to prevent conflicts with user's git config
@@ -262,17 +257,17 @@ func CommonCmdServEnvs() []string {
var ErrBrokenCommand = errors.New("git command is broken")
func (c *Command) WithDir(dir string) *Command {
c.opts.Dir = dir
c.gitDir = dir
return c
}
func (c *Command) WithEnv(env []string) *Command {
c.opts.Env = env
c.cmdEnv = env
return c
}
func (c *Command) WithTimeout(timeout time.Duration) *Command {
c.opts.Timeout = timeout
c.cmdTimeout = timeout
return c
}
@@ -361,7 +356,7 @@ func (c *Command) WithStdoutCopy(w io.Writer) *Command {
// The returned error of Run / Wait can be joined errors from the pipeline function, context cause, and command exit error.
// Caller can get the pipeline function's error (if any) by UnwrapPipelineError.
func (c *Command) WithPipelineFunc(f func(ctx Context) error) *Command {
c.opts.PipelineFunc = f
c.pipelineFunc = f
return c
}
@@ -418,7 +413,7 @@ func (c *Command) Start(ctx context.Context) (retErr error) {
c.WithParentCallerInfo()
}
// these logs are for debugging purposes only, so no guarantee of correctness or stability
desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", c.callerInfo, logArgSanitize(c.opts.Dir), cmdLogString)
desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", c.callerInfo, logArgSanitize(c.gitDir), cmdLogString)
log.Debug("git.Command: %s", desc)
_, span := gtprof.GetTracer().Start(ctx, gtprof.TraceSpanGitRun)
@@ -426,24 +421,24 @@ func (c *Command) Start(ctx context.Context) (retErr error) {
span.SetAttributeString(gtprof.TraceAttrFuncCaller, c.callerInfo)
span.SetAttributeString(gtprof.TraceAttrGitCommand, cmdLogString)
if c.opts.Timeout <= 0 {
if c.cmdTimeout <= 0 {
c.cmdCtx, c.cmdCancel, c.cmdFinished = process.GetManager().AddContext(ctx, desc)
} else {
c.cmdCtx, c.cmdCancel, c.cmdFinished = process.GetManager().AddContextTimeout(ctx, c.opts.Timeout, desc)
c.cmdCtx, c.cmdCancel, c.cmdFinished = process.GetManager().AddContextTimeout(ctx, c.cmdTimeout, desc)
}
c.cmdStartTime = time.Now()
c.cmd = exec.CommandContext(c.cmdCtx, c.prog, append(c.configArgs, c.args...)...)
if c.opts.Env == nil {
if c.cmdEnv == nil {
c.cmd.Env = os.Environ()
} else {
c.cmd.Env = c.opts.Env
c.cmd.Env = c.cmdEnv
}
process.SetSysProcAttribute(c.cmd)
c.cmd.Env = append(c.cmd.Env, CommonGitCmdEnvs()...)
c.cmd.Dir = c.opts.Dir
c.cmd.Dir = c.gitDir
c.cmd.Stdout = c.cmdStdout
c.cmd.Stdin = c.cmdStdin
c.cmd.Stderr = c.cmdStderr
@@ -481,8 +476,8 @@ func (c *Command) Wait() error {
c.cmdFinished()
}()
if c.opts.PipelineFunc != nil {
errPipeline := c.opts.PipelineFunc(&cmdContext{Context: c.cmdCtx, cmd: c})
if c.pipelineFunc != nil {
errPipeline := c.pipelineFunc(&cmdContext{Context: c.cmdCtx, cmd: c})
if context.Cause(c.cmdCtx) == nil {
// if the context is not canceled explicitly, we need to discard the unread data,

View File

@@ -25,7 +25,7 @@ type RepositoryFacade interface {
}
func (c *Command) WithRepo(repo RepositoryFacade) *Command {
c.opts.Dir = RepoLocalPath(repo)
c.gitDir = RepoLocalPath(repo)
return c
}

View File

@@ -15,19 +15,19 @@ import (
)
// CatFileBatchCheck runs cat-file with --batch-check
func CatFileBatchCheck(ctx context.Context, cmd *gitcmd.Command, tmpBasePath string) error {
func CatFileBatchCheck(ctx context.Context, cmd *gitcmd.Command, repo git.RepositoryFacade) error {
cmd.AddArguments("cat-file", "--batch-check")
return cmd.WithDir(tmpBasePath).RunWithStderr(ctx)
return cmd.WithRepo(repo).RunWithStderr(ctx)
}
// CatFileBatchCheckAllObjects runs cat-file with --batch-check --batch-all
func CatFileBatchCheckAllObjects(ctx context.Context, cmd *gitcmd.Command, gitRepo *git.Repository) error {
return cmd.AddArguments("cat-file", "--batch-check", "--batch-all-objects").WithRepo(gitRepo).RunWithStderr(ctx)
func CatFileBatchCheckAllObjects(ctx context.Context, cmd *gitcmd.Command, repo git.RepositoryFacade) error {
return cmd.AddArguments("cat-file", "--batch-check", "--batch-all-objects").WithRepo(repo).RunWithStderr(ctx)
}
// CatFileBatch runs cat-file --batch
func CatFileBatch(ctx context.Context, cmd *gitcmd.Command, gitRepo git.RepositoryFacade) error {
return cmd.AddArguments("cat-file", "--batch").WithRepo(gitRepo).RunWithStderr(ctx)
func CatFileBatch(ctx context.Context, cmd *gitcmd.Command, repo git.RepositoryFacade) error {
return cmd.AddArguments("cat-file", "--batch").WithRepo(repo).RunWithStderr(ctx)
}
// BlobsLessThan1024FromCatFileBatchCheck reads a pipeline from cat-file --batch-check and returns the blobs <1024 in size

View File

@@ -9,16 +9,17 @@ import (
"io"
"strings"
"gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd"
)
// RevListObjects run rev-list --objects from headSHA to baseSHA
func RevListObjects(ctx context.Context, cmd *gitcmd.Command, tmpBasePath, headSHA, baseSHA string) error {
func RevListObjects(ctx context.Context, cmd *gitcmd.Command, repo git.RepositoryFacade, headSHA, baseSHA string) error {
cmd.AddArguments("rev-list", "--objects").AddDynamicArguments(headSHA)
if baseSHA != "" {
cmd = cmd.AddArguments("--not").AddDynamicArguments(baseSHA)
}
return cmd.WithDir(tmpBasePath).RunWithStderr(ctx)
return cmd.WithRepo(repo).RunWithStderr(ctx)
}
// BlobsFromRevListObjects reads a RevListAllObjects and only selects blobs

View File

@@ -105,8 +105,8 @@ func IsRepoURLAccessible(ctx context.Context, url string) bool {
}
// InitRepositoryLocal initializes a new Git repository.
func InitRepositoryLocal(ctx context.Context, repoPath string, bare bool, objectFormatName string) error {
err := os.MkdirAll(repoPath, os.ModePerm)
func InitRepositoryLocal(ctx context.Context, localRepoPath string, bare bool, objectFormatName string) error {
err := os.MkdirAll(localRepoPath, os.ModePerm)
if err != nil {
return err
}
@@ -123,7 +123,7 @@ func InitRepositoryLocal(ctx context.Context, repoPath string, bare bool, object
if bare {
cmd.AddArguments("--bare")
}
_, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
_, _, err = cmd.WithDir(localRepoPath).RunStdString(ctx)
return err
}
@@ -234,7 +234,7 @@ type PushOptions struct {
}
// Push pushs local commits to given remote branch.
func Push(ctx context.Context, repoPath string, opts PushOptions) error {
func Push(ctx context.Context, localRepoPath string, opts PushOptions) error {
cmd := gitcmd.NewCommand("push")
if opts.ForceWithLease != "" {
cmd.AddOptionFormat("--force-with-lease=%s", opts.ForceWithLease)
@@ -256,7 +256,7 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
}
cmd.AddDashesAndList(remoteBranchArgs...)
stdout, stderr, err := cmd.WithEnv(opts.Env).WithTimeout(opts.Timeout).WithDir(repoPath).RunStdString(ctx)
stdout, stderr, err := cmd.WithEnv(opts.Env).WithTimeout(opts.Timeout).WithDir(localRepoPath).RunStdString(ctx)
if err != nil {
if strings.Contains(stderr, "non-fast-forward") {
return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err}

View File

@@ -19,11 +19,11 @@ type TemplateSubmoduleCommit struct {
// GetTemplateSubmoduleCommits returns a list of submodules paths and their commits from a repository
// This function is only for generating new repos based on existing template, the template couldn't be too large.
func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submoduleCommits []TemplateSubmoduleCommit, _ error) {
func GetTemplateSubmoduleCommits(ctx context.Context, repo gitcmd.RepositoryFacade) (submoduleCommits []TemplateSubmoduleCommit, _ error) {
cmd := gitcmd.NewCommand("ls-tree", "-r", "--", "HEAD")
stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe()
defer stdoutReaderClose()
err := cmd.WithDir(repoPath).
err := cmd.WithRepo(repo).
WithPipelineFunc(func(ctx gitcmd.Context) error {
scanner := bufio.NewScanner(stdoutReader)
for scanner.Scan() {
@@ -46,11 +46,11 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
// AddTemplateSubmoduleIndexes Adds the given submodules to the git index.
// It is only for generating new repos based on existing template, requires the .gitmodules file to be already present in the work dir.
func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error {
func AddTemplateSubmoduleIndexes(ctx context.Context, tmpRepoPath string, submodules []TemplateSubmoduleCommit) error {
for _, submodule := range submodules {
cmd := gitcmd.NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path)
if stdout, _, err := cmd.WithDir(repoPath).RunStdString(ctx); err != nil {
log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err)
if stdout, _, err := cmd.WithDir(tmpRepoPath).RunStdString(ctx); err != nil {
log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, tmpRepoPath, stdout, err)
return err
}
}

View File

@@ -15,7 +15,7 @@ import (
)
func TestGetTemplateSubmoduleCommits(t *testing.T) {
testRepoPath := filepath.Join(testReposDir, "repo4_submodules")
testRepoPath := mockRepository("repo4_submodules")
submodules, err := GetTemplateSubmoduleCommits(t.Context(), testRepoPath)
require.NoError(t, err)
@@ -41,7 +41,7 @@ func TestAddTemplateSubmoduleIndexes(t *testing.T) {
require.NoError(t, err)
_, _, err = gitcmd.NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").WithDir(tmpDir).RunStdString(ctx)
require.NoError(t, err)
submodules, err := GetTemplateSubmoduleCommits(t.Context(), tmpDir)
submodules, err := GetTemplateSubmoduleCommits(t.Context(), mockRepository(tmpDir))
require.NoError(t, err)
assert.Len(t, submodules, 1)
assert.Equal(t, "new-dir", submodules[0].Path)

View File

@@ -265,20 +265,20 @@ var (
func dummyInfoRefs(ctx *context.Context) {
infoRefsOnce.Do(func() {
tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-info-refs-cache")
tmpEmptyRepoDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-info-refs-cache")
if err != nil {
log.Error("Failed to create temp dir for git-receive-pack cache: %v", err)
return
}
defer cleanup()
if err := git.InitRepositoryLocal(ctx, tmpDir, true, git.Sha1ObjectFormat.Name()); err != nil {
if err := git.InitRepositoryLocal(ctx, tmpEmptyRepoDir, true, git.Sha1ObjectFormat.Name()); err != nil {
log.Error("Failed to init bare repo for git-receive-pack cache: %v", err)
return
}
refs, _, err := gitcmd.NewCommand("receive-pack", "--stateless-rpc", "--advertise-refs", ".").
WithDir(tmpDir).
WithDir(tmpEmptyRepoDir).
RunStdBytes(ctx)
if err != nil {
log.Error(fmt.Sprintf("%v - %s", err, string(refs)))

View File

@@ -7,8 +7,8 @@ import (
"context"
"time"
"gitea.dev/models"
git_model "gitea.dev/models/git"
"gitea.dev/models/repostats"
user_model "gitea.dev/models/user"
"gitea.dev/models/webhook"
"gitea.dev/modules/git/gitcmd"
@@ -69,7 +69,7 @@ func registerCheckRepoStats() {
RunAtStart: true,
Schedule: "@midnight",
}, func(ctx context.Context, _ *user_model.User, _ Config) error {
return models.CheckRepoStats(ctx)
return repostats.CheckRepoStats(ctx)
})
}

View File

@@ -7,9 +7,9 @@ import (
"context"
"fmt"
"gitea.dev/models"
"gitea.dev/models/db"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/repostats"
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/log"
@@ -57,7 +57,7 @@ func checkHooks(ctx context.Context, logger log.Logger, autofix bool) error {
func checkUserStarNum(ctx context.Context, logger log.Logger, autofix bool) error {
if autofix {
if err := models.DoctorUserStarNum(ctx); err != nil {
if err := repostats.DoctorUserStarNum(ctx); err != nil {
logger.Critical("Unable update User Stars numbers")
return err
}

View File

@@ -13,10 +13,10 @@ import (
"strings"
"time"
"gitea.dev/models"
"gitea.dev/models/db"
issues_model "gitea.dev/models/issues"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/repostats"
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd"
@@ -965,7 +965,7 @@ func (g *GiteaLocalUploader) Finish(ctx context.Context) error {
return err
}
if err := models.UpdateRepoStats(ctx, g.repo.ID); err != nil {
if err := repostats.UpdateRepoStats(ctx, g.repo.ID); err != nil {
return err
}

View File

@@ -64,7 +64,7 @@ func LFSPush(ctx context.Context, tmpBasePath string, tmpRepo git.RepositoryFaca
// 3. Run batch-check on the objects retrieved from rev-list
wg.Go(func() error {
return pipeline.CatFileBatchCheck(ctx, cmd3BathCheck, tmpBasePath)
return pipeline.CatFileBatchCheck(ctx, cmd3BathCheck, tmpRepo)
})
// 2. Check each object retrieved rejecting those without names as they will be commits or trees
@@ -74,7 +74,7 @@ func LFSPush(ctx context.Context, tmpBasePath string, tmpRepo git.RepositoryFaca
// 1. Run rev-list objects from mergeHead to mergeBase
wg.Go(func() error {
return pipeline.RevListObjects(ctx, cmd1RevList, tmpBasePath, mergeHeadSHA, mergeBaseSHA)
return pipeline.RevListObjects(ctx, cmd1RevList, tmpRepo, mergeHeadSHA, mergeBaseSHA)
})
return wg.Wait()

View File

@@ -38,7 +38,7 @@ type mergeContext struct {
func (ctx *mergeContext) PrepareGitCmd(cmd *gitcmd.Command) *gitcmd.Command {
ctx.outbuf.Reset()
return cmd.WithEnv(ctx.env).
WithDir(ctx.tmpBasePath).
WithRepo(ctx.tmpRepo).
WithParentCallerInfo().
WithStdoutBuffer(ctx.outbuf)
}
@@ -77,7 +77,7 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque
trackingCommitID, _, err := gitcmd.NewCommand("show-ref", "--hash").
AddDynamicArguments(git.BranchPrefix + tmpRepoTrackingBranch).
WithEnv(mergeCtx.env).
WithDir(mergeCtx.tmpBasePath).
WithRepo(mergeCtx.tmpRepo).
RunStdString(ctx)
if err != nil {
defer cancel()
@@ -154,7 +154,7 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
}
defer sparseCheckoutListFile.Close() // we will close it earlier but we need to ensure it is closed if there is an error
if err := getDiffTree(ctx, ctx.tmpBasePath, tmpRepoBaseBranch, tmpRepoTrackingBranch, sparseCheckoutListFile); err != nil {
if err := getDiffTree(ctx, ctx.tmpRepo, tmpRepoBaseBranch, tmpRepoTrackingBranch, sparseCheckoutListFile); err != nil {
log.Error("%-v getDiffTree(%s, %s, %s): %v", ctx.pr, ctx.tmpBasePath, tmpRepoBaseBranch, tmpRepoTrackingBranch, err)
return fmt.Errorf("getDiffTree: %w", err)
}
@@ -206,13 +206,13 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
}
// getDiffTree returns a string containing all the files that were changed between headBranch and baseBranch
// the filenames are escaped so as to fit the format required for .git/info/sparse-checkout
func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, out io.Writer) error {
// the filenames are escaped to fit the format required for .git/info/sparse-checkout
func getDiffTree(ctx context.Context, repo git.RepositoryFacade, baseBranch, headBranch string, out io.Writer) error {
cmd := gitcmd.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root")
diffOutReader, diffOutReaderClose := cmd.MakeStdoutPipe()
defer diffOutReaderClose()
err := cmd.AddDynamicArguments(baseBranch, headBranch).
WithDir(repoPath).
WithRepo(repo).
WithPipelineFunc(func(ctx gitcmd.Context) error {
// Now scan the output from the command
scanner := bufio.NewScanner(diffOutReader)

View File

@@ -16,7 +16,7 @@ import (
// getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request.
func getRebaseAmendMessage(ctx *mergeContext, baseGitRepo *git.Repository) (message string, err error) {
// Get existing commit message.
commitMessage, _, err := gitcmd.NewCommand("show", "--format=%B", "-s").WithDir(ctx.tmpBasePath).RunStdString(ctx)
commitMessage, _, err := gitcmd.NewCommand("show", "--format=%B", "-s").WithRepo(ctx.tmpRepo).RunStdString(ctx)
if err != nil {
return "", err
}
@@ -76,7 +76,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
cmdCommit := gitcmd.NewCommand("commit", "--amend").
AddOptionFormat("--message=%s", newMessage)
addCommitSigningOptions(cmdCommit, ctx.signKey)
if err := cmdCommit.WithDir(ctx.tmpBasePath).Run(ctx); err != nil {
if err := cmdCommit.WithRepo(ctx.tmpRepo).Run(ctx); err != nil {
log.Error("Unable to amend commit message: %v", err)
return err
}

View File

@@ -73,23 +73,23 @@ func checkPullRequestMergeableByTmpRepo(ctx context.Context, pr *issues_model.Pu
}
defer cancel()
gitRepo, err := git.OpenRepositoryLocal(prCtx.tmpBasePath)
tmpGitRepo, err := git.OpenRepositoryLocal(prCtx.tmpBasePath)
if err != nil {
return fmt.Errorf("OpenRepository: %w", err)
}
defer gitRepo.Close()
defer tmpGitRepo.Close()
// 1. update merge base
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base", "--", tmpRepoBaseBranch, tmpRepoTrackingBranch).WithDir(prCtx.tmpBasePath).RunStdString(ctx)
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base", "--", tmpRepoBaseBranch, tmpRepoTrackingBranch).WithRepo(prCtx.tmpRepo).RunStdString(ctx)
if err != nil {
var err2 error
pr.MergeBase, err2 = gitRepo.GetRefCommitID(ctx, git.BranchPrefix+tmpRepoBaseBranch)
pr.MergeBase, err2 = tmpGitRepo.GetRefCommitID(ctx, git.BranchPrefix+tmpRepoBaseBranch)
if err2 != nil {
return fmt.Errorf("GetMergeBase: %v and can't find commit ID for base: %w", err, err2)
}
}
pr.MergeBase = strings.TrimSpace(pr.MergeBase)
if pr.HeadCommitID, err = gitRepo.GetRefCommitID(ctx, git.BranchPrefix+tmpRepoTrackingBranch); err != nil {
if pr.HeadCommitID, err = tmpGitRepo.GetRefCommitID(ctx, git.BranchPrefix+tmpRepoTrackingBranch); err != nil {
return fmt.Errorf("GetBranchCommitID: can't find commit ID for head: %w", err)
}
@@ -99,7 +99,7 @@ func checkPullRequestMergeableByTmpRepo(ctx context.Context, pr *issues_model.Pu
}
// 2. Check for conflicts
conflicts, err := checkConflictsByTmpRepo(ctx, pr, gitRepo, prCtx.tmpBasePath)
conflicts, err := checkConflictsByTmpRepo(ctx, pr, tmpGitRepo, prCtx.tmpBasePath)
if err != nil {
return err
}
@@ -110,7 +110,7 @@ func checkPullRequestMergeableByTmpRepo(ctx context.Context, pr *issues_model.Pu
}
// 3. Check for protected files changes
if err = checkPullFilesProtection(ctx, pr, gitRepo, tmpRepoTrackingBranch); err != nil {
if err = checkPullFilesProtection(ctx, pr, tmpGitRepo, tmpRepoTrackingBranch); err != nil {
return fmt.Errorf("pr.CheckPullFilesProtection(): %w", err)
}
@@ -127,7 +127,7 @@ func (e *errMergeConflict) Error() string {
return "conflict detected at: " + e.filename
}
func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, filesToRemove *[]string, filesToAdd *[]git.IndexObjectInfo) error {
func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, tmpGitRepo *git.Repository, filesToRemove *[]string, filesToAdd *[]git.IndexObjectInfo) error {
log.Trace("Attempt to merge:\n%v", file)
switch {
@@ -182,7 +182,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}
// Need to get the objects from the object db to attempt to merge
root, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).WithDir(tmpBasePath).RunStdString(ctx)
root, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).WithRepo(tmpGitRepo).RunStdString(ctx)
if err != nil {
return fmt.Errorf("unable to get root object: %s at path: %s for merging. Error: %w", file.stage1.sha, file.stage1.path, err)
}
@@ -191,7 +191,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
_ = util.Remove(filepath.Join(tmpBasePath, root))
}()
base, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).WithDir(tmpBasePath).RunStdString(ctx)
base, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).WithRepo(tmpGitRepo).RunStdString(ctx)
if err != nil {
return fmt.Errorf("unable to get base object: %s at path: %s for merging. Error: %w", file.stage2.sha, file.stage2.path, err)
}
@@ -199,7 +199,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
defer func() {
_ = util.Remove(base)
}()
head, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).WithDir(tmpBasePath).RunStdString(ctx)
head, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).WithRepo(tmpGitRepo).RunStdString(ctx)
if err != nil {
return fmt.Errorf("unable to get head object:%s at path: %s for merging. Error: %w", file.stage3.sha, file.stage3.path, err)
}
@@ -209,13 +209,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}()
// now git merge-file annoyingly takes a different order to the merge-tree ...
_, _, conflictErr := gitcmd.NewCommand("merge-file").AddDynamicArguments(base, root, head).WithDir(tmpBasePath).RunStdString(ctx)
_, _, conflictErr := gitcmd.NewCommand("merge-file").AddDynamicArguments(base, root, head).WithRepo(tmpGitRepo).RunStdString(ctx)
if conflictErr != nil {
return &errMergeConflict{file.stage2.path}
}
// base now contains the merged data
hash, _, err := gitcmd.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).WithDir(tmpBasePath).RunStdString(ctx)
hash, _, err := gitcmd.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).WithRepo(tmpGitRepo).RunStdString(ctx)
if err != nil {
return err
}
@@ -240,7 +240,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
defer cancel()
// First we use read-tree to do a simple three-way merge
if err := gitcmd.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).WithDir(gitPath).RunWithStderr(ctx); err != nil {
if err := gitcmd.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).WithRepo(gitRepo).RunWithStderr(ctx); err != nil {
log.Error("Unable to run read-tree -m! Error: %v", err)
return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err)
}
@@ -250,7 +250,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
// Then we use git ls-files -u to list the unmerged files and collate the triples in unmergedfiles
unmerged := make(chan *unmergedFile)
go unmergedFiles(ctx, gitPath, unmerged)
go unmergedFiles(ctx, gitRepo, unmerged)
defer func() {
cancel()
@@ -273,7 +273,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
}
// OK now we have the unmerged file triplet attempt to merge it
if err := attemptMerge(ctx, file, gitPath, &filesToRemove, &filesToAdd); err != nil {
if err := attemptMerge(ctx, file, gitPath, gitRepo, &filesToRemove, &filesToAdd); err != nil {
if conflictErr, ok := err.(*errMergeConflict); ok {
log.Trace("Conflict: %s in %s", conflictErr.filename, description)
conflict = true
@@ -298,14 +298,14 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
return conflict, conflictedFiles, nil
}
func checkConflictsByTmpRepo(ctx context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository, tmpBasePath string) (bool, error) {
func checkConflictsByTmpRepo(ctx context.Context, pr *issues_model.PullRequest, tmpGitRepo *git.Repository, tmpBasePath string) (bool, error) {
// 1. checkConflictsByTmpRepo resets the conflict status - therefore - reset the conflict status
pr.ConflictedFiles = nil
// 2. AttemptThreeWayMerge first - this is much quicker than plain patch to base
description := fmt.Sprintf("PR[%d] %s/%s#%d", pr.ID, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Index)
conflict, conflictFiles, err := AttemptThreeWayMerge(ctx,
tmpBasePath, gitRepo, pr.MergeBase, tmpRepoBaseBranch, tmpRepoTrackingBranch, description)
tmpBasePath, tmpGitRepo, pr.MergeBase, tmpRepoBaseBranch, tmpRepoTrackingBranch, description)
if err != nil {
return false, err
}
@@ -314,13 +314,13 @@ func checkConflictsByTmpRepo(ctx context.Context, pr *issues_model.PullRequest,
// No conflicts detected so we need to check if the patch is empty...
// a. Write the newly merged tree and check the new tree-hash
var treeHash string
treeHash, _, err = gitcmd.NewCommand("write-tree").WithDir(tmpBasePath).RunStdString(ctx)
treeHash, _, err = gitcmd.NewCommand("write-tree").WithRepo(tmpGitRepo).RunStdString(ctx)
if err != nil {
lsfiles, _, _ := gitcmd.NewCommand("ls-files", "-u").WithDir(tmpBasePath).RunStdString(ctx)
lsfiles, _, _ := gitcmd.NewCommand("ls-files", "-u").WithRepo(tmpGitRepo).RunStdString(ctx)
return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles)
}
treeHash = strings.TrimSpace(treeHash)
baseTree, err := gitRepo.GetTree(ctx, tmpRepoBaseBranch)
baseTree, err := tmpGitRepo.GetTree(ctx, tmpRepoBaseBranch)
if err != nil {
return false, err
}
@@ -365,11 +365,11 @@ func (err ErrFilePathProtected) Unwrap() error {
}
// CheckFileProtection check file Protection
func CheckFileProtection(ctx context.Context, repo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) {
func CheckFileProtection(ctx context.Context, gitRepo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) {
if len(patterns) == 0 {
return nil, nil
}
affectedFiles, err := git.GetAffectedFiles(ctx, repo, branchName, oldCommitID, newCommitID, env)
affectedFiles, err := git.GetAffectedFiles(ctx, gitRepo, branchName, oldCommitID, newCommitID, env)
if err != nil {
return nil, err
}
@@ -395,11 +395,11 @@ func CheckFileProtection(ctx context.Context, repo *git.Repository, branchName,
}
// CheckUnprotectedFiles check if the commit only touches unprotected files
func CheckUnprotectedFiles(ctx context.Context, repo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, env []string) (bool, error) {
func CheckUnprotectedFiles(ctx context.Context, gitRepo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, env []string) (bool, error) {
if len(patterns) == 0 {
return false, nil
}
affectedFiles, err := git.GetAffectedFiles(ctx, repo, branchName, oldCommitID, newCommitID, env)
affectedFiles, err := git.GetAffectedFiles(ctx, gitRepo, branchName, oldCommitID, newCommitID, env)
if err != nil {
return false, err
}

View File

@@ -12,6 +12,7 @@ import (
"strconv"
"strings"
"gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/log"
)
@@ -53,7 +54,7 @@ func (line *lsFileLine) String() string {
// readUnmergedLsFileLines calls git ls-files -u -z and parses the lines into mode-sha-stage-path quadruplets
// it will push these to the provided channel closing it at the end
func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan chan *lsFileLine) {
func readUnmergedLsFileLines(ctx context.Context, tmpRepo git.RepositoryFacade, outputChan chan *lsFileLine) {
defer func() {
// Always close the outputChan at the end of this function
close(outputChan)
@@ -62,7 +63,7 @@ func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan
cmd := gitcmd.NewCommand("ls-files", "-u", "-z")
lsFilesReader, lsFilesReaderClose := cmd.MakeStdoutPipe()
defer lsFilesReaderClose()
err := cmd.WithDir(tmpBasePath).
err := cmd.WithRepo(tmpRepo).
WithPipelineFunc(func(gitcmd.Context) error {
bufferedReader := bufio.NewReader(lsFilesReader)
@@ -123,7 +124,7 @@ func (u *unmergedFile) String() string {
// unmergedFiles will collate the output from readUnstagedLsFileLines in to file triplets and send them
// to the provided channel, closing at the end.
func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmergedFile) {
func unmergedFiles(ctx context.Context, tmpGitRepo git.RepositoryFacade, unmerged chan *unmergedFile) {
defer func() {
// Always close the channel
close(unmerged)
@@ -131,7 +132,7 @@ func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmer
ctx, cancel := context.WithCancel(ctx)
lsFileLineChan := make(chan *lsFileLine, 10) // give lsFileLineChan a buffer
go readUnmergedLsFileLines(ctx, tmpBasePath, lsFileLineChan)
go readUnmergedLsFileLines(ctx, tmpGitRepo, lsFileLineChan)
defer func() {
cancel()
for range lsFileLineChan {

View File

@@ -536,7 +536,7 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest,
stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe()
defer stdoutReaderClose()
if err := cmd.WithDir(prCtx.tmpBasePath).
if err := cmd.WithRepo(prCtx.tmpRepo).
WithPipelineFunc(func(ctx gitcmd.Context) error {
return util.IsEmptyReader(stdoutReader)
}).

View File

@@ -40,7 +40,7 @@ type prTmpRepoContext struct {
// Do NOT use it with gitcmd.RunStd*() functions, otherwise it will panic
func (ctx *prTmpRepoContext) PrepareGitCmd(cmd *gitcmd.Command) *gitcmd.Command {
ctx.outbuf.Reset()
return cmd.WithDir(ctx.tmpBasePath).WithStdoutBuffer(ctx.outbuf)
return cmd.WithRepo(ctx.tmpRepo).WithStdoutBuffer(ctx.outbuf)
}
// createTemporaryRepoForPR creates a temporary repo with "base" for pr.BaseBranch and "tracking" for pr.HeadBranch

View File

@@ -29,7 +29,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
// Determine the old merge-base before the rebase - we use this for LFS push later on
oldMergeBase, _, _ := gitcmd.NewCommand("merge-base").AddDashesAndList(tmpRepoBaseBranch, tmpRepoTrackingBranch).
WithDir(mergeCtx.tmpBasePath).RunStdString(ctx)
WithRepo(mergeCtx.tmpRepo).RunStdString(ctx)
oldMergeBase = strings.TrimSpace(oldMergeBase)
// Rebase the tracking branch on to the base as the staging branch
@@ -81,7 +81,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
pr.ID,
pr.Index,
)).
WithDir(mergeCtx.tmpBasePath).
WithRepo(mergeCtx.tmpRepo).
WithStdoutBuffer(mergeCtx.outbuf).
RunWithStderr(ctx); err != nil {
if strings.Contains(err.Stderr(), "non-fast-forward") {

View File

@@ -168,7 +168,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
cmdApply.AddArguments("-3")
}
if err := cmdApply.WithDir(t.basePath).
if err := cmdApply.WithRepo(t.gitRepo).
WithStdinBytes([]byte(opts.Content)).
RunWithStderr(ctx); err != nil {
return nil, fmt.Errorf("git apply error: %w", err)

View File

@@ -6,7 +6,6 @@ package files
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
@@ -18,7 +17,6 @@ import (
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/log"
repo_module "gitea.dev/modules/repository"
"gitea.dev/modules/setting"
"gitea.dev/modules/util"
@@ -76,7 +74,7 @@ func (t *TemporaryUploadRepository) Clone(ctx context.Context, branch string, ba
Name: t.repo.Name,
}
}
return fmt.Errorf("Clone: %w %s", err, stderr)
return fmt.Errorf("temp repo clone error: %w, %s", err, stderr)
}
gitRepo, err := git.OpenRepositoryLocal(t.basePath)
if err != nil {
@@ -101,7 +99,7 @@ func (t *TemporaryUploadRepository) Init(ctx context.Context, objectFormatName s
// SetDefaultIndex sets the git index to our HEAD
func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error {
if err := gitcmd.NewCommand("read-tree", "HEAD").WithDir(t.basePath).RunWithStderr(ctx); err != nil {
if err := gitcmd.NewCommand("read-tree", "HEAD").WithRepo(t.gitRepo).RunWithStderr(ctx); err != nil {
return fmt.Errorf("SetDefaultIndex: %w", err)
}
return nil
@@ -109,7 +107,7 @@ func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error {
// RefreshIndex looks at the current index and checks to see if merges or updates are needed by checking stat() information.
func (t *TemporaryUploadRepository) RefreshIndex(ctx context.Context) error {
if err := gitcmd.NewCommand("update-index", "--refresh").WithDir(t.basePath).RunWithStderr(ctx); err != nil {
if err := gitcmd.NewCommand("update-index", "--refresh").WithRepo(t.gitRepo).RunWithStderr(ctx); err != nil {
return fmt.Errorf("RefreshIndex: %w", err)
}
return nil
@@ -119,7 +117,7 @@ func (t *TemporaryUploadRepository) RefreshIndex(ctx context.Context) error {
func (t *TemporaryUploadRepository) LsFiles(ctx context.Context, filenames ...string) ([]string, error) {
stdOut := new(bytes.Buffer)
if err := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...).
WithDir(t.basePath).
WithRepo(t.gitRepo).
WithStdoutBuffer(stdOut).
RunWithStderr(ctx); err != nil {
return nil, fmt.Errorf("unable to run git ls-files for temporary repo of: %s, error: %w", t.repo.FullName(), err)
@@ -136,7 +134,7 @@ func (t *TemporaryUploadRepository) LsFiles(ctx context.Context, filenames ...st
func (t *TemporaryUploadRepository) RemoveRecursivelyFromIndex(ctx context.Context, path string) error {
_, _, err := gitcmd.NewCommand("rm", "--cached", "-r").
AddDynamicArguments(path).
WithDir(t.basePath).
WithRepo(t.gitRepo).
RunStdBytes(ctx)
return err
}
@@ -157,7 +155,7 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, fi
}
if err := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info").
WithDir(t.basePath).
WithRepo(t.gitRepo).
WithStdinBytes(stdIn.Bytes()).
RunWithStderr(ctx); err != nil {
return fmt.Errorf("unable to update-index for temporary repo: %q, error: %w", t.repo.FullName(), err)
@@ -169,7 +167,7 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, fi
func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, content io.Reader) (string, error) {
stdOut := new(bytes.Buffer)
if err := gitcmd.NewCommand("hash-object", "-w", "--stdin").
WithDir(t.basePath).
WithRepo(t.gitRepo).
WithStdoutBuffer(stdOut).
WithStdinCopy(content).
RunWithStderr(ctx); err != nil {
@@ -182,7 +180,7 @@ func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, cont
// AddObjectToIndex adds the provided object hash to the index with the provided mode and path
func (t *TemporaryUploadRepository) AddObjectToIndex(ctx context.Context, mode, objectHash, objectPath string) error {
cmd := gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo").
AddDynamicArguments(mode + "," + objectHash + "," + objectPath).WithDir(t.basePath)
AddDynamicArguments(mode + "," + objectHash + "," + objectPath).WithRepo(t.gitRepo)
if err := cmd.RunWithStderr(ctx); err != nil {
if matched, _ := regexp.MatchString(".*Invalid path '.*", err.Stderr()); matched {
return ErrFilePathInvalid{
@@ -197,10 +195,9 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(ctx context.Context, mode,
// WriteTree writes the current index as a tree to the object db and returns its hash
func (t *TemporaryUploadRepository) WriteTree(ctx context.Context) (string, error) {
stdout, _, err := gitcmd.NewCommand("write-tree").WithDir(t.basePath).RunStdString(ctx)
stdout, _, err := gitcmd.NewCommand("write-tree").WithRepo(t.gitRepo).RunStdString(ctx)
if err != nil {
log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err)
return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %w", t.repo.FullName(), err)
return "", fmt.Errorf("unable to write-tree in temporary repo for %s, error: %w", t.repo.FullName(), err)
}
return strings.TrimSpace(stdout), nil
}
@@ -215,10 +212,9 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ctx context.Context, ref
if ref == "" {
ref = "HEAD"
}
stdout, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(ref).WithDir(t.basePath).RunStdString(ctx)
stdout, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(ref).WithRepo(t.gitRepo).RunStdString(ctx)
if err != nil {
log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err)
return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %w", ref, t.repo.FullName(), err)
return "", fmt.Errorf("unable to rev-parse ref %s in temporary repo for: %s, error: %w", ref, t.repo.FullName(), err)
}
return strings.TrimSpace(stdout), nil
}
@@ -328,11 +324,11 @@ func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *Commit
stdout := new(bytes.Buffer)
if err := cmdCommitTree.
WithEnv(env).
WithDir(t.basePath).
WithRepo(t.gitRepo).
WithStdoutBuffer(stdout).
WithStdinBytes(messageBytes.Bytes()).
RunWithStderr(ctx); err != nil {
return "", fmt.Errorf("unable to commit-tree in temporary repo: %s Error: %w", t.repo.FullName(), err)
return "", fmt.Errorf("unable to commit-tree in temporary repo: %s, error: %w", t.repo.FullName(), err)
}
return strings.TrimSpace(stdout.String()), nil
}
@@ -351,10 +347,7 @@ func (t *TemporaryUploadRepository) Push(ctx context.Context, doer *user_model.U
} else if git.IsErrPushRejected(err) {
return err
}
log.Error("Unable to push back to repo from temporary repo: %s (%s)\nError: %v",
t.repo.FullName(), t.basePath, err)
return fmt.Errorf("Unable to push back to repo from temporary repo: %s (%s) Error: %v",
t.repo.FullName(), t.basePath, err)
return fmt.Errorf("unable to push back to repo %s from temporary repo, error: %w", t.repo.FullName(), err)
}
return nil
}
@@ -367,7 +360,7 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context, oldContent, n
defer stdoutReaderClose()
err := cmd.WithTimeout(30 * time.Second).
WithDir(t.basePath).
WithRepo(t.gitRepo).
WithPipelineFunc(func(ctx gitcmd.Context) error {
var diffErr error
diff, diffErr = gitdiff.ParsePatch(ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
@@ -390,16 +383,10 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context, oldContent, n
// GetBranchCommit Gets the commit object of the given branch
func (t *TemporaryUploadRepository) GetBranchCommit(ctx context.Context, branch string) (*git.Commit, error) {
if t.gitRepo == nil {
return nil, errors.New("repository has not been cloned")
}
return t.gitRepo.GetBranchCommit(ctx, branch)
}
// GetCommit Gets the commit object of the given commit ID
func (t *TemporaryUploadRepository) GetCommit(ctx context.Context, commitID string) (*git.Commit, error) {
if t.gitRepo == nil {
return nil, errors.New("repository has not been cloned")
}
return t.gitRepo.GetCommit(ctx, commitID)
}

View File

@@ -218,7 +218,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
}
// Get active submodules from the template
submodules, err := git.GetTemplateSubmoduleCommits(ctx, tmpDir)
submodules, err := git.GetTemplateSubmoduleCommits(ctx, templateRepo)
if err != nil {
return fmt.Errorf("GetTemplateSubmoduleCommits: %w", err)
}