Skip to content

Commit

Permalink
minimal packfile support (#27)
Browse files Browse the repository at this point in the history
resolves #9
  • Loading branch information
richardjennings authored Oct 24, 2024
1 parent 810d501 commit b0b1994
Show file tree
Hide file tree
Showing 51 changed files with 1,345 additions and 106 deletions.
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
.idea
.mygit
test-dir
/.idea
/gitg

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
DefaultBranchName = "main"
DefaultEditor = "vim"
DefaultPackedRefsFile = "info/refs"
DefaultPackfileDirectory = "pack"
)

var config Cnf
Expand All @@ -36,6 +37,7 @@ type (
RefsDirectory string
RefsHeadsDirectory string
PackedRefsFile string
PackfileDirectory string
DefaultBranch string
GitIgnore []string
Editor string
Expand Down Expand Up @@ -72,6 +74,7 @@ func Configure(opts ...Opt) error {
RefsDirectory: DefaultRefsDirectory,
RefsHeadsDirectory: DefaultRefsHeadsDirectory,
PackedRefsFile: DefaultPackedRefsFile,
PackfileDirectory: DefaultPackfileDirectory,
DefaultBranch: DefaultBranchName,
Editor: DefaultEditor,
GitIgnore: []string{ //@todo read from .gitignore
Expand Down Expand Up @@ -130,6 +133,10 @@ func PackedRefsFile() string {
return filepath.Join(config.Path, config.GitDirectory, config.PackedRefsFile)
}

func ObjectPackfileDirectory() string {
return filepath.Join(config.Path, config.GitDirectory, config.ObjectsDirectory, config.PackfileDirectory)
}

func GitHeadPath() string {
return filepath.Join(config.Path, config.GitDirectory, config.HeadFile)
}
Expand Down
21 changes: 19 additions & 2 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type (
Finfo os.FileInfo
}
Sha struct {
set bool
hash [20]byte
}
IndexStatus uint8
Expand Down Expand Up @@ -72,18 +73,34 @@ type (
// NewSha creates a Sha from either a binary or hex encoded byte slice
func NewSha(b []byte) (Sha, error) {
if len(b) == 40 {
s := Sha{}
s := Sha{set: true}
_, _ = hex.Decode(s.hash[:], b)
return s, nil
}
if len(b) == 20 {
s := Sha{}
s := Sha{set: true}
copy(s.hash[:], b)
return s, nil
}
return Sha{}, fmt.Errorf("invalid sha %s", b)
}

func ShaFromHexString(s string) (Sha, error) {
v, err := hex.DecodeString(s)
if err != nil {
return Sha{}, err
}
return NewSha(v)
}

func (s Sha) String() string {
return s.AsHexString()
}

func (s Sha) IsSet() bool {
return s.set
}

func (s Sha) AsHexString() string {
return hex.EncodeToString(s.hash[:])
}
Expand Down
6 changes: 3 additions & 3 deletions git/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ func testCommit(t *testing.T, message []byte) []byte {
if err != nil {
t.Fatal(err)
}
if len(sha) != 20 {
t.Errorf("expected sha len 20 got %d", len(sha))
if len(sha) != 40 {
t.Errorf("expected sha len 40 got %d", len(sha))
}
commitSha, err := g.NewSha(sha)
if err != nil {
Expand All @@ -234,7 +234,7 @@ func testCommit(t *testing.T, message []byte) []byte {
}

// read object
c, err := g.ReadCommit(commitSha.AsHexBytes())
c, err := g.ReadCommit(commitSha)
if err != nil {
t.Error(err)
return sha
Expand Down
2 changes: 1 addition & 1 deletion git/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func Restore(path string, staged bool) error {
// this should not happen
return errors.New("index did not return file for some reason")
}
obj, err := g.ReadObject(file.Sha.AsHexBytes())
obj, err := g.ReadObject(file.Sha)
if err != nil {
return err
}
Expand Down
18 changes: 1 addition & 17 deletions git/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,10 @@ import (
// 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 := g.ReadIndex()
files, err := g.CurrentStatus()
if err != nil {
return err
}

commitSha, err := g.LastCommit()
if err != nil {
// @todo error types to check for e.g no previous commits as source of error
return err
}

files, err := g.Status(idx, commitSha)

if err != nil {
return err
}

for _, v := range files.Files() {
if v.IdxStatus == g.IndexNotUpdated && v.WdStatus == g.WDIndexAndWorkingTreeMatch {
continue
Expand All @@ -36,6 +21,5 @@ func Status(o io.Writer) error {
return err
}
}

return nil
}
4 changes: 2 additions & 2 deletions git/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func SwitchBranch(name string) error {
return err
}

if commitSha == nil {
if !commitSha.IsSet() {
return fmt.Errorf("fatal: invalid reference: %s", name)
}

Expand Down Expand Up @@ -97,7 +97,7 @@ func SwitchBranch(name string) error {
idx = g.NewIndex()

for _, v := range commitFiles {
obj, err := g.ReadObject(v.Sha.AsHexBytes())
obj, err := g.ReadObject(v.Sha)
if err != nil {
return err
}
Expand Down
21 changes: 0 additions & 21 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,27 +241,6 @@ func fromIndexItemP(p *indexItemP) *Finfo {
return f
}

// Status returns a FileSet containing all files from commit, index and working directory
// with the corresponding status.
func Status(idx *Index, commitSha []byte) (*FileSet, error) {
var commitFiles []*File
var err error
if commitSha != nil {
commitFiles, err = CommittedFiles(commitSha)
if err != nil {
return nil, err
}
}
files := NewFileSet(commitFiles)
files.MergeFromIndex(NewFileSet(idx.Files()))
workingDirectoryFiles, err := Ls(Path())
if err != nil {
return nil, err
}
files.MergeFromWD(NewFileSet(workingDirectoryFiles))
return files, nil
}

// FsStatus returns a FileSet containing all files from the index and working directory
// with the corresponding status.
func FsStatus(path string) (*FileSet, error) {
Expand Down
57 changes: 44 additions & 13 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type (
Commit struct {
Sha []byte
Tree []byte
Parents [][]byte
Parents []Sha
Author string
AuthorEmail string
AuthoredTime time.Time
Expand Down Expand Up @@ -67,7 +67,7 @@ func (c Commit) String() string {
o += fmt.Sprintf("commit: %s\n", string(c.Sha))
o += fmt.Sprintf("tree: %s\n", string(c.Tree))
for _, v := range c.Parents {
o += fmt.Sprintf("parent: %s\n", string(v))
o += fmt.Sprintf("parent: %s\n", v.AsHexString())
}
o += fmt.Sprintf("%s <%s> %s\n", c.Author, c.AuthorEmail, c.AuthoredTime.String())
o += fmt.Sprintf("%s <%s> %s\n", c.Committer, c.CommitterEmail, c.CommittedTime.String())
Expand Down Expand Up @@ -131,10 +131,21 @@ func (o *Object) FlattenTree() []*File {
return objFiles
}

func ReadObject(sha []byte) (*Object, error) {
func ReadObject(sha Sha) (*Object, error) {
var err error
o := &Object{Sha: sha}
o.ReadCloser = ObjectReadCloser(sha)
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 errors.Is(err, os.ErrNotExist) {
return lookupInPackfiles(sha)
} else {
return nil, err
}
}

o = &Object{Sha: sha.AsHexBytes()}
o.ReadCloser = ObjectReadCloser(sha.AsHexBytes())
z, err := o.ReadCloser()
if err != nil {
return o, err
Expand All @@ -159,6 +170,7 @@ func ReadObject(sha []byte) (*Object, error) {
return nil, fmt.Errorf("unknown %s", string(header[0]))
}
o.Length, err = strconv.Atoi(string(header[1][:len(header[1])-1]))

return o, err
}

Expand All @@ -175,18 +187,25 @@ func ObjectReadCloser(sha []byte) func() (io.ReadCloser, error) {
}

// ReadObjectTree reads an object from the object store
func ReadObjectTree(sha []byte) (*Object, error) {
func ReadObjectTree(sha Sha) (*Object, error) {
obj, err := ReadObject(sha)
if err != nil {
return nil, err
}
if obj == nil {
return nil, fmt.Errorf("object %s not found", sha.AsHexString())
}
switch obj.Typ {
case ObjectTypeCommit:
commit, err := readCommit(obj)
if err != nil {
return obj, err
}
co, err := ReadObjectTree(commit.Tree)
sha, err := NewSha(commit.Tree)
if err != nil {
return obj, err
}
co, err := ReadObjectTree(sha)
if err != nil {
return nil, err
}
Expand All @@ -198,7 +217,11 @@ func ReadObjectTree(sha []byte) (*Object, error) {
return nil, err
}
for _, v := range tree.Items {
o, err := ReadObjectTree(v.Sha)
sha, err := NewSha(v.Sha)
if err != nil {
return obj, err
}
o, err := ReadObjectTree(sha)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -283,7 +306,7 @@ func ReadHeadBytes(r io.ReadCloser, obj *Object) error {
return nil
}

func ReadCommit(sha []byte) (*Commit, error) {
func ReadCommit(sha Sha) (*Commit, error) {
o, err := ReadObject(sha)
if err != nil {
return nil, err
Expand Down Expand Up @@ -326,7 +349,11 @@ func readCommit(obj *Object) (*Commit, error) {
}
// can be parent
if t == "parent" {
c.Parents = append(c.Parents, p[1])
sha, err := NewSha(p[1])
if err != nil {
return nil, err
}
c.Parents = append(c.Parents, sha)
continue
}
if c.Author == "" {
Expand Down Expand Up @@ -408,7 +435,7 @@ func readCommitter(b []byte, c *Commit) error {
return nil
}

func CommittedFiles(sha []byte) ([]*File, error) {
func CommittedFiles(sha Sha) ([]*File, error) {
obj, err := ReadObjectTree(sha)
if err != nil {
return nil, err
Expand Down Expand Up @@ -529,13 +556,17 @@ func WriteCommit(c *Commit) ([]byte, error) {
c.Message,
))
header := []byte(fmt.Sprintf("commit %d%s", len(content), string(byte(0))))
sha, err := WriteObject(header, content, "", ObjectPath())
b, err := WriteObject(header, content, "", ObjectPath())
if err != nil {
return nil, err
}
branch, err := CurrentBranch()
if err != nil {
return nil, err
}
return sha, UpdateBranchHead(branch, sha)
sha, err := NewSha(b)
if err != nil {
return nil, err
}
return sha.AsHexBytes(), UpdateBranchHead(branch, sha)
}
Loading

0 comments on commit b0b1994

Please sign in to comment.