Monorepo for Tangled tangled.org
3

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 entries := make([]string, len(subtree.Entries)) 52 for i, e := range subtree.Entries { 53 entries[i] = e.Name 54 } 55 56 lastCommitDir := lastCommitDir{ 57 dir: parent, 58 entries: entries, 59 } 60 61 times, err := g.lastCommitDirIn(ctx, lastCommitDir, 300*time.Millisecond) 62 if err != nil { 63 return nts 64 } 65 66 for _, e := range subtree.Entries { 67 var sz int64 68 blob, err := object.GetBlob(g.r.Storer, e.Hash) 69 if err == nil { 70 sz = blob.Size 71 } 72 fpath := path.Join(parent, e.Name) 73 74 var lastCommit *types.LastCommitInfo 75 if t, ok := times[fpath]; ok { 76 lastCommit = &types.LastCommitInfo{ 77 Hash: t.hash, 78 Message: t.message, 79 When: t.when, 80 } 81 } 82 83 nts = append(nts, types.NiceTree{ 84 Name: e.Name, 85 Mode: e.Mode.String(), 86 Size: sz, 87 LastCommit: lastCommit, 88 }) 89 90 } 91 92 return nts 93} 94 95var ( 96 TerminateWalk error = errors.New("terminate walk") 97) 98 99type callback = func(node object.TreeEntry, parent *object.Tree, fullPath string) error 100 101func (g *GitRepo) Walk( 102 ctx context.Context, 103 root string, 104 cb callback, 105) error { 106 c, err := g.r.CommitObject(g.h) 107 if err != nil { 108 return fmt.Errorf("commit object: %w", err) 109 } 110 111 tree, err := c.Tree() 112 if err != nil { 113 return fmt.Errorf("file tree: %w", err) 114 } 115 116 subtree := tree 117 if root != "" { 118 subtree, err = tree.Tree(root) 119 if err != nil { 120 return fmt.Errorf("sub tree: %w", err) 121 } 122 } 123 124 return g.walkHelper(ctx, root, subtree, cb) 125} 126 127func (g *GitRepo) walkHelper( 128 ctx context.Context, 129 root string, 130 currentTree *object.Tree, 131 cb callback, 132) error { 133 for _, e := range currentTree.Entries { 134 // check if context hits deadline before processing 135 select { 136 case <-ctx.Done(): 137 return ctx.Err() 138 default: 139 } 140 141 if e.Mode.IsFile() { 142 if err := cb(e, currentTree, root); errors.Is(err, TerminateWalk) { 143 return err 144 } 145 } 146 147 // e is a directory 148 if e.Mode == filemode.Dir { 149 subtree, err := currentTree.Tree(e.Name) 150 if err != nil { 151 return fmt.Errorf("sub tree %s: %w", e.Name, err) 152 } 153 154 fullPath := path.Join(root, e.Name) 155 156 err = g.walkHelper(ctx, fullPath, subtree, cb) 157 if err != nil { 158 return err 159 } 160 } 161 } 162 163 return nil 164}