diff --git a/modules/context/context.go b/modules/context/context.go index ef6c19ed125c3..4c828e9aa2e1b 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -47,6 +47,15 @@ type Context struct { Org *Organization } +// Close Releases resources used by this context, like file descriptors +func (ctx *Context) Close() { + if ctx.Repo != nil && ctx.Repo.GitRepo != nil { + if err := ctx.Repo.GitRepo.Close(); err != nil { + log.Warn("Unable to clean up repository resources %s", err.Error()) + } + } +} + // IsUserSiteAdmin returns true if current user is a site admin func (ctx *Context) IsUserSiteAdmin() bool { return ctx.IsSigned && ctx.User.IsAdmin @@ -343,3 +352,12 @@ func Contexter() macaron.Handler { c.Map(ctx) } } + +// Cleanup Cleans up used resources like open file descriptors at the end of the request +func Cleanup() macaron.Handler { + return func(ctx *Context) { + defer ctx.Close() + + ctx.Next() + } +} diff --git a/modules/git/repo.go b/modules/git/repo.go index 4c6690b9133e5..24c4af1d13597 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -122,6 +122,11 @@ func OpenRepository(repoPath string) (*Repository, error) { }, nil } +// Close Release file descriptors that are left open for performance reasons +func (repo *Repository) Close() error { + return repo.gogitStorage.Close() +} + // GoGitRepo gets the go-git repo representation func (repo *Repository) GoGitRepo() *gogit.Repository { return repo.gogitRepo diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 95cba3cbf2571..bfc70cf04acfc 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -73,6 +73,13 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { // Check if repository name has been changed. if repo.LowerName != strings.ToLower(newRepoName) { isNameChanged = true + + // Close any file descriptors, primarily for Windows which refuses to move directories with open descriptors + if err := ctx.Repo.GitRepo.Close(); err != nil { + ctx.ServerError("ChangeRepositoryName", err) + return + } + if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil { ctx.Data["Err_RepoName"] = true switch { @@ -377,6 +384,12 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } + // Close any file descriptors, primarily for Windows which refuses to move directories with open descriptors + if err := ctx.Repo.GitRepo.Close(); err != nil { + ctx.ServerError("ChangeRepositoryName", err) + return + } + oldOwnerID := ctx.Repo.Owner.ID if err = models.TransferOwnership(ctx.User, newOwner, repo); err != nil { if models.IsErrRepoAlreadyExist(err) { diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 13a5bb27084d0..9e63aa6b83e32 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -226,6 +226,8 @@ func NewMacaron() *macaron.Macaron { // OK we are now set-up enough to allow us to create a nicer recovery than // the default macaron recovery m.Use(context.Recovery()) + + m.Use(context.Cleanup()) m.SetAutoHead(true) return m }