diff --git a/cmd/admin.go b/cmd/admin.go index 11837107f04..14cdc81e37d 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -128,7 +128,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error { } log.Trace("Processing next %d repos of %d", len(repos), count) for _, repo := range repos { - log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RelativePath()) + log.Trace("Synchronizing repo %s", repo.FullName()) gitRepo, err := gitrepo.OpenRepository(repo) if err != nil { log.Warn("OpenRepository: %v", err) diff --git a/models/renderhelper/commit_checker.go b/models/renderhelper/commit_checker.go index 59a2fe12e8b..1a5eeb31a5d 100644 --- a/models/renderhelper/commit_checker.go +++ b/models/renderhelper/commit_checker.go @@ -7,22 +7,23 @@ import ( "context" "io" + repo_model "gitea.dev/models/repo" "gitea.dev/modules/git" "gitea.dev/modules/gitrepo" "gitea.dev/modules/log" ) type commitChecker struct { - ctx context.Context - commitCache map[string]bool - gitRepoFacade gitrepo.Repository + ctx context.Context + commitCache map[string]bool + repo *repo_model.Repository gitRepo *git.Repository gitRepoCloser io.Closer } -func newCommitChecker(ctx context.Context, gitRepo gitrepo.Repository) *commitChecker { - return &commitChecker{ctx: ctx, commitCache: make(map[string]bool), gitRepoFacade: gitRepo} +func newCommitChecker(ctx context.Context, repo *repo_model.Repository) *commitChecker { + return &commitChecker{ctx: ctx, commitCache: make(map[string]bool), repo: repo} } func (c *commitChecker) Close() error { @@ -39,9 +40,9 @@ func (c *commitChecker) IsCommitIDExisting(commitID string) bool { } if c.gitRepo == nil { - r, closer, err := gitrepo.RepositoryFromContextOrOpen(c.ctx, c.gitRepoFacade) + r, closer, err := gitrepo.RepositoryFromContextOrOpen(c.ctx, c.repo) if err != nil { - log.Error("Unable to open repository: %s Error: %v", c.gitRepoFacade.RelativePath(), err) + log.Error("Unable to open repository: %s, error: %v", c.repo.FullName(), err) return false } c.gitRepo, c.gitRepoCloser = r, closer diff --git a/models/repo/repo.go b/models/repo/repo.go index b783b06993b..6357b268410 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -230,15 +230,23 @@ func RelativePath(ownerName, repoName string) string { return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git" } -// RelativePath should be a unix style path like "owner-name/repo-name.git" -func (repo *Repository) RelativePath() string { +func (repo *Repository) GitRepoLocation() string { return RelativePath(repo.OwnerName, repo.Name) } +func (repo *Repository) GitRepoUniqueID() string { + return strconv.FormatInt(repo.ID, 10) +} + type StorageRepo string -// RelativePath should be an unix style path like username/reponame.git -func (sr StorageRepo) RelativePath() string { +func (sr StorageRepo) GitRepoUniqueID() string { + // TODO: need to correctly refactor this method in the future. + // "unique id" should be a cache-key-friendly string, but not a full repo path + return string(sr) +} + +func (sr StorageRepo) GitRepoLocation() string { return string(sr) } diff --git a/models/repo/wiki_test.go b/models/repo/wiki_test.go index 62a881f6f84..b6646e01e4e 100644 --- a/models/repo/wiki_test.go +++ b/models/repo/wiki_test.go @@ -26,5 +26,5 @@ func TestRepository_RelativeWikiPath(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) assert.Equal(t, "user2/repo1.wiki.git", repo_model.RelativeWikiPath(repo.OwnerName, repo.Name)) - assert.Equal(t, "user2/repo1.wiki.git", repo.WikiStorageRepo().RelativePath()) + assert.Equal(t, "user2/repo1.wiki.git", repo.WikiStorageRepo().GitRepoLocation()) } diff --git a/modules/git/git.go b/modules/git/git.go index 0c2deb0281e..03d12af8962 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -14,6 +14,7 @@ import ( "strings" "gitea.dev/modules/git/gitcmd" + "gitea.dev/modules/globallock" "gitea.dev/modules/log" "gitea.dev/modules/setting" "gitea.dev/modules/tempdir" @@ -195,3 +196,11 @@ func runGitTests(m interface{ Run() int }) int { } return m.Run() } + +func LockConfigAndDo(ctx context.Context, repo RepositoryFacade, fn func(ctx context.Context) error) error { + return globallock.LockAndDo(ctx, "repo-config:"+repo.GitRepoUniqueID(), fn) +} + +func LockWriteAndDo(ctx context.Context, repo RepositoryFacade, fn func(ctx context.Context) error) error { + return globallock.LockAndDo(ctx, "repo-write:"+repo.GitRepoUniqueID(), fn) +} diff --git a/modules/git/gitcmd/repo.go b/modules/git/gitcmd/repo.go new file mode 100644 index 00000000000..1eef5e9fddb --- /dev/null +++ b/modules/git/gitcmd/repo.go @@ -0,0 +1,28 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitcmd + +import ( + "path/filepath" + + "gitea.dev/modules/setting" +) + +type RepositoryFacade interface { + GitRepoUniqueID() string + GitRepoLocation() string +} + +func (c *Command) WithRepo(repo RepositoryFacade) *Command { + c.opts.Dir = RepoLocalPath(repo) + return c +} + +func RepoLocalPath(repo RepositoryFacade) string { + repoLoc := repo.GitRepoLocation() + if filepath.IsAbs(repoLoc) { + return repoLoc + } + return filepath.Join(setting.RepoRootPath, filepath.FromSlash(repoLoc)) +} diff --git a/modules/git/repo.go b/modules/git/repo.go index 7760a804472..68c7db131f5 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -5,26 +5,26 @@ package git import ( - "bytes" "context" "fmt" "net/url" "os" "path" + "path/filepath" "strconv" "strings" "time" "gitea.dev/modules/git/gitcmd" "gitea.dev/modules/proxy" + "gitea.dev/modules/setting" + "gitea.dev/modules/util" ) -type RepositoryFacade interface { - RelativePath() string -} +type RepositoryFacade = gitcmd.RepositoryFacade type RepositoryBase struct { - Path string + Path string // absolute path LastCommitCache *LastCommitCache @@ -32,40 +32,35 @@ type RepositoryBase struct { objectFormatCache ObjectFormat } -func prepareRepositoryBase(repoPath string) RepositoryBase { - return RepositoryBase{Path: repoPath, tagCache: newObjectCache[*Tag]()} -} - -const prettyLogFormat = `--pretty=format:%H` - -func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) { - // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git [...] -- [...]' - logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat). - AddDynamicArguments(revisionRange).AddArguments("--").WithDir(repo.Path). - RunStdBytes(ctx) +func OpenRepository(repoPath string) (*Repository, error) { + repoPath, err := filepath.Abs(repoPath) if err != nil { return nil, err } - return repo.parsePrettyFormatLogToList(ctx, logs) + exist, err := util.IsDir(repoPath) + if err != nil { + return nil, err + } + if !exist { + return nil, util.NewNotExistErrorf("no such file or directory") + } + gitRepo := &Repository{ + RepositoryBase: RepositoryBase{Path: repoPath, tagCache: newObjectCache[*Tag]()}, + } + if err = openRepositoryInternal(gitRepo); err != nil { + return nil, err + } + return gitRepo, nil } -func (repo *Repository) parsePrettyFormatLogToList(ctx context.Context, logs []byte) ([]*Commit, error) { - var commits []*Commit - if len(logs) == 0 { - return commits, nil +func (repo *Repository) Close() error { + if repo == nil { + setting.PanicInDevOrTesting("don't close a nil repository") + return nil } - - parts := bytes.SplitSeq(logs, []byte{'\n'}) - - for commitID := range parts { - commit, err := repo.GetCommit(ctx, string(commitID)) - if err != nil { - return nil, err - } - commits = append(commits, commit) - } - - return commits, nil + repo.LastCommitCache = nil + repo.tagCache = nil + return repo.closeInternal() } // IsRepoURLAccessible checks if given repository URL is accessible. diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go index 878a8b3b6fc..b7d6c69d0f0 100644 --- a/modules/git/repo_base_gogit.go +++ b/modules/git/repo_base_gogit.go @@ -9,9 +9,7 @@ package git import ( "path/filepath" - gitealog "gitea.dev/modules/log" "gitea.dev/modules/setting" - "gitea.dev/modules/util" "github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5/osfs" @@ -30,25 +28,13 @@ type Repository struct { gogitStorage *filesystem.Storage } -func OpenRepository(repoPath string) (*Repository, error) { - repoPath, err := filepath.Abs(repoPath) - if err != nil { - return nil, err - } - exist, err := util.IsDir(repoPath) - if err != nil { - return nil, err - } - if !exist { - return nil, util.NewNotExistErrorf("no such file or directory") - } - - fs := osfs.New(repoPath) - _, err = fs.Stat(".git") +func openRepositoryInternal(gitRepo *Repository) error { + fs := osfs.New(gitRepo.Path) + _, err := fs.Stat(".git") if err == nil { fs, err = fs.Chroot(".git") if err != nil { - return nil, err + return err } } // the "clone --shared" repo doesn't work well with go-git AlternativeFS, https://github.com/go-git/go-git/issues/1006 @@ -59,33 +45,23 @@ func OpenRepository(repoPath string) (*Repository, error) { } else { altFs = osfs.New("/") } - storage := filesystem.NewStorageWithOptions(fs, cache.NewObjectLRUDefault(), filesystem.Options{KeepDescriptors: true, LargeObjectThreshold: setting.Git.LargeObjectThreshold, AlternatesFS: altFs}) - gogitRepo, err := gogit.Open(storage, fs) + gitRepo.objectFormatCache = ParseGogitHash(plumbing.ZeroHash).Type() + gitRepo.gogitStorage = filesystem.NewStorageWithOptions(fs, cache.NewObjectLRUDefault(), filesystem.Options{KeepDescriptors: true, LargeObjectThreshold: setting.Git.LargeObjectThreshold, AlternatesFS: altFs}) + gitRepo.gogitRepo, err = gogit.Open(gitRepo.gogitStorage, fs) if err != nil { - return nil, err + _ = gitRepo.gogitStorage.Close() + return err } - - repo := &Repository{ - RepositoryBase: prepareRepositoryBase(repoPath), - gogitRepo: gogitRepo, - gogitStorage: storage, - } - repo.objectFormatCache = ParseGogitHash(plumbing.ZeroHash).Type() - return repo, nil + return nil } -// Close this repository, in particular close the underlying gogitStorage if this is not nil -func (repo *Repository) Close() error { - if repo == nil || repo.gogitStorage == nil { +func (repo *Repository) closeInternal() error { + if repo.gogitStorage == nil { return nil } - if err := repo.gogitStorage.Close(); err != nil { - gitealog.Error("Error closing storage: %v", err) - } + err := repo.gogitStorage.Close() repo.gogitStorage = nil - repo.LastCommitCache = nil - repo.tagCache = nil - return nil + return err } // GoGitRepo gets the go-git repo representation diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go index c07fbf4d319..7d0f66e8a31 100644 --- a/modules/git/repo_base_nogogit.go +++ b/modules/git/repo_base_nogogit.go @@ -8,12 +8,9 @@ package git import ( "context" - "path/filepath" "sync" "gitea.dev/modules/log" - "gitea.dev/modules/setting" - "gitea.dev/modules/util" ) const isGogit = false @@ -26,19 +23,8 @@ type Repository struct { catFileBatchInUse bool } -func OpenRepository(repoPath string) (*Repository, error) { - repoPath, err := filepath.Abs(repoPath) - if err != nil { - return nil, err - } - exist, err := util.IsDir(repoPath) - if err != nil { - return nil, err - } - if !exist { - return nil, util.NewNotExistErrorf("no such file or directory") - } - return &Repository{RepositoryBase: prepareRepositoryBase(repoPath)}, nil +func openRepositoryInternal(_ *Repository) error { + return nil } // CatFileBatch obtains a "batch object provider" for this repository. @@ -80,11 +66,7 @@ func (repo *Repository) CatFileBatch(ctx context.Context) (_ CatFileBatch, close return tempBatch, tempBatch.Close, nil } -func (repo *Repository) Close() error { - if repo == nil { - setting.PanicInDevOrTesting("don't close a nil repository") - return nil - } +func (repo *Repository) closeInternal() error { repo.mu.Lock() defer repo.mu.Unlock() if repo.catFileBatchCloser != nil { @@ -92,7 +74,5 @@ func (repo *Repository) Close() error { repo.catFileBatchCloser = nil repo.catFileBatchInUse = false } - repo.LastCommitCache = nil - repo.tagCache = nil return nil } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index e6d4d23bc54..63977002726 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -45,6 +45,38 @@ func (repo *Repository) GetTagCommit(ctx context.Context, name string) (*Commit, return repo.GetCommit(ctx, RefNameFromTag(name).String()) } +const prettyLogFormat = `--pretty=format:%H` + +func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) { + // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git [...] -- [...]' + logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat). + AddDynamicArguments(revisionRange).AddArguments("--").WithDir(repo.Path). + RunStdBytes(ctx) + if err != nil { + return nil, err + } + return repo.parsePrettyFormatLogToList(ctx, logs) +} + +func (repo *Repository) parsePrettyFormatLogToList(ctx context.Context, logs []byte) ([]*Commit, error) { + var commits []*Commit + if len(logs) == 0 { + return commits, nil + } + + parts := bytes.SplitSeq(logs, []byte{'\n'}) + + for commitID := range parts { + commit, err := repo.GetCommit(ctx, string(commitID)) + if err != nil { + return nil, err + } + commits = append(commits, commit) + } + + return commits, nil +} + func (repo *Repository) getCommitByPathWithID(ctx context.Context, id ObjectID, relpath string) (*Commit, error) { // File name starts with ':' must be escaped. if strings.HasPrefix(relpath, ":") { diff --git a/modules/gitrepo/archive.go b/modules/gitrepo/archive.go index d7075d6dd7b..b28edfd2aea 100644 --- a/modules/gitrepo/archive.go +++ b/modules/gitrepo/archive.go @@ -10,7 +10,6 @@ import ( "os" "path" "path/filepath" - "slices" "strings" "gitea.dev/modules/git/gitcmd" @@ -18,24 +17,22 @@ import ( ) // CreateArchive create archive content to the target path -func CreateArchive(ctx context.Context, repo Repository, format string, target io.Writer, usePrefix bool, commitID string, paths []string) error { +func CreateArchive(ctx context.Context, repo Repository, repoName, format string, target io.Writer, commitID string, paths []string) error { if format == "unknown" { return fmt.Errorf("unknown format: %v", format) } cmd := gitcmd.NewCommand("archive") - if usePrefix { - cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.RelativePath(), ".git"))+"/") + if setting.Repository.PrefixArchiveFiles { + cmd.AddOptionFormat("--prefix=%s", strings.ToLower(repoName)+"/") } cmd.AddOptionFormat("--format=%s", format) cmd.AddDynamicArguments(commitID) - paths = slices.Clone(paths) for i := range paths { // although "git archive" already ensures the paths won't go outside the repo, we still clean them here for safety - paths[i] = path.Clean(paths[i]) + cmd.AddDynamicArguments(path.Clean(paths[i])) } - cmd.AddDynamicArguments(paths...) return RunCmdWithStderr(ctx, repo, cmd.WithStdoutCopy(target)) } diff --git a/modules/gitrepo/blame_sha256_test.go b/modules/gitrepo/blame_sha256_test.go index 872f21f71d4..274cded9297 100644 --- a/modules/gitrepo/blame_sha256_test.go +++ b/modules/gitrepo/blame_sha256_test.go @@ -24,7 +24,7 @@ func TestReadingBlameOutputSha256(t *testing.T) { } t.Run("Without .git-blame-ignore-revs", func(t *testing.T) { - storage := &mockRepository{path: "repo5_pulls_sha256"} + storage := mockRepository("repo5_pulls_sha256") repo, err := OpenRepository(storage) assert.NoError(t, err) defer repo.Close() @@ -70,7 +70,7 @@ func TestReadingBlameOutputSha256(t *testing.T) { }) t.Run("With .git-blame-ignore-revs", func(t *testing.T) { - storage := &mockRepository{path: "repo6_blame_sha256"} + storage := mockRepository("repo6_blame_sha256") repo, err := OpenRepository(storage) assert.NoError(t, err) defer repo.Close() diff --git a/modules/gitrepo/blame_test.go b/modules/gitrepo/blame_test.go index 6ba831b8fac..6b658512441 100644 --- a/modules/gitrepo/blame_test.go +++ b/modules/gitrepo/blame_test.go @@ -19,7 +19,7 @@ func TestReadingBlameOutput(t *testing.T) { defer cancel() t.Run("Without .git-blame-ignore-revs", func(t *testing.T) { - storage := &mockRepository{path: "repo5_pulls"} + storage := mockRepository("repo5_pulls") repo, err := OpenRepository(storage) assert.NoError(t, err) defer repo.Close() @@ -64,7 +64,7 @@ func TestReadingBlameOutput(t *testing.T) { }) t.Run("With .git-blame-ignore-revs", func(t *testing.T) { - storage := &mockRepository{path: "repo6_blame"} + storage := mockRepository("repo6_blame") repo, err := OpenRepository(storage) assert.NoError(t, err) defer repo.Close() diff --git a/modules/gitrepo/command.go b/modules/gitrepo/command.go index f672ca1dbd7..8365a1c61e2 100644 --- a/modules/gitrepo/command.go +++ b/modules/gitrepo/command.go @@ -10,17 +10,17 @@ import ( ) func RunCmd(ctx context.Context, repo Repository, cmd *gitcmd.Command) error { - return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().Run(ctx) + return cmd.WithRepo(repo).WithParentCallerInfo().Run(ctx) } func RunCmdString(ctx context.Context, repo Repository, cmd *gitcmd.Command) (string, string, gitcmd.RunStdError) { - return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunStdString(ctx) + return cmd.WithRepo(repo).WithParentCallerInfo().RunStdString(ctx) } func RunCmdBytes(ctx context.Context, repo Repository, cmd *gitcmd.Command) ([]byte, []byte, gitcmd.RunStdError) { - return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunStdBytes(ctx) + return cmd.WithRepo(repo).WithParentCallerInfo().RunStdBytes(ctx) } func RunCmdWithStderr(ctx context.Context, repo Repository, cmd *gitcmd.Command) gitcmd.RunStdError { - return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunWithStderr(ctx) + return cmd.WithRepo(repo).WithParentCallerInfo().RunWithStderr(ctx) } diff --git a/modules/gitrepo/commit_file_test.go b/modules/gitrepo/commit_file_test.go index ec1018eebae..f0dd9320829 100644 --- a/modules/gitrepo/commit_file_test.go +++ b/modules/gitrepo/commit_file_test.go @@ -116,7 +116,7 @@ func TestParseCommitFileStatus(t *testing.T) { } func TestGetCommitFileStatusMerges(t *testing.T) { - bareRepo6 := &mockRepository{path: "repo6_merge"} + bareRepo6 := mockRepository("repo6_merge") commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo6, "022f4ce6214973e018f02bf363bf8a2e3691f699") assert.NoError(t, err) @@ -139,7 +139,7 @@ func TestGetCommitFileStatusMerges(t *testing.T) { } func TestGetCommitFileStatusMergesSha256(t *testing.T) { - bareRepo6Sha256 := &mockRepository{path: "repo6_merge_sha256"} + bareRepo6Sha256 := mockRepository("repo6_merge_sha256") commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo6Sha256, "d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1") assert.NoError(t, err) diff --git a/modules/gitrepo/commit_test.go b/modules/gitrepo/commit_test.go index a97a2bee265..c742b17f9c1 100644 --- a/modules/gitrepo/commit_test.go +++ b/modules/gitrepo/commit_test.go @@ -10,7 +10,7 @@ import ( ) func TestCommitsCount(t *testing.T) { - bareRepo1 := &mockRepository{path: "repo1_bare"} + bareRepo1 := mockRepository("repo1_bare") commitsCount, err := CommitsCount(t.Context(), bareRepo1, CommitsCountOptions{ @@ -22,7 +22,7 @@ func TestCommitsCount(t *testing.T) { } func TestCommitsCountWithSinceUntil(t *testing.T) { - bareRepo1 := &mockRepository{path: "repo1_bare"} + bareRepo1 := mockRepository("repo1_bare") revision := []string{"8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"} // The three commits on this revision are dated 2018-04-18, 2017-12-19 and 2017-12-19. @@ -53,7 +53,7 @@ func TestCommitsCountWithSinceUntil(t *testing.T) { } func TestCommitsCountWithoutBase(t *testing.T) { - bareRepo1 := &mockRepository{path: "repo1_bare"} + bareRepo1 := mockRepository("repo1_bare") commitsCount, err := CommitsCount(t.Context(), bareRepo1, CommitsCountOptions{ @@ -66,7 +66,7 @@ func TestCommitsCountWithoutBase(t *testing.T) { } func TestGetLatestCommitTime(t *testing.T) { - bareRepo1 := &mockRepository{path: "repo1_bare"} + bareRepo1 := mockRepository("repo1_bare") lct, err := GetLatestCommitTime(t.Context(), bareRepo1) assert.NoError(t, err) // Time is Sun Nov 13 16:40:14 2022 +0100 diff --git a/modules/gitrepo/compare_test.go b/modules/gitrepo/compare_test.go index 770a3589ea8..3808417ba5a 100644 --- a/modules/gitrepo/compare_test.go +++ b/modules/gitrepo/compare_test.go @@ -15,14 +15,6 @@ import ( "github.com/stretchr/testify/require" ) -type mockRepository struct { - path string -} - -func (r *mockRepository) RelativePath() string { - return r.path -} - func TestMergeBaseNoCommonHistory(t *testing.T) { repoDir := filepath.Join(t.TempDir(), "repo.git") require.NoError(t, gitcmd.NewCommand("init").AddDynamicArguments(repoDir).Run(t.Context())) @@ -44,13 +36,13 @@ data 12 Hello from 2 `))).RunStdString(t.Context()) require.NoError(t, runErr) - mergeBase, err := MergeBase(t.Context(), &mockRepository{path: repoDir}, "branch1", "branch2") + mergeBase, err := MergeBase(t.Context(), mockRepository(repoDir), "branch1", "branch2") assert.Empty(t, mergeBase) assert.ErrorIs(t, err, util.ErrNotExist) } func TestRepoGetDivergingCommits(t *testing.T) { - repo := &mockRepository{path: "repo1_bare"} + repo := mockRepository("repo1_bare") do, err := GetDivergingCommits(t.Context(), repo, "master", "branch2") assert.NoError(t, err) assert.Equal(t, &DivergeObject{ @@ -74,7 +66,7 @@ func TestRepoGetDivergingCommits(t *testing.T) { } func TestGetCommitIDsBetweenReverse(t *testing.T) { - repo := &mockRepository{path: "repo1_bare"} + repo := mockRepository("repo1_bare") // tests raw commit IDs commitIDs, err := GetCommitIDsBetweenReverse(t.Context(), repo, diff --git a/modules/gitrepo/config.go b/modules/gitrepo/config.go index a6e97d594d2..56e3d0e82d3 100644 --- a/modules/gitrepo/config.go +++ b/modules/gitrepo/config.go @@ -6,17 +6,13 @@ package gitrepo import ( "context" + "gitea.dev/modules/git" "gitea.dev/modules/git/gitcmd" - "gitea.dev/modules/globallock" ) -func getRepoConfigLockKey(repoStoragePath string) string { - return "repo-config:" + repoStoragePath -} - // GitConfigAdd add a git configuration key to a specific value for the given repository. func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error { - return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { + return git.LockConfigAndDo(ctx, repo, func(ctx context.Context) error { _, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config", "--add"). AddDynamicArguments(key, value)) return err @@ -27,7 +23,7 @@ func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error // If the key does not exist, it will be created. // If the key exists, it will be updated to the new value. func GitConfigSet(ctx context.Context, repo Repository, key, value string) error { - return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { + return git.LockConfigAndDo(ctx, repo, func(ctx context.Context) error { _, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config"). AddDynamicArguments(key, value)) return err diff --git a/modules/gitrepo/fetch.go b/modules/gitrepo/fetch.go index b50571e5b2b..aae43497144 100644 --- a/modules/gitrepo/fetch.go +++ b/modules/gitrepo/fetch.go @@ -6,8 +6,8 @@ package gitrepo import ( "context" + "gitea.dev/modules/git" "gitea.dev/modules/git/gitcmd" - "gitea.dev/modules/globallock" ) // FetchRemoteCommit fetches a specific commit and its related objects from a remote @@ -20,7 +20,7 @@ import ( // This behavior is sufficient for temporary operations, such as determining the // merge base between commits. func FetchRemoteCommit(ctx context.Context, repo, remoteRepo Repository, commitID string) error { - return globallock.LockAndDo(ctx, getRepoWriteLockKey(repo.RelativePath()), func(ctx context.Context) error { + return git.LockWriteAndDo(ctx, repo, func(ctx context.Context) error { return RunCmd(ctx, repo, gitcmd.NewCommand("fetch", "--no-tags"). AddDynamicArguments(repoPath(remoteRepo)). AddDynamicArguments(commitID)) diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go index bdc8c389fcf..1c3cb67477b 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -14,17 +14,12 @@ import ( "gitea.dev/modules/git" "gitea.dev/modules/git/gitcmd" "gitea.dev/modules/reqctx" - "gitea.dev/modules/setting" "gitea.dev/modules/util" ) -type Repository = git.RepositoryFacade +type Repository = gitcmd.RepositoryFacade -// repoPath resolves the Repository.RelativePath (which is a unix-style path like "username/reponame.git") -// to a local filesystem path according to setting.RepoRootPath -var repoPath = func(repo Repository) string { - return filepath.Join(setting.RepoRootPath, filepath.FromSlash(repo.RelativePath())) -} +var repoPath = gitcmd.RepoLocalPath // OpenRepository opens the repository at the given relative path with the provided context. func OpenRepository(repo Repository) (*git.Repository, error) { @@ -33,7 +28,7 @@ func OpenRepository(repo Repository) (*git.Repository, error) { // contextKey is a value for use with context.WithValue. type contextKey struct { - repoPath string + uniqueID string } // RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it @@ -51,11 +46,11 @@ func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Rep // RepositoryFromRequestContextOrOpen opens the repository at the given relative path in the provided request context. // Caller shouldn't close the git repo manually, the git repo will be automatically closed when the request context is done. func RepositoryFromRequestContextOrOpen(ctx reqctx.RequestContext, repo Repository) (*git.Repository, error) { - ck := contextKey{repoPath: repoPath(repo)} + ck := contextKey{uniqueID: repo.GitRepoUniqueID()} if gitRepo, ok := ctx.Value(ck).(*git.Repository); ok { return gitRepo, nil } - gitRepo, err := git.OpenRepository(ck.repoPath) + gitRepo, err := git.OpenRepository(repoPath(repo)) if err != nil { return nil, err } diff --git a/modules/gitrepo/main_test.go b/modules/gitrepo/main_test.go index d7dd292155c..0e730cd329b 100644 --- a/modules/gitrepo/main_test.go +++ b/modules/gitrepo/main_test.go @@ -7,19 +7,20 @@ import ( "path/filepath" "testing" + "gitea.dev/models/repo" "gitea.dev/modules/git" "gitea.dev/modules/setting" ) -func TestMain(m *testing.M) { - // resolve repository path relative to the test directory - setting.SetupGiteaTestEnv() - giteaRoot := setting.GetGiteaTestSourceRoot() - repoPath = func(repo Repository) string { - if filepath.IsAbs(repo.RelativePath()) { - return repo.RelativePath() // for testing purpose only - } - return filepath.Join(giteaRoot, "modules/git/tests/repos", repo.RelativePath()) +func mockRepository(repoPath string) repo.StorageRepo { + if !filepath.IsAbs(repoPath) { + // resolve repository path relative to the unit test fixture directory + repoPath = filepath.Join(setting.GetGiteaTestSourceRoot(), "modules/git/tests/repos", repoPath) } + return repo.StorageRepo(repoPath) +} + +func TestMain(m *testing.M) { + setting.SetupGiteaTestEnv() git.RunGitTests(m) } diff --git a/modules/gitrepo/merge_tree_test.go b/modules/gitrepo/merge_tree_test.go index 7d1075fd7b3..89f689e8013 100644 --- a/modules/gitrepo/merge_tree_test.go +++ b/modules/gitrepo/merge_tree_test.go @@ -69,7 +69,7 @@ M 100644 :5 z/d func TestMergeTreeDirectoryRenameConflictWithoutFiles(t *testing.T) { repoDir := prepareRepoDirRenameConflict(t) require.DirExists(t, repoDir) - repo := &mockRepository{path: repoDir} + repo := mockRepository(repoDir) mergeBase, err := MergeBase(t.Context(), repo, "add", "split") require.NoError(t, err) diff --git a/modules/gitrepo/remote.go b/modules/gitrepo/remote.go index eeef123ba72..96a68d2fdd7 100644 --- a/modules/gitrepo/remote.go +++ b/modules/gitrepo/remote.go @@ -10,7 +10,6 @@ import ( "gitea.dev/modules/git" "gitea.dev/modules/git/gitcmd" giturl "gitea.dev/modules/git/url" - "gitea.dev/modules/globallock" "gitea.dev/modules/util" ) @@ -22,7 +21,7 @@ const ( ) func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...RemoteOption) error { - return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { + return git.LockConfigAndDo(ctx, repo, func(ctx context.Context) error { cmd := gitcmd.NewCommand("remote", "add") if len(options) > 0 { switch options[0] { @@ -40,7 +39,7 @@ func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL st } func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error { - return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { + return git.LockConfigAndDo(ctx, repo, func(ctx context.Context) error { cmd := gitcmd.NewCommand("remote", "rm").AddDynamicArguments(remoteName) _, _, err := RunCmdString(ctx, repo, cmd) return err diff --git a/modules/gitrepo/repo_lock.go b/modules/gitrepo/repo_lock.go deleted file mode 100644 index 2eb89ce8079..00000000000 --- a/modules/gitrepo/repo_lock.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2026 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package gitrepo - -// getRepoWriteLockKey returns the global lock key for write operations on the repository. -// Parallel write operations on the same git repository should be avoided to prevent data corruption. -func getRepoWriteLockKey(repoStoragePath string) string { - return "repo-write:" + repoStoragePath -} diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 17f8711e27e..4a2ca016001 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1157,7 +1157,7 @@ func parseCompareInfo(ctx *context.APIContext, compareParam string) (result *git return nil, nil } - log.Trace("Repo path: %q, base ref: %q->%q, head ref: %q->%q", ctx.Repo.Repository.RelativePath(), compareReq.BaseOriRef+compareReq.BaseOriRefSuffix, baseRef, compareReq.HeadOriRef+compareReq.HeadOriRefSuffix, headRef) + log.Trace("Repo: %q, base ref: %q->%q, head ref: %q->%q", ctx.Repo.Repository.FullName(), compareReq.BaseOriRef+compareReq.BaseOriRefSuffix, baseRef, compareReq.HeadOriRef+compareReq.HeadOriRefSuffix, headRef) baseRefValid := baseRef.IsBranch() || baseRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName), baseRef.ShortName()) headRefValid := headRef.IsBranch() || headRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(headRepo.ObjectFormatName), headRef.ShortName()) diff --git a/routers/private/serv.go b/routers/private/serv.go index 02135ea6e47..e732647f3e3 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -318,7 +318,7 @@ func ServCommand(ctx *context.PrivateContext) { } } - results.RepoStoragePath = util.Iif(results.IsWiki, repo_model.RelativeWikiPath(repo.OwnerName, repo.Name), repo.RelativePath()) + results.RepoStoragePath = util.Iif(results.IsWiki, repo_model.RelativeWikiPath(repo.OwnerName, repo.Name), repo.GitRepoLocation()) log.Debug("Serv Results: %+v", results) ctx.JSON(http.StatusOK, results) // We will update the keys in a different call. diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 3034a4aa591..04a6142ed53 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -384,7 +384,7 @@ func loadScopedWorkflowModel(ctx *context.Context, repo *repo_model.Repository, } content, err := actions_service.ScopedWorkflowContent(ctx, sourceRepo, workflowID) if err != nil { - log.Error("scoped dispatch: content of %s in %s: %v", workflowID, sourceRepo.RelativePath(), err) + log.Error("scoped dispatch: content of %s in %s: %v", workflowID, sourceRepo.FullName(), err) return nil } if content == nil { diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 4ae2955f6d4..15577e245c8 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -418,7 +418,8 @@ func serviceRPC(ctx *context.Context, service string) { WithStdoutCopy(ctx.Resp), ); err != nil { if !gitcmd.IsErrorCanceledOrKilled(err) { - log.Error("Fail to serve RPC(%s) in %s: %v", service, h.getStorageRepo().RelativePath(), err) + repoLogName := h.repo.FullName() + util.Iif(h.isWiki, ".wiki", "") + log.Error("Fail to serve RPC(%s) for repo %s: %v", service, repoLogName, err) } } } diff --git a/routers/web/shared/actions/scoped_workflows.go b/routers/web/shared/actions/scoped_workflows.go index 0db7c51819f..14d4efa7c64 100644 --- a/routers/web/shared/actions/scoped_workflows.go +++ b/routers/web/shared/actions/scoped_workflows.go @@ -203,7 +203,7 @@ func listSourceScopedWorkflowFiles(ctx *context.Context, repo *repo_model.Reposi if !repo.IsEmpty { _, parsed, err := actions_service.LoadParsedScopedWorkflows(ctx, repo) if err != nil { - log.Error("scoped workflows settings: parse %s: %v", repo.RelativePath(), err) + log.Error("scoped workflows settings: parse %s: %v", repo.FullName(), err) } else { for _, p := range parsed { info := scopedWorkflowInfo{ diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index db852d1f12f..4721b58dee1 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -195,7 +195,7 @@ func notify(ctx context.Context, input *notifyInput) error { } log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules", - input.Repo.RelativePath(), + input.Repo.FullName(), commit.ID, input.Event, len(workflows), @@ -204,7 +204,7 @@ func notify(ctx context.Context, input *notifyInput) error { for _, wf := range workflows { if actionsConfig.IsWorkflowDisabled(wf.EntryName) { - log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName) + log.Trace("repo %s has disable workflows %s", input.Repo.FullName(), wf.EntryName) continue } @@ -215,7 +215,7 @@ func notify(ctx context.Context, input *notifyInput) error { for _, wf := range filtered { if actionsConfig.IsWorkflowDisabled(wf.EntryName) { - log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName) + log.Trace("repo %s has disable workflows %s", input.Repo.FullName(), wf.EntryName) continue } @@ -236,11 +236,11 @@ func notify(ctx context.Context, input *notifyInput) error { return fmt.Errorf("DetectWorkflows: %w", err) } if len(baseWorkflows) == 0 { - log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.RelativePath(), baseCommit.ID) + log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.FullName(), baseCommit.ID) } else { for _, wf := range baseWorkflows { if actionsConfig.IsWorkflowDisabled(wf.EntryName) { - log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName) + log.Trace("repo %s has disable workflows %s", input.Repo.FullName(), wf.EntryName) continue } if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget { @@ -250,7 +250,7 @@ func notify(ctx context.Context, input *notifyInput) error { } for _, wf := range baseFiltered { if actionsConfig.IsWorkflowDisabled(wf.EntryName) { - log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName) + log.Trace("repo %s has disable workflows %s", input.Repo.FullName(), wf.EntryName) continue } if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget { @@ -285,11 +285,11 @@ func skipWorkflows(ctx context.Context, input *notifyInput, commit *git.Commit) if slices.Contains(skipWorkflowEvents, input.Event) { for _, s := range setting.Actions.SkipWorkflowStrings { if input.PullRequest != nil && strings.Contains(input.PullRequest.Issue.Title, s) { - log.Debug("repo %s: skipped run for pr %v because of %s string", input.Repo.RelativePath(), input.PullRequest.Issue.ID, s) + log.Debug("repo %s: skipped run for pr %v because of %s string", input.Repo.FullName(), input.PullRequest.Issue.ID, s) return true } if strings.Contains(commit.MessageRaw, s) { - log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.RelativePath(), commit.ID, s) + log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.FullName(), commit.ID, s) return true } } @@ -312,7 +312,7 @@ func skipWorkflows(ctx context.Context, input *notifyInput, commit *git.Commit) } } // skip workflow runs events exceeding the maximum of 5 recursive events - log.Debug("repo %s: skipped workflow_run because of recursive event of 5", input.Repo.RelativePath()) + log.Debug("repo %s: skipped workflow_run because of recursive event of 5", input.Repo.FullName()) return true } return false @@ -326,7 +326,7 @@ func handleWorkflows( ref git.RefName, ) error { if len(detectedWorkflows) == 0 { - log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RelativePath(), commit.ID) + log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.FullName(), commit.ID) return nil } @@ -340,7 +340,7 @@ func handleWorkflows( for _, dwf := range detectedWorkflows { // repo-level run: the workflow content is this repo at this commit if err := buildApproveAndInsertRun(ctx, input, ref, commit, string(p), isForkPullRequest, dwf, input.Repo.ID, commit.ID.String(), false); err != nil { - log.Error("repo %s: %v", input.Repo.RelativePath(), err) + log.Error("repo %s: %v", input.Repo.FullName(), err) continue } } @@ -404,7 +404,7 @@ func handleFilteredWorkflows(ctx context.Context, input *notifyInput, filteredWo } requiredGlobs, err := getAllRequiredStatusContextGlobs(ctx, input.Repo) if err != nil { - log.Error("repo %s: required status contexts: %v", input.Repo.RelativePath(), err) + log.Error("repo %s: required status contexts: %v", input.Repo.FullName(), err) return } if len(requiredGlobs) == 0 { @@ -412,7 +412,7 @@ func handleFilteredWorkflows(ctx context.Context, input *notifyInput, filteredWo } for _, dwf := range filteredWorkflows { if err := CreateSkippedCommitStatusForFilteredWorkflow(ctx, input.Repo, input.Event, dwf.TriggerEvent.Name, dwf.EntryName, dwf.Content, input.Payload, "", requiredGlobs); err != nil { - log.Error("repo %s: skipped commit status for workflow %s: %v", input.Repo.RelativePath(), dwf.EntryName, err) + log.Error("repo %s: skipped commit status for workflow %s: %v", input.Repo.FullName(), dwf.EntryName, err) continue } } @@ -540,7 +540,7 @@ func handleSchedules( } if len(detectedWorkflows) == 0 { - log.Trace("repo %s with commit %s couldn't find schedules", input.Repo.RelativePath(), commit.ID) + log.Trace("repo %s with commit %s couldn't find schedules", input.Repo.FullName(), commit.ID) return nil } @@ -668,7 +668,7 @@ func detectAndHandleScopedWorkflows( // A filtered-out scoped workflow only posts a skipped status when its context is a required check. requiredGlobs, err := getAllRequiredStatusContextGlobs(ctx, input.Repo) if err != nil { - log.Error("scoped workflows: required status contexts for %s: %v", input.Repo.RelativePath(), err) + log.Error("scoped workflows: required status contexts for %s: %v", input.Repo.FullName(), err) } // The same source repo may be registered at both the owner and instance level; dedup @@ -688,7 +688,7 @@ func detectAndHandleScopedWorkflows( sourceRepo := sourceRepos[sourceRepoID] if sourceRepo == nil { // don't abort the other effective sources for this event - log.Error("scoped workflows: source repo %d for consumer %s not found", sourceRepoID, input.Repo.RelativePath()) + log.Error("scoped workflows: source repo %d for consumer %s not found", sourceRepoID, input.Repo.FullName()) continue } if sourceRepo.IsEmpty { @@ -697,7 +697,7 @@ func detectAndHandleScopedWorkflows( sourceCommitSHA, detected, filtered, err := detectScopedWorkflowsForSource(ctx, input, consumerGitRepo, consumerCommit, sourceRepo) if err != nil { - log.Error("scoped workflows: source %d for consumer %s: %v", sourceRepoID, input.Repo.RelativePath(), err) + log.Error("scoped workflows: source %d for consumer %s: %v", sourceRepoID, input.Repo.FullName(), err) continue } @@ -709,7 +709,7 @@ func detectAndHandleScopedWorkflows( } if err := buildApproveAndInsertRun(ctx, input, ref, consumerCommit, string(p), isForkPullRequest, dwf, sourceRepo.ID, sourceCommitSHA, true); err != nil { - log.Error("scoped workflows: source %s workflow %s: %v", sourceRepo.RelativePath(), dwf.EntryName, err) + log.Error("scoped workflows: source %s workflow %s: %v", sourceRepo.FullName(), dwf.EntryName, err) continue } } @@ -722,7 +722,7 @@ func detectAndHandleScopedWorkflows( continue } if err := CreateSkippedCommitStatusForFilteredWorkflow(ctx, input.Repo, input.Event, dwf.TriggerEvent.Name, dwf.EntryName, dwf.Content, input.Payload, scopedPrefix, requiredGlobs); err != nil { - log.Error("scoped workflows: skipped commit status for source %s workflow %s: %v", sourceRepo.RelativePath(), dwf.EntryName, err) + log.Error("scoped workflows: skipped commit status for source %s workflow %s: %v", sourceRepo.FullName(), dwf.EntryName, err) continue } } diff --git a/services/actions/reusable_workflow.go b/services/actions/reusable_workflow.go index 3c03ee65522..119c11cba48 100644 --- a/services/actions/reusable_workflow.go +++ b/services/actions/reusable_workflow.go @@ -64,7 +64,7 @@ func loadReusableWorkflowSource(ctx context.Context, run *actions_model.ActionRu if run.IsScopedRun { // A scoped workflow's cross-repo "uses:" is resolved with the consuming repo's read permission, // so the referenced repo must be readable by every consumer. Make that explicit in the failure. - return nil, 0, "", fmt.Errorf("no permission to read reusable workflow %s/%s: a scoped workflow's cross-repo \"uses:\" is resolved with the consuming repository %q read permission", ref.Owner, ref.Repo, run.Repo.RelativePath()) + return nil, 0, "", fmt.Errorf("no permission to read reusable workflow %s/%s: a scoped workflow's cross-repo \"uses:\" is resolved with the consuming repository %q read permission", ref.Owner, ref.Repo, run.Repo.FullName()) } return nil, 0, "", fmt.Errorf("no permission to read reusable workflow from %s/%s", ref.Owner, ref.Repo) } diff --git a/services/context/repo.go b/services/context/repo.go index 81291a8cc9c..ca770df7e4a 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -692,7 +692,7 @@ func repoAssignmentPrepareGitRepo(ctx *Context, data *repoAssignmentPrepareDataS ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo) if err != nil { if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") { - log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RelativePath(), err) + log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.FullName(), err) ctx.Repo.Repository.MarkAsBrokenEmpty() // Only allow access to base of repo or settings if !repoAssignmentIsHomeOrSettings(ctx, data) { @@ -944,7 +944,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) { if err == nil && len(brs) != 0 { refShortName = brs[0] } else if len(brs) == 0 { - log.Error("No branches in non-empty repository %s", ctx.Repo.Repository.RelativePath()) + log.Error("No branches in non-empty repository %s", ctx.Repo.Repository.FullName()) } else { log.Error("GetBranches error: %v", err) } diff --git a/services/convert/pull.go b/services/convert/pull.go index c8c5583ca62..74560837e10 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -147,7 +147,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u gitRepo, err := gitrepo.OpenRepository(pr.BaseRepo) if err != nil { - log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err) + log.Error("OpenRepository[%s]: %v", pr.BaseRepo.FullName(), err) return nil } defer gitRepo.Close() @@ -193,7 +193,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u headGitRepo, err := gitrepo.OpenRepository(pr.HeadRepo) if err != nil { - log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RelativePath(), err) + log.Error("OpenRepository[%s]: %v", pr.HeadRepo.FullName(), err) return nil } defer headGitRepo.Close() @@ -249,7 +249,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 { baseGitRepo, err := gitrepo.OpenRepository(pr.BaseRepo) if err != nil { - log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err) + log.Error("OpenRepository[%s]: %v", pr.BaseRepo.FullName(), err) return nil } defer baseGitRepo.Close() diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 4d75c85fbf7..4fccae4c65a 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -70,7 +70,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return repo_model.UpdateRepositoryColsNoAutoTime(ctx, m.Repo, "original_url") } -func pruneBrokenReferences(ctx context.Context, m *repo_model.Mirror, gitRepo gitrepo.Repository, timeout time.Duration) error { +func pruneBrokenReferences(ctx context.Context, m *repo_model.Mirror, repoLogName string, gitRepo gitrepo.Repository, timeout time.Duration) error { // Never follow HTTP redirects, see cmdFetch in runSync. cmd := gitcmd.NewCommand("remote", "prune").AddConfig("http.followRedirects", "false").AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout) stdout, _, pruneErr := gitrepo.RunCmdString(ctx, gitRepo, cmd) @@ -79,8 +79,8 @@ func pruneBrokenReferences(ctx context.Context, m *repo_model.Mirror, gitRepo gi stderrMessage := util.SanitizeCredentialURLs(pruneErr.Stderr()) stdoutMessage := util.SanitizeCredentialURLs(stdout) - log.Error("Failed to prune mirror repository %s references:\nStdout: %s\nStderr: %s\nErr: %v", gitRepo.RelativePath(), stdoutMessage, stderrMessage, pruneErr) - desc := fmt.Sprintf("Failed to prune mirror repository (%s) references: %s", m.Repo.FullName(), stderrMessage) + log.Error("Failed to prune mirror repository %s references:\nStdout: %s\nStderr: %s\nErr: %v", repoLogName, stdoutMessage, stderrMessage, pruneErr) + desc := fmt.Sprintf("Failed to prune mirror repository (%s) references: %s", repoLogName, stderrMessage) if err := system_model.CreateRepositoryNotice(desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } @@ -150,7 +150,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResu log.Warn("SyncMirrors [repo: %-v]: failed to update mirror repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err) err = nil // Attempt prune - pruneErr := pruneBrokenReferences(ctx, m, m.Repo, timeout) + pruneErr := pruneBrokenReferences(ctx, m, m.Repo.FullName(), m.Repo, timeout) if pruneErr == nil { // Successful prune - reattempt mirror fetchStdout, fetchStderr, err = gitrepo.RunCmdString(ctx, m.Repo, cmdFetch()) @@ -232,7 +232,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResu err = nil // Attempt prune - pruneErr := pruneBrokenReferences(ctx, m, m.Repo.WikiStorageRepo(), timeout) + pruneErr := pruneBrokenReferences(ctx, m, m.Repo.FullName()+".wiki", m.Repo.WikiStorageRepo(), timeout) if pruneErr == nil { // Successful prune - reattempt mirror stdout, stderr, err = gitrepo.RunCmdString(ctx, m.Repo.WikiStorageRepo(), cmdRemoteUpdatePrune()) diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 3b6b0a2d4ec..6df5a5d701c 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -128,10 +128,11 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { if isWiki { storageRepo = repo.WikiStorageRepo() } + mirrorLogName := fmt.Sprintf("%s%s[mirror=%d]", m.Repo.FullName(), util.Iif(isWiki, ".wiki", ""), m.ID) remoteURL, err := gitrepo.GitRemoteGetURL(ctx, storageRepo, m.RemoteName) if err != nil { - log.Error("GetRemoteURL(%s) Error %v", storageRepo.RelativePath(), err) - return errors.New("Unexpected error") + log.Error("GetRemoteURL %s failed, error %v", mirrorLogName, err) + return errors.New("GitRemoteGetURL failed") } if setting.LFS.StartServer { @@ -139,8 +140,8 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { gitRepo, err := gitrepo.OpenRepository(storageRepo) if err != nil { - log.Error("OpenRepository: %v", err) - return errors.New("Unexpected error") + log.Error("OpenRepository %s failed: %v", mirrorLogName, err) + return errors.New("OpenRepository failed") } defer gitRepo.Close() @@ -153,7 +154,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { } } - log.Trace("Pushing %s mirror[%d] remote %s", storageRepo.RelativePath(), m.ID, m.RemoteName) + log.Trace("Pushing %s remote %s", mirrorLogName, m.ID, m.RemoteName) envs := proxy.EnvWithProxy(remoteURL.URL) if err := gitrepo.PushToExternal(ctx, storageRepo, git.PushOptions{ @@ -163,8 +164,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { Timeout: timeout, Env: envs, }); err != nil { - log.Error("Error pushing %s mirror[%d] remote %s: %v", storageRepo.RelativePath(), m.ID, m.RemoteName, err) - + log.Error("Error pushing %s remote %s: %v", mirrorLogName, m.RemoteName, err) return util.SanitizeErrorCredentialURLs(err) } diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go index 35843615335..e7c0d12616c 100644 --- a/services/repository/archiver/archiver.go +++ b/services/repository/archiver/archiver.go @@ -161,9 +161,9 @@ func (aReq *ArchiveRequest) Stream(ctx context.Context, w io.Writer) error { return gitrepo.CreateArchive( ctx, aReq.Repo, + aReq.Repo.Name, aReq.Type.String(), w, - setting.Repository.PrefixArchiveFiles, aReq.CommitID, aReq.Paths, ) diff --git a/services/repository/check.go b/services/repository/check.go index 8f73186181f..8b921b0eb4b 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -95,7 +95,7 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du if err := system_model.CreateRepositoryNotice(desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } - return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %w", repo.RelativePath(), err) + return fmt.Errorf("repository garbage collection failed in repo: %s, error: %w", repo.FullName(), err) } // Now update the size of the repository @@ -105,7 +105,7 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du if err := system_model.CreateRepositoryNotice(desc); err != nil { log.Error("CreateRepositoryNotice: %v", err) } - return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %w", repo.RelativePath(), err) + return fmt.Errorf("updating size as part of garbage collection failed in repo: %s, error: %w", repo.FullName(), err) } return nil @@ -190,7 +190,7 @@ func ReinitMissingRepositories(ctx context.Context) error { } log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) if err := gitrepo.InitRepository(ctx, repo, repo.ObjectFormatName); err != nil { - log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RelativePath(), err) + log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.FullName(), err) if err2 := system_model.CreateRepositoryNotice("InitRepository (%s) [%d]: %v", repo.FullName(), repo.ID, err); err2 != nil { log.Error("CreateRepositoryNotice: %v", err2) } diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index 3b25b6457fa..9ff7f849d83 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -72,7 +72,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato // confirm that commit is exist gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) if err != nil { - return fmt.Errorf("OpenRepository[%s]: %w", repo.RelativePath(), err) + return fmt.Errorf("OpenRepository[%s]: %w", repo.FullName(), err) } defer closer.Close() diff --git a/services/repository/migrate.go b/services/repository/migrate.go index 39938fcc78f..d3371c01b44 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -36,12 +36,12 @@ func cloneWiki(ctx context.Context, repo *repo_model.Repository, opts migration. storageRepo := repo.WikiStorageRepo() if err := gitrepo.DeleteRepository(ctx, storageRepo); err != nil { - return "", fmt.Errorf("failed to remove existing wiki dir %q, err: %w", storageRepo.RelativePath(), err) + return "", fmt.Errorf("failed to remove existing wiki for repo %q, err: %w", repo.FullName(), err) } cleanIncompleteWikiPath := func() { if err := gitrepo.DeleteRepository(ctx, storageRepo); err != nil { - log.Error("Failed to remove incomplete wiki dir %q, err: %v", storageRepo.RelativePath(), err) + log.Error("Failed to remove incomplete wiki for repo %q, err: %v", repo.FullName(), err) } } if err := gitrepo.CloneExternalRepo(ctx, wikiRemoteURL, storageRepo, git.CloneRepoOptions{ @@ -63,7 +63,7 @@ func cloneWiki(ctx context.Context, repo *repo_model.Repository, opts migration. defaultBranch, err := gitrepo.GetDefaultBranch(ctx, storageRepo) if err != nil { cleanIncompleteWikiPath() - return "", fmt.Errorf("failed to get wiki repo default branch for %q, err: %w", storageRepo.RelativePath(), err) + return "", fmt.Errorf("failed to get wiki repo default branch for %q, err: %w", repo.FullName(), err) } return defaultBranch, nil diff --git a/services/repository/repository.go b/services/repository/repository.go index c02bf86ce56..aa69bcea296 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -334,7 +334,7 @@ func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, na repo := repo_model.StorageRepo(repo_model.RelativePath(owner.Name, name)) isExist, err := gitrepo.IsRepositoryExist(ctx, repo) if err != nil { - log.Error("Unable to check if %s exists. Error: %v", repo.RelativePath(), err) + log.Error("Unable to check if repo %s/%s exists, error: %v", owner.Name, name, err) return err } if !overwriteOrAdopt && isExist { diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 76051ac4f71..05c35e8806b 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -311,7 +311,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName // Rename remote wiki repository to new path and delete local copy. wikiStorageRepo := repo_model.StorageRepo(repo_model.RelativeWikiPath(oldOwner.Name, repo.Name)) if isExist, err := gitrepo.IsRepositoryExist(ctx, wikiStorageRepo); err != nil { - log.Error("Unable to check if %s exists. Error: %v", wikiStorageRepo.RelativePath(), err) + log.Error("Unable to check if wiki of repo %s/%s exists. Error: %v", oldOwner.Name, repo.Name, err) return err } else if isExist { if err := gitrepo.RenameRepository(ctx, wikiStorageRepo, repo_model.StorageRepo(repo_model.RelativeWikiPath(newOwner.Name, repo.Name))); err != nil {