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}