git2go/managed_tree.go

120 lines
2.2 KiB
Go

package git
import (
"bytes"
"errors"
"runtime"
"strconv"
)
// An equivalent of the Tree struct except this one is parsed in Go. There is no
// corresponding libgit2 object so you cannot do everything that you can with a
// Tree.
type ManagedTree struct {
m map[string]*TreeEntry
l []*TreeEntry
}
func (t *ManagedTree) EntryById(id *Oid) *TreeEntry {
for _, entry := range t.l {
if entry.Id.Equal(id) {
return entry
}
}
return nil
}
func (t *ManagedTree) EntryByName(filename string) *TreeEntry {
return t.m[filename]
}
func (t *ManagedTree) EntryByIndex(index uint64) *TreeEntry {
return t.l[index]
}
func (t *ManagedTree) EntryCount() uint64 {
return uint64(len(t.l))
}
// NewManagedTree retrieves the tree with the given id and parses it.
func NewManagedTree(r *Repository, id *Oid) (*ManagedTree, error) {
odb, err := r.Odb()
if err != nil {
return nil, err
}
obj, err := odb.Read(id)
if err != nil {
return nil, err
}
if obj.Type() != ObjectTree {
return nil, errors.New("object is not a tree")
}
data := obj.Data()
l := make([]*TreeEntry, 0, 8)
m := make(map[string]*TreeEntry)
var done bool
for !done {
spAt := bytes.IndexByte(data, ' ')
if spAt < 0 {
return nil, errors.New("failed to find SP after mode")
}
mode, err := strconv.ParseInt(string(data[:spAt]), 8, 32)
if err != nil {
return nil, err
}
data = data[spAt+1:]
nulAt := bytes.IndexByte(data, 0)
if nulAt < 0 {
return nil, errors.New("failed to find NUL after filename")
}
var name bytes.Buffer
name.Grow(nulAt)
name.Write(data[:nulAt])
data = data[nulAt+1:]
oid := NewOidFromBytes(data)
if len(data) > 20 {
data = data[20:]
} else {
done = true
}
entry := &TreeEntry{
Name: name.String(),
Id: oid,
Type: typeFromMode(mode),
Filemode: Filemode(mode),
}
l = append(l, entry)
m[entry.Name] = entry
}
// This avoids the runtime from garbage-collecting 'obj' and freeing the
// memory we're borrowing from libgit2.
runtime.KeepAlive(obj)
return &ManagedTree{
l: l,
m: m,
}, nil
}
func typeFromMode(mode int64) ObjectType {
switch Filemode(mode) {
case FilemodeTree:
return ObjectTree
case FilemodeCommit:
return ObjectCommit
default:
return ObjectBlob
}
}