forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
this repo has no description
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/filemode"
11 "github.com/go-git/go-git/v5/plumbing/object"
12 "tangled.org/core/types"
13)
14
15func (g *GitRepo) FileTree(ctx context.Context, path string) ([]types.NiceTree, error) {
16 c, err := g.r.CommitObject(g.h)
17 if err != nil {
18 return nil, fmt.Errorf("commit object: %w", err)
19 }
20
21 files := []types.NiceTree{}
22 tree, err := c.Tree()
23 if err != nil {
24 return nil, fmt.Errorf("file tree: %w", err)
25 }
26
27 if path == "" {
28 files = g.makeNiceTree(ctx, tree, "")
29 } else {
30 o, err := tree.FindEntry(path)
31 if err != nil {
32 return nil, err
33 }
34
35 if !o.Mode.IsFile() {
36 subtree, err := tree.Tree(path)
37 if err != nil {
38 return nil, err
39 }
40
41 files = g.makeNiceTree(ctx, subtree, path)
42 }
43 }
44
45 return files, nil
46}
47
48func (g *GitRepo) makeNiceTree(ctx context.Context, subtree *object.Tree, parent string) []types.NiceTree {
49 nts := []types.NiceTree{}
50
51 times, err := g.calculateCommitTimeIn(ctx, subtree, parent, 2*time.Second)
52 if err != nil {
53 return nts
54 }
55
56 for _, e := range subtree.Entries {
57 sz, _ := subtree.Size(e.Name)
58 fpath := path.Join(parent, e.Name)
59
60 var lastCommit *types.LastCommitInfo
61 if t, ok := times[fpath]; ok {
62 lastCommit = &types.LastCommitInfo{
63 Hash: t.hash,
64 Message: t.message,
65 When: t.when,
66 }
67 }
68
69 nts = append(nts, types.NiceTree{
70 Name: e.Name,
71 Mode: e.Mode.String(),
72 Size: sz,
73 LastCommit: lastCommit,
74 })
75
76 }
77
78 return nts
79}
80
81var (
82 TerminateWalk error = errors.New("terminate walk")
83)
84
85type callback = func(node object.TreeEntry, parent *object.Tree, fullPath string) error
86
87func (g *GitRepo) Walk(
88 ctx context.Context,
89 root string,
90 cb callback,
91) error {
92 c, err := g.r.CommitObject(g.h)
93 if err != nil {
94 return fmt.Errorf("commit object: %w", err)
95 }
96
97 tree, err := c.Tree()
98 if err != nil {
99 return fmt.Errorf("file tree: %w", err)
100 }
101
102 subtree := tree
103 if root != "" {
104 subtree, err = tree.Tree(root)
105 if err != nil {
106 return fmt.Errorf("sub tree: %w", err)
107 }
108 }
109
110 return g.walkHelper(ctx, root, subtree, cb)
111}
112
113func (g *GitRepo) walkHelper(
114 ctx context.Context,
115 root string,
116 currentTree *object.Tree,
117 cb callback,
118) error {
119 for _, e := range currentTree.Entries {
120 // check if context hits deadline before processing
121 select {
122 case <-ctx.Done():
123 return ctx.Err()
124 default:
125 }
126
127 if e.Mode.IsFile() {
128 if err := cb(e, currentTree, root); errors.Is(err, TerminateWalk) {
129 return err
130 }
131 }
132
133 // e is a directory
134 if e.Mode == filemode.Dir {
135 subtree, err := currentTree.Tree(e.Name)
136 if err != nil {
137 return fmt.Errorf("sub tree %s: %w", e.Name, err)
138 }
139
140 fullPath := path.Join(root, e.Name)
141
142 err = g.walkHelper(ctx, fullPath, subtree, cb)
143 if err != nil {
144 return err
145 }
146 }
147 }
148
149 return nil
150}