diff --git a/models/repo/archiver.go b/models/repo/archiver.go index 14ffa1d89b120..5a3eac9f148c0 100644 --- a/models/repo/archiver.go +++ b/models/repo/archiver.go @@ -56,16 +56,11 @@ func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) { if err != nil { return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} } - nameExts := strings.SplitN(parts[2], ".", 2) - if len(nameExts) != 2 { + commitID, archiveType := git.SplitArchiveNameType(parts[2]) + if archiveType == git.ArchiveUnknown { return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} } - - return &RepoArchiver{ - RepoID: repoID, - CommitID: parts[1] + nameExts[0], - Type: git.ToArchiveType(nameExts[1]), - }, nil + return &RepoArchiver{RepoID: repoID, CommitID: commitID, Type: archiveType}, nil } // GetRepoArchiver get an archiver diff --git a/modules/git/ref.go b/modules/git/ref.go index aab4c5d77d75c..051b75a15a1d5 100644 --- a/modules/git/ref.go +++ b/modules/git/ref.go @@ -80,6 +80,10 @@ func RefNameFromTag(shortName string) RefName { return RefName(TagPrefix + shortName) } +func RefNameFromCommit(shortName string) RefName { + return RefName(shortName) +} + func (ref RefName) String() string { return string(ref) } diff --git a/modules/git/repo_archive.go b/modules/git/repo_archive.go index 2b45a50f191b6..92f3e88f7cdcb 100644 --- a/modules/git/repo_archive.go +++ b/modules/git/repo_archive.go @@ -16,37 +16,35 @@ import ( type ArchiveType int const ( - // ZIP zip archive type - ZIP ArchiveType = iota + 1 - // TARGZ tar gz archive type - TARGZ - // BUNDLE bundle archive type - BUNDLE + ArchiveUnknown ArchiveType = iota + ArchiveZip // 1 + ArchiveTarGz // 2 + ArchiveBundle // 3 ) -// String converts an ArchiveType to string +// String converts an ArchiveType to string: the extension of the archive file without prefix dot func (a ArchiveType) String() string { switch a { - case ZIP: + case ArchiveZip: return "zip" - case TARGZ: + case ArchiveTarGz: return "tar.gz" - case BUNDLE: + case ArchiveBundle: return "bundle" } return "unknown" } -func ToArchiveType(s string) ArchiveType { - switch s { - case "zip": - return ZIP - case "tar.gz": - return TARGZ - case "bundle": - return BUNDLE +func SplitArchiveNameType(s string) (string, ArchiveType) { + switch { + case strings.HasSuffix(s, ".zip"): + return strings.TrimSuffix(s, ".zip"), ArchiveZip + case strings.HasSuffix(s, ".tar.gz"): + return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz + case strings.HasSuffix(s, ".bundle"): + return strings.TrimSuffix(s, ".bundle"), ArchiveBundle } - return 0 + return s, ArchiveUnknown } // CreateArchive create archive content to the target path diff --git a/modules/git/repo_archive_test.go b/modules/git/repo_archive_test.go new file mode 100644 index 0000000000000..c9b6b26baae15 --- /dev/null +++ b/modules/git/repo_archive_test.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestArchiveType(t *testing.T) { + name, archiveType := SplitArchiveNameType("test.tar.gz") + assert.Equal(t, "test", name) + assert.Equal(t, "tar.gz", archiveType.String()) + + name, archiveType = SplitArchiveNameType("a/b/test.zip") + assert.Equal(t, "a/b/test", name) + assert.Equal(t, "zip", archiveType.String()) + + name, archiveType = SplitArchiveNameType("1234.bundle") + assert.Equal(t, "1234", name) + assert.Equal(t, "bundle", archiveType.String()) + + name, archiveType = SplitArchiveNameType("test") + assert.Equal(t, "test", name) + assert.Equal(t, "unknown", archiveType.String()) + + name, archiveType = SplitArchiveNameType("test.xz") + assert.Equal(t, "test.xz", name) + assert.Equal(t, "unknown", archiveType.String()) +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9d240ac8978a9..93155caa109f4 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1115,9 +1115,7 @@ blame.ignore_revs = Ignoring revisions in .git-blame-ignore-revs.git-blame-ignore-revs. user_search_tooltip = Shows a maximum of 30 users -tree_path_not_found_commit = Path %[1]s doesn't exist in commit %[2]s -tree_path_not_found_branch = Path %[1]s doesn't exist in branch %[2]s -tree_path_not_found_tag = Path %[1]s doesn't exist in tag %[2]s +tree_path_not_found = Path %[1]s doesn't exist in %[2]s transfer.accept = Accept Transfer transfer.accept_desc = Transfer to "%s" diff --git a/routers/api/v1/repo/download.go b/routers/api/v1/repo/download.go index a8a23c4a8d425..e6296c9fe7324 100644 --- a/routers/api/v1/repo/download.go +++ b/routers/api/v1/repo/download.go @@ -17,11 +17,11 @@ func DownloadArchive(ctx *context.APIContext) { var tp git.ArchiveType switch ballType := ctx.PathParam("ball_type"); ballType { case "tarball": - tp = git.TARGZ + tp = git.ArchiveTarGz case "zipball": - tp = git.ZIP + tp = git.ArchiveZip case "bundle": - tp = git.BUNDLE + tp = git.ArchiveBundle default: ctx.Error(http.StatusBadRequest, "", fmt.Sprintf("Unknown archive type: %s", ballType)) return @@ -36,7 +36,7 @@ func DownloadArchive(ctx *context.APIContext) { } } - r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"), tp) + r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")+"."+tp.String()) if err != nil { ctx.ServerError("NewRequest", err) return diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 7c7f53a56520d..3eefd2ae29253 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -293,14 +293,7 @@ func GetArchive(ctx *context.APIContext) { } func archiveDownload(ctx *context.APIContext) { - uri := ctx.PathParam("*") - ext, tp, err := archiver_service.ParseFileName(uri) - if err != nil { - ctx.Error(http.StatusBadRequest, "ParseFileName", err) - return - } - - aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp) + aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")) if err != nil { if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { ctx.Error(http.StatusBadRequest, "unknown archive format", err) diff --git a/routers/web/feed/file.go b/routers/web/feed/file.go index 973226435192f..518d995ccbcb7 100644 --- a/routers/web/feed/file.go +++ b/routers/web/feed/file.go @@ -24,7 +24,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string } commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange( git.CommitsByFileAndRangeOptions{ - Revision: ctx.Repo.RefName, + Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName File: fileName, Page: 1, }) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 638b5e680aaa4..b5498ccd2c901 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -222,7 +222,7 @@ func FileHistory(ctx *context.Context) { return } - commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefName, fileName) + commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), fileName) // FIXME: legacy code used ShortName if err != nil { ctx.ServerError("FileCommitsCount", err) return @@ -238,7 +238,7 @@ func FileHistory(ctx *context.Context) { commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange( git.CommitsByFileAndRangeOptions{ - Revision: ctx.Repo.RefName, + Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName File: fileName, Page: page, }) diff --git a/routers/web/repo/helper.go b/routers/web/repo/helper.go index ed6216fa5ce2a..3bf064c1e386d 100644 --- a/routers/web/repo/helper.go +++ b/routers/web/repo/helper.go @@ -4,25 +4,14 @@ package repo import ( - "net/url" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/services/context" ) func HandleGitError(ctx *context.Context, msg string, err error) { if git.IsErrNotExist(err) { - refType := "" - switch { - case ctx.Repo.IsViewBranch: - refType = "branch" - case ctx.Repo.IsViewTag: - refType = "tag" - case ctx.Repo.IsViewCommit: - refType = "commit" - } - ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found_"+refType, ctx.Repo.TreePath, url.PathEscape(ctx.Repo.RefName)) - ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + refType + "/" + url.PathEscape(ctx.Repo.RefName) + ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found", ctx.Repo.TreePath, ctx.Repo.RefTypeNameSubURL()) + ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL() ctx.NotFound(msg, err) } else { ctx.ServerError(msg, err) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 0f408b22e096d..e5c397ec5415e 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -463,13 +463,7 @@ func RedirectDownload(ctx *context.Context) { // Download an archive of a repository func Download(ctx *context.Context) { - uri := ctx.PathParam("*") - ext, tp, err := archiver_service.ParseFileName(uri) - if err != nil { - ctx.ServerError("ParseFileName", err) - return - } - aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp) + aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")) if err != nil { if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { ctx.Error(http.StatusBadRequest, err.Error()) @@ -527,15 +521,9 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep // a request that's already in-progress, but the archiver service will just // kind of drop it on the floor if this is the case. func InitiateDownload(ctx *context.Context) { - uri := ctx.PathParam("*") - ext, tp, err := archiver_service.ParseFileName(uri) - if err != nil { - ctx.ServerError("ParseFileName", err) - return - } - aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp) + aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")) if err != nil { - ctx.ServerError("archiver_service.NewRequest", err) + ctx.Error(http.StatusBadRequest, "invalid archive request") return } if aReq == nil { diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go index f4be4783fb739..0b4381edfb057 100644 --- a/routers/web/repo/view_file.go +++ b/routers/web/repo/view_file.go @@ -42,7 +42,7 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) { } defer dataRc.Close() - ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) + ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName()) ctx.Data["FileIsSymlink"] = entry.IsLink() ctx.Data["FileName"] = blob.Name() ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go index 7aa8a72430485..622072adeb719 100644 --- a/routers/web/repo/view_home.go +++ b/routers/web/repo/view_home.go @@ -135,7 +135,7 @@ func prepareToRenderDirectory(ctx *context.Context) { if ctx.Repo.TreePath != "" { ctx.Data["HideRepoInfo"] = true - ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) + ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName()) } subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true) diff --git a/services/context/repo.go b/services/context/repo.go index 121910235f1e8..822aef90646aa 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -58,10 +58,10 @@ type Repository struct { IsViewTag bool IsViewCommit bool - RefName string - BranchName string - TagName string - TreePath string + RefFullName git.RefName + BranchName string + TagName string + TreePath string // Commit it is always set to the commit for the branch or tag Commit *git.Commit @@ -596,7 +596,6 @@ func RepoAssignment(ctx *Context) { // Disable everything when the repo is being created if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() { - ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch if !isHomeOrSettings { ctx.Redirect(ctx.Repo.RepoLink) } @@ -616,7 +615,6 @@ func RepoAssignment(ctx *Context) { 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.MarkAsBrokenEmpty() - ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch // Only allow access to base of repo or settings if !isHomeOrSettings { ctx.Redirect(ctx.Repo.RepoLink) @@ -629,7 +627,6 @@ func RepoAssignment(ctx *Context) { // Stop at this point when the repo is empty. if ctx.Repo.Repository.IsEmpty { - ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch return } @@ -655,22 +652,6 @@ func RepoAssignment(ctx *Context) { ctx.Data["BranchesCount"] = branchesTotal - // If no branch is set in the request URL, try to guess a default one. - if len(ctx.Repo.BranchName) == 0 { - if len(ctx.Repo.Repository.DefaultBranch) > 0 && ctx.Repo.GitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) { - ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch - } else { - ctx.Repo.BranchName, _ = gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository) - if ctx.Repo.BranchName == "" { - // If it still can't get a default branch, fall back to default branch from setting. - // Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug. - ctx.Repo.BranchName = setting.Repository.DefaultBranch - } - } - ctx.Repo.RefName = ctx.Repo.BranchName - } - ctx.Data["BranchName"] = ctx.Repo.BranchName - // People who have push access or have forked repository can propose a new pull request. canPush := ctx.Repo.CanWrite(unit_model.TypeCode) || (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)) @@ -844,26 +825,39 @@ type RepoRefByTypeOptions struct { IgnoreNotExistErr bool } +func repoRefFullName(shortName string, typ RepoRefType) git.RefName { + switch typ { + case RepoRefBranch: + return git.RefNameFromBranch(shortName) + case RepoRefTag: + return git.RefNameFromTag(shortName) + case RepoRefCommit: + return git.RefNameFromCommit(shortName) + default: + setting.PanicInDevOrTesting("Unknown RepoRefType: %v", typ) + return git.RefNameFromBranch("main") // just a dummy result, it shouldn't happen + } +} + // RepoRefByType handles repository reference name for a specific type // of repository reference func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) { opt := util.OptionalArg(opts) return func(ctx *Context) { + var err error refType := detectRefType // 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.Repo.RefFullName = git.RefNameFromBranch(ctx.Repo.BranchName) + // these variables are used by the template to "add/upload" new files + ctx.Data["BranchName"] = ctx.Repo.BranchName ctx.Data["TreePath"] = "" return } - var ( - refName string - err error - ) - if ctx.Repo.GitRepo == nil { ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository) if err != nil { @@ -873,22 +867,23 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func } // Get default branch. + var refShortName string reqPath := ctx.PathParam("*") if reqPath == "" { - refName = ctx.Repo.Repository.DefaultBranch - if !ctx.Repo.GitRepo.IsBranchExist(refName) { + refShortName = ctx.Repo.Repository.DefaultBranch + if !ctx.Repo.GitRepo.IsBranchExist(refShortName) { brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1) if err == nil && len(brs) != 0 { - refName = brs[0].Name + refShortName = brs[0].Name } else if len(brs) == 0 { log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path) } else { log.Error("GetBranches error: %v", err) } } - ctx.Repo.RefName = refName - ctx.Repo.BranchName = refName - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) + ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName) + ctx.Repo.BranchName = refShortName + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName) 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") { @@ -902,35 +897,37 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func } else { // there is a path in request guessLegacyPath := refType == RepoRefUnknown if guessLegacyPath { - refName, refType = getRefNameLegacy(ctx.Base, ctx.Repo, reqPath, "") + refShortName, refType = getRefNameLegacy(ctx.Base, ctx.Repo, reqPath, "") } else { - refName = getRefName(ctx.Base, ctx.Repo, reqPath, refType) + refShortName = getRefName(ctx.Base, ctx.Repo, reqPath, refType) } - ctx.Repo.RefName = refName + ctx.Repo.RefFullName = repoRefFullName(refShortName, refType) isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool) if isRenamedBranch && has { renamedBranchName := ctx.Data["RenamedBranchName"].(string) - ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName)) - link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1) + ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refShortName, renamedBranchName)) + link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refShortName), util.PathEscapeSegments(renamedBranchName), 1) ctx.Redirect(link) return } - if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refName) { + if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refShortName) { ctx.Repo.IsViewBranch = true - ctx.Repo.BranchName = refName + ctx.Repo.BranchName = refShortName + ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName) - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName) if err != nil { ctx.ServerError("GetBranchCommit", err) return } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refName) { + } else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refShortName) { ctx.Repo.IsViewTag = true - ctx.Repo.TagName = refName + ctx.Repo.RefFullName = git.RefNameFromTag(refShortName) + ctx.Repo.TagName = refShortName - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName) + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refShortName) if err != nil { if git.IsErrNotExist(err) { ctx.NotFound("GetTagCommit", err) @@ -940,25 +937,26 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func return } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) { + } else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refShortName, 7) { ctx.Repo.IsViewCommit = true - ctx.Repo.CommitID = refName + ctx.Repo.RefFullName = git.RefNameFromCommit(refShortName) + ctx.Repo.CommitID = refShortName - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName) + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refShortName) if err != nil { ctx.NotFound("GetCommit", err) return } // If short commit ID add canonical link header - if len(refName) < ctx.Repo.GetObjectFormat().FullLength() { - canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1)) + if len(refShortName) < ctx.Repo.GetObjectFormat().FullLength() { + canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refShortName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1)) ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL)) } } else { if opt.IgnoreNotExistErr { return } - ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) + ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refShortName)) return } @@ -975,15 +973,19 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func } } - ctx.Data["BranchName"] = ctx.Repo.BranchName - ctx.Data["RefName"] = ctx.Repo.RefName + ctx.Data["RefFullName"] = ctx.Repo.RefFullName ctx.Data["RefTypeNameSubURL"] = ctx.Repo.RefTypeNameSubURL() - ctx.Data["TagName"] = ctx.Repo.TagName - ctx.Data["CommitID"] = ctx.Repo.CommitID ctx.Data["TreePath"] = ctx.Repo.TreePath + ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch + ctx.Data["BranchName"] = ctx.Repo.BranchName + ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag + ctx.Data["TagName"] = ctx.Repo.TagName + ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit + ctx.Data["CommitID"] = ctx.Repo.CommitID + ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() // only used by the branch selector dropdown: AllowCreateNewRef ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go index e1addbed335cb..d39acc080d584 100644 --- a/services/repository/archiver/archiver.go +++ b/services/repository/archiver/archiver.go @@ -31,19 +31,20 @@ import ( // handle elsewhere. type ArchiveRequest struct { RepoID int64 - refName string Type git.ArchiveType CommitID string + + archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id" } // ErrUnknownArchiveFormat request archive format is not supported type ErrUnknownArchiveFormat struct { - RequestFormat string + RequestNameType string } // Error implements error func (err ErrUnknownArchiveFormat) Error() string { - return fmt.Sprintf("unknown format: %s", err.RequestFormat) + return fmt.Sprintf("unknown format: %s", err.RequestNameType) } // Is implements error @@ -54,12 +55,12 @@ func (ErrUnknownArchiveFormat) Is(err error) bool { // RepoRefNotFoundError is returned when a requested reference (commit, tag) was not found. type RepoRefNotFoundError struct { - RefName string + RefShortName string } // Error implements error. func (e RepoRefNotFoundError) Error() string { - return fmt.Sprintf("unrecognized repository reference: %s", e.RefName) + return fmt.Sprintf("unrecognized repository reference: %s", e.RefShortName) } func (e RepoRefNotFoundError) Is(err error) bool { @@ -67,43 +68,23 @@ func (e RepoRefNotFoundError) Is(err error) bool { return ok } -func ParseFileName(uri string) (ext string, tp git.ArchiveType, err error) { - switch { - case strings.HasSuffix(uri, ".zip"): - ext = ".zip" - tp = git.ZIP - case strings.HasSuffix(uri, ".tar.gz"): - ext = ".tar.gz" - tp = git.TARGZ - case strings.HasSuffix(uri, ".bundle"): - ext = ".bundle" - tp = git.BUNDLE - default: - return "", 0, ErrUnknownArchiveFormat{RequestFormat: uri} - } - return ext, tp, nil -} - // NewRequest creates an archival request, based on the URI. The // resulting ArchiveRequest is suitable for being passed to Await() // if it's determined that the request still needs to be satisfied. -func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git.ArchiveType) (*ArchiveRequest, error) { - if fileType < git.ZIP || fileType > git.BUNDLE { - return nil, ErrUnknownArchiveFormat{RequestFormat: fileType.String()} - } - - r := &ArchiveRequest{ - RepoID: repoID, - refName: refName, - Type: fileType, +func NewRequest(repoID int64, repo *git.Repository, archiveRefExt string) (*ArchiveRequest, error) { + // here the archiveRefShortName is not a clear ref, it could be a tag, branch or commit id + archiveRefShortName, archiveType := git.SplitArchiveNameType(archiveRefExt) + if archiveType == git.ArchiveUnknown { + return nil, ErrUnknownArchiveFormat{archiveRefExt} } // Get corresponding commit. - commitID, err := repo.ConvertToGitID(r.refName) + commitID, err := repo.ConvertToGitID(archiveRefShortName) if err != nil { - return nil, RepoRefNotFoundError{RefName: r.refName} + return nil, RepoRefNotFoundError{RefShortName: archiveRefShortName} } + r := &ArchiveRequest{RepoID: repoID, archiveRefShortName: archiveRefShortName, Type: archiveType} r.CommitID = commitID.String() return r, nil } @@ -111,11 +92,11 @@ func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git // GetArchiveName returns the name of the caller, based on the ref used by the // caller to create this request. func (aReq *ArchiveRequest) GetArchiveName() string { - return strings.ReplaceAll(aReq.refName, "/", "-") + "." + aReq.Type.String() + return strings.ReplaceAll(aReq.archiveRefShortName, "/", "-") + "." + aReq.Type.String() } // Await awaits the completion of an ArchiveRequest. If the archive has -// already been prepared the method returns immediately. Otherwise an archiver +// already been prepared the method returns immediately. Otherwise, an archiver // process will be started and its completion awaited. On success the returned // RepoArchiver may be used to download the archive. Note that even if the // context is cancelled/times out a started archiver will still continue to run @@ -208,8 +189,8 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver rd, w := io.Pipe() defer func() { - w.Close() - rd.Close() + _ = w.Close() + _ = rd.Close() }() done := make(chan error, 1) // Ensure that there is some capacity which will ensure that the goroutine below can always finish repo, err := repo_model.GetRepositoryByID(ctx, archiver.RepoID) @@ -230,7 +211,7 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver } }() - if archiver.Type == git.BUNDLE { + if archiver.Type == git.ArchiveBundle { err = gitRepo.CreateBundle( ctx, archiver.CommitID, diff --git a/services/repository/archiver/archiver_test.go b/services/repository/archiver/archiver_test.go index 1d0c6e513d836..522f90558a728 100644 --- a/services/repository/archiver/archiver_test.go +++ b/services/repository/archiver/archiver_test.go @@ -9,7 +9,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/services/contexttest" _ "code.gitea.io/gitea/models/actions" @@ -31,47 +30,47 @@ func TestArchive_Basic(t *testing.T) { contexttest.LoadGitRepo(t, ctx) defer ctx.Repo.GitRepo.Close() - bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) + bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) assert.NotNil(t, bogusReq) assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName()) // Check a series of bogus requests. // Step 1, valid commit with a bad extension. - bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, 100) + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".unknown") assert.Error(t, err) assert.Nil(t, bogusReq) // Step 2, missing commit. - bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff", git.ZIP) + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff.zip") assert.Error(t, err) assert.Nil(t, bogusReq) // Step 3, doesn't look like branch/tag/commit. - bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db", git.ZIP) + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db.zip") assert.Error(t, err) assert.Nil(t, bogusReq) - bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master", git.ZIP) + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master.zip") assert.NoError(t, err) assert.NotNil(t, bogusReq) assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName()) - bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive", git.ZIP) + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive.zip") assert.NoError(t, err) assert.NotNil(t, bogusReq) assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName()) // Now two valid requests, firstCommit with valid extensions. - zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) + zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) assert.NotNil(t, zipReq) - tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.TARGZ) + tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".tar.gz") assert.NoError(t, err) assert.NotNil(t, tgzReq) - secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.ZIP) + secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".bundle") assert.NoError(t, err) assert.NotNil(t, secondReq) @@ -91,7 +90,7 @@ func TestArchive_Basic(t *testing.T) { // Sleep two seconds to make sure the queue doesn't change. time.Sleep(2 * time.Second) - zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) + zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) // This zipReq should match what's sitting in the queue, as we haven't // let it release yet. From the consumer's point of view, this looks like @@ -106,12 +105,12 @@ func TestArchive_Basic(t *testing.T) { // Now we'll submit a request and TimedWaitForCompletion twice, before and // after we release it. We should trigger both the timeout and non-timeout // cases. - timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.TARGZ) + timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".tar.gz") assert.NoError(t, err) assert.NotNil(t, timedReq) doArchive(db.DefaultContext, timedReq) - zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) + zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) // Now, we're guaranteed to have released the original zipReq from the queue. // Ensure that we don't get handed back the released entry somehow, but they @@ -129,6 +128,6 @@ func TestArchive_Basic(t *testing.T) { } func TestErrUnknownArchiveFormat(t *testing.T) { - err := ErrUnknownArchiveFormat{RequestFormat: "master"} + err := ErrUnknownArchiveFormat{RequestNameType: "xxx"} assert.ErrorIs(t, err, ErrUnknownArchiveFormat{}) } diff --git a/templates/repo/clone_panel.tmpl b/templates/repo/clone_panel.tmpl index d3496bdb73a04..c1c2c87b75561 100644 --- a/templates/repo/clone_panel.tmpl +++ b/templates/repo/clone_panel.tmpl @@ -32,12 +32,14 @@ {{end}} - {{if and (not $.DisableDownloadSourceArchives) $.RefName}} + {{if and (not $.DisableDownloadSourceArchives) $.RefFullName}}
- {{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}} - {{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}} - {{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}} + {{/* FIXME: here it only uses the shortname in the ref to build the link, it can't distinguish the branch/tag/commit with the same name + in the future, it's better to use something like "/archive/branch/the-name.zip", "/archive/tag/the-name.zip" */}} + {{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}} + {{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}} + {{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}
{{end}} {{end}} diff --git a/tests/integration/empty_repo_test.go b/tests/integration/empty_repo_test.go index 0801b093dfb07..8cab04a5a5e49 100644 --- a/tests/integration/empty_repo_test.go +++ b/tests/integration/empty_repo_test.go @@ -60,7 +60,9 @@ func TestEmptyRepoAddFile(t *testing.T) { session := loginUser(t, "user30") req := NewRequest(t, "GET", "/user30/empty") resp := session.MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), "empty-repo-guide") + bodyString := resp.Body.String() + assert.Contains(t, bodyString, "empty-repo-guide") + assert.True(t, test.IsNormalPageCompleted(bodyString)) req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch) resp = session.MakeRequest(t, req, http.StatusOK)