Skip to content

Commit

Permalink
🚸 Prevent applying commit with no files staged
Browse files Browse the repository at this point in the history
When no files were staged, applying a commit would fail with a Git
error message. This change prevents applying a commit if there are no
staged files even if the commit message is valid.
  • Loading branch information
mikelorant committed Feb 7, 2023
1 parent 2c9df9a commit fcc9b23
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 13 deletions.
10 changes: 10 additions & 0 deletions internal/repository/worktree.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ func (r *Repository) Worktree() (Worktree, error) {
return wt, nil
}

func (w *Worktree) IsStaged() bool {
for _, s := range w.Status {
if !(s.Staging == git.Unmodified || s.Staging == git.Untracked) {
return true
}
}

return false
}

// Alternative method to determine file status. Modified from original
// version which was part of the following pull request.
// https://github.com/zricethezav/gitleaks/pull/463
Expand Down
122 changes: 122 additions & 0 deletions internal/repository/worktree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,125 @@ func TestWorktree(t *testing.T) {
})
}
}

func TestIsStaged(t *testing.T) {
type file struct {
name string
statusCode git.StatusCode
}

tests := []struct {
name string
files []file
staged bool
}{
{
name: "unmodified",
files: []file{
{name: "test", statusCode: git.Unmodified},
},
staged: false,
},
{
name: "untracked",
files: []file{
{name: "test", statusCode: git.Untracked},
},
staged: false,
},
{
name: "modified",
files: []file{
{name: "test", statusCode: git.Modified},
},
staged: true,
},
{
name: "added",
files: []file{
{name: "test", statusCode: git.Added},
},
staged: true,
},
{
name: "deleted",
files: []file{
{name: "test", statusCode: git.Deleted},
},
staged: true,
},
{
name: "renamed",
files: []file{
{name: "test", statusCode: git.Renamed},
},
staged: true,
},
{
name: "copied",
files: []file{
{name: "test", statusCode: git.Copied},
},
staged: true,
},
{
name: "updated_but_unmerged",
files: []file{
{name: "test", statusCode: git.UpdatedButUnmerged},
},
staged: true,
},
{
name: "multiple_staged",
files: []file{
{name: "modified", statusCode: git.Modified},
{name: "added", statusCode: git.Added},
},
staged: true,
},
{
name: "multiple_unstaged",
files: []file{
{name: "unmodified", statusCode: git.Unmodified},
{name: "untracked", statusCode: git.Untracked},
},
staged: false,
},
{
name: "multiple_mixed",
files: []file{
{name: "modified", statusCode: git.Modified},
{name: "unmodified", statusCode: git.Unmodified},
},
staged: true,
},
{
name: "empty",
files: []file{},
staged: false,
},
{
name: "nil",
files: nil,
staged: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
files := make(map[string]*git.FileStatus, len(tt.files))

for _, v := range tt.files {
files[v.name] = &git.FileStatus{
Staging: v.statusCode,
}
}

wt := repository.Worktree{
Status: files,
}

assert.Equal(t, tt.staged, wt.IsStaged(), tt.name)
})
}
}
13 changes: 1 addition & 12 deletions internal/ui/header/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/go-git/go-git/v5"
"github.com/mikelorant/committed/internal/commit"
"github.com/mikelorant/committed/internal/config"
"github.com/mikelorant/committed/internal/emoji"
Expand Down Expand Up @@ -247,7 +246,7 @@ func (m Model) readyCommitType() string {

func (m Model) ready() string {
switch {
case !m.isStaged() && !m.Amend:
case !m.state.Repository.Worktree.IsStaged() && !m.Amend:
return m.styles.readyError.String()
case len(m.Summary()) < 1:
return m.styles.readyIncomplete.String()
Expand All @@ -264,16 +263,6 @@ func (m Model) commitType() string {
return m.styles.commitTypeNew.String()
}

func (m Model) isStaged() bool {
for _, s := range m.state.Repository.Worktree.Status {
if !(s.Staging == git.Unmodified || s.Staging == git.Untracked) {
return true
}
}

return false
}

func ToModel(m tea.Model, c tea.Cmd) (Model, tea.Cmd) {
return m.(Model), c
}
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ func (m Model) commit(q quit) Model {
}

func (m Model) validate() bool {
return m.models.header.Summary() != ""
return m.state.Repository.Worktree.IsStaged() && m.models.header.Summary() != ""
}

func (m *Model) restoreModel(save savedState) {
Expand Down
8 changes: 8 additions & 0 deletions internal/ui/ui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/go-git/go-git/v5"
"github.com/hexops/autogold/v2"
"github.com/mikelorant/committed/internal/commit"
"github.com/mikelorant/committed/internal/config"
Expand Down Expand Up @@ -787,6 +788,13 @@ func testState() commit.State {
Hash: "1",
When: time.Date(2022, time.January, 1, 1, 0, 0, 0, time.UTC),
},
Worktree: repository.Worktree{
Status: map[string]*git.FileStatus{
"test": {
Staging: git.Added,
},
},
},
},
Emojis: &emoji.Set{
Emojis: []emoji.Emoji{
Expand Down

0 comments on commit fcc9b23

Please sign in to comment.