diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index 27bcafa6497b9..54fe292cf0380 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -44,12 +44,12 @@ func IsWorkflow(path string) bool { return strings.HasPrefix(path, ".gitea/workflows") || strings.HasPrefix(path, ".github/workflows") } -func ListWorkflows(commit *git.Commit) (string, git.Entries, error) { +func ListWorkflows(rootTree *git.Tree) (string, git.Entries, error) { rpath := ".gitea/workflows" - tree, err := commit.SubTree(rpath) + tree, err := rootTree.SubTree(rpath) if _, ok := err.(git.ErrNotExist); ok { rpath = ".github/workflows" - tree, err = commit.SubTree(rpath) + tree, err = rootTree.SubTree(rpath) } if _, ok := err.(git.ErrNotExist); ok { return "", nil, nil @@ -105,7 +105,7 @@ func DetectWorkflows( payload api.Payloader, detectSchedule bool, ) ([]*DetectedWorkflow, []*DetectedWorkflow, error) { - _, entries, err := ListWorkflows(commit) + _, entries, err := ListWorkflows(git.NewTree(gitRepo, commit.TreeID)) if err != nil { return nil, nil, err } @@ -150,7 +150,7 @@ func DetectWorkflows( } func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) { - _, entries, err := ListWorkflows(commit) + _, entries, err := ListWorkflows(git.NewTree(gitRepo, commit.TreeID)) if err != nil { return nil, err } @@ -204,7 +204,7 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web case // push webhook_module.HookEventPush: - return matchPushEvent(commit, payload.(*api.PushPayload), evt) + return matchPushEvent(gitRepo, commit, payload.(*api.PushPayload), evt) case // issues webhook_module.HookEventIssues, @@ -256,7 +256,7 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web } } -func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobparser.Event) bool { +func matchPushEvent(gitRepo *git.Repository, commit *git.Commit, pushPayload *api.PushPayload, evt *jobparser.Event) bool { // with no special filter parameters if len(evt.Acts()) == 0 { return true @@ -322,7 +322,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa matchTimes++ break } - filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before) + filesChanged, err := gitRepo.GetFilesChangedBetween(pushPayload.Before, commit.ID.String()) if err != nil { log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) } else { @@ -339,7 +339,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa matchTimes++ break } - filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before) + filesChanged, err := gitRepo.GetFilesChangedBetween(pushPayload.Before, commit.ID.String()) if err != nil { log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) } else { @@ -478,7 +478,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa matchTimes++ } case "paths": - filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase) + filesChanged, err := gitRepo.GetFilesChangedBetween(prPayload.PullRequest.MergeBase, headCommit.ID.String()) if err != nil { log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err) } else { @@ -491,7 +491,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa } } case "paths-ignore": - filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase) + filesChanged, err := gitRepo.GetFilesChangedBetween(prPayload.PullRequest.MergeBase, headCommit.ID.String()) if err != nil { log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err) } else { diff --git a/modules/fileicon/entry.go b/modules/fileicon/entry.go index 0326c2bfa8ab9..4353a3132de27 100644 --- a/modules/fileicon/entry.go +++ b/modules/fileicon/entry.go @@ -12,10 +12,10 @@ type EntryInfo struct { IsOpen bool } -func EntryInfoFromGitTreeEntry(commit *git.Commit, fullPath string, gitEntry *git.TreeEntry) *EntryInfo { +func EntryInfoFromGitTreeEntry(tree *git.Tree, fullPath string, gitEntry *git.TreeEntry) *EntryInfo { ret := &EntryInfo{BaseName: gitEntry.Name(), EntryMode: gitEntry.Mode()} if gitEntry.IsLink() { - if res, err := git.EntryFollowLink(commit, fullPath, gitEntry); err == nil && res.TargetEntry.IsDir() { + if res, err := git.EntryFollowLink(tree, fullPath, gitEntry); err == nil && res.TargetEntry.IsDir() { ret.SymlinkToMode = res.TargetEntry.Mode() } } diff --git a/modules/git/blame.go b/modules/git/blame.go index db10df129e2f8..a723b35051944 100644 --- a/modules/git/blame.go +++ b/modules/git/blame.go @@ -132,7 +132,7 @@ func (r *BlameReader) Close() error { } // CreateBlameReader creates reader for given repository, commit and file -func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (rd *BlameReader, err error) { +func CreateBlameReader(ctx context.Context, repo *Repository, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (rd *BlameReader, err error) { var ignoreRevsFileName string var ignoreRevsFileCleanup func() defer func() { @@ -144,7 +144,8 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath cmd := NewCommand("blame", "--porcelain") if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore { - ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(commit) + tree := NewTree(repo, commit.TreeID) + ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(tree) if err != nil && !IsErrNotExist(err) { return nil, err } @@ -190,8 +191,8 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath }, nil } -func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func(), error) { - entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs") +func tryCreateBlameIgnoreRevsFile(tree *Tree) (string, func(), error) { + entry, err := tree.GetTreeEntryByPath(".git-blame-ignore-revs") if err != nil { return "", nil, err } diff --git a/modules/git/blame_sha256_test.go b/modules/git/blame_sha256_test.go index c0a97bed3bd35..0845cb7168ff0 100644 --- a/modules/git/blame_sha256_test.go +++ b/modules/git/blame_sha256_test.go @@ -47,7 +47,7 @@ func TestReadingBlameOutputSha256(t *testing.T) { } for _, bypass := range []bool{false, true} { - blameReader, err := CreateBlameReader(ctx, Sha256ObjectFormat, "./tests/repos/repo5_pulls_sha256", commit, "README.md", bypass) + blameReader, err := CreateBlameReader(ctx, repo, Sha256ObjectFormat, "./tests/repos/repo5_pulls_sha256", commit, "README.md", bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() @@ -131,7 +131,7 @@ func TestReadingBlameOutputSha256(t *testing.T) { for _, c := range cases { commit, err := repo.GetCommit(c.CommitID) assert.NoError(t, err) - blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) + blameReader, err := CreateBlameReader(ctx, repo, objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go index 809d6fbcf7381..a341e34163778 100644 --- a/modules/git/blame_test.go +++ b/modules/git/blame_test.go @@ -42,7 +42,7 @@ func TestReadingBlameOutput(t *testing.T) { } for _, bypass := range []bool{false, true} { - blameReader, err := CreateBlameReader(ctx, Sha1ObjectFormat, "./tests/repos/repo5_pulls", commit, "README.md", bypass) + blameReader, err := CreateBlameReader(ctx, repo, Sha1ObjectFormat, "./tests/repos/repo5_pulls", commit, "README.md", bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() @@ -127,7 +127,7 @@ func TestReadingBlameOutput(t *testing.T) { commit, err := repo.GetCommit(c.CommitID) assert.NoError(t, err) - blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) + blameReader, err := CreateBlameReader(ctx, repo, objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() diff --git a/modules/git/commit.go b/modules/git/commit.go index 0dc894d3ab6cd..6de073ecab68f 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -8,9 +8,7 @@ import ( "bufio" "bytes" "context" - "errors" "io" - "os/exec" "strconv" "strings" @@ -20,16 +18,13 @@ import ( // Commit represents a git commit. type Commit struct { - Tree // FIXME: bad design, this field can be nil if the commit is from "last commit cache" - ID ObjectID + TreeID ObjectID + Parents []ObjectID // ID strings Author *Signature // never nil Committer *Signature // never nil CommitMessage string Signature *CommitSignature - - Parents []ObjectID // ID strings - submoduleCache *ObjectCache[*SubModule] } // CommitSignature represents a git commit signature part. @@ -58,33 +53,12 @@ func (c *Commit) ParentID(n int) (ObjectID, error) { return c.Parents[n], nil } -// Parent returns n-th parent (0-based index) of the commit. -func (c *Commit) Parent(n int) (*Commit, error) { - id, err := c.ParentID(n) - if err != nil { - return nil, err - } - parent, err := c.repo.getCommit(id) - if err != nil { - return nil, err - } - return parent, nil -} - // ParentCount returns number of parents of the commit. // 0 if this is the root commit, otherwise 1,2, etc. func (c *Commit) ParentCount() int { return len(c.Parents) } -// GetCommitByPath return the commit of relative path object. -func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) { - if c.repo.LastCommitCache != nil { - return c.repo.LastCommitCache.GetCommitByPath(c.ID.String(), relpath) - } - return c.repo.getCommitByPathWithID(c.ID, relpath) -} - // AddChanges marks local changes to be ready for commit. func AddChanges(ctx context.Context, repoPath string, all bool, files ...string) error { cmd := NewCommand().AddArguments("add") @@ -180,78 +154,6 @@ func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error) return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64) } -// CommitsCount returns number of total commits of until current revision. -func (c *Commit) CommitsCount() (int64, error) { - return CommitsCount(c.repo.Ctx, CommitsCountOptions{ - RepoPath: c.repo.Path, - Revision: []string{c.ID.String()}, - }) -} - -// CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize -func (c *Commit) CommitsByRange(page, pageSize int, not, since, until string) ([]*Commit, error) { - return c.repo.commitsByRangeWithTime(c.ID, page, pageSize, not, since, until) -} - -// CommitsBefore returns all the commits before current revision -func (c *Commit) CommitsBefore() ([]*Commit, error) { - return c.repo.getCommitsBefore(c.ID) -} - -// HasPreviousCommit returns true if a given commitHash is contained in commit's parents -func (c *Commit) HasPreviousCommit(objectID ObjectID) (bool, error) { - this := c.ID.String() - that := objectID.String() - - if this == that { - return false, nil - } - - _, _, err := NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path}) - if err == nil { - return true, nil - } - var exitError *exec.ExitError - if errors.As(err, &exitError) { - if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 { - return false, nil - } - } - return false, err -} - -// IsForcePush returns true if a push from oldCommitHash to this is a force push -func (c *Commit) IsForcePush(oldCommitID string) (bool, error) { - objectFormat, err := c.repo.GetObjectFormat() - if err != nil { - return false, err - } - if oldCommitID == objectFormat.EmptyObjectID().String() { - return false, nil - } - - oldCommit, err := c.repo.GetCommit(oldCommitID) - if err != nil { - return false, err - } - hasPreviousCommit, err := c.HasPreviousCommit(oldCommit.ID) - return !hasPreviousCommit, err -} - -// CommitsBeforeLimit returns num commits before current revision -func (c *Commit) CommitsBeforeLimit(num int) ([]*Commit, error) { - return c.repo.getCommitsBeforeLimit(c.ID, num) -} - -// CommitsBeforeUntil returns the commits between commitID to current revision -func (c *Commit) CommitsBeforeUntil(commitID string) ([]*Commit, error) { - endCommit, err := c.repo.GetCommit(commitID) - if err != nil { - return nil, err - } - return c.repo.CommitsBetween(c, endCommit) -} - // SearchCommitsOptions specify the parameters for SearchCommits type SearchCommitsOptions struct { Keywords []string @@ -291,82 +193,6 @@ func NewSearchCommitsOptions(searchString string, forAllRefs bool) SearchCommits } } -// SearchCommits returns the commits match the keyword before current revision -func (c *Commit) SearchCommits(opts SearchCommitsOptions) ([]*Commit, error) { - return c.repo.searchCommits(c.ID, opts) -} - -// GetFilesChangedSinceCommit get all changed file names between pastCommit to current revision -func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error) { - return c.repo.GetFilesChangedBetween(pastCommit, c.ID.String()) -} - -// FileChangedSinceCommit Returns true if the file given has changed since the past commit -// YOU MUST ENSURE THAT pastCommit is a valid commit ID. -func (c *Commit) FileChangedSinceCommit(filename, pastCommit string) (bool, error) { - return c.repo.FileChangedBetweenCommits(filename, pastCommit, c.ID.String()) -} - -// HasFile returns true if the file given exists on this commit -// This does only mean it's there - it does not mean the file was changed during the commit. -func (c *Commit) HasFile(filename string) (bool, error) { - _, err := c.GetBlobByPath(filename) - if err != nil { - return false, err - } - return true, nil -} - -// GetFileContent reads a file content as a string or returns false if this was not possible -func (c *Commit) GetFileContent(filename string, limit int) (string, error) { - entry, err := c.GetTreeEntryByPath(filename) - if err != nil { - return "", err - } - - r, err := entry.Blob().DataAsync() - if err != nil { - return "", err - } - defer r.Close() - - if limit> 0 { - bs := make([]byte, limit) - n, err := util.ReadAtMost(r, bs) - if err != nil { - return "", err - } - return string(bs[:n]), nil - } - - bytes, err := io.ReadAll(r) - if err != nil { - return "", err - } - return string(bytes), nil -} - -// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only') -func (c *Commit) GetBranchName() (string, error) { - cmd := NewCommand("name-rev") - if DefaultFeatures().CheckVersionAtLeast("2.13.0") { - cmd.AddArguments("--exclude", "refs/tags/*") - } - cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) - data, _, err := cmd.RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path}) - if err != nil { - // handle special case where git can not describe commit - if strings.Contains(err.Error(), "cannot describe") { - return "", nil - } - - return "", err - } - - // name-rev commitID output will be "master" or "master~12" - return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil -} - // CommitFileStatus represents status of files in a commit. type CommitFileStatus struct { Added []string @@ -457,14 +283,6 @@ func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, err return strings.TrimSpace(commitID), nil } -// GetRepositoryDefaultPublicGPGKey returns the default public key for this commit -func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings, error) { - if c.repo == nil { - return nil, nil - } - return c.repo.GetDefaultPublicGPGKey(forceUpdate) -} - func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool { maxLen := 64 // sha256 if objFmt != nil { diff --git a/modules/git/commit_convert_gogit.go b/modules/git/commit_convert_gogit.go index d7b945ed6b8c3..3f5a098c41c3b 100644 --- a/modules/git/commit_convert_gogit.go +++ b/modules/git/commit_convert_gogit.go @@ -66,6 +66,7 @@ func convertPGPSignature(c *object.Commit) *CommitSignature { func convertCommit(c *object.Commit) *Commit { return &Commit{ ID: ParseGogitHash(c.Hash), + TreeID: ParseGogitHash(c.TreeHash), CommitMessage: c.Message, Committer: &c.Committer, Author: &c.Author, diff --git a/modules/git/commit_info.go b/modules/git/commit_info.go index 4f76a28f31c0b..bc8c5649d5bbc 100644 --- a/modules/git/commit_info.go +++ b/modules/git/commit_info.go @@ -7,17 +7,17 @@ package git type CommitInfo struct { Entry *TreeEntry Commit *Commit - SubmoduleFile *CommitSubmoduleFile + SubmoduleFile *SubmoduleFile } -func GetCommitInfoSubmoduleFile(repoLink, fullPath string, commit *Commit, refCommitID ObjectID) (*CommitSubmoduleFile, error) { - submodule, err := commit.GetSubModule(fullPath) +func GetCommitInfoSubmoduleFile(gitRepo *Repository, repoLink, fullPath string, treeID, refCommitID ObjectID) (*SubmoduleFile, error) { + submodule, err := NewTree(gitRepo, treeID).GetSubModule(fullPath) if err != nil { return nil, err } if submodule == nil { // unable to find submodule from ".gitmodules" file - return NewCommitSubmoduleFile(repoLink, fullPath, "", refCommitID.String()), nil + return NewSubmoduleFile(repoLink, fullPath, "", refCommitID.String()), nil } - return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, refCommitID.String()), nil + return NewSubmoduleFile(repoLink, fullPath, submodule.URL, refCommitID.String()), nil } diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go index 73227347bc71d..daca9cb6b9e0d 100644 --- a/modules/git/commit_info_gogit.go +++ b/modules/git/commit_info_gogit.go @@ -16,7 +16,7 @@ import ( ) // GetCommitsInfo gets information of all commits that are corresponding to these entries -func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) { +func (tes Entries) GetCommitsInfo(ctx context.Context, gitRepo *Repository, repoLink string, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) { entryPaths := make([]string, len(tes)+1) // Get the commit for the treePath itself entryPaths[0] = "" @@ -24,7 +24,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * entryPaths[i+1] = entry.Name() } - commitNodeIndex, commitGraphFile := commit.repo.CommitNodeIndex() + commitNodeIndex, commitGraphFile := gitRepo.CommitNodeIndex() if commitGraphFile != nil { defer commitGraphFile.Close() } @@ -35,14 +35,14 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * } var revs map[string]*Commit - if commit.repo.LastCommitCache != nil { + if gitRepo.LastCommitCache != nil { var unHitPaths []string - revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache) + revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, gitRepo.LastCommitCache) if err != nil { return nil, nil, err } if len(unHitPaths)> 0 { - revs2, err := GetLastCommitForPaths(ctx, commit.repo.LastCommitCache, c, treePath, unHitPaths) + revs2, err := GetLastCommitForPaths(ctx, gitRepo.LastCommitCache, c, treePath, unHitPaths) if err != nil { return nil, nil, err } @@ -58,7 +58,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * return nil, nil, err } - commit.repo.gogitStorage.Close() + gitRepo.gogitStorage.Close() commitsInfo := make([]CommitInfo, len(tes)) for i, entry := range tes { @@ -73,7 +73,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * // If the entry is a submodule, add a submodule file for this if entry.IsSubModule() { - commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(repoLink, path.Join(treePath, entry.Name()), commit, entry.ID) + commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(gitRepo, repoLink, path.Join(treePath, entry.Name()), commit.TreeID, entry.ID) if err != nil { return nil, nil, err } @@ -84,11 +84,10 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * // get it for free during the tree traversal and it's used for listing // pages to display information about newest commit for a given path. var treeCommit *Commit - var ok bool if treePath == "" { treeCommit = commit - } else if treeCommit, ok = revs[""]; ok { - treeCommit.repo = commit.repo + } else { + treeCommit = revs[""] } return commitsInfo, treeCommit, nil } diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go index ed775332a92db..5383a67bf43a5 100644 --- a/modules/git/commit_info_nogogit.go +++ b/modules/git/commit_info_nogogit.go @@ -15,7 +15,7 @@ import ( ) // GetCommitsInfo gets information of all commits that are corresponding to these entries -func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) { +func (tes Entries) GetCommitsInfo(ctx context.Context, gitRepo *Repository, repoLink string, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) { entryPaths := make([]string, len(tes)+1) // Get the commit for the treePath itself entryPaths[0] = "" @@ -26,15 +26,15 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * var err error var revs map[string]*Commit - if commit.repo.LastCommitCache != nil { + if gitRepo.LastCommitCache != nil { var unHitPaths []string - revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache) + revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, gitRepo.LastCommitCache) if err != nil { return nil, nil, err } if len(unHitPaths)> 0 { sort.Strings(unHitPaths) - commits, err := GetLastCommitForPaths(ctx, commit, treePath, unHitPaths) + commits, err := GetLastCommitForPaths(ctx, gitRepo, commit, treePath, unHitPaths) if err != nil { return nil, nil, err } @@ -43,7 +43,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * } } else { sort.Strings(entryPaths) - revs, err = GetLastCommitForPaths(ctx, commit, treePath, entryPaths) + revs, err = GetLastCommitForPaths(ctx, gitRepo, commit, treePath, entryPaths) } if err != nil { return nil, nil, err @@ -64,7 +64,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * // If the entry is a submodule, add a submodule file for this if entry.IsSubModule() { - commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(repoLink, path.Join(treePath, entry.Name()), commit, entry.ID) + commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(gitRepo, repoLink, path.Join(treePath, entry.Name()), commit.TreeID, entry.ID) if err != nil { return nil, nil, err } @@ -75,11 +75,10 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit * // get it for free during the tree traversal, and it's used for listing // pages to display information about the newest commit for a given path. var treeCommit *Commit - var ok bool if treePath == "" { treeCommit = commit - } else if treeCommit, ok = revs[""]; ok { - treeCommit.repo = commit.repo + } else { + treeCommit = revs[""] } return commitsInfo, treeCommit, nil } @@ -104,9 +103,9 @@ func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cac } // GetLastCommitForPaths returns last commit information -func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) { +func GetLastCommitForPaths(ctx context.Context, gitRepo *Repository, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) { // We read backwards from the commit to obtain all of the commits - revs, err := WalkGitLog(ctx, commit.repo, commit, treePath, paths...) + revs, err := WalkGitLog(ctx, gitRepo, commit, treePath, paths...) if err != nil { return nil, err } @@ -126,7 +125,7 @@ func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string, continue } - c, err := commit.repo.GetCommit(commitID) // Ensure the commit exists in the repository + c, err := gitRepo.GetCommit(commitID) // Ensure the commit exists in the repository if err != nil { return nil, err } diff --git a/modules/git/commit_info_test.go b/modules/git/commit_info_test.go index 51e1551d2d32c..346a545e32465 100644 --- a/modules/git/commit_info_test.go +++ b/modules/git/commit_info_test.go @@ -62,10 +62,9 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) { continue } assert.NotNil(t, commit) - assert.NotNil(t, commit.Tree) - assert.NotNil(t, commit.Tree.repo) + assert.NotNil(t, commit.TreeID) - tree, err := commit.Tree.SubTree(testCase.Path) + tree, err := NewTree(repo1, commit.TreeID).SubTree(testCase.Path) if err != nil { assert.NoError(t, err, "Unable to get subtree: %s of commit: %s from testcase due to error: %v", testCase.Path, testCase.CommitID, err) // no point trying to do anything else for this test. @@ -83,7 +82,7 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) { } // FIXME: Context.TODO() - if graceful has started we should use its Shutdown context otherwise use install signals in TestMain. - commitsInfo, treeCommit, err := entries.GetCommitsInfo(t.Context(), "/any/repo-link", commit, testCase.Path) + commitsInfo, treeCommit, err := entries.GetCommitsInfo(t.Context(), repo1, "/any/repo-link", commit, testCase.Path) assert.NoError(t, err, "Unable to get commit information for entries of subtree: %s in commit: %s from testcase due to error: %v", testCase.Path, testCase.CommitID, err) if err != nil { t.FailNow() @@ -125,11 +124,12 @@ func TestEntries_GetCommitsInfo(t *testing.T) { t.Run("NonExistingSubmoduleAsNil", func(t *testing.T) { commit, err := bareRepo1.GetCommit("HEAD") require.NoError(t, err) - treeEntry, err := commit.GetTreeEntryByPath("file1.txt") + tree := NewTree(bareRepo1, commit.TreeID) + treeEntry, err := tree.GetTreeEntryByPath("file1.txt") require.NoError(t, err) - cisf, err := GetCommitInfoSubmoduleFile("/any/repo-link", "file1.txt", commit, treeEntry.ID) + cisf, err := GetCommitInfoSubmoduleFile(bareRepo1, "/any/repo-link", "file1.txt", commit.TreeID, treeEntry.ID) require.NoError(t, err) - assert.Equal(t, &CommitSubmoduleFile{ + assert.Equal(t, &SubmoduleFile{ repoLink: "/any/repo-link", fullPath: "file1.txt", refURL: "", @@ -170,14 +170,14 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) { if commit, err = repo.GetBranchCommit("master"); err != nil { b.Fatal(err) - } else if entries, err = commit.Tree.ListEntries(); err != nil { + } else if entries, err = NewTree(repo, commit.TreeID).ListEntries(); err != nil { b.Fatal(err) } entries.Sort() b.ResetTimer() b.Run(benchmark.name, func(b *testing.B) { for b.Loop() { - _, _, err := entries.GetCommitsInfo(b.Context(), "/any/repo-link", commit, "") + _, _, err := entries.GetCommitsInfo(b.Context(), repo, "/any/repo-link", commit, "") if err != nil { b.Fatal(err) } diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go index eb8f4c6322765..3879a889e9459 100644 --- a/modules/git/commit_reader.go +++ b/modules/git/commit_reader.go @@ -15,7 +15,7 @@ const ( commitHeaderGpgsigSha256 = "gpgsig-sha256" ) -func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, headerValue []byte) error { +func assignCommitFields(commit *Commit, headerKey string, headerValue []byte) error { if len(headerValue)> 0 && headerValue[len(headerValue)-1] == '\n' { headerValue = headerValue[:len(headerValue)-1] // remove trailing newline } @@ -25,7 +25,7 @@ func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, h if err != nil { return fmt.Errorf("invalid tree ID %q: %w", string(headerValue), err) } - commit.Tree = *NewTree(gitRepo, objID) + commit.TreeID = objID case "parent": objID, err := NewIDFromString(string(headerValue)) if err != nil { @@ -48,7 +48,7 @@ func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, h // We need this to interpret commits from cat-file or cat-file --batch // // If used as part of a cat-file --batch stream you need to limit the reader to the correct size -func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader) (*Commit, error) { +func CommitFromReader(objectID ObjectID, reader io.Reader) (*Commit, error) { commit := &Commit{ ID: objectID, Author: &Signature{}, @@ -74,7 +74,7 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader) k, v, _ := bytes.Cut(line, []byte{' '}) if len(k) != 0 || !inHeader { if headerKey != "" { - if err = assignCommitFields(gitRepo, commit, headerKey, headerValue); err != nil { + if err = assignCommitFields(commit, headerKey, headerValue); err != nil { return nil, fmt.Errorf("unable to parse commit %q: %w", objectID.String(), err) } } diff --git a/modules/git/commit_sha256_test.go b/modules/git/commit_sha256_test.go index 772f5eedb2798..2798959645fa3 100644 --- a/modules/git/commit_sha256_test.go +++ b/modules/git/commit_sha256_test.go @@ -92,7 +92,7 @@ signed commit` assert.NotNil(t, gitRepo) defer gitRepo.Close() - commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString)) + commitFromReader, err := CommitFromReader(sha, strings.NewReader(commitString)) assert.NoError(t, err) require.NotNil(t, commitFromReader) assert.EqualValues(t, sha, commitFromReader.ID) @@ -120,7 +120,7 @@ committer Adam Majer 1698676906 +0100 signed commit`, commitFromReader.Signature.Payload) assert.Equal(t, "Adam Majer ", commitFromReader.Author.String()) - commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n")) + commitFromReader2, err := CommitFromReader(sha, strings.NewReader(commitString+"\n\n")) assert.NoError(t, err) commitFromReader.CommitMessage += "\n\n" commitFromReader.Signature.Payload += "\n\n" @@ -145,15 +145,15 @@ func TestHasPreviousCommitSha256(t *testing.T) { assert.Equal(t, objectFormat, parentSHA.Type()) assert.Equal(t, "sha256", objectFormat.Name()) - haz, err := commit.HasPreviousCommit(parentSHA) + haz, err := repo.HasPreviousCommit(commit, parentSHA) assert.NoError(t, err) assert.True(t, haz) - hazNot, err := commit.HasPreviousCommit(notParentSHA) + hazNot, err := repo.HasPreviousCommit(commit, notParentSHA) assert.NoError(t, err) assert.False(t, hazNot) - selfNot, err := commit.HasPreviousCommit(commit.ID) + selfNot, err := repo.HasPreviousCommit(commit, commit.ID) assert.NoError(t, err) assert.False(t, selfNot) } diff --git a/modules/git/commit_submodule.go b/modules/git/commit_submodule.go deleted file mode 100644 index ff253b7ecab22..0000000000000 --- a/modules/git/commit_submodule.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package git - -type SubmoduleWebLink struct { - RepoWebLink, CommitWebLink string -} - -// GetSubModules get all the submodules of current revision git tree -func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) { - if c.submoduleCache != nil { - return c.submoduleCache, nil - } - - entry, err := c.GetTreeEntryByPath(".gitmodules") - if err != nil { - if _, ok := err.(ErrNotExist); ok { - return nil, nil - } - return nil, err - } - - rd, err := entry.Blob().DataAsync() - if err != nil { - return nil, err - } - defer rd.Close() - - // at the moment we do not strictly limit the size of the .gitmodules file because some users would have huge .gitmodules files (>1MB) - c.submoduleCache, err = configParseSubModules(rd) - if err != nil { - return nil, err - } - return c.submoduleCache, nil -} - -// GetSubModule gets the submodule by the entry name. -// It returns "nil, nil" if the submodule does not exist, caller should always remember to check the "nil" -func (c *Commit) GetSubModule(entryName string) (*SubModule, error) { - modules, err := c.GetSubModules() - if err != nil { - return nil, err - } - - if modules != nil { - if module, has := modules.Get(entryName); has { - return module, nil - } - } - return nil, nil -} diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index 688b4e294f5bb..6019d5ebbb605 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -88,7 +88,7 @@ empty commit` assert.NotNil(t, gitRepo) defer gitRepo.Close() - commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString)) + commitFromReader, err := CommitFromReader(sha, strings.NewReader(commitString)) assert.NoError(t, err) require.NotNil(t, commitFromReader) assert.EqualValues(t, sha, commitFromReader.ID) @@ -116,7 +116,7 @@ committer silverwind 1563741793 +0200 empty commit`, commitFromReader.Signature.Payload) assert.Equal(t, "silverwind ", commitFromReader.Author.String()) - commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n")) + commitFromReader2, err := CommitFromReader(sha, strings.NewReader(commitString+"\n\n")) assert.NoError(t, err) commitFromReader.CommitMessage += "\n\n" commitFromReader.Signature.Payload += "\n\n" @@ -152,7 +152,7 @@ ISO-8859-1` assert.NotNil(t, gitRepo) defer gitRepo.Close() - commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString)) + commitFromReader, err := CommitFromReader(sha, strings.NewReader(commitString)) assert.NoError(t, err) require.NotNil(t, commitFromReader) assert.EqualValues(t, sha, commitFromReader.ID) @@ -179,7 +179,7 @@ encoding ISO-8859-1 ISO-8859-1`, commitFromReader.Signature.Payload) assert.Equal(t, "KN4CK3R ", commitFromReader.Author.String()) - commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n")) + commitFromReader2, err := CommitFromReader(sha, strings.NewReader(commitString+"\n\n")) assert.NoError(t, err) commitFromReader.CommitMessage += "\n\n" commitFromReader.Signature.Payload += "\n\n" @@ -199,15 +199,15 @@ func TestHasPreviousCommit(t *testing.T) { parentSHA := MustIDFromString("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2") notParentSHA := MustIDFromString("2839944139e0de9737a044f78b0e4b40d989a9e3") - haz, err := commit.HasPreviousCommit(parentSHA) + haz, err := repo.HasPreviousCommit(commit, parentSHA) assert.NoError(t, err) assert.True(t, haz) - hazNot, err := commit.HasPreviousCommit(notParentSHA) + hazNot, err := repo.HasPreviousCommit(commit, notParentSHA) assert.NoError(t, err) assert.False(t, hazNot) - selfNot, err := commit.HasPreviousCommit(commit.ID) + selfNot, err := repo.HasPreviousCommit(commit, commit.ID) assert.NoError(t, err) assert.False(t, selfNot) } diff --git a/modules/git/diff.go b/modules/git/diff.go index 35d115be0e5da..1e955a8bf1cc1 100644 --- a/modules/git/diff.go +++ b/modules/git/diff.go @@ -64,7 +64,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff } else if commit.ParentCount() == 0 { cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...) } else { - c, err := commit.Parent(0) + c, err := repo.ParentCommit(commit, 0) if err != nil { return err } @@ -77,7 +77,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff } else if commit.ParentCount() == 0 { cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...) } else { - c, err := commit.Parent(0) + c, err := repo.ParentCommit(commit, 0) if err != nil { return err } diff --git a/modules/git/languagestats/language_stats_nogogit.go b/modules/git/languagestats/language_stats_nogogit.go index 94cf9fff8c129..7ef52244ba5a8 100644 --- a/modules/git/languagestats/language_stats_nogogit.go +++ b/modules/git/languagestats/language_stats_nogogit.go @@ -48,7 +48,7 @@ func GetLanguageStats(repo *git.Repository, commitID string) (map[string]int64, return nil, git.ErrNotExist{ID: commitID} } - commit, err := git.CommitFromReader(repo, sha, io.LimitReader(batchReader, size)) + commit, err := git.CommitFromReader(sha, io.LimitReader(batchReader, size)) if err != nil { log.Debug("Unable to get commit for: %s. Err: %v", commitID, err) return nil, err @@ -57,7 +57,7 @@ func GetLanguageStats(repo *git.Repository, commitID string) (map[string]int64, return nil, err } - tree := commit.Tree + tree := git.NewTree(repo, commit.TreeID) entries, err := tree.ListEntriesRecursiveWithSize() if err != nil { diff --git a/modules/git/last_commit_cache_gogit.go b/modules/git/last_commit_cache_gogit.go index 3afc213094225..10666f965435c 100644 --- a/modules/git/last_commit_cache_gogit.go +++ b/modules/git/last_commit_cache_gogit.go @@ -13,21 +13,21 @@ import ( ) // CacheCommit will cache the commit from the gitRepository -func (c *Commit) CacheCommit(ctx context.Context) error { - if c.repo.LastCommitCache == nil { +func (repo *Repository) CacheCommit(ctx context.Context, c *Commit) error { + if repo.LastCommitCache == nil { return nil } - commitNodeIndex, _ := c.repo.CommitNodeIndex() + commitNodeIndex, _ := repo.CommitNodeIndex() index, err := commitNodeIndex.Get(plumbing.Hash(c.ID.RawValue())) if err != nil { return err } - return c.recursiveCache(ctx, index, &c.Tree, "", 1) + return repo.recursiveCache(ctx, c, index, NewTree(repo, c.TreeID), "", 1) } -func (c *Commit) recursiveCache(ctx context.Context, index cgobject.CommitNode, tree *Tree, treePath string, level int) error { +func (repo *Repository) recursiveCache(ctx context.Context, c *Commit, index cgobject.CommitNode, tree *Tree, treePath string, level int) error { if level == 0 { return nil } @@ -44,7 +44,7 @@ func (c *Commit) recursiveCache(ctx context.Context, index cgobject.CommitNode, entryMap[entry.Name()] = entry } - commits, err := GetLastCommitForPaths(ctx, c.repo.LastCommitCache, index, treePath, entryPaths) + commits, err := GetLastCommitForPaths(ctx, repo.LastCommitCache, index, treePath, entryPaths) if err != nil { return err } @@ -55,7 +55,7 @@ func (c *Commit) recursiveCache(ctx context.Context, index cgobject.CommitNode, if err != nil { return err } - if err := c.recursiveCache(ctx, index, subTree, entry, level-1); err != nil { + if err := repo.recursiveCache(ctx, c, index, subTree, entry, level-1); err != nil { return err } } diff --git a/modules/git/last_commit_cache_nogogit.go b/modules/git/last_commit_cache_nogogit.go index 155cb3cb7c85c..46d935a282cbf 100644 --- a/modules/git/last_commit_cache_nogogit.go +++ b/modules/git/last_commit_cache_nogogit.go @@ -10,14 +10,14 @@ import ( ) // CacheCommit will cache the commit from the gitRepository -func (c *Commit) CacheCommit(ctx context.Context) error { - if c.repo.LastCommitCache == nil { +func (repo *Repository) CacheCommit(ctx context.Context, c *Commit) error { + if repo.LastCommitCache == nil { return nil } - return c.recursiveCache(ctx, &c.Tree, "", 1) + return repo.recursiveCache(ctx, c, NewTree(repo, c.TreeID), "", 1) } -func (c *Commit) recursiveCache(ctx context.Context, tree *Tree, treePath string, level int) error { +func (repo *Repository) recursiveCache(ctx context.Context, c *Commit, tree *Tree, treePath string, level int) error { if level == 0 { return nil } @@ -32,7 +32,7 @@ func (c *Commit) recursiveCache(ctx context.Context, tree *Tree, treePath string entryPaths[i] = entry.Name() } - _, err = WalkGitLog(ctx, c.repo, c, treePath, entryPaths...) + _, err = WalkGitLog(ctx, repo, c, treePath, entryPaths...) if err != nil { return err } @@ -44,7 +44,7 @@ func (c *Commit) recursiveCache(ctx context.Context, tree *Tree, treePath string if err != nil { return err } - if err := c.recursiveCache(ctx, subTree, treeEntry.Name(), level-1); err != nil { + if err := repo.recursiveCache(ctx, c, subTree, treeEntry.Name(), level-1); err != nil { return err } } diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index dfdef38ef9d86..896d34ee58377 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -298,7 +298,7 @@ func (g *LogNameStatusRepoParser) Close() { func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath string, paths ...string) (map[string]string, error) { headRef := head.ID.String() - tree, err := head.SubTree(treepath) + tree, err := NewTree(repo, head.TreeID).SubTree(treepath) if err != nil { return nil, err } diff --git a/modules/git/notes_gogit.go b/modules/git/notes_gogit.go index f802443b00e5b..cb38cf968dc8f 100644 --- a/modules/git/notes_gogit.go +++ b/modules/git/notes_gogit.go @@ -30,7 +30,13 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) remainingCommitID := commitID path := "" - currentTree := notes.Tree.gogitTree + tree := NewTree(repo, notes.TreeID) + if err := tree.loadTreeObject(); err != nil { + log.Error("Unable to get git tree for notes commit %q. Error: %v", notes.ID, err) + return err + } + currentTree := tree.gogitTree + log.Trace("Found tree with ID %q while searching for git note corresponding to the commit %q", currentTree.Entries[0].Name, commitID) var file *object.File for len(remainingCommitID)> 2 { diff --git a/modules/git/notes_nogogit.go b/modules/git/notes_nogogit.go index 4da375c3211a3..287d871689f7a 100644 --- a/modules/git/notes_nogogit.go +++ b/modules/git/notes_nogogit.go @@ -28,7 +28,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) path := "" - tree := ¬es.Tree + tree := NewTree(repo, notes.TreeID) log.Trace("Found tree with ID %q while searching for git note corresponding to the commit %q", tree.ID, commitID) var entry *TreeEntry @@ -80,7 +80,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) path = path[idx+1:] } - lastCommits, err := GetLastCommitForPaths(ctx, notes, treePath, []string{path}) + lastCommits, err := GetLastCommitForPaths(ctx, repo, notes, treePath, []string{path}) if err != nil { log.Error("Unable to get the commit for the path %q. Error: %v", treePath, err) return err diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go index c5eed737011a6..e7b7ff8ed8469 100644 --- a/modules/git/pipeline/lfs_nogogit.go +++ b/modules/git/pipeline/lfs_nogogit.go @@ -99,7 +99,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err continue case "commit": // Read in the commit to get its tree and in case this is one of the last used commits - curCommit, err = git.CommitFromReader(repo, git.MustIDFromString(string(commitID)), io.LimitReader(batchReader, size)) + curCommit, err = git.CommitFromReader(git.MustIDFromString(string(commitID)), io.LimitReader(batchReader, size)) if err != nil { return nil, err } @@ -107,7 +107,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err return nil, err } - if _, err := batchStdinWriter.Write([]byte(curCommit.Tree.ID.String() + "\n")); err != nil { + if _, err := batchStdinWriter.Write([]byte(curCommit.TreeID.String() + "\n")); err != nil { return nil, err } curPath = "" diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 4066a1ca7ba1f..c370cd35904c7 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -6,8 +6,10 @@ package git import ( "bytes" + "errors" "io" "os" + "os/exec" "strconv" "strings" @@ -73,7 +75,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com } // GetCommitByPath returns the last commit of relative path. -func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { +func (repo *Repository) GetCommitByPathDefaultBranch(relpath string) (*Commit, error) { stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr @@ -89,8 +91,8 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { return commits[0], nil } -// commitsByRangeWithTime returns the specific page commits before current revision, with not, since, until support -func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, not, since, until string) ([]*Commit, error) { +// CommitsByRangeWithTime returns the specific page commits before current revision, with not, since, until support +func (repo *Repository) CommitsByRangeWithTime(id ObjectID, page, pageSize int, not, since, until string) ([]*Commit, error) { cmd := NewCommand("log"). AddOptionFormat("--skip=%d", (page-1)*pageSize). AddOptionFormat("--max-count=%d", pageSize). @@ -115,7 +117,8 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, return repo.parsePrettyFormatLogToList(stdout) } -func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([]*Commit, error) { +// SearchCommits returns the commits match the keyword before current revision +func (repo *Repository) SearchCommits(id ObjectID, opts SearchCommitsOptions) ([]*Commit, error) { // add common arguments to git command addCommonSearchArgs := func(c *Command) { // ignore case @@ -443,11 +446,13 @@ func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) return commits, nil } -func (repo *Repository) getCommitsBefore(id ObjectID) ([]*Commit, error) { +// CommitsBefore returns all the commits before current revision +func (repo *Repository) CommitsBefore(id ObjectID) ([]*Commit, error) { return repo.commitsBefore(id, 0) } -func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit, error) { +// CommitsBeforeLimit returns num commits before current revision +func (repo *Repository) CommitsBeforeLimit(id ObjectID, num int) ([]*Commit, error) { return repo.commitsBefore(id, num) } @@ -520,11 +525,7 @@ func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err e func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error { if repo.LastCommitCache == nil { commitsCount, err := cache.GetInt64(cacheKey, func() (int64, error) { - commit, err := repo.GetCommit(sha) - if err != nil { - return 0, err - } - return commit.CommitsCount() + return repo.CommitsCount(sha) }) if err != nil { return err @@ -565,3 +566,103 @@ func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID s return "", nil } + +// Parent returns n-th parent (0-based index) of the commit. +func (repo *Repository) ParentCommit(c *Commit, n int) (*Commit, error) { + id, err := c.ParentID(n) + if err != nil { + return nil, err + } + parent, err := repo.getCommit(id) + if err != nil { + return nil, err + } + return parent, nil +} + +// GetCommitByPath return the commit of relative path object. +func (repo *Repository) GetCommitByPath(commitID ObjectID, relpath string) (*Commit, error) { + if repo.LastCommitCache != nil { + return repo.LastCommitCache.GetCommitByPath(commitID.String(), relpath) + } + return repo.getCommitByPathWithID(commitID, relpath) +} + +// CommitsCount returns number of total commits of until current revision. +func (repo *Repository) CommitsCount(commitID string) (int64, error) { + return CommitsCount(repo.Ctx, CommitsCountOptions{ + RepoPath: repo.Path, + Revision: []string{commitID}, + }) +} + +// HasPreviousCommit returns true if a given commitHash is contained in commit's parents +func (repo *Repository) HasPreviousCommit(c *Commit, objectID ObjectID) (bool, error) { + this := c.ID.String() + that := objectID.String() + + if this == that { + return false, nil + } + + _, _, err := NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this). + RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + if err == nil { + return true, nil + } + var exitError *exec.ExitError + if errors.As(err, &exitError) { + if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 { + return false, nil + } + } + return false, err +} + +// IsForcePush returns true if a push from oldCommitHash to this is a force push +func (repo *Repository) IsForcePush(c *Commit, oldCommitID string) (bool, error) { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return false, err + } + if oldCommitID == objectFormat.EmptyObjectID().String() { + return false, nil + } + + oldCommit, err := repo.GetCommit(oldCommitID) + if err != nil { + return false, err + } + hasPreviousCommit, err := repo.HasPreviousCommit(c, oldCommit.ID) + return !hasPreviousCommit, err +} + +// CommitsBeforeUntil returns the commits between commitID to current revision +func (repo *Repository) CommitsBeforeUntil(c *Commit, commitID string) ([]*Commit, error) { + endCommit, err := repo.GetCommit(commitID) + if err != nil { + return nil, err + } + return repo.CommitsBetween(c, endCommit) +} + +// GetClosestBranchName gets the closest branch name (as returned by 'git name-rev --name-only') +func (repo *Repository) GetClosestBranchName(c *Commit) (string, error) { + cmd := NewCommand("name-rev") + if DefaultFeatures().CheckVersionAtLeast("2.13.0") { + cmd.AddArguments("--exclude", "refs/tags/*") + } + cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) + data, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + if err != nil { + // handle special case where git can not describe commit + if strings.Contains(err.Error(), "cannot describe") { + return "", nil + } + + return "", err + } + + // name-rev commitID output will be "master" or "master~12" + return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil +} diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go index a88902e209dc6..af96de251ebef 100644 --- a/modules/git/repo_commit_gogit.go +++ b/modules/git/repo_commit_gogit.go @@ -103,16 +103,5 @@ func (repo *Repository) getCommit(id ObjectID) (*Commit, error) { return nil, err } - commit := convertCommit(gogitCommit) - commit.repo = repo - - tree, err := gogitCommit.Tree() - if err != nil { - return nil, err - } - - commit.Tree.ID = ParseGogitHash(tree.Hash) - commit.Tree.gogitTree = tree - - return commit, nil + return convertCommit(gogitCommit), nil } diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go index 3ead3e22165f4..18d2a7883b0a3 100644 --- a/modules/git/repo_commit_nogogit.go +++ b/modules/git/repo_commit_nogogit.go @@ -123,7 +123,7 @@ func (repo *Repository) getCommitFromBatchReader(wr WriteCloserError, rd *bufio. return commit, nil case "commit": - commit, err := CommitFromReader(repo, id, io.LimitReader(rd, size)) + commit, err := CommitFromReader(id, io.LimitReader(rd, size)) if err != nil { return nil, err } diff --git a/modules/git/repo_tree_nogogit.go b/modules/git/repo_tree_nogogit.go index 1954f8516219d..d154d6d30dcb4 100644 --- a/modules/git/repo_tree_nogogit.go +++ b/modules/git/repo_tree_nogogit.go @@ -43,18 +43,20 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { if err != nil { return nil, err } - commit.Tree.ResolvedID = resolvedID - return &commit.Tree, nil + tree := NewTree(repo, commit.TreeID) + tree.ResolvedID = resolvedID + return tree, nil case "commit": - commit, err := CommitFromReader(repo, id, io.LimitReader(rd, size)) + commit, err := CommitFromReader(id, io.LimitReader(rd, size)) if err != nil { return nil, err } if _, err := rd.Discard(1); err != nil { return nil, err } - commit.Tree.ResolvedID = commit.ID - return &commit.Tree, nil + tree := NewTree(repo, commit.TreeID) + tree.ResolvedID = commit.ID + return tree, nil case "tree": tree := NewTree(repo, id) tree.ResolvedID = id diff --git a/modules/git/submodule.go b/modules/git/submodule.go index 31a32f1a9e2ef..c8d8d9379f963 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -3,64 +3,50 @@ package git -import ( - "bufio" - "context" - "fmt" - "os" - - "code.gitea.io/gitea/modules/log" -) - -type TemplateSubmoduleCommit struct { - Path string - Commit string +type SubmoduleWebLink struct { + RepoWebLink, CommitWebLink string } -// GetTemplateSubmoduleCommits returns a list of submodules paths and their commits from a repository -// This function is only for generating new repos based on existing template, the template couldn't be too large. -func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submoduleCommits []TemplateSubmoduleCommit, _ error) { - stdoutReader, stdoutWriter, err := os.Pipe() +// GetSubModules get all the submodules of current revision git tree +func (t *Tree) GetSubModules() (*ObjectCache[*SubModule], error) { + if t.submoduleCache != nil { + return t.submoduleCache, nil + } + + entry, err := t.GetTreeEntryByPath(".gitmodules") if err != nil { + if _, ok := err.(ErrNotExist); ok { + return nil, nil + } return nil, err } - opts := &RunOpts{ - Dir: repoPath, - Stdout: stdoutWriter, - PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { - _ = stdoutWriter.Close() - defer stdoutReader.Close() - scanner := bufio.NewScanner(stdoutReader) - for scanner.Scan() { - entry, err := parseLsTreeLine(scanner.Bytes()) - if err != nil { - cancel() - return err - } - if entry.EntryMode == EntryModeCommit { - submoduleCommits = append(submoduleCommits, TemplateSubmoduleCommit{Path: entry.Name, Commit: entry.ID.String()}) - } - } - return scanner.Err() - }, + rd, err := entry.Blob().DataAsync() + if err != nil { + return nil, err } - err = NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts) + defer rd.Close() + + // at the moment we do not strictly limit the size of the .gitmodules file because some users would have huge .gitmodules files (>1MB) + t.submoduleCache, err = configParseSubModules(rd) if err != nil { - return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err) + return nil, err } - return submoduleCommits, nil + return t.submoduleCache, nil } -// AddTemplateSubmoduleIndexes Adds the given submodules to the git index. -// It is only for generating new repos based on existing template, requires the .gitmodules file to be already present in the work dir. -func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error { - for _, submodule := range submodules { - cmd := NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path) - if stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil { - log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err) - return err +// GetSubModule gets the submodule by the entry name. +// It returns "nil, nil" if the submodule does not exist, caller should always remember to check the "nil" +func (t *Tree) GetSubModule(entryName string) (*SubModule, error) { + modules, err := t.GetSubModules() + if err != nil { + return nil, err + } + + if modules != nil { + if module, has := modules.Get(entryName); has { + return module, nil } } - return nil + return nil, nil } diff --git a/modules/git/commit_submodule_file.go b/modules/git/submodule_file.go similarity index 68% rename from modules/git/commit_submodule_file.go rename to modules/git/submodule_file.go index efcf53b07c867..d33dee099dd84 100644 --- a/modules/git/commit_submodule_file.go +++ b/modules/git/submodule_file.go @@ -13,8 +13,8 @@ import ( "code.gitea.io/gitea/modules/util" ) -// CommitSubmoduleFile represents a file with submodule type. -type CommitSubmoduleFile struct { +// SubmoduleFile represents a file with submodule type. +type SubmoduleFile struct { repoLink string fullPath string refURL string @@ -24,20 +24,20 @@ type CommitSubmoduleFile struct { parsedTargetLink string } -// NewCommitSubmoduleFile create a new submodule file -func NewCommitSubmoduleFile(repoLink, fullPath, refURL, refID string) *CommitSubmoduleFile { - return &CommitSubmoduleFile{repoLink: repoLink, fullPath: fullPath, refURL: refURL, refID: refID} +// NewSubmoduleFile create a new submodule file +func NewSubmoduleFile(repoLink, fullPath, refURL, refID string) *SubmoduleFile { + return &SubmoduleFile{repoLink: repoLink, fullPath: fullPath, refURL: refURL, refID: refID} } // RefID returns the commit ID of the submodule, it returns empty string for nil receiver -func (sf *CommitSubmoduleFile) RefID() string { +func (sf *SubmoduleFile) RefID() string { if sf == nil { return "" } return sf.refID } -func (sf *CommitSubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreLinkPath string) *SubmoduleWebLink { +func (sf *SubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreLinkPath string) *SubmoduleWebLink { if sf == nil || sf.refURL == "" { return nil } @@ -58,12 +58,12 @@ func (sf *CommitSubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreL // SubmoduleWebLinkTree tries to make the submodule's tree link in its own repo, it also works on "nil" receiver // It returns nil if the submodule does not have a valid URL or is nil -func (sf *CommitSubmoduleFile) SubmoduleWebLinkTree(ctx context.Context, optCommitID ...string) *SubmoduleWebLink { +func (sf *SubmoduleFile) SubmoduleWebLinkTree(ctx context.Context, optCommitID ...string) *SubmoduleWebLink { return sf.getWebLinkInTargetRepo(ctx, "/tree/"+util.OptionalArg(optCommitID, sf.RefID())) } // SubmoduleWebLinkCompare tries to make the submodule's compare link in its own repo, it also works on "nil" receiver // It returns nil if the submodule does not have a valid URL or is nil -func (sf *CommitSubmoduleFile) SubmoduleWebLinkCompare(ctx context.Context, commitID1, commitID2 string) *SubmoduleWebLink { +func (sf *SubmoduleFile) SubmoduleWebLinkCompare(ctx context.Context, commitID1, commitID2 string) *SubmoduleWebLink { return sf.getWebLinkInTargetRepo(ctx, "/compare/"+commitID1+"..."+commitID2) } diff --git a/modules/git/commit_submodule_file_test.go b/modules/git/submodule_file_test.go similarity index 60% rename from modules/git/commit_submodule_file_test.go rename to modules/git/submodule_file_test.go index 33fe1464446c6..79e4f66be81ea 100644 --- a/modules/git/commit_submodule_file_test.go +++ b/modules/git/submodule_file_test.go @@ -9,14 +9,14 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCommitSubmoduleLink(t *testing.T) { - assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkTree(t.Context())) - assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkCompare(t.Context(), "", "")) - assert.Nil(t, (&CommitSubmoduleFile{}).SubmoduleWebLinkTree(t.Context())) - assert.Nil(t, (&CommitSubmoduleFile{}).SubmoduleWebLinkCompare(t.Context(), "", "")) +func TestSubmoduleLink(t *testing.T) { + assert.Nil(t, (*SubmoduleFile)(nil).SubmoduleWebLinkTree(t.Context())) + assert.Nil(t, (*SubmoduleFile)(nil).SubmoduleWebLinkCompare(t.Context(), "", "")) + assert.Nil(t, (&SubmoduleFile{}).SubmoduleWebLinkTree(t.Context())) + assert.Nil(t, (&SubmoduleFile{}).SubmoduleWebLinkCompare(t.Context(), "", "")) t.Run("GitHubRepo", func(t *testing.T) { - sf := NewCommitSubmoduleFile("/any/repo-link", "full-path", "git@github.com:user/repo.git", "aaaa") + sf := NewSubmoduleFile("/any/repo-link", "full-path", "git@github.com:user/repo.git", "aaaa") wl := sf.SubmoduleWebLinkTree(t.Context()) assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink) assert.Equal(t, "https://github.com/user/repo/tree/aaaa", wl.CommitWebLink) @@ -27,12 +27,12 @@ func TestCommitSubmoduleLink(t *testing.T) { }) t.Run("RelativePath", func(t *testing.T) { - sf := NewCommitSubmoduleFile("/subpath/any/repo-home-link", "full-path", "../../user/repo", "aaaa") + sf := NewSubmoduleFile("/subpath/any/repo-home-link", "full-path", "../../user/repo", "aaaa") wl := sf.SubmoduleWebLinkTree(t.Context()) assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink) assert.Equal(t, "/subpath/user/repo/tree/aaaa", wl.CommitWebLink) - sf = NewCommitSubmoduleFile("/subpath/any/repo-home-link", "dir/submodule", "../../user/repo", "aaaa") + sf = NewSubmoduleFile("/subpath/any/repo-home-link", "dir/submodule", "../../user/repo", "aaaa") wl = sf.SubmoduleWebLinkCompare(t.Context(), "1111", "2222") assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink) assert.Equal(t, "/subpath/user/repo/compare/1111...2222", wl.CommitWebLink) diff --git a/modules/git/submodule_template.go b/modules/git/submodule_template.go new file mode 100644 index 0000000000000..31a32f1a9e2ef --- /dev/null +++ b/modules/git/submodule_template.go @@ -0,0 +1,66 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "bufio" + "context" + "fmt" + "os" + + "code.gitea.io/gitea/modules/log" +) + +type TemplateSubmoduleCommit struct { + Path string + Commit string +} + +// GetTemplateSubmoduleCommits returns a list of submodules paths and their commits from a repository +// This function is only for generating new repos based on existing template, the template couldn't be too large. +func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submoduleCommits []TemplateSubmoduleCommit, _ error) { + stdoutReader, stdoutWriter, err := os.Pipe() + if err != nil { + return nil, err + } + opts := &RunOpts{ + Dir: repoPath, + Stdout: stdoutWriter, + PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { + _ = stdoutWriter.Close() + defer stdoutReader.Close() + + scanner := bufio.NewScanner(stdoutReader) + for scanner.Scan() { + entry, err := parseLsTreeLine(scanner.Bytes()) + if err != nil { + cancel() + return err + } + if entry.EntryMode == EntryModeCommit { + submoduleCommits = append(submoduleCommits, TemplateSubmoduleCommit{Path: entry.Name, Commit: entry.ID.String()}) + } + } + return scanner.Err() + }, + } + err = NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts) + if err != nil { + return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err) + } + return submoduleCommits, nil +} + +// AddTemplateSubmoduleIndexes Adds the given submodules to the git index. +// It is only for generating new repos based on existing template, requires the .gitmodules file to be already present in the work dir. +func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error { + for _, submodule := range submodules { + cmd := NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path) + if stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil { + log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err) + return err + } + } + return nil +} diff --git a/modules/git/submodule_test.go b/modules/git/submodule_template_test.go similarity index 100% rename from modules/git/submodule_test.go rename to modules/git/submodule_template_test.go diff --git a/modules/git/tree.go b/modules/git/tree.go index 38fb45f3b1149..25e29bca3402b 100644 --- a/modules/git/tree.go +++ b/modules/git/tree.go @@ -6,7 +6,10 @@ package git import ( "bytes" + "io" "strings" + + "code.gitea.io/gitea/modules/util" ) // NewTree create a new tree according the repository and tree id @@ -73,3 +76,32 @@ func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Comm } return repo.GetCommit(strings.TrimSpace(stdout)) } + +// GetFileContent reads a file content as a string or returns false if this was not possible +func (t *Tree) GetFileContent(filename string, limit int) (string, error) { + entry, err := t.GetTreeEntryByPath(filename) + if err != nil { + return "", err + } + + r, err := entry.Blob().DataAsync() + if err != nil { + return "", err + } + defer r.Close() + + if limit> 0 { + bs := make([]byte, limit) + n, err := util.ReadAtMost(r, bs) + if err != nil { + return "", err + } + return string(bs[:n]), nil + } + + bytes, err := io.ReadAll(r) + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go index 5099d8ee79bc3..e9821cd52239f 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -30,7 +30,7 @@ type EntryFollowResult struct { TargetEntry *TreeEntry } -func EntryFollowLink(commit *Commit, fullPath string, te *TreeEntry) (*EntryFollowResult, error) { +func EntryFollowLink(tree *Tree, fullPath string, te *TreeEntry) (*EntryFollowResult, error) { if !te.IsLink() { return nil, util.ErrorWrap(util.ErrUnprocessableContent, "%q is not a symlink", fullPath) } @@ -51,18 +51,18 @@ func EntryFollowLink(commit *Commit, fullPath string, te *TreeEntry) (*EntryFoll } targetFullPath := path.Join(path.Dir(fullPath), link) - targetEntry, err := commit.GetTreeEntryByPath(targetFullPath) + targetEntry, err := tree.GetTreeEntryByPath(targetFullPath) if err != nil { return &EntryFollowResult{SymlinkContent: link}, err } return &EntryFollowResult{SymlinkContent: link, TargetFullPath: targetFullPath, TargetEntry: targetEntry}, nil } -func EntryFollowLinks(commit *Commit, firstFullPath string, firstTreeEntry *TreeEntry, optLimit ...int) (res *EntryFollowResult, err error) { +func EntryFollowLinks(tree *Tree, firstFullPath string, firstTreeEntry *TreeEntry, optLimit ...int) (res *EntryFollowResult, err error) { limit := util.OptionalArg(optLimit, 10) treeEntry, fullPath := firstTreeEntry, firstFullPath for range limit { - res, err = EntryFollowLink(commit, fullPath, treeEntry) + res, err = EntryFollowLink(tree, fullPath, treeEntry) if err != nil { return res, err } diff --git a/modules/git/tree_entry_common_test.go b/modules/git/tree_entry_common_test.go index 8e20ee56ff6ac..d9a668ef1aa87 100644 --- a/modules/git/tree_entry_common_test.go +++ b/modules/git/tree_entry_common_test.go @@ -20,15 +20,17 @@ func TestFollowLink(t *testing.T) { commit, err := r.GetCommit("37991dec2c8e592043f47155ce4808d4580f9123") require.NoError(t, err) + tree := NewTree(r, commit.TreeID) + // get the symlink { lnkFullPath := "foo/bar/link_to_hello" - lnk, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello") + lnk, err := tree.GetTreeEntryByPath("foo/bar/link_to_hello") require.NoError(t, err) assert.True(t, lnk.IsLink()) // should be able to dereference to target - res, err := EntryFollowLink(commit, lnkFullPath, lnk) + res, err := EntryFollowLink(tree, lnkFullPath, lnk) require.NoError(t, err) assert.Equal(t, "hello", res.TargetEntry.Name()) assert.Equal(t, "foo/nar/hello", res.TargetFullPath) @@ -38,38 +40,38 @@ func TestFollowLink(t *testing.T) { { // should error when called on a normal file - entry, err := commit.Tree.GetTreeEntryByPath("file1.txt") + entry, err := tree.GetTreeEntryByPath("file1.txt") require.NoError(t, err) - res, err := EntryFollowLink(commit, "file1.txt", entry) + res, err := EntryFollowLink(tree, "file1.txt", entry) assert.ErrorIs(t, err, util.ErrUnprocessableContent) assert.Nil(t, res) } { // should error for broken links - entry, err := commit.Tree.GetTreeEntryByPath("foo/broken_link") + entry, err := tree.GetTreeEntryByPath("foo/broken_link") require.NoError(t, err) assert.True(t, entry.IsLink()) - res, err := EntryFollowLink(commit, "foo/broken_link", entry) + res, err := EntryFollowLink(tree, "foo/broken_link", entry) assert.ErrorIs(t, err, util.ErrNotExist) assert.Equal(t, "nar/broken_link", res.SymlinkContent) } { // should error for external links - entry, err := commit.Tree.GetTreeEntryByPath("foo/outside_repo") + entry, err := tree.GetTreeEntryByPath("foo/outside_repo") require.NoError(t, err) assert.True(t, entry.IsLink()) - res, err := EntryFollowLink(commit, "foo/outside_repo", entry) + res, err := EntryFollowLink(tree, "foo/outside_repo", entry) assert.ErrorIs(t, err, util.ErrNotExist) assert.Equal(t, "../../outside_repo", res.SymlinkContent) } { // testing fix for short link bug - entry, err := commit.Tree.GetTreeEntryByPath("foo/link_short") + entry, err := tree.GetTreeEntryByPath("foo/link_short") require.NoError(t, err) - res, err := EntryFollowLink(commit, "foo/link_short", entry) + res, err := EntryFollowLink(tree, "foo/link_short", entry) assert.ErrorIs(t, err, util.ErrNotExist) assert.Equal(t, "a", res.SymlinkContent) } diff --git a/modules/git/tree_entry_gogit.go b/modules/git/tree_entry_gogit.go index e6845f1c776fe..1102abc670556 100644 --- a/modules/git/tree_entry_gogit.go +++ b/modules/git/tree_entry_gogit.go @@ -7,6 +7,8 @@ package git import ( + "code.gitea.io/gitea/modules/log" + "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/object" @@ -41,6 +43,11 @@ func (te *TreeEntry) Size() int64 { return te.size } + if err := te.ptree.loadTreeObject(); err != nil { + log.Error("Unable to load tree object: %v", err) + return 0 + } + file, err := te.ptree.gogitTree.TreeEntryFile(te.gogitTreeEntry) if err != nil { return 0 diff --git a/modules/git/tree_gogit.go b/modules/git/tree_gogit.go index 272b018ffdd18..dac00c4e67101 100644 --- a/modules/git/tree_gogit.go +++ b/modules/git/tree_gogit.go @@ -23,9 +23,14 @@ type Tree struct { // parent tree ptree *Tree + + submoduleCache *ObjectCache[*SubModule] } func (t *Tree) loadTreeObject() error { + if t.gogitTree != nil { + return nil + } gogitTree, err := t.repo.gogitRepo.TreeObject(plumbing.Hash(t.ID.RawValue())) if err != nil { return err @@ -37,11 +42,8 @@ func (t *Tree) loadTreeObject() error { // ListEntries returns all entries of current tree. func (t *Tree) ListEntries() (Entries, error) { - if t.gogitTree == nil { - err := t.loadTreeObject() - if err != nil { - return nil, err - } + if err := t.loadTreeObject(); err != nil { + return nil, err } entries := make([]*TreeEntry, len(t.gogitTree.Entries)) @@ -58,11 +60,9 @@ func (t *Tree) ListEntries() (Entries, error) { // ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) { - if t.gogitTree == nil { - err := t.loadTreeObject() - if err != nil { - return nil, err - } + err := t.loadTreeObject() + if err != nil { + return nil, err } var entries []*TreeEntry diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go index f88788418e27d..531cfbee3a25b 100644 --- a/modules/git/tree_nogogit.go +++ b/modules/git/tree_nogogit.go @@ -24,6 +24,8 @@ type Tree struct { entriesRecursive Entries entriesRecursiveParsed bool + + submoduleCache *ObjectCache[*SubModule] } // ListEntries returns all entries of current tree. diff --git a/modules/git/tree_test.go b/modules/git/tree_test.go index 67f95fe74894c..44b9d3e8721b4 100644 --- a/modules/git/tree_test.go +++ b/modules/git/tree_test.go @@ -18,9 +18,11 @@ func TestSubTree_Issue29101(t *testing.T) { commit, err := repo.GetCommit("ce064814f4a0d337b333e646ece456cd39fab612") assert.NoError(t, err) + tree := NewTree(repo, commit.TreeID) + // old code could produce a different error if called multiple times for range 10 { - _, err = commit.SubTree("file1.txt") + _, err = tree.SubTree("file1.txt") assert.Error(t, err) assert.True(t, IsErrNotExist(err)) } diff --git a/modules/issue/template/unmarshal.go b/modules/issue/template/unmarshal.go index 1d8e9dd02d995..f4d8c55e7a497 100644 --- a/modules/issue/template/unmarshal.go +++ b/modules/issue/template/unmarshal.go @@ -47,8 +47,8 @@ func UnmarshalFromEntry(entry *git.TreeEntry, dir string) (*api.IssueTemplate, e } // UnmarshalFromCommit parses out a valid template from the commit -func UnmarshalFromCommit(commit *git.Commit, filename string) (*api.IssueTemplate, error) { - entry, err := commit.GetTreeEntryByPath(filename) +func UnmarshalFromCommit(tree *git.Tree, filename string) (*api.IssueTemplate, error) { + entry, err := tree.GetTreeEntryByPath(filename) if err != nil { return nil, fmt.Errorf("get entry for %q: %w", filename, err) } @@ -62,7 +62,7 @@ func UnmarshalFromRepo(repo *git.Repository, branch, filename string) (*api.Issu return nil, fmt.Errorf("get commit on branch %q: %w", branch, err) } - return UnmarshalFromCommit(commit, filename) + return UnmarshalFromCommit(git.NewTree(repo, commit.TreeID), filename) } func unmarshalFromEntry(entry *git.TreeEntry, filename string) (*api.IssueTemplate, error) { diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 65fac45aa11f2..7794975ee837a 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -80,7 +80,7 @@ func GetBranch(ctx *context.APIContext) { return } - br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branchName, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) + br, err := convert.ToBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) if err != nil { ctx.APIErrorInternal(err) return @@ -271,7 +271,7 @@ func CreateBranch(ctx *context.APIContext) { return } - br, err := convert.ToBranch(ctx, ctx.Repo.Repository, opt.BranchName, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) + br, err := convert.ToBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, opt.BranchName, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) if err != nil { ctx.APIErrorInternal(err) return @@ -366,7 +366,7 @@ func ListBranches(ctx *context.APIContext) { } branchProtection := rules.GetFirstMatched(branches[i].Name) - apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i].Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) + apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, branches[i].Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 6a93be624f615..dce4791638a8c 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -235,7 +235,7 @@ func GetAllCommits(ctx *context.APIContext) { } // Query commits - commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not, since, until) + commits, err = ctx.Repo.GitRepo.CommitsByRangeWithTime(baseCommit.ID, listOptions.Page, listOptions.PageSize, not, since, until) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index a85dda79d003f..e52b73cf6f13c 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -228,7 +228,8 @@ func GetRawFileOrLFS(ctx *context.APIContext) { } func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified *time.Time) { - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + entry, err := tree.GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { if git.IsErrNotExist(err) { ctx.APIErrorNotFound() diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index ac47e15d645c3..20fdab7aee86c 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -174,7 +174,7 @@ func TestHook(ctx *context.APIContext) { return } - commit := convert.ToPayloadCommit(ctx, ctx.Repo.Repository, ctx.Repo.Commit) + commit := convert.ToPayloadCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.Commit) commitID := ctx.Repo.Commit.ID.String() if err := webhook_service.PrepareWebhook(ctx, hook, webhook_module.HookEventPush, &api.PushPayload{ diff --git a/routers/api/v1/repo/patch.go b/routers/api/v1/repo/patch.go index e9f5cf5d908da..31b479a5fd1c4 100644 --- a/routers/api/v1/repo/patch.go +++ b/routers/api/v1/repo/patch.go @@ -57,7 +57,7 @@ func ApplyDiffPatch(ctx *context.APIContext) { Signoff: changeRepoFileOpts.Signoff, } - fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts) + fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Doer, opts) if err != nil { handleChangeRepoFilesError(ctx, err) } else { diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 9e77637282a3e..8dd5e761dbaa5 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -114,7 +114,7 @@ func GetAnnotatedTag(ctx *context.APIContext) { if err != nil { ctx.APIError(http.StatusBadRequest, err) } - ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, tag, commit)) + ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, tag, commit)) } } diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index 8e24ffa465c14..b4365b7ef5665 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -177,17 +177,17 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi } // lookup filename in wiki - get filecontent, real filename - content, pageFilename := wikiContentsByName(ctx, commit, wikiName, false) + content, pageFilename := wikiContentsByName(ctx, wikiRepo, commit, wikiName, false) if ctx.Written() { return nil } - sidebarContent, _ := wikiContentsByName(ctx, commit, "_Sidebar", true) + sidebarContent, _ := wikiContentsByName(ctx, wikiRepo, commit, "_Sidebar", true) if ctx.Written() { return nil } - footerContent, _ := wikiContentsByName(ctx, commit, "_Footer", true) + footerContent, _ := wikiContentsByName(ctx, wikiRepo, commit, "_Footer", true) if ctx.Written() { return nil } @@ -196,7 +196,7 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename) // Get last change information. - lastCommit, err := wikiRepo.GetCommitByPath(pageFilename) + lastCommit, err := wikiRepo.GetCommitByPathDefaultBranch(pageFilename) if err != nil { ctx.APIErrorInternal(err) return nil @@ -307,7 +307,7 @@ func ListWikiPages(ctx *context.APIContext) { skip := (page - 1) * limit maxNum := page * limit - entries, err := commit.ListEntries() + entries, err := git.NewTree(wikiRepo, commit.TreeID).ListEntries() if err != nil { ctx.APIErrorInternal(err) return @@ -317,7 +317,7 @@ func ListWikiPages(ctx *context.APIContext) { if i < skip || i>= maxNum || !entry.IsRegular() { continue } - c, err := wikiRepo.GetCommitByPath(entry.Name()) + c, err := wikiRepo.GetCommitByPathDefaultBranch(entry.Name()) if err != nil { ctx.APIErrorInternal(err) return @@ -423,7 +423,7 @@ func ListPageRevisions(ctx *context.APIContext) { } // lookup filename in wiki - get filecontent, gitTree entry , real filename - _, pageFilename := wikiContentsByName(ctx, commit, pageName, false) + _, pageFilename := wikiContentsByName(ctx, wikiRepo, commit, pageName, false) if ctx.Written() { return } @@ -450,8 +450,8 @@ func ListPageRevisions(ctx *context.APIContext) { } // findEntryForFile finds the tree entry for a target filepath. -func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) { - entry, err := commit.GetTreeEntryByPath(target) +func findEntryForFile(tree *git.Tree, target string) (*git.TreeEntry, error) { + entry, err := tree.GetTreeEntryByPath(target) if err != nil { return nil, err } @@ -464,7 +464,7 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) if unescapedTarget, err = url.QueryUnescape(target); err != nil { return nil, err } - return commit.GetTreeEntryByPath(unescapedTarget) + return tree.GetTreeEntryByPath(unescapedTarget) } // findWikiRepoCommit opens the wiki repo and returns the latest commit, writing to context on error. @@ -509,9 +509,9 @@ func wikiContentsByEntry(ctx *context.APIContext, entry *git.TreeEntry) string { // wikiContentsByName returns the contents of a wiki page, along with a boolean // indicating whether the page exists. Writes to ctx if an error occurs. -func wikiContentsByName(ctx *context.APIContext, commit *git.Commit, wikiName wiki_service.WebPath, isSidebarOrFooter bool) (string, string) { +func wikiContentsByName(ctx *context.APIContext, wikiRepo *git.Repository, commit *git.Commit, wikiName wiki_service.WebPath, isSidebarOrFooter bool) (string, string) { gitFilename := wiki_service.WebPathToGitPath(wikiName) - entry, err := findEntryForFile(commit, gitFilename) + entry, err := findEntryForFile(git.NewTree(wikiRepo, commit.TreeID), gitFilename) if err != nil { if git.IsErrNotExist(err) { if !isSidebarOrFooter { diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go index 57d0964ead21d..9ac4d147dfc02 100644 --- a/routers/private/hook_verification.go +++ b/routers/private/hook_verification.go @@ -91,11 +91,11 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error { Stdout: stdoutWriter, PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { _ = stdoutWriter.Close() - commit, err := git.CommitFromReader(repo, commitID, stdoutReader) + commit, err := git.CommitFromReader(commitID, stdoutReader) if err != nil { return err } - verification := asymkey_service.ParseCommitWithSignature(ctx, commit) + verification := asymkey_service.ParseCommitWithSignature(ctx, repo, commit) if !verification.Verified { cancel() return &errUnverifiedCommit{ diff --git a/routers/web/feed/branch.go b/routers/web/feed/branch.go index eb7f6dc5bc572..1ad2ac69474ce 100644 --- a/routers/web/feed/branch.go +++ b/routers/web/feed/branch.go @@ -19,7 +19,7 @@ func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType stri var commits []*git.Commit var err error if ctx.Repo.Commit != nil { - commits, err = ctx.Repo.Commit.CommitsByRange(0, 10, "", "", "") + commits, err = ctx.Repo.GitRepo.CommitsByRangeWithTime(ctx.Repo.Commit.ID, 0, 10, "", "", "") if err != nil { ctx.ServerError("ShowBranchFeed", err) return diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 202da407d298b..5e09fc9f13366 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -126,7 +126,7 @@ func prepareWorkflowDispatchTemplate(ctx *context.Context, commit *git.Commit) ( var curWorkflow *model.Workflow - _, entries, err := actions.ListWorkflows(commit) + _, entries, err := actions.ListWorkflows(git.NewTree(ctx.Repo.GitRepo, commit.TreeID)) if err != nil { ctx.ServerError("ListWorkflows", err) return nil diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 3422128026ffd..341d27f943a40 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -80,7 +80,7 @@ func ViewWorkflowFile(ctx *context_module.Context) { }, err) return } - rpath, entries, err := actions.ListWorkflows(commit) + rpath, entries, err := actions.ListWorkflows(git.NewTree(ctx.Repo.GitRepo, commit.TreeID)) if err != nil { ctx.ServerError("ListWorkflows", err) return diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index e304633f95238..19661b6a386df 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -50,7 +50,8 @@ func RefBlame(ctx *context.Context) { ctx.NotFound(nil) return } - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + entry, err := tree.GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err) return @@ -91,7 +92,7 @@ func RefBlame(ctx *context.Context) { } bypassBlameIgnore, _ := strconv.ParseBool(ctx.FormString("bypass-blame-ignore")) - result, err := performBlame(ctx, ctx.Repo.Repository, ctx.Repo.Commit, ctx.Repo.TreePath, bypassBlameIgnore) + result, err := performBlame(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.Commit, ctx.Repo.TreePath, bypassBlameIgnore) if err != nil { ctx.NotFound(err) return @@ -116,10 +117,10 @@ type blameResult struct { FaultyIgnoreRevsFile bool } -func performBlame(ctx *context.Context, repo *repo_model.Repository, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) { +func performBlame(ctx *context.Context, repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) { objectFormat := ctx.Repo.GetObjectFormat() - blameReader, err := git.CreateBlameReader(ctx, objectFormat, repo.RepoPath(), commit, file, bypassBlameIgnore) + blameReader, err := git.CreateBlameReader(ctx, gitRepo, objectFormat, repo.RepoPath(), commit, file, bypassBlameIgnore) if err != nil { return nil, err } @@ -135,7 +136,7 @@ func performBlame(ctx *context.Context, repo *repo_model.Repository, commit *git if len(r.Parts) == 0 && r.UsesIgnoreRevs { // try again without ignored revs - blameReader, err = git.CreateBlameReader(ctx, objectFormat, repo.RepoPath(), commit, file, true) + blameReader, err = git.CreateBlameReader(ctx, gitRepo, objectFormat, repo.RepoPath(), commit, file, true) if err != nil { return nil, err } diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 0c60abcecd07f..d62b5a9b5afa2 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -76,7 +76,7 @@ func Commits(ctx *context.Context) { } // Both `git log branchName` and `git log commitId` work. - commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize, "", "", "") + commits, err := ctx.Repo.GitRepo.CommitsByRangeWithTime(ctx.Repo.Commit.ID, page, pageSize, "", "", "") if err != nil { ctx.ServerError("CommitsByRange", err) return @@ -194,7 +194,7 @@ func SearchCommits(ctx *context.Context) { all := ctx.FormBool("all") opts := git.NewSearchCommitsOptions(query, all) - commits, err := ctx.Repo.Commit.SearchCommits(opts) + commits, err := ctx.Repo.GitRepo.SearchCommits(ctx.Repo.Commit.ID, opts) if err != nil { ctx.ServerError("SearchCommits", err) return @@ -386,7 +386,7 @@ func Diff(ctx *context.Context) { ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses) ctx.Data["CommitStatuses"] = statuses - verification := asymkey_service.ParseCommitWithSignature(ctx, commit) + verification := asymkey_service.ParseCommitWithSignature(ctx, ctx.Repo.GitRepo, commit) ctx.Data["Verification"] = verification ctx.Data["Author"] = user_model.ValidateCommitWithEmail(ctx, commit) ctx.Data["Parents"] = parents @@ -454,7 +454,7 @@ func RawDiff(ctx *context.Context) { } func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) ([]*git_model.SignCommitWithStatuses, error) { - commits, err := git_service.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository) + commits, err := git_service.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository, ctx.Repo.GitRepo) if err != nil { return nil, err } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index a7084bf0aec97..c08eef81892c7 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -60,7 +60,7 @@ func setCompareContext(ctx *context.Context, before, head *git.Commit, headOwner return nil } - blob, err := commit.GetBlobByPath(path) + blob, err := git.NewTree(ctx.Repo.GitRepo, commit.TreeID).GetBlobByPath(path) if err != nil { return nil } @@ -900,9 +900,9 @@ func ExcerptBlob(ctx *context.Context) { idxRight -= chunkSize leftHunkSize += chunkSize rightHunkSize += chunkSize - section.Lines, err = getExcerptLines(commit, filePath, idxLeft-1, idxRight-1, chunkSize) + section.Lines, err = getExcerptLines(gitRepo, commit, filePath, idxLeft-1, idxRight-1, chunkSize) } else if direction == "down" && (idxLeft-lastLeft)> chunkSize { - section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, chunkSize) + section.Lines, err = getExcerptLines(gitRepo, commit, filePath, lastLeft, lastRight, chunkSize) lastLeft += chunkSize lastRight += chunkSize } else { @@ -910,7 +910,7 @@ func ExcerptBlob(ctx *context.Context) { if direction == "down" { offset = 0 } - section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, idxRight-lastRight+offset) + section.Lines, err = getExcerptLines(gitRepo, commit, filePath, lastLeft, lastRight, idxRight-lastRight+offset) leftHunkSize = 0 rightHunkSize = 0 idxLeft = lastLeft @@ -953,8 +953,8 @@ func ExcerptBlob(ctx *context.Context) { ctx.HTML(http.StatusOK, tplBlobExcerpt) } -func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chunkSize int) ([]*gitdiff.DiffLine, error) { - blob, err := commit.Tree.GetBlobByPath(filePath) +func getExcerptLines(gitRepo *git.Repository, commit *git.Commit, filePath string, idxLeft, idxRight, chunkSize int) ([]*gitdiff.DiffLine, error) { + blob, err := git.NewTree(gitRepo, commit.TreeID).GetBlobByPath(filePath) if err != nil { return nil, err } diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go index 6f394aae27d02..455cfe6296bbf 100644 --- a/routers/web/repo/download.go +++ b/routers/web/repo/download.go @@ -82,7 +82,8 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim } func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) { - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + entry, err := tree.GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { if git.IsErrNotExist(err) { ctx.NotFound(err) diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 2a5ac102824d9..1146207963eeb 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -221,7 +221,8 @@ func redirectForCommitChoice[T any](ctx *context.Context, parsed *preparedEditor } func editFileOpenExisting(ctx *context.Context) (prefetch []byte, dataRc io.ReadCloser, fInfo *fileInfo) { - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + entry, err := tree.GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { HandleGitError(ctx, "GetTreeEntryByPath", err) return nil, nil, nil diff --git a/routers/web/repo/editor_apply_patch.go b/routers/web/repo/editor_apply_patch.go index bd2811cc5f01d..07ac404597871 100644 --- a/routers/web/repo/editor_apply_patch.go +++ b/routers/web/repo/editor_apply_patch.go @@ -31,7 +31,7 @@ func NewDiffPatchPost(ctx *context.Context) { } defaultCommitMessage := ctx.Locale.TrString("repo.editor.patch") - _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{ + _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Doer, &files.ApplyDiffPatchOptions{ LastCommitID: parsed.form.LastCommit, OldBranch: parsed.OldBranchName, NewBranch: parsed.NewBranchName, diff --git a/routers/web/repo/editor_cherry_pick.go b/routers/web/repo/editor_cherry_pick.go index 10c2741b1cb22..3fe7add5bdd53 100644 --- a/routers/web/repo/editor_cherry_pick.go +++ b/routers/web/repo/editor_cherry_pick.go @@ -72,7 +72,7 @@ func CherryPickPost(ctx *context.Context) { } if err == nil { opts.Content = buf.String() - _, err = files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts) + _, err = files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Doer, opts) if err != nil { err = util.ErrorWrapLocale(err, "repo.editor.fail_to_apply_patch") } diff --git a/routers/web/repo/editor_preview.go b/routers/web/repo/editor_preview.go index 14be5b72b65bd..629db26ea4748 100644 --- a/routers/web/repo/editor_preview.go +++ b/routers/web/repo/editor_preview.go @@ -6,6 +6,7 @@ package repo import ( "net/http" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" ) @@ -18,7 +19,9 @@ func DiffPreviewPost(ctx *context.Context) { return } - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treePath) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + + entry, err := tree.GetTreeEntryByPath(treePath) if err != nil { ctx.ServerError("GetTreeEntryByPath", err) return diff --git a/routers/web/repo/editor_util.go b/routers/web/repo/editor_util.go index f910f0bd40729..c7fbe8e3c1447 100644 --- a/routers/web/repo/editor_util.go +++ b/routers/web/repo/editor_util.go @@ -46,7 +46,8 @@ func getClosestParentWithFiles(gitRepo *git.Repository, branchName, originTreePa return "" } // see if the tree has entries - if tree, err := commit.SubTree(treePath); err != nil { + tree := git.NewTree(gitRepo, commit.TreeID) + if tree, err := tree.SubTree(treePath); err != nil { return f(path.Dir(treePath), commit) // failed to get the tree, going up a dir } else if entries, err := tree.ListEntries(); err != nil || len(entries) == 0 { return f(path.Dir(treePath), commit) // no files in this dir, going up a dir diff --git a/routers/web/repo/issue_new.go b/routers/web/repo/issue_new.go index 887019b146b89..a9f5407981724 100644 --- a/routers/web/repo/issue_new.go +++ b/routers/web/repo/issue_new.go @@ -50,11 +50,12 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles templateCandidates = append(templateCandidates, possibleFiles...) // Append files to the end because they should be fallback templateErrs := map[string]error{} + tree := git.NewTree(ctx.Repo.GitRepo, commit.TreeID) for _, filename := range templateCandidates { - if ok, _ := commit.HasFile(filename); !ok { + if entry, _ := tree.GetTreeEntryByPath(filename); entry == nil { continue } - template, err := issue_template.UnmarshalFromCommit(commit, filename) + template, err := issue_template.UnmarshalFromCommit(tree, filename) if err != nil { templateErrs[filename] = err continue diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 83aaf75363fbd..33c8bb8d651a3 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -711,7 +711,7 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { } } } else { - beforeCommit, err = afterCommit.Parent(0) + beforeCommit, err = gitRepo.ParentCommit(afterCommit, 0) if err != nil { ctx.ServerError("Parent", err) return diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 36ea20c23e6f7..dc366e19595fa 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -60,7 +60,7 @@ func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model return fmt.Errorf("GetBranchCommit(DefaultBranch): %w", err) } } - countCache[target], err = commit.CommitsCount() + countCache[target], err = repoCtx.GitRepo.CommitsCount(commit.ID.String()) if err != nil { return fmt.Errorf("CommitsCount: %w", err) } diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go index 689174dfa1aa5..ed106d9096271 100644 --- a/routers/web/repo/render.go +++ b/routers/web/repo/render.go @@ -24,7 +24,7 @@ func RenderFile(ctx *context.Context) { var blob *git.Blob var err error if ctx.Repo.TreePath != "" { - blob, err = ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath) + blob, err = git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID).GetBlobByPath(ctx.Repo.TreePath) } else { blob, err = ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha")) } diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go index 340b2bc091741..6495c304d45bc 100644 --- a/routers/web/repo/treelist.go +++ b/routers/web/repo/treelist.go @@ -22,7 +22,7 @@ import ( // TreeList get all files' entries of a repository func TreeList(ctx *context.Context) { - tree, err := ctx.Repo.Commit.SubTree("/") + tree, err := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID).SubTree("/") if err != nil { ctx.ServerError("Repo.Commit.SubTree", err) return @@ -144,7 +144,8 @@ func transformDiffTreeForWeb(renderedIconPool *fileicon.RenderedIconPool, diffTr func TreeViewNodes(ctx *context.Context) { renderedIconPool := fileicon.NewRenderedIconPool() - results, err := files_service.GetTreeViewNodes(ctx, ctx.Repo.RepoLink, renderedIconPool, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormString("sub_path")) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + results, err := files_service.GetTreeViewNodes(ctx, ctx.Repo.RepoLink, renderedIconPool, tree, ctx.Repo.TreePath, ctx.FormString("sub_path")) if err != nil { ctx.ServerError("GetTreeViewNodes", err) return diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index e47bc56d081a6..496676a7e2223 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -124,7 +124,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool { // or of directory if not in root directory. ctx.Data["LatestCommit"] = latestCommit if latestCommit != nil { - verification := asymkey_service.ParseCommitWithSignature(ctx, latestCommit) + verification := asymkey_service.ParseCommitWithSignature(ctx, ctx.Repo.GitRepo, latestCommit) if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) { return repo_model.IsOwnerMemberCollaborator(ctx, ctx.Repo.Repository, user.ID) @@ -260,9 +260,10 @@ func LastCommit(ctx *context.Context) { func prepareDirectoryFileIcons(ctx *context.Context, files []git.CommitInfo) { renderedIconPool := fileicon.NewRenderedIconPool() fileIcons := map[string]template.HTML{} + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) for _, f := range files { fullPath := path.Join(ctx.Repo.TreePath, f.Entry.Name()) - entryInfo := fileicon.EntryInfoFromGitTreeEntry(ctx.Repo.Commit, fullPath, f.Entry) + entryInfo := fileicon.EntryInfoFromGitTreeEntry(tree, fullPath, f.Entry) fileIcons[f.Entry.Name()] = fileicon.RenderEntryIconHTML(renderedIconPool, entryInfo) } fileIcons[".."] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolder()) @@ -271,7 +272,8 @@ func prepareDirectoryFileIcons(ctx *context.Context, files []git.CommitInfo) { } func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entries { - tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath) + rootTree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + tree, err := rootTree.SubTree(ctx.Repo.TreePath) if err != nil { HandleGitError(ctx, "Repo.Commit.SubTree", err) return nil @@ -280,7 +282,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + url.PathEscape(ctx.Repo.CommitID) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) // Get current entry user currently looking at. - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + entry, err := rootTree.GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err) return nil @@ -305,7 +307,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri defer cancel() } - files, latestCommit, err := allEntries.GetCommitsInfo(commitInfoCtx, ctx.Repo.RepoLink, ctx.Repo.Commit, ctx.Repo.TreePath) + files, latestCommit, err := allEntries.GetCommitsInfo(commitInfoCtx, ctx.Repo.GitRepo, ctx.Repo.RepoLink, ctx.Repo.Commit, ctx.Repo.TreePath) if err != nil { ctx.ServerError("GetCommitsInfo", err) return nil diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go index 2d5bddd939f10..49f412f1fe9e5 100644 --- a/routers/web/repo/view_file.go +++ b/routers/web/repo/view_file.go @@ -32,7 +32,7 @@ import ( ) func prepareLatestCommitInfo(ctx *context.Context) bool { - commit, err := ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath) + commit, err := ctx.Repo.GitRepo.GetCommitByPath(ctx.Repo.Commit.ID, ctx.Repo.TreePath) if err != nil { ctx.ServerError("GetCommitByPath", err) return false @@ -177,6 +177,7 @@ func prepareFileView(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["FileTreePath"] = ctx.Repo.TreePath ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) if ctx.Repo.TreePath == ".editorconfig" { _, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit) if editorconfigWarning != nil { @@ -186,7 +187,7 @@ func prepareFileView(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["FileError"] = strings.TrimSpace(editorconfigErr.Error()) } } else if issue_service.IsTemplateConfig(ctx.Repo.TreePath) { - _, issueConfigErr := issue_service.GetTemplateConfig(ctx.Repo.GitRepo, ctx.Repo.TreePath, ctx.Repo.Commit) + _, issueConfigErr := issue_service.GetTemplateConfig(ctx.Repo.GitRepo, ctx.Repo.TreePath, tree) if issueConfigErr != nil { ctx.Data["FileError"] = strings.TrimSpace(issueConfigErr.Error()) } diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go index f475e93f60489..9165970de1fcb 100644 --- a/routers/web/repo/view_home.go +++ b/routers/web/repo/view_home.go @@ -96,7 +96,7 @@ func prepareHomeSidebarCitationFile(entry *git.TreeEntry) func(ctx *context.Cont if entry.Name() != "" { return } - tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath) + tree, err := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID).SubTree(ctx.Repo.TreePath) if err != nil { HandleGitError(ctx, "Repo.Commit.SubTree", err) return @@ -262,8 +262,8 @@ func isViewHomeOnlyContent(ctx *context.Context) bool { return ctx.FormBool("only_content") } -func handleRepoViewSubmodule(ctx *context.Context, commitSubmoduleFile *git.CommitSubmoduleFile) { - submoduleWebLink := commitSubmoduleFile.SubmoduleWebLinkTree(ctx) +func handleRepoViewSubmodule(ctx *context.Context, submoduleFile *git.SubmoduleFile) { + submoduleWebLink := submoduleFile.SubmoduleWebLinkTree(ctx) if submoduleWebLink == nil { ctx.Data["NotFoundPrompt"] = ctx.Repo.TreePath ctx.NotFound(nil) @@ -286,12 +286,12 @@ func handleRepoViewSubmodule(ctx *context.Context, commitSubmoduleFile *git.Comm func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) { return func(ctx *context.Context) { if entry.IsSubModule() { - commitSubmoduleFile, err := git.GetCommitInfoSubmoduleFile(ctx.Repo.RepoLink, ctx.Repo.TreePath, ctx.Repo.Commit, entry.ID) + submoduleFile, err := git.GetCommitInfoSubmoduleFile(ctx.Repo.GitRepo, ctx.Repo.RepoLink, ctx.Repo.TreePath, ctx.Repo.Commit.TreeID, entry.ID) if err != nil { HandleGitError(ctx, "prepareToRenderDirOrFile: GetCommitInfoSubmoduleFile", err) return } - handleRepoViewSubmodule(ctx, commitSubmoduleFile) + handleRepoViewSubmodule(ctx, submoduleFile) } else if entry.IsDir() { prepareToRenderDirectory(ctx) } else { @@ -347,7 +347,7 @@ func redirectFollowSymlink(ctx *context.Context, treePathEntry *git.TreeEntry) b return false } if treePathEntry.IsLink() { - if res, err := git.EntryFollowLinks(ctx.Repo.Commit, ctx.Repo.TreePath, treePathEntry); err == nil { + if res, err := git.EntryFollowLinks(git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID), ctx.Repo.TreePath, treePathEntry); err == nil { redirect := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(res.TargetFullPath) + "?" + ctx.Req.URL.RawQuery ctx.Redirect(redirect) return true @@ -389,7 +389,7 @@ func Home(ctx *context.Context) { prepareHomeTreeSideBarSwitch(ctx) // get the current git entry which doer user is currently looking at. - entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) + entry, err := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID).GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err) return diff --git a/routers/web/repo/view_home_test.go b/routers/web/repo/view_home_test.go index dd74ae560b4d4..9b3495d4f0f11 100644 --- a/routers/web/repo/view_home_test.go +++ b/routers/web/repo/view_home_test.go @@ -18,19 +18,19 @@ func TestViewHomeSubmoduleRedirect(t *testing.T) { unittest.PrepareTestEnv(t) ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule") - submodule := git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id") + submodule := git_module.NewSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id") handleRepoViewSubmodule(ctx, submodule) assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus()) assert.Equal(t, "/user2/repo-other/tree/any-ref-id", ctx.Resp.Header().Get("Location")) ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule") - submodule = git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "https://other/user2/repo-other.git", "any-ref-id") + submodule = git_module.NewSubmoduleFile("/user2/repo1", "test-submodule", "https://other/user2/repo-other.git", "any-ref-id") handleRepoViewSubmodule(ctx, submodule) // do not auto-redirect for external URLs, to avoid open redirect or phishing assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus()) ctx, respWriter := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule?only_content=true") - submodule = git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id") + submodule = git_module.NewSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id") handleRepoViewSubmodule(ctx, submodule) assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus()) assert.Equal(t, `/user2/repo-other/tree/any-ref-id`, respWriter.Body.String()) diff --git a/routers/web/repo/view_readme.go b/routers/web/repo/view_readme.go index ba03febff3ddf..5c426e9465765 100644 --- a/routers/web/repo/view_readme.go +++ b/routers/web/repo/view_readme.go @@ -64,12 +64,13 @@ func findReadmeFileInEntries(ctx *context.Context, parentDir string, entries []* exts := append(localizedExtensions(".md", ctx.Locale.Language()), ".txt", "") // sorted by priority extCount := len(exts) readmeFiles := make([]*git.TreeEntry, extCount+1) + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) for _, entry := range entries { if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok { fullPath := path.Join(parentDir, entry.Name()) if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].Name(), entry.Blob().Name()) { if entry.IsLink() { - res, err := git.EntryFollowLinks(ctx.Repo.Commit, fullPath, entry) + res, err := git.EntryFollowLinks(tree, fullPath, entry) if err == nil && (res.TargetEntry.IsExecutable() || res.TargetEntry.IsRegular()) { readmeFiles[i] = entry } @@ -146,7 +147,7 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil readmeFullPath := path.Join(ctx.Repo.TreePath, subfolder, readmeFile.Name()) readmeTargetEntry := readmeFile if readmeFile.IsLink() { - if res, err := git.EntryFollowLinks(ctx.Repo.Commit, readmeFullPath, readmeFile); err == nil { + if res, err := git.EntryFollowLinks(git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID), readmeFullPath, readmeFile); err == nil { readmeTargetEntry = res.TargetEntry } else { readmeTargetEntry = nil // if we cannot resolve the symlink, we cannot render the readme, ignore the error diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 289db11a4f830..47984a7d4f7fd 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -78,8 +78,8 @@ type PageMeta struct { } // findEntryForFile finds the tree entry for a target filepath. -func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) { - entry, err := commit.GetTreeEntryByPath(target) +func findEntryForFile(tree *git.Tree, target string) (*git.TreeEntry, error) { + entry, err := tree.GetTreeEntryByPath(target) if err != nil && !git.IsErrNotExist(err) { return nil, err } @@ -92,7 +92,7 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) if unescapedTarget, err = url.QueryUnescape(target); err != nil { return nil, err } - return commit.GetTreeEntryByPath(unescapedTarget) + return tree.GetTreeEntryByPath(unescapedTarget) } func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) { @@ -144,10 +144,10 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte { // wikiEntryByName returns the entry of a wiki page, along with a boolean // indicating whether the entry exists. Writes to ctx if an error occurs. // The last return value indicates whether the file should be returned as a raw file -func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) (*git.TreeEntry, string, bool, bool) { +func wikiEntryByName(ctx *context.Context, tree *git.Tree, wikiName wiki_service.WebPath) (*git.TreeEntry, string, bool, bool) { isRaw := false gitFilename := wiki_service.WebPathToGitPath(wikiName) - entry, err := findEntryForFile(commit, gitFilename) + entry, err := findEntryForFile(tree, gitFilename) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findEntryForFile", err) return nil, "", false, false @@ -155,7 +155,7 @@ func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_ser if entry == nil { // check if the file without ".md" suffix exists gitFilename := strings.TrimSuffix(gitFilename, ".md") - entry, err = findEntryForFile(commit, gitFilename) + entry, err = findEntryForFile(tree, gitFilename) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findEntryForFile", err) return nil, "", false, false @@ -170,8 +170,8 @@ func wikiEntryByName(ctx *context.Context, commit *git.Commit, wikiName wiki_ser // wikiContentsByName returns the contents of a wiki page, along with a boolean // indicating whether the page exists. Writes to ctx if an error occurs. -func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) { - entry, gitFilename, noEntry, _ := wikiEntryByName(ctx, commit, wikiName) +func wikiContentsByName(ctx *context.Context, tree *git.Tree, wikiName wiki_service.WebPath) ([]byte, *git.TreeEntry, string, bool) { + entry, gitFilename, noEntry, _ := wikiEntryByName(ctx, tree, wikiName) if entry == nil { return nil, nil, "", true } @@ -187,8 +187,9 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { return nil, nil } + tree := git.NewTree(wikiGitRepo, commit.TreeID) // get the wiki pages list. - entries, err := commit.ListEntries() + entries, err := tree.ListEntries() if err != nil { ctx.ServerError("ListEntries", err) return nil, nil @@ -233,7 +234,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { isFooter := pageName == "_Footer" // lookup filename in wiki - get gitTree entry , real filename - entry, pageFilename, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName) + entry, pageFilename, noEntry, isRaw := wikiEntryByName(ctx, tree, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } @@ -286,7 +287,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { } if !isSideBar { - sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar") + sidebarContent, _, _, _ := wikiContentsByName(ctx, tree, "_Sidebar") if ctx.Written() { return nil, nil } @@ -298,7 +299,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { } if !isFooter { - footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer") + footerContent, _, _, _ := wikiContentsByName(ctx, tree, "_Footer") if ctx.Written() { return nil, nil } @@ -340,8 +341,10 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name + tree := git.NewTree(wikiGitRepo, commit.TreeID) + // lookup filename in wiki - get page content, gitTree entry , real filename - _, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) + _, entry, pageFilename, noEntry := wikiContentsByName(ctx, tree, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } @@ -367,7 +370,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) ctx.ServerError("CommitsByFileAndRange", err) return nil, nil } - ctx.Data["Commits"], err = git_service.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository) + ctx.Data["Commits"], err = git_service.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository, wikiGitRepo) if err != nil { ctx.ServerError("ConvertFromGitCommit", err) return nil, nil @@ -381,7 +384,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) } func renderEditPage(ctx *context.Context) { - _, commit, err := findWikiRepoCommit(ctx) + wikiGitRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { if !git.IsErrNotExist(err) { ctx.ServerError("GetBranchCommit", err) @@ -401,8 +404,10 @@ func renderEditPage(ctx *context.Context) { ctx.Data["Title"] = displayName ctx.Data["title"] = displayName + tree := git.NewTree(wikiGitRepo, commit.TreeID) + // lookup filename in wiki - gitTree entry , real filename - entry, _, noEntry, isRaw := wikiEntryByName(ctx, commit, pageName) + entry, _, noEntry, isRaw := wikiEntryByName(ctx, tree, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } @@ -497,7 +502,7 @@ func Wiki(ctx *context.Context) { ctx.Data["FormatWarning"] = ext + " rendering is not supported at the moment. Rendered as Markdown." } // Get last change information. - lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath) + lastCommit, err := wikiGitRepo.GetCommitByPathDefaultBranch(wikiPath) if err != nil { ctx.ServerError("GetCommitByPath", err) return @@ -529,7 +534,7 @@ func WikiRevision(ctx *context.Context) { // Get last change information. wikiPath := entry.Name() - lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath) + lastCommit, err := wikiGitRepo.GetCommitByPathDefaultBranch(wikiPath) if err != nil { ctx.ServerError("GetCommitByPath", err) return @@ -549,14 +554,15 @@ func WikiPages(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.wiki.pages") ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived - _, commit, err := findWikiRepoCommit(ctx) + wikiGitRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { ctx.Redirect(ctx.Repo.RepoLink + "/wiki") return } + rootTree := git.NewTree(wikiGitRepo, commit.TreeID) treePath := "" // To support list sub folders' pages in the future - tree, err := commit.SubTree(treePath) + tree, err := rootTree.SubTree(treePath) if err != nil { ctx.ServerError("SubTree", err) return @@ -569,7 +575,7 @@ func WikiPages(ctx *context.Context) { } allEntries.CustomSort(base.NaturalSortLess) - entries, _, err := allEntries.GetCommitsInfo(ctx, ctx.Repo.RepoLink, commit, treePath) + entries, _, err := allEntries.GetCommitsInfo(ctx, wikiGitRepo, ctx.Repo.RepoLink, commit, treePath) if err != nil { ctx.ServerError("GetCommitsInfo", err) return @@ -603,7 +609,7 @@ func WikiPages(ctx *context.Context) { // WikiRaw outputs raw blob requested by user (image for example) func WikiRaw(ctx *context.Context) { - _, commit, err := findWikiRepoCommit(ctx) + wikiGitRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { if git.IsErrNotExist(err) { ctx.NotFound(nil) @@ -617,8 +623,9 @@ func WikiRaw(ctx *context.Context) { providedGitPath := wiki_service.WebPathToGitPath(providedWebPath) var entry *git.TreeEntry if commit != nil { + tree := git.NewTree(wikiGitRepo, commit.TreeID) // Try to find a file with that name - entry, err = findEntryForFile(commit, providedGitPath) + entry, err = findEntryForFile(tree, providedGitPath) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findFile", err) return @@ -627,7 +634,7 @@ func WikiRaw(ctx *context.Context) { if entry == nil { // Try to find a wiki page with that name providedGitPath = strings.TrimSuffix(providedGitPath, ".md") - entry, err = findEntryForFile(commit, providedGitPath) + entry, err = findEntryForFile(tree, providedGitPath) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findFile", err) return diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index 409d7c9a05bca..688d43df97bcc 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -34,7 +34,8 @@ func wikiEntry(t *testing.T, repo *repo_model.Repository, wikiName wiki_service. defer wikiRepo.Close() commit, err := wikiRepo.GetBranchCommit("master") assert.NoError(t, err) - entries, err := commit.ListEntries() + tree := git.NewTree(wikiRepo, commit.TreeID) + entries, err := tree.ListEntries() assert.NoError(t, err) for _, entry := range entries { if entry.Name() == wiki_service.WebPathToGitPath(wikiName) { diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 2bd0abc4c03f7..6ee4250f467a0 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -122,7 +122,7 @@ func FindOwnerProfileReadme(ctx *context.Context, doer *user_model.User, optProf return nil, nil } - profileReadmeBlob, _ = commit.GetBlobByPath("README.md") // no need to handle this error + profileReadmeBlob, _ = git.NewTree(profileGitRepo, commit.TreeID).GetBlobByPath("README.md") // no need to handle this error return profileDbRepo, profileReadmeBlob } diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index b8bc20cdbb1a7..22e54413ee647 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -236,7 +236,7 @@ func notify(ctx context.Context, input *notifyInput) error { } if shouldDetectSchedules { - if err := handleSchedules(ctx, schedules, commit, input, ref.String()); err != nil { + if err := handleSchedules(ctx, gitRepo, schedules, commit, input, ref.String()); err != nil { return err } } @@ -497,12 +497,13 @@ func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun, repo *rep func handleSchedules( ctx context.Context, + gitRepo *git.Repository, detectedWorkflows []*actions_module.DetectedWorkflow, commit *git.Commit, input *notifyInput, ref string, ) error { - branch, err := commit.GetBranchName() + branch, err := gitRepo.GetClosestBranchName(commit) if err != nil { return err } @@ -614,5 +615,5 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository) // so we use action user as the Doer of the notifyInput notifyInput := newNotifyInputForSchedules(repo) - return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch) + return handleSchedules(ctx, gitRepo, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch) } diff --git a/services/actions/workflow.go b/services/actions/workflow.go index 233e22b5ddfe2..ad9c728e25b80 100644 --- a/services/actions/workflow.go +++ b/services/actions/workflow.go @@ -91,7 +91,8 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re } // get workflow entry from runTargetCommit - _, entries, err := actions.ListWorkflows(runTargetCommit) + tree := git.NewTree(gitRepo, runTargetCommit.TreeID) + _, entries, err := actions.ListWorkflows(tree) if err != nil { return err } diff --git a/services/asymkey/commit.go b/services/asymkey/commit.go index 54ef052a507a9..f02626ada5b00 100644 --- a/services/asymkey/commit.go +++ b/services/asymkey/commit.go @@ -23,7 +23,7 @@ import ( ) // ParseCommitWithSignature check if signature is good against keystore. -func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *asymkey_model.CommitVerification { +func ParseCommitWithSignature(ctx context.Context, gitRepo *git.Repository, c *git.Commit) *asymkey_model.CommitVerification { committer, err := user_model.GetUserByEmail(ctx, c.Committer.Email) if err != nil && !user_model.IsErrUserNotExist(err) { log.Error("GetUserByEmail: %v", err) @@ -32,14 +32,14 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *asymkey_model Reason: "gpg.error.no_committer_account", // this error is not right, but such error should seldom happen } } - return ParseCommitWithSignatureCommitter(ctx, c, committer) + return ParseCommitWithSignatureCommitter(ctx, gitRepo, c, committer) } // ParseCommitWithSignatureCommitter parses a commit's GPG or SSH signature. // The caller guarantees that the committer user is related to the commit by checking its activated email addresses or no-reply address. // If the commit is singed by an instance key, then committer can be nil. // If the signature exists, even if committer is nil, the returned CommittingUser will be a non-nil fake user (e.g.: instance key) -func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification { +func ParseCommitWithSignatureCommitter(ctx context.Context, gitRepo *git.Repository, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification { // If no signature, just report the committer if c.Signature == nil { return &asymkey_model.CommitVerification{ @@ -58,10 +58,10 @@ func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, commi if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") { return parseCommitWithSSHSignature(ctx, c, committer) } - return parseCommitWithGPGSignature(ctx, c, committer) + return parseCommitWithGPGSignature(ctx, gitRepo, c, committer) } -func parseCommitWithGPGSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification { +func parseCommitWithGPGSignature(ctx context.Context, gitRepo *git.Repository, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification { // Parsing signature sig, err := asymkey_model.ExtractSignature(c.Signature.Signature) if err != nil { // Skipping failed to extract sign @@ -162,7 +162,7 @@ func parseCommitWithGPGSignature(ctx context.Context, c *git.Commit, committer * } } - defaultGPGSettings, err := c.GetRepositoryDefaultPublicGPGKey(false) + defaultGPGSettings, err := gitRepo.GetDefaultPublicGPGKey(false) if err != nil { log.Error("Error getting default public gpg key: %v", err) } else if defaultGPGSettings == nil { diff --git a/services/asymkey/commit_test.go b/services/asymkey/commit_test.go index 6edba1e90aff3..c09507e6b39f8 100644 --- a/services/asymkey/commit_test.go +++ b/services/asymkey/commit_test.go @@ -37,7 +37,7 @@ func TestParseCommitWithSSHSignature(t *testing.T) { require.NoError(t, err) t.Run("UserSSHKey", func(t *testing.T) { - commit, err := git.CommitFromReader(nil, git.Sha1ObjectFormat.EmptyObjectID(), strings.NewReader(`tree a3b1fad553e0f9a2b4a58327bebde36c7da75aa2 + commit, err := git.CommitFromReader(git.Sha1ObjectFormat.EmptyObjectID(), strings.NewReader(`tree a3b1fad553e0f9a2b4a58327bebde36c7da75aa2 author user2 1752194028 -0700 committer user2 1752194028 -0700 gpgsig -----BEGIN SSH SIGNATURE----- @@ -68,7 +68,7 @@ init project defer test.MockVariableValue(&setting.Repository.Signing.SigningEmail, "gitea@fake.local")() defer test.MockVariableValue(&setting.Repository.Signing.TrustedSSHKeys, []string{"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH6Y4idVaW3E+bLw1uqoAfJD7o5Siu+HqS51E9oQLPE9"})() - commit, err := git.CommitFromReader(nil, git.Sha1ObjectFormat.EmptyObjectID(), strings.NewReader(`tree 9a93ffa76e8b72bdb6431910b3a506fa2b39f42e + commit, err := git.CommitFromReader(git.Sha1ObjectFormat.EmptyObjectID(), strings.NewReader(`tree 9a93ffa76e8b72bdb6431910b3a506fa2b39f42e author User Two 1749230009 +0200 committer User Two 1749230009 +0200 gpgsig -----BEGIN SSH SIGNATURE----- diff --git a/services/asymkey/sign.go b/services/asymkey/sign.go index 8fd2adc8b4804..5acc3695d0448 100644 --- a/services/asymkey/sign.go +++ b/services/asymkey/sign.go @@ -258,7 +258,7 @@ Loop: if commit.Signature == nil { return false, nil, nil, &ErrWontSign{parentSigned} } - verification := ParseCommitWithSignature(ctx, commit) + verification := ParseCommitWithSignature(ctx, gitRepo, commit) if !verification.Verified { return false, nil, nil, &ErrWontSign{parentSigned} } @@ -316,7 +316,7 @@ Loop: if commit.Signature == nil { return false, nil, nil, &ErrWontSign{parentSigned} } - verification := ParseCommitWithSignature(ctx, commit) + verification := ParseCommitWithSignature(ctx, gitRepo, commit) if !verification.Verified { return false, nil, nil, &ErrWontSign{parentSigned} } @@ -389,7 +389,7 @@ Loop: if err != nil { return false, nil, nil, err } - verification := ParseCommitWithSignature(ctx, commit) + verification := ParseCommitWithSignature(ctx, gitRepo, commit) if !verification.Verified { return false, nil, nil, &ErrWontSign{baseSigned} } @@ -405,7 +405,7 @@ Loop: if err != nil { return false, nil, nil, err } - verification := ParseCommitWithSignature(ctx, commit) + verification := ParseCommitWithSignature(ctx, gitRepo, commit) if !verification.Verified { return false, nil, nil, &ErrWontSign{headSigned} } @@ -421,21 +421,25 @@ Loop: if err != nil { return false, nil, nil, err } - verification := ParseCommitWithSignature(ctx, commit) + verification := ParseCommitWithSignature(ctx, gitRepo, commit) if !verification.Verified { return false, nil, nil, &ErrWontSign{commitsSigned} } // need to work out merge-base - mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit) + mergeBaseCommitID, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit) if err != nil { return false, nil, nil, err } - commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit) + mergeBaseCommit, err := gitRepo.GetCommit(mergeBaseCommitID) + if err != nil { + return false, nil, nil, err + } + commitList, err := gitRepo.CommitsBetween(commit, mergeBaseCommit) if err != nil { return false, nil, nil, err } for _, commit := range commitList { - verification := ParseCommitWithSignature(ctx, commit) + verification := ParseCommitWithSignature(ctx, gitRepo, commit) if !verification.Verified { return false, nil, nil, &ErrWontSign{commitsSigned} } diff --git a/services/context/repo.go b/services/context/repo.go index afc6de9b1666d..36d93216b1618 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -208,7 +208,7 @@ func (r *Repository) GetCommitsCount() (int64, error) { contextName := r.RefFullName.ShortName() isRef := r.RefFullName.IsBranch() || r.RefFullName.IsTag() return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, isRef), func() (int64, error) { - return r.Commit.CommitsCount() + return r.GitRepo.CommitsCount(r.Commit.ID.String()) }) } @@ -255,7 +255,7 @@ func (r *Repository) GetEditorconfig(optCommit ...*git.Commit) (cfg *editorconfi return nil, nil, err } } - treeEntry, err := commit.GetTreeEntryByPath(".editorconfig") + treeEntry, err := git.NewTree(r.GitRepo, commit.TreeID).GetTreeEntryByPath(".editorconfig") if err != nil { return nil, nil, err } diff --git a/services/convert/convert.go b/services/convert/convert.go index 0de38221409bb..34ff53e14c9c3 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -61,7 +61,7 @@ func ToEmailSearch(email *user_model.SearchEmailResult) *api.Email { } // ToBranch convert a git.Commit and git.Branch to an api.Branch -func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName string, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) { +func ToBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, branchName string, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) { if bp == nil { var hasPerm bool var canPush bool @@ -81,7 +81,7 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName strin return &api.Branch{ Name: branchName, - Commit: ToPayloadCommit(ctx, repo, c), + Commit: ToPayloadCommit(ctx, repo, gitRepo, c), Protected: false, RequiredApprovals: 0, EnableStatusCheck: false, @@ -93,7 +93,7 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName strin branch := &api.Branch{ Name: branchName, - Commit: ToPayloadCommit(ctx, repo, c), + Commit: ToPayloadCommit(ctx, repo, gitRepo, c), Protected: true, RequiredApprovals: bp.RequiredApprovals, EnableStatusCheck: bp.EnableStatusCheck, @@ -390,11 +390,11 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task }, nil } -func getActionWorkflowEntry(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow { +func getActionWorkflowEntry(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow { cfgUnit := repo.MustGetUnit(ctx, unit.TypeActions) cfg := cfgUnit.ActionsConfig() - defaultBranch, _ := commit.GetBranchName() + defaultBranch, _ := gitRepo.GetClosestBranchName(commit) workflowURL := fmt.Sprintf("%s/actions/workflows/%s", repo.APIURL(), util.PathEscapeSegments(entry.Name())) workflowRepoURL := fmt.Sprintf("%s/src/branch/%s/%s/%s", repo.HTMLURL(ctx), util.PathEscapeSegments(defaultBranch), util.PathEscapeSegments(folder), util.PathEscapeSegments(entry.Name())) @@ -455,14 +455,14 @@ func ListActionWorkflows(ctx context.Context, gitrepo *git.Repository, repo *rep return nil, err } - folder, entries, err := actions.ListWorkflows(defaultBranchCommit) + folder, entries, err := actions.ListWorkflows(git.NewTree(gitrepo, defaultBranchCommit.TreeID)) if err != nil { return nil, err } workflows := make([]*api.ActionWorkflow, len(entries)) for i, entry := range entries { - workflows[i] = getActionWorkflowEntry(ctx, repo, defaultBranchCommit, folder, entry) + workflows[i] = getActionWorkflowEntry(ctx, repo, gitrepo, defaultBranchCommit, folder, entry) } return workflows, nil @@ -530,8 +530,8 @@ func ToActionRunner(ctx context.Context, runner *actions_model.ActionRunner) *ap } // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification -func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification { - verif := asymkey_service.ParseCommitWithSignature(ctx, c) +func ToVerification(ctx context.Context, gitRepo *git.Repository, c *git.Commit) *api.PayloadCommitVerification { + verif := asymkey_service.ParseCommitWithSignature(ctx, gitRepo, c) commitVerification := &api.PayloadCommitVerification{ Verified: verif.Verified, Reason: verif.Reason, @@ -697,7 +697,7 @@ func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([] } // ToAnnotatedTag convert git.Tag to api.AnnotatedTag -func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag { +func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag { return &api.AnnotatedTag{ Tag: t.Name, SHA: t.ID.String(), @@ -705,7 +705,7 @@ func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag Message: t.Message, URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()), Tagger: ToCommitUser(t.Tagger), - Verification: ToVerification(ctx, c), + Verification: ToVerification(ctx, gitRepo, c), } } diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go index 3ec81b52eebdc..a7225179d346d 100644 --- a/services/convert/git_commit.go +++ b/services/convert/git_commit.go @@ -39,7 +39,7 @@ func ToCommitMeta(repo *repo_model.Repository, tag *git.Tag) *api.CommitMeta { } // ToPayloadCommit convert a git.Commit to api.PayloadCommit -func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, c *git.Commit) *api.PayloadCommit { +func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, c *git.Commit) *api.PayloadCommit { authorUsername := "" if author, err := user_model.GetUserByEmail(ctx, c.Author.Email); err == nil { authorUsername = author.Name @@ -69,7 +69,7 @@ func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, c *git.Co UserName: committerUsername, }, Timestamp: c.Author.When, - Verification: ToVerification(ctx, c), + Verification: ToVerification(ctx, gitRepo, c), } } @@ -185,7 +185,7 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep // Retrieve verification for commit if opts.Verification { - res.RepoCommit.Verification = ToVerification(ctx, commit) + res.RepoCommit.Verification = ToVerification(ctx, gitRepo, commit) } // Retrieve files affected by the commit diff --git a/services/git/commit.go b/services/git/commit.go index e4755ef93d7b0..94b6bfd500b2a 100644 --- a/services/git/commit.go +++ b/services/git/commit.go @@ -17,7 +17,7 @@ import ( ) // ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys. -func ParseCommitsWithSignature(ctx context.Context, repo *repo_model.Repository, oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType) ([]*asymkey_model.SignCommit, error) { +func ParseCommitsWithSignature(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType) ([]*asymkey_model.SignCommit, error) { newCommits := make([]*asymkey_model.SignCommit, 0, len(oldCommits)) keyMap := map[string]bool{} @@ -37,7 +37,7 @@ func ParseCommitsWithSignature(ctx context.Context, repo *repo_model.Repository, committerUser := emailUsers.GetByEmail(c.Committer.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"? signCommit := &asymkey_model.SignCommit{ UserCommit: c, - Verification: asymkey_service.ParseCommitWithSignatureCommitter(ctx, c.Commit, committerUser), + Verification: asymkey_service.ParseCommitWithSignatureCommitter(ctx, gitRepo, c.Commit, committerUser), } isOwnerMemberCollaborator := func(user *user_model.User) (bool, error) { @@ -52,7 +52,7 @@ func ParseCommitsWithSignature(ctx context.Context, repo *repo_model.Repository, } // ConvertFromGitCommit converts git commits into SignCommitWithStatuses -func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) ([]*git_model.SignCommitWithStatuses, error) { +func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository, gitRepo *git.Repository) ([]*git_model.SignCommitWithStatuses, error) { validatedCommits, err := user_model.ValidateCommitsWithEmails(ctx, commits) if err != nil { return nil, err @@ -60,6 +60,7 @@ func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo signedCommits, err := ParseCommitsWithSignature( ctx, repo, + gitRepo, validatedCommits, repo.GetTrustModel(), ) diff --git a/services/gitdiff/git_diff_tree.go b/services/gitdiff/git_diff_tree.go index ed94bfbfe45f9..f1c946fa662e8 100644 --- a/services/gitdiff/git_diff_tree.go +++ b/services/gitdiff/git_diff_tree.go @@ -96,7 +96,7 @@ func validateGitDiffTreeArguments(gitRepo *git.Repository, useMergeBase bool, ba return false, objectFormat.EmptyTree().String(), headCommitID, nil } - baseCommit, err := headCommit.Parent(0) + baseCommit, err := gitRepo.ParentCommit(headCommit, 0) if err != nil { return false, "", "", fmt.Errorf("baseSha is '', attempted to use parent of commit %s, got error: %v", headCommit.ID.String(), err) } diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 7c99e049d548a..803534429cdb7 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -357,17 +357,17 @@ type DiffLimitedContent struct { } // GetTailSectionAndLimitedContent creates a fake DiffLineSection if the last section is not the end of the file -func (diffFile *DiffFile) GetTailSectionAndLimitedContent(leftCommit, rightCommit *git.Commit) (_ *DiffSection, diffLimitedContent DiffLimitedContent) { +func (diffFile *DiffFile) GetTailSectionAndLimitedContent(gitRepo *git.Repository, leftCommit, rightCommit *git.Commit) (_ *DiffSection, diffLimitedContent DiffLimitedContent) { var leftLineCount, rightLineCount int diffLimitedContent = DiffLimitedContent{} if diffFile.IsBin || diffFile.IsLFSFile { return nil, diffLimitedContent } if (diffFile.Type == DiffFileDel || diffFile.Type == DiffFileChange) && leftCommit != nil { - leftLineCount, diffLimitedContent.LeftContent = getCommitFileLineCountAndLimitedContent(leftCommit, diffFile.OldName) + leftLineCount, diffLimitedContent.LeftContent = getCommitFileLineCountAndLimitedContent(gitRepo, leftCommit, diffFile.OldName) } if (diffFile.Type == DiffFileAdd || diffFile.Type == DiffFileChange) && rightCommit != nil { - rightLineCount, diffLimitedContent.RightContent = getCommitFileLineCountAndLimitedContent(rightCommit, diffFile.OldName) + rightLineCount, diffLimitedContent.RightContent = getCommitFileLineCountAndLimitedContent(gitRepo, rightCommit, diffFile.OldName) } if len(diffFile.Sections) == 0 || diffFile.Type != DiffFileChange { return nil, diffLimitedContent @@ -433,8 +433,8 @@ func (l *limitByteWriter) Write(p []byte) (n int, err error) { return l.buf.Write(p) } -func getCommitFileLineCountAndLimitedContent(commit *git.Commit, filePath string) (lineCount int, limitWriter *limitByteWriter) { - blob, err := commit.GetBlobByPath(filePath) +func getCommitFileLineCountAndLimitedContent(gitRepo *git.Repository, commit *git.Commit, filePath string) (lineCount int, limitWriter *limitByteWriter) { + blob, err := git.NewTree(gitRepo, commit.TreeID).GetBlobByPath(filePath) if err != nil { return 0, nil } @@ -1102,7 +1102,7 @@ func guessBeforeCommitForDiff(gitRepo *git.Repository, beforeCommitID string, af actualBeforeCommitID = commitObjectFormat.EmptyTree() } else { if isBeforeCommitIDEmpty { - actualBeforeCommit, err = afterCommit.Parent(0) + actualBeforeCommit, err = gitRepo.ParentCommit(afterCommit, 0) } else { actualBeforeCommit, err = gitRepo.GetCommit(beforeCommitID) } @@ -1213,7 +1213,7 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit // Populate Submodule URLs if diffFile.SubmoduleDiffInfo != nil { - diffFile.SubmoduleDiffInfo.PopulateURL(repoLink, diffFile, beforeCommit, afterCommit) + diffFile.SubmoduleDiffInfo.PopulateURL(gitRepo, repoLink, diffFile, beforeCommit, afterCommit) } if !isVendored.Has() { @@ -1225,7 +1225,7 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit isGenerated = optional.Some(analyze.IsGenerated(diffFile.Name)) } diffFile.IsGenerated = isGenerated.Value() - tailSection, limitedContent := diffFile.GetTailSectionAndLimitedContent(beforeCommit, afterCommit) + tailSection, limitedContent := diffFile.GetTailSectionAndLimitedContent(gitRepo, beforeCommit, afterCommit) if tailSection != nil { diffFile.Sections = append(diffFile.Sections, tailSection) } diff --git a/services/gitdiff/submodule.go b/services/gitdiff/submodule.go index 4347743e3d0b0..8254fd7e36cc3 100644 --- a/services/gitdiff/submodule.go +++ b/services/gitdiff/submodule.go @@ -15,12 +15,12 @@ import ( type SubmoduleDiffInfo struct { SubmoduleName string - SubmoduleFile *git.CommitSubmoduleFile // it might be nil if the submodule is not found or unable to parse + SubmoduleFile *git.SubmoduleFile // it might be nil if the submodule is not found or unable to parse NewRefID string PreviousRefID string } -func (si *SubmoduleDiffInfo) PopulateURL(repoLink string, diffFile *DiffFile, leftCommit, rightCommit *git.Commit) { +func (si *SubmoduleDiffInfo) PopulateURL(gitRepo *git.Repository, repoLink string, diffFile *DiffFile, leftCommit, rightCommit *git.Commit) { si.SubmoduleName = diffFile.Name submoduleCommit := rightCommit // If the submodule is added or updated, check at the right commit if diffFile.IsDeleted { @@ -31,13 +31,13 @@ func (si *SubmoduleDiffInfo) PopulateURL(repoLink string, diffFile *DiffFile, le } submoduleFullPath := diffFile.GetDiffFileName() - submodule, err := submoduleCommit.GetSubModule(submoduleFullPath) + submodule, err := git.NewTree(gitRepo, submoduleCommit.TreeID).GetSubModule(submoduleFullPath) if err != nil { log.Error("Unable to PopulateURL for submodule %q: GetSubModule: %v", submoduleFullPath, err) return // ignore the error, do not cause 500 errors for end users } if submodule != nil { - si.SubmoduleFile = git.NewCommitSubmoduleFile(repoLink, submoduleFullPath, submodule.URL, submoduleCommit.ID.String()) + si.SubmoduleFile = git.NewSubmoduleFile(repoLink, submoduleFullPath, submodule.URL, submoduleCommit.ID.String()) } } diff --git a/services/gitdiff/submodule_test.go b/services/gitdiff/submodule_test.go index 9f2cf1d1c6040..81f1185bd1d8a 100644 --- a/services/gitdiff/submodule_test.go +++ b/services/gitdiff/submodule_test.go @@ -226,7 +226,7 @@ func TestSubmoduleInfo(t *testing.T) { assert.EqualValues(t, "aaaa...bbbb", sdi.CompareRefIDLinkHTML(ctx)) assert.EqualValues(t, "name", sdi.SubmoduleRepoLinkHTML(ctx)) - sdi.SubmoduleFile = git.NewCommitSubmoduleFile("/any/repo-link", "fullpath", "https://github.com/owner/repo", "1234") + sdi.SubmoduleFile = git.NewSubmoduleFile("/any/repo-link", "fullpath", "https://github.com/owner/repo", "1234") assert.EqualValues(t, `1111`, sdi.CommitRefIDLinkHTML(ctx, "1111")) assert.EqualValues(t, `aaaa...bbbb`, sdi.CompareRefIDLinkHTML(ctx)) assert.EqualValues(t, `name`, sdi.SubmoduleRepoLinkHTML(ctx)) diff --git a/services/issue/comments.go b/services/issue/comments.go index 9442701029b57..f3729d4bad380 100644 --- a/services/issue/comments.go +++ b/services/issue/comments.go @@ -177,7 +177,7 @@ func LoadCommentPushCommits(ctx context.Context, c *issues_model.Comment) (err e } defer closer.Close() - c.Commits, err = git_service.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) + c.Commits, err = git_service.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo, gitRepo) if err != nil { return err } diff --git a/services/issue/pull.go b/services/issue/pull.go index 8ee14c0a4b5ae..d2c866756fff1 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -80,9 +80,10 @@ func PullRequestCodeOwnersReview(ctx context.Context, pr *issues_model.PullReque return nil, err } + tree := git.NewTree(repo, commit.TreeID) var data string for _, file := range codeOwnerFiles { - if blob, err := commit.GetBlobByPath(file); err == nil { + if blob, err := tree.GetBlobByPath(file); err == nil { data, err = blob.GetBlobContent(setting.UI.MaxDisplayFileSize) if err == nil { break diff --git a/services/issue/template.go b/services/issue/template.go index 4b0f1aa987047..9e5230fceb415 100644 --- a/services/issue/template.go +++ b/services/issue/template.go @@ -47,12 +47,12 @@ func GetDefaultTemplateConfig() api.IssueConfig { // GetTemplateConfig loads the given issue config file. // It never returns a nil config. -func GetTemplateConfig(gitRepo *git.Repository, path string, commit *git.Commit) (api.IssueConfig, error) { +func GetTemplateConfig(gitRepo *git.Repository, path string, tree *git.Tree) (api.IssueConfig, error) { if gitRepo == nil { return GetDefaultTemplateConfig(), nil } - treeEntry, err := commit.GetTreeEntryByPath(path) + treeEntry, err := tree.GetTreeEntryByPath(path) if err != nil { return GetDefaultTemplateConfig(), err } @@ -124,8 +124,10 @@ func ParseTemplatesFromDefaultBranch(repo *repo.Repository, gitRepo *git.Reposit return ret } + tree := git.NewTree(gitRepo, commit.TreeID) + for _, dirName := range templateDirCandidates { - tree, err := commit.SubTree(dirName) + tree, err := tree.SubTree(dirName) if err != nil { log.Debug("get sub tree of %s: %v", dirName, err) continue @@ -165,13 +167,15 @@ func GetTemplateConfigFromDefaultBranch(repo *repo.Repository, gitRepo *git.Repo return GetDefaultTemplateConfig(), err } + tree := git.NewTree(gitRepo, commit.TreeID) + for _, configName := range templateConfigCandidates { - if _, err := commit.GetTreeEntryByPath(configName + ".yaml"); err == nil { - return GetTemplateConfig(gitRepo, configName+".yaml", commit) + if _, err := tree.GetTreeEntryByPath(configName + ".yaml"); err == nil { + return GetTemplateConfig(gitRepo, configName+".yaml", tree) } - if _, err := commit.GetTreeEntryByPath(configName + ".yml"); err == nil { - return GetTemplateConfig(gitRepo, configName+".yml", commit) + if _, err := tree.GetTreeEntryByPath(configName + ".yml"); err == nil { + return GetTemplateConfig(gitRepo, configName+".yml", tree) } } diff --git a/services/markup/renderhelper_codepreview.go b/services/markup/renderhelper_codepreview.go index fa1eb824a2f54..17d94e1c36149 100644 --- a/services/markup/renderhelper_codepreview.go +++ b/services/markup/renderhelper_codepreview.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/languagestats" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/indexer/code" @@ -62,7 +63,7 @@ func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePrevie } language, _ := languagestats.GetFileLanguage(ctx, gitRepo, opts.CommitID, opts.FilePath) - blob, err := commit.GetBlobByPath(opts.FilePath) + blob, err := git.NewTree(gitRepo, commit.TreeID).GetBlobByPath(opts.FilePath) if err != nil { return "", err } diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 75eb06d01fa3f..5e8eef5aeb523 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -302,7 +302,7 @@ func (g *GiteaLocalUploader) CreateReleases(ctx context.Context, releases ...*ba return fmt.Errorf("GetTagCommit[%v]: %w", rel.TagName, err) } rel.Sha1 = commit.ID.String() - rel.NumCommits, err = commit.CommitsCount() + rel.NumCommits, err = g.gitRepo.CommitsCount(commit.ID.String()) if err != nil { return fmt.Errorf("CommitsCount: %w", err) } diff --git a/services/pull/merge.go b/services/pull/merge.go index a941c204357ac..5877cc7bc2290 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -71,7 +71,7 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue if err != nil { return "", "", err } - templateContent, err := commit.GetFileContent(templateFilepath, setting.Repository.PullRequest.DefaultMergeMessageSize) + templateContent, err := git.NewTree(baseGitRepo, commit.TreeID).GetFileContent(templateFilepath, setting.Repository.PullRequest.DefaultMergeMessageSize) if err != nil { if !git.IsErrNotExist(err) { return "", "", err diff --git a/services/pull/pull.go b/services/pull/pull.go index 20d33b53311c7..a63ec2b603735 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -1038,7 +1038,7 @@ func IsHeadEqualWithBranch(ctx context.Context, pr *issues_model.PullRequest, br return false, err } } - return baseCommit.HasPreviousCommit(headCommit.ID) + return baseGitRepo.HasPreviousCommit(baseCommit, headCommit.ID) } type CommitInfo struct { diff --git a/services/release/release.go b/services/release/release.go index 1e2132bb60c7a..62bee73947ab9 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -147,7 +147,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel } rel.Sha1 = commit.ID.String() - rel.NumCommits, err = commit.CommitsCount() + rel.NumCommits, err = gitRepo.CommitsCount(commit.ID.String()) if err != nil { return false, fmt.Errorf("CommitsCount: %w", err) } diff --git a/services/repository/branch.go b/services/repository/branch.go index df7202227aa70..5bf110243d284 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -739,7 +739,7 @@ func GetBranchDivergingInfo(ctx reqctx.RequestContext, baseRepo *repo_model.Repo if err != nil { return nil, err } - hasPreviousCommit, _ := headCommit.HasPreviousCommit(baseCommitID) + hasPreviousCommit, _ := headGitRepo.HasPreviousCommit(headCommit, baseCommitID) info.BaseHasNewCommits = !hasPreviousCommit return info, nil } diff --git a/services/repository/cache.go b/services/repository/cache.go index b0811a99fc03b..d4411779f324c 100644 --- a/services/repository/cache.go +++ b/services/repository/cache.go @@ -19,12 +19,17 @@ func CacheRef(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep } if gitRepo.LastCommitCache == nil { - commitsCount, err := cache.GetInt64(repo.GetCommitsCountCacheKey(fullRefName.ShortName(), true), commit.CommitsCount) + commitsCount, err := cache.GetInt64(repo.GetCommitsCountCacheKey(fullRefName.ShortName(), true), func() (int64, error) { + return git.CommitsCount(gitRepo.Ctx, git.CommitsCountOptions{ + RepoPath: gitRepo.Path, + Revision: []string{commit.ID.String()}, + }) + }) if err != nil { return err } gitRepo.LastCommitCache = git.NewLastCommitCache(commitsCount, repo.FullName(), gitRepo, cache.GetCache()) } - return commit.CacheCommit(ctx) + return gitRepo.CacheCommit(ctx, commit) } diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go index 6818bb343d2ee..caab47c1e032c 100644 --- a/services/repository/files/cherry_pick.go +++ b/services/repository/files/cherry_pick.go @@ -140,8 +140,8 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod return nil, err } - fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil - verification := GetPayloadCommitVerification(ctx, commit) + fileCommitResponse, _ := GetFileCommitResponse(repo, t.gitRepo, commit) // ok if fails, then will be nil + verification := GetPayloadCommitVerification(ctx, t.gitRepo, commit) fileResponse := &structs.FileResponse{ Commit: fileCommitResponse, Verification: verification, diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go index 3cc326d065b16..b2a07b6234c5a 100644 --- a/services/repository/files/commit.go +++ b/services/repository/files/commit.go @@ -22,9 +22,9 @@ func CountDivergingCommits(ctx context.Context, repo *repo_model.Repository, bra } // GetPayloadCommitVerification returns the verification information of a commit -func GetPayloadCommitVerification(ctx context.Context, commit *git.Commit) *structs.PayloadCommitVerification { +func GetPayloadCommitVerification(ctx context.Context, gitRepo *git.Repository, commit *git.Commit) *structs.PayloadCommitVerification { verification := &structs.PayloadCommitVerification{} - commitVerification := asymkey_service.ParseCommitWithSignature(ctx, commit) + commitVerification := asymkey_service.ParseCommitWithSignature(ctx, gitRepo, commit) if commit.Signature != nil { verification.Signature = commit.Signature.Signature verification.Payload = commit.Signature.Payload diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 2c1e88bb59ecb..2bcb6c99ccfcc 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -46,7 +46,7 @@ type GetContentsOrListOptions struct { // GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree // directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, opts GetContentsOrListOptions) (ret api.ContentsExtResponse, _ error) { - entry, err := prepareGetContentsEntry(refCommit, &opts.TreePath) + entry, err := prepareGetContentsEntry(gitRepo, refCommit, &opts.TreePath) if repo.IsEmpty && opts.TreePath == "" { return api.ContentsExtResponse{DirContents: make([]*api.ContentsResponse, 0)}, nil } @@ -61,7 +61,8 @@ func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, gitRepo } // list directory contents - gitTree, err := refCommit.Commit.SubTree(opts.TreePath) + tree := git.NewTree(gitRepo, refCommit.Commit.TreeID) + gitTree, err := tree.SubTree(opts.TreePath) if err != nil { return ret, err } @@ -99,7 +100,7 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType { } } -func prepareGetContentsEntry(refCommit *utils.RefCommit, treePath *string) (*git.TreeEntry, error) { +func prepareGetContentsEntry(gitRepo *git.Repository, refCommit *utils.RefCommit, treePath *string) (*git.TreeEntry, error) { // Check that the path given in opts.treePath is valid (not a git path) cleanTreePath := CleanGitTreePath(*treePath) if cleanTreePath == "" && *treePath != "" { @@ -113,12 +114,13 @@ func prepareGetContentsEntry(refCommit *utils.RefCommit, treePath *string) (*git return nil, util.NewNotExistErrorf("no commit found for the ref [ref: %s]", refCommit.RefName) } - return refCommit.Commit.GetTreeEntryByPath(*treePath) + tree := git.NewTree(gitRepo, refCommit.Commit.TreeID) + return tree.GetTreeEntryByPath(*treePath) } // GetFileContents gets the metadata on a file's contents. Ref can be a branch, commit or tag func GetFileContents(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, opts GetContentsOrListOptions) (*api.ContentsResponse, error) { - entry, err := prepareGetContentsEntry(refCommit, &opts.TreePath) + entry, err := prepareGetContentsEntry(gitRepo, refCommit, &opts.TreePath) if err != nil { return nil, err } @@ -146,13 +148,15 @@ func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Reposito }, } + tree := git.NewTree(gitRepo, commit.TreeID) + if opts.IncludeCommitMetadata || opts.IncludeCommitMessage { err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID) if err != nil { return nil, err } - lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath) + lastCommit, err := gitRepo.GetCommitByPath(refCommit.Commit.ID, opts.TreePath) if err != nil { return nil, err } @@ -202,7 +206,7 @@ func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Reposito contentsResponse.Target = &targetFromContent } else if entry.IsSubModule() { contentsResponse.Type = string(ContentTypeSubmodule) - submodule, err := commit.GetSubModule(opts.TreePath) + submodule, err := tree.GetSubModule(opts.TreePath) if err != nil { return nil, err } diff --git a/services/repository/files/file.go b/services/repository/files/file.go index f48e32b427b24..75c92142b8fb8 100644 --- a/services/repository/files/file.go +++ b/services/repository/files/file.go @@ -45,8 +45,8 @@ func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Reposito func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treeNames []string) (*api.FilesResponse, error) { files := GetContentsListFromTreePaths(ctx, repo, gitRepo, refCommit, treeNames) - fileCommitResponse, _ := GetFileCommitResponse(repo, refCommit.Commit) // ok if fails, then will be nil - verification := GetPayloadCommitVerification(ctx, refCommit.Commit) + fileCommitResponse, _ := GetFileCommitResponse(repo, gitRepo, refCommit.Commit) // ok if fails, then will be nil + verification := GetPayloadCommitVerification(ctx, gitRepo, refCommit.Commit) filesResponse := &api.FilesResponse{ Files: files, Commit: fileCommitResponse, @@ -70,18 +70,19 @@ func GetFileResponseFromFilesResponse(filesResponse *api.FilesResponse, index in } // GetFileCommitResponse Constructs a FileCommitResponse from a Commit object -func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*api.FileCommitResponse, error) { +func GetFileCommitResponse(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit) (*api.FileCommitResponse, error) { if repo == nil { return nil, errors.New("repo cannot be nil") } if commit == nil { return nil, errors.New("commit cannot be nil") } + commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String())) - commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.Tree.ID.String())) + commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.TreeID.String())) parents := make([]*api.CommitMeta, commit.ParentCount()) for i := 0; i <= commit.ParentCount(); i++ { - if parent, err := commit.Parent(i); err == nil && parent != nil { + if parent, err := gitRepo.ParentCommit(commit, i); err == nil && parent != nil { parentCommitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(parent.ID.String())) parents[i] = &api.CommitMeta{ SHA: parent.ID.String(), @@ -113,7 +114,7 @@ func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*ap Message: commit.Message(), Tree: &api.CommitMeta{ URL: commitTreeURL.String(), - SHA: commit.Tree.ID.String(), + SHA: commit.TreeID.String(), }, Parents: parents, } diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index 11a8744b7f4ba..92d91f129bdf6 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -109,7 +109,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode } // ApplyDiffPatch applies a patch to the given repository -func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) { +func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doer *user_model.User, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) { err := repo.MustNotBeArchived() if err != nil { return nil, err @@ -209,8 +209,8 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user return nil, err } - fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil - verification := GetPayloadCommitVerification(ctx, commit) + fileCommitResponse, _ := GetFileCommitResponse(repo, gitRepo, commit) // ok if fails, then will be nil + verification := GetPayloadCommitVerification(ctx, gitRepo, commit) fileResponse := &structs.FileResponse{ Commit: fileCommitResponse, Verification: verification, diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index e481a3e7d2672..719ebd9ff1de1 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -151,14 +151,14 @@ func (node *TreeViewNode) sortLevel() int { return util.Iif(node.EntryMode == "tree" || node.EntryMode == "commit", 0, 1) } -func newTreeViewNodeFromEntry(ctx context.Context, repoLink string, renderedIconPool *fileicon.RenderedIconPool, commit *git.Commit, parentDir string, entry *git.TreeEntry) *TreeViewNode { +func newTreeViewNodeFromEntry(ctx context.Context, repoLink string, renderedIconPool *fileicon.RenderedIconPool, tree *git.Tree, parentDir string, entry *git.TreeEntry) *TreeViewNode { node := &TreeViewNode{ EntryName: entry.Name(), EntryMode: entryModeString(entry.Mode()), FullPath: path.Join(parentDir, entry.Name()), } - entryInfo := fileicon.EntryInfoFromGitTreeEntry(commit, node.FullPath, entry) + entryInfo := fileicon.EntryInfoFromGitTreeEntry(tree, node.FullPath, entry) node.EntryIcon = fileicon.RenderEntryIconHTML(renderedIconPool, entryInfo) if entryInfo.EntryMode.IsDir() { entryInfo.IsOpen = true @@ -166,10 +166,10 @@ func newTreeViewNodeFromEntry(ctx context.Context, repoLink string, renderedIcon } if node.EntryMode == "commit" { - if subModule, err := commit.GetSubModule(node.FullPath); err != nil { + if subModule, err := tree.GetSubModule(node.FullPath); err != nil { log.Error("GetSubModule: %v", err) } else if subModule != nil { - submoduleFile := git.NewCommitSubmoduleFile(repoLink, node.FullPath, subModule.URL, entry.ID.String()) + submoduleFile := git.NewSubmoduleFile(repoLink, node.FullPath, subModule.URL, entry.ID.String()) webLink := submoduleFile.SubmoduleWebLinkTree(ctx) if webLink != nil { node.SubmoduleURL = webLink.CommitWebLink @@ -191,7 +191,7 @@ func sortTreeViewNodes(nodes []*TreeViewNode) { }) } -func listTreeNodes(ctx context.Context, repoLink string, renderedIconPool *fileicon.RenderedIconPool, commit *git.Commit, tree *git.Tree, treePath, subPath string) ([]*TreeViewNode, error) { +func listTreeNodes(ctx context.Context, repoLink string, renderedIconPool *fileicon.RenderedIconPool, tree *git.Tree, treePath, subPath string) ([]*TreeViewNode, error) { entries, err := tree.ListEntries() if err != nil { return nil, err @@ -200,14 +200,14 @@ func listTreeNodes(ctx context.Context, repoLink string, renderedIconPool *filei subPathDirName, subPathRemaining, _ := strings.Cut(subPath, "/") nodes := make([]*TreeViewNode, 0, len(entries)) for _, entry := range entries { - node := newTreeViewNodeFromEntry(ctx, repoLink, renderedIconPool, commit, treePath, entry) + node := newTreeViewNodeFromEntry(ctx, repoLink, renderedIconPool, tree, treePath, entry) nodes = append(nodes, node) if entry.IsDir() && subPathDirName == entry.Name() { subTreePath := treePath + "/" + node.EntryName if subTreePath[0] == '/' { subTreePath = subTreePath[1:] } - subNodes, err := listTreeNodes(ctx, repoLink, renderedIconPool, commit, entry.Tree(), subTreePath, subPathRemaining) + subNodes, err := listTreeNodes(ctx, repoLink, renderedIconPool, entry.Tree(), subTreePath, subPathRemaining) if err != nil { log.Error("listTreeNodes: %v", err) } else { @@ -219,10 +219,10 @@ func listTreeNodes(ctx context.Context, repoLink string, renderedIconPool *filei return nodes, nil } -func GetTreeViewNodes(ctx context.Context, repoLink string, renderedIconPool *fileicon.RenderedIconPool, commit *git.Commit, treePath, subPath string) ([]*TreeViewNode, error) { - entry, err := commit.GetTreeEntryByPath(treePath) +func GetTreeViewNodes(ctx context.Context, repoLink string, renderedIconPool *fileicon.RenderedIconPool, tree *git.Tree, treePath, subPath string) ([]*TreeViewNode, error) { + entry, err := tree.GetTreeEntryByPath(treePath) if err != nil { return nil, err } - return listTreeNodes(ctx, repoLink, renderedIconPool, commit, entry.Tree(), treePath, subPath) + return listTreeNodes(ctx, repoLink, renderedIconPool, entry.Tree(), treePath, subPath) } diff --git a/services/repository/files/tree_test.go b/services/repository/files/tree_test.go index 38ac9f25fc2db..e076f33da8e1e 100644 --- a/services/repository/files/tree_test.go +++ b/services/repository/files/tree_test.go @@ -75,7 +75,8 @@ func TestGetTreeViewNodes(t *testing.T) { mockOpenIconForFolder := func(id string) template.HTML { return template.HTML(``) } - treeNodes, err := GetTreeViewNodes(ctx, curRepoLink, renderedIconPool, ctx.Repo.Commit, "", "") + tree := git.NewTree(ctx.Repo.GitRepo, ctx.Repo.Commit.TreeID) + treeNodes, err := GetTreeViewNodes(ctx, curRepoLink, renderedIconPool, tree, "", "") assert.NoError(t, err) assert.Equal(t, []*TreeViewNode{ { @@ -87,7 +88,7 @@ func TestGetTreeViewNodes(t *testing.T) { }, }, treeNodes) - treeNodes, err = GetTreeViewNodes(ctx, curRepoLink, renderedIconPool, ctx.Repo.Commit, "", "docs/README.md") + treeNodes, err = GetTreeViewNodes(ctx, curRepoLink, renderedIconPool, tree, "", "docs/README.md") assert.NoError(t, err) assert.Equal(t, []*TreeViewNode{ { @@ -107,7 +108,7 @@ func TestGetTreeViewNodes(t *testing.T) { }, }, treeNodes) - treeNodes, err = GetTreeViewNodes(ctx, curRepoLink, renderedIconPool, ctx.Repo.Commit, "docs", "README.md") + treeNodes, err = GetTreeViewNodes(ctx, curRepoLink, renderedIconPool, tree, "docs", "README.md") assert.NoError(t, err) assert.Equal(t, []*TreeViewNode{ { diff --git a/services/repository/files/update.go b/services/repository/files/update.go index e871f777e544a..d1e00cde6729a 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -250,7 +250,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use } for _, file := range opts.Files { - if err = handleCheckErrors(file, commit, opts); err != nil { + if err = handleCheckErrors(t.gitRepo, file, commit, opts); err != nil { return nil, err } } @@ -387,12 +387,13 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string { } // handles the check for various issues for ChangeRepoFiles -func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error { +func handleCheckErrors(gitRepo *git.Repository, file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error { // check old entry (fromTreePath/fromEntry) + tree := git.NewTree(gitRepo, commit.TreeID) if file.Operation == "update" || file.Operation == "upload" || file.Operation == "delete" || file.Operation == "rename" { var fromEntryIDString string { - fromEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath) + fromEntry, err := tree.GetTreeEntryByPath(file.Options.fromTreePath) if file.Operation == "upload" && git.IsErrNotExist(err) { fromEntry = nil } else if err != nil { @@ -417,7 +418,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep // If a lastCommitID given doesn't match the branch head's commitID throw // an error, but only if we aren't creating a new branch. if commit.ID.String() != opts.LastCommitID && opts.OldBranch == opts.NewBranch { - if changed, err := commit.FileChangedSinceCommit(file.Options.treePath, opts.LastCommitID); err != nil { + if changed, err := gitRepo.FileChangedBetweenCommits(file.Options.treePath, opts.LastCommitID, commit.ID.String()); err != nil { return err } else if changed { return ErrCommitIDDoesNotMatch{ @@ -443,7 +444,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep subTreePath := "" for index, part := range treePathParts { subTreePath = path.Join(subTreePath, part) - entry, err := commit.GetTreeEntryByPath(subTreePath) + entry, err := tree.GetTreeEntryByPath(subTreePath) if err != nil { if git.IsErrNotExist(err) { // Means there is no item with that name, so we're good @@ -622,7 +623,7 @@ func writeRepoObjectForRename(ctx context.Context, t *TemporaryUploadRepository, if err != nil { return nil, err } - oldEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath) + oldEntry, err := git.NewTree(t.gitRepo, commit.TreeID).GetTreeEntryByPath(file.Options.fromTreePath) if err != nil { return nil, err } diff --git a/services/repository/gitgraph/graph_models.go b/services/repository/gitgraph/graph_models.go index 02b0268cd9a26..c4b3ff92a22bb 100644 --- a/services/repository/gitgraph/graph_models.go +++ b/services/repository/gitgraph/graph_models.go @@ -115,7 +115,7 @@ func (graph *Graph) LoadAndProcessCommits(ctx context.Context, repository *repo_ } } - c.Verification = asymkey_service.ParseCommitWithSignature(ctx, c.Commit) + c.Verification = asymkey_service.ParseCommitWithSignature(ctx, gitRepo, c.Commit) _ = asymkey_model.CalculateTrustStatus(c.Verification, repository.GetTrustModel(), func(user *user_model.User) (bool, error) { return repo_model.IsOwnerMemberCollaborator(ctx, repository, user.ID) diff --git a/services/repository/license.go b/services/repository/license.go index 8622911fa24aa..f61441a609a06 100644 --- a/services/repository/license.go +++ b/services/repository/license.go @@ -84,7 +84,7 @@ func repoLicenseUpdater(items ...*LicenseUpdaterOptions) []*LicenseUpdaterOption log.Error("repoLicenseUpdater [%d] failed: GetBranchCommit: %v", opts.RepoID, err) continue } - if err = UpdateRepoLicenses(ctx, repo, commit); err != nil { + if err = UpdateRepoLicenses(ctx, repo, gitRepo, commit); err != nil { log.Error("repoLicenseUpdater [%d] failed: updateRepoLicenses: %v", opts.RepoID, err) } } @@ -115,12 +115,12 @@ func SyncRepoLicenses(ctx context.Context) error { } // UpdateRepoLicenses will update repository licenses col if license file exists -func UpdateRepoLicenses(ctx context.Context, repo *repo_model.Repository, commit *git.Commit) error { +func UpdateRepoLicenses(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit) error { if commit == nil { return nil } - b, err := commit.GetBlobByPath(LicenseFileName) + b, err := git.NewTree(gitRepo, commit.TreeID).GetBlobByPath(LicenseFileName) if err != nil && !git.IsErrNotExist(err) { return fmt.Errorf("GetBlobByPath: %w", err) } diff --git a/services/repository/push.go b/services/repository/push.go index 7c68a7f176308..c45d8b6ff1b07 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -180,9 +180,9 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { // Push new branch. var l []*git.Commit if opts.IsNewRef() { - l, err = pushNewBranch(ctx, repo, pusher, opts, newCommit) + l, err = pushNewBranch(ctx, repo, gitRepo, pusher, opts, newCommit) } else { - l, err = pushUpdateBranch(ctx, repo, pusher, opts, newCommit) + l, err = pushUpdateBranch(ctx, repo, gitRepo, pusher, opts, newCommit) } if err != nil { return err @@ -273,7 +273,7 @@ func getCompareURL(repo *repo_model.Repository, gitRepo *git.Repository, objectF return "" } -func pushNewBranch(ctx context.Context, repo *repo_model.Repository, pusher *user_model.User, opts *repo_module.PushUpdateOptions, newCommit *git.Commit) ([]*git.Commit, error) { +func pushNewBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, pusher *user_model.User, opts *repo_module.PushUpdateOptions, newCommit *git.Commit) ([]*git.Commit, error) { if repo.IsEmpty { // Change default branch and empty status only if pushed ref is non-empty branch. repo.DefaultBranch = opts.RefName() repo.IsEmpty = false @@ -288,7 +288,7 @@ func pushNewBranch(ctx context.Context, repo *repo_model.Repository, pusher *use } } - l, err := newCommit.CommitsBeforeLimit(10) + l, err := gitRepo.CommitsBeforeLimit(newCommit.ID, 10) if err != nil { return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %w", err) } @@ -296,15 +296,19 @@ func pushNewBranch(ctx context.Context, repo *repo_model.Repository, pusher *use return l, nil } -func pushUpdateBranch(_ context.Context, repo *repo_model.Repository, pusher *user_model.User, opts *repo_module.PushUpdateOptions, newCommit *git.Commit) ([]*git.Commit, error) { - l, err := newCommit.CommitsBeforeUntil(opts.OldCommitID) +func pushUpdateBranch(_ context.Context, repo *repo_model.Repository, gitRepo *git.Repository, pusher *user_model.User, opts *repo_module.PushUpdateOptions, newCommit *git.Commit) ([]*git.Commit, error) { + oldCommit, err := gitRepo.GetCommit(opts.OldCommitID) + if err != nil { + return nil, fmt.Errorf("gitRepo.GetCommit(%s): %w", opts.OldCommitID, err) + } + l, err := gitRepo.CommitsBetween(newCommit, oldCommit) if err != nil { return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %w", err) } branch := opts.RefFullName.BranchName() - isForcePush, err := newCommit.IsForcePush(opts.OldCommitID) + isForcePush, err := gitRepo.IsForcePush(newCommit, opts.OldCommitID) if err != nil { log.Error("IsForcePush %s:%s failed: %v", repo.FullName(), branch, err) } diff --git a/tests/integration/api_packages_cargo_test.go b/tests/integration/api_packages_cargo_test.go index d7a89e446d7cd..485527ec6150a 100644 --- a/tests/integration/api_packages_cargo_test.go +++ b/tests/integration/api_packages_cargo_test.go @@ -16,6 +16,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/json" cargo_module "code.gitea.io/gitea/modules/packages/cargo" @@ -83,7 +84,7 @@ func testPackageCargo(t *testing.T, _ *neturl.URL) { commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) assert.NoError(t, err) - blob, err := commit.GetBlobByPath(path) + blob, err := git.NewTree(gitRepo, commit.TreeID).GetBlobByPath(path) assert.NoError(t, err) content, err := blob.GetBlobContent(1024) diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index af3bc546803a1..83eb66f367b60 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -186,7 +186,7 @@ func TestAPICreateFile(t *testing.T) { gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1) defer gitRepo.Close() commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) - lastCommit, _ := gitRepo.GetCommitByPath(treePath) + lastCommit, _ := gitRepo.GetCommitByPathDefaultBranch(treePath) expectedFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{ repoFullName: "user2/repo1", commitID: commitID, @@ -312,7 +312,7 @@ func TestAPICreateFile(t *testing.T) { gitRepo, _ := gitrepo.OpenRepository(t.Context(), emptyRepo) defer gitRepo.Close() commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) - latestCommit, _ := gitRepo.GetCommitByPath(treePath) + latestCommit, _ := gitRepo.GetCommitByPathDefaultBranch(treePath) expectedFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{ repoFullName: "user2/empty-repo", commitID: commitID, diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index 9a56711da6a1d..5a6a245a2c92f 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -142,7 +142,7 @@ func TestAPIUpdateFile(t *testing.T) { gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1) defer gitRepo.Close() commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) - lasCommit, _ := gitRepo.GetCommitByPath(treePath) + lasCommit, _ := gitRepo.GetCommitByPathDefaultBranch(treePath) expectedFileResponse := getExpectedFileResponseForUpdate(apiFileResponseInfo{ commitID: commitID, treePath: treePath, diff --git a/tests/integration/api_repo_files_change_test.go b/tests/integration/api_repo_files_change_test.go index 999bcdc680fbc..9713e8a755dd7 100644 --- a/tests/integration/api_repo_files_change_test.go +++ b/tests/integration/api_repo_files_change_test.go @@ -96,8 +96,8 @@ func TestAPIChangeFiles(t *testing.T) { gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1) defer gitRepo.Close() commitID, _ := gitRepo.GetBranchCommitID(changeFilesOptions.NewBranchName) - createLasCommit, _ := gitRepo.GetCommitByPath(createTreePath) - updateLastCommit, _ := gitRepo.GetCommitByPath(updateTreePath) + createLasCommit, _ := gitRepo.GetCommitByPathDefaultBranch(createTreePath) + updateLastCommit, _ := gitRepo.GetCommitByPathDefaultBranch(updateTreePath) expectedCreateFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{ repoFullName: fmt.Sprintf("%s/%s", user2.Name, repo1.Name), commitID: commitID, diff --git a/tests/integration/api_repo_files_get_test.go b/tests/integration/api_repo_files_get_test.go index edf4f390634e4..68d18775d1664 100644 --- a/tests/integration/api_repo_files_get_test.go +++ b/tests/integration/api_repo_files_get_test.go @@ -45,7 +45,7 @@ func TestAPIGetRequestedFiles(t *testing.T) { gitRepo, err := gitrepo.OpenRepository(t.Context(), repo1) assert.NoError(t, err) defer gitRepo.Close() - lastCommit, _ := gitRepo.GetCommitByPath("README.md") + lastCommit, _ := gitRepo.GetCommitByPathDefaultBranch("README.md") requestFiles := func(t *testing.T, url string, files []string, expectedStatusCode ...int) (ret []*api.ContentsResponse) { req := NewRequestWithJSON(t, "POST", url, &api.GetFilesOptions{Files: files}) diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go index 5a53b0eca9ed2..8ce1b5306f644 100644 --- a/tests/integration/api_repo_get_contents_list_test.go +++ b/tests/integration/api_repo_get_contents_list_test.go @@ -98,7 +98,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { var contentsListResponse []*api.ContentsResponse DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - lastCommit, err := gitRepo.GetCommitByPath("README.md") + lastCommit, err := gitRepo.GetCommitByPathDefaultBranch("README.md") assert.NoError(t, err) expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.Equal(t, expectedContentsListResponse, contentsListResponse) @@ -122,7 +122,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { assert.NotNil(t, contentsListResponse) branchCommit, err := gitRepo.GetBranchCommit(ref) assert.NoError(t, err) - lastCommit, err = branchCommit.GetCommitByPath("README.md") + lastCommit, err = gitRepo.GetCommitByPath(branchCommit.ID, "README.md") assert.NoError(t, err) expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.Equal(t, expectedContentsListResponse, contentsListResponse) @@ -136,7 +136,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { assert.NotNil(t, contentsListResponse) tagCommit, err := gitRepo.GetTagCommit(ref) assert.NoError(t, err) - lastCommit, err = tagCommit.GetCommitByPath("README.md") + lastCommit, err = gitRepo.GetCommitByPath(tagCommit.ID, "README.md") assert.NoError(t, err) expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.Equal(t, expectedContentsListResponse, contentsListResponse) diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go index 3e863eed6ff7e..3906781e93e37 100644 --- a/tests/integration/api_repo_get_contents_test.go +++ b/tests/integration/api_repo_get_contents_test.go @@ -108,7 +108,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = MakeRequest(t, req, http.StatusOK) var contentsResponse api.ContentsResponse DecodeJSON(t, resp, &contentsResponse) - lastCommit, _ := gitRepo.GetCommitByPath("README.md") + lastCommit, _ := gitRepo.GetCommitByPathDefaultBranch("README.md") expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.Equal(t, *expectedContentsResponse, contentsResponse) @@ -127,7 +127,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) branchCommit, _ := gitRepo.GetBranchCommit(ref) - lastCommit, _ = branchCommit.GetCommitByPath("README.md") + lastCommit, _ = gitRepo.GetCommitByPath(branchCommit.ID, "README.md") expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.Equal(t, *expectedContentsResponse, contentsResponse) @@ -138,7 +138,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) tagCommit, _ := gitRepo.GetTagCommit(ref) - lastCommit, _ = tagCommit.GetCommitByPath("README.md") + lastCommit, _ = gitRepo.GetCommitByPath(tagCommit.ID, "README.md") expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.Equal(t, *expectedContentsResponse, contentsResponse) diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index 5dd2aabcdfbe3..4a04e505c1547 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -318,9 +318,9 @@ index 0000000000..bbbbbbbbbb }, ) - commit1, err := gitRepo.GetCommitByPath("patch-file-1.txt") + commit1, err := gitRepo.GetCommitByPathDefaultBranch("patch-file-1.txt") require.NoError(t, err) - commit2, err := gitRepo.GetCommitByPath("patch-file-2.txt") + commit2, err := gitRepo.GetCommitByPathDefaultBranch("patch-file-2.txt") require.NoError(t, err) resp1, _ := testWebGit(t, "/user2/repo1/_cherrypick/"+commit1.ID.String()+"/master", map[string]string{"revert": "true"}, diff --git a/tests/integration/git_misc_test.go b/tests/integration/git_misc_test.go index eb039c7c1c9cf..ca2c06b0b5fb0 100644 --- a/tests/integration/git_misc_test.go +++ b/tests/integration/git_misc_test.go @@ -49,7 +49,7 @@ func TestDataAsyncDoubleRead_Issue29101(t *testing.T) { commit, err := gitRepo.GetCommit(sha) assert.NoError(t, err) - entry, err := commit.GetTreeEntryByPath("test.txt") + entry, err := git.NewTree(gitRepo, commit.TreeID).GetTreeEntryByPath("test.txt") assert.NoError(t, err) b := entry.Blob() diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index 6821f8bf61175..b33c1d65e9445 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -415,7 +415,7 @@ func TestChangeRepoFilesForCreate(t *testing.T) { defer gitRepo.Close() commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) - lastCommit, _ := gitRepo.GetCommitByPath("new/file.txt") + lastCommit, _ := gitRepo.GetCommitByPathDefaultBranch("new/file.txt") expectedFileResponse := getExpectedFileResponseForRepoFilesCreate(commitID, lastCommit) assert.NotNil(t, expectedFileResponse) if expectedFileResponse != nil { @@ -452,7 +452,7 @@ func TestChangeRepoFilesForUpdate(t *testing.T) { defer gitRepo.Close() commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) - lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath) + lastCommit, _ := gitRepo.GetCommitByPath(commit.ID, opts.Files[0].TreePath) expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When) assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0]) assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA) @@ -488,17 +488,18 @@ func TestChangeRepoFilesForUpdateWithFileMove(t *testing.T) { defer gitRepo.Close() commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) - lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath) + lastCommit, _ := gitRepo.GetCommitByPath(commit.ID, opts.Files[0].TreePath) expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When) // assert that the old file no longer exists in the last commit of the branch - fromEntry, err := commit.GetTreeEntryByPath(opts.Files[0].FromTreePath) + tree := git.NewTree(gitRepo, commit.TreeID) + fromEntry, err := tree.GetTreeEntryByPath(opts.Files[0].FromTreePath) switch err.(type) { case git.ErrNotExist: // correct, continue default: t.Fatalf("expected git.ErrNotExist, got:%v", err) } - toEntry, err := commit.GetTreeEntryByPath(opts.Files[0].TreePath) + toEntry, err := tree.GetTreeEntryByPath(opts.Files[0].TreePath) assert.NoError(t, err) assert.Nil(t, fromEntry) // Should no longer exist here assert.NotNil(t, toEntry) // Should exist here @@ -534,7 +535,7 @@ func TestChangeRepoFilesForUpdateWithFileRename(t *testing.T) { defer gitRepo.Close() commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch) - lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath) + lastCommit, _ := gitRepo.GetCommitByPath(commit.ID, opts.Files[0].TreePath) expectedFileResponse := getExpectedFileResponseForRepoFilesUpdateRename(commit.ID.String(), lastCommit.ID.String()) for _, file := range filesResponse.Files { file.LastCommitterDate, file.LastAuthorDate = nil, nil // there might be different time in one operation, so we ignore them @@ -571,7 +572,7 @@ func TestChangeRepoFilesWithoutBranchNames(t *testing.T) { defer gitRepo.Close() commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch) - lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath) + lastCommit, _ := gitRepo.GetCommitByPath(commit.ID, opts.Files[0].TreePath) expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When) assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0]) })

AltStyle によって変換されたページ (->オリジナル) /