aboutsummaryrefslogtreecommitdiffstats
path: root/formats/packfile/objects.go
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2015-10-23 14:28:49 +0200
committerMáximo Cuadros <mcuadros@gmail.com>2015-10-23 14:28:49 +0200
commitd0a18ccd8eea3bdabc76d6dc5420af1ea30aae9f (patch)
treed6b15f621627027810a6a0ecf33edb74ab433905 /formats/packfile/objects.go
parentcf2874632223220e0445abf0a7806dc772c0b37a (diff)
downloadgo-git-d0a18ccd8eea3bdabc76d6dc5420af1ea30aae9f.tar.gz
formats/packfile: type Hash instead of strings
Diffstat (limited to 'formats/packfile/objects.go')
-rw-r--r--formats/packfile/objects.go237
1 files changed, 237 insertions, 0 deletions
diff --git a/formats/packfile/objects.go b/formats/packfile/objects.go
new file mode 100644
index 0000000..4c7ee75
--- /dev/null
+++ b/formats/packfile/objects.go
@@ -0,0 +1,237 @@
+package packfile
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "encoding/hex"
+ "fmt"
+ "strconv"
+ "time"
+)
+
+type ObjectType string
+
+const (
+ CommitObject ObjectType = "commit"
+ TreeObject ObjectType = "tree"
+ BlobObject ObjectType = "blob"
+)
+
+// Object generic object interface
+type Object interface {
+ Type() ObjectType
+ Hash() Hash
+}
+
+// Hash SHA1 hased content
+type Hash [20]byte
+
+// ComputeHash compute the hash for a given objType and content
+func ComputeHash(t ObjectType, content []byte) Hash {
+ h := []byte(t)
+ h = append(h, ' ')
+ h = strconv.AppendInt(h, int64(len(content)), 10)
+ h = append(h, 0)
+ h = append(h, content...)
+
+ return Hash(sha1.Sum(h))
+}
+
+func (h Hash) String() string {
+ return hex.EncodeToString(h[:])
+}
+
+// Commit points to a single tree, marking it as what the project looked like
+// at a certain point in time. It contains meta-information about that point
+// in time, such as a timestamp, the author of the changes since the last
+// commit, a pointer to the previous commit(s), etc.
+// http://schacon.github.io/gitbook/1_the_git_object_model.html
+type Commit struct {
+ Tree Hash
+ Parents []Hash
+ Author Signature
+ Committer Signature
+ Message string
+ hash Hash
+}
+
+// ParseCommit transform a byte slice into a Commit struct
+func ParseCommit(b []byte) (*Commit, error) {
+ // b64 := base64.StdEncoding.EncodeToString(b)
+ //fmt.Printf("%q\n", b64)
+
+ o := &Commit{hash: ComputeHash(CommitObject, b)}
+
+ lines := bytes.Split(b, []byte{'\n'})
+ for i := range lines {
+ if len(lines[i]) > 0 {
+ var err error
+
+ split := bytes.SplitN(lines[i], []byte{' '}, 2)
+ switch string(split[0]) {
+ case "tree":
+ _, err = hex.Decode(o.Tree[:], split[1])
+ case "parent":
+ var h Hash
+ _, err = hex.Decode(h[:], split[1])
+ if err == nil {
+ o.Parents = append(o.Parents, h)
+ }
+ case "author":
+ o.Author = ParseSignature(split[1])
+ case "committer":
+ o.Committer = ParseSignature(split[1])
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ o.Message = string(bytes.Join(append(lines[i+1:]), []byte{'\n'}))
+ break
+ }
+
+ }
+
+ return o, nil
+}
+
+// Type returns the object type
+func (o *Commit) Type() ObjectType {
+ return CommitObject
+}
+
+// Hash returns the computed hash of the commit
+func (o *Commit) Hash() Hash {
+ return o.hash
+}
+
+type Tree struct {
+ Entries []TreeEntry
+ hash Hash
+}
+
+type TreeEntry struct {
+ Name string
+ Hash Hash
+}
+
+func NewTree(body []byte) (*Tree, error) {
+ o := &Tree{hash: ComputeHash(TreeObject, body)}
+
+ if len(body) == 0 {
+ return o, nil
+ }
+
+ for {
+ split := bytes.SplitN(body, []byte{0}, 2)
+ split1 := bytes.SplitN(split[0], []byte{' '}, 2)
+
+ entry := TreeEntry{}
+ entry.Name = string(split1[1])
+ copy(entry.Hash[:], split[1][0:20])
+
+ o.Entries = append(o.Entries, entry)
+
+ body = split[1][20:]
+ if len(split[1]) == 20 {
+ break
+ }
+ }
+
+ return o, nil
+}
+
+func (o *Tree) Type() ObjectType {
+ return TreeObject
+}
+
+func (o *Tree) Hash() Hash {
+ return o.hash
+}
+
+type Blob struct {
+ Len int
+ hash Hash
+}
+
+func NewBlob(b []byte) (*Blob, error) {
+ return &Blob{
+ Len: len(b),
+ hash: ComputeHash(BlobObject, b),
+ }, nil
+}
+
+func (o *Blob) Type() ObjectType {
+ return BlobObject
+}
+
+func (o *Blob) Hash() Hash {
+ return o.hash
+}
+
+type ContentCallback func(hash Hash, content []byte)
+
+// Signature represents an action signed by a person
+type Signature struct {
+ Name string
+ Email string
+ When time.Time
+}
+
+// ParseSignature parse a byte slice returning a new action signature.
+func ParseSignature(signature []byte) Signature {
+ ret := Signature{}
+ if len(signature) == 0 {
+ return ret
+ }
+
+ from := 0
+ state := 'n' // n: name, e: email, t: timestamp, z: timezone
+ for i := 0; ; i++ {
+ var c byte
+ var end bool
+ if i < len(signature) {
+ c = signature[i]
+ } else {
+ end = true
+ }
+
+ switch state {
+ case 'n':
+ if c == '<' || end {
+ if i == 0 {
+ break
+ }
+ ret.Name = string(signature[from : i-1])
+ state = 'e'
+ from = i + 1
+ }
+ case 'e':
+ if c == '>' || end {
+ ret.Email = string(signature[from:i])
+ i++
+ state = 't'
+ from = i + 1
+ }
+ case 't':
+ if c == ' ' || end {
+ t, err := strconv.ParseInt(string(signature[from:i]), 10, 64)
+ if err == nil {
+ ret.When = time.Unix(t, 0)
+ }
+ end = true
+ }
+ }
+
+ if end {
+ break
+ }
+ }
+
+ return ret
+}
+
+func (s *Signature) String() string {
+ return fmt.Sprintf("%q <%s> @ %s", s.Name, s.Email, s.When)
+}