1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-28 13:18:37 +00:00

Allow renaming/moving binary/LFS files in the UI (#34350)

Adds the ability to rename/move binary files like binary blobs or images
and files that are too large in the web ui.
This was purposed in #24722, along with the ability edit images via an
upload of a new image, which I didn't implement here (could be done in a
separate PR).

Binary file content:

![binary](https://github.com/user-attachments/assets/61d9ff71-25d3-4832-9288-452cdefc7283)

File too large:

![toolarge](https://github.com/user-attachments/assets/3b42dbd0-e76a-4c3c-92d2-52ebffedea64)

GitHub does the same (I've copied the text from there):

![gh](https://github.com/user-attachments/assets/e1499813-fb71-4544-9d58-086046a5f13e)
This commit is contained in:
bytedream
2025-06-17 02:15:07 +02:00
committed by GitHub
parent 24ce2058e8
commit 3a37d63d61
12 changed files with 417 additions and 115 deletions

View File

@@ -145,10 +145,6 @@ func editFile(ctx *context.Context, isNewFile bool) {
}
blob := entry.Blob()
if blob.Size() >= setting.UI.MaxDisplayFileSize {
ctx.NotFound(err)
return
}
buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, blob)
if err != nil {
@@ -162,22 +158,37 @@ func editFile(ctx *context.Context, isNewFile bool) {
defer dataRc.Close()
ctx.Data["FileSize"] = blob.Size()
// Only some file types are editable online as text.
if !fInfo.st.IsRepresentableAsText() || fInfo.isLFSFile {
ctx.NotFound(nil)
return
if fInfo.isLFSFile {
lfsLock, err := git_model.GetTreePathLock(ctx, ctx.Repo.Repository.ID, ctx.Repo.TreePath)
if err != nil {
ctx.ServerError("GetTreePathLock", err)
return
}
if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID {
ctx.NotFound(nil)
return
}
}
d, _ := io.ReadAll(dataRc)
ctx.Data["FileSize"] = fInfo.fileSize
buf = append(buf, d...)
if content, err := charset.ToUTF8(buf, charset.ConvertOpts{KeepBOM: true}); err != nil {
log.Error("ToUTF8: %v", err)
ctx.Data["FileContent"] = string(buf)
// Only some file types are editable online as text.
if fInfo.isLFSFile {
ctx.Data["NotEditableReason"] = ctx.Tr("repo.editor.cannot_edit_lfs_files")
} else if !fInfo.st.IsRepresentableAsText() {
ctx.Data["NotEditableReason"] = ctx.Tr("repo.editor.cannot_edit_non_text_files")
} else if fInfo.fileSize >= setting.UI.MaxDisplayFileSize {
ctx.Data["NotEditableReason"] = ctx.Tr("repo.editor.cannot_edit_too_large_file")
} else {
ctx.Data["FileContent"] = content
d, _ := io.ReadAll(dataRc)
buf = append(buf, d...)
if content, err := charset.ToUTF8(buf, charset.ConvertOpts{KeepBOM: true}); err != nil {
log.Error("ToUTF8: %v", err)
ctx.Data["FileContent"] = string(buf)
} else {
ctx.Data["FileContent"] = content
}
}
} else {
// Append filename from query, or empty string to allow username the new file.
@@ -280,6 +291,10 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
operation := "update"
if isNewFile {
operation = "create"
} else if !form.Content.Has() && ctx.Repo.TreePath != form.TreePath {
// The form content only has data if file is representable as text, is not too large and not in lfs. If it doesn't
// have data, the only possible operation is a rename
operation = "rename"
}
if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
@@ -292,7 +307,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
Operation: operation,
FromTreePath: ctx.Repo.TreePath,
TreePath: form.TreePath,
ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")),
ContentReader: strings.NewReader(strings.ReplaceAll(form.Content.Value(), "\r", "")),
},
},
Signoff: form.Signoff,

View File

@@ -99,7 +99,7 @@ func NewDiffPatchPost(ctx *context.Context) {
OldBranch: ctx.Repo.BranchName,
NewBranch: branchName,
Message: message,
Content: strings.ReplaceAll(form.Content, "\r", ""),
Content: strings.ReplaceAll(form.Content.Value(), "\r", ""),
Author: gitCommitter,
Committer: gitCommitter,
})

View File

@@ -285,10 +285,10 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
}
}
prepareToRenderButtons(ctx, fInfo.isLFSFile, isRepresentableAsText, lfsLock)
prepareToRenderButtons(ctx, lfsLock)
}
func prepareToRenderButtons(ctx *context.Context, isLFSFile, isRepresentableAsText bool, lfsLock *git_model.LFSLock) {
func prepareToRenderButtons(ctx *context.Context, lfsLock *git_model.LFSLock) {
// archived or mirror repository, the buttons should not be shown
if ctx.Repo.Repository.IsArchived || !ctx.Repo.Repository.CanEnableEditor() {
return
@@ -301,33 +301,16 @@ func prepareToRenderButtons(ctx *context.Context, isLFSFile, isRepresentableAsTe
return
}
if isLFSFile {
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files")
} else if !isRepresentableAsText {
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files")
}
if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
if !isLFSFile { // lfs file cannot be edited after fork
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit")
}
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit")
ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access")
return
}
// it's a lfs file and the user is not the owner of the lock
if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID {
ctx.Data["CanEditFile"] = false
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.this_file_locked")
ctx.Data["CanDeleteFile"] = false
ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.this_file_locked")
return
}
if !isLFSFile { // lfs file cannot be edited
ctx.Data["CanEditFile"] = true
ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file")
}
ctx.Data["CanDeleteFile"] = true
ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file")
isLFSLocked := lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID
ctx.Data["CanEditFile"] = !isLFSLocked
ctx.Data["EditFileTooltip"] = util.Iif(isLFSLocked, ctx.Tr("repo.editor.this_file_locked"), ctx.Tr("repo.editor.edit_this_file"))
ctx.Data["CanDeleteFile"] = !isLFSLocked
ctx.Data["DeleteFileTooltip"] = util.Iif(isLFSLocked, ctx.Tr("repo.editor.this_file_locked"), ctx.Tr("repo.editor.delete_this_file"))
}