// SPDX-FileCopyrightText: 2025 Romain Maneschi // // SPDX-License-Identifier: EUPL-1.2 package repository import ( "errors" "fmt" "io" "strings" git "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/format/diff" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/storer" "github.com/samber/oops" ) type LastCommit struct { Commit *object.Commit Filepath []string } func commitChange(com *object.Commit, useToFound bool, toFound map[string]bool) (LastCommit, error) { currentRes := LastCommit{Commit: com, Filepath: []string{}} tree, err := com.Tree() if err != nil { return currentRes, oops.Wrapf(err, "can't tree commit %s", com.Hash.String()) } p, err := com.Parents().Next() if err != nil && !errors.Is(err, io.EOF) { return currentRes, oops.Wrapf(err, "can't parent %s", com.Hash.String()) } var pTree *object.Tree if p != nil { pTree, err = p.Tree() if err != nil { return currentRes, oops.Wrapf(err, "can't pTree commit %s", p.Hash.String()) } } changes, err := object.DiffTree(tree, pTree) if err != nil { return currentRes, oops.Wrapf(err, "can't DiffTree commit %s and %s", com.Hash.String(), p.Hash.String()) } for _, file := range changes { name := file.From.Name if file.From.Name == "" { name = file.To.Name } if useToFound { if val, ok := toFound[name]; ok && !val { currentRes.Filepath = append(currentRes.Filepath, name) toFound[name] = true } } else { currentRes.Filepath = append(currentRes.Filepath, name) } } return currentRes, nil } func (repo *GitRootRepository) GetLastCommits(filepath []string) ([]LastCommit, error) { comIter, err := repo.repo.Log(&git.LogOptions{Order: git.LogOrderCommitterTime}) if err != nil { return nil, repo.errorHandler.Wrapf(err, "can't Log") } defer comIter.Close() nbToFound := len(filepath) found := make(map[string]bool, len(filepath)) for _, path := range filepath { found[path] = false } res := []LastCommit{} err = comIter.ForEach(func(com *object.Commit) error { currentRes, err := commitChange(com, true, found) if err != nil { return oops.Wrapf(err, "commitChange") } if len(currentRes.Filepath) > 0 { res = append(res, currentRes) for _, path := range currentRes.Filepath { found[path] = true nbToFound = nbToFound - 1 } } if nbToFound == 0 { return storer.ErrStop } return nil }) return res, oops.Wrapf(err, "err in log fro GetLastCommit") } func (repo *GitRootRepositoryWrite) GetDiff(hash plumbing.Hash, filepath string) (string, error) { com, err := repo.repoWrite.CommitObject(hash) if err != nil { return "", repo.errorHandler.Wrapf(err, "can't find CommitObject") } parent, _ := com.Parent(0) diffStr := "```diff" patch, _ := parent.Patch(com) // TODO find a way to get diff only on one file for _, d := range patch.FilePatches() { from, to := d.Files() if from != nil && to != nil && from.Path() == to.Path() && from.Path() == filepath { for _, chunk := range d.Chunks() { op := "=" if chunk.Type() == diff.Add { op = "+" } else if chunk.Type() == diff.Delete { op = "-" } chunksStr := strings.Split(chunk.Content(), "\n") for i, c := range strings.Split(chunk.Content(), "\n") { chunksStr[i] = fmt.Sprintf("%s %s", op, c) } diffStr = fmt.Sprintf("%s\n%s", diffStr, strings.Join(chunksStr, "\n")) } break } } return diffStr + "\n```\n", nil }