From f6c20e988651369b968b7930616d592a0d2f8ccf Mon Sep 17 00:00:00 2001 From: David Rotermund Date: Fri, 16 May 2025 21:01:07 +0200 Subject: [PATCH] Changed strategie to git rm -r as suggested by earl-warren --- modules/git/repo_index.go | 25 ------- services/repository/files/temp_repo.go | 40 ++++++++++ services/repository/files/update.go | 85 +++++++++++++++++----- tests/integration/recursive_delete_test.go | 1 - 4 files changed, 106 insertions(+), 45 deletions(-) diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index daf04ddf7e..f58757a9a2 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -6,7 +6,6 @@ package git import ( "bytes" "context" - "errors" "os" "path/filepath" "strings" @@ -103,30 +102,6 @@ func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { return filelist, err } -// Gives a list of all files in a directory and below -func (repo *Repository) LsFilesFromDirectory(directory, branch string) ([]string, error) { - if branch == "" { - return nil, errors.New("branch not found in context URL") - } - - cmd := NewCommand(repo.Ctx, "ls-files").AddOptionFormat("--with-tree=%s", branch) - if len(directory) > 0 { - cmd.AddArguments("--directory").AddDashesAndList(directory) - } - res, stderror, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path}) - if err != nil { - return nil, err - } - - if len(stderror) > 0 { - return nil, errors.New(string(stderror)) - } - - lines := strings.Split(string(res), "\n") - - return lines, nil -} - // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { objectFormat, err := repo.GetObjectFormat() diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index b3aadbc6cb..273db04663 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -380,3 +380,43 @@ func (t *TemporaryUploadRepository) GetCommit(commitID string) (*git.Commit, err } return t.gitRepo.GetCommit(commitID) } + +// Run LFS prune to clean up LFS objects +func (t *TemporaryUploadRepository) PruneLFSFiles() error { + + stdOut := new(bytes.Buffer) + stdErr := new(bytes.Buffer) + stdIn := new(bytes.Buffer) + + if err := git.NewCommand(t.ctx, "lfs", "prune"). + Run(&git.RunOpts{ + Dir: t.basePath, + Stdin: stdIn, + Stdout: stdOut, + Stderr: stdErr, + }); err != nil { + log.Error("Unable to prune lfs files for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) + return fmt.Errorf("Unable to prune lfs files for temporary repo: %s Error: %w\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) + } + return nil +} + +// Remove the directory recursively +func (t *TemporaryUploadRepository) RemoveDirectoryRecursively(directory string) error { + + stdOut := new(bytes.Buffer) + stdErr := new(bytes.Buffer) + stdIn := new(bytes.Buffer) + + if err := git.NewCommand(t.ctx, "rm", "-r").AddDynamicArguments(directory). + Run(&git.RunOpts{ + Dir: t.basePath, + Stdin: stdIn, + Stdout: stdOut, + Stderr: stdErr, + }); err != nil { + log.Error("Unable to remove directory %s recursively for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", directory, t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) + return fmt.Errorf("Unable to remove directory %s recursively for temporary repo: %s Error: %w\nstdout: %s\nstderr: %s", directory, t.repo.FullName(), err, stdOut.String(), stdErr.String()) + } + return nil +} diff --git a/services/repository/files/update.go b/services/repository/files/update.go index 6ef4ae9b25..8f53b07a32 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -93,26 +93,73 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use } if opts.IsDir { - var newOptsFiles []*ChangeRepoFile - for _, file := range opts.Files { - if file.Operation != "delete" { - return nil, errors.New("invalid operation: only delete is allowed for directory paths") - } - treePath := CleanUploadFileName(file.TreePath) - filelist, err := gitRepo.LsFilesFromDirectory(treePath, opts.OldBranch) - if err != nil { - return nil, err - } - for _, filename := range filelist { - if len(filename) > 0 { - newOptsFiles = append(newOptsFiles, &ChangeRepoFile{ - Operation: "delete", - TreePath: filename, - }) - } - } + if len(opts.Files) != 1 { + return nil, errors.New("expected exactly one directory for deletion") } - opts.Files = newOptsFiles + + if opts.Files[0].Operation != "delete" { + return nil, errors.New("invalid operation: only delete is allowed for directory paths") + } + treePath := CleanUploadFileName(opts.Files[0].TreePath) + + // if we mean the root then we need to replace it with a "." + if treePath == "" { + treePath = "." + } + + message := strings.TrimSpace(opts.Message) + author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) + + t, err := NewTemporaryUploadRepository(ctx, repo) + if err != nil { + log.Error("NewTemporaryUploadRepository failed: %v", err) + } + defer t.Close() + + if err := t.Clone(opts.OldBranch, false); err != nil { + return nil, err + } + + if err := t.SetDefaultIndex(); err != nil { + return nil, err + } + + if err := t.RefreshIndex(); err != nil { + return nil, err + } + + if err := t.RemoveDirectoryRecursively(treePath); err != nil { + return nil, err + } + + treeHash, err := t.WriteTree() + if err != nil { + return nil, err + } + + // Now commit the tree + var commitHash string + if opts.Dates != nil { + commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) + } else { + commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff) + } + if err != nil { + return nil, err + } + + // Then push this tree to NewBranch + if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { + log.Error("%T %v", err, err) + return nil, err + } + + if err := t.PruneLFSFiles(); err != nil { + return nil, err + } + + return nil, nil + } var treePaths []string diff --git a/tests/integration/recursive_delete_test.go b/tests/integration/recursive_delete_test.go index da037aea0b..e10a1a4171 100644 --- a/tests/integration/recursive_delete_test.go +++ b/tests/integration/recursive_delete_test.go @@ -1,6 +1,5 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -// make test-sqlite#TestRecursiveDelete package integration import (