package object import ( "bytes" "io" "strings" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/utils/binary" "github.com/go-git/go-git/v5/utils/ioutil" ) // File represents git file objects. type File struct { // Name is the path of the file. It might be relative to a tree, // depending of the function that generates it. Name string // Mode is the file mode. Mode filemode.FileMode // Blob with the contents of the file. Blob } // NewFile returns a File based on the given blob object func NewFile(name string, m filemode.FileMode, b *Blob) *File { return &File{Name: name, Mode: m, Blob: *b} } // Contents returns the contents of a file as a string. func (f *File) Contents() (content string, err error) { reader, err := f.Reader() if err != nil { return "", err } defer ioutil.CheckClose(reader, &err) buf := new(bytes.Buffer) if _, err := buf.ReadFrom(reader); err != nil { return "", err } return buf.String(), nil } // IsBinary returns if the file is binary or not func (f *File) IsBinary() (bin bool, err error) { reader, err := f.Reader() if err != nil { return false, err } defer ioutil.CheckClose(reader, &err) return binary.IsBinary(reader) } // Lines returns a slice of lines from the contents of a file, stripping // all end of line characters. If the last line is empty (does not end // in an end of line), it is also stripped. func (f *File) Lines() ([]string, error) { content, err := f.Contents() if err != nil { return nil, err } splits := strings.Split(content, "\n") // remove the last line if it is empty if splits[len(splits)-1] == "" { return splits[:len(splits)-1], nil } return splits, nil } // FileIter provides an iterator for the files in a tree. type FileIter struct { s storer.EncodedObjectStorer w TreeWalker } // NewFileIter takes a storer.EncodedObjectStorer and a Tree and returns a // *FileIter that iterates over all files contained in the tree, recursively. func NewFileIter(s storer.EncodedObjectStorer, t *Tree) *FileIter { return &FileIter{s: s, w: *NewTreeWalker(t, true, nil)} } // Next moves the iterator to the next file and returns a pointer to it. If // there are no more files, it returns io.EOF. func (iter *FileIter) Next() (*File, error) { for { name, entry, err := iter.w.Next() if err != nil { return nil, err } if entry.Mode == filemode.Dir || entry.Mode == filemode.Submodule { continue } blob, err := GetBlob(iter.s, entry.Hash) if err != nil { return nil, err } return NewFile(name, entry.Mode, blob), nil } } // ForEach call the cb function for each file contained in this iter until // an error happens or the end of the iter is reached. If plumbing.ErrStop is sent // the iteration is stop but no error is returned. The iterator is closed. func (iter *FileIter) ForEach(cb func(*File) error) error { defer iter.Close() for { f, err := iter.Next() if err != nil { if err == io.EOF { return nil } return err } if err := cb(f); err != nil { if err == storer.ErrStop { return nil } return err } } } func (iter *FileIter) Close() { iter.w.Close() }