aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/git.yml5
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--options.go4
-rw-r--r--worktree_commit.go15
-rw-r--r--worktree_commit_test.go26
-rw-r--r--worktree_test.go36
7 files changed, 83 insertions, 9 deletions
diff --git a/.github/workflows/git.yml b/.github/workflows/git.yml
index fef2127..ba664a2 100644
--- a/.github/workflows/git.yml
+++ b/.github/workflows/git.yml
@@ -38,3 +38,8 @@ jobs:
- name: Test
run: make test-coverage
+
+ - name: Build go-git with CGO disabled
+ run: go build ./...
+ env:
+ CGO_ENABLED: 0
diff --git a/go.mod b/go.mod
index 3e271b1..2b8239e 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jessevdk/go-flags v1.5.0
github.com/kevinburke/ssh_config v1.2.0
- github.com/pjbgf/sha1cd v0.2.0
+ github.com/pjbgf/sha1cd v0.2.3
github.com/pkg/errors v0.9.1 // indirect
github.com/sergi/go-diff v1.1.0
github.com/skeema/knownhosts v1.1.0
diff --git a/go.sum b/go.sum
index e2915e5..e7e0c4d 100644
--- a/go.sum
+++ b/go.sum
@@ -45,8 +45,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/pjbgf/sha1cd v0.2.0 h1:gIsJVwjbRviE4gydidGztxH1IlJQoYBcCrwG4Dz8wvM=
-github.com/pjbgf/sha1cd v0.2.0/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
+github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
+github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
diff --git a/options.go b/options.go
index 7e5c1b4..747d512 100644
--- a/options.go
+++ b/options.go
@@ -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
diff --git a/worktree_commit.go b/worktree_commit.go
index dc79569..e927721 100644
--- a/worktree_commit.go
+++ b/worktree_commit.go
@@ -2,6 +2,7 @@ package git
import (
"bytes"
+ "errors"
"path"
"sort"
"strings"
@@ -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) {
@@ -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
}
@@ -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{}
diff --git a/worktree_commit_test.go b/worktree_commit_test.go
index 097c6e5..bfeb81d 100644
--- a/worktree_commit_test.go
+++ b/worktree_commit_test.go
@@ -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)
@@ -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")
diff --git a/worktree_test.go b/worktree_test.go
index 4a14126..4c06333 100644
--- a/worktree_test.go
+++ b/worktree_test.go
@@ -3,7 +3,6 @@ package git
import (
"bytes"
"context"
- "errors"
"io"
"io/ioutil"
"os"
@@ -2167,6 +2166,8 @@ func (s *WorktreeSuite) TestGrep(c *C) {
}
func (s *WorktreeSuite) TestAddAndCommit(c *C) {
+ expectedFiles := 2
+
dir, clean := s.TemporalDir()
defer clean()
@@ -2176,17 +2177,23 @@ 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 {
@@ -2194,11 +2201,34 @@ func (s *WorktreeSuite) TestAddAndCommit(c *C) {
}
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) {