Skip to content

Commit

Permalink
more sha reworking (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
richardjennings authored Oct 24, 2024
1 parent b0b1994 commit 520c675
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 64 deletions.
3 changes: 1 addition & 2 deletions cmd/gitg/commit.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"encoding/hex"
"fmt"
"github.com/richardjennings/g/git"
"github.com/spf13/cobra"
Expand All @@ -26,7 +25,7 @@ var commitCmd = &cobra.Command{
fmt.Println(err)
os.Exit(1)
}
fmt.Println(hex.EncodeToString(sha))
fmt.Println(sha.AsHexString())
},
}

Expand Down
2 changes: 1 addition & 1 deletion git/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func Add(paths ...string) error {
if err != nil {
return err
}
v.Sha, _ = g.NewSha(obj.Sha)
v.Sha = obj.Sha
}
if err := idx.Add(v); err != nil {
return err
Expand Down
10 changes: 5 additions & 5 deletions git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ import (
)

// Commit writes a git commit object from the files in the index
func Commit(message []byte) ([]byte, error) {
func Commit(message []byte) (g.Sha, error) {
idx, err := g.ReadIndex()
if err != nil {
return nil, err
return g.Sha{}, err
}
root := g.ObjectTree(idx.Files())
tree, err := root.WriteTree()
if err != nil {
return nil, err
return g.Sha{}, err
}
// git has the --allow-empty flag which here defaults to true currently
// @todo check for changes to be committed.
previousCommits, err := g.PreviousCommits()
if err != nil {
// @todo error types to check for e.g no previous commits as source of error
return nil, err
return g.Sha{}, err
}
commit := &g.Commit{
Tree: tree,
Expand Down Expand Up @@ -59,7 +59,7 @@ func Commit(message []byte) ([]byte, error) {
commit.Message = msg
}
if len(commit.Message) == 0 {
return nil, errors.New("Aborting commit due to empty commit message.")
return g.Sha{}, errors.New("Aborting commit due to empty commit message.")
}
return g.WriteCommit(commit)
}
12 changes: 2 additions & 10 deletions git/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,22 +219,14 @@ func testRestore(t *testing.T, path string, staged bool) {
}
}

func testCommit(t *testing.T, message []byte) []byte {
func testCommit(t *testing.T, message []byte) g.Sha {
sha, err := Commit(message)
if err != nil {
t.Fatal(err)
}
if len(sha) != 40 {
t.Errorf("expected sha len 40 got %d", len(sha))
}
commitSha, err := g.NewSha(sha)
if err != nil {
t.Error(err)
return sha
}

// read object
c, err := g.ReadCommit(commitSha)
c, err := g.ReadCommit(sha)
if err != nil {
t.Error(err)
return sha
Expand Down
89 changes: 44 additions & 45 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type (
Object struct {
Path string
Typ objectType
Sha []byte
Sha Sha
Objects []*Object
Length int
HeaderLength int
Expand All @@ -30,8 +30,8 @@ type (
}
objectType int
Commit struct {
Sha []byte
Tree []byte
Sha Sha
Tree Sha
Parents []Sha
Author string
AuthorEmail string
Expand All @@ -43,7 +43,7 @@ type (
Message []byte
}
Tree struct {
Sha []byte
Sha Sha
Typ objectType
Path string
Items []*TreeItem
Expand All @@ -64,8 +64,8 @@ const (

func (c Commit) String() string {
var o string
o += fmt.Sprintf("commit: %s\n", string(c.Sha))
o += fmt.Sprintf("tree: %s\n", string(c.Tree))
o += fmt.Sprintf("commit: %s\n", c.Sha.AsHexString())
o += fmt.Sprintf("tree: %s\n", c.Tree.AsHexString())
for _, v := range c.Parents {
o += fmt.Sprintf("parent: %s\n", v.AsHexString())
}
Expand All @@ -78,21 +78,23 @@ func (c Commit) String() string {
// ObjectTree creates a Tree Object with child Objects representing the files and
// paths in the provided files.
func ObjectTree(files []*File) *Object {
root := &Object{}
root := &Object{
Typ: ObjectTypeTree,
}
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, WorkingDirectory()), string(filepath.Separator))
if len(parts) == 1 {
root.Objects = append(root.Objects, &Object{Typ: ObjectTypeBlob, Path: v.Path, Sha: v.Sha.AsByteSlice()})
root.Objects = append(root.Objects, &Object{Typ: ObjectTypeBlob, Path: v.Path, Sha: v.Sha})
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.AsByteSlice()})
pn.Objects = append(pn.Objects, &Object{Typ: ObjectTypeBlob, Path: v.Path, Sha: v.Sha})
continue // leaf
}
// key for cached nodes
Expand All @@ -116,8 +118,7 @@ func ObjectTree(files []*File) *Object {
func (o *Object) FlattenTree() []*File {
var objFiles []*File
if o.Typ == ObjectTypeBlob {
s, _ := NewSha(o.Sha)
f := []*File{{Path: o.Path, Sha: s}}
f := []*File{{Path: o.Path, Sha: o.Sha}}
return f
}
for _, v := range o.Objects {
Expand All @@ -136,15 +137,15 @@ func ReadObject(sha Sha) (*Object, error) {
var o *Object

// check if a loose file or in a packfile
if _, err := os.Stat(filepath.Join(ObjectPath(), string(sha.AsHexBytes()[0:2]), string(sha.AsHexBytes()[2:]))); err != nil {
if _, err := os.Stat(filepath.Join(ObjectPath(), string(sha.AsHexString()[0:2]), string(sha.AsHexString()[2:]))); err != nil {
if errors.Is(err, os.ErrNotExist) {
return lookupInPackfiles(sha)
} else {
return nil, err
}
}

o = &Object{Sha: sha.AsHexBytes()}
o = &Object{Sha: sha}
o.ReadCloser = ObjectReadCloser(sha.AsHexBytes())
z, err := o.ReadCloser()
if err != nil {
Expand Down Expand Up @@ -201,11 +202,7 @@ func ReadObjectTree(sha Sha) (*Object, error) {
if err != nil {
return obj, err
}
sha, err := NewSha(commit.Tree)
if err != nil {
return obj, err
}
co, err := ReadObjectTree(sha)
co, err := ReadObjectTree(commit.Tree)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -340,11 +337,14 @@ func readCommit(obj *Object) (*Commit, error) {
l := s.Bytes()
p := bytes.SplitN(l, []byte(" "), 2)
t := string(p[0])
if c.Tree == nil {
if !c.Tree.IsSet() {
if t != "tree" {
return nil, fmt.Errorf("expected tree got %s", t)
}
c.Tree = p[1]
c.Tree, err = NewSha(p[1])
if err != nil {
return nil, err
}
continue
}
// can be parent
Expand Down Expand Up @@ -444,15 +444,15 @@ func CommittedFiles(sha Sha) ([]*File, error) {
}

// WriteTree writes an Object Tree to the object store.
func (o *Object) WriteTree() ([]byte, error) {
func (o *Object) WriteTree() (Sha, error) {
// resolve child tree Objects
for i, v := range o.Objects {
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()
if err != nil {
return nil, err
return Sha{}, err
}
o.Objects[i].Sha = sha
}
Expand All @@ -461,7 +461,7 @@ func (o *Object) WriteTree() ([]byte, error) {
return o.writeTree()
}

func (o *Object) writeTree() ([]byte, error) {
func (o *Object) writeTree() (Sha, error) {
var content []byte
var mode string
for _, fo := range o.Objects {
Expand All @@ -472,14 +472,14 @@ func (o *Object) writeTree() ([]byte, error) {
mode = "100644"
}
// @todo replace base..
content = append(content, []byte(fmt.Sprintf("%s %s%s%s", mode, filepath.Base(fo.Path), string(byte(0)), fo.Sha))...)
content = append(content, []byte(fmt.Sprintf("%s %s%s%s", mode, filepath.Base(fo.Path), string(byte(0)), fo.Sha.AsByteSlice()))...)
}
header := []byte(fmt.Sprintf("tree %d%s", len(content), string(byte(0))))
return WriteObject(header, content, "", ObjectPath())
}

// WriteObject writes an object to the object store
func WriteObject(header []byte, content []byte, contentFile string, path string) ([]byte, error) {
func WriteObject(header []byte, content []byte, contentFile string, path string) (Sha, error) {
var f *os.File
var err error
buf := bytes.NewBuffer(nil)
Expand All @@ -488,41 +488,44 @@ func WriteObject(header []byte, content []byte, contentFile string, path string)
r := io.MultiWriter(h, z)

if _, err := r.Write(header); err != nil {
return nil, err
return Sha{}, err
}
if len(content) > 0 {
if _, err := r.Write(content); err != nil {
return nil, err
return Sha{}, err
}
}
if contentFile != "" {
f, err = os.Open(contentFile)
if err != nil {
return nil, err
return Sha{}, err
}
if _, err := io.Copy(r, f); err != nil {
return nil, err
return Sha{}, err
}
if err := f.Close(); err != nil {
return nil, err
return Sha{}, err
}
}

sha := h.Sum(nil)
path = filepath.Join(path, hex.EncodeToString(sha)[:2])
sha, err := NewSha(h.Sum(nil))
if err != nil {
return Sha{}, err
}
path = filepath.Join(path, sha.AsHexString()[:2])
// create object sha[:2] directory if needed
if err := os.MkdirAll(path, 0744); err != nil {
return nil, err
return Sha{}, err
}
path = filepath.Join(path, hex.EncodeToString(sha)[2:])
path = filepath.Join(path, sha.AsHexString()[2:])
// if object exists with Sha already we can avoid writing again
_, err = os.Stat(path)
if err == nil || !errors.Is(err, fs.ErrNotExist) {
// file exists
return sha, err
}
if err := z.Close(); err != nil {
return nil, err
return Sha{}, err
}
return sha, os.WriteFile(path, buf.Bytes(), 0655)
}
Expand All @@ -540,14 +543,14 @@ func WriteBlob(path string) (*Object, error) {
return &Object{Sha: sha, Path: path}, err
}

func WriteCommit(c *Commit) ([]byte, error) {
func WriteCommit(c *Commit) (Sha, error) {
var parentCommits string
for _, v := range c.Parents {
parentCommits += fmt.Sprintf("parent %s\n", v)
}
content := []byte(fmt.Sprintf(
"tree %s\n%sauthor %s %d +0000\ncommitter %s %d +0000\n\n%s",
hex.EncodeToString(c.Tree),
c.Tree.AsHexString(),
parentCommits,
c.Author,
c.AuthoredTime.Unix(),
Expand All @@ -556,17 +559,13 @@ func WriteCommit(c *Commit) ([]byte, error) {
c.Message,
))
header := []byte(fmt.Sprintf("commit %d%s", len(content), string(byte(0))))
b, err := WriteObject(header, content, "", ObjectPath())
sha, err := WriteObject(header, content, "", ObjectPath())
if err != nil {
return nil, err
return Sha{}, err
}
branch, err := CurrentBranch()
if err != nil {
return nil, err
}
sha, err := NewSha(b)
if err != nil {
return nil, err
return Sha{}, err
}
return sha.AsHexBytes(), UpdateBranchHead(branch, sha)
return sha, UpdateBranchHead(branch, sha)
}
5 changes: 4 additions & 1 deletion packfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ func lookupInPackfiles(sha Sha) (*Object, error) {
if err := filepath.Walk(
ObjectPackfileDirectory(),
func(path string, info os.FileInfo, err error) error {
if info == nil {
return nil
}
if info.IsDir() {
return nil
}
Expand Down Expand Up @@ -267,7 +270,7 @@ func findObjectInPack(offset uint32, path string, sha Sha) (*Object, error) {
case ObjTree:
obj.Typ = ObjectTypeTree
}
obj.Sha = sha.AsByteSlice() // @todo use Sha
obj.Sha = sha
obj.Length = int(length)
// This HeaderLength was added before I knew about pack files,
// the purpose was to create a factory that allowed a reader to
Expand Down

0 comments on commit 520c675

Please sign in to comment.