Skip to content

Commit

Permalink
init rewriting code to be clearer, better documented and better tested
Browse files Browse the repository at this point in the history
  • Loading branch information
richardjennings committed Oct 25, 2024
1 parent 520c675 commit b637585
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 173 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@

Learning Git Internals by writing a Git in Go.

## Library Usage

See `./integration_test.go`

## CLI Usage

See `go run ./cmd/gitg --help`
4 changes: 4 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const (
DefaultPackfileDirectory = "pack"
)

func init() {
Configure()

Check failure on line 24 in config.go

View workflow job for this annotation

GitHub Actions / lint

Error return value is not checked (errcheck)

Check failure on line 24 in config.go

View workflow job for this annotation

GitHub Actions / lint

Error return value is not checked (errcheck)
}

var config Cnf

type (
Expand Down
181 changes: 166 additions & 15 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package g
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
Expand Down Expand Up @@ -35,8 +37,8 @@ const (
type (
File struct {
Path string
IdxStatus IndexStatus
WdStatus WDStatus
idxStatus IndexStatus
wdStatus WDStatus
Sha Sha
Finfo os.FileInfo
}
Expand Down Expand Up @@ -202,11 +204,11 @@ func NewFileSet(files []*File) *FileSet {
func (fs *FileSet) Merge(fss *FileSet) {
for _, v := range fss.idx {
if _, ok := fs.idx[v.Path]; ok {
if fs.idx[v.Path].IdxStatus == IndexNotUpdated {
fs.idx[v.Path].IdxStatus = v.IdxStatus
if fs.idx[v.Path].idxStatus == IndexNotUpdated {
fs.idx[v.Path].idxStatus = v.idxStatus
}
if fs.idx[v.Path].WdStatus == WDIndexAndWorkingTreeMatch {
fs.idx[v.Path].WdStatus = v.WdStatus
if fs.idx[v.Path].wdStatus == WDIndexAndWorkingTreeMatch {
fs.idx[v.Path].wdStatus = v.wdStatus
}
} else {
fs.files = append(fs.files, v)
Expand All @@ -222,19 +224,19 @@ func (fs *FileSet) MergeFromIndex(fss *FileSet) {
// in index but not in commit files
fs.files = append(fs.files, v)
fs.idx[v.Path] = v
v.IdxStatus = IndexAddedInIndex
v.idxStatus = IndexAddedInIndex
continue
}
fs.idx[v.Path].Finfo = v.Finfo
if !bytes.Equal(v.Sha.AsByteSlice(), fs.idx[v.Path].Sha.AsByteSlice()) {
fs.idx[v.Path].IdxStatus = IndexUpdatedInIndex
fs.idx[v.Path].idxStatus = IndexUpdatedInIndex
continue
}
}
for _, v := range fs.files {
if _, ok := fss.idx[v.Path]; !ok {
// file exists in commit but not in index
v.IdxStatus = IndexDeletedInIndex
v.idxStatus = IndexDeletedInIndex
}
}
}
Expand All @@ -246,20 +248,23 @@ func (fs *FileSet) MergeFromWD(fss *FileSet) {
// in working directory but not in index or commit files
fs.files = append(fs.files, v)
fs.idx[v.Path] = v
v.WdStatus = WDUntracked
v.IdxStatus = IndexUntracked
v.wdStatus = WDUntracked
v.idxStatus = IndexUntracked
continue
}

if fs.idx[v.Path].Finfo == nil {
// this is a commit file and not in the index
// @todo should this be able to happen ?
fs.idx[v.Path].WdStatus = WDUntracked
fs.idx[v.Path].IdxStatus = IndexUntracked
fs.idx[v.Path].wdStatus = WDUntracked
fs.idx[v.Path].idxStatus = IndexUntracked
} else {
if v.Finfo.ModTime() != fs.idx[v.Path].Finfo.ModTime() {
fs.idx[v.Path].WdStatus = WDWorktreeChangedSinceIndex
fs.idx[v.Path].wdStatus = WDWorktreeChangedSinceIndex
fs.idx[v.Path].Finfo = v.Finfo
// flag that the object needs to be indexed
// perhaps index add should be smarter instead ?
fs.idx[v.Path].Sha = Sha{}
continue
}

Expand All @@ -268,7 +273,7 @@ func (fs *FileSet) MergeFromWD(fss *FileSet) {
for _, v := range fs.files {
if _, ok := fss.idx[v.Path]; !ok {
// file exists in commit but not in index
v.WdStatus = WDDeletedInWorktree
v.wdStatus = WDDeletedInWorktree
}
}
}
Expand Down Expand Up @@ -329,3 +334,149 @@ func Init() error {
// set default main branch
return os.WriteFile(GitHeadPath(), []byte(fmt.Sprintf("ref: %s\n", filepath.Join(RefsHeadPrefix(), DefaultBranch()))), 0644)
}

func (f File) IndexStatus() IndexStatus {
return f.idxStatus
}

func (f File) WorkingDirectoryStatus() WDStatus {
return f.wdStatus
}

// SwitchToBranch updates the repository content to match that of a specified branch name
// or returns an error when it is not safe to do so. This should likely be cahnged to
// SwitchToCommit in the future to handle the broader use-case.
func SwitchToBranch(name string) error {

// get commit sha
commitSha, err := HeadSHA(name)
if err != nil {
return err
}

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

// index
idx, err := ReadIndex()
if err != nil {
return err
}

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

//

currentStatus, err := Status(idx, currentCommit)
if err != nil {
return err
}

// get commit files
commitFiles, err := CommittedFiles(commitSha)
if err != nil {
return err
}

commitSet := NewFileSet(commitFiles)

var errorWdFiles []*File
var errorIdxFiles []*File
var deleteFiles []*File

for _, v := range currentStatus.Files() {
if v.IndexStatus() == IndexUpdatedInIndex {
errorIdxFiles = append(errorIdxFiles, v)
continue
}
if _, ok := commitSet.Contains(v.Path); ok {
if v.WorkingDirectoryStatus() == 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(Path(), v.Path)); err != nil {
return err
}
}

idx = NewIndex()

for _, v := range commitFiles {
obj, err := ReadObject(v.Sha)
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(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 = WDUntracked
if err := idx.Add(v); err != nil {
return err
}
}

if err := idx.Write(); err != nil {
return err
}

// update HEAD
if err := UpdateHead(name); err != nil {
return err
}

return nil
}
15 changes: 3 additions & 12 deletions git/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func Add(paths ...string) error {
if p == "." {
// special case meaning add everything
for _, v := range wdFiles.Files() {
switch v.WdStatus {
switch v.WorkingDirectoryStatus() {
case g.WDUntracked, g.WDWorktreeChangedSinceIndex, g.WDDeletedInWorktree:
updates = append(updates, v)
}
Expand All @@ -32,7 +32,7 @@ func Add(paths ...string) error {
found := false
for _, v := range wdFiles.Files() {
if v.Path == p {
switch v.WdStatus {
switch v.WorkingDirectoryStatus() {
case g.WDUntracked, g.WDWorktreeChangedSinceIndex, g.WDDeletedInWorktree:
updates = append(updates, v)
}
Expand All @@ -44,7 +44,7 @@ func Add(paths ...string) error {
// try directory @todo more efficient implementation
for _, v := range wdFiles.Files() {
if strings.HasPrefix(v.Path, p+string(filepath.Separator)) {
switch v.WdStatus {
switch v.WorkingDirectoryStatus() {
case g.WDUntracked, g.WDWorktreeChangedSinceIndex, g.WDDeletedInWorktree:
updates = append(updates, v)
}
Expand All @@ -59,15 +59,6 @@ func Add(paths ...string) error {
}
}
for _, v := range updates {
switch v.WdStatus {
case g.WDUntracked, g.WDWorktreeChangedSinceIndex:
// add the file to the object store
obj, err := g.WriteBlob(v.Path)
if err != nil {
return err
}
v.Sha = obj.Sha
}
if err := idx.Add(v); err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions git/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ func Restore(path string, staged bool) error {

fileStatus, ok := currentStatus.Contains(path)
// if the path not found or is untracked working directory fileStatus then error
if !ok || fileStatus.WdStatus == g.WDUntracked {
if !ok || fileStatus.WorkingDirectoryStatus() == g.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 == g.IndexAddedInIndex && fileStatus.WdStatus != g.WDWorktreeChangedSinceIndex {
if fileStatus.IndexStatus() == g.IndexAddedInIndex && fileStatus.WorkingDirectoryStatus() != g.WDWorktreeChangedSinceIndex {
// there is nothing to do, right ? ...
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions git/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ func Status(o io.Writer) error {
return err
}
for _, v := range files.Files() {
if v.IdxStatus == g.IndexNotUpdated && v.WdStatus == g.WDIndexAndWorkingTreeMatch {
if v.IndexStatus() == g.IndexNotUpdated && v.WorkingDirectoryStatus() == g.WDIndexAndWorkingTreeMatch {
continue
}
if _, err := fmt.Fprintf(o, "%s%s %s\n", v.IdxStatus, v.WdStatus, v.Path); err != nil {
if _, err := fmt.Fprintf(o, "%s%s %s\n", v.IndexStatus(), v.WorkingDirectoryStatus(), v.Path); err != nil {
return err
}
}
Expand Down
Loading

0 comments on commit b637585

Please sign in to comment.