Skip to content

Commit c7050e7

Browse files
authored
Merge pull request #623 from pjbgf/empty-commit
Return error instead of creating empty commits
2 parents 3e07c50 + a513415 commit c7050e7

4 files changed

Lines changed: 75 additions & 6 deletions

File tree

options.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,10 @@ type CommitOptions struct {
458458
// All automatically stage files that have been modified and deleted, but
459459
// new files you have not told Git about are not affected.
460460
All bool
461+
// AllowEmptyCommits enable empty commits to be created. An empty commit
462+
// is when no changes to the tree were made, but a new commit message is
463+
// provided. The default behavior is false, which results in ErrEmptyCommit.
464+
AllowEmptyCommits bool
461465
// Author is the author's signature of the commit. If Author is empty the
462466
// Name and Email is read from the config, and time.Now it's used as When.
463467
Author *object.Signature

worktree_commit.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package git
22

33
import (
44
"bytes"
5+
"errors"
56
"path"
67
"sort"
78
"strings"
@@ -16,6 +17,12 @@ import (
1617
"github.com/go-git/go-billy/v5"
1718
)
1819

20+
var (
21+
// ErrEmptyCommit occurs when a commit is attempted using a clean
22+
// working tree, with no changes to be committed.
23+
ErrEmptyCommit = errors.New("cannot create empty commit: clean working tree")
24+
)
25+
1926
// Commit stores the current contents of the index in a new commit along with
2027
// a log message from the user describing the changes.
2128
func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error) {
@@ -39,7 +46,7 @@ func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error
3946
s: w.r.Storer,
4047
}
4148

42-
tree, err := h.BuildTree(idx)
49+
tree, err := h.BuildTree(idx, opts)
4350
if err != nil {
4451
return plumbing.ZeroHash, err
4552
}
@@ -145,7 +152,11 @@ type buildTreeHelper struct {
145152

146153
// BuildTree builds the tree objects and push its to the storer, the hash
147154
// of the root tree is returned.
148-
func (h *buildTreeHelper) BuildTree(idx *index.Index) (plumbing.Hash, error) {
155+
func (h *buildTreeHelper) BuildTree(idx *index.Index, opts *CommitOptions) (plumbing.Hash, error) {
156+
if len(idx.Entries) == 0 && (opts == nil || !opts.AllowEmptyCommits) {
157+
return plumbing.ZeroHash, ErrEmptyCommit
158+
}
159+
149160
const rootNode = ""
150161
h.trees = map[string]*object.Tree{rootNode: {}}
151162
h.entries = map[string]*object.TreeEntry{}

worktree_commit_test.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ import (
2626
)
2727

2828
func (s *WorktreeSuite) TestCommitEmptyOptions(c *C) {
29-
r, err := Init(memory.NewStorage(), memfs.New())
29+
fs := memfs.New()
30+
r, err := Init(memory.NewStorage(), fs)
3031
c.Assert(err, IsNil)
3132

3233
w, err := r.Worktree()
3334
c.Assert(err, IsNil)
3435

36+
util.WriteFile(fs, "foo", []byte("foo"), 0644)
37+
38+
_, err = w.Add("foo")
39+
c.Assert(err, IsNil)
40+
3541
hash, err := w.Commit("foo", &CommitOptions{})
3642
c.Assert(err, IsNil)
3743
c.Assert(hash.IsZero(), Equals, false)
@@ -65,6 +71,24 @@ func (s *WorktreeSuite) TestCommitInitial(c *C) {
6571
assertStorageStatus(c, r, 1, 1, 1, expected)
6672
}
6773

74+
func (s *WorktreeSuite) TestNothingToCommit(c *C) {
75+
expected := plumbing.NewHash("838ea833ce893e8555907e5ef224aa076f5e274a")
76+
77+
r, err := Init(memory.NewStorage(), memfs.New())
78+
c.Assert(err, IsNil)
79+
80+
w, err := r.Worktree()
81+
c.Assert(err, IsNil)
82+
83+
hash, err := w.Commit("failed empty commit\n", &CommitOptions{Author: defaultSignature()})
84+
c.Assert(hash, Equals, plumbing.ZeroHash)
85+
c.Assert(err, Equals, ErrEmptyCommit)
86+
87+
hash, err = w.Commit("enable empty commits\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true})
88+
c.Assert(hash, Equals, expected)
89+
c.Assert(err, IsNil)
90+
}
91+
6892
func (s *WorktreeSuite) TestCommitParent(c *C) {
6993
expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22")
7094

worktree_test.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package git
33
import (
44
"bytes"
55
"context"
6-
"errors"
76
"io"
87
"io/ioutil"
98
"os"
@@ -2167,6 +2166,8 @@ func (s *WorktreeSuite) TestGrep(c *C) {
21672166
}
21682167

21692168
func (s *WorktreeSuite) TestAddAndCommit(c *C) {
2169+
expectedFiles := 2
2170+
21702171
dir, clean := s.TemporalDir()
21712172
defer clean()
21722173

@@ -2176,29 +2177,58 @@ func (s *WorktreeSuite) TestAddAndCommit(c *C) {
21762177
w, err := repo.Worktree()
21772178
c.Assert(err, IsNil)
21782179

2180+
os.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0o644)
2181+
os.WriteFile(filepath.Join(dir, "bar"), []byte("foo"), 0o644)
2182+
21792183
_, err = w.Add(".")
21802184
c.Assert(err, IsNil)
21812185

2182-
w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
2186+
_, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
21832187
Name: "foo",
21842188
Email: "foo@foo.foo",
21852189
When: time.Now(),
21862190
}})
2191+
c.Assert(err, IsNil)
21872192

21882193
iter, err := w.r.Log(&LogOptions{})
21892194
c.Assert(err, IsNil)
2195+
2196+
filesFound := 0
21902197
err = iter.ForEach(func(c *object.Commit) error {
21912198
files, err := c.Files()
21922199
if err != nil {
21932200
return err
21942201
}
21952202

21962203
err = files.ForEach(func(f *object.File) error {
2197-
return errors.New("Expected no files, got at least 1")
2204+
filesFound++
2205+
return nil
21982206
})
21992207
return err
22002208
})
22012209
c.Assert(err, IsNil)
2210+
c.Assert(filesFound, Equals, expectedFiles)
2211+
}
2212+
2213+
func (s *WorktreeSuite) TestAddAndCommitEmpty(c *C) {
2214+
dir, clean := s.TemporalDir()
2215+
defer clean()
2216+
2217+
repo, err := PlainInit(dir, false)
2218+
c.Assert(err, IsNil)
2219+
2220+
w, err := repo.Worktree()
2221+
c.Assert(err, IsNil)
2222+
2223+
_, err = w.Add(".")
2224+
c.Assert(err, IsNil)
2225+
2226+
_, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
2227+
Name: "foo",
2228+
Email: "foo@foo.foo",
2229+
When: time.Now(),
2230+
}})
2231+
c.Assert(err, Equals, ErrEmptyCommit)
22022232
}
22032233

22042234
func (s *WorktreeSuite) TestLinkedWorktree(c *C) {

0 commit comments

Comments
 (0)