refactor: clean up git repo and model migration packages (#38564)

enable the golangci depguard lint rule: deny "models" and its sub
packages in "modelmigration" package.
This commit is contained in:
wxiaoguang
2026-07-23 09:22:26 +08:00
committed by GitHub
parent fa64b4627a
commit 1acf93d854
43 changed files with 243 additions and 167 deletions
+3 -3
View File
@@ -55,10 +55,10 @@ linters:
files: files:
- '**/modelmigration/**/*.go' - '**/modelmigration/**/*.go'
deny: deny:
- pkg: gitea.dev/models$ # FIXME: it should deny all sub packages like "gitea.dev/models/repo" - pkg: gitea.dev/models
desc: migrations must not depend on the models package desc: "migrations must not depend on the models package. HINT: MIGRATION-STRUCT-FROZEN"
- pkg: gitea.dev/modules/structs - pkg: gitea.dev/modules/structs
desc: migrations must not depend on modules/structs (API structures change over time) desc: "migrations must not depend on modules/structs. HINT: MIGRATION-STRUCT-FROZEN"
nolintlint: nolintlint:
allow-unused: false allow-unused: false
require-explanation: true require-explanation: true
+6 -7
View File
@@ -11,19 +11,14 @@ import (
"regexp" "regexp"
"strings" "strings"
"gitea.dev/models/db" "gitea.dev/models/db" //nolint:depguard // allow to access db in migration
"gitea.dev/modules/log" "gitea.dev/modules/log"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"xorm.io/builder"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
) )
// Migrations should never use model structs directly, because the model structs can be different in different releases.
// e.g. if one migration uses "User" model, it works in the early releases, then one day,
// when the User model changes, the migration breaks because it will use the new (incorrect) User model,
// it should only use the old User model. The same to "modules/structs".
// However, many the existing migrations already abuses "modules/structs" (search "gitea.dev/models/" in the migrations).
// TODO: need to fully decouple the migration package and models & structs package
type ( type (
EngineMigration = db.EngineMigration EngineMigration = db.EngineMigration
Session = db.Session Session = db.Session
@@ -519,3 +514,7 @@ func ModifyColumn(x EngineMigration, tableName string, col *schemas.Column) erro
} }
return nil return nil
} }
func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
return db.Iterate(ctx, cond, f)
}
-13
View File
@@ -1,13 +0,0 @@
// 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)
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package base
import (
"gitea.dev/models/db" //nolint:depguard // allow to access db in migration
"gitea.dev/modules/git/gitrepo"
)
// HINT: MIGRATION-STRUCT-FROZEN: the structs used in migrations should not be affected by the changes happen in the future,
// because the details can be different in different releases.
// e.g. if one migration uses "User" model, it works in the early releases,
// then one day, when the User model changes, the existing migration will break because it will use the new (incorrect) User model,
// it should only use the old User model.
//
// Related: "models", "modules/structs", git repo directory layout on the disk, etc.
//
// If changes happen, the old migrations need to use a snapshot struct/function (copy the code and freeze)
type ResourceIndex = db.ResourceIndex
func LocalCodeGitRepo(ownerName, repoName string) gitrepo.RepositoryFacade {
return gitrepo.CodeRepoByName(ownerName, repoName)
}
+2 -2
View File
@@ -9,8 +9,8 @@ import (
"testing" "testing"
"gitea.dev/modelmigration/base" "gitea.dev/modelmigration/base"
"gitea.dev/models/db" "gitea.dev/models/db" //nolint:depguard // allow to access db in migration tests
"gitea.dev/models/unittest" "gitea.dev/models/unittest" //nolint:depguard // allow to access db in migration tests
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/modules/testlogger" "gitea.dev/modules/testlogger"
+1 -1
View File
@@ -5,7 +5,7 @@ package v1_17
import ( import (
"gitea.dev/modelmigration/base" "gitea.dev/modelmigration/base"
packages_model "gitea.dev/models/packages" packages_model "gitea.dev/models/packages" //nolint:depguard // only consts are used
container_module "gitea.dev/modules/packages/container" container_module "gitea.dev/modules/packages/container"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
+11 -2
View File
@@ -7,7 +7,7 @@ import (
"fmt" "fmt"
"gitea.dev/modelmigration/base" "gitea.dev/modelmigration/base"
"gitea.dev/models/issues" "gitea.dev/modules/timeutil"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -19,6 +19,15 @@ func UpdateOpenMilestoneCounts(x base.EngineMigration) error {
return fmt.Errorf("error selecting open milestone IDs: %w", err) return fmt.Errorf("error selecting open milestone IDs: %w", err)
} }
type Milestone struct {
ID int64
IsClosed bool
NumIssues int
NumClosedIssues int
Completeness int
UpdatedUnix timeutil.TimeStamp
}
for _, id := range openMilestoneIDs { for _, id := range openMilestoneIDs {
_, err := x.ID(id). _, err := x.ID(id).
Cols("num_issues", "num_closed_issues"). Cols("num_issues", "num_closed_issues").
@@ -31,7 +40,7 @@ func UpdateOpenMilestoneCounts(x base.EngineMigration) error {
"is_closed": true, "is_closed": true,
}, },
)). )).
Update(&issues.Milestone{}) Update(&Milestone{})
if err != nil { if err != nil {
return fmt.Errorf("error updating issue counts in milestone %d: %w", id, err) return fmt.Errorf("error updating issue counts in milestone %d: %w", id, err)
} }
+22 -4
View File
@@ -7,16 +7,34 @@ import (
"testing" "testing"
"gitea.dev/modelmigration/migrationtest" "gitea.dev/modelmigration/migrationtest"
"gitea.dev/models/issues" "gitea.dev/modules/timeutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func Test_UpdateOpenMilestoneCounts(t *testing.T) { func Test_UpdateOpenMilestoneCounts(t *testing.T) {
type ExpectedMilestone issues.Milestone type Issue struct {
ID int64
RepoID int64
Index int64
MilestoneID int64
IsClosed bool
UpdatedUnix timeutil.TimeStamp
}
type Milestone struct {
ID int64
IsClosed bool
NumIssues int
NumClosedIssues int
Completeness int
UpdatedUnix timeutil.TimeStamp
}
type ExpectedMilestone Milestone
// Prepare and load the testing database // Prepare and load the testing database
x, deferable := migrationtest.PrepareTestEnv(t, 0, new(issues.Milestone), new(ExpectedMilestone), new(issues.Issue)) x, deferable := migrationtest.PrepareTestEnv(t, 0, new(Milestone), new(ExpectedMilestone), new(Issue))
defer deferable() defer deferable()
if x == nil || t.Failed() { if x == nil || t.Failed() {
return return
@@ -32,7 +50,7 @@ func Test_UpdateOpenMilestoneCounts(t *testing.T) {
return return
} }
got := []issues.Milestone{} got := []Milestone{}
if err := x.Table("milestone").Asc("id").Find(&got); !assert.NoError(t, err) { if err := x.Table("milestone").Asc("id").Find(&got); !assert.NoError(t, err) {
return return
} }
+1 -2
View File
@@ -5,7 +5,6 @@ package v1_19
import ( import (
"gitea.dev/modelmigration/base" "gitea.dev/modelmigration/base"
"gitea.dev/models/db"
"gitea.dev/modules/timeutil" "gitea.dev/modules/timeutil"
) )
@@ -96,7 +95,7 @@ func AddActionsTables(x base.EngineMigration) error {
NumClosedActionRuns int `xorm:"NOT NULL DEFAULT 0"` NumClosedActionRuns int `xorm:"NOT NULL DEFAULT 0"`
} }
type ActionRunIndex db.ResourceIndex type ActionRunIndex base.ResourceIndex
type ActionTask struct { type ActionTask struct {
ID int64 ID int64
+1 -2
View File
@@ -8,7 +8,6 @@ import (
"errors" "errors"
"gitea.dev/modelmigration/base" "gitea.dev/modelmigration/base"
"gitea.dev/models/db"
"gitea.dev/modules/timeutil" "gitea.dev/modules/timeutil"
) )
@@ -60,7 +59,7 @@ func AddBranchTable(x base.EngineMigration) error {
} }
branches := make([]Branch, 0, 100) branches := make([]Branch, 0, 100)
if err := db.Iterate(context.Background(), nil, func(ctx context.Context, deletedBranch *DeletedBranch) error { if err := base.Iterate(context.Background(), nil, func(ctx context.Context, deletedBranch *DeletedBranch) error {
branches = append(branches, Branch{ branches = append(branches, Branch{
RepoID: deletedBranch.RepoID, RepoID: deletedBranch.RepoID,
Name: deletedBranch.Name, Name: deletedBranch.Name,
+19 -15
View File
@@ -9,6 +9,24 @@ import (
"gitea.dev/modules/timeutil" "gitea.dev/modules/timeutil"
) )
type ProjectBoardV293 struct {
ID int64 `xorm:"pk autoincr"`
Title string
Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
Sorting int8 `xorm:"NOT NULL DEFAULT 0"`
Color string `xorm:"VARCHAR(7)"`
ProjectID int64 `xorm:"INDEX NOT NULL"`
CreatorID int64 `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
func (ProjectBoardV293) TableName() string {
return "project_board"
}
// CheckProjectColumnsConsistency ensures there is exactly one default board per project present // CheckProjectColumnsConsistency ensures there is exactly one default board per project present
func CheckProjectColumnsConsistency(x base.EngineMigration) error { func CheckProjectColumnsConsistency(x base.EngineMigration) error {
sess := x.NewSession() sess := x.NewSession()
@@ -25,20 +43,6 @@ func CheckProjectColumnsConsistency(x base.EngineMigration) error {
BoardID int64 BoardID int64
} }
type ProjectBoard struct {
ID int64 `xorm:"pk autoincr"`
Title string
Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
Sorting int8 `xorm:"NOT NULL DEFAULT 0"`
Color string `xorm:"VARCHAR(7)"`
ProjectID int64 `xorm:"INDEX NOT NULL"`
CreatorID int64 `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
for { for {
if err := sess.Begin(); err != nil { if err := sess.Begin(); err != nil {
return err return err
@@ -56,7 +60,7 @@ func CheckProjectColumnsConsistency(x base.EngineMigration) error {
} }
for _, p := range projects { for _, p := range projects {
if _, err := sess.Insert(ProjectBoard{ if _, err := sess.Insert(ProjectBoardV293{
ProjectID: p.ID, ProjectID: p.ID,
Default: true, Default: true,
Title: "Uncategorized", Title: "Uncategorized",
+26 -6
View File
@@ -7,14 +7,30 @@ import (
"testing" "testing"
"gitea.dev/modelmigration/migrationtest" "gitea.dev/modelmigration/migrationtest"
"gitea.dev/models/project" "gitea.dev/modules/timeutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func Test_CheckProjectColumnsConsistency(t *testing.T) { func Test_CheckProjectColumnsConsistency(t *testing.T) {
// Prepare and load the testing database type Project struct {
x, deferable := migrationtest.PrepareTestEnv(t, 0, new(project.Project), new(project.Column)) ID int64
Title string
Description string
OwnerID int64
RepoID int64
CreatorID int64
IsClosed bool
TemplateType uint8
BoardType uint8
Type uint8
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
ClosedDateUnix timeutil.TimeStamp
}
x, deferable := migrationtest.PrepareTestEnv(t, 0, new(Project), new(ProjectBoardV293))
defer deferable() defer deferable()
if x == nil || t.Failed() { if x == nil || t.Failed() {
return return
@@ -23,7 +39,7 @@ func Test_CheckProjectColumnsConsistency(t *testing.T) {
assert.NoError(t, CheckProjectColumnsConsistency(x)) assert.NoError(t, CheckProjectColumnsConsistency(x))
// check if default column was added // check if default column was added
var defaultColumn project.Column var defaultColumn ProjectBoardV293
has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultColumn) has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultColumn)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, has) assert.True(t, has)
@@ -31,13 +47,17 @@ func Test_CheckProjectColumnsConsistency(t *testing.T) {
assert.True(t, defaultColumn.Default) assert.True(t, defaultColumn.Default)
// check if multiple defaults, previous were removed and last will be kept // check if multiple defaults, previous were removed and last will be kept
expectDefaultColumn, err := project.GetColumn(t.Context(), 2) var expectDefaultColumn ProjectBoardV293
has, err = x.ID(2).Get(&expectDefaultColumn)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, has)
assert.Equal(t, int64(2), expectDefaultColumn.ProjectID) assert.Equal(t, int64(2), expectDefaultColumn.ProjectID)
assert.False(t, expectDefaultColumn.Default) assert.False(t, expectDefaultColumn.Default)
expectNonDefaultColumn, err := project.GetColumn(t.Context(), 3) var expectNonDefaultColumn ProjectBoardV293
has, err = x.ID(3).Get(&expectNonDefaultColumn)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, has)
assert.Equal(t, int64(2), expectNonDefaultColumn.ProjectID) assert.Equal(t, int64(2), expectNonDefaultColumn.ProjectID)
assert.True(t, expectNonDefaultColumn.Default) assert.True(t, expectNonDefaultColumn.Default)
} }
+1 -2
View File
@@ -5,7 +5,6 @@ package v1_27
import ( import (
"gitea.dev/modelmigration/base" "gitea.dev/modelmigration/base"
"gitea.dev/models/db"
"xorm.io/xorm" "xorm.io/xorm"
) )
@@ -25,7 +24,7 @@ func AddReusableWorkflowFieldsToActionRunJob(x base.EngineMigration) error {
ReusableWorkflowContent []byte `xorm:"LONGBLOB"` ReusableWorkflowContent []byte `xorm:"LONGBLOB"`
} }
type ActionRunAttemptJobIDIndex db.ResourceIndex type ActionRunAttemptJobIDIndex base.ResourceIndex
if _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ActionRunJob)); err != nil { if _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ActionRunJob)); err != nil {
return err return err
+8 -27
View File
@@ -5,42 +5,23 @@ package repo
import ( import (
"strconv" "strconv"
"strings"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
) )
func repoCodeGitRepoRelativePath(ownerName, repoName string) string {
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
}
func repoWikiGitRepoRelativePath(ownerName, repoName string) string {
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git"
}
// CodeRepoByName returns an unmanaged repository facade for the code repository of the given owner and repository name.
// Usually it is used for migration fixes or repository adoption/creation/rename/transfer.
func CodeRepoByName(ownerName, repoName string) gitcmd.RepositoryFacade {
return gitcmd.RepositoryUnmanaged(repoCodeGitRepoRelativePath(ownerName, repoName))
}
func WikiRepoByName(ownerName, repoName string) gitcmd.RepositoryFacade {
return gitcmd.RepositoryUnmanaged(repoWikiGitRepoRelativePath(ownerName, repoName))
}
func repoCodeGitRepoManagedID(repoID int64) string { func repoCodeGitRepoManagedID(repoID int64) string {
return "repo-" + strconv.FormatInt(repoID, 10) return "repo-" + strconv.FormatInt(repoID, 10)
} }
func (repo *Repository) CodeStorageRepo() gitcmd.RepositoryFacade { func (repo *Repository) CodeStorageRepo() gitrepo.RepositoryFacade {
id := repoCodeGitRepoManagedID(repo.ID) id := repoCodeGitRepoManagedID(repo.ID)
repoPath := repoCodeGitRepoRelativePath(repo.OwnerName, repo.Name) repoPath := gitrepo.RepoCodeGitRepoRelativePath(repo.OwnerName, repo.Name)
return gitcmd.RepositoryManaged(id, repoPath) return gitrepo.RepositoryManaged(id, repoPath)
} }
func (repo *Repository) GitRepoLocation() string { func (repo *Repository) GitRepoLocation() string {
// TODO: use CodeGitRepo instead of this one // TODO: use CodeGitRepo instead of this one
return repoCodeGitRepoRelativePath(repo.OwnerName, repo.Name) return gitrepo.RepoCodeGitRepoRelativePath(repo.OwnerName, repo.Name)
} }
func (repo *Repository) GitRepoManagedID() string { func (repo *Repository) GitRepoManagedID() string {
@@ -48,9 +29,9 @@ func (repo *Repository) GitRepoManagedID() string {
return repoCodeGitRepoManagedID(repo.ID) return repoCodeGitRepoManagedID(repo.ID)
} }
func (repo *Repository) WikiStorageRepo() gitcmd.RepositoryFacade { func (repo *Repository) WikiStorageRepo() gitrepo.RepositoryFacade {
// The wiki repository should have the same object format as the code repository. TODO: old comment, REALLY? Why? // The wiki repository should have the same object format as the code repository. TODO: old comment, REALLY? Why?
id := "repo-wiki-" + strconv.FormatInt(repo.ID, 10) id := "repo-wiki-" + strconv.FormatInt(repo.ID, 10)
repoPath := repoWikiGitRepoRelativePath(repo.OwnerName, repo.Name) repoPath := gitrepo.RepoWikiGitRepoRelativePath(repo.OwnerName, repo.Name)
return gitcmd.RepositoryManaged(id, repoPath) return gitrepo.RepositoryManaged(id, repoPath)
} }
+3 -2
View File
@@ -8,6 +8,7 @@ import (
repo_model "gitea.dev/models/repo" repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest" "gitea.dev/models/unittest"
"gitea.dev/modules/git/gitrepo"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -16,11 +17,11 @@ func TestRepository_GitRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.Equal(t, "user2/repo1.git", repo_model.CodeRepoByName(repo.OwnerName, repo.Name).GitRepoLocation()) assert.Equal(t, "user2/repo1.git", gitrepo.CodeRepoByName(repo.OwnerName, repo.Name).GitRepoLocation())
assert.Equal(t, "user2/repo1.git", repo.CodeStorageRepo().GitRepoLocation()) assert.Equal(t, "user2/repo1.git", repo.CodeStorageRepo().GitRepoLocation())
assert.Equal(t, "repo-1", repo.CodeStorageRepo().GitRepoManagedID()) assert.Equal(t, "repo-1", repo.CodeStorageRepo().GitRepoManagedID())
assert.Equal(t, "user2/repo1.wiki.git", repo_model.WikiRepoByName(repo.OwnerName, repo.Name).GitRepoLocation()) assert.Equal(t, "user2/repo1.wiki.git", gitrepo.WikiRepoByName(repo.OwnerName, repo.Name).GitRepoLocation())
assert.Equal(t, "user2/repo1.wiki.git", repo.WikiStorageRepo().GitRepoLocation()) assert.Equal(t, "user2/repo1.wiki.git", repo.WikiStorageRepo().GitRepoLocation())
assert.Equal(t, "repo-wiki-1", repo.WikiStorageRepo().GitRepoManagedID()) assert.Equal(t, "repo-wiki-1", repo.WikiStorageRepo().GitRepoManagedID())
} }
+2 -1
View File
@@ -13,6 +13,7 @@ import (
"strings" "strings"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
) )
@@ -48,7 +49,7 @@ func CreateBundle(ctx context.Context, repo RepositoryFacade, commit string, out
} }
defer cleanup() defer cleanup()
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(gitcmd.RepoLocalPath(repo), "objects")) env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(gitrepo.RepoLocalPath(repo), "objects"))
gitTmpCmd := func() *gitcmd.Command { gitTmpCmd := func() *gitcmd.Command {
return gitcmd.NewCommand().WithDir(tmpDir).WithEnv(env) return gitcmd.NewCommand().WithDir(tmpDir).WithEnv(env)
} }
+2 -2
View File
@@ -8,7 +8,7 @@ import (
"io" "io"
"os" "os"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/util" "gitea.dev/modules/util"
) )
@@ -50,7 +50,7 @@ type CatFileBatchCloser interface {
// NewBatch creates a "batch object provider (CatFileBatch)" for the given repository path to retrieve object info and content efficiently. // NewBatch creates a "batch object provider (CatFileBatch)" for the given repository path to retrieve object info and content efficiently.
// The CatFileBatch and the readers create by it should only be used in the same goroutine. // 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) { func NewBatch(ctx context.Context, repo RepositoryFacade) (CatFileBatchCloser, error) {
repoPath := gitcmd.RepoLocalPath(repo) repoPath := gitrepo.RepoLocalPath(repo)
if _, err := os.Stat(repoPath); err != nil { if _, err := os.Stat(repoPath); err != nil {
return nil, util.NewNotExistErrorf("repo %q doesn't exist", repo.LogString()) return nil, util.NewNotExistErrorf("repo %q doesn't exist", repo.LogString())
} }
+3 -3
View File
@@ -9,7 +9,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/test" "gitea.dev/modules/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -26,10 +26,10 @@ func TestCatFileBatch(t *testing.T) {
func testCatFileBatch(t *testing.T) { func testCatFileBatch(t *testing.T) {
repo1Path, _ := filepath.Abs(filepath.Join(testReposDir, "repo1_bare")) repo1Path, _ := filepath.Abs(filepath.Join(testReposDir, "repo1_bare"))
repo1 := gitcmd.RepositoryUnmanaged(repo1Path) repo1 := gitrepo.RepositoryUnmanaged(repo1Path)
t.Run("CorruptedGitRepo", func(t *testing.T) { t.Run("CorruptedGitRepo", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
batch, err := NewBatch(t.Context(), gitcmd.RepositoryUnmanaged(tmpDir)) batch, err := NewBatch(t.Context(), gitrepo.RepositoryUnmanaged(tmpDir))
// as long as the directory exists, no error, because we can't really know whether the git repo is valid until we run commands // as long as the directory exists, no error, because we can't really know whether the git repo is valid until we run commands
require.NoError(t, err) require.NoError(t, err)
defer batch.Close() defer batch.Close()
+4 -4
View File
@@ -6,19 +6,19 @@ package git
import ( import (
"context" "context"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
) )
// CloneExternalRepo clones an external repository to the managed repository. // CloneExternalRepo clones an external repository to the managed repository.
func CloneExternalRepo(ctx context.Context, fromRemoteURL string, toRepo RepositoryFacade, opts CloneRepoOptions) error { func CloneExternalRepo(ctx context.Context, fromRemoteURL string, toRepo RepositoryFacade, opts CloneRepoOptions) error {
return Clone(ctx, fromRemoteURL, gitcmd.RepoLocalPath(toRepo), opts) return Clone(ctx, fromRemoteURL, gitrepo.RepoLocalPath(toRepo), opts)
} }
// CloneRepoToLocal clones a managed repository to a local path. // CloneRepoToLocal clones a managed repository to a local path.
func CloneRepoToLocal(ctx context.Context, fromRepo RepositoryFacade, toLocalPath string, opts CloneRepoOptions) error { func CloneRepoToLocal(ctx context.Context, fromRepo RepositoryFacade, toLocalPath string, opts CloneRepoOptions) error {
return Clone(ctx, gitcmd.RepoLocalPath(fromRepo), toLocalPath, opts) return Clone(ctx, gitrepo.RepoLocalPath(fromRepo), toLocalPath, opts)
} }
func CloneManaged(ctx context.Context, fromRepo, toRepo RepositoryFacade, opts CloneRepoOptions) error { func CloneManaged(ctx context.Context, fromRepo, toRepo RepositoryFacade, opts CloneRepoOptions) error {
return Clone(ctx, gitcmd.RepoLocalPath(fromRepo), gitcmd.RepoLocalPath(toRepo), opts) return Clone(ctx, gitrepo.RepoLocalPath(fromRepo), gitrepo.RepoLocalPath(toRepo), opts)
} }
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"context" "context"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/git/gitrepo"
) )
// FetchRemoteCommit fetches a specific commit and its related objects from a remote // FetchRemoteCommit fetches a specific commit and its related objects from a remote
@@ -21,7 +22,7 @@ import (
func FetchRemoteCommit(ctx context.Context, repo, remoteRepo RepositoryFacade, commitID string) error { func FetchRemoteCommit(ctx context.Context, repo, remoteRepo RepositoryFacade, commitID string) error {
return LockWriteAndDo(ctx, repo, func(ctx context.Context) error { return LockWriteAndDo(ctx, repo, func(ctx context.Context) error {
return gitcmd.NewCommand("fetch", "--no-tags"). return gitcmd.NewCommand("fetch", "--no-tags").
AddDynamicArguments(gitcmd.RepoLocalPath(remoteRepo)). AddDynamicArguments(gitrepo.RepoLocalPath(remoteRepo)).
AddDynamicArguments(commitID). AddDynamicArguments(commitID).
WithRepo(repo).Run(ctx) WithRepo(repo).Run(ctx)
}) })
+6
View File
@@ -16,6 +16,7 @@ import (
"strings" "strings"
"time" "time"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions "gitea.dev/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions
"gitea.dev/modules/gtprof" "gitea.dev/modules/gtprof"
"gitea.dev/modules/log" "gitea.dev/modules/log"
@@ -261,6 +262,11 @@ func (c *Command) WithDir(dir string) *Command {
return c return c
} }
func (c *Command) WithRepo(repo gitrepo.RepositoryFacade) *Command {
c.gitDir = gitrepo.RepoLocalPath(repo)
return c
}
func (c *Command) WithEnv(env []string) *Command { func (c *Command) WithEnv(env []string) *Command {
c.cmdEnv = env c.cmdEnv = env
return c return c
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gitrepo
import "strings"
func RepoCodeGitRepoRelativePath(ownerName, repoName string) string {
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
}
func RepoWikiGitRepoRelativePath(ownerName, repoName string) string {
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git"
}
// CodeRepoByName returns an unmanaged repository facade for the code repository of the given owner and repository name.
// Usually it is used for migration fixes or repository adoption/creation/rename/transfer.
func CodeRepoByName(ownerName, repoName string) RepositoryFacade {
return RepositoryUnmanaged(RepoCodeGitRepoRelativePath(ownerName, repoName))
}
func WikiRepoByName(ownerName, repoName string) RepositoryFacade {
return RepositoryUnmanaged(RepoWikiGitRepoRelativePath(ownerName, repoName))
}
@@ -1,7 +1,7 @@
// Copyright 2026 The Gitea Authors. All rights reserved. // Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package gitcmd package gitrepo
import ( import (
"path/filepath" "path/filepath"
@@ -24,11 +24,6 @@ type RepositoryFacade interface {
LogString() string LogString() string
} }
func (c *Command) WithRepo(repo RepositoryFacade) *Command {
c.gitDir = RepoLocalPath(repo)
return c
}
// RepoLocalPath returns an absolute path for a RepositoryFacade. // RepoLocalPath returns an absolute path for a RepositoryFacade.
// TODO: most of the calls to this function should be replaced with a "Repo FS" in the future // TODO: most of the calls to this function should be replaced with a "Repo FS" in the future
// to handle file accesses in the git repo (e.g.: read, write, list, remove). // to handle file accesses in the git repo (e.g.: read, write, list, remove).
+2 -2
View File
@@ -7,7 +7,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/modules/test" "gitea.dev/modules/test"
@@ -79,7 +79,7 @@ func TestGrepSearch(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, res) assert.Empty(t, res)
nonExistingRepo := &Repository{RepositoryBase: RepositoryBase{repoFacade: gitcmd.RepositoryUnmanaged("no-such-git-repo")}} nonExistingRepo := &Repository{RepositoryBase: RepositoryBase{repoFacade: gitrepo.RepositoryUnmanaged("no-such-git-repo")}}
res, err = GrepSearch(t.Context(), nonExistingRepo, "no-such-content", GrepOptions{}) res, err = GrepSearch(t.Context(), nonExistingRepo, "no-such-content", GrepOptions{})
assert.Error(t, err) assert.Error(t, err)
assert.Empty(t, res) assert.Empty(t, res)
+3 -3
View File
@@ -11,7 +11,7 @@ import (
"slices" "slices"
"strings" "strings"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/util" "gitea.dev/modules/util"
) )
@@ -41,7 +41,7 @@ type Hook struct {
// GetHook returns a Git hook by given name and repository. // GetHook returns a Git hook by given name and repository.
func GetHook(repo RepositoryFacade, name string) (*Hook, error) { func GetHook(repo RepositoryFacade, name string) (*Hook, error) {
repoPath := gitcmd.RepoLocalPath(repo) repoPath := gitrepo.RepoLocalPath(repo)
if !IsValidHookName(name) { if !IsValidHookName(name) {
return nil, ErrNotValidHook return nil, ErrNotValidHook
} }
@@ -100,7 +100,7 @@ func (h *Hook) Update() error {
// ListHooks returns a list of Git hooks of given repository. // ListHooks returns a list of Git hooks of given repository.
func ListHooks(repo RepositoryFacade) (_ []*Hook, err error) { func ListHooks(repo RepositoryFacade) (_ []*Hook, err error) {
exist, err := util.IsDir(filepath.Join(gitcmd.RepoLocalPath(repo), "hooks")) exist, err := util.IsDir(filepath.Join(gitrepo.RepoLocalPath(repo), "hooks"))
if err != nil { if err != nil {
return nil, err return nil, err
} else if !exist { } else if !exist {
+3 -3
View File
@@ -10,7 +10,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/modules/util" "gitea.dev/modules/util"
) )
@@ -109,7 +109,7 @@ done
// CreateDelegateHooks creates all the hooks scripts for the repo // CreateDelegateHooks creates all the hooks scripts for the repo
func CreateDelegateHooks(_ context.Context, repo RepositoryFacade) (err error) { func CreateDelegateHooks(_ context.Context, repo RepositoryFacade) (err error) {
return createDelegateHooks(filepath.Join(gitcmd.RepoLocalPath(repo), "hooks")) return createDelegateHooks(filepath.Join(gitrepo.RepoLocalPath(repo), "hooks"))
} }
func createDelegateHooks(hookDir string) (err error) { func createDelegateHooks(hookDir string) (err error) {
@@ -176,7 +176,7 @@ func ensureExecutable(filename string) error {
// CheckDelegateHooks checks the hooks scripts for the repo // CheckDelegateHooks checks the hooks scripts for the repo
func CheckDelegateHooks(_ context.Context, repo RepositoryFacade) ([]string, error) { func CheckDelegateHooks(_ context.Context, repo RepositoryFacade) ([]string, error) {
return checkDelegateHooks(filepath.Join(gitcmd.RepoLocalPath(repo), "hooks")) return checkDelegateHooks(filepath.Join(gitrepo.RepoLocalPath(repo), "hooks"))
} }
func checkDelegateHooks(hookDir string) ([]string, error) { func checkDelegateHooks(hookDir string) ([]string, error) {
+11 -11
View File
@@ -11,59 +11,59 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/util" "gitea.dev/modules/util"
) )
// IsRepositoryExist returns true if the repository directory exists in the disk // IsRepositoryExist returns true if the repository directory exists in the disk
func IsRepositoryExist(ctx context.Context, repo RepositoryFacade) (bool, error) { func IsRepositoryExist(ctx context.Context, repo RepositoryFacade) (bool, error) {
return util.IsExist(gitcmd.RepoLocalPath(repo)) return util.IsExist(gitrepo.RepoLocalPath(repo))
} }
// DeleteRepository deletes the repository directory from the disk, it will return // DeleteRepository deletes the repository directory from the disk, it will return
// nil if the repository does not exist. // nil if the repository does not exist.
func DeleteRepository(ctx context.Context, repo RepositoryFacade) error { func DeleteRepository(ctx context.Context, repo RepositoryFacade) error {
return util.RemoveAll(gitcmd.RepoLocalPath(repo)) return util.RemoveAll(gitrepo.RepoLocalPath(repo))
} }
// RenameRepository renames a repository's name on disk // RenameRepository renames a repository's name on disk
func RenameRepository(ctx context.Context, repo, newRepo RepositoryFacade) error { func RenameRepository(ctx context.Context, repo, newRepo RepositoryFacade) error {
dstDir := gitcmd.RepoLocalPath(newRepo) dstDir := gitrepo.RepoLocalPath(newRepo)
if err := os.MkdirAll(filepath.Dir(dstDir), os.ModePerm); err != nil { if err := os.MkdirAll(filepath.Dir(dstDir), os.ModePerm); err != nil {
return fmt.Errorf("Failed to create dir %s: %w", filepath.Dir(dstDir), err) return fmt.Errorf("Failed to create dir %s: %w", filepath.Dir(dstDir), err)
} }
if err := util.Rename(gitcmd.RepoLocalPath(repo), dstDir); err != nil { if err := util.Rename(gitrepo.RepoLocalPath(repo), dstDir); err != nil {
return fmt.Errorf("rename repository directory: %w", err) return fmt.Errorf("rename repository directory: %w", err)
} }
return nil return nil
} }
func InitRepository(ctx context.Context, repo RepositoryFacade, objectFormatName string) error { func InitRepository(ctx context.Context, repo RepositoryFacade, objectFormatName string) error {
return InitRepositoryLocal(ctx, gitcmd.RepoLocalPath(repo), true, objectFormatName) return InitRepositoryLocal(ctx, gitrepo.RepoLocalPath(repo), true, objectFormatName)
} }
func GetRepoFS(repo RepositoryFacade) fs.FS { func GetRepoFS(repo RepositoryFacade) fs.FS {
return os.DirFS(gitcmd.RepoLocalPath(repo)) return os.DirFS(gitrepo.RepoLocalPath(repo))
} }
func IsRepoFileExist(ctx context.Context, repo RepositoryFacade, relativeFilePath string) (bool, error) { func IsRepoFileExist(ctx context.Context, repo RepositoryFacade, relativeFilePath string) (bool, error) {
absoluteFilePath := filepath.Join(gitcmd.RepoLocalPath(repo), relativeFilePath) absoluteFilePath := filepath.Join(gitrepo.RepoLocalPath(repo), relativeFilePath)
return util.IsExist(absoluteFilePath) return util.IsExist(absoluteFilePath)
} }
func IsRepoDirExist(ctx context.Context, repo RepositoryFacade, relativeDirPath string) (bool, error) { func IsRepoDirExist(ctx context.Context, repo RepositoryFacade, relativeDirPath string) (bool, error) {
absoluteDirPath := filepath.Join(gitcmd.RepoLocalPath(repo), relativeDirPath) absoluteDirPath := filepath.Join(gitrepo.RepoLocalPath(repo), relativeDirPath)
return util.IsDir(absoluteDirPath) return util.IsDir(absoluteDirPath)
} }
func RemoveRepoFileOrDir(ctx context.Context, repo RepositoryFacade, relativeFileOrDirPath string) error { func RemoveRepoFileOrDir(ctx context.Context, repo RepositoryFacade, relativeFileOrDirPath string) error {
absoluteFilePath := filepath.Join(gitcmd.RepoLocalPath(repo), relativeFileOrDirPath) absoluteFilePath := filepath.Join(gitrepo.RepoLocalPath(repo), relativeFileOrDirPath)
return util.Remove(absoluteFilePath) return util.Remove(absoluteFilePath)
} }
func CreateRepoFile(ctx context.Context, repo RepositoryFacade, relativeFilePath string) (io.WriteCloser, error) { func CreateRepoFile(ctx context.Context, repo RepositoryFacade, relativeFilePath string) (io.WriteCloser, error) {
absoluteFilePath := filepath.Join(gitcmd.RepoLocalPath(repo), relativeFilePath) absoluteFilePath := filepath.Join(gitrepo.RepoLocalPath(repo), relativeFilePath)
if err := os.MkdirAll(filepath.Dir(absoluteFilePath), os.ModePerm); err != nil { if err := os.MkdirAll(filepath.Dir(absoluteFilePath), os.ModePerm); err != nil {
return nil, err return nil, err
} }
+3 -3
View File
@@ -7,17 +7,17 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
) )
const testReposDir = "tests/repos/" const testReposDir = "tests/repos/"
func mockRepository(repoPath string) gitcmd.RepositoryFacade { func mockRepository(repoPath string) RepositoryFacade {
if !filepath.IsAbs(repoPath) { if !filepath.IsAbs(repoPath) {
// resolve repository path relative to the unit test fixture directory // resolve repository path relative to the unit test fixture directory
repoPath, _ = filepath.Abs(filepath.Join(testReposDir, repoPath)) repoPath, _ = filepath.Abs(filepath.Join(testReposDir, repoPath))
} }
return gitcmd.RepositoryUnmanaged(repoPath) return gitrepo.RepositoryUnmanaged(repoPath)
} }
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
+5 -5
View File
@@ -6,22 +6,22 @@ package git
import ( import (
"context" "context"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
) )
// PushToExternal pushes a managed repository to an external remote. // PushToExternal pushes a managed repository to an external remote.
func PushToExternal(ctx context.Context, repo RepositoryFacade, opts PushOptions) error { func PushToExternal(ctx context.Context, repo RepositoryFacade, opts PushOptions) error {
return Push(ctx, gitcmd.RepoLocalPath(repo), opts) return Push(ctx, gitrepo.RepoLocalPath(repo), opts)
} }
// PushManaged pushes from one managed repository to another managed repository. // PushManaged pushes from one managed repository to another managed repository.
func PushManaged(ctx context.Context, fromRepo, toRepo RepositoryFacade, opts PushOptions) error { func PushManaged(ctx context.Context, fromRepo, toRepo RepositoryFacade, opts PushOptions) error {
opts.Remote = gitcmd.RepoLocalPath(toRepo) opts.Remote = gitrepo.RepoLocalPath(toRepo)
return Push(ctx, gitcmd.RepoLocalPath(fromRepo), opts) return Push(ctx, gitrepo.RepoLocalPath(fromRepo), opts)
} }
// PushFromLocal pushes from a local path to a managed repository. // PushFromLocal pushes from a local path to a managed repository.
func PushFromLocal(ctx context.Context, fromLocalPath string, toRepo RepositoryFacade, opts PushOptions) error { func PushFromLocal(ctx context.Context, fromLocalPath string, toRepo RepositoryFacade, opts PushOptions) error {
opts.Remote = gitcmd.RepoLocalPath(toRepo) opts.Remote = gitrepo.RepoLocalPath(toRepo)
return Push(ctx, fromLocalPath, opts) return Push(ctx, fromLocalPath, opts)
} }
+6 -5
View File
@@ -17,12 +17,13 @@ import (
"time" "time"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/proxy" "gitea.dev/modules/proxy"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/modules/util" "gitea.dev/modules/util"
) )
type RepositoryFacade = gitcmd.RepositoryFacade type RepositoryFacade = gitrepo.RepositoryFacade
type RepositoryBase struct { type RepositoryBase struct {
LastCommitCache *LastCommitCache LastCommitCache *LastCommitCache
@@ -36,7 +37,7 @@ type RepositoryBase struct {
catFileBatchInUse bool catFileBatchInUse bool
} }
var _ gitcmd.RepositoryFacade = (*Repository)(nil) var _ RepositoryFacade = (*Repository)(nil)
func (repo *Repository) GitRepoManagedID() string { func (repo *Repository) GitRepoManagedID() string {
return repo.repoFacade.GitRepoManagedID() return repo.repoFacade.GitRepoManagedID()
@@ -51,7 +52,7 @@ func (repo *Repository) LogString() string {
} }
func OpenRepository(repo RepositoryFacade) (*Repository, error) { func OpenRepository(repo RepositoryFacade) (*Repository, error) {
repoPath := gitcmd.RepoLocalPath(repo) repoPath := gitrepo.RepoLocalPath(repo)
exist, err := util.IsDir(repoPath) exist, err := util.IsDir(repoPath)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -77,7 +78,7 @@ func OpenRepositoryLocal(localPath string) (_ *Repository, err error) {
return nil, err return nil, err
} }
} }
return OpenRepository(gitcmd.RepositoryUnmanaged(localPath)) return OpenRepository(gitrepo.RepositoryUnmanaged(localPath))
} }
func (repo *Repository) Close() error { func (repo *Repository) Close() error {
@@ -130,7 +131,7 @@ func InitRepositoryLocal(ctx context.Context, localRepoPath string, bare bool, o
// IsEmpty Check if repository is empty. // IsEmpty Check if repository is empty.
func (repo *Repository) IsEmpty(ctx context.Context) (bool, error) { func (repo *Repository) IsEmpty(ctx context.Context) (bool, error) {
stdout, _, err := gitcmd.NewCommand(). stdout, _, err := gitcmd.NewCommand().
AddOptionFormat("--git-dir=%s", gitcmd.RepoLocalPath(repo)). // TODO: all git commands should use "--git-dir" or "GIT_DIR=..." AddOptionFormat("--git-dir=%s", gitrepo.RepoLocalPath(repo)). // TODO: all git commands should use "--git-dir" or "GIT_DIR=..."
AddArguments("rev-list", "-n", "1", "--all"). AddArguments("rev-list", "-n", "1", "--all").
WithRepo(repo). WithRepo(repo).
RunStdString(ctx) RunStdString(ctx)
+2 -2
View File
@@ -9,7 +9,7 @@ package git
import ( import (
"path/filepath" "path/filepath"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5"
@@ -30,7 +30,7 @@ type Repository struct {
} }
func openRepositoryInternal(gitRepo *Repository) error { func openRepositoryInternal(gitRepo *Repository) error {
repoPath := gitcmd.RepoLocalPath(gitRepo) repoPath := gitrepo.RepoLocalPath(gitRepo)
fs := osfs.New(repoPath) fs := osfs.New(repoPath)
_, err := fs.Stat(".git") _, err := fs.Stat(".git")
if err == nil { if err == nil {
+2 -2
View File
@@ -10,7 +10,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/log" "gitea.dev/modules/log"
commitgraph "github.com/go-git/go-git/v5/plumbing/format/commitgraph/v2" commitgraph "github.com/go-git/go-git/v5/plumbing/format/commitgraph/v2"
@@ -19,7 +19,7 @@ import (
// CommitNodeIndex returns the index for walking commit graph // CommitNodeIndex returns the index for walking commit graph
func (repo *Repository) CommitNodeIndex() (_ cgobject.CommitNodeIndex, closer func()) { func (repo *Repository) CommitNodeIndex() (_ cgobject.CommitNodeIndex, closer func()) {
indexPath := filepath.Join(gitcmd.RepoLocalPath(repo), "objects", "info", "commit-graph") indexPath := filepath.Join(gitrepo.RepoLocalPath(repo), "objects", "info", "commit-graph")
file, err := os.Open(indexPath) file, err := os.Open(indexPath)
if err == nil { if err == nil {
var index commitgraph.Index var index commitgraph.Index
+2 -2
View File
@@ -7,7 +7,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
) )
const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
@@ -15,7 +15,7 @@ const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | o
// CalcRepositorySize returns the disk consumption for a given path // CalcRepositorySize returns the disk consumption for a given path
func CalcRepositorySize(repo RepositoryFacade) (int64, error) { func CalcRepositorySize(repo RepositoryFacade) (int64, error) {
var size int64 var size int64
err := filepath.WalkDir(gitcmd.RepoLocalPath(repo), func(_ string, entry os.DirEntry, err error) error { err := filepath.WalkDir(gitrepo.RepoLocalPath(repo), func(_ string, entry os.DirEntry, err error) error {
if os.IsNotExist(err) { // ignore the error because some files (like temp/lock file) may be deleted during traversing. if os.IsNotExist(err) { // ignore the error because some files (like temp/lock file) may be deleted during traversing.
return nil return nil
} else if err != nil { } else if err != nil {
+1 -1
View File
@@ -19,7 +19,7 @@ type TemplateSubmoduleCommit struct {
// GetTemplateSubmoduleCommits returns a list of submodules paths and their commits from a repository // 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. // This function is only for generating new repos based on existing template, the template couldn't be too large.
func GetTemplateSubmoduleCommits(ctx context.Context, repo gitcmd.RepositoryFacade) (submoduleCommits []TemplateSubmoduleCommit, _ error) { func GetTemplateSubmoduleCommits(ctx context.Context, repo RepositoryFacade) (submoduleCommits []TemplateSubmoduleCommit, _ error) {
cmd := gitcmd.NewCommand("ls-tree", "-r", "--", "HEAD") cmd := gitcmd.NewCommand("ls-tree", "-r", "--", "HEAD")
stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe() stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe()
defer stdoutReaderClose() defer stdoutReaderClose()
+2 -2
View File
@@ -8,7 +8,7 @@ import (
"fmt" "fmt"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
) )
@@ -19,6 +19,6 @@ func CreateTemporaryGitRepo(prefix string) (tmpPath string, tmpRepo git.Reposito
if err != nil { if err != nil {
return "", nil, nil, fmt.Errorf("failed to create temp dir with prefix %s: %w", tmpNamePrefix, err) return "", nil, nil, fmt.Errorf("failed to create temp dir with prefix %s: %w", tmpNamePrefix, err)
} }
tmpRepo = gitcmd.RepositoryUnmanaged(tmpPath) tmpRepo = gitrepo.RepositoryUnmanaged(tmpPath)
return tmpPath, tmpRepo, cancel, nil return tmpPath, tmpRepo, cancel, nil
} }
+3 -2
View File
@@ -9,6 +9,7 @@ import (
repo_model "gitea.dev/models/repo" repo_model "gitea.dev/models/repo"
user_model "gitea.dev/models/user" user_model "gitea.dev/models/user"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/routers/api/v1/utils" "gitea.dev/routers/api/v1/utils"
"gitea.dev/services/context" "gitea.dev/services/context"
repo_service "gitea.dev/services/repository" repo_service "gitea.dev/services/repository"
@@ -99,7 +100,7 @@ func AdoptRepository(ctx *context.APIContext) {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
} }
exist, err := git.IsRepositoryExist(ctx, repo_model.CodeRepoByName(ctxUser.Name, repoName)) exist, err := git.IsRepositoryExist(ctx, gitrepo.CodeRepoByName(ctxUser.Name, repoName))
if err != nil { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
@@ -161,7 +162,7 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
} }
exist, err := git.IsRepositoryExist(ctx, repo_model.CodeRepoByName(ctxUser.Name, repoName)) exist, err := git.IsRepositoryExist(ctx, gitrepo.CodeRepoByName(ctxUser.Name, repoName))
if err != nil { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
+2 -2
View File
@@ -14,7 +14,7 @@ import (
"gitea.dev/models/unit" "gitea.dev/models/unit"
user_model "gitea.dev/models/user" user_model "gitea.dev/models/user"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/log" "gitea.dev/modules/log"
"gitea.dev/modules/private" "gitea.dev/modules/private"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
@@ -320,7 +320,7 @@ func ServCommand(ctx *context.PrivateContext) {
} }
gitRepo := util.Iif(results.IsWiki, repo.WikiStorageRepo(), repo.CodeStorageRepo()) gitRepo := util.Iif(results.IsWiki, repo.WikiStorageRepo(), repo.CodeStorageRepo())
results.RepoStoragePath = gitcmd.RepoLocalPath(gitRepo) results.RepoStoragePath = gitrepo.RepoLocalPath(gitRepo)
log.Debug("Serv Results: %+v", results) log.Debug("Serv Results: %+v", results)
ctx.JSON(http.StatusOK, results) ctx.JSON(http.StatusOK, results)
// We will update the keys in a different call. // We will update the keys in a different call.
+2 -1
View File
@@ -12,6 +12,7 @@ import (
repo_model "gitea.dev/models/repo" repo_model "gitea.dev/models/repo"
user_model "gitea.dev/models/user" user_model "gitea.dev/models/user"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/log" "gitea.dev/modules/log"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/modules/templates" "gitea.dev/modules/templates"
@@ -134,7 +135,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
ctx.ServerError("IsRepositoryExist", err) ctx.ServerError("IsRepositoryExist", err)
return return
} }
exist, err := git.IsRepositoryExist(ctx, repo_model.CodeRepoByName(ctxUser.Name, repoName)) exist, err := git.IsRepositoryExist(ctx, gitrepo.CodeRepoByName(ctxUser.Name, repoName))
if err != nil { if err != nil {
ctx.ServerError("IsDir", err) ctx.ServerError("IsDir", err)
return return
+2 -1
View File
@@ -6,6 +6,7 @@ package setting
import ( import (
repo_model "gitea.dev/models/repo" repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/services/context" "gitea.dev/services/context"
repo_service "gitea.dev/services/repository" repo_service "gitea.dev/services/repository"
@@ -32,7 +33,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
return return
} }
exist, err := git.IsRepositoryExist(ctx, repo_model.CodeRepoByName(ctxUser.Name, dir)) exist, err := git.IsRepositoryExist(ctx, gitrepo.CodeRepoByName(ctxUser.Name, dir))
if err != nil { if err != nil {
ctx.ServerError("IsDir", err) ctx.ServerError("IsDir", err)
return return
+3 -2
View File
@@ -18,6 +18,7 @@ import (
user_model "gitea.dev/models/user" user_model "gitea.dev/models/user"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd" "gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/log" "gitea.dev/modules/log"
base "gitea.dev/modules/migration" base "gitea.dev/modules/migration"
"gitea.dev/modules/repository" "gitea.dev/modules/repository"
@@ -161,7 +162,7 @@ func (g *RepositoryDumper) CreateRepo(ctx context.Context, repo *base.Repository
return fmt.Errorf("clone code: %w", err) return fmt.Errorf("clone code: %w", err)
} }
repoLocal := gitcmd.RepositoryUnmanaged(repoAbsPath) repoLocal := gitrepo.RepositoryUnmanaged(repoAbsPath)
if err := git.WriteCommitGraph(ctx, repoLocal); err != nil { if err := git.WriteCommitGraph(ctx, repoLocal); err != nil {
return err return err
} }
@@ -176,7 +177,7 @@ func (g *RepositoryDumper) CreateRepo(ctx context.Context, repo *base.Repository
if err := os.MkdirAll(wikiAbsPath, os.ModePerm); err != nil { if err := os.MkdirAll(wikiAbsPath, os.ModePerm); err != nil {
return fmt.Errorf("failed to create %s: %w", wikiAbsPath, err) return fmt.Errorf("failed to create %s: %w", wikiAbsPath, err)
} }
wikiLocal := gitcmd.RepositoryUnmanaged(wikiAbsPath) wikiLocal := gitrepo.RepositoryUnmanaged(wikiAbsPath)
if err := git.Clone(ctx, wikiRemotePath, wikiAbsPath, git.CloneRepoOptions{ if err := git.Clone(ctx, wikiRemotePath, wikiAbsPath, git.CloneRepoOptions{
Mirror: true, Mirror: true,
Quiet: true, Quiet: true,
+2 -1
View File
@@ -17,6 +17,7 @@ import (
user_model "gitea.dev/models/user" user_model "gitea.dev/models/user"
"gitea.dev/modules/container" "gitea.dev/modules/container"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/glob" "gitea.dev/modules/glob"
"gitea.dev/modules/graceful" "gitea.dev/modules/graceful"
"gitea.dev/modules/log" "gitea.dev/modules/log"
@@ -213,7 +214,7 @@ func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, re
return err return err
} }
codeRepo := repo_model.CodeRepoByName(u.Name, repoName) codeRepo := gitrepo.CodeRepoByName(u.Name, repoName)
exist, err := git.IsRepositoryExist(ctx, codeRepo) exist, err := git.IsRepositoryExist(ctx, codeRepo)
if err != nil { if err != nil {
log.Error("Unable to check if repo %s/%s exists. Error: %v", u.Name, repoName, err) log.Error("Unable to check if repo %s/%s exists. Error: %v", u.Name, repoName, err)
+2 -1
View File
@@ -19,6 +19,7 @@ import (
"gitea.dev/models/unit" "gitea.dev/models/unit"
user_model "gitea.dev/models/user" user_model "gitea.dev/models/user"
git2 "gitea.dev/modules/git" git2 "gitea.dev/modules/git"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/graceful" "gitea.dev/modules/graceful"
issue_indexer "gitea.dev/modules/indexer/issues" issue_indexer "gitea.dev/modules/indexer/issues"
"gitea.dev/modules/log" "gitea.dev/modules/log"
@@ -331,7 +332,7 @@ func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, na
} else if has { } else if has {
return repo_model.ErrRepoAlreadyExist{Uname: owner.Name, Name: name} return repo_model.ErrRepoAlreadyExist{Uname: owner.Name, Name: name}
} }
repo := repo_model.CodeRepoByName(owner.Name, name) repo := gitrepo.CodeRepoByName(owner.Name, name)
isExist, err := git2.IsRepositoryExist(ctx, repo) isExist, err := git2.IsRepositoryExist(ctx, repo)
if err != nil { if err != nil {
log.Error("Unable to check if repo %s/%s exists, error: %v", owner.Name, name, err) log.Error("Unable to check if repo %s/%s exists, error: %v", owner.Name, name, err)
+12 -11
View File
@@ -18,6 +18,7 @@ import (
repo_model "gitea.dev/models/repo" repo_model "gitea.dev/models/repo"
user_model "gitea.dev/models/user" user_model "gitea.dev/models/user"
"gitea.dev/modules/git" "gitea.dev/modules/git"
"gitea.dev/modules/git/gitrepo"
"gitea.dev/modules/globallock" "gitea.dev/modules/globallock"
"gitea.dev/modules/log" "gitea.dev/modules/log"
"gitea.dev/modules/util" "gitea.dev/modules/util"
@@ -94,7 +95,7 @@ func isRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoNa
if err != nil { if err != nil {
return false, err return false, err
} }
repo := repo_model.CodeRepoByName(u.Name, repoName) repo := gitrepo.CodeRepoByName(u.Name, repoName)
isExist, err := git.IsRepositoryExist(ctx, repo) isExist, err := git.IsRepositoryExist(ctx, repo)
return has || isExist, err return has || isExist, err
} }
@@ -117,16 +118,16 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
if repoRenamed { if repoRenamed {
// revert the rename // revert the rename
from := repo_model.CodeRepoByName(newOwnerName, repo.Name) from := gitrepo.CodeRepoByName(newOwnerName, repo.Name)
to := repo_model.CodeRepoByName(oldOwnerName, repo.Name) to := gitrepo.CodeRepoByName(oldOwnerName, repo.Name)
if err := git.RenameRepository(ctx, from, to); err != nil { if err := git.RenameRepository(ctx, from, to); err != nil {
log.Error("Unable to revert repository %s/%s to %s/%s: %v", newOwnerName, repo.Name, oldOwnerName, repo.Name, err) log.Error("Unable to revert repository %s/%s to %s/%s: %v", newOwnerName, repo.Name, oldOwnerName, repo.Name, err)
} }
} }
if wikiRenamed { if wikiRenamed {
from := repo_model.WikiRepoByName(newOwnerName, repo.Name) from := gitrepo.WikiRepoByName(newOwnerName, repo.Name)
to := repo_model.WikiRepoByName(oldOwnerName, repo.Name) to := gitrepo.WikiRepoByName(oldOwnerName, repo.Name)
if err := git.RenameRepository(ctx, from, to); err != nil { if err := git.RenameRepository(ctx, from, to); err != nil {
log.Error("Unable to revert wiki repository %s/%s to %s/%s: %v", newOwnerName, repo.Name, oldOwnerName, repo.Name, err) log.Error("Unable to revert wiki repository %s/%s to %s/%s: %v", newOwnerName, repo.Name, oldOwnerName, repo.Name, err)
} }
@@ -303,20 +304,20 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
} }
// Rename remote repository to new path and delete local copy. // Rename remote repository to new path and delete local copy.
oldCodeRepo := repo_model.CodeRepoByName(oldOwner.Name, repo.Name) oldCodeRepo := gitrepo.CodeRepoByName(oldOwner.Name, repo.Name)
newCodeRepo := repo_model.CodeRepoByName(newOwner.Name, repo.Name) newCodeRepo := gitrepo.CodeRepoByName(newOwner.Name, repo.Name)
if err := git.RenameRepository(ctx, oldCodeRepo, newCodeRepo); err != nil { if err := git.RenameRepository(ctx, oldCodeRepo, newCodeRepo); err != nil {
return fmt.Errorf("rename repository directory: %w", err) return fmt.Errorf("rename repository directory: %w", err)
} }
repoRenamed = true repoRenamed = true
// Rename remote wiki repository to new path and delete local copy. // Rename remote wiki repository to new path and delete local copy.
oldWikiRepo := repo_model.WikiRepoByName(oldOwner.Name, repo.Name) oldWikiRepo := gitrepo.WikiRepoByName(oldOwner.Name, repo.Name)
if isExist, err := git.IsRepositoryExist(ctx, oldWikiRepo); err != nil { if isExist, err := git.IsRepositoryExist(ctx, oldWikiRepo); err != nil {
log.Error("Unable to check if wiki of repo %s/%s exists. Error: %v", oldOwner.Name, repo.Name, err) log.Error("Unable to check if wiki of repo %s/%s exists. Error: %v", oldOwner.Name, repo.Name, err)
return err return err
} else if isExist { } else if isExist {
newWikiRepo := repo_model.WikiRepoByName(newOwner.Name, repo.Name) newWikiRepo := gitrepo.WikiRepoByName(newOwner.Name, repo.Name)
if err := git.RenameRepository(ctx, oldWikiRepo, newWikiRepo); err != nil { if err := git.RenameRepository(ctx, oldWikiRepo, newWikiRepo); err != nil {
return fmt.Errorf("rename repository wiki: %w", err) return fmt.Errorf("rename repository wiki: %w", err)
} }
@@ -376,13 +377,13 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
} }
} }
newCodeRepo := repo_model.CodeRepoByName(repo.OwnerName, newRepoName) newCodeRepo := gitrepo.CodeRepoByName(repo.OwnerName, newRepoName)
if err = git.RenameRepository(ctx, repo, newCodeRepo); err != nil { if err = git.RenameRepository(ctx, repo, newCodeRepo); err != nil {
return fmt.Errorf("rename repository directory: %w", err) return fmt.Errorf("rename repository directory: %w", err)
} }
if HasWiki(ctx, repo) { if HasWiki(ctx, repo) {
newWikiRepo := repo_model.WikiRepoByName(repo.OwnerName, newRepoName) newWikiRepo := gitrepo.WikiRepoByName(repo.OwnerName, newRepoName)
if err = git.RenameRepository(ctx, repo.WikiStorageRepo(), newWikiRepo); err != nil { if err = git.RenameRepository(ctx, repo.WikiStorageRepo(), newWikiRepo); err != nil {
return fmt.Errorf("rename repository wiki: %w", err) return fmt.Errorf("rename repository wiki: %w", err)
} }