192 lines
6.9 KiB
Diff
192 lines
6.9 KiB
Diff
--- 9.0.3 2024-12-12 08:06:13.000000000 +0100
|
|
+++ aneksajo 2024-12-16 08:23:15.000000000 +0100
|
|
@@ -34,6 +34,7 @@
|
|
unit_model "code.gitea.io/gitea/models/unit"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/actions"
|
|
+ "code.gitea.io/gitea/modules/annex"
|
|
"code.gitea.io/gitea/modules/base"
|
|
"code.gitea.io/gitea/modules/charset"
|
|
"code.gitea.io/gitea/modules/git"
|
|
@@ -209,14 +210,59 @@
|
|
}
|
|
|
|
type fileInfo struct {
|
|
- isTextFile bool
|
|
- isLFSFile bool
|
|
- fileSize int64
|
|
- lfsMeta *lfs.Pointer
|
|
- st typesniffer.SniffedType
|
|
+ isTextFile bool
|
|
+ isLFSFile bool
|
|
+ isAnnexFile bool
|
|
+ isAnnexFilePresent bool
|
|
+ fileSize int64
|
|
+ lfsMeta *lfs.Pointer
|
|
+ st typesniffer.SniffedType
|
|
}
|
|
|
|
func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, io.ReadCloser, *fileInfo, error) {
|
|
+ isAnnexed, err := annex.IsAnnexed(blob)
|
|
+ if err != nil {
|
|
+ return nil, nil, nil, err
|
|
+ }
|
|
+ if isAnnexed {
|
|
+ // TODO: this code could be merged with the LFS case, especially the redundant type sniffer,
|
|
+ // but it is *currently* written this way to make merging with the non-annex upstream easier:
|
|
+ // this way, the git-annex patch is (mostly) pure additions.
|
|
+
|
|
+ annexContent, err := annex.Content(blob)
|
|
+ if err != nil {
|
|
+ // If annex.Content returns an error it can mean that the blob does not
|
|
+ // refer to an annexed file or that it is not present here. Since we already
|
|
+ // checked that it is annexed the latter must be the case. So we return the
|
|
+ // content of the blob instead and indicate that the file is indeed annexed,
|
|
+ // but not present here. The template can then communicate the situation.
|
|
+ dataRc, err := blob.DataAsync()
|
|
+ if err != nil {
|
|
+ return nil, nil, nil, err
|
|
+ }
|
|
+
|
|
+ buf := make([]byte, 1024)
|
|
+ n, _ := util.ReadAtMost(dataRc, buf)
|
|
+ buf = buf[:n]
|
|
+
|
|
+ st := typesniffer.DetectContentType(buf)
|
|
+ return buf, dataRc, &fileInfo{st.IsText(), false, true, false, blob.Size(), nil, st}, nil
|
|
+ }
|
|
+
|
|
+ stat, err := annexContent.Stat()
|
|
+ if err != nil {
|
|
+ return nil, nil, nil, err
|
|
+ }
|
|
+
|
|
+ buf := make([]byte, 1024)
|
|
+ n, _ := util.ReadAtMost(annexContent, buf)
|
|
+ buf = buf[:n]
|
|
+
|
|
+ st := typesniffer.DetectContentType(buf)
|
|
+
|
|
+ return buf, annexContent, &fileInfo{st.IsText(), false, true, true, stat.Size(), nil, st}, nil
|
|
+ }
|
|
+
|
|
dataRc, err := blob.DataAsync()
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
@@ -231,18 +277,18 @@
|
|
|
|
// FIXME: what happens when README file is an image?
|
|
if !isTextFile || !setting.LFS.StartServer {
|
|
- return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
|
|
+ return buf, dataRc, &fileInfo{isTextFile, false, false, false, blob.Size(), nil, st}, nil
|
|
}
|
|
|
|
pointer, _ := lfs.ReadPointerFromBuffer(buf)
|
|
if !pointer.IsValid() { // fallback to plain file
|
|
- return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
|
|
+ return buf, dataRc, &fileInfo{isTextFile, false, false, false, blob.Size(), nil, st}, nil
|
|
}
|
|
|
|
meta, err := git_model.GetLFSMetaObjectByOid(ctx, repoID, pointer.Oid)
|
|
if err != nil { // fallback to plain file
|
|
log.Warn("Unable to access LFS pointer %s in repo %d: %v", pointer.Oid, repoID, err)
|
|
- return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
|
|
+ return buf, dataRc, &fileInfo{isTextFile, false, false, false, blob.Size(), nil, st}, nil
|
|
}
|
|
|
|
dataRc.Close()
|
|
@@ -262,7 +308,7 @@
|
|
|
|
st = typesniffer.DetectContentType(buf)
|
|
|
|
- return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil
|
|
+ return buf, dataRc, &fileInfo{st.IsText(), true, false, false, meta.Size, &meta.Pointer, st}, nil
|
|
}
|
|
|
|
func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) {
|
|
@@ -325,6 +371,7 @@
|
|
},
|
|
Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx),
|
|
GitRepo: ctx.Repo.GitRepo,
|
|
+ Blob: target.Blob(),
|
|
}, rd)
|
|
if err != nil {
|
|
log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err)
|
|
@@ -447,10 +494,17 @@
|
|
isDisplayingSource := ctx.FormString("display") == "source"
|
|
isDisplayingRendered := !isDisplayingSource
|
|
|
|
- if fInfo.isLFSFile {
|
|
+ if fInfo.isLFSFile || fInfo.isAnnexFile {
|
|
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
|
}
|
|
|
|
+ if fInfo.isAnnexFile {
|
|
+ // pre-git-annex v7, all annexed files were represented in-repo as symlinks;
|
|
+ // but we pretend they aren't, since that's a distracting quirk of git-annex
|
|
+ // and not a meaningful choice on the user's part
|
|
+ ctx.Data["FileIsSymlink"] = false
|
|
+ }
|
|
+
|
|
isRepresentableAsText := fInfo.st.IsRepresentableAsText()
|
|
if !isRepresentableAsText {
|
|
// If we can't show plain text, always try to render.
|
|
@@ -458,6 +512,8 @@
|
|
isDisplayingRendered = true
|
|
}
|
|
ctx.Data["IsLFSFile"] = fInfo.isLFSFile
|
|
+ ctx.Data["IsAnnexFile"] = fInfo.isAnnexFile
|
|
+ ctx.Data["IsAnnexFilePresent"] = fInfo.isAnnexFilePresent
|
|
ctx.Data["FileSize"] = fInfo.fileSize
|
|
ctx.Data["IsTextFile"] = fInfo.isTextFile
|
|
ctx.Data["IsRepresentableAsText"] = isRepresentableAsText
|
|
@@ -492,6 +548,8 @@
|
|
// Assume file is not editable first.
|
|
if fInfo.isLFSFile {
|
|
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files")
|
|
+ } else if fInfo.isAnnexFile {
|
|
+ ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_annex_files")
|
|
} else if !isRepresentableAsText {
|
|
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files")
|
|
}
|
|
@@ -546,6 +604,7 @@
|
|
},
|
|
Metas: metas,
|
|
GitRepo: ctx.Repo.GitRepo,
|
|
+ Blob: entry.Blob(),
|
|
}, rd)
|
|
if err != nil {
|
|
ctx.ServerError("Render", err)
|
|
@@ -599,7 +658,7 @@
|
|
ctx.Data["FileContent"] = fileContent
|
|
ctx.Data["LineEscapeStatus"] = statuses
|
|
}
|
|
- if !fInfo.isLFSFile {
|
|
+ if !fInfo.isLFSFile && !fInfo.isAnnexFile {
|
|
if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) {
|
|
if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID {
|
|
ctx.Data["CanEditFile"] = false
|
|
@@ -644,6 +703,7 @@
|
|
},
|
|
Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx),
|
|
GitRepo: ctx.Repo.GitRepo,
|
|
+ Blob: entry.Blob(),
|
|
}, rd)
|
|
if err != nil {
|
|
ctx.ServerError("Render", err)
|
|
@@ -1153,6 +1213,15 @@
|
|
ctx.Data["TreeNames"] = treeNames
|
|
ctx.Data["BranchLink"] = branchLink
|
|
ctx.Data["CodeIndexerDisabled"] = !setting.Indexer.RepoIndexerEnabled
|
|
+ isAnnexFile, okAnnexFile := ctx.Data["IsAnnexFile"]
|
|
+ isAnnexFilePresent, okAnnexFilePresent := ctx.Data["IsAnnexFilePresent"]
|
|
+ if okAnnexFile && okAnnexFilePresent && isAnnexFile.(bool) && !isAnnexFilePresent.(bool) {
|
|
+ // If the file to be viewed is annexed but not present then render it normally
|
|
+ // (which will show the plain git blob content, i.e. the symlink or pointer target)
|
|
+ // but make the status code a 404.
|
|
+ ctx.HTML(http.StatusNotFound, tplRepoHome)
|
|
+ return
|
|
+ }
|
|
ctx.HTML(http.StatusOK, tplRepoHome)
|
|
}
|
|
|