GitRoot

craft your forge, build your project, grow your community freely
  1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
  2//
  3// SPDX-License-Identifier: EUPL-1.2
  4
  5package plugin
  6
  7import (
  8	"context"
  9	"fmt"
 10	"time"
 11
 12	"github.com/go-git/go-git/v5/plumbing"
 13	"github.com/go-git/go-git/v5/plumbing/object"
 14	"github.com/go-git/go-git/v5/plumbing/protocol/packp"
 15	"gitroot.dev/server/logger"
 16	"gitroot.dev/server/repository"
 17	"gitroot.dev/server/user"
 18)
 19
 20type commitForDiffAction int
 21
 22const (
 23	commitForDiffActionAdd commitForDiffAction = iota
 24	commitForDiffActionMod
 25	commitForDiffActionDel
 26)
 27
 28type CommandForDiff struct {
 29	branch       plumbing.ReferenceName
 30	branchAction commitForDiffAction
 31	commits      []commitForDiffCommit
 32	pusher       user.SimpleUser
 33}
 34
 35type commitForDiffCommit struct {
 36	parent      *object.Commit
 37	hash        plumbing.Hash
 38	message     string
 39	sshCommiter string
 40	files       []commitForDiffFile
 41	date        time.Time
 42	committer   string
 43}
 44
 45type commitForDiffFile struct {
 46	path    string
 47	oldPath string //only if action == commitForDiffActionMod
 48	action  commitForDiffAction
 49}
 50
 51func CommandForDiffFromPackpCmd(ctx context.Context, log *logger.Logger, repo *repository.GitRootRepository, commands []*packp.Command, pusher user.SimpleUser) ([]CommandForDiff, error) {
 52	res := make([]CommandForDiff, len(commands))
 53	for i, cmd := range commands {
 54		branchAction := commitForDiffActionMod
 55		log.Debug("CommandForDiffFromPackpCmd cmd", logger.NewLoggerPair("new", cmd.New.String()), logger.NewLoggerPair("old", cmd.Old.String()))
 56		if cmd.New == plumbing.ZeroHash {
 57			branchAction = commitForDiffActionDel
 58		} else if cmd.Old == plumbing.ZeroHash {
 59			branchAction = commitForDiffActionAdd
 60		}
 61		commits := make([]commitForDiffCommit, 0)
 62		if cmd.Old.String() != cmd.New.String() { //nothing todo
 63			if err := repo.WalkCommit(cmd.Old, cmd.New, func(com *object.Commit) error {
 64				parent, err := com.Parent(0) // TODO what parent?
 65				if err != nil {
 66					return err
 67				}
 68				patch, err := parent.PatchContext(ctx, com)
 69				if err != nil {
 70					return err
 71				}
 72				files := make([]commitForDiffFile, 0)
 73				for _, d := range patch.FilePatches() {
 74					from, to := d.Files()
 75					fileAction := commitForDiffActionMod
 76					path := ""
 77					oldPath := ""
 78					if from == nil && to != nil {
 79						fileAction = commitForDiffActionAdd
 80						path = to.Path()
 81					} else if from != nil && to == nil {
 82						fileAction = commitForDiffActionDel
 83						path = from.Path()
 84					} else {
 85						fileAction = commitForDiffActionMod
 86						path = to.Path()
 87						oldPath = from.Path()
 88					}
 89					files = append(files, commitForDiffFile{path: path, oldPath: oldPath, action: fileAction})
 90				}
 91				commits = append(commits, commitToCommitForDiff(com, parent, files))
 92				return nil
 93			}); err != nil {
 94				return nil, err
 95			}
 96		}
 97		res[i] = CommandForDiff{
 98			branch:       cmd.Name,
 99			branchAction: branchAction,
100			commits:      commits,
101			pusher:       pusher,
102		}
103	}
104	return res, nil
105}
106
107func CommandForDiffFromCommitCmd(ctx context.Context, repo *repository.GitRootRepository, plugin Plugin, com repository.LastCommit, cmd CommandForDiff) (CommandForDiff, error) {
108	files := []commitForDiffFile{}
109	for _, f := range com.Filepath {
110		files = append(files, commitForDiffFile{
111			path:    f,
112			oldPath: f,
113			action:  commitForDiffActionMod, //TODO can be add
114		})
115	}
116	comP, err := com.Commit.Parents().Next()
117	if err != nil {
118		return CommandForDiff{}, err
119	}
120	commits := []commitForDiffCommit{{
121		parent:      comP,
122		hash:        com.Commit.Hash,
123		message:     com.Commit.Message,
124		sshCommiter: plugin.commiter.SimpleUser.Ssh,
125		files:       files,
126		date:        com.Commit.Committer.When,
127	}}
128	return CommandForDiff{
129		branch:       cmd.branch,
130		branchAction: commitForDiffActionMod,
131		commits:      commits,
132		pusher:       plugin.commiter.SimpleUser,
133	}, nil
134}
135
136func commitToCommitForDiff(com *object.Commit, parent *object.Commit, files []commitForDiffFile) commitForDiffCommit {
137	return commitForDiffCommit{
138		parent:      parent,
139		hash:        com.Hash,
140		message:     com.Message,
141		sshCommiter: com.PGPSignature,
142		files:       files,
143		date:        com.Committer.When,
144		committer:   fmt.Sprintf("%s (%s)", com.Committer.Email, com.Committer.Name),
145	}
146}
147
148func (c CommandForDiff) IsFileTouched(branch plumbing.ReferenceName, filepath string) bool {
149	if branch != c.branch {
150		return false
151	}
152	for _, com := range c.commits {
153		for _, f := range com.files {
154			if f.path == filepath || f.oldPath == filepath {
155				return true
156			}
157		}
158	}
159	return false
160}
161
162func IsFileTouched(cmds []CommandForDiff, branch plumbing.ReferenceName, filepath string) bool {
163	for _, c := range cmds {
164		if branch != c.branch {
165			continue
166		}
167		for _, com := range c.commits {
168			for _, f := range com.files {
169				if f.path == filepath || f.oldPath == filepath {
170					return true
171				}
172			}
173		}
174	}
175	return false
176}