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