diff --git a/models/repo.go b/models/repo.go index 7944149a7..860c5c481 100644 --- a/models/repo.go +++ b/models/repo.go @@ -146,6 +146,7 @@ const ( RepositoryReady RepositoryStatus = iota // a normal repository RepositoryBeingMigrated // repository is migrating RepositoryPendingTransfer // repository pending in ownership transfer state + RepositoryBroken // repository is in a permanently broken state ) // TrustModelType defines the types of trust model for this repository @@ -289,6 +290,11 @@ func (repo *Repository) IsBeingCreated() bool { return repo.IsBeingMigrated() } +// IsBroken indicates that repository is broken +func (repo *Repository) IsBroken() bool { + return repo.Status == RepositoryBroken +} + // AfterLoad is invoked from XORM after setting the values of all fields of this object. func (repo *Repository) AfterLoad() { // FIXME: use models migration to solve all at once. diff --git a/modules/context/repo.go b/modules/context/repo.go index c96d34f2f..1f1a03526 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -522,14 +522,30 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { } } + isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || ctx.Link == ctx.Repo.RepoLink+"/settings" || strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/") + // Disable everything when the repo is being created - if ctx.Repo.Repository.IsBeingCreated() { + if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() { ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch + if !isHomeOrSettings { + ctx.Redirect(ctx.Repo.RepoLink) + } return } gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName)) 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.RepoPath(), err) + ctx.Repo.Repository.Status = models.RepositoryBroken + ctx.Repo.Repository.IsEmpty = true + ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch + // Only allow access to base of repo or settings + if !isHomeOrSettings { + ctx.Redirect(ctx.Repo.RepoLink) + } + return + } ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err) return } @@ -551,6 +567,17 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { tags, err := ctx.Repo.GitRepo.GetTags(0, 0) if err != nil { + if strings.Contains(err.Error(), "fatal: not a git repository ") { + log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err) + ctx.Repo.Repository.Status = models.RepositoryBroken + ctx.Repo.Repository.IsEmpty = true + ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch + // Only allow access to base of repo or settings + if !isHomeOrSettings { + ctx.Redirect(ctx.Repo.RepoLink) + } + return + } ctx.ServerError("GetTags", err) return } @@ -919,6 +946,11 @@ func UnitTypes() func(ctx *Context) { // IssueTemplatesFromDefaultBranch checks for issue templates in the repo's default branch func (ctx *Context) IssueTemplatesFromDefaultBranch() []api.IssueTemplate { var issueTemplates []api.IssueTemplate + + if ctx.Repo.Repository.IsEmpty { + return issueTemplates + } + if ctx.Repo.Commit == nil { var err error ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6e39b4b03..ca6cd6662 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -946,6 +946,7 @@ clone_this_repo = Clone this repository create_new_repo_command = Creating a new repository on the command line push_exist_repo = Pushing an existing repository from the command line empty_message = This repository does not contain any content. +broken_message = The git data underlying this repository cannot be read. Contact the administrator of this instance or delete this repository. code = Code code.desc = Access source code, files, commits and branches. diff --git a/routers/private/serv.go b/routers/private/serv.go index a7ef980d2..329d80476 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -162,6 +162,14 @@ func ServCommand(ctx *context.PrivateContext) { return } + if repo.IsBroken() { + ctx.JSON(http.StatusInternalServerError, private.ErrServCommand{ + Results: results, + Err: "Repository is in a broken state", + }) + return + } + // We can shortcut at this point if the repo is a mirror if mode > models.AccessModeRead && repo.IsMirror { ctx.JSON(http.StatusForbidden, private.ErrServCommand{ diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl index 20563f59e..6da9e28e1 100644 --- a/templates/repo/empty.tmpl +++ b/templates/repo/empty.tmpl @@ -10,7 +10,11 @@ {{.i18n.Tr "repo.archive.title"}} {{end}} - {{if .CanWriteCode}} + {{if .Repository.IsBroken}} +