Skip to content

Commit

Permalink
move index.Write, rename Object type constants to include Type and mi…
Browse files Browse the repository at this point in the history
…grate Object tree to objects package
  • Loading branch information
richardjennings committed Apr 12, 2024
1 parent 4fdccdd commit e5fd33f
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 118 deletions.
49 changes: 49 additions & 0 deletions internal/mygit/index/index.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package index

import (
"crypto/sha1"
"encoding/binary"
"errors"
"fmt"
"github.com/richardjennings/mygit/internal/mygit/config"
"github.com/richardjennings/mygit/internal/mygit/gfs"
"io"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -118,6 +121,52 @@ func (idx *Index) Add(f *gfs.File) error {
return nil
}

// Write writes an Index struct to the Git Index
func (idx *Index) Write() error {
if idx.header.NumEntries != uint32(len(idx.items)) {
return errors.New("index numEntries and length of items inconsistent")
}
path := config.IndexFilePath()
f, err := os.OpenFile(path, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
// use a multi-writer to allow both writing the the file whilst incrementally generating
// a Sha hash of the content as it is written
h := sha1.New()
mw := io.MultiWriter(f, h)

// write header
if err := binary.Write(mw, binary.BigEndian, idx.header); err != nil {
return err
}
// write each item fixed size entry
for _, item := range idx.items {
if err := binary.Write(mw, binary.BigEndian, item.indexItemP); err != nil {
return err
}
// write name
if _, err := mw.Write(item.Name); err != nil {
return err
}
// write padding
padding := make([]byte, 8-(62+len(item.Name))%8)
if _, err := mw.Write(padding); err != nil {
return err
}
}
// use the generated hash
sha := h.Sum(nil)
copy(idx.sig[:], sha)
// write Sha hash of Index
if err := binary.Write(f, binary.BigEndian, &sha); err != nil {
return err
}

return f.Close()
}

func item(f *gfs.File) (*indexItem, error) {
if f.Sha == nil {
return nil, errors.New("missing Sha from working directory file toIndexItem")
Expand Down
46 changes: 0 additions & 46 deletions internal/mygit/index/tree.go

This file was deleted.

56 changes: 0 additions & 56 deletions internal/mygit/index/writer.go

This file was deleted.

2 changes: 1 addition & 1 deletion internal/mygit/mygit.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func Commit(message []byte) ([]byte, error) {
if err != nil {
return nil, err
}
root := index.ObjectTree(idx.Files())
root := objects.ObjectTree(idx.Files())
tree, err := root.WriteTree()
if err != nil {
return nil, err
Expand Down
49 changes: 45 additions & 4 deletions internal/mygit/objects/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package objects

import (
"fmt"
"github.com/richardjennings/mygit/internal/mygit/config"
"github.com/richardjennings/mygit/internal/mygit/gfs"
"io"
"path/filepath"
"strings"
"time"
)

Expand Down Expand Up @@ -45,10 +49,10 @@ type (
)

const (
ObjectInvalid objectType = iota
ObjectBlob
ObjectTree
ObjectCommit
ObjectTypeInvalid objectType = iota
ObjectTypeBlob
ObjectTypeTree
ObjectTypeCommit
)

func (c Commit) String() string {
Expand All @@ -63,3 +67,40 @@ func (c Commit) String() string {
o += fmt.Sprintf("message: \n%s\n", c.Message)
return o
}

// ObjectTree creates a Tree Object with child Objects representing the files and
// paths in the provided files.
func ObjectTree(files []*gfs.File) *Object {
root := &Object{}
var n *Object // current node
var pn *Object // previous node
// mp holds a cache of file paths to objectTree nodes
mp := make(map[string]*Object)
for _, v := range files {
parts := strings.Split(strings.TrimPrefix(v.Path, config.WorkingDirectory()), string(filepath.Separator))
if len(parts) == 1 {
root.Objects = append(root.Objects, &Object{Typ: ObjectTypeBlob, Path: v.Path, Sha: v.Sha.AsBytes()})
continue // top level file
}
pn = root
for i, p := range parts {
if i == len(parts)-1 {
pn.Objects = append(pn.Objects, &Object{Typ: ObjectTypeBlob, Path: v.Path, Sha: v.Sha.AsBytes()})
continue // leaf
}
// key for cached nodes
key := strings.Join(parts[0:i+1], string(filepath.Separator))
cached, ok := mp[key]
if ok {
n = cached
} else {
n = &Object{Typ: ObjectTypeTree, Path: p}
pn.Objects = append(pn.Objects, n)
mp[key] = n
}
pn = n
}
}

return root
}
18 changes: 9 additions & 9 deletions internal/mygit/objects/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
// FlattenTree turns a TreeObject structure into a flat list of file paths
func (o *Object) FlattenTree() []*gfs.File {
var objFiles []*gfs.File
if o.Typ == ObjectBlob {
if o.Typ == ObjectTypeBlob {
s, _ := gfs.NewSha(o.Sha)
f := []*gfs.File{{Path: o.Path, Sha: s}}
return f
Expand Down Expand Up @@ -54,11 +54,11 @@ func ReadObject(sha []byte) (*Object, error) {

switch string(header[0]) {
case "commit":
o.Typ = ObjectCommit
o.Typ = ObjectTypeCommit
case "tree":
o.Typ = ObjectTree
o.Typ = ObjectTypeTree
case "blob":
o.Typ = ObjectBlob
o.Typ = ObjectTypeBlob
default:
return nil, fmt.Errorf("unknown %s", string(header[0]))
}
Expand All @@ -85,7 +85,7 @@ func ReadObjectTree(sha []byte) (*Object, error) {
return nil, err
}
switch obj.Typ {
case ObjectCommit:
case ObjectTypeCommit:
commit, err := readCommit(obj)
if err != nil {
return obj, err
Expand All @@ -96,7 +96,7 @@ func ReadObjectTree(sha []byte) (*Object, error) {
}
obj.Objects = append(obj.Objects, co)
return obj, nil
case ObjectTree:
case ObjectTypeTree:
tree, err := ReadTree(obj)
if err != nil {
return nil, err
Expand All @@ -113,7 +113,7 @@ func ReadObjectTree(sha []byte) (*Object, error) {
obj.Objects = append(obj.Objects, o)
}
return obj, nil
case ObjectBlob:
case ObjectTypeBlob:
// lets not read the whole blob
return obj, nil
}
Expand Down Expand Up @@ -158,12 +158,12 @@ func ReadTree(obj *Object) (*Tree, error) {
item := bytes.Fields(p)
itm.Sha = []byte(hex.EncodeToString(sha))
if string(item[0]) == "40000" {
itm.Typ = ObjectTree
itm.Typ = ObjectTypeTree
if err != nil {
return nil, err
}
} else {
itm.Typ = ObjectBlob
itm.Typ = ObjectTypeBlob
}
itm.Path = string(item[1][:len(item[1])-1])

Expand Down
4 changes: 2 additions & 2 deletions internal/mygit/objects/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
func (o *Object) WriteTree() ([]byte, error) {
// resolve child tree Objects
for i, v := range o.Objects {
if v.Typ == ObjectTree {
if v.Typ == ObjectTypeTree {
// if the tree only has blobs, write them and then
// add the corresponding tree returning the Sha
sha, err := v.WriteTree()
Expand All @@ -38,7 +38,7 @@ func (o *Object) writeTree() ([]byte, error) {
var mode string
for _, fo := range o.Objects {
// @todo add executable support
if fo.Typ == ObjectTree {
if fo.Typ == ObjectTypeTree {
mode = "40000"
} else {
mode = "100644"
Expand Down

0 comments on commit e5fd33f

Please sign in to comment.