From 2add3a2501a54b9bd5d8e535718a5aaafec085f4 Mon Sep 17 00:00:00 2001 From: mediocregopher <> Date: Fri, 21 Feb 2020 17:37:19 -0700 Subject: [PATCH] Have the dehub command check if there are any staged changes before committing message: |- Have the dehub command check if there are any staged changes before committing The check happens before msg is filled by EDITOR, to save frustration for the user. I'm not super convinced the logic here is correct, as the Status returned by worktree is somewhat complex. But it's the best I can do at the moment, and the tests pass, so it's gonna get shipped. change_hash: AIqqhJwt4URaUOxePbgXWOdLwrG6dtuh+CL9T4CFWQ3M credentials: - type: pgp_signature pub_key_id: 95C46FA6A41148AC body: iQIzBAABAgAdFiEEJ6tQKp6olvZKJ0lwlcRvpqQRSKwFAl5Qd7kACgkQlcRvpqQRSKyeKw//WAQEn99jYyfFSVQeozuczha3wgnIMU59Pa+NIz2hz6BCF9O2L8iuHn+4zBr5GCywTmf2gRfLkl5ZC0yPVy1JgXobtnWFmLxkUyZV9JZiSHVeJmTsA3kO2VUtR9W4HrFBRUk+tUF3VhY4rbVslRACPmF3iEgmhqQ+H3yuogvfUewu7UIcczEP8+lorTqfV/8IC2yRCU2/wiBwbTDiKBF1M95Yms3K63ZOuCRKnkreB0o2KAHCGJ7QSHp2GwzeqXrB4dTYEOJmy0m1a1cWEd5703kqJ/dVdLGfOkn4MBrj22UOnFW7Y/i6NtNsWN1ILDncieykgIgec9sXxA2NILVGMnasmj6qW2wovukxz8xJzfS6dpke/wRu5J5qHfVx9bHJVIFOySNe25qCfm+lchCmsCRiRGg++J+bw/Ubsy8Tlq8SjTlQVm5ACqQCd4C0RsuR5DwzXHIDMnXsgwVWa19pQ4KM5EhMnn0WU+qRJpg3qLYWDMjj0pMuPxxSkXCc3YSf6nLeplCotXDnM8maoukJMvYqr4zEQ5SHrSkLaDNXdBVifuMEiGow1DDXJw+iYZKXNT/LNWCrafzI9IdI1suQgJUqwiJPMBKLzcgi/b5PKNXpBtiADCTvJelQQEOsVqCjsgO4bdemnecztdb9DezJfMEibhd8RoPcXj9W+X7OYZU= account: mediocregopher --- cmd/dehub/main.go | 11 ++++++++++- commit.go | 23 +++++++++++++++++++++++ repo_test.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) 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) +}