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

Allow adding new files to an empty repo #24164

Merged
merged 8 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions models/fixtures/repo_unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,9 @@
repo_id: 56
type: 1
created_unix: 946684810

-
id: 85
repo_id: 52
type: 1
created_unix: 946684810
1 change: 1 addition & 0 deletions models/fixtures/repository.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,7 @@
owner_name: user30
lower_name: empty
name: empty
default_branch: master
num_watches: 0
num_stars: 0
num_forks: 0
Expand Down
4 changes: 2 additions & 2 deletions models/fixtures/user.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1091,11 +1091,11 @@
max_repo_creation: -1
is_active: true
is_admin: false
is_restricted: true
is_restricted: false
allow_git_hook: false
allow_import_local: false
allow_create_organization: true
prohibit_login: true
prohibit_login: false
avatar: avatar29
avatar_email: user30@example.com
use_custom_avatar: false
Expand Down
8 changes: 7 additions & 1 deletion models/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ func (repo *Repository) IsBroken() bool {
return repo.Status == RepositoryBroken
}

// MarkAsBrokenEmpty marks the repo as broken and empty
func (repo *Repository) MarkAsBrokenEmpty() {
repo.Status = RepositoryBroken
repo.IsEmpty = true
}

// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (repo *Repository) AfterLoad() {
repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
Expand Down Expand Up @@ -729,7 +735,7 @@ func IsRepositoryExist(ctx context.Context, u *user_model.User, repoName string)
return false, err
}
isDir, err := util.IsDir(RepoPath(u.Name, repoName))
return has && isDir, err
return has || isDir, err
}

// GetTemplateRepo populates repo.TemplateRepo for a generated repository and
Expand Down
2 changes: 1 addition & 1 deletion modules/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {

// it's safe to show internal error to admin users, and it helps
if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
ctx.Data["ErrorMsg"] = logErr
ctx.Data["ErrorMsg"] = fmt.Sprintf("%s, %s", logMsg, logErr)
}
}

Expand Down
37 changes: 23 additions & 14 deletions modules/context/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ func (r *Repository) CanCreateIssueDependencies(user *user_model.User, isPull bo

// GetCommitsCount returns cached commit count for current view
func (r *Repository) GetCommitsCount() (int64, error) {
if r.Commit == nil {
return 0, nil
}
var contextName string
if r.IsViewBranch {
contextName = r.BranchName
Expand Down Expand Up @@ -642,8 +645,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
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 = repo_model.RepositoryBroken
ctx.Repo.Repository.IsEmpty = true
ctx.Repo.Repository.MarkAsBrokenEmpty()
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
// Only allow access to base of repo or settings
if !isHomeOrSettings {
Expand Down Expand Up @@ -689,7 +691,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
ctx.Data["BranchesCount"] = len(brs)

// If not branch selected, try default one.
// If default branch doesn't exists, fall back to some other branch.
// If default branch doesn't exist, fall back to some other branch.
if len(ctx.Repo.BranchName) == 0 {
if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
Expand Down Expand Up @@ -878,6 +880,10 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return func(ctx *Context) (cancel context.CancelFunc) {
// Empty repository does not have reference information.
if ctx.Repo.Repository.IsEmpty {
// assume the user is viewing the (non-existent) default branch
ctx.Repo.IsViewBranch = true
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
ctx.Data["TreePath"] = ctx.Repo.BranchName
return
}

Expand Down Expand Up @@ -907,27 +913,30 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
refName = ctx.Repo.Repository.DefaultBranch
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 0)
if err != nil {
ctx.ServerError("GetBranches", err)
return
if err == nil && len(brs) != 0 {
refName = brs[0]
} else if len(brs) == 0 {
err = fmt.Errorf("No branches in non-empty repository %s",
ctx.Repo.GitRepo.Path)
ctx.ServerError("GetBranches", err)
return
log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
ctx.Repo.Repository.MarkAsBrokenEmpty()
} else {
log.Error("GetBranches error: %v", err)
ctx.Repo.Repository.MarkAsBrokenEmpty()
}
refName = brs[0]
}
ctx.Repo.RefName = refName
ctx.Repo.BranchName = refName
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
if err != nil {
if err == nil {
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
} else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") {
// if the repository is broken, we can continue to the handler code, to show "Settings -> Delete Repository" for end users
log.Error("GetBranchCommit: %v", err)
ctx.Repo.Repository.MarkAsBrokenEmpty()
} else {
ctx.ServerError("GetBranchCommit", err)
return
}
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
ctx.Repo.IsViewBranch = true

} else {
refName = getRefName(ctx, refType)
ctx.Repo.RefName = refName
Expand Down
16 changes: 12 additions & 4 deletions modules/git/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,18 @@ type RunOpts struct {
Env []string
Timeout time.Duration
UseContextTimeout bool
Dir string
Stdout, Stderr io.Writer
Stdin io.Reader
PipelineFunc func(context.Context, context.CancelFunc) error

// Dir is the working dir for the git command, however:
// FIXME: this could be incorrect in many cases, for example:
// * /some/path/.git
// * /some/path/.git/gitea-data/data/repositories/user/repo.git
// If "user/repo.git" is invalid/broken, then running git command in it will use "/some/path/.git", and produce unexpected results
// The correct approach is to use `--git-dir" global argument
Dir string

Stdout, Stderr io.Writer
Stdin io.Reader
PipelineFunc func(context.Context, context.CancelFunc) error
}

func commonBaseEnvs() []string {
Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func InitRepository(ctx context.Context, repoPath string, bare bool) error {
// IsEmpty Check if repository is empty.
func (repo *Repository) IsEmpty() (bool, error) {
var errbuf, output strings.Builder
if err := NewCommand(repo.Ctx, "show-ref", "--head", "^HEAD$").
if err := NewCommand(repo.Ctx).AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("show-ref", "--head", "^HEAD$").
Run(&RunOpts{
Dir: repo.Path,
Stdout: &output,
Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo_base_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) {
}

repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath)
repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repo.Path)
repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repoPath)

return repo, nil
}
Expand Down
4 changes: 3 additions & 1 deletion modules/indexer/code/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ func Init() {
log.Trace("IndexerData Process Repo: %d", indexerData.RepoID)

if err := index(ctx, indexer, indexerData.RepoID); err != nil {
log.Error("index: %v", err)
if !setting.IsInTesting {
log.Error("indexer index error for repo %v: %v", indexerData.RepoID, err)
}
if indexer.Ping() {
continue
}
Expand Down
7 changes: 5 additions & 2 deletions modules/indexer/stats/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
)

// DBIndexer implements Indexer interface to use database's like search
Expand Down Expand Up @@ -46,7 +47,7 @@ func (db *DBIndexer) Index(id int64) error {
// Get latest commit for default branch
commitID, err := gitRepo.GetBranchCommitID(repo.DefaultBranch)
if err != nil {
if git.IsErrBranchNotExist(err) || git.IsErrNotExist(err) {
if git.IsErrBranchNotExist(err) || git.IsErrNotExist(err) || setting.IsInTesting {
log.Debug("Unable to get commit ID for default branch %s in %s ... skipping this repository", repo.DefaultBranch, repo.RepoPath())
return nil
}
Expand All @@ -62,7 +63,9 @@ func (db *DBIndexer) Index(id int64) error {
// Calculate and save language statistics to database
stats, err := gitRepo.GetLanguageStats(commitID)
if err != nil {
log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err)
if !setting.IsInTesting {
log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err)
}
return err
}
err = repo_model.UpdateLanguageStats(repo, commitID, stats)
Expand Down
5 changes: 4 additions & 1 deletion modules/indexer/stats/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
)

// statsQueue represents a queue to handle repository stats updates
Expand All @@ -20,7 +21,9 @@ func handle(data ...queue.Data) []queue.Data {
for _, datum := range data {
opts := datum.(int64)
if err := indexer.Index(opts); err != nil {
log.Error("stats queue indexer.Index(%d) failed: %v", opts, err)
if !setting.IsInTesting {
log.Error("stats queue indexer.Index(%d) failed: %v", opts, err)
}
}
}
return nil
Expand Down
10 changes: 8 additions & 2 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
var (
// AppVer is the version of the current build of Gitea. It is set in main.go from main.Version.
AppVer string
// AppBuiltWith represents a human readable version go runtime build version and build tags. (See main.go formatBuiltWith().)
// AppBuiltWith represents a human-readable version go runtime build version and build tags. (See main.go formatBuiltWith().)
AppBuiltWith string
// AppStartTime store time gitea has started
AppStartTime time.Time
Expand All @@ -40,14 +40,19 @@ var (
// AppWorkPath is used as the base path for several other paths.
AppWorkPath string

// Global setting objects
// Other global setting objects

CfgProvider ConfigProvider
CustomPath string // Custom directory path
CustomConf string
RunMode string
RunUser string
IsProd bool
IsWindows bool

// IsInTesting indicates whether the testing is running. A lot of unreliable code causes a lot of nonsense error logs during testing
// TODO: this is only a temporary solution, we should make the test code more reliable
IsInTesting = false
)

func getAppPath() (string, error) {
Expand Down Expand Up @@ -117,6 +122,7 @@ func init() {
log.Fatal("Failed to get app path: %v", err)
}
AppWorkPath = getWorkPath(AppPath)
AppVer = "dev"
}

func forcePathSeparator(path string) {
Expand Down
2 changes: 1 addition & 1 deletion routers/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ func GlobalInitInstalled(ctx context.Context) {
}

mustInitCtx(ctx, git.InitFull)
log.Info("Gitea Version: %s%s", setting.AppVer, setting.AppBuiltWith)
log.Info("Git Version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf)
log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode))
log.Info("Gitea v%s%s", setting.AppVer, setting.AppBuiltWith)

// Setup i18n
translation.InitLocales(ctx)
Expand Down
6 changes: 5 additions & 1 deletion routers/web/repo/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
}

// Check if the filename (and additional path) is specified in the querystring
// (filename is a misnomer, but kept for compatibility with Github)
// (filename is a misnomer, but kept for compatibility with GitHub)
filePath, fileName := path.Split(ctx.Req.URL.Query().Get("filename"))
filePath = strings.Trim(filePath, "/")
treeNames, treePaths := getParentTreeFields(path.Join(ctx.Repo.TreePath, filePath))
Expand Down Expand Up @@ -327,6 +327,10 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
}
}

if ctx.Repo.Repository.IsEmpty {
_ = repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, IsEmpty: false}, "is_empty")
}

if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) {
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName))
} else {
Expand Down
41 changes: 19 additions & 22 deletions routers/web/repo/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,6 @@ func renderDirectory(ctx *context.Context, treeLink string) {
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
}

// Check permission to add or upload new file.
if ctx.Repo.CanWrite(unit_model.TypeCode) && ctx.Repo.IsViewBranch {
ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchived
ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived
}

if ctx.Written() {
return
}

subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)
if err != nil {
ctx.ServerError("findReadmeFileInEntries", err)
Expand Down Expand Up @@ -868,21 +858,25 @@ func renderRepoTopics(ctx *context.Context) {

func renderCode(ctx *context.Context) {
ctx.Data["PageIsViewCode"] = true
ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled

if ctx.Repo.Repository.IsEmpty {
reallyEmpty := true
if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() {
showEmpty := true
var err error
if ctx.Repo.GitRepo != nil {
reallyEmpty, err = ctx.Repo.GitRepo.IsEmpty()
showEmpty, err = ctx.Repo.GitRepo.IsEmpty()
if err != nil {
ctx.ServerError("GitRepo.IsEmpty", err)
return
log.Error("GitRepo.IsEmpty: %v", err)
ctx.Repo.Repository.Status = repo_model.RepositoryBroken
showEmpty = true
ctx.Flash.Error(ctx.Tr("error.occurred"), true)
}
}
if reallyEmpty {
if showEmpty {
ctx.HTML(http.StatusOK, tplRepoEMPTY)
return
}

// the repo is not really empty, so we should update the modal in database
// such problem may be caused by:
// 1) an error occurs during pushing/receiving. 2) the user replaces an empty git repo manually
Expand All @@ -898,6 +892,14 @@ func renderCode(ctx *context.Context) {
ctx.ServerError("UpdateRepoSize", err)
return
}

// the repo's IsEmpty has been updated, redirect to this page to make sure middlewares can get the correct values
link := ctx.Link
if ctx.Req.URL.RawQuery != "" {
link += "?" + ctx.Req.URL.RawQuery
}
ctx.Redirect(link)
return
}

title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name
Expand Down Expand Up @@ -927,12 +929,7 @@ func renderCode(ctx *context.Context) {
return
}

if !ctx.Repo.Repository.IsEmpty {
checkCitationFile(ctx, entry)
if ctx.Written() {
return
}
}
checkCitationFile(ctx, entry)

renderLanguageStats(ctx)
if ctx.Written() {
Expand Down
Loading