Skip to content

Commit

Permalink
🚧 Add Git worktree status
Browse files Browse the repository at this point in the history
Add the status of the Git worktree for the repository. This is
necessary for determining the state of all the files.

Testing required an alternative approach as stubbing the status method
was difficult. This prevents some error branches from being tested.
  • Loading branch information
mikelorant committed Jan 29, 2023
1 parent 5b71585 commit 9251ba2
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 14 deletions.
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ require (
github.com/charmbracelet/lipgloss v0.6.0
github.com/creack/pty v1.1.18
github.com/forPelevin/gomoji v1.1.8
github.com/go-git/go-billy/v5 v5.3.1
github.com/go-git/go-git-fixtures/v4 v4.3.1
github.com/go-git/go-git/v5 v5.5.1
github.com/goccy/go-yaml v1.9.8
github.com/hexops/autogold/v2 v2.0.2
Expand Down Expand Up @@ -42,14 +44,15 @@ require (
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/hexops/valast v1.4.3 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
Expand All @@ -64,6 +67,7 @@ require (
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
Expand All @@ -77,6 +81,7 @@ require (
golang.org/x/text v0.5.0 // indirect
golang.org/x/tools v0.4.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
mvdan.cc/gofumpt v0.4.0 // indirect
)
29 changes: 21 additions & 8 deletions internal/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,26 @@ type Brancher interface {
References() (storer.ReferenceIter, error)
}

type Worktreer interface {
Worktree() (*git.Worktree, error)
}

type Repository struct {
Opener func(string, *git.PlainOpenOptions) (*git.Repository, error)
GlobalConfig func(config.Scope) (*config.Config, error)
Configer Configer
Remoter Remoter
Header Header
Brancher Brancher
Worktreer Worktreer
}

type Description struct {
Users []User
Remotes []string
Head Head
Branch Branch
Users []User
Remotes []string
Head Head
Branch Branch
Worktree Worktree
}

const repositoryPath string = "."
Expand Down Expand Up @@ -73,6 +79,7 @@ func (r *Repository) Open() error {
r.Remoter = repo
r.Header = repo
r.Brancher = repo
r.Worktreer = repo

return nil
}
Expand All @@ -98,10 +105,16 @@ func (r *Repository) Describe() (Description, error) {
return Description{}, fmt.Errorf("unable to get branch: %w", err)
}

wt, err := r.Worktree()
if err != nil {
return Description{}, fmt.Errorf("unable to get worktree: %w", err)
}

return Description{
Users: us,
Remotes: rs,
Head: h,
Branch: b,
Users: us,
Remotes: rs,
Head: h,
Branch: b,
Worktree: wt,
}, nil
}
24 changes: 19 additions & 5 deletions internal/repository/repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"testing"

fixtures "github.com/go-git/go-git-fixtures/v4"
"github.com/go-git/go-git/v5"
"github.com/mikelorant/committed/internal/repository"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -80,14 +81,16 @@ func TestDescribe(t *testing.T) {
remoteErr error
headErr error
branchErr error
worktreeErr error
}

type want struct {
users []repository.User
remotes []string
head repository.Head
branch repository.Branch
err error
users []repository.User
remotes []string
head repository.Head
branch repository.Branch
worktree repository.Worktree
err error
}

tests := []struct {
Expand Down Expand Up @@ -149,6 +152,16 @@ func TestDescribe(t *testing.T) {
err: errMockDescribe,
},
},
{
name: "error_worktree",
args: args{
worktreeErr: errMockDescribe,
localBranch: "master",
},
want: want{
err: errMockDescribe,
},
},
}

for _, tt := range tests {
Expand All @@ -159,6 +172,7 @@ func TestDescribe(t *testing.T) {
Remoter: MockRepositoryRemote{err: tt.args.remoteErr},
Header: MockRepositoryHead{headErr: tt.args.headErr},
Brancher: MockRepositoryBranch{local: tt.args.localBranch, headErr: tt.args.branchErr},
Worktreer: MockRepositoryWorktree{fixture: fixtures.Basic().One(), err: tt.args.worktreeErr},
}

d, err := r.Describe()
Expand Down
28 changes: 28 additions & 0 deletions internal/repository/worktree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package repository

import (
"fmt"

"github.com/go-git/go-git/v5"
)

type Worktree struct {
Status git.Status
}

func (r *Repository) Worktree() (Worktree, error) {
var wt Worktree

w, err := r.Worktreer.Worktree()
if err != nil {
return Worktree{}, fmt.Errorf("unable to get worktree: %w", err)
}

s, err := w.Status()
if err != nil {
return Worktree{}, fmt.Errorf("unable to get status of worktree: %w", err)
}
wt.Status = s

return wt, nil
}
102 changes: 102 additions & 0 deletions internal/repository/worktree_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package repository_test

import (
"errors"
"testing"

"github.com/go-git/go-billy/v5/memfs"
fixtures "github.com/go-git/go-git-fixtures/v4"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/storage/filesystem"
"github.com/mikelorant/committed/internal/repository"
"github.com/stretchr/testify/assert"
)

type MockRepositoryWorktree struct {
fixture *fixtures.Fixture
err error
}

func (m MockRepositoryWorktree) Worktree() (*git.Worktree, error) {
dotgit := m.fixture.DotGit()
st := filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault())
wt := memfs.New()

repo, err := git.Open(st, wt)
if err != nil {
return &git.Worktree{}, err
}

if m.err != nil {
return &git.Worktree{}, errMockWorktree
}

return repo.Worktree()
}

var errMockWorktree = errors.New("error")

func TestWorktree(t *testing.T) {
type args struct {
fixture *fixtures.Fixture
err error
}

type want struct {
count int
err error
}

tests := []struct {
name string
args args
want want
}{
{
name: "dirty",
args: args{
fixture: fixtures.Basic().One(),
},
want: want{
count: 9,
},
},
{
name: "empty",
args: args{
fixture: fixtures.ByTag("empty")[0],
},
},
{
name: "error",
args: args{
fixture: fixtures.Basic().One(),
err: errMockWorktree,
},
want: want{
err: errMockWorktree,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var r repository.Repository

r.Worktreer = MockRepositoryWorktree{
fixture: tt.args.fixture,
err: tt.args.err,
}

wt, err := r.Worktree()
if tt.want.err != nil {
assert.Error(t, err)
assert.ErrorIs(t, err, errMockWorktree)
return
}
assert.NoError(t, err)
assert.Len(t, wt.Status, tt.want.count)
})
}
}

0 comments on commit 9251ba2

Please sign in to comment.