+79
knotserver/git/tree.go
+79
knotserver/git/tree.go
···
2
3
import (
4
"context"
5
+
"errors"
6
"fmt"
7
"path"
8
"time"
···
79
80
return nts
81
}
82
+
83
+
var (
84
+
TerminateWalk error = errors.New("terminate walk")
85
+
)
86
+
87
+
type callback = func(node object.TreeEntry, parent *object.Tree, fullPath string) error
88
+
89
+
func (g *GitRepo) Walk(
90
+
ctx context.Context,
91
+
root string,
92
+
cb callback,
93
+
) error {
94
+
c, err := g.r.CommitObject(g.h)
95
+
if err != nil {
96
+
return fmt.Errorf("commit object: %w", err)
97
+
}
98
+
99
+
tree, err := c.Tree()
100
+
if err != nil {
101
+
return fmt.Errorf("file tree: %w", err)
102
+
}
103
+
104
+
subtree := tree
105
+
if root != "" {
106
+
subtree, err = tree.Tree(root)
107
+
if err != nil {
108
+
return fmt.Errorf("sub tree: %w", err)
109
+
}
110
+
}
111
+
112
+
return g.walkHelper(ctx, root, subtree, cb)
113
+
}
114
+
115
+
func (g *GitRepo) walkHelper(
116
+
ctx context.Context,
117
+
root string,
118
+
currentTree *object.Tree,
119
+
cb callback,
120
+
) error {
121
+
for _, e := range currentTree.Entries {
122
+
// check if context hits deadline before processing
123
+
select {
124
+
case <-ctx.Done():
125
+
return ctx.Err()
126
+
default:
127
+
}
128
+
129
+
mode, err := e.Mode.ToOSFileMode()
130
+
if err != nil {
131
+
// TODO: log this
132
+
continue
133
+
}
134
+
135
+
if e.Mode.IsFile() {
136
+
err = cb(e, currentTree, root)
137
+
if errors.Is(err, TerminateWalk) {
138
+
return err
139
+
}
140
+
}
141
+
142
+
// e is a directory
143
+
if mode.IsDir() {
144
+
subtree, err := currentTree.Tree(e.Name)
145
+
if err != nil {
146
+
return fmt.Errorf("sub tree %s: %w", e.Name, err)
147
+
}
148
+
149
+
fullPath := path.Join(root, e.Name)
150
+
151
+
err = g.walkHelper(ctx, fullPath, subtree, cb)
152
+
if err != nil {
153
+
return err
154
+
}
155
+
}
156
+
}
157
+
158
+
return nil
159
+
}