From e5fd33faeb4d518b1d3721736950c9b65fdc683c Mon Sep 17 00:00:00 2001 From: Richard Jennings Date: Fri, 12 Apr 2024 08:05:31 +0100 Subject: [PATCH 1/4] move index.Write, rename Object type constants to include Type and migrate Object tree to objects package --- internal/mygit/index/index.go | 49 ++++++++++++++++++++++++++++ internal/mygit/index/tree.go | 46 -------------------------- internal/mygit/index/writer.go | 56 -------------------------------- internal/mygit/mygit.go | 2 +- internal/mygit/objects/object.go | 49 +++++++++++++++++++++++++--- internal/mygit/objects/reader.go | 18 +++++----- internal/mygit/objects/writer.go | 4 +-- 7 files changed, 106 insertions(+), 118 deletions(-) delete mode 100644 internal/mygit/index/tree.go delete mode 100644 internal/mygit/index/writer.go diff --git a/internal/mygit/index/index.go b/internal/mygit/index/index.go index d551f23..a8e4b8d 100644 --- a/internal/mygit/index/index.go +++ b/internal/mygit/index/index.go @@ -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" @@ -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") diff --git a/internal/mygit/index/tree.go b/internal/mygit/index/tree.go deleted file mode 100644 index e94b9f7..0000000 --- a/internal/mygit/index/tree.go +++ /dev/null @@ -1,46 +0,0 @@ -package index - -import ( - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/gfs" - "github.com/richardjennings/mygit/internal/mygit/objects" - "path/filepath" - "strings" -) - -// ObjectTree creates a Tree Object with child Objects representing the files and -// paths in the provided files. -func ObjectTree(files []*gfs.File) *objects.Object { - root := &objects.Object{} - var n *objects.Object // current node - var pn *objects.Object // previous node - // mp holds a cache of file paths to objectTree nodes - mp := make(map[string]*objects.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, &objects.Object{Typ: objects.ObjectBlob, 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, &objects.Object{Typ: objects.ObjectBlob, 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 = &objects.Object{Typ: objects.ObjectTree, Path: p} - pn.Objects = append(pn.Objects, n) - mp[key] = n - } - pn = n - } - } - - return root -} diff --git a/internal/mygit/index/writer.go b/internal/mygit/index/writer.go deleted file mode 100644 index b48a13d..0000000 --- a/internal/mygit/index/writer.go +++ /dev/null @@ -1,56 +0,0 @@ -package index - -import ( - "crypto/sha1" - "encoding/binary" - "errors" - "github.com/richardjennings/mygit/internal/mygit/config" - "io" - "os" -) - -// 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() -} diff --git a/internal/mygit/mygit.go b/internal/mygit/mygit.go index dee9a64..3463cdc 100644 --- a/internal/mygit/mygit.go +++ b/internal/mygit/mygit.go @@ -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 diff --git a/internal/mygit/objects/object.go b/internal/mygit/objects/object.go index 46049cb..dd168e2 100644 --- a/internal/mygit/objects/object.go +++ b/internal/mygit/objects/object.go @@ -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" ) @@ -45,10 +49,10 @@ type ( ) const ( - ObjectInvalid objectType = iota - ObjectBlob - ObjectTree - ObjectCommit + ObjectTypeInvalid objectType = iota + ObjectTypeBlob + ObjectTypeTree + ObjectTypeCommit ) func (c Commit) String() string { @@ -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 +} diff --git a/internal/mygit/objects/reader.go b/internal/mygit/objects/reader.go index d55675f..2a9b7e8 100644 --- a/internal/mygit/objects/reader.go +++ b/internal/mygit/objects/reader.go @@ -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 @@ -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])) } @@ -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 @@ -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 @@ -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 } @@ -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]) diff --git a/internal/mygit/objects/writer.go b/internal/mygit/objects/writer.go index 92d892a..1528c2b 100644 --- a/internal/mygit/objects/writer.go +++ b/internal/mygit/objects/writer.go @@ -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() @@ -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" From 8ea40a5ba451cfa430052fb6dc882d1b9393cbd8 Mon Sep 17 00:00:00 2001 From: Richard Jennings Date: Thu, 2 May 2024 08:35:21 +0100 Subject: [PATCH 2/4] move from internal to pkg --- cmd/add.go | 2 +- cmd/branch.go | 2 +- cmd/commit.go | 2 +- cmd/init.go | 2 +- cmd/log.go | 4 ++-- cmd/ls-files.go | 2 +- cmd/restore.go | 2 +- cmd/root.go | 2 +- cmd/status.go | 2 +- cmd/switch.go | 2 +- {internal => pkg}/mygit/config/config.go | 0 {internal => pkg}/mygit/gfs/file.go | 4 ++-- {internal => pkg}/mygit/ignore/ignore.go | 2 +- {internal => pkg}/mygit/index/index.go | 4 ++-- {internal => pkg}/mygit/index/index_darwin.go | 0 {internal => pkg}/mygit/index/index_linux.go | 0 {internal => pkg}/mygit/index/reader.go | 2 +- {internal => pkg}/mygit/index/status.go | 6 +++--- {internal => pkg}/mygit/mygit.go | 10 +++++----- {internal => pkg}/mygit/mygit_test.go | 8 ++++---- {internal => pkg}/mygit/objects/object.go | 4 ++-- {internal => pkg}/mygit/objects/reader.go | 4 ++-- {internal => pkg}/mygit/objects/writer.go | 4 ++-- {internal => pkg}/mygit/refs/refs.go | 2 +- 24 files changed, 36 insertions(+), 36 deletions(-) rename {internal => pkg}/mygit/config/config.go (100%) rename {internal => pkg}/mygit/gfs/file.go (98%) rename {internal => pkg}/mygit/ignore/ignore.go (89%) rename {internal => pkg}/mygit/index/index.go (98%) rename {internal => pkg}/mygit/index/index_darwin.go (100%) rename {internal => pkg}/mygit/index/index_linux.go (100%) rename {internal => pkg}/mygit/index/reader.go (95%) rename {internal => pkg}/mygit/index/status.go (86%) rename {internal => pkg}/mygit/mygit.go (97%) rename {internal => pkg}/mygit/mygit_test.go (96%) rename {internal => pkg}/mygit/objects/object.go (95%) rename {internal => pkg}/mygit/objects/reader.go (98%) rename {internal => pkg}/mygit/objects/writer.go (96%) rename {internal => pkg}/mygit/refs/refs.go (97%) diff --git a/cmd/add.go b/cmd/add.go index f122a33..6c7ccff 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "log" ) diff --git a/cmd/branch.go b/cmd/branch.go index 1c074c8..dc42dd6 100644 --- a/cmd/branch.go +++ b/cmd/branch.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/commit.go b/cmd/commit.go index 68a091d..a53a47e 100644 --- a/cmd/commit.go +++ b/cmd/commit.go @@ -3,7 +3,7 @@ package cmd import ( "encoding/hex" "fmt" - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/init.go b/cmd/init.go index 63e1967..20bf95f 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "log" ) diff --git a/cmd/log.go b/cmd/log.go index 63a19b9..a2a4403 100644 --- a/cmd/log.go +++ b/cmd/log.go @@ -1,8 +1,8 @@ package cmd import ( - "github.com/richardjennings/mygit/internal/mygit" - "github.com/richardjennings/mygit/internal/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/config" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/ls-files.go b/cmd/ls-files.go index 8961cba..a0ade86 100644 --- a/cmd/ls-files.go +++ b/cmd/ls-files.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "log" ) diff --git a/cmd/restore.go b/cmd/restore.go index 45b4636..250b07f 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/root.go b/cmd/root.go index a6f8192..f5c5828 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/internal/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/config" "github.com/spf13/cobra" "log" ) diff --git a/cmd/status.go b/cmd/status.go index 6268699..5dee6c4 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "os" ) diff --git a/cmd/switch.go b/cmd/switch.go index a94a0df..8b816fa 100644 --- a/cmd/switch.go +++ b/cmd/switch.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/internal/mygit" + "github.com/richardjennings/mygit/pkg/mygit" "github.com/spf13/cobra" "log" "os" diff --git a/internal/mygit/config/config.go b/pkg/mygit/config/config.go similarity index 100% rename from internal/mygit/config/config.go rename to pkg/mygit/config/config.go diff --git a/internal/mygit/gfs/file.go b/pkg/mygit/gfs/file.go similarity index 98% rename from internal/mygit/gfs/file.go rename to pkg/mygit/gfs/file.go index 710b5a9..a87ef21 100644 --- a/internal/mygit/gfs/file.go +++ b/pkg/mygit/gfs/file.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/hex" "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/ignore" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/ignore" "os" "path/filepath" "strings" diff --git a/internal/mygit/ignore/ignore.go b/pkg/mygit/ignore/ignore.go similarity index 89% rename from internal/mygit/ignore/ignore.go rename to pkg/mygit/ignore/ignore.go index 960f2c6..2bb6991 100644 --- a/internal/mygit/ignore/ignore.go +++ b/pkg/mygit/ignore/ignore.go @@ -1,7 +1,7 @@ package ignore import ( - "github.com/richardjennings/mygit/internal/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/config" "path/filepath" "strings" ) diff --git a/internal/mygit/index/index.go b/pkg/mygit/index/index.go similarity index 98% rename from internal/mygit/index/index.go rename to pkg/mygit/index/index.go index a8e4b8d..7350e52 100644 --- a/internal/mygit/index/index.go +++ b/pkg/mygit/index/index.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" "io" "os" "path/filepath" diff --git a/internal/mygit/index/index_darwin.go b/pkg/mygit/index/index_darwin.go similarity index 100% rename from internal/mygit/index/index_darwin.go rename to pkg/mygit/index/index_darwin.go diff --git a/internal/mygit/index/index_linux.go b/pkg/mygit/index/index_linux.go similarity index 100% rename from internal/mygit/index/index_linux.go rename to pkg/mygit/index/index_linux.go diff --git a/internal/mygit/index/reader.go b/pkg/mygit/index/reader.go similarity index 95% rename from internal/mygit/index/reader.go rename to pkg/mygit/index/reader.go index 34a7e57..4ba3aa8 100644 --- a/internal/mygit/index/reader.go +++ b/pkg/mygit/index/reader.go @@ -3,7 +3,7 @@ package index import ( "encoding/binary" "errors" - "github.com/richardjennings/mygit/internal/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/config" "os" ) diff --git a/internal/mygit/index/status.go b/pkg/mygit/index/status.go similarity index 86% rename from internal/mygit/index/status.go rename to pkg/mygit/index/status.go index 5aca28d..3b3c48a 100644 --- a/internal/mygit/index/status.go +++ b/pkg/mygit/index/status.go @@ -1,9 +1,9 @@ package index import ( - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/gfs" - "github.com/richardjennings/mygit/internal/mygit/objects" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/objects" ) // Status returns a FileSet containing all files from commit, index and working directory diff --git a/internal/mygit/mygit.go b/pkg/mygit/mygit.go similarity index 97% rename from internal/mygit/mygit.go rename to pkg/mygit/mygit.go index 3463cdc..2b9379e 100644 --- a/internal/mygit/mygit.go +++ b/pkg/mygit/mygit.go @@ -3,11 +3,11 @@ package mygit import ( "errors" "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/gfs" - "github.com/richardjennings/mygit/internal/mygit/index" - "github.com/richardjennings/mygit/internal/mygit/objects" - "github.com/richardjennings/mygit/internal/mygit/refs" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/index" + "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/mygit/refs" "io" "log" "os" diff --git a/internal/mygit/mygit_test.go b/pkg/mygit/mygit_test.go similarity index 96% rename from internal/mygit/mygit_test.go rename to pkg/mygit/mygit_test.go index b3a707b..2a92dae 100644 --- a/internal/mygit/mygit_test.go +++ b/pkg/mygit/mygit_test.go @@ -3,10 +3,10 @@ package mygit import ( "bytes" "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/gfs" - "github.com/richardjennings/mygit/internal/mygit/objects" - "github.com/richardjennings/mygit/internal/mygit/refs" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/mygit/refs" "github.com/stretchr/testify/assert" "io/fs" "os" diff --git a/internal/mygit/objects/object.go b/pkg/mygit/objects/object.go similarity index 95% rename from internal/mygit/objects/object.go rename to pkg/mygit/objects/object.go index dd168e2..2c66ec8 100644 --- a/internal/mygit/objects/object.go +++ b/pkg/mygit/objects/object.go @@ -2,8 +2,8 @@ package objects import ( "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" "io" "path/filepath" "strings" diff --git a/internal/mygit/objects/reader.go b/pkg/mygit/objects/reader.go similarity index 98% rename from internal/mygit/objects/reader.go rename to pkg/mygit/objects/reader.go index 2a9b7e8..d1e0bbb 100644 --- a/internal/mygit/objects/reader.go +++ b/pkg/mygit/objects/reader.go @@ -7,8 +7,8 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" "io" "os" "path/filepath" diff --git a/internal/mygit/objects/writer.go b/pkg/mygit/objects/writer.go similarity index 96% rename from internal/mygit/objects/writer.go rename to pkg/mygit/objects/writer.go index 1528c2b..d56d4f5 100644 --- a/internal/mygit/objects/writer.go +++ b/pkg/mygit/objects/writer.go @@ -7,8 +7,8 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" - "github.com/richardjennings/mygit/internal/mygit/refs" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/refs" "io" "io/fs" "os" diff --git a/internal/mygit/refs/refs.go b/pkg/mygit/refs/refs.go similarity index 97% rename from internal/mygit/refs/refs.go rename to pkg/mygit/refs/refs.go index 3c88766..ffd36dd 100644 --- a/internal/mygit/refs/refs.go +++ b/pkg/mygit/refs/refs.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/richardjennings/mygit/internal/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/config" "io/fs" "os" "path/filepath" From 80a431547e47d428f1501f2c102f9c761782072c Mon Sep 17 00:00:00 2001 From: Richard Jennings Date: Thu, 2 May 2024 10:03:49 +0100 Subject: [PATCH 3/4] restructure --- README.md | 6 + cmd/add.go | 4 +- cmd/branch.go | 8 +- cmd/commit.go | 4 +- cmd/init.go | 4 +- cmd/log.go | 4 +- cmd/ls-files.go | 4 +- cmd/restore.go | 4 +- cmd/status.go | 4 +- cmd/switch.go | 4 +- pkg/mygit/git/add.go | 80 +++ pkg/mygit/git/add_test.go | 1 + pkg/mygit/git/branch.go | 51 ++ pkg/mygit/git/branch_test.go | 1 + pkg/mygit/git/commit.go | 68 +++ pkg/mygit/git/commit_test.go | 1 + pkg/mygit/git/init.go | 27 + pkg/mygit/git/init_test.go | 2 + pkg/mygit/git/log.go | 29 ++ pkg/mygit/git/log_test.go | 2 + pkg/mygit/git/ls-files.go | 16 + pkg/mygit/git/ls-files_test.go | 1 + pkg/mygit/git/restore.go | 79 +++ pkg/mygit/git/restore_test.go | 1 + pkg/mygit/git/status.go | 43 ++ pkg/mygit/git/status_test.go | 1 + pkg/mygit/git/switch.go | 148 ++++++ pkg/mygit/git/switch_test.go | 1 + .../{mygit_test.go => integration_test.go} | 44 +- pkg/mygit/mygit.go | 466 ------------------ 30 files changed, 594 insertions(+), 514 deletions(-) create mode 100644 pkg/mygit/git/add.go create mode 100644 pkg/mygit/git/add_test.go create mode 100644 pkg/mygit/git/branch.go create mode 100644 pkg/mygit/git/branch_test.go create mode 100644 pkg/mygit/git/commit.go create mode 100644 pkg/mygit/git/commit_test.go create mode 100644 pkg/mygit/git/init.go create mode 100644 pkg/mygit/git/init_test.go create mode 100644 pkg/mygit/git/log.go create mode 100644 pkg/mygit/git/log_test.go create mode 100644 pkg/mygit/git/ls-files.go create mode 100644 pkg/mygit/git/ls-files_test.go create mode 100644 pkg/mygit/git/restore.go create mode 100644 pkg/mygit/git/restore_test.go create mode 100644 pkg/mygit/git/status.go create mode 100644 pkg/mygit/git/status_test.go create mode 100644 pkg/mygit/git/switch.go create mode 100644 pkg/mygit/git/switch_test.go rename pkg/mygit/{mygit_test.go => integration_test.go} (86%) delete mode 100644 pkg/mygit/mygit.go diff --git a/README.md b/README.md index 4c5ecff..eeb65f6 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,9 @@ Playing around writing a toy git + +## Init + +``` + +``` diff --git a/cmd/add.go b/cmd/add.go index 6c7ccff..cf06434 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" ) @@ -13,7 +13,7 @@ var addCmd = &cobra.Command{ if err := configure(); err != nil { log.Fatalln(err) } - return mygit.Add(args...) + return git.Add(args...) }, } diff --git a/cmd/branch.go b/cmd/branch.go index dc42dd6..3713c77 100644 --- a/cmd/branch.go +++ b/cmd/branch.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" "os" @@ -18,14 +18,14 @@ var branchCmd = &cobra.Command{ } if len(args) == 0 { // default to list branches - return mygit.ListBranches(os.Stdout) + return git.ListBranches(os.Stdout) } if len(args) == 1 { if branchDelete { - return mygit.DeleteBranch(args[0]) + return git.DeleteBranch(args[0]) } else { // create a branch - return mygit.CreateBranch(args[0]) + return git.CreateBranch(args[0]) } } return nil diff --git a/cmd/commit.go b/cmd/commit.go index a53a47e..f6db881 100644 --- a/cmd/commit.go +++ b/cmd/commit.go @@ -3,7 +3,7 @@ package cmd import ( "encoding/hex" "fmt" - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" "os" @@ -21,7 +21,7 @@ var commitCmd = &cobra.Command{ if cmd.Flags().Changed("message") { msg = []byte(commitMessage) } - sha, err := mygit.Commit(msg) + sha, err := git.Commit(msg) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/init.go b/cmd/init.go index 20bf95f..ad5fd0e 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" ) @@ -12,7 +12,7 @@ var initCmd = &cobra.Command{ if err := configure(); err != nil { log.Fatalln(err) } - return mygit.Init() + return git.Init() }, } diff --git a/cmd/log.go b/cmd/log.go index a2a4403..3676359 100644 --- a/cmd/log.go +++ b/cmd/log.go @@ -1,8 +1,8 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit" "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" "os" @@ -23,7 +23,7 @@ var logCmd = &cobra.Command{ return err } c.Stdout = os.Stdout - err = mygit.Log(w) + err = git.Log(w) if err != nil { return err } diff --git a/cmd/ls-files.go b/cmd/ls-files.go index a0ade86..f518fa8 100644 --- a/cmd/ls-files.go +++ b/cmd/ls-files.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" ) @@ -13,7 +13,7 @@ var lsFilesCmd = &cobra.Command{ if err := configure(); err != nil { log.Fatalln(err) } - files, err := mygit.LsFiles() + files, err := git.LsFiles() if err != nil { return err } diff --git a/cmd/restore.go b/cmd/restore.go index 250b07f..7b4df24 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" "os" @@ -17,7 +17,7 @@ var restoreCmd = &cobra.Command{ if err := configure(); err != nil { log.Fatalln(err) } - if err := mygit.Restore(args[0], restoreStaged); err != nil { + if err := git.Restore(args[0], restoreStaged); err != nil { fmt.Println(err) os.Exit(1) } diff --git a/cmd/status.go b/cmd/status.go index 5dee6c4..526a3c2 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "os" ) @@ -14,7 +14,7 @@ var statusCmd = &cobra.Command{ fmt.Println(err) os.Exit(1) } - if err := mygit.Status(os.Stdout); err != nil { + if err := git.Status(os.Stdout); err != nil { fmt.Println(err) os.Exit(1) } diff --git a/cmd/switch.go b/cmd/switch.go index 8b816fa..552b7c2 100644 --- a/cmd/switch.go +++ b/cmd/switch.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/spf13/cobra" "log" "os" @@ -15,7 +15,7 @@ var switchCmd = &cobra.Command{ if err := configure(); err != nil { log.Fatalln(err) } - if err := mygit.SwitchBranch(args[0]); err != nil { + if err := git.SwitchBranch(args[0]); err != nil { fmt.Println(err) os.Exit(1) } diff --git a/pkg/mygit/git/add.go b/pkg/mygit/git/add.go new file mode 100644 index 0000000..0f742a0 --- /dev/null +++ b/pkg/mygit/git/add.go @@ -0,0 +1,80 @@ +package git + +import ( + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/index" + "github.com/richardjennings/mygit/pkg/mygit/objects" + "path/filepath" + "strings" +) + +// Add adds one or more file paths to the Index. +func Add(paths ...string) error { + idx, err := index.ReadIndex() + if err != nil { + return err + } + // get working directory files with idx status + wdFiles, err := index.FsStatus(config.Path()) + if err != nil { + return err + } + var updates []*gfs.File + for _, p := range paths { + if p == "." { + // special case meaning add everything + for _, v := range wdFiles.Files() { + switch v.WdStatus { + case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex, gfs.WDDeletedInWorktree: + updates = append(updates, v) + } + } + } else { + found := false + for _, v := range wdFiles.Files() { + if v.Path == p { + switch v.WdStatus { + case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex, gfs.WDDeletedInWorktree: + updates = append(updates, v) + } + found = true + break + } + } + if !found { + // try directory @todo more efficient implementation + for _, v := range wdFiles.Files() { + if strings.HasPrefix(v.Path, p+string(filepath.Separator)) { + switch v.WdStatus { + case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex, gfs.WDDeletedInWorktree: + updates = append(updates, v) + } + found = true + } + } + } + + if !found { + return fmt.Errorf("fatal: pathspec '%s' did not match any files (directories not implemented yet)", p) + } + } + } + for _, v := range updates { + switch v.WdStatus { + case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex: + // add the file to the object store + obj, err := objects.WriteBlob(v.Path) + if err != nil { + return err + } + v.Sha, _ = gfs.NewSha(obj.Sha) + } + if err := idx.Add(v); err != nil { + return err + } + } + // once all files are added to idx struct, write it out + return idx.Write() +} diff --git a/pkg/mygit/git/add_test.go b/pkg/mygit/git/add_test.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/mygit/git/add_test.go @@ -0,0 +1 @@ +package git diff --git a/pkg/mygit/git/branch.go b/pkg/mygit/git/branch.go new file mode 100644 index 0000000..aa6b2e6 --- /dev/null +++ b/pkg/mygit/git/branch.go @@ -0,0 +1,51 @@ +package git + +import ( + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/refs" + "io" +) + +const DeleteBranchCheckedOutErrFmt = "error: Cannot delete branch '%s' checked out at '%s'" + +func DeleteBranch(name string) error { + // Delete Branch removes any branch that is not checked out + // @todo more correct semantics + currentBranch, err := refs.CurrentBranch() + if err != nil { + return err + } + if name == currentBranch { + return fmt.Errorf(DeleteBranchCheckedOutErrFmt, name, config.Path()) + } + return refs.DeleteBranch(name) +} + +func CreateBranch(name string) error { + return refs.CreateBranch(name) +} + +func ListBranches(o io.Writer) error { + var err error + currentBranch, err := refs.CurrentBranch() + if err != nil { + return err + } + branches, err := refs.ListBranches() + if err != nil { + return err + } + for _, v := range branches { + if v == currentBranch { + _, err = o.Write([]byte(fmt.Sprintf("* %v\n", v))) + } else { + _, err = o.Write([]byte(fmt.Sprintf(" %v\n", v))) + } + if err != nil { + return err + } + } + return nil +} + diff --git a/pkg/mygit/git/branch_test.go b/pkg/mygit/git/branch_test.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/mygit/git/branch_test.go @@ -0,0 +1 @@ +package git diff --git a/pkg/mygit/git/commit.go b/pkg/mygit/git/commit.go new file mode 100644 index 0000000..4b6ef4b --- /dev/null +++ b/pkg/mygit/git/commit.go @@ -0,0 +1,68 @@ +package git + +import ( + "errors" + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/index" + "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/mygit/refs" + "log" + "os" + "os/exec" + "time" +) + +// Commit writes a git commit object from the files in the index +func Commit(message []byte) ([]byte, error) { + idx, err := index.ReadIndex() + if err != nil { + return nil, err + } + root := objects.ObjectTree(idx.Files()) + tree, err := root.WriteTree() + if err != nil { + return nil, err + } + // git has the --allow-empty flag which here defaults to true currently + // @todo check for changes to be committed. + previousCommits, err := refs.PreviousCommits() + if err != nil { + // @todo error types to check for e.g no previous commits as source of error + return nil, err + } + commit := &objects.Commit{ + Tree: tree, + Parents: previousCommits, + Author: fmt.Sprintf("%s <%s>", config.AuthorName(), config.AuthorEmail()), + AuthoredTime: time.Now(), + Committer: fmt.Sprintf("%s <%s>", config.CommitterName(), config.CommitterEmail()), + CommittedTime: time.Now(), + } + if message != nil { + commit.Message = message + } else { + // empty commit file + if err := os.WriteFile(config.EditorFile(), []byte{}, 0600); err != nil { + log.Fatalln(err) + } + ed, args := config.Editor() + args = append(args, config.EditorFile()) + cmd := exec.Command(ed, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + err = cmd.Run() + if err != nil { + log.Fatalln(err) + } + msg, err := os.ReadFile(args[0]) + if err != nil { + log.Fatalln(msg) + } + commit.Message = msg + } + if len(commit.Message) == 0 { + return nil, errors.New("Aborting commit due to empty commit message.") + } + return objects.WriteCommit(commit) +} diff --git a/pkg/mygit/git/commit_test.go b/pkg/mygit/git/commit_test.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/mygit/git/commit_test.go @@ -0,0 +1 @@ +package git diff --git a/pkg/mygit/git/init.go b/pkg/mygit/git/init.go new file mode 100644 index 0000000..9d240e2 --- /dev/null +++ b/pkg/mygit/git/init.go @@ -0,0 +1,27 @@ +package git + +import ( + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/config" + "log" + "os" +) + +// Init initializes a git repository +func Init() error { + path := config.GitPath() + if err := os.MkdirAll(path, 0755); err != nil { + return err + } + for _, v := range []string{ + config.ObjectPath(), + config.RefsDirectory(), + config.RefsHeadsDirectory(), + } { + if err := os.MkdirAll(v, 0755); err != nil { + log.Fatalln(err) + } + } + // set default main branch + return os.WriteFile(config.GitHeadPath(), []byte(fmt.Sprintf("ref: %s\n", config.Config.DefaultBranch)), 0644) +} diff --git a/pkg/mygit/git/init_test.go b/pkg/mygit/git/init_test.go new file mode 100644 index 0000000..6abb9ef --- /dev/null +++ b/pkg/mygit/git/init_test.go @@ -0,0 +1,2 @@ +package git + diff --git a/pkg/mygit/git/log.go b/pkg/mygit/git/log.go new file mode 100644 index 0000000..dfd778b --- /dev/null +++ b/pkg/mygit/git/log.go @@ -0,0 +1,29 @@ +package git + +import ( + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/mygit/refs" + "io" +) + +// Log prints out the commit log for the current branch +func Log(o io.Writer) error { + branch, err := refs.CurrentBranch() + if err != nil { + return err + } + commitSha, err := refs.HeadSHA(branch) + if err != nil { + return err + } + for c, err := objects.ReadCommit(commitSha); c != nil && err == nil; c, err = objects.ReadCommit(c.Parents[0]) { + _, _ = fmt.Fprintf(o, "commit %s\nAuthor: %s <%s>\nDate: %s\n\n%8s\n", c.Sha, c.Author, c.AuthorEmail, c.AuthoredTime.String(), c.Message) + if len(c.Parents) == 0 { + break + } + } + + return nil +} + diff --git a/pkg/mygit/git/log_test.go b/pkg/mygit/git/log_test.go new file mode 100644 index 0000000..6abb9ef --- /dev/null +++ b/pkg/mygit/git/log_test.go @@ -0,0 +1,2 @@ +package git + diff --git a/pkg/mygit/git/ls-files.go b/pkg/mygit/git/ls-files.go new file mode 100644 index 0000000..3de1898 --- /dev/null +++ b/pkg/mygit/git/ls-files.go @@ -0,0 +1,16 @@ +package git + +import "github.com/richardjennings/mygit/pkg/mygit/index" + +// LsFiles returns a list of files in the index +func LsFiles() ([]string, error) { + idx, err := index.ReadIndex() + if err != nil { + return nil, err + } + var files []string + for _, v := range idx.Files() { + files = append(files, v.Path) + } + return files, nil +} diff --git a/pkg/mygit/git/ls-files_test.go b/pkg/mygit/git/ls-files_test.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/mygit/git/ls-files_test.go @@ -0,0 +1 @@ +package git diff --git a/pkg/mygit/git/restore.go b/pkg/mygit/git/restore.go new file mode 100644 index 0000000..dc2a883 --- /dev/null +++ b/pkg/mygit/git/restore.go @@ -0,0 +1,79 @@ +package git + +import ( + "errors" + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/index" + "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/mygit/refs" + "io" + "os" + "path/filepath" +) + +func Restore(path string, staged bool) error { + idx, err := index.ReadIndex() + if err != nil { + return err + } + currentCommit, err := refs.LastCommit() + if err != nil { + return err + } + currentStatus, err := index.Status(idx, currentCommit) + if err != nil { + return err + } + if staged { + // remove file from index + if err := idx.Rm(path); err != nil { + return err + } + return idx.Write() + } + + fileStatus, ok := currentStatus.Contains(path) + // if the path not found or is untracked working directory fileStatus then error + if !ok || fileStatus.WdStatus == gfs.WDUntracked { + return fmt.Errorf("error: pathspec '%s' did not match any fileStatus(s) known to git", path) + } + // if in index but not committed + if fileStatus.IdxStatus == gfs.IndexAddedInIndex && fileStatus.WdStatus != gfs.WDWorktreeChangedSinceIndex { + // there is nothing to do, right ? ... + return nil + } + + // update working directory fileStatus with object referenced by index + file := idx.File(path) + if file == nil { + // this should not happen + return errors.New("index did not return file for some reason") + } + obj, err := objects.ReadObject(file.Sha.AsHexBytes()) + if err != nil { + return err + } + fh, err := os.OpenFile(filepath.Join(config.Path(), path), os.O_TRUNC|os.O_RDWR, 0600) + if err != nil { + return err + } + defer func() { _ = fh.Close() }() + reader, err := obj.ReadCloser() + if err != nil { + return err + } + if err := objects.ReadHeadBytes(reader, obj); err != nil { + return err + } + _, err = io.Copy(fh, reader) + if err != nil { + return err + } + if err := fh.Close(); err != nil { + return err + } + return os.Chtimes(filepath.Join(config.Path(), path), file.Finfo.ModTime(), file.Finfo.ModTime()) +} + diff --git a/pkg/mygit/git/restore_test.go b/pkg/mygit/git/restore_test.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/mygit/git/restore_test.go @@ -0,0 +1 @@ +package git diff --git a/pkg/mygit/git/status.go b/pkg/mygit/git/status.go new file mode 100644 index 0000000..577a27f --- /dev/null +++ b/pkg/mygit/git/status.go @@ -0,0 +1,43 @@ +package git + +import ( + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/index" + "github.com/richardjennings/mygit/pkg/mygit/refs" + "io" +) + +// Status currently displays the file statuses comparing the working directory +// to the index and the index to the last commit (if any). +func Status(o io.Writer) error { + var err error + // index + idx, err := index.ReadIndex() + if err != nil { + return err + } + + commitSha, err := refs.LastCommit() + if err != nil { + // @todo error types to check for e.g no previous commits as source of error + return err + } + + files, err := index.Status(idx, commitSha) + + if err != nil { + return err + } + + for _, v := range files.Files() { + if v.IdxStatus == gfs.IndexNotUpdated && v.WdStatus == gfs.WDIndexAndWorkingTreeMatch { + continue + } + if _, err := fmt.Fprintf(o, "%s%s %s\n", v.IdxStatus, v.WdStatus, v.Path); err != nil { + return err + } + } + + return nil +} diff --git a/pkg/mygit/git/status_test.go b/pkg/mygit/git/status_test.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/mygit/git/status_test.go @@ -0,0 +1 @@ +package git diff --git a/pkg/mygit/git/switch.go b/pkg/mygit/git/switch.go new file mode 100644 index 0000000..3b954c7 --- /dev/null +++ b/pkg/mygit/git/switch.go @@ -0,0 +1,148 @@ +package git + +import ( + "errors" + "fmt" + "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/index" + "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/mygit/refs" + "io" + "os" + "path/filepath" +) + +func SwitchBranch(name string) error { + + // index + idx, err := index.ReadIndex() + if err != nil { + return err + } + + // get commit sha + commitSha, err := refs.HeadSHA(name) + if err != nil { + return err + } + + if commitSha == nil { + return fmt.Errorf("fatal: invalid reference: %s", name) + } + + currentCommit, err := refs.LastCommit() + if err != nil { + // @todo error types to check for e.g no previous commits as source of error + return err + } + + currentStatus, err := index.Status(idx, currentCommit) + if err != nil { + return err + } + + // get commit files + commitFiles, err := objects.CommittedFiles(commitSha) + if err != nil { + return err + } + + commitSet := gfs.NewFileSet(commitFiles) + + var errorWdFiles []*gfs.File + var errorIdxFiles []*gfs.File + var deleteFiles []*gfs.File + + for _, v := range currentStatus.Files() { + if v.IdxStatus == gfs.IndexUpdatedInIndex { + errorIdxFiles = append(errorIdxFiles, v) + continue + } + if _, ok := commitSet.Contains(v.Path); ok { + if v.WdStatus == gfs.WDUntracked { + errorWdFiles = append(errorWdFiles, v) + continue + } + } else { + // should be deleted + deleteFiles = append(deleteFiles, v) + } + } + var errMsg = "" + if len(errorIdxFiles) > 0 { + filestr := "" + for _, v := range errorIdxFiles { + filestr += fmt.Sprintf("\t%s\n", v.Path) + } + errMsg = fmt.Sprintf("error: The following untracked working tree files would be overwritten by checkout:\n %sPlease move or remove them before you switch branches.\nAborting", filestr) + } + if len(errorWdFiles) > 0 { + filestr := "" + for _, v := range errorWdFiles { + filestr += fmt.Sprintf("\t%s\n", v.Path) + } + if errMsg != "" { + errMsg += "\n" + } + errMsg += fmt.Sprintf("error: The following untracked working tree files would be overwritten by checkout:\n %sPlease move or remove them before you switch branches.\nAborting", filestr) + } + + if errMsg != "" { + return errors.New(errMsg) + } + + for _, v := range deleteFiles { + if err := os.Remove(filepath.Join(config.Path(), v.Path)); err != nil { + return err + } + } + + idx = index.NewIndex() + + for _, v := range commitFiles { + obj, err := objects.ReadObject(v.Sha.AsHexBytes()) + if err != nil { + return err + } + r, err := obj.ReadCloser() + if err != nil { + return err + } + buf := make([]byte, obj.HeaderLength) + if _, err := r.Read(buf); err != nil { + return err + } + f, err := os.OpenFile(filepath.Join(config.Path(), v.Path), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0655) + if err != nil { + return err + } + + if _, err := io.Copy(f, r); err != nil { + return err + } + if err := r.Close(); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + v.WdStatus = gfs.WDUntracked + if err := idx.Add(v); err != nil { + return err + } + } + + if err := idx.Write(); err != nil { + return err + } + + // update HEAD + if err := refs.UpdateHead(name); err != nil { + return err + } + + return nil + +} + diff --git a/pkg/mygit/git/switch_test.go b/pkg/mygit/git/switch_test.go new file mode 100644 index 0000000..cd99cdb --- /dev/null +++ b/pkg/mygit/git/switch_test.go @@ -0,0 +1 @@ +package git diff --git a/pkg/mygit/mygit_test.go b/pkg/mygit/integration_test.go similarity index 86% rename from pkg/mygit/mygit_test.go rename to pkg/mygit/integration_test.go index 2a92dae..35ed2fd 100644 --- a/pkg/mygit/mygit_test.go +++ b/pkg/mygit/integration_test.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/richardjennings/mygit/pkg/mygit/config" "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/mygit/git" "github.com/richardjennings/mygit/pkg/mygit/objects" "github.com/richardjennings/mygit/pkg/mygit/refs" "github.com/stretchr/testify/assert" @@ -16,24 +17,11 @@ import ( "time" ) -func Test_Init(t *testing.T) { - dir := testDir(t) - defer func() { _ = os.RemoveAll(dir) }() - testConfigure(t, dir) - if err := Init(); err != nil { - t.Fatal(err) - } - - actual := testListFiles(t, dir, true) - expected := []string{".git", ".git/HEAD", ".git/objects", ".git/refs", ".git/refs/heads"} - assert.Equal(t, expected, actual) -} - func Test_DefaultBranch(t *testing.T) { dir := testDir(t) defer func() { _ = os.RemoveAll(dir) }() testConfigure(t, dir) - if err := Init(); err != nil { + if err := git.Init(); err != nil { t.Fatal(err) } @@ -49,7 +37,7 @@ func Test_End_To_End(t *testing.T) { testConfigure(t, dir) // git init - if err := Init(); err != nil { + if err := git.Init(); err != nil { t.Fatal(err) } @@ -111,7 +99,7 @@ func Test_End_To_End(t *testing.T) { // create a branch called test // git branch test - assert.Nil(t, CreateBranch("test")) + assert.Nil(t, git.CreateBranch("test")) // check it is now listed // git branch @@ -119,12 +107,12 @@ func Test_End_To_End(t *testing.T) { // trying to delete current checkout branch gives error // git branch -d main - err := DeleteBranch("main") - assert.Equal(t, fmt.Sprintf(DeleteBranchCheckedOutErrFmt, "main", dir), err.Error()) + err := git.DeleteBranch("main") + assert.Equal(t, fmt.Sprintf(git.DeleteBranchCheckedOutErrFmt, "main", dir), err.Error()) // delete test branch // git branch -d test - assert.Nil(t, DeleteBranch("test")) + assert.Nil(t, git.DeleteBranch("test")) // should be just main left // git branch @@ -133,7 +121,7 @@ func Test_End_To_End(t *testing.T) { // create a branch called test2 // git branch test2 - assert.Nil(t, CreateBranch("test2")) + assert.Nil(t, git.CreateBranch("test2")) // add a file to main and commit // echo "world" > world @@ -211,10 +199,10 @@ func testListFiles(t *testing.T, path string, dirs bool) []string { } func testAdd(t *testing.T, path string, numIdxFiles int) { - if err := Add(path); err != nil { + if err := git.Add(path); err != nil { t.Fatal(err) } - files, err := LsFiles() + files, err := git.LsFiles() if err != nil { t.Fatal(err) } @@ -223,20 +211,20 @@ func testAdd(t *testing.T, path string, numIdxFiles int) { func testStatus(t *testing.T, expected string) { buf := bytes.NewBuffer(nil) - if err := Status(buf); err != nil { + if err := git.Status(buf); err != nil { t.Fatal(err) } assert.Equal(t, expected, buf.String()) } func testRestore(t *testing.T, path string, staged bool) { - if err := Restore(path, staged); err != nil { + if err := git.Restore(path, staged); err != nil { t.Fatal(err) } } func testCommit(t *testing.T, message []byte) []byte { - sha, err := Commit(message) + sha, err := git.Commit(message) if err != nil { t.Fatal(err) } @@ -268,7 +256,7 @@ func testCommit(t *testing.T, message []byte) []byte { func testLog(t *testing.T) []byte { buf := bytes.NewBuffer(nil) - err := Log(buf) + err := git.Log(buf) if err != nil { t.Fatal(err) } @@ -277,7 +265,7 @@ func testLog(t *testing.T) []byte { func testBranchLs(t *testing.T, expected string) { buf := bytes.NewBuffer(nil) - err := ListBranches(buf) + err := git.ListBranches(buf) if err != nil { t.Fatal(err) } @@ -285,7 +273,7 @@ func testBranchLs(t *testing.T, expected string) { } func testSwitchBranch(t *testing.T, branch string) { - if err := SwitchBranch(branch); err != nil { + if err := git.SwitchBranch(branch); err != nil { t.Fatal(err) } } diff --git a/pkg/mygit/mygit.go b/pkg/mygit/mygit.go deleted file mode 100644 index 2b9379e..0000000 --- a/pkg/mygit/mygit.go +++ /dev/null @@ -1,466 +0,0 @@ -package mygit - -import ( - "errors" - "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" - "github.com/richardjennings/mygit/pkg/mygit/index" - "github.com/richardjennings/mygit/pkg/mygit/objects" - "github.com/richardjennings/mygit/pkg/mygit/refs" - "io" - "log" - "os" - "os/exec" - "path/filepath" - "strings" - "time" -) - -// Init initializes a git repository -func Init() error { - path := config.GitPath() - if err := os.MkdirAll(path, 0755); err != nil { - return err - } - for _, v := range []string{ - config.ObjectPath(), - config.RefsDirectory(), - config.RefsHeadsDirectory(), - } { - if err := os.MkdirAll(v, 0755); err != nil { - log.Fatalln(err) - } - } - // set default main branch - return os.WriteFile(config.GitHeadPath(), []byte(fmt.Sprintf("ref: %s\n", config.Config.DefaultBranch)), 0644) -} - -// Log prints out the commit log for the current branch -func Log(o io.Writer) error { - branch, err := refs.CurrentBranch() - if err != nil { - return err - } - commitSha, err := refs.HeadSHA(branch) - if err != nil { - return err - } - for c, err := objects.ReadCommit(commitSha); c != nil && err == nil; c, err = objects.ReadCommit(c.Parents[0]) { - _, _ = fmt.Fprintf(o, "commit %s\nAuthor: %s <%s>\nDate: %s\n\n%8s\n", c.Sha, c.Author, c.AuthorEmail, c.AuthoredTime.String(), c.Message) - if len(c.Parents) == 0 { - break - } - } - - return nil -} - -// Add adds one or more file paths to the Index. -func Add(paths ...string) error { - idx, err := index.ReadIndex() - if err != nil { - return err - } - // get working directory files with idx status - wdFiles, err := index.FsStatus(config.Path()) - if err != nil { - return err - } - var updates []*gfs.File - for _, p := range paths { - if p == "." { - // special case meaning add everything - for _, v := range wdFiles.Files() { - switch v.WdStatus { - case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex, gfs.WDDeletedInWorktree: - updates = append(updates, v) - } - } - } else { - found := false - for _, v := range wdFiles.Files() { - if v.Path == p { - switch v.WdStatus { - case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex, gfs.WDDeletedInWorktree: - updates = append(updates, v) - } - found = true - break - } - } - if !found { - // try directory @todo more efficient implementation - for _, v := range wdFiles.Files() { - if strings.HasPrefix(v.Path, p+string(filepath.Separator)) { - switch v.WdStatus { - case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex, gfs.WDDeletedInWorktree: - updates = append(updates, v) - } - found = true - } - } - } - - if !found { - return fmt.Errorf("fatal: pathspec '%s' did not match any files (directories not implemented yet)", p) - } - } - } - for _, v := range updates { - switch v.WdStatus { - case gfs.WDUntracked, gfs.WDWorktreeChangedSinceIndex: - // add the file to the object store - obj, err := objects.WriteBlob(v.Path) - if err != nil { - return err - } - v.Sha, _ = gfs.NewSha(obj.Sha) - } - if err := idx.Add(v); err != nil { - return err - } - } - // once all files are added to idx struct, write it out - return idx.Write() -} - -// LsFiles returns a list of files in the index -func LsFiles() ([]string, error) { - idx, err := index.ReadIndex() - if err != nil { - return nil, err - } - var files []string - for _, v := range idx.Files() { - files = append(files, v.Path) - } - return files, nil -} - -// Commit writes a git commit object from the files in the index -func Commit(message []byte) ([]byte, error) { - idx, err := index.ReadIndex() - if err != nil { - return nil, err - } - root := objects.ObjectTree(idx.Files()) - tree, err := root.WriteTree() - if err != nil { - return nil, err - } - // git has the --allow-empty flag which here defaults to true currently - // @todo check for changes to be committed. - previousCommits, err := refs.PreviousCommits() - if err != nil { - // @todo error types to check for e.g no previous commits as source of error - return nil, err - } - commit := &objects.Commit{ - Tree: tree, - Parents: previousCommits, - Author: fmt.Sprintf("%s <%s>", config.AuthorName(), config.AuthorEmail()), - AuthoredTime: time.Now(), - Committer: fmt.Sprintf("%s <%s>", config.CommitterName(), config.CommitterEmail()), - CommittedTime: time.Now(), - } - if message != nil { - commit.Message = message - } else { - // empty commit file - if err := os.WriteFile(config.EditorFile(), []byte{}, 0600); err != nil { - log.Fatalln(err) - } - ed, args := config.Editor() - args = append(args, config.EditorFile()) - cmd := exec.Command(ed, args...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - err = cmd.Run() - if err != nil { - log.Fatalln(err) - } - msg, err := os.ReadFile(args[0]) - if err != nil { - log.Fatalln(msg) - } - commit.Message = msg - } - if len(commit.Message) == 0 { - return nil, errors.New("Aborting commit due to empty commit message.") - } - return objects.WriteCommit(commit) -} - -// Status currently displays the file statuses comparing the working directory -// to the index and the index to the last commit (if any). -func Status(o io.Writer) error { - var err error - // index - idx, err := index.ReadIndex() - if err != nil { - return err - } - - commitSha, err := refs.LastCommit() - if err != nil { - // @todo error types to check for e.g no previous commits as source of error - return err - } - - files, err := index.Status(idx, commitSha) - - if err != nil { - return err - } - - for _, v := range files.Files() { - if v.IdxStatus == gfs.IndexNotUpdated && v.WdStatus == gfs.WDIndexAndWorkingTreeMatch { - continue - } - if _, err := fmt.Fprintf(o, "%s%s %s\n", v.IdxStatus, v.WdStatus, v.Path); err != nil { - return err - } - } - - return nil -} - -const DeleteBranchCheckedOutErrFmt = "error: Cannot delete branch '%s' checked out at '%s'" - -func DeleteBranch(name string) error { - // Delete Branch removes any branch that is not checked out - // @todo more correct semantics - currentBranch, err := refs.CurrentBranch() - if err != nil { - return err - } - if name == currentBranch { - return fmt.Errorf(DeleteBranchCheckedOutErrFmt, name, config.Path()) - } - return refs.DeleteBranch(name) -} - -func CreateBranch(name string) error { - return refs.CreateBranch(name) -} - -func ListBranches(o io.Writer) error { - var err error - currentBranch, err := refs.CurrentBranch() - if err != nil { - return err - } - branches, err := refs.ListBranches() - if err != nil { - return err - } - for _, v := range branches { - if v == currentBranch { - _, err = o.Write([]byte(fmt.Sprintf("* %v\n", v))) - } else { - _, err = o.Write([]byte(fmt.Sprintf(" %v\n", v))) - } - if err != nil { - return err - } - } - return nil -} - -func SwitchBranch(name string) error { - - // index - idx, err := index.ReadIndex() - if err != nil { - return err - } - - // get commit sha - commitSha, err := refs.HeadSHA(name) - if err != nil { - return err - } - - if commitSha == nil { - return fmt.Errorf("fatal: invalid reference: %s", name) - } - - currentCommit, err := refs.LastCommit() - if err != nil { - // @todo error types to check for e.g no previous commits as source of error - return err - } - - currentStatus, err := index.Status(idx, currentCommit) - if err != nil { - return err - } - - // get commit files - commitFiles, err := objects.CommittedFiles(commitSha) - if err != nil { - return err - } - - commitSet := gfs.NewFileSet(commitFiles) - - var errorWdFiles []*gfs.File - var errorIdxFiles []*gfs.File - var deleteFiles []*gfs.File - - for _, v := range currentStatus.Files() { - if v.IdxStatus == gfs.IndexUpdatedInIndex { - errorIdxFiles = append(errorIdxFiles, v) - continue - } - if _, ok := commitSet.Contains(v.Path); ok { - if v.WdStatus == gfs.WDUntracked { - errorWdFiles = append(errorWdFiles, v) - continue - } - } else { - // should be deleted - deleteFiles = append(deleteFiles, v) - } - } - var errMsg = "" - if len(errorIdxFiles) > 0 { - filestr := "" - for _, v := range errorIdxFiles { - filestr += fmt.Sprintf("\t%s\n", v.Path) - } - errMsg = fmt.Sprintf("error: The following untracked working tree files would be overwritten by checkout:\n %sPlease move or remove them before you switch branches.\nAborting", filestr) - } - if len(errorWdFiles) > 0 { - filestr := "" - for _, v := range errorWdFiles { - filestr += fmt.Sprintf("\t%s\n", v.Path) - } - if errMsg != "" { - errMsg += "\n" - } - errMsg += fmt.Sprintf("error: The following untracked working tree files would be overwritten by checkout:\n %sPlease move or remove them before you switch branches.\nAborting", filestr) - } - - if errMsg != "" { - return errors.New(errMsg) - } - - for _, v := range deleteFiles { - if err := os.Remove(filepath.Join(config.Path(), v.Path)); err != nil { - return err - } - } - - idx = index.NewIndex() - - for _, v := range commitFiles { - obj, err := objects.ReadObject(v.Sha.AsHexBytes()) - if err != nil { - return err - } - r, err := obj.ReadCloser() - if err != nil { - return err - } - buf := make([]byte, obj.HeaderLength) - if _, err := r.Read(buf); err != nil { - return err - } - f, err := os.OpenFile(filepath.Join(config.Path(), v.Path), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0655) - if err != nil { - return err - } - - if _, err := io.Copy(f, r); err != nil { - return err - } - if err := r.Close(); err != nil { - return err - } - if err := f.Close(); err != nil { - return err - } - v.WdStatus = gfs.WDUntracked - if err := idx.Add(v); err != nil { - return err - } - } - - if err := idx.Write(); err != nil { - return err - } - - // update HEAD - if err := refs.UpdateHead(name); err != nil { - return err - } - - return nil - -} - -func Restore(path string, staged bool) error { - idx, err := index.ReadIndex() - if err != nil { - return err - } - currentCommit, err := refs.LastCommit() - if err != nil { - return err - } - currentStatus, err := index.Status(idx, currentCommit) - if err != nil { - return err - } - if staged { - // remove file from index - if err := idx.Rm(path); err != nil { - return err - } - return idx.Write() - } - - fileStatus, ok := currentStatus.Contains(path) - // if the path not found or is untracked working directory fileStatus then error - if !ok || fileStatus.WdStatus == gfs.WDUntracked { - return fmt.Errorf("error: pathspec '%s' did not match any fileStatus(s) known to git", path) - } - // if in index but not committed - if fileStatus.IdxStatus == gfs.IndexAddedInIndex && fileStatus.WdStatus != gfs.WDWorktreeChangedSinceIndex { - // there is nothing to do, right ? ... - return nil - } - - // update working directory fileStatus with object referenced by index - file := idx.File(path) - if file == nil { - // this should not happen - return errors.New("index did not return file for some reason") - } - obj, err := objects.ReadObject(file.Sha.AsHexBytes()) - if err != nil { - return err - } - fh, err := os.OpenFile(filepath.Join(config.Path(), path), os.O_TRUNC|os.O_RDWR, 0600) - if err != nil { - return err - } - defer func() { _ = fh.Close() }() - reader, err := obj.ReadCloser() - if err != nil { - return err - } - if err := objects.ReadHeadBytes(reader, obj); err != nil { - return err - } - _, err = io.Copy(fh, reader) - if err != nil { - return err - } - if err := fh.Close(); err != nil { - return err - } - return os.Chtimes(filepath.Join(config.Path(), path), file.Finfo.ModTime(), file.Finfo.ModTime()) -} From c020eebc524cd0ba236fa91421971207f620bc1f Mon Sep 17 00:00:00 2001 From: Richard Jennings Date: Thu, 2 May 2024 10:12:37 +0100 Subject: [PATCH 4/4] more restructure --- README.md | 28 ++++++++++++++++++++++----- cmd/add.go | 2 +- cmd/branch.go | 2 +- cmd/commit.go | 2 +- cmd/init.go | 2 +- cmd/log.go | 4 ++-- cmd/ls-files.go | 2 +- cmd/restore.go | 2 +- cmd/root.go | 2 +- cmd/status.go | 2 +- cmd/switch.go | 2 +- pkg/{mygit => }/config/config.go | 0 pkg/{mygit => }/gfs/file.go | 4 ++-- pkg/{mygit => }/git/add.go | 8 ++++---- pkg/{mygit => }/git/add_test.go | 0 pkg/{mygit => }/git/branch.go | 5 ++--- pkg/{mygit => }/git/branch_test.go | 0 pkg/{mygit => }/git/commit.go | 8 ++++---- pkg/{mygit => }/git/commit_test.go | 0 pkg/{mygit => }/git/init.go | 2 +- pkg/{mygit => }/git/init_test.go | 0 pkg/{mygit => }/git/log.go | 5 ++--- pkg/{mygit => }/git/log_test.go | 0 pkg/{mygit => }/git/ls-files.go | 2 +- pkg/{mygit => }/git/ls-files_test.go | 0 pkg/{mygit => }/git/restore.go | 11 +++++------ pkg/{mygit => }/git/restore_test.go | 0 pkg/{mygit => }/git/status.go | 6 +++--- pkg/{mygit => }/git/status_test.go | 0 pkg/{mygit => }/git/switch.go | 11 +++++------ pkg/{mygit => }/git/switch_test.go | 0 pkg/{mygit => }/ignore/ignore.go | 2 +- pkg/{mygit => }/index/index.go | 4 ++-- pkg/{mygit => }/index/index_darwin.go | 0 pkg/{mygit => }/index/index_linux.go | 0 pkg/{mygit => }/index/reader.go | 2 +- pkg/{mygit => }/index/status.go | 6 +++--- pkg/{mygit => }/integration_test.go | 12 ++++++------ pkg/{mygit => }/objects/object.go | 4 ++-- pkg/{mygit => }/objects/reader.go | 4 ++-- pkg/{mygit => }/objects/writer.go | 4 ++-- pkg/{mygit => }/refs/refs.go | 2 +- 42 files changed, 83 insertions(+), 69 deletions(-) rename pkg/{mygit => }/config/config.go (100%) rename pkg/{mygit => }/gfs/file.go (98%) rename pkg/{mygit => }/git/add.go (89%) rename pkg/{mygit => }/git/add_test.go (100%) rename pkg/{mygit => }/git/branch.go (90%) rename pkg/{mygit => }/git/branch_test.go (100%) rename pkg/{mygit => }/git/commit.go (88%) rename pkg/{mygit => }/git/commit_test.go (100%) rename pkg/{mygit => }/git/init.go (90%) rename pkg/{mygit => }/git/init_test.go (100%) rename pkg/{mygit => }/git/log.go (84%) rename pkg/{mygit => }/git/log_test.go (100%) rename pkg/{mygit => }/git/ls-files.go (82%) rename pkg/{mygit => }/git/ls-files_test.go (100%) rename pkg/{mygit => }/git/restore.go (87%) rename pkg/{mygit => }/git/restore_test.go (100%) rename pkg/{mygit => }/git/status.go (83%) rename pkg/{mygit => }/git/status_test.go (100%) rename pkg/{mygit => }/git/switch.go (91%) rename pkg/{mygit => }/git/switch_test.go (100%) rename pkg/{mygit => }/ignore/ignore.go (90%) rename pkg/{mygit => }/index/index.go (98%) rename pkg/{mygit => }/index/index_darwin.go (100%) rename pkg/{mygit => }/index/index_linux.go (100%) rename pkg/{mygit => }/index/reader.go (96%) rename pkg/{mygit => }/index/status.go (87%) rename pkg/{mygit => }/integration_test.go (95%) rename pkg/{mygit => }/objects/object.go (95%) rename pkg/{mygit => }/objects/reader.go (98%) rename pkg/{mygit => }/objects/writer.go (97%) rename pkg/{mygit => }/refs/refs.go (98%) diff --git a/README.md b/README.md index eeb65f6..d9fae39 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,30 @@ -# MyGit +# G ## About -Playing around writing a toy git +An experimental `git` client written in Go. +``` +Usage: + [command] -## Init +Available Commands: + add + branch + commit + completion Generate the autocompletion script for the specified shell + help Help about any command + init + log + ls-files + restore + status + switch -``` +Flags: + --git-directory string --git-directory (default ".git") + -h, --help help for this command + --path string --path (default ".") -``` +Use " [command] --help" for more information about a command. +``` \ No newline at end of file diff --git a/cmd/add.go b/cmd/add.go index cf06434..7113b5e 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" ) diff --git a/cmd/branch.go b/cmd/branch.go index 3713c77..c96134e 100644 --- a/cmd/branch.go +++ b/cmd/branch.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/commit.go b/cmd/commit.go index f6db881..3416e45 100644 --- a/cmd/commit.go +++ b/cmd/commit.go @@ -3,7 +3,7 @@ package cmd import ( "encoding/hex" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/init.go b/cmd/init.go index ad5fd0e..ad83916 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" ) diff --git a/cmd/log.go b/cmd/log.go index 3676359..fa10f53 100644 --- a/cmd/log.go +++ b/cmd/log.go @@ -1,8 +1,8 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/ls-files.go b/cmd/ls-files.go index f518fa8..dd0ceed 100644 --- a/cmd/ls-files.go +++ b/cmd/ls-files.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" ) diff --git a/cmd/restore.go b/cmd/restore.go index 7b4df24..a130736 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/root.go b/cmd/root.go index f5c5828..492224d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/config" "github.com/spf13/cobra" "log" ) diff --git a/cmd/status.go b/cmd/status.go index 526a3c2..7762c5c 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "os" ) diff --git a/cmd/switch.go b/cmd/switch.go index 552b7c2..9cbf648 100644 --- a/cmd/switch.go +++ b/cmd/switch.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/git" + "github.com/richardjennings/mygit/pkg/git" "github.com/spf13/cobra" "log" "os" diff --git a/pkg/mygit/config/config.go b/pkg/config/config.go similarity index 100% rename from pkg/mygit/config/config.go rename to pkg/config/config.go diff --git a/pkg/mygit/gfs/file.go b/pkg/gfs/file.go similarity index 98% rename from pkg/mygit/gfs/file.go rename to pkg/gfs/file.go index a87ef21..d0e7171 100644 --- a/pkg/mygit/gfs/file.go +++ b/pkg/gfs/file.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/hex" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/ignore" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/ignore" "os" "path/filepath" "strings" diff --git a/pkg/mygit/git/add.go b/pkg/git/add.go similarity index 89% rename from pkg/mygit/git/add.go rename to pkg/git/add.go index 0f742a0..5aabf12 100644 --- a/pkg/mygit/git/add.go +++ b/pkg/git/add.go @@ -2,10 +2,10 @@ package git import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" - "github.com/richardjennings/mygit/pkg/mygit/index" - "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" + "github.com/richardjennings/mygit/pkg/index" + "github.com/richardjennings/mygit/pkg/objects" "path/filepath" "strings" ) diff --git a/pkg/mygit/git/add_test.go b/pkg/git/add_test.go similarity index 100% rename from pkg/mygit/git/add_test.go rename to pkg/git/add_test.go diff --git a/pkg/mygit/git/branch.go b/pkg/git/branch.go similarity index 90% rename from pkg/mygit/git/branch.go rename to pkg/git/branch.go index aa6b2e6..97889bb 100644 --- a/pkg/mygit/git/branch.go +++ b/pkg/git/branch.go @@ -2,8 +2,8 @@ package git import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/refs" "io" ) @@ -48,4 +48,3 @@ func ListBranches(o io.Writer) error { } return nil } - diff --git a/pkg/mygit/git/branch_test.go b/pkg/git/branch_test.go similarity index 100% rename from pkg/mygit/git/branch_test.go rename to pkg/git/branch_test.go diff --git a/pkg/mygit/git/commit.go b/pkg/git/commit.go similarity index 88% rename from pkg/mygit/git/commit.go rename to pkg/git/commit.go index 4b6ef4b..17d7e41 100644 --- a/pkg/mygit/git/commit.go +++ b/pkg/git/commit.go @@ -3,10 +3,10 @@ package git import ( "errors" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/index" - "github.com/richardjennings/mygit/pkg/mygit/objects" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/index" + "github.com/richardjennings/mygit/pkg/objects" + "github.com/richardjennings/mygit/pkg/refs" "log" "os" "os/exec" diff --git a/pkg/mygit/git/commit_test.go b/pkg/git/commit_test.go similarity index 100% rename from pkg/mygit/git/commit_test.go rename to pkg/git/commit_test.go diff --git a/pkg/mygit/git/init.go b/pkg/git/init.go similarity index 90% rename from pkg/mygit/git/init.go rename to pkg/git/init.go index 9d240e2..a7d15a6 100644 --- a/pkg/mygit/git/init.go +++ b/pkg/git/init.go @@ -2,7 +2,7 @@ package git import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/config" "log" "os" ) diff --git a/pkg/mygit/git/init_test.go b/pkg/git/init_test.go similarity index 100% rename from pkg/mygit/git/init_test.go rename to pkg/git/init_test.go diff --git a/pkg/mygit/git/log.go b/pkg/git/log.go similarity index 84% rename from pkg/mygit/git/log.go rename to pkg/git/log.go index dfd778b..4ba94af 100644 --- a/pkg/mygit/git/log.go +++ b/pkg/git/log.go @@ -2,8 +2,8 @@ package git import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/objects" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/objects" + "github.com/richardjennings/mygit/pkg/refs" "io" ) @@ -26,4 +26,3 @@ func Log(o io.Writer) error { return nil } - diff --git a/pkg/mygit/git/log_test.go b/pkg/git/log_test.go similarity index 100% rename from pkg/mygit/git/log_test.go rename to pkg/git/log_test.go diff --git a/pkg/mygit/git/ls-files.go b/pkg/git/ls-files.go similarity index 82% rename from pkg/mygit/git/ls-files.go rename to pkg/git/ls-files.go index 3de1898..2cf589c 100644 --- a/pkg/mygit/git/ls-files.go +++ b/pkg/git/ls-files.go @@ -1,6 +1,6 @@ package git -import "github.com/richardjennings/mygit/pkg/mygit/index" +import "github.com/richardjennings/mygit/pkg/index" // LsFiles returns a list of files in the index func LsFiles() ([]string, error) { diff --git a/pkg/mygit/git/ls-files_test.go b/pkg/git/ls-files_test.go similarity index 100% rename from pkg/mygit/git/ls-files_test.go rename to pkg/git/ls-files_test.go diff --git a/pkg/mygit/git/restore.go b/pkg/git/restore.go similarity index 87% rename from pkg/mygit/git/restore.go rename to pkg/git/restore.go index dc2a883..9bf26a1 100644 --- a/pkg/mygit/git/restore.go +++ b/pkg/git/restore.go @@ -3,11 +3,11 @@ package git import ( "errors" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" - "github.com/richardjennings/mygit/pkg/mygit/index" - "github.com/richardjennings/mygit/pkg/mygit/objects" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" + "github.com/richardjennings/mygit/pkg/index" + "github.com/richardjennings/mygit/pkg/objects" + "github.com/richardjennings/mygit/pkg/refs" "io" "os" "path/filepath" @@ -76,4 +76,3 @@ func Restore(path string, staged bool) error { } return os.Chtimes(filepath.Join(config.Path(), path), file.Finfo.ModTime(), file.Finfo.ModTime()) } - diff --git a/pkg/mygit/git/restore_test.go b/pkg/git/restore_test.go similarity index 100% rename from pkg/mygit/git/restore_test.go rename to pkg/git/restore_test.go diff --git a/pkg/mygit/git/status.go b/pkg/git/status.go similarity index 83% rename from pkg/mygit/git/status.go rename to pkg/git/status.go index 577a27f..9490957 100644 --- a/pkg/mygit/git/status.go +++ b/pkg/git/status.go @@ -2,9 +2,9 @@ package git import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/gfs" - "github.com/richardjennings/mygit/pkg/mygit/index" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/gfs" + "github.com/richardjennings/mygit/pkg/index" + "github.com/richardjennings/mygit/pkg/refs" "io" ) diff --git a/pkg/mygit/git/status_test.go b/pkg/git/status_test.go similarity index 100% rename from pkg/mygit/git/status_test.go rename to pkg/git/status_test.go diff --git a/pkg/mygit/git/switch.go b/pkg/git/switch.go similarity index 91% rename from pkg/mygit/git/switch.go rename to pkg/git/switch.go index 3b954c7..ce2f0b8 100644 --- a/pkg/mygit/git/switch.go +++ b/pkg/git/switch.go @@ -3,11 +3,11 @@ package git import ( "errors" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" - "github.com/richardjennings/mygit/pkg/mygit/index" - "github.com/richardjennings/mygit/pkg/mygit/objects" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" + "github.com/richardjennings/mygit/pkg/index" + "github.com/richardjennings/mygit/pkg/objects" + "github.com/richardjennings/mygit/pkg/refs" "io" "os" "path/filepath" @@ -145,4 +145,3 @@ func SwitchBranch(name string) error { return nil } - diff --git a/pkg/mygit/git/switch_test.go b/pkg/git/switch_test.go similarity index 100% rename from pkg/mygit/git/switch_test.go rename to pkg/git/switch_test.go diff --git a/pkg/mygit/ignore/ignore.go b/pkg/ignore/ignore.go similarity index 90% rename from pkg/mygit/ignore/ignore.go rename to pkg/ignore/ignore.go index 2bb6991..c093b3d 100644 --- a/pkg/mygit/ignore/ignore.go +++ b/pkg/ignore/ignore.go @@ -1,7 +1,7 @@ package ignore import ( - "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/config" "path/filepath" "strings" ) diff --git a/pkg/mygit/index/index.go b/pkg/index/index.go similarity index 98% rename from pkg/mygit/index/index.go rename to pkg/index/index.go index 7350e52..9cffd6e 100644 --- a/pkg/mygit/index/index.go +++ b/pkg/index/index.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" "io" "os" "path/filepath" diff --git a/pkg/mygit/index/index_darwin.go b/pkg/index/index_darwin.go similarity index 100% rename from pkg/mygit/index/index_darwin.go rename to pkg/index/index_darwin.go diff --git a/pkg/mygit/index/index_linux.go b/pkg/index/index_linux.go similarity index 100% rename from pkg/mygit/index/index_linux.go rename to pkg/index/index_linux.go diff --git a/pkg/mygit/index/reader.go b/pkg/index/reader.go similarity index 96% rename from pkg/mygit/index/reader.go rename to pkg/index/reader.go index 4ba3aa8..89c66d5 100644 --- a/pkg/mygit/index/reader.go +++ b/pkg/index/reader.go @@ -3,7 +3,7 @@ package index import ( "encoding/binary" "errors" - "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/config" "os" ) diff --git a/pkg/mygit/index/status.go b/pkg/index/status.go similarity index 87% rename from pkg/mygit/index/status.go rename to pkg/index/status.go index 3b3c48a..bf4b82b 100644 --- a/pkg/mygit/index/status.go +++ b/pkg/index/status.go @@ -1,9 +1,9 @@ package index import ( - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" - "github.com/richardjennings/mygit/pkg/mygit/objects" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" + "github.com/richardjennings/mygit/pkg/objects" ) // Status returns a FileSet containing all files from commit, index and working directory diff --git a/pkg/mygit/integration_test.go b/pkg/integration_test.go similarity index 95% rename from pkg/mygit/integration_test.go rename to pkg/integration_test.go index 35ed2fd..7deda7e 100644 --- a/pkg/mygit/integration_test.go +++ b/pkg/integration_test.go @@ -1,13 +1,13 @@ -package mygit +package pkg import ( "bytes" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" - "github.com/richardjennings/mygit/pkg/mygit/git" - "github.com/richardjennings/mygit/pkg/mygit/objects" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" + "github.com/richardjennings/mygit/pkg/git" + "github.com/richardjennings/mygit/pkg/objects" + "github.com/richardjennings/mygit/pkg/refs" "github.com/stretchr/testify/assert" "io/fs" "os" diff --git a/pkg/mygit/objects/object.go b/pkg/objects/object.go similarity index 95% rename from pkg/mygit/objects/object.go rename to pkg/objects/object.go index 2c66ec8..5f5d7e4 100644 --- a/pkg/mygit/objects/object.go +++ b/pkg/objects/object.go @@ -2,8 +2,8 @@ package objects import ( "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" "io" "path/filepath" "strings" diff --git a/pkg/mygit/objects/reader.go b/pkg/objects/reader.go similarity index 98% rename from pkg/mygit/objects/reader.go rename to pkg/objects/reader.go index d1e0bbb..9677f87 100644 --- a/pkg/mygit/objects/reader.go +++ b/pkg/objects/reader.go @@ -7,8 +7,8 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/gfs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/gfs" "io" "os" "path/filepath" diff --git a/pkg/mygit/objects/writer.go b/pkg/objects/writer.go similarity index 97% rename from pkg/mygit/objects/writer.go rename to pkg/objects/writer.go index d56d4f5..962d7a7 100644 --- a/pkg/mygit/objects/writer.go +++ b/pkg/objects/writer.go @@ -7,8 +7,8 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" - "github.com/richardjennings/mygit/pkg/mygit/refs" + "github.com/richardjennings/mygit/pkg/config" + "github.com/richardjennings/mygit/pkg/refs" "io" "io/fs" "os" diff --git a/pkg/mygit/refs/refs.go b/pkg/refs/refs.go similarity index 98% rename from pkg/mygit/refs/refs.go rename to pkg/refs/refs.go index ffd36dd..2666870 100644 --- a/pkg/mygit/refs/refs.go +++ b/pkg/refs/refs.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/richardjennings/mygit/pkg/mygit/config" + "github.com/richardjennings/mygit/pkg/config" "io/fs" "os" "path/filepath"