More pleasantly handle broken or missing git repositories (#17747)
* More pleasantly handle broken or missing git repositories In #17742 it was noted that there a completely invalid git repository underlying a repo on gitea.com. This happened due to a problem during a migration however, it is not beyond the realms of possibility that a corruption could occur to another user. This PR adds a check to RepoAssignment that will detect if a repository loading has failed due to an absent git repository. It will then show a page suggesting the user contacts the administrator or deletes the repository. Fix #17742 Signed-off-by: Andrew Thornton <art27@cantab.net> * Update options/locale/locale_en-US.ini Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
		
					parent
					
						
							
								baed01f247
							
						
					
				
			
			
				commit
				
					
						1dbc58f742
					
				
			
		
					 6 changed files with 63 additions and 4 deletions
				
			
		| 
						 | 
					@ -146,6 +146,7 @@ const (
 | 
				
			||||||
	RepositoryReady           RepositoryStatus = iota // a normal repository
 | 
						RepositoryReady           RepositoryStatus = iota // a normal repository
 | 
				
			||||||
	RepositoryBeingMigrated                           // repository is migrating
 | 
						RepositoryBeingMigrated                           // repository is migrating
 | 
				
			||||||
	RepositoryPendingTransfer                         // repository pending in ownership transfer state
 | 
						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
 | 
					// TrustModelType defines the types of trust model for this repository
 | 
				
			||||||
| 
						 | 
					@ -289,6 +290,11 @@ func (repo *Repository) IsBeingCreated() bool {
 | 
				
			||||||
	return repo.IsBeingMigrated()
 | 
						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.
 | 
					// AfterLoad is invoked from XORM after setting the values of all fields of this object.
 | 
				
			||||||
func (repo *Repository) AfterLoad() {
 | 
					func (repo *Repository) AfterLoad() {
 | 
				
			||||||
	// FIXME: use models migration to solve all at once.
 | 
						// FIXME: use models migration to solve all at once.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
						// 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
 | 
							ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
 | 
							if !isHomeOrSettings {
 | 
				
			||||||
 | 
								ctx.Redirect(ctx.Repo.RepoLink)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
 | 
						gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
 | 
				
			||||||
	if err != nil {
 | 
						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)
 | 
							ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -551,6 +567,17 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags, err := ctx.Repo.GitRepo.GetTags(0, 0)
 | 
						tags, err := ctx.Repo.GitRepo.GetTags(0, 0)
 | 
				
			||||||
	if err != nil {
 | 
						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)
 | 
							ctx.ServerError("GetTags", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -919,6 +946,11 @@ func UnitTypes() func(ctx *Context) {
 | 
				
			||||||
// IssueTemplatesFromDefaultBranch checks for issue templates in the repo's default branch
 | 
					// IssueTemplatesFromDefaultBranch checks for issue templates in the repo's default branch
 | 
				
			||||||
func (ctx *Context) IssueTemplatesFromDefaultBranch() []api.IssueTemplate {
 | 
					func (ctx *Context) IssueTemplatesFromDefaultBranch() []api.IssueTemplate {
 | 
				
			||||||
	var issueTemplates []api.IssueTemplate
 | 
						var issueTemplates []api.IssueTemplate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.Repo.Repository.IsEmpty {
 | 
				
			||||||
 | 
							return issueTemplates
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.Repo.Commit == nil {
 | 
						if ctx.Repo.Commit == nil {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
 | 
							ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -946,6 +946,7 @@ clone_this_repo = Clone this repository
 | 
				
			||||||
create_new_repo_command = Creating a new repository on the command line
 | 
					create_new_repo_command = Creating a new repository on the command line
 | 
				
			||||||
push_exist_repo = Pushing an existing repository from the command line
 | 
					push_exist_repo = Pushing an existing repository from the command line
 | 
				
			||||||
empty_message = This repository does not contain any content.
 | 
					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 = Code
 | 
				
			||||||
code.desc = Access source code, files, commits and branches.
 | 
					code.desc = Access source code, files, commits and branches.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -162,6 +162,14 @@ func ServCommand(ctx *context.PrivateContext) {
 | 
				
			||||||
			return
 | 
								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
 | 
							// We can shortcut at this point if the repo is a mirror
 | 
				
			||||||
		if mode > models.AccessModeRead && repo.IsMirror {
 | 
							if mode > models.AccessModeRead && repo.IsMirror {
 | 
				
			||||||
			ctx.JSON(http.StatusForbidden, private.ErrServCommand{
 | 
								ctx.JSON(http.StatusForbidden, private.ErrServCommand{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,11 @@
 | 
				
			||||||
						{{.i18n.Tr "repo.archive.title"}}
 | 
											{{.i18n.Tr "repo.archive.title"}}
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
				{{if .CanWriteCode}}
 | 
									{{if .Repository.IsBroken}}
 | 
				
			||||||
 | 
											<div class="ui segment center">
 | 
				
			||||||
 | 
												{{.i18n.Tr "repo.broken_message"}}
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
									{{else if .CanWriteCode}}
 | 
				
			||||||
					<h4 class="ui top attached header">
 | 
										<h4 class="ui top attached header">
 | 
				
			||||||
						{{.i18n.Tr "repo.quick_guide"}}
 | 
											{{.i18n.Tr "repo.quick_guide"}}
 | 
				
			||||||
					</h4>
 | 
										</h4>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,7 @@
 | 
				
			||||||
				{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullName}}</a></div>{{end}}
 | 
									{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullName}}</a></div>{{end}}
 | 
				
			||||||
				{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{.TemplateRepo.FullName}}</a></div>{{end}}
 | 
									{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{.TemplateRepo.FullName}}</a></div>{{end}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			{{if not .IsBeingCreated}}
 | 
								{{if not (or .IsBeingCreated .IsBroken)}}
 | 
				
			||||||
				<div class="repo-buttons">
 | 
									<div class="repo-buttons">
 | 
				
			||||||
					{{if $.RepoTransfer}}
 | 
										{{if $.RepoTransfer}}
 | 
				
			||||||
						<form method="post" action="{{$.RepoLink}}/action/accept_transfer?redirect_to={{$.RepoLink}}">
 | 
											<form method="post" action="{{$.RepoLink}}/action/accept_transfer?redirect_to={{$.RepoLink}}">
 | 
				
			||||||
| 
						 | 
					@ -100,7 +100,7 @@
 | 
				
			||||||
	</div><!-- end container -->
 | 
						</div><!-- end container -->
 | 
				
			||||||
{{end}}
 | 
					{{end}}
 | 
				
			||||||
	<div class="ui tabs container">
 | 
						<div class="ui tabs container">
 | 
				
			||||||
		{{if not .Repository.IsBeingCreated}}
 | 
							{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
 | 
				
			||||||
			<div class="ui tabular stackable menu navbar">
 | 
								<div class="ui tabular stackable menu navbar">
 | 
				
			||||||
				{{if .Permission.CanRead $.UnitTypeCode}}
 | 
									{{if .Permission.CanRead $.UnitTypeCode}}
 | 
				
			||||||
				<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL}}{{end}}">
 | 
									<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL}}{{end}}">
 | 
				
			||||||
| 
						 | 
					@ -172,6 +172,14 @@
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
							{{else if .Permission.IsAdmin}}
 | 
				
			||||||
 | 
								<div class="ui tabular stackable menu navbar">
 | 
				
			||||||
 | 
									<div class="right menu">
 | 
				
			||||||
 | 
										<a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings">
 | 
				
			||||||
 | 
											{{svg "octicon-tools"}} {{.i18n.Tr "repo.settings"}}
 | 
				
			||||||
 | 
										</a>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="ui tabs divider"></div>
 | 
						<div class="ui tabs divider"></div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue