forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at master 2.9 kB view raw
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}