mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-23 05:03:39 +09:00
refactor: correct git repo design and fix some legacy problems (#38512)
This commit is contained in:
+1
-1
@@ -128,7 +128,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
|
|||||||
}
|
}
|
||||||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
log.Trace("Processing next %d repos of %d", len(repos), count)
|
||||||
for _, repo := range repos {
|
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)
|
gitRepo, err := gitrepo.OpenRepository(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("OpenRepository: %v", err)
|
log.Warn("OpenRepository: %v", err)
|
||||||
|
|||||||
@@ -7,22 +7,23 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
repo_model "gitea.dev/models/repo"
|
||||||
"gitea.dev/modules/git"
|
"gitea.dev/modules/git"
|
||||||
"gitea.dev/modules/gitrepo"
|
"gitea.dev/modules/gitrepo"
|
||||||
"gitea.dev/modules/log"
|
"gitea.dev/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type commitChecker struct {
|
type commitChecker struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
commitCache map[string]bool
|
commitCache map[string]bool
|
||||||
gitRepoFacade gitrepo.Repository
|
repo *repo_model.Repository
|
||||||
|
|
||||||
gitRepo *git.Repository
|
gitRepo *git.Repository
|
||||||
gitRepoCloser io.Closer
|
gitRepoCloser io.Closer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCommitChecker(ctx context.Context, gitRepo gitrepo.Repository) *commitChecker {
|
func newCommitChecker(ctx context.Context, repo *repo_model.Repository) *commitChecker {
|
||||||
return &commitChecker{ctx: ctx, commitCache: make(map[string]bool), gitRepoFacade: gitRepo}
|
return &commitChecker{ctx: ctx, commitCache: make(map[string]bool), repo: repo}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *commitChecker) Close() error {
|
func (c *commitChecker) Close() error {
|
||||||
@@ -39,9 +40,9 @@ func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.gitRepo == nil {
|
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 {
|
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
|
return false
|
||||||
}
|
}
|
||||||
c.gitRepo, c.gitRepoCloser = r, closer
|
c.gitRepo, c.gitRepoCloser = r, closer
|
||||||
|
|||||||
+12
-4
@@ -230,15 +230,23 @@ func RelativePath(ownerName, repoName string) string {
|
|||||||
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
|
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
|
||||||
}
|
}
|
||||||
|
|
||||||
// RelativePath should be a unix style path like "owner-name/repo-name.git"
|
func (repo *Repository) GitRepoLocation() string {
|
||||||
func (repo *Repository) RelativePath() string {
|
|
||||||
return RelativePath(repo.OwnerName, repo.Name)
|
return RelativePath(repo.OwnerName, repo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) GitRepoUniqueID() string {
|
||||||
|
return strconv.FormatInt(repo.ID, 10)
|
||||||
|
}
|
||||||
|
|
||||||
type StorageRepo string
|
type StorageRepo string
|
||||||
|
|
||||||
// RelativePath should be an unix style path like username/reponame.git
|
func (sr StorageRepo) GitRepoUniqueID() string {
|
||||||
func (sr StorageRepo) RelativePath() 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)
|
return string(sr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,5 +26,5 @@ func TestRepository_RelativeWikiPath(t *testing.T) {
|
|||||||
|
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
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_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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
|
"gitea.dev/modules/globallock"
|
||||||
"gitea.dev/modules/log"
|
"gitea.dev/modules/log"
|
||||||
"gitea.dev/modules/setting"
|
"gitea.dev/modules/setting"
|
||||||
"gitea.dev/modules/tempdir"
|
"gitea.dev/modules/tempdir"
|
||||||
@@ -195,3 +196,11 @@ func runGitTests(m interface{ Run() int }) int {
|
|||||||
}
|
}
|
||||||
return m.Run()
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
+28
-33
@@ -5,26 +5,26 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
"gitea.dev/modules/proxy"
|
"gitea.dev/modules/proxy"
|
||||||
|
"gitea.dev/modules/setting"
|
||||||
|
"gitea.dev/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RepositoryFacade interface {
|
type RepositoryFacade = gitcmd.RepositoryFacade
|
||||||
RelativePath() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RepositoryBase struct {
|
type RepositoryBase struct {
|
||||||
Path string
|
Path string // absolute path
|
||||||
|
|
||||||
LastCommitCache *LastCommitCache
|
LastCommitCache *LastCommitCache
|
||||||
|
|
||||||
@@ -32,40 +32,35 @@ type RepositoryBase struct {
|
|||||||
objectFormatCache ObjectFormat
|
objectFormatCache ObjectFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareRepositoryBase(repoPath string) RepositoryBase {
|
func OpenRepository(repoPath string) (*Repository, error) {
|
||||||
return RepositoryBase{Path: repoPath, tagCache: newObjectCache[*Tag]()}
|
repoPath, err := filepath.Abs(repoPath)
|
||||||
}
|
|
||||||
|
|
||||||
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 <command> [<revision>...] -- [<file>...]'
|
|
||||||
logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat).
|
|
||||||
AddDynamicArguments(revisionRange).AddArguments("--").WithDir(repo.Path).
|
|
||||||
RunStdBytes(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (repo *Repository) Close() error {
|
||||||
var commits []*Commit
|
if repo == nil {
|
||||||
if len(logs) == 0 {
|
setting.PanicInDevOrTesting("don't close a nil repository")
|
||||||
return commits, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
repo.LastCommitCache = nil
|
||||||
parts := bytes.SplitSeq(logs, []byte{'\n'})
|
repo.tagCache = nil
|
||||||
|
return repo.closeInternal()
|
||||||
for commitID := range parts {
|
|
||||||
commit, err := repo.GetCommit(ctx, string(commitID))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
commits = append(commits, commit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return commits, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRepoURLAccessible checks if given repository URL is accessible.
|
// IsRepoURLAccessible checks if given repository URL is accessible.
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ package git
|
|||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
gitealog "gitea.dev/modules/log"
|
|
||||||
"gitea.dev/modules/setting"
|
"gitea.dev/modules/setting"
|
||||||
"gitea.dev/modules/util"
|
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5"
|
"github.com/go-git/go-billy/v5"
|
||||||
"github.com/go-git/go-billy/v5/osfs"
|
"github.com/go-git/go-billy/v5/osfs"
|
||||||
@@ -30,25 +28,13 @@ type Repository struct {
|
|||||||
gogitStorage *filesystem.Storage
|
gogitStorage *filesystem.Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenRepository(repoPath string) (*Repository, error) {
|
func openRepositoryInternal(gitRepo *Repository) error {
|
||||||
repoPath, err := filepath.Abs(repoPath)
|
fs := osfs.New(gitRepo.Path)
|
||||||
if err != nil {
|
_, err := fs.Stat(".git")
|
||||||
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")
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fs, err = fs.Chroot(".git")
|
fs, err = fs.Chroot(".git")
|
||||||
if err != nil {
|
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
|
// 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 {
|
} else {
|
||||||
altFs = osfs.New("/")
|
altFs = osfs.New("/")
|
||||||
}
|
}
|
||||||
storage := filesystem.NewStorageWithOptions(fs, cache.NewObjectLRUDefault(), filesystem.Options{KeepDescriptors: true, LargeObjectThreshold: setting.Git.LargeObjectThreshold, AlternatesFS: altFs})
|
gitRepo.objectFormatCache = ParseGogitHash(plumbing.ZeroHash).Type()
|
||||||
gogitRepo, err := gogit.Open(storage, fs)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
_ = gitRepo.gogitStorage.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
repo := &Repository{
|
|
||||||
RepositoryBase: prepareRepositoryBase(repoPath),
|
|
||||||
gogitRepo: gogitRepo,
|
|
||||||
gogitStorage: storage,
|
|
||||||
}
|
|
||||||
repo.objectFormatCache = ParseGogitHash(plumbing.ZeroHash).Type()
|
|
||||||
return repo, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close this repository, in particular close the underlying gogitStorage if this is not nil
|
func (repo *Repository) closeInternal() error {
|
||||||
func (repo *Repository) Close() error {
|
if repo.gogitStorage == nil {
|
||||||
if repo == nil || repo.gogitStorage == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := repo.gogitStorage.Close(); err != nil {
|
err := repo.gogitStorage.Close()
|
||||||
gitealog.Error("Error closing storage: %v", err)
|
|
||||||
}
|
|
||||||
repo.gogitStorage = nil
|
repo.gogitStorage = nil
|
||||||
repo.LastCommitCache = nil
|
return err
|
||||||
repo.tagCache = nil
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoGitRepo gets the go-git repo representation
|
// GoGitRepo gets the go-git repo representation
|
||||||
|
|||||||
@@ -8,12 +8,9 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.dev/modules/log"
|
"gitea.dev/modules/log"
|
||||||
"gitea.dev/modules/setting"
|
|
||||||
"gitea.dev/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const isGogit = false
|
const isGogit = false
|
||||||
@@ -26,19 +23,8 @@ type Repository struct {
|
|||||||
catFileBatchInUse bool
|
catFileBatchInUse bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenRepository(repoPath string) (*Repository, error) {
|
func openRepositoryInternal(_ *Repository) error {
|
||||||
repoPath, err := filepath.Abs(repoPath)
|
return nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CatFileBatch obtains a "batch object provider" for this repository.
|
// 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
|
return tempBatch, tempBatch.Close, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) Close() error {
|
func (repo *Repository) closeInternal() error {
|
||||||
if repo == nil {
|
|
||||||
setting.PanicInDevOrTesting("don't close a nil repository")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
repo.mu.Lock()
|
repo.mu.Lock()
|
||||||
defer repo.mu.Unlock()
|
defer repo.mu.Unlock()
|
||||||
if repo.catFileBatchCloser != nil {
|
if repo.catFileBatchCloser != nil {
|
||||||
@@ -92,7 +74,5 @@ func (repo *Repository) Close() error {
|
|||||||
repo.catFileBatchCloser = nil
|
repo.catFileBatchCloser = nil
|
||||||
repo.catFileBatchInUse = false
|
repo.catFileBatchInUse = false
|
||||||
}
|
}
|
||||||
repo.LastCommitCache = nil
|
|
||||||
repo.tagCache = nil
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,38 @@ func (repo *Repository) GetTagCommit(ctx context.Context, name string) (*Commit,
|
|||||||
return repo.GetCommit(ctx, RefNameFromTag(name).String())
|
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 <command> [<revision>...] -- [<file>...]'
|
||||||
|
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) {
|
func (repo *Repository) getCommitByPathWithID(ctx context.Context, id ObjectID, relpath string) (*Commit, error) {
|
||||||
// File name starts with ':' must be escaped.
|
// File name starts with ':' must be escaped.
|
||||||
if strings.HasPrefix(relpath, ":") {
|
if strings.HasPrefix(relpath, ":") {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
@@ -18,24 +17,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CreateArchive create archive content to the target path
|
// 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" {
|
if format == "unknown" {
|
||||||
return fmt.Errorf("unknown format: %v", format)
|
return fmt.Errorf("unknown format: %v", format)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := gitcmd.NewCommand("archive")
|
cmd := gitcmd.NewCommand("archive")
|
||||||
if usePrefix {
|
if setting.Repository.PrefixArchiveFiles {
|
||||||
cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.RelativePath(), ".git"))+"/")
|
cmd.AddOptionFormat("--prefix=%s", strings.ToLower(repoName)+"/")
|
||||||
}
|
}
|
||||||
cmd.AddOptionFormat("--format=%s", format)
|
cmd.AddOptionFormat("--format=%s", format)
|
||||||
cmd.AddDynamicArguments(commitID)
|
cmd.AddDynamicArguments(commitID)
|
||||||
|
|
||||||
paths = slices.Clone(paths)
|
|
||||||
for i := range 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
|
// 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))
|
return RunCmdWithStderr(ctx, repo, cmd.WithStdoutCopy(target))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestReadingBlameOutputSha256(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Without .git-blame-ignore-revs", func(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)
|
repo, err := OpenRepository(storage)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer repo.Close()
|
defer repo.Close()
|
||||||
@@ -70,7 +70,7 @@ func TestReadingBlameOutputSha256(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With .git-blame-ignore-revs", func(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)
|
repo, err := OpenRepository(storage)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer repo.Close()
|
defer repo.Close()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestReadingBlameOutput(t *testing.T) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
t.Run("Without .git-blame-ignore-revs", func(t *testing.T) {
|
t.Run("Without .git-blame-ignore-revs", func(t *testing.T) {
|
||||||
storage := &mockRepository{path: "repo5_pulls"}
|
storage := mockRepository("repo5_pulls")
|
||||||
repo, err := OpenRepository(storage)
|
repo, err := OpenRepository(storage)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer repo.Close()
|
defer repo.Close()
|
||||||
@@ -64,7 +64,7 @@ func TestReadingBlameOutput(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With .git-blame-ignore-revs", func(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)
|
repo, err := OpenRepository(storage)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer repo.Close()
|
defer repo.Close()
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func RunCmd(ctx context.Context, repo Repository, cmd *gitcmd.Command) error {
|
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) {
|
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) {
|
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 {
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func TestParseCommitFileStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetCommitFileStatusMerges(t *testing.T) {
|
func TestGetCommitFileStatusMerges(t *testing.T) {
|
||||||
bareRepo6 := &mockRepository{path: "repo6_merge"}
|
bareRepo6 := mockRepository("repo6_merge")
|
||||||
|
|
||||||
commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo6, "022f4ce6214973e018f02bf363bf8a2e3691f699")
|
commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo6, "022f4ce6214973e018f02bf363bf8a2e3691f699")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -139,7 +139,7 @@ func TestGetCommitFileStatusMerges(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetCommitFileStatusMergesSha256(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")
|
commitFileStatus, err := GetCommitFileStatus(t.Context(), bareRepo6Sha256, "d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestCommitsCount(t *testing.T) {
|
func TestCommitsCount(t *testing.T) {
|
||||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
bareRepo1 := mockRepository("repo1_bare")
|
||||||
|
|
||||||
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
|
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
|
||||||
CommitsCountOptions{
|
CommitsCountOptions{
|
||||||
@@ -22,7 +22,7 @@ func TestCommitsCount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCommitsCountWithSinceUntil(t *testing.T) {
|
func TestCommitsCountWithSinceUntil(t *testing.T) {
|
||||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
bareRepo1 := mockRepository("repo1_bare")
|
||||||
revision := []string{"8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"}
|
revision := []string{"8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"}
|
||||||
|
|
||||||
// The three commits on this revision are dated 2018-04-18, 2017-12-19 and 2017-12-19.
|
// 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) {
|
func TestCommitsCountWithoutBase(t *testing.T) {
|
||||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
bareRepo1 := mockRepository("repo1_bare")
|
||||||
|
|
||||||
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
|
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
|
||||||
CommitsCountOptions{
|
CommitsCountOptions{
|
||||||
@@ -66,7 +66,7 @@ func TestCommitsCountWithoutBase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetLatestCommitTime(t *testing.T) {
|
func TestGetLatestCommitTime(t *testing.T) {
|
||||||
bareRepo1 := &mockRepository{path: "repo1_bare"}
|
bareRepo1 := mockRepository("repo1_bare")
|
||||||
lct, err := GetLatestCommitTime(t.Context(), bareRepo1)
|
lct, err := GetLatestCommitTime(t.Context(), bareRepo1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Time is Sun Nov 13 16:40:14 2022 +0100
|
// Time is Sun Nov 13 16:40:14 2022 +0100
|
||||||
|
|||||||
@@ -15,14 +15,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockRepository struct {
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *mockRepository) RelativePath() string {
|
|
||||||
return r.path
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeBaseNoCommonHistory(t *testing.T) {
|
func TestMergeBaseNoCommonHistory(t *testing.T) {
|
||||||
repoDir := filepath.Join(t.TempDir(), "repo.git")
|
repoDir := filepath.Join(t.TempDir(), "repo.git")
|
||||||
require.NoError(t, gitcmd.NewCommand("init").AddDynamicArguments(repoDir).Run(t.Context()))
|
require.NoError(t, gitcmd.NewCommand("init").AddDynamicArguments(repoDir).Run(t.Context()))
|
||||||
@@ -44,13 +36,13 @@ data 12
|
|||||||
Hello from 2
|
Hello from 2
|
||||||
`))).RunStdString(t.Context())
|
`))).RunStdString(t.Context())
|
||||||
require.NoError(t, runErr)
|
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.Empty(t, mergeBase)
|
||||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRepoGetDivergingCommits(t *testing.T) {
|
func TestRepoGetDivergingCommits(t *testing.T) {
|
||||||
repo := &mockRepository{path: "repo1_bare"}
|
repo := mockRepository("repo1_bare")
|
||||||
do, err := GetDivergingCommits(t.Context(), repo, "master", "branch2")
|
do, err := GetDivergingCommits(t.Context(), repo, "master", "branch2")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, &DivergeObject{
|
assert.Equal(t, &DivergeObject{
|
||||||
@@ -74,7 +66,7 @@ func TestRepoGetDivergingCommits(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetCommitIDsBetweenReverse(t *testing.T) {
|
func TestGetCommitIDsBetweenReverse(t *testing.T) {
|
||||||
repo := &mockRepository{path: "repo1_bare"}
|
repo := mockRepository("repo1_bare")
|
||||||
|
|
||||||
// tests raw commit IDs
|
// tests raw commit IDs
|
||||||
commitIDs, err := GetCommitIDsBetweenReverse(t.Context(), repo,
|
commitIDs, err := GetCommitIDsBetweenReverse(t.Context(), repo,
|
||||||
|
|||||||
@@ -6,17 +6,13 @@ package gitrepo
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"gitea.dev/modules/git"
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"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.
|
// 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 {
|
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").
|
_, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config", "--add").
|
||||||
AddDynamicArguments(key, value))
|
AddDynamicArguments(key, value))
|
||||||
return err
|
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 does not exist, it will be created.
|
||||||
// If the key exists, it will be updated to the new value.
|
// If the key exists, it will be updated to the new value.
|
||||||
func GitConfigSet(ctx context.Context, repo Repository, key, value string) error {
|
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").
|
_, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config").
|
||||||
AddDynamicArguments(key, value))
|
AddDynamicArguments(key, value))
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ package gitrepo
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"gitea.dev/modules/git"
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
"gitea.dev/modules/globallock"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FetchRemoteCommit fetches a specific commit and its related objects from a remote
|
// 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
|
// This behavior is sufficient for temporary operations, such as determining the
|
||||||
// merge base between commits.
|
// merge base between commits.
|
||||||
func FetchRemoteCommit(ctx context.Context, repo, remoteRepo Repository, commitID string) error {
|
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").
|
return RunCmd(ctx, repo, gitcmd.NewCommand("fetch", "--no-tags").
|
||||||
AddDynamicArguments(repoPath(remoteRepo)).
|
AddDynamicArguments(repoPath(remoteRepo)).
|
||||||
AddDynamicArguments(commitID))
|
AddDynamicArguments(commitID))
|
||||||
|
|||||||
@@ -14,17 +14,12 @@ import (
|
|||||||
"gitea.dev/modules/git"
|
"gitea.dev/modules/git"
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
"gitea.dev/modules/reqctx"
|
"gitea.dev/modules/reqctx"
|
||||||
"gitea.dev/modules/setting"
|
|
||||||
"gitea.dev/modules/util"
|
"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")
|
var repoPath = gitcmd.RepoLocalPath
|
||||||
// to a local filesystem path according to setting.RepoRootPath
|
|
||||||
var repoPath = func(repo Repository) string {
|
|
||||||
return filepath.Join(setting.RepoRootPath, filepath.FromSlash(repo.RelativePath()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenRepository opens the repository at the given relative path with the provided context.
|
// OpenRepository opens the repository at the given relative path with the provided context.
|
||||||
func OpenRepository(repo Repository) (*git.Repository, error) {
|
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.
|
// contextKey is a value for use with context.WithValue.
|
||||||
type contextKey struct {
|
type contextKey struct {
|
||||||
repoPath string
|
uniqueID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
|
// 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.
|
// 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.
|
// 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) {
|
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 {
|
if gitRepo, ok := ctx.Value(ck).(*git.Repository); ok {
|
||||||
return gitRepo, nil
|
return gitRepo, nil
|
||||||
}
|
}
|
||||||
gitRepo, err := git.OpenRepository(ck.repoPath)
|
gitRepo, err := git.OpenRepository(repoPath(repo))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,19 +7,20 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dev/models/repo"
|
||||||
"gitea.dev/modules/git"
|
"gitea.dev/modules/git"
|
||||||
"gitea.dev/modules/setting"
|
"gitea.dev/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func mockRepository(repoPath string) repo.StorageRepo {
|
||||||
// resolve repository path relative to the test directory
|
if !filepath.IsAbs(repoPath) {
|
||||||
setting.SetupGiteaTestEnv()
|
// resolve repository path relative to the unit test fixture directory
|
||||||
giteaRoot := setting.GetGiteaTestSourceRoot()
|
repoPath = filepath.Join(setting.GetGiteaTestSourceRoot(), "modules/git/tests/repos", repoPath)
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
return repo.StorageRepo(repoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
setting.SetupGiteaTestEnv()
|
||||||
git.RunGitTests(m)
|
git.RunGitTests(m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ M 100644 :5 z/d
|
|||||||
func TestMergeTreeDirectoryRenameConflictWithoutFiles(t *testing.T) {
|
func TestMergeTreeDirectoryRenameConflictWithoutFiles(t *testing.T) {
|
||||||
repoDir := prepareRepoDirRenameConflict(t)
|
repoDir := prepareRepoDirRenameConflict(t)
|
||||||
require.DirExists(t, repoDir)
|
require.DirExists(t, repoDir)
|
||||||
repo := &mockRepository{path: repoDir}
|
repo := mockRepository(repoDir)
|
||||||
|
|
||||||
mergeBase, err := MergeBase(t.Context(), repo, "add", "split")
|
mergeBase, err := MergeBase(t.Context(), repo, "add", "split")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"gitea.dev/modules/git"
|
"gitea.dev/modules/git"
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
giturl "gitea.dev/modules/git/url"
|
giturl "gitea.dev/modules/git/url"
|
||||||
"gitea.dev/modules/globallock"
|
|
||||||
"gitea.dev/modules/util"
|
"gitea.dev/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...RemoteOption) error {
|
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")
|
cmd := gitcmd.NewCommand("remote", "add")
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
switch 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 {
|
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)
|
cmd := gitcmd.NewCommand("remote", "rm").AddDynamicArguments(remoteName)
|
||||||
_, _, err := RunCmdString(ctx, repo, cmd)
|
_, _, err := RunCmdString(ctx, repo, cmd)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1157,7 +1157,7 @@ func parseCompareInfo(ctx *context.APIContext, compareParam string) (result *git
|
|||||||
return nil, nil
|
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())
|
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())
|
headRefValid := headRef.IsBranch() || headRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(headRepo.ObjectFormatName), headRef.ShortName())
|
||||||
|
|||||||
@@ -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)
|
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.
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ func loadScopedWorkflowModel(ctx *context.Context, repo *repo_model.Repository,
|
|||||||
}
|
}
|
||||||
content, err := actions_service.ScopedWorkflowContent(ctx, sourceRepo, workflowID)
|
content, err := actions_service.ScopedWorkflowContent(ctx, sourceRepo, workflowID)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
if content == nil {
|
if content == nil {
|
||||||
|
|||||||
@@ -418,7 +418,8 @@ func serviceRPC(ctx *context.Context, service string) {
|
|||||||
WithStdoutCopy(ctx.Resp),
|
WithStdoutCopy(ctx.Resp),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
if !gitcmd.IsErrorCanceledOrKilled(err) {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ func listSourceScopedWorkflowFiles(ctx *context.Context, repo *repo_model.Reposi
|
|||||||
if !repo.IsEmpty {
|
if !repo.IsEmpty {
|
||||||
_, parsed, err := actions_service.LoadParsedScopedWorkflows(ctx, repo)
|
_, parsed, err := actions_service.LoadParsedScopedWorkflows(ctx, repo)
|
||||||
if err != nil {
|
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 {
|
} else {
|
||||||
for _, p := range parsed {
|
for _, p := range parsed {
|
||||||
info := scopedWorkflowInfo{
|
info := scopedWorkflowInfo{
|
||||||
|
|||||||
@@ -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",
|
log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules",
|
||||||
input.Repo.RelativePath(),
|
input.Repo.FullName(),
|
||||||
commit.ID,
|
commit.ID,
|
||||||
input.Event,
|
input.Event,
|
||||||
len(workflows),
|
len(workflows),
|
||||||
@@ -204,7 +204,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
|
|
||||||
for _, wf := range workflows {
|
for _, wf := range workflows {
|
||||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
|
|
||||||
for _, wf := range filtered {
|
for _, wf := range filtered {
|
||||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,11 +236,11 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||||
}
|
}
|
||||||
if len(baseWorkflows) == 0 {
|
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 {
|
} else {
|
||||||
for _, wf := range baseWorkflows {
|
for _, wf := range baseWorkflows {
|
||||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
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
|
continue
|
||||||
}
|
}
|
||||||
if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget {
|
if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget {
|
||||||
@@ -250,7 +250,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
}
|
}
|
||||||
for _, wf := range baseFiltered {
|
for _, wf := range baseFiltered {
|
||||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
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
|
continue
|
||||||
}
|
}
|
||||||
if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget {
|
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) {
|
if slices.Contains(skipWorkflowEvents, input.Event) {
|
||||||
for _, s := range setting.Actions.SkipWorkflowStrings {
|
for _, s := range setting.Actions.SkipWorkflowStrings {
|
||||||
if input.PullRequest != nil && strings.Contains(input.PullRequest.Issue.Title, s) {
|
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
|
return true
|
||||||
}
|
}
|
||||||
if strings.Contains(commit.MessageRaw, s) {
|
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
|
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
|
// 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 true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -326,7 +326,7 @@ func handleWorkflows(
|
|||||||
ref git.RefName,
|
ref git.RefName,
|
||||||
) error {
|
) error {
|
||||||
if len(detectedWorkflows) == 0 {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +340,7 @@ func handleWorkflows(
|
|||||||
for _, dwf := range detectedWorkflows {
|
for _, dwf := range detectedWorkflows {
|
||||||
// repo-level run: the workflow content is this repo at this commit
|
// 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 {
|
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
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,7 +404,7 @@ func handleFilteredWorkflows(ctx context.Context, input *notifyInput, filteredWo
|
|||||||
}
|
}
|
||||||
requiredGlobs, err := getAllRequiredStatusContextGlobs(ctx, input.Repo)
|
requiredGlobs, err := getAllRequiredStatusContextGlobs(ctx, input.Repo)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
if len(requiredGlobs) == 0 {
|
if len(requiredGlobs) == 0 {
|
||||||
@@ -412,7 +412,7 @@ func handleFilteredWorkflows(ctx context.Context, input *notifyInput, filteredWo
|
|||||||
}
|
}
|
||||||
for _, dwf := range filteredWorkflows {
|
for _, dwf := range filteredWorkflows {
|
||||||
if err := CreateSkippedCommitStatusForFilteredWorkflow(ctx, input.Repo, input.Event, dwf.TriggerEvent.Name, dwf.EntryName, dwf.Content, input.Payload, "", requiredGlobs); err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,7 +540,7 @@ func handleSchedules(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(detectedWorkflows) == 0 {
|
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
|
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.
|
// A filtered-out scoped workflow only posts a skipped status when its context is a required check.
|
||||||
requiredGlobs, err := getAllRequiredStatusContextGlobs(ctx, input.Repo)
|
requiredGlobs, err := getAllRequiredStatusContextGlobs(ctx, input.Repo)
|
||||||
if err != nil {
|
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
|
// The same source repo may be registered at both the owner and instance level; dedup
|
||||||
@@ -688,7 +688,7 @@ func detectAndHandleScopedWorkflows(
|
|||||||
sourceRepo := sourceRepos[sourceRepoID]
|
sourceRepo := sourceRepos[sourceRepoID]
|
||||||
if sourceRepo == nil {
|
if sourceRepo == nil {
|
||||||
// don't abort the other effective sources for this event
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
if sourceRepo.IsEmpty {
|
if sourceRepo.IsEmpty {
|
||||||
@@ -697,7 +697,7 @@ func detectAndHandleScopedWorkflows(
|
|||||||
|
|
||||||
sourceCommitSHA, detected, filtered, err := detectScopedWorkflowsForSource(ctx, input, consumerGitRepo, consumerCommit, sourceRepo)
|
sourceCommitSHA, detected, filtered, err := detectScopedWorkflowsForSource(ctx, input, consumerGitRepo, consumerCommit, sourceRepo)
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,7 +709,7 @@ func detectAndHandleScopedWorkflows(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := buildApproveAndInsertRun(ctx, input, ref, consumerCommit, string(p), isForkPullRequest, dwf, sourceRepo.ID, sourceCommitSHA, true); err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -722,7 +722,7 @@ func detectAndHandleScopedWorkflows(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := CreateSkippedCommitStatusForFilteredWorkflow(ctx, input.Repo, input.Event, dwf.TriggerEvent.Name, dwf.EntryName, dwf.Content, input.Payload, scopedPrefix, requiredGlobs); err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func loadReusableWorkflowSource(ctx context.Context, run *actions_model.ActionRu
|
|||||||
if run.IsScopedRun {
|
if run.IsScopedRun {
|
||||||
// A scoped workflow's cross-repo "uses:" is resolved with the consuming repo's read permission,
|
// 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.
|
// 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)
|
return nil, 0, "", fmt.Errorf("no permission to read reusable workflow from %s/%s", ref.Owner, ref.Repo)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -692,7 +692,7 @@ func repoAssignmentPrepareGitRepo(ctx *Context, data *repoAssignmentPrepareDataS
|
|||||||
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
|
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
|
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()
|
ctx.Repo.Repository.MarkAsBrokenEmpty()
|
||||||
// Only allow access to base of repo or settings
|
// Only allow access to base of repo or settings
|
||||||
if !repoAssignmentIsHomeOrSettings(ctx, data) {
|
if !repoAssignmentIsHomeOrSettings(ctx, data) {
|
||||||
@@ -944,7 +944,7 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
|||||||
if err == nil && len(brs) != 0 {
|
if err == nil && len(brs) != 0 {
|
||||||
refShortName = brs[0]
|
refShortName = brs[0]
|
||||||
} else if len(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 {
|
} else {
|
||||||
log.Error("GetBranches error: %v", err)
|
log.Error("GetBranches error: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
|||||||
|
|
||||||
gitRepo, err := gitrepo.OpenRepository(pr.BaseRepo)
|
gitRepo, err := gitrepo.OpenRepository(pr.BaseRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err)
|
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.FullName(), err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer gitRepo.Close()
|
defer gitRepo.Close()
|
||||||
@@ -193,7 +193,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
|||||||
|
|
||||||
headGitRepo, err := gitrepo.OpenRepository(pr.HeadRepo)
|
headGitRepo, err := gitrepo.OpenRepository(pr.HeadRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RelativePath(), err)
|
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.FullName(), err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer headGitRepo.Close()
|
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 {
|
if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 {
|
||||||
baseGitRepo, err := gitrepo.OpenRepository(pr.BaseRepo)
|
baseGitRepo, err := gitrepo.OpenRepository(pr.BaseRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err)
|
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.FullName(), err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer baseGitRepo.Close()
|
defer baseGitRepo.Close()
|
||||||
|
|||||||
@@ -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")
|
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.
|
// Never follow HTTP redirects, see cmdFetch in runSync.
|
||||||
cmd := gitcmd.NewCommand("remote", "prune").AddConfig("http.followRedirects", "false").AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout)
|
cmd := gitcmd.NewCommand("remote", "prune").AddConfig("http.followRedirects", "false").AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout)
|
||||||
stdout, _, pruneErr := gitrepo.RunCmdString(ctx, gitRepo, cmd)
|
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())
|
stderrMessage := util.SanitizeCredentialURLs(pruneErr.Stderr())
|
||||||
stdoutMessage := util.SanitizeCredentialURLs(stdout)
|
stdoutMessage := util.SanitizeCredentialURLs(stdout)
|
||||||
|
|
||||||
log.Error("Failed to prune mirror repository %s references:\nStdout: %s\nStderr: %s\nErr: %v", gitRepo.RelativePath(), stdoutMessage, stderrMessage, pruneErr)
|
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", m.Repo.FullName(), stderrMessage)
|
desc := fmt.Sprintf("Failed to prune mirror repository (%s) references: %s", repoLogName, stderrMessage)
|
||||||
if err := system_model.CreateRepositoryNotice(desc); err != nil {
|
if err := system_model.CreateRepositoryNotice(desc); err != nil {
|
||||||
log.Error("CreateRepositoryNotice: %v", err)
|
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)
|
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
|
err = nil
|
||||||
// Attempt prune
|
// Attempt prune
|
||||||
pruneErr := pruneBrokenReferences(ctx, m, m.Repo, timeout)
|
pruneErr := pruneBrokenReferences(ctx, m, m.Repo.FullName(), m.Repo, timeout)
|
||||||
if pruneErr == nil {
|
if pruneErr == nil {
|
||||||
// Successful prune - reattempt mirror
|
// Successful prune - reattempt mirror
|
||||||
fetchStdout, fetchStderr, err = gitrepo.RunCmdString(ctx, m.Repo, cmdFetch())
|
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
|
err = nil
|
||||||
|
|
||||||
// Attempt prune
|
// 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 {
|
if pruneErr == nil {
|
||||||
// Successful prune - reattempt mirror
|
// Successful prune - reattempt mirror
|
||||||
stdout, stderr, err = gitrepo.RunCmdString(ctx, m.Repo.WikiStorageRepo(), cmdRemoteUpdatePrune())
|
stdout, stderr, err = gitrepo.RunCmdString(ctx, m.Repo.WikiStorageRepo(), cmdRemoteUpdatePrune())
|
||||||
|
|||||||
@@ -128,10 +128,11 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
|||||||
if isWiki {
|
if isWiki {
|
||||||
storageRepo = repo.WikiStorageRepo()
|
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)
|
remoteURL, err := gitrepo.GitRemoteGetURL(ctx, storageRepo, m.RemoteName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetRemoteURL(%s) Error %v", storageRepo.RelativePath(), err)
|
log.Error("GetRemoteURL %s failed, error %v", mirrorLogName, err)
|
||||||
return errors.New("Unexpected error")
|
return errors.New("GitRemoteGetURL failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.LFS.StartServer {
|
if setting.LFS.StartServer {
|
||||||
@@ -139,8 +140,8 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
|||||||
|
|
||||||
gitRepo, err := gitrepo.OpenRepository(storageRepo)
|
gitRepo, err := gitrepo.OpenRepository(storageRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("OpenRepository: %v", err)
|
log.Error("OpenRepository %s failed: %v", mirrorLogName, err)
|
||||||
return errors.New("Unexpected error")
|
return errors.New("OpenRepository failed")
|
||||||
}
|
}
|
||||||
defer gitRepo.Close()
|
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)
|
envs := proxy.EnvWithProxy(remoteURL.URL)
|
||||||
if err := gitrepo.PushToExternal(ctx, storageRepo, git.PushOptions{
|
if err := gitrepo.PushToExternal(ctx, storageRepo, git.PushOptions{
|
||||||
@@ -163,8 +164,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
|||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Env: envs,
|
Env: envs,
|
||||||
}); err != nil {
|
}); 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)
|
return util.SanitizeErrorCredentialURLs(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,9 +161,9 @@ func (aReq *ArchiveRequest) Stream(ctx context.Context, w io.Writer) error {
|
|||||||
return gitrepo.CreateArchive(
|
return gitrepo.CreateArchive(
|
||||||
ctx,
|
ctx,
|
||||||
aReq.Repo,
|
aReq.Repo,
|
||||||
|
aReq.Repo.Name,
|
||||||
aReq.Type.String(),
|
aReq.Type.String(),
|
||||||
w,
|
w,
|
||||||
setting.Repository.PrefixArchiveFiles,
|
|
||||||
aReq.CommitID,
|
aReq.CommitID,
|
||||||
aReq.Paths,
|
aReq.Paths,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du
|
|||||||
if err := system_model.CreateRepositoryNotice(desc); err != nil {
|
if err := system_model.CreateRepositoryNotice(desc); err != nil {
|
||||||
log.Error("CreateRepositoryNotice: %v", err)
|
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
|
// 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 {
|
if err := system_model.CreateRepositoryNotice(desc); err != nil {
|
||||||
log.Error("CreateRepositoryNotice: %v", err)
|
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
|
return nil
|
||||||
@@ -190,7 +190,7 @@ func ReinitMissingRepositories(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
|
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
|
||||||
if err := gitrepo.InitRepository(ctx, repo, repo.ObjectFormatName); err != nil {
|
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 {
|
if err2 := system_model.CreateRepositoryNotice("InitRepository (%s) [%d]: %v", repo.FullName(), repo.ID, err); err2 != nil {
|
||||||
log.Error("CreateRepositoryNotice: %v", err2)
|
log.Error("CreateRepositoryNotice: %v", err2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
|
|||||||
// confirm that commit is exist
|
// confirm that commit is exist
|
||||||
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
|
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("OpenRepository[%s]: %w", repo.RelativePath(), err)
|
return fmt.Errorf("OpenRepository[%s]: %w", repo.FullName(), err)
|
||||||
}
|
}
|
||||||
defer closer.Close()
|
defer closer.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ func cloneWiki(ctx context.Context, repo *repo_model.Repository, opts migration.
|
|||||||
storageRepo := repo.WikiStorageRepo()
|
storageRepo := repo.WikiStorageRepo()
|
||||||
|
|
||||||
if err := gitrepo.DeleteRepository(ctx, storageRepo); err != nil {
|
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() {
|
cleanIncompleteWikiPath := func() {
|
||||||
if err := gitrepo.DeleteRepository(ctx, storageRepo); err != nil {
|
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{
|
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)
|
defaultBranch, err := gitrepo.GetDefaultBranch(ctx, storageRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanIncompleteWikiPath()
|
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
|
return defaultBranch, nil
|
||||||
|
|||||||
@@ -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))
|
repo := repo_model.StorageRepo(repo_model.RelativePath(owner.Name, name))
|
||||||
isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
|
isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
|
||||||
if err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
if !overwriteOrAdopt && isExist {
|
if !overwriteOrAdopt && isExist {
|
||||||
|
|||||||
@@ -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.
|
// Rename remote wiki repository to new path and delete local copy.
|
||||||
wikiStorageRepo := repo_model.StorageRepo(repo_model.RelativeWikiPath(oldOwner.Name, repo.Name))
|
wikiStorageRepo := repo_model.StorageRepo(repo_model.RelativeWikiPath(oldOwner.Name, repo.Name))
|
||||||
if isExist, err := gitrepo.IsRepositoryExist(ctx, wikiStorageRepo); err != nil {
|
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
|
return err
|
||||||
} else if isExist {
|
} else if isExist {
|
||||||
if err := gitrepo.RenameRepository(ctx, wikiStorageRepo, repo_model.StorageRepo(repo_model.RelativeWikiPath(newOwner.Name, repo.Name))); err != nil {
|
if err := gitrepo.RenameRepository(ctx, wikiStorageRepo, repo_model.StorageRepo(repo_model.RelativeWikiPath(newOwner.Name, repo.Name))); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user