Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more sha reworking #28

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading