aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hash.go6
-rw-r--r--headers.go1
-rw-r--r--ident.go5
-rw-r--r--obj.go32
-rw-r--r--obj_blob.go9
-rw-r--r--obj_commit.go7
-rw-r--r--obj_tag.go9
-rw-r--r--obj_tree.go44
-rw-r--r--pack_pack.go1
-rw-r--r--refs.go3
-rw-r--r--repo.go13
11 files changed, 97 insertions, 33 deletions
diff --git a/hash.go b/hash.go
index 98c36c2e..2278f7cc 100644
--- a/hash.go
+++ b/hash.go
@@ -8,7 +8,7 @@ import (
const maxHashSize = 32
-// Hash represents a Git object identifier.
+// Hash represents a Git object ID.
type Hash struct {
data [maxHashSize]byte
size int
@@ -35,12 +35,12 @@ var hashFuncs = map[int]hashFunc{
},
}
-// String returns the ID as hex using its internal size.
+// String returns a hexadecimal string representation of the hash.
func (hash Hash) String() string {
return hex.EncodeToString(hash.data[:hash.size])
}
-// Bytes returns a mutable copy of the underlying bytes using its internal size.
+// Bytes returns a mutable copy of the hash bytes.
func (hash Hash) Bytes() []byte {
return append([]byte(nil), hash.data[:hash.size]...)
}
diff --git a/headers.go b/headers.go
index 0efc5398..9ee5cc61 100644
--- a/headers.go
+++ b/headers.go
@@ -1,5 +1,6 @@
package furgit
+// ExtraHeader represents an extra header in a Git object.
type ExtraHeader struct {
Key string
Value []byte
diff --git a/ident.go b/ident.go
index 5a01d8ea..b5cb38b2 100644
--- a/ident.go
+++ b/ident.go
@@ -10,8 +10,7 @@ import (
"time"
)
-// Ident models an author/committer identity together with its timestamp
-// and timezone offset, mirroring the fields that appear in Git objects.
+// Ident represents a Git identity (author/committer/tagger).
type Ident struct {
Name []byte
Email []byte
@@ -116,7 +115,7 @@ func (ident Ident) Serialize() ([]byte, error) {
return []byte(b.String()), nil
}
-// When returns the timestamp as time.Time using the embedded offset.
+// When returns the ident's time.Time with the correct timezone.
func (ident Ident) When() time.Time {
loc := time.FixedZone("git", int(ident.OffsetMinutes)*60)
return time.Unix(ident.WhenUnix, 0).In(loc)
diff --git a/obj.go b/obj.go
index ee11a024..7deb330e 100644
--- a/obj.go
+++ b/obj.go
@@ -11,13 +11,21 @@ import (
type ObjectType uint8
const (
- ObjectTypeInvalid ObjectType = 0
- ObjectTypeCommit ObjectType = 1
- ObjectTypeTree ObjectType = 2
- ObjectTypeBlob ObjectType = 3
- ObjectTypeTag ObjectType = 4
- ObjectTypeFuture ObjectType = 5
+ // An invalid object.
+ ObjectTypeInvalid ObjectType = 0
+ // A commit object.
+ ObjectTypeCommit ObjectType = 1
+ // A tree object.
+ ObjectTypeTree ObjectType = 2
+ // A blob object.
+ ObjectTypeBlob ObjectType = 3
+ // An annotated tag object.
+ ObjectTypeTag ObjectType = 4
+ // An object type reserved for future use.
+ ObjectTypeFuture ObjectType = 5
+ // A packfile offset delta object. This is not typically exposed.
ObjectTypeOfsDelta ObjectType = 6
+ // A packfile reference delta object. This is not typically exposed.
ObjectTypeRefDelta ObjectType = 7
)
@@ -28,12 +36,13 @@ const (
objectTypeNameTag = "tag"
)
-// Object describes any Git object variant.
+// Object represents a Git object.
type Object interface {
ObjectType() ObjectType
}
-// StoredObject describes a Git object with a known hash.
+// StoredObject describes a Git object with a known hash, such as
+// one read from storage.
type StoredObject interface {
Object
Hash() Hash
@@ -82,7 +91,7 @@ func parseObjectBody(ty ObjectType, id Hash, body []byte, repo *Repository) (Sto
}
}
-// ReadObject resolves an ID by consulting loose then packed storage.
+// ReadObject resolves an ID.
func (repo *Repository) ReadObject(id Hash) (Object, error) {
obj, err := repo.looseRead(id)
if err == nil {
@@ -98,7 +107,10 @@ func (repo *Repository) ReadObject(id Hash) (Object, error) {
return obj, err
}
-// ReadObjectTypeSize reports the object type and size without inflating the body.
+// ReadObjectTypeSize reports the object type and size.
+//
+// Typicall, this is more efficient than reading the full object,
+// as it avoids decompressing the entire object body.
func (repo *Repository) ReadObjectTypeSize(id Hash) (ObjectType, int64, error) {
ty, size, err := repo.looseTypeSize(id)
if err == nil {
diff --git a/obj_blob.go b/obj_blob.go
index 6a987604..05fd5311 100644
--- a/obj_blob.go
+++ b/obj_blob.go
@@ -1,6 +1,6 @@
package furgit
-// Blob represents the contents of a Git blob.
+// Blob represents a Git blob object.
type Blob struct {
Data []byte
}
@@ -16,7 +16,9 @@ func (sBlob *StoredBlob) Hash() Hash {
return sBlob.hash
}
-// ObjectType allows Blob to satisfy the Object interface.
+// ObjectType returns the object type of the blob.
+//
+// It always returns ObjectTypeBlob.
func (blob *Blob) ObjectType() ObjectType {
_ = blob
return ObjectTypeBlob
@@ -32,7 +34,8 @@ func parseBlob(id Hash, body []byte) (*StoredBlob, error) {
}, nil
}
-// Serialize renders the full "blob size\\0body" representation.
+// Serialize renders the blob into its raw byte representation,
+// including the header (i.e., "type size\0").
func (blob *Blob) Serialize() ([]byte, error) {
header, err := headerForType(ObjectTypeBlob, blob.Data)
if err != nil {
diff --git a/obj_commit.go b/obj_commit.go
index c3a4e5db..9953f690 100644
--- a/obj_commit.go
+++ b/obj_commit.go
@@ -27,7 +27,9 @@ func (sCommit *StoredCommit) Hash() Hash {
return sCommit.hash
}
-// ObjectType allows Commit to satisfy the Object interface.
+// ObjectType returns the object type of the commit.
+//
+// It always returns ObjectTypeCommit.
func (commit *Commit) ObjectType() ObjectType {
_ = commit
return ObjectTypeCommit
@@ -128,7 +130,8 @@ func commitBody(c *Commit) ([]byte, error) {
return buf.Bytes(), nil
}
-// Serialize renders a Commit into canonical Git format.
+// Serialize renders the commit into its raw byte representation,
+// including the header (i.e., "type size\0").
func (commit *Commit) Serialize() ([]byte, error) {
body, err := commitBody(commit)
if err != nil {
diff --git a/obj_tag.go b/obj_tag.go
index 2157e251..d21286c8 100644
--- a/obj_tag.go
+++ b/obj_tag.go
@@ -6,7 +6,7 @@ import (
"fmt"
)
-// Tag represents an annotated Git tag object.
+// Tag represents a Git annotated tag object.
type Tag struct {
Target Hash
TargetType ObjectType
@@ -26,7 +26,9 @@ func (sTag *StoredTag) Hash() Hash {
return sTag.hash
}
-// ObjectType allows Tag to satisfy the Object interface.
+// ObjectType returns the object type of the tag.
+//
+// It always returns ObjectTypeTag.
func (tag *Tag) ObjectType() ObjectType {
_ = tag
return ObjectTypeTag
@@ -142,7 +144,8 @@ func tagBody(t *Tag) ([]byte, error) {
return buf.Bytes(), nil
}
-// Serialize renders a Tag into canonical Git format.
+// Serialize renders the tag into its raw byte representation,
+// including the header (i.e., "type size\0").
func (tag *Tag) Serialize() ([]byte, error) {
body, err := tagBody(tag)
if err != nil {
diff --git a/obj_tree.go b/obj_tree.go
index eb926832..7b04b231 100644
--- a/obj_tree.go
+++ b/obj_tree.go
@@ -30,7 +30,9 @@ type TreeEntry struct {
ID Hash
}
-// ObjectType allows Tree to satisfy the Object interface.
+// ObjectType returns the object type of the tree.
+//
+// It always returns ObjectTypeTree.
func (tree *Tree) ObjectType() ObjectType {
_ = tree
return ObjectTypeTree
@@ -108,7 +110,8 @@ func treeBody(t *Tree) []byte {
return body
}
-// Serialize renders a Tree into canonical Git format.
+// Serialize renders the tree into its raw byte representation,
+// including the header (i.e., "type size\0").
func (tree *Tree) Serialize() ([]byte, error) {
body := treeBody(tree)
header, err := headerForType(ObjectTypeTree, body)
@@ -123,6 +126,9 @@ func (tree *Tree) Serialize() ([]byte, error) {
}
// Entry looks up a tree entry by name.
+//
+// Lookups are not recursive.
+// It returns nil if no such entry exists.
func (tree *Tree) Entry(name []byte) *TreeEntry {
low, high := 0, len(tree.Entries)-1
for low <= high {
@@ -138,3 +144,37 @@ func (tree *Tree) Entry(name []byte) *TreeEntry {
}
return nil
}
+
+// EntryRecursive looks up a tree entry by path.
+//
+// Lookups are recursive.
+// It returns nil if no such entry exists.
+func (tree *Tree) EntryRecursive(repo *Repository, path [][]byte) (*TreeEntry, error) {
+ if len(path) == 0 {
+ return nil, errors.New("furgit: tree: empty path")
+ }
+
+ currentTree := tree
+ for i, part := range path {
+ entry := currentTree.Entry(part)
+ if entry == nil {
+ return nil, nil
+ }
+ if i == len(path)-1 {
+ return entry, nil
+ }
+ obj, err := repo.ReadObject(entry.ID)
+ if err != nil {
+ return nil, err
+ }
+ nextTree, ok := obj.(*Tree)
+ if !ok {
+ return nil, fmt.Errorf("furgit: tree: expected tree object at %s, got %T", part, obj)
+ // TODO: It may be useful to check the mode instead of reporting
+ // an object type error.
+ }
+ currentTree = nextTree
+ }
+
+ return nil, nil
+}
diff --git a/pack_pack.go b/pack_pack.go
index 4930c139..15eedf60 100644
--- a/pack_pack.go
+++ b/pack_pack.go
@@ -20,7 +20,6 @@ const (
packVersion2 = 2
)
-// packlocation identifies the path to a pack file and an offset inside it.
type packlocation struct {
PackPath string
Offset uint64
diff --git a/refs.go b/refs.go
index ca1c61ad..b1824251 100644
--- a/refs.go
+++ b/refs.go
@@ -75,7 +75,8 @@ func (repo *Repository) resolvePackedRef(refname string) (Hash, error) {
return Hash{}, ErrInvalidObject
}
-// ResolveHEAD reads HEAD and returns the ref that HEAD points to.
+// ResolveHEAD reads HEAD and returns the fully qualified
+// ref name it points to.
func (repo *Repository) ResolveHEAD() (string, error) {
data, err := os.ReadFile(repo.repoPath("HEAD"))
if err != nil {
diff --git a/repo.go b/repo.go
index 0736a943..a33221fa 100644
--- a/repo.go
+++ b/repo.go
@@ -12,7 +12,7 @@ import (
"git.sr.ht/~runxiyu/furgit/config"
)
-// Repository represents the root of a Git repository.
+// Repository represents a Git repository.
type Repository struct {
rootPath string
hashSize int
@@ -29,9 +29,11 @@ type Repository struct {
closeOnce sync.Once
}
-// OpenRepository opens the repository at the provided path. The path is expected to be
-// the actual repository directory, i.e., the repository itself for bare repositories,
-// or the .git subdirectory for non-bare repositories.
+// OpenRepository opens the repository at the provided path.
+//
+// The path is expected to be the actual repository directory, i.e.,
+// the repository itself for bare repositories, or the .git
+// subdirectory for non-bare repositories.
func OpenRepository(path string) (*Repository, error) {
fi, err := os.Stat(path)
if err != nil {
@@ -129,7 +131,8 @@ func (repo *Repository) packFile(rel string) (*packFile, error) {
return pf, nil
}
-// ParseHash converts a hex string into a Hash, validating it matches the repository's hash size.
+// ParseHash converts a hex string into a Hash, validating
+// it matches the repository's hash size.
func (repo *Repository) ParseHash(s string) (Hash, error) {
var id Hash
if len(s)%2 != 0 {