tree.go

  1package cmd
  2
  3import (
  4	"fmt"
  5
  6	"github.com/charmbracelet/soft-serve/git"
  7	"github.com/charmbracelet/soft-serve/server/errors"
  8	"github.com/dustin/go-humanize"
  9	"github.com/spf13/cobra"
 10)
 11
 12// treeCommand returns a command that list file or directory at path.
 13func treeCommand() *cobra.Command {
 14	cmd := &cobra.Command{
 15		Use:               "tree REPOSITORY [REFERENCE] [PATH]",
 16		Short:             "Print repository tree at path",
 17		Args:              cobra.RangeArgs(1, 3),
 18		PersistentPreRunE: checkIfReadable,
 19		RunE: func(cmd *cobra.Command, args []string) error {
 20			ctx := cmd.Context()
 21			be, _ := fromContext(cmd)
 22			rn := args[0]
 23			path := ""
 24			ref := ""
 25			switch len(args) {
 26			case 2:
 27				path = args[1]
 28			case 3:
 29				ref = args[1]
 30				path = args[2]
 31			}
 32			rr, err := be.Repository(ctx, rn)
 33			if err != nil {
 34				return err
 35			}
 36
 37			r, err := rr.Open()
 38			if err != nil {
 39				return err
 40			}
 41
 42			if ref == "" {
 43				head, err := r.HEAD()
 44				if err != nil {
 45					if bs, err := r.Branches(); err != nil && len(bs) == 0 {
 46						return fmt.Errorf("repository is empty")
 47					}
 48					return err
 49				}
 50
 51				ref = head.Hash.String()
 52			}
 53
 54			tree, err := r.LsTree(ref)
 55			if err != nil {
 56				return err
 57			}
 58
 59			ents := git.Entries{}
 60			if path != "" && path != "/" {
 61				te, err := tree.TreeEntry(path)
 62				if err == git.ErrRevisionNotExist {
 63					return errors.ErrFileNotFound
 64				}
 65				if err != nil {
 66					return err
 67				}
 68				if te.Type() == "tree" {
 69					tree, err = tree.SubTree(path)
 70					if err != nil {
 71						return err
 72					}
 73					ents, err = tree.Entries()
 74					if err != nil {
 75						return err
 76					}
 77				} else {
 78					ents = append(ents, te)
 79				}
 80			} else {
 81				ents, err = tree.Entries()
 82				if err != nil {
 83					return err
 84				}
 85			}
 86			ents.Sort()
 87			for _, ent := range ents {
 88				size := ent.Size()
 89				ssize := ""
 90				if size == 0 {
 91					ssize = "-"
 92				} else {
 93					ssize = humanize.Bytes(uint64(size))
 94				}
 95				cmd.Printf("%s\t%s\t %s\n", ent.Mode(), ssize, ent.Name())
 96			}
 97			return nil
 98		},
 99	}
100	return cmd
101}