forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
Monorepo for Tangled
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package git
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "path"
8 "time"
9
10 "github.com/go-git/go-git/v5/plumbing/object"
11 "tangled.sh/tangled.sh/core/types"
12)
13
14func (g *GitRepo) FileTree(ctx context.Context, path string) ([]types.NiceTree, error) {
15 c, err := g.r.CommitObject(g.h)
16 if err != nil {
17 return nil, fmt.Errorf("commit object: %w", err)
18 }
19
20 files := []types.NiceTree{}
21 tree, err := c.Tree()
22 if err != nil {
23 return nil, fmt.Errorf("file tree: %w", err)
24 }
25
26 if path == "" {
27 files = g.makeNiceTree(ctx, tree, "")
28 } else {
29 o, err := tree.FindEntry(path)
30 if err != nil {
31 return nil, err
32 }
33
34 if !o.Mode.IsFile() {
35 subtree, err := tree.Tree(path)
36 if err != nil {
37 return nil, err
38 }
39
40 files = g.makeNiceTree(ctx, subtree, path)
41 }
42 }
43
44 return files, nil
45}
46
47func (g *GitRepo) makeNiceTree(ctx context.Context, subtree *object.Tree, parent string) []types.NiceTree {
48 nts := []types.NiceTree{}
49
50 times, err := g.calculateCommitTimeIn(ctx, subtree, parent, 2*time.Second)
51 if err != nil {
52 return nts
53 }
54
55 for _, e := range subtree.Entries {
56 mode, _ := e.Mode.ToOSFileMode()
57 sz, _ := subtree.Size(e.Name)
58
59 fpath := path.Join(parent, e.Name)
60
61 var lastCommit *types.LastCommitInfo
62 if t, ok := times[fpath]; ok {
63 lastCommit = &types.LastCommitInfo{
64 Hash: t.hash,
65 Message: t.message,
66 When: t.when,
67 }
68 }
69
70 nts = append(nts, types.NiceTree{
71 Name: e.Name,
72 Mode: mode.String(),
73 IsFile: e.Mode.IsFile(),
74 Size: sz,
75 LastCommit: lastCommit,
76 })
77
78 }
79
80 return nts
81}
82
83var (
84 TerminateWalk error = errors.New("terminate walk")
85)
86
87type callback = func(node object.TreeEntry, parent *object.Tree, fullPath string) error
88
89func (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
115func (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}