Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix data-race problems in git module (quick patch) #19934

Merged
merged 3 commits into from
Jun 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions integrations/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
assert.NoError(t, unittest.LoadFixtures())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitWithConfigSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand Down Expand Up @@ -576,7 +576,7 @@ func resetFixtures(t *testing.T) {
assert.NoError(t, unittest.LoadFixtures())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitWithConfigSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand Down
2 changes: 1 addition & 1 deletion integrations/migration-test/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func initMigrationTest(t *testing.T) func() {
assert.True(t, len(setting.RepoRootPath) != 0)
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitWithConfigSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand Down
2 changes: 1 addition & 1 deletion models/migrations/migrations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
deferFn := PrintCurrentTest(t, ourSkip)
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitWithConfigSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
Expand Down
4 changes: 2 additions & 2 deletions models/unittest/testdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
fatalTestError("util.CopyDir: %v\n", err)
}
if err = git.InitWithConfigSync(context.Background()); err != nil {
if err = git.InitOnceWithSync(context.Background()); err != nil {
fatalTestError("git.Init: %v\n", err)
}

Expand Down Expand Up @@ -202,7 +202,7 @@ func PrepareTestEnv(t testing.TB) {
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
assert.NoError(t, git.InitWithConfigSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background()))

ownerDirs, err := os.ReadDir(setting.RepoRootPath)
assert.NoError(t, err)
Expand Down
78 changes: 37 additions & 41 deletions modules/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,12 @@ var (
GitExecutable = "git"

// DefaultContext is the default context to run git commands in
// will be overwritten by InitWithConfigSync with HammerContext
// will be overwritten by InitXxx with HammerContext
DefaultContext = context.Background()

// SupportProcReceive version >= 2.29.0
SupportProcReceive bool

// initMutex is used to avoid Golang's data race error. see the comments below.
initMutex sync.Mutex

gitVersion *version.Version
)

Expand Down Expand Up @@ -131,15 +128,6 @@ func VersionInfo() string {
return fmt.Sprintf(format, args...)
}

// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
// This method doesn't change anything to filesystem
func InitSimple(ctx context.Context) error {
initMutex.Lock()
defer initMutex.Unlock()

return initSimpleInternal(ctx)
}

// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string {
if setting.RepoRootPath == "" {
Expand All @@ -153,11 +141,9 @@ func HomeDir() string {
return setting.RepoRootPath
}

func initSimpleInternal(ctx context.Context) error {
// at the moment, when running integration tests, the git.InitXxx would be called twice.
// one is called by the GlobalInitInstalled, one is called by TestMain.
// so the init functions should be protected by a mutex to avoid Golang's data race error.

// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
// This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race
func InitSimple(ctx context.Context) error {
DefaultContext = ctx

if setting.Git.Timeout.Default > 0 {
Expand All @@ -174,33 +160,45 @@ func initSimpleInternal(ctx context.Context) error {
return nil
}

// InitWithConfigSync initializes git module. This method may create directories or write files into filesystem
func InitWithConfigSync(ctx context.Context) error {
initMutex.Lock()
defer initMutex.Unlock()
var initOnce sync.Once

err := initSimpleInternal(ctx)
if err != nil {
return err
}
// InitOnceWithSync initializes git module with version check and change global variables, sync gitconfig.
// This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too),
// otherwise there will be data-race problem at the moment.
func InitOnceWithSync(ctx context.Context) (err error) {
initOnce.Do(func() {
err = InitSimple(ctx)
if err != nil {
return
}

if err = os.MkdirAll(setting.RepoRootPath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err)
}
// Since git wire protocol has been released from git v2.18
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
}

// By default partial clones are disabled, enable them from git v2.22
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
}

if CheckGitVersionAtLeast("2.9") == nil {
// Explicitly disable credential helper, otherwise Git credentials might leak
globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
}
if CheckGitVersionAtLeast("2.9") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
}

// Since git wire protocol has been released from git v2.18
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil
})
if err != nil {
return err
}
return syncGitConfig()
}

// By default partial clones are disabled, enable them from git v2.22
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig() (err error) {
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err)
}

// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
Expand Down Expand Up @@ -235,17 +233,15 @@ func InitWithConfigSync(ctx context.Context) error {
}
}

if CheckGitVersionAtLeast("2.29") == nil {
if SupportProcReceive {
// set support for AGit flow
if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
return err
}
SupportProcReceive = true
} else {
if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
return err
}
SupportProcReceive = false
}

if runtime.GOOS == "windows" {
Expand Down
2 changes: 1 addition & 1 deletion modules/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func testRun(m *testing.M) error {
defer util.RemoveAll(repoRootPath)
setting.RepoRootPath = repoRootPath

if err = InitWithConfigSync(context.Background()); err != nil {
if err = InitOnceWithSync(context.Background()); err != nil {
return fmt.Errorf("failed to call Init: %w", err)
}

Expand Down
5 changes: 0 additions & 5 deletions modules/indexer/stats/indexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"

Expand All @@ -30,10 +29,6 @@ func TestMain(m *testing.M) {
}

func TestRepoStatsIndex(t *testing.T) {
if err := git.InitWithConfigSync(context.Background()); !assert.NoError(t, err) {
return
}

assert.NoError(t, unittest.PrepareTestDatabase())
setting.Cfg = ini.Empty()

Expand Down
2 changes: 1 addition & 1 deletion routers/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func GlobalInitInstalled(ctx context.Context) {
log.Fatal("Gitea is not installed")
}

mustInitCtx(ctx, git.InitWithConfigSync)
mustInitCtx(ctx, git.InitOnceWithSync)
log.Info("Git Version: %s (home: %s)", git.VersionInfo(), git.HomeDir())

git.CheckLFSVersion()
Expand Down