// SPDX-FileCopyrightText: 2025 Romain Maneschi // // SPDX-License-Identifier: EUPL-1.2 package repository import ( "errors" "fmt" "io" git "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/storer" "github.com/samber/oops" "github.com/sergi/go-diff/diffmatchpatch" ) 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, oldFilepath string, newFilepath 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) src, err := getFileContent(parent, oldFilepath) if err != nil { return "", repo.errorHandler.With("hash", parent.Hash.String(), "filepath", oldFilepath).Wrapf(err, "can't getFileContent parent") } dst, err := getFileContent(com, newFilepath) if err != nil { return "", repo.errorHandler.With("hash", hash.String(), "filepath", newFilepath).Wrapf(err, "can't getFileContent") } return "```diff\n" + diffmatchpatch.New().Unified( src, dst, diffmatchpatch.UnifiedContextLines(3), diffmatchpatch.UnifiedLabels(fmt.Sprintf("a/%s", oldFilepath), fmt.Sprintf("b/%s", newFilepath)), ) + "```\n", nil } func getFileContent(com *object.Commit, filepath string) (string, error) { fromTree, err := com.Tree() if err != nil { return "", err } fromFile, err := fromTree.File(filepath) if err != nil { return "", err } return fromFile.Contents() }