Skip to content

Commit

Permalink
Add pipe.OpFuncs, streamline vcs.CommitFiles
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnStarich committed Nov 20, 2019
1 parent aa28182 commit 180e071
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 64 deletions.
15 changes: 14 additions & 1 deletion pipe/pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type Op interface {
Do() error
}

// OpFunc can easily wrap an anonymous function into an Op
// OpFunc makes it easy to wrap an anonymous function into an Op
type OpFunc func() error

// Do implements the Op interface
Expand All @@ -25,3 +25,16 @@ func (ops Ops) Do() error {
}
return nil
}

// OpFuncs makes it easy to wrap a slice of functions into an Op
type OpFuncs []func() error

// Do implements the Op interface
func (ops OpFuncs) Do() error {
for _, op := range ops {
if err := op(); err != nil {
return err
}
}
return nil
}
29 changes: 29 additions & 0 deletions pipe/pipe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,32 @@ func TestOpsDo(t *testing.T) {
assert.False(t, ranAfterError)
})
}

func TestOpFuncsDo(t *testing.T) {
nilOp := func() error {
return nil
}
someErr := errors.New("some error")
errOp := func() error {
return someErr
}

detectOp := func(ran *bool) func() error {
return func() error {
*ran = true
return nil
}
}

t.Run("no errors", func(t *testing.T) {
var ranLast bool
assert.NoError(t, OpFuncs{nilOp, nilOp, nilOp, detectOp(&ranLast)}.Do())
assert.True(t, ranLast)
})

t.Run("stops on first error", func(t *testing.T) {
var ranAfterError bool
assert.Equal(t, someErr, OpFuncs{nilOp, errOp, detectOp(&ranAfterError), nilOp}.Do())
assert.False(t, ranAfterError)
})
}
132 changes: 69 additions & 63 deletions vcs/vcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,41 +47,41 @@ func initVCS(path string) (*git.Repository, error) {
var repo *git.Repository
var tree *git.Worktree
var status git.Status
return repo, pipe.Ops{
pipe.OpFunc(func() error {
return repo, pipe.OpFuncs{
func() error {
repo, err = git.PlainInit(path, false)
return err
}),
pipe.OpFunc(func() error {
},
func() error {
tree, err = repo.Worktree()
return err
}),
pipe.OpFunc(func() error {
},
func() error {
status, err = tree.Status()
return err
}),
pipe.OpFunc(func() error {
var ops pipe.Ops
},
func() error {
var ops pipe.OpFuncs
added := false
for file, stat := range status {
// add any untracked files, excluding hidden and tmp files
if stat.Worktree == git.Untracked && !strings.HasPrefix(file, ".") && !strings.HasSuffix(file, ".tmp") {
fileCopy := file
ops = append(ops, pipe.OpFunc(func() error {
ops = append(ops, func() error {
_, err := tree.Add(fileCopy)
return err
}))
})
added = true
}
}
if added {
ops = append(ops, pipe.OpFunc(func() error {
ops = append(ops, func() error {
_, err := tree.Commit("Initial commit", &git.CommitOptions{Author: sageAuthor()})
return err
}))
})
}
return ops.Do()
}),
},
}.Do()
}

Expand All @@ -100,58 +100,64 @@ func (s *syncRepo) CommitFiles(prepFiles func() error, message string, paths ...
}
s.mu.Lock()
defer s.mu.Unlock()
if err := prepFiles(); err != nil {
return err
}

tree, err := s.repo.Worktree()
if err != nil {
return err
}
_, headErr := s.repo.Head()
if headErr != nil && headErr != plumbing.ErrReferenceNotFound {
return headErr
}
if headErr != plumbing.ErrReferenceNotFound {
if err := tree.Reset(&git.ResetOptions{}); err != nil { // unstage everything
var err error
var tree *git.Worktree
var repoStatus git.Status
return pipe.OpFuncs{
prepFiles,
func() error {
tree, err = s.repo.Worktree()
return err
}
}

// relativize paths to repo root
rootPath := tree.Filesystem.Root()
for i := range paths {
path, err := filepath.Rel(rootPath, paths[i])
if err != nil {
},
func() error {
_, headErr := s.repo.Head()
if headErr != nil && headErr != plumbing.ErrReferenceNotFound {
return headErr
}
if headErr != plumbing.ErrReferenceNotFound {
// if possible (HEAD exists), unstage all files
return tree.Reset(&git.ResetOptions{})
}
return nil
},
func() error {
var ops pipe.OpFuncs
rootPath := tree.Filesystem.Root()
for i := range paths {
path := &paths[i]
ops = append(ops, func() error {
*path, err = filepath.Rel(rootPath, *path)
return err
}, func() error {
_, err := tree.Add(*path)
return errors.Wrapf(err, "Failed to add %s to the git index", *path)
})
}
return ops.Do()
},
func() error {
repoStatus, err = tree.Status()
return err
}
paths[i] = path
}

for _, path := range paths {
if _, err = tree.Add(path); err != nil {
return errors.Wrapf(err, "Failed to add %s to the git index", path)
}
}
},
func() error {
shouldCommit := false
for _, path := range paths {
status, ok := repoStatus[path]
if ok && status.Staging != git.Unmodified {
shouldCommit = true
break
}
}
if !shouldCommit {
return nil
}

repoStatus, err := tree.Status()
if err != nil {
return err
}
shouldCommit := false
for _, path := range paths {
status, ok := repoStatus[path]
if ok && status.Staging != git.Unmodified {
shouldCommit = true
break
}
}
if !shouldCommit {
return nil
}
_, err = tree.Commit(message, &git.CommitOptions{
Author: sageAuthor(),
})
return err
},
}.Do()

_, err = tree.Commit(message, &git.CommitOptions{
Author: sageAuthor(),
})
return err
}
6 changes: 6 additions & 0 deletions vcs/vcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ func TestCommitFiles(t *testing.T) {
require.IsType(t, &syncRepo{}, repoInt)
repo := repoInt.(*syncRepo)

// committing nothing fails
err = repo.CommitFiles(nil, "")
require.Error(t, err)
assert.Equal(t, "No files to commit", err.Error())

// modify and commit files a few times
err = repo.CommitFiles(func() error {
return ioutil.WriteFile(filepath.Join(testDBPath, "some file.txt"), []byte("hello world"), 0750)
}, "add some file", filepath.Join(testDBPath, "some file.txt"))
Expand Down

0 comments on commit 180e071

Please sign in to comment.