diff --git a/cmd/dehub/main.go b/cmd/dehub/main.go index bbd166e..5b645c2 100644 --- a/cmd/dehub/main.go +++ b/cmd/dehub/main.go @@ -102,10 +102,19 @@ var subCmds = []subCmd{ name: "commit", descr: "commits staged changes to the head of the current branch", body: func(sctx subCmdCtx) error { - msg := sctx.flag.String("msg", "", "Commit message to use") + msg := sctx.flag.String("msg", "", "Commit message") accountID := sctx.flag.String("account-id", "", "Account to sign commit as") sctx.flagParse() + // Don't bother checking any of the parameters, especially commit + // message, if there's no staged changes, + hasStaged, err := sctx.repo().HasStagedChanges() + if err != nil { + return fmt.Errorf("error determining if any changes have been staged: %w", err) + } else if !hasStaged { + return errors.New("no changes have been staged for commit") + } + if *accountID == "" { flag.PrintDefaults() return errors.New("-account-id is required") diff --git a/commit.go b/commit.go index 2a8cadc..e8f2548 100644 --- a/commit.go +++ b/commit.go @@ -93,6 +93,29 @@ func (r *Repo) Commit(m encoding.TextMarshaler, accountID string) (plumbing.Hash }) } +// HasStagedChanges returns true if there are file changes which have been +// staged (e.g. via "git add"). +func (r *Repo) HasStagedChanges() (bool, error) { + w, err := r.GitRepo.Worktree() + if err != nil { + return false, fmt.Errorf("error retrieving worktree: %w", err) + } + + status, err := w.Status() + if err != nil { + return false, fmt.Errorf("error retrieving worktree status: %w", err) + } + + var any bool + for _, fileStatus := range status { + if fileStatus.Staging != git.Unmodified { + any = true + break + } + } + return any, nil +} + // NewChangeCommit constructs a ChangeCommit using the given SignifierInterface // to create a Credential for it. func (r *Repo) NewChangeCommit(msg, accountID string, sig sigcred.SignifierInterface) (ChangeCommit, error) { diff --git a/repo_test.go b/repo_test.go index c7f13aa..8e4a424 100644 --- a/repo_test.go +++ b/repo_test.go @@ -7,6 +7,7 @@ import ( "io" "math/rand" "path/filepath" + "runtime/debug" "testing" "gopkg.in/src-d/go-git.v4/plumbing" @@ -113,3 +114,30 @@ func (h *harness) changeCommit(msg, accountID string, sig sigcred.SignifierInter return tc, hash } + +func TestHasStagedChanges(t *testing.T) { + harness := newHarness(t) + assertHasStaged := func(expHasStaged bool) { + hasStaged, err := harness.repo.HasStagedChanges() + if err != nil { + debug.PrintStack() + t.Fatalf("error calling HasStagedChanges: %v", err) + } else if hasStaged != expHasStaged { + debug.PrintStack() + t.Fatalf("expected HasStagedChanges to return %v", expHasStaged) + } + } + + // the harness starts with some staged changes + assertHasStaged(true) + + harness.stage(map[string]string{"foo": "bar"}) + assertHasStaged(true) + harness.changeCommit("first commit", "root", harness.sig) + assertHasStaged(false) + + harness.stage(map[string]string{"foo": ""}) // delete foo + assertHasStaged(true) + harness.changeCommit("second commit", "root", harness.sig) + assertHasStaged(false) +}