Use annexed content for comparison in diffs (#57)

This makes it such that annexed files are treated like plain git files
in comparisons (e.g. the diff of a commit).

It also changes the image diff viewer to show a more reasonable error
message when one of the annexed files under comparison is missing.

Fixes #56.

Reviewed-on: https://codeberg.org/forgejo-aneksajo/forgejo-aneksajo/pulls/57
Co-authored-by: Matthias Riße <m.risse@fz-juelich.de>
Co-committed-by: Matthias Riße <m.risse@fz-juelich.de>
This commit is contained in:
Matthias Riße 2025-01-31 00:43:20 +00:00
parent 85812d8b01
commit e741b44667
3 changed files with 44 additions and 7 deletions

View file

@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/typesniffer"
)
// ErrBlobIsNotAnnexed occurs if a blob does not contain a valid annex key
@ -178,3 +179,14 @@ func UUID2RepoPath(uuid string) (string, error) {
// Otherwise just return the cached entry
return repoPath, nil
}
// GuessContentType guesses the content type of the annexed blob.
func GuessContentType(blob *git.Blob) (typesniffer.SniffedType, error) {
r, err := Content(blob)
if err != nil {
return typesniffer.SniffedType{}, err
}
defer r.Close()
return typesniffer.DetectContentTypeFromReader(r)
}

View file

@ -23,6 +23,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/annex"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
csv_module "code.gitea.io/gitea/modules/csv"
@ -72,7 +73,21 @@ func setCompareContext(ctx *context.Context, before, head *git.Commit, headOwner
return st
}
st, err := blob.GuessContentType()
isAnnexed, err := annex.IsAnnexed(blob)
if err != nil {
log.Error("IsAnnexed failed: %v", err)
return st
}
if isAnnexed {
st, err = annex.GuessContentType(blob)
if err != nil {
log.Error("GuessContentType failed: %v", err)
return st
}
return st
}
st, err = blob.GuessContentType()
if err != nil {
log.Error("GuessContentType failed: %v", err)
return st
@ -90,18 +105,18 @@ func SourceCommitURL(owner, name string, commit *git.Commit) string {
return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/src/commit/" + url.PathEscape(commit.ID.String())
}
// RawCommitURL creates a relative URL for the raw commit in the given repository
func RawCommitURL(owner, name string, commit *git.Commit) string {
return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/raw/commit/" + url.PathEscape(commit.ID.String())
// MediaCommitURL creates a relative URL for the commit media (plain git, LFS, or annex content) in the given repository
func MediaCommitURL(owner, name string, commit *git.Commit) string {
return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/media/commit/" + url.PathEscape(commit.ID.String())
}
// setPathsCompareContext sets context data for source and raw paths
func setPathsCompareContext(ctx *context.Context, base, head *git.Commit, headOwner, headName string) {
ctx.Data["SourcePath"] = SourceCommitURL(headOwner, headName, head)
ctx.Data["RawPath"] = RawCommitURL(headOwner, headName, head)
ctx.Data["RawPath"] = MediaCommitURL(headOwner, headName, head)
if base != nil {
ctx.Data["BeforeSourcePath"] = SourceCommitURL(headOwner, headName, base)
ctx.Data["BeforeRawPath"] = RawCommitURL(headOwner, headName, base)
ctx.Data["BeforeRawPath"] = MediaCommitURL(headOwner, headName, base)
}
}

View file

@ -92,7 +92,17 @@ export function initImageDiff() {
return loadElem(img, info.path);
}));
// only the first images is associated with $boundsInfo
if (!success) info.$boundsInfo.text('(image error)');
if (!success) {
const blobContent = await GET(info.path.replace('/media/', '/raw/')).then((response) => response.text());
if (blobContent.startsWith('.git/annex/objects')) {
for (const item of document.querySelectorAll('.image-diff .overflow-menu-items .item')) {
item.style.display = 'none';
}
info.$boundsInfo[0].parentElement.textContent = 'annexed file is not present on the server';
} else {
info.$boundsInfo.text('(image error)');
}
}
if (info.mime === 'image/svg+xml') {
const resp = await GET(info.path);
const text = await resp.text();