Skip to content

Commit

Permalink
Merge pull request #623 from pjbgf/empty-commit
Browse files Browse the repository at this point in the history
Return error instead of creating empty commits
  • Loading branch information
mcuadros committed Dec 11, 2022
2 parents 3e07c50 + a513415 commit c7050e7
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 6 deletions.
4 changes: 4 additions & 0 deletions options.go
Expand Up @@ -458,6 +458,10 @@ type CommitOptions struct {
// All automatically stage files that have been modified and deleted, but
// new files you have not told Git about are not affected.
All bool
// AllowEmptyCommits enable empty commits to be created. An empty commit
// is when no changes to the tree were made, but a new commit message is
// provided. The default behavior is false, which results in ErrEmptyCommit.
AllowEmptyCommits bool
// Author is the author's signature of the commit. If Author is empty the
// Name and Email is read from the config, and time.Now it's used as When.
Author *object.Signature
Expand Down
15 changes: 13 additions & 2 deletions worktree_commit.go
Expand Up @@ -2,6 +2,7 @@ package git

import (
"bytes"
"errors"
"path"
"sort"
"strings"
Expand All @@ -16,6 +17,12 @@ import (
"github.com/go-git/go-billy/v5"
)

var (
// ErrEmptyCommit occurs when a commit is attempted using a clean
// working tree, with no changes to be committed.
ErrEmptyCommit = errors.New("cannot create empty commit: clean working tree")
)

// Commit stores the current contents of the index in a new commit along with
// a log message from the user describing the changes.
func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error) {
Expand All @@ -39,7 +46,7 @@ func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error
s: w.r.Storer,
}

tree, err := h.BuildTree(idx)
tree, err := h.BuildTree(idx, opts)
if err != nil {
return plumbing.ZeroHash, err
}
Expand Down Expand Up @@ -145,7 +152,11 @@ type buildTreeHelper struct {

// BuildTree builds the tree objects and push its to the storer, the hash
// of the root tree is returned.
func (h *buildTreeHelper) BuildTree(idx *index.Index) (plumbing.Hash, error) {
func (h *buildTreeHelper) BuildTree(idx *index.Index, opts *CommitOptions) (plumbing.Hash, error) {
if len(idx.Entries) == 0 && (opts == nil || !opts.AllowEmptyCommits) {
return plumbing.ZeroHash, ErrEmptyCommit
}

const rootNode = ""
h.trees = map[string]*object.Tree{rootNode: {}}
h.entries = map[string]*object.TreeEntry{}
Expand Down
26 changes: 25 additions & 1 deletion worktree_commit_test.go
Expand Up @@ -26,12 +26,18 @@ import (
)

func (s *WorktreeSuite) TestCommitEmptyOptions(c *C) {
r, err := Init(memory.NewStorage(), memfs.New())
fs := memfs.New()
r, err := Init(memory.NewStorage(), fs)
c.Assert(err, IsNil)

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

util.WriteFile(fs, "foo", []byte("foo"), 0644)

_, err = w.Add("foo")
c.Assert(err, IsNil)

hash, err := w.Commit("foo", &CommitOptions{})
c.Assert(err, IsNil)
c.Assert(hash.IsZero(), Equals, false)
Expand Down Expand Up @@ -65,6 +71,24 @@ func (s *WorktreeSuite) TestCommitInitial(c *C) {
assertStorageStatus(c, r, 1, 1, 1, expected)
}

func (s *WorktreeSuite) TestNothingToCommit(c *C) {
expected := plumbing.NewHash("838ea833ce893e8555907e5ef224aa076f5e274a")

r, err := Init(memory.NewStorage(), memfs.New())
c.Assert(err, IsNil)

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

hash, err := w.Commit("failed empty commit\n", &CommitOptions{Author: defaultSignature()})
c.Assert(hash, Equals, plumbing.ZeroHash)
c.Assert(err, Equals, ErrEmptyCommit)

hash, err = w.Commit("enable empty commits\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true})
c.Assert(hash, Equals, expected)
c.Assert(err, IsNil)
}

func (s *WorktreeSuite) TestCommitParent(c *C) {
expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22")

Expand Down
36 changes: 33 additions & 3 deletions worktree_test.go
Expand Up @@ -3,7 +3,6 @@ package git
import (
"bytes"
"context"
"errors"
"io"
"io/ioutil"
"os"
Expand Down Expand Up @@ -2167,6 +2166,8 @@ func (s *WorktreeSuite) TestGrep(c *C) {
}

func (s *WorktreeSuite) TestAddAndCommit(c *C) {
expectedFiles := 2

dir, clean := s.TemporalDir()
defer clean()

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

os.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0o644)
os.WriteFile(filepath.Join(dir, "bar"), []byte("foo"), 0o644)

_, err = w.Add(".")
c.Assert(err, IsNil)

w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
_, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
Name: "foo",
Email: "foo@foo.foo",
When: time.Now(),
}})
c.Assert(err, IsNil)

iter, err := w.r.Log(&LogOptions{})
c.Assert(err, IsNil)

filesFound := 0
err = iter.ForEach(func(c *object.Commit) error {
files, err := c.Files()
if err != nil {
return err
}

err = files.ForEach(func(f *object.File) error {
return errors.New("Expected no files, got at least 1")
filesFound++
return nil
})
return err
})
c.Assert(err, IsNil)
c.Assert(filesFound, Equals, expectedFiles)
}

func (s *WorktreeSuite) TestAddAndCommitEmpty(c *C) {
dir, clean := s.TemporalDir()
defer clean()

repo, err := PlainInit(dir, false)
c.Assert(err, IsNil)

w, err := repo.Worktree()
c.Assert(err, IsNil)

_, err = w.Add(".")
c.Assert(err, IsNil)

_, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
Name: "foo",
Email: "foo@foo.foo",
When: time.Now(),
}})
c.Assert(err, Equals, ErrEmptyCommit)
}

func (s *WorktreeSuite) TestLinkedWorktree(c *C) {
Expand Down

0 comments on commit c7050e7

Please sign in to comment.