fork of go-git with some jj specific features

Merge pull request #623 from pjbgf/empty-commit

Return error instead of creating empty commits

authored by Máximo Cuadros and committed by GitHub c7050e7c 3e07c503

+4
options.go
··· 458 458 // All automatically stage files that have been modified and deleted, but 459 459 // new files you have not told Git about are not affected. 460 460 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 461 465 // Author is the author's signature of the commit. If Author is empty the 462 466 // Name and Email is read from the config, and time.Now it's used as When. 463 467 Author *object.Signature
+13 -2
worktree_commit.go
··· 2 2 3 3 import ( 4 4 "bytes" 5 + "errors" 5 6 "path" 6 7 "sort" 7 8 "strings" ··· 14 15 15 16 "github.com/ProtonMail/go-crypto/openpgp" 16 17 "github.com/go-git/go-billy/v5" 18 + ) 19 + 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") 17 24 ) 18 25 19 26 // Commit stores the current contents of the index in a new commit along with ··· 39 46 s: w.r.Storer, 40 47 } 41 48 42 - tree, err := h.BuildTree(idx) 49 + tree, err := h.BuildTree(idx, opts) 43 50 if err != nil { 44 51 return plumbing.ZeroHash, err 45 52 } ··· 145 152 146 153 // BuildTree builds the tree objects and push its to the storer, the hash 147 154 // 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 + 149 160 const rootNode = "" 150 161 h.trees = map[string]*object.Tree{rootNode: {}} 151 162 h.entries = map[string]*object.TreeEntry{}
+25 -1
worktree_commit_test.go
··· 26 26 ) 27 27 28 28 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) 30 31 c.Assert(err, IsNil) 31 32 32 33 w, err := r.Worktree() 34 + c.Assert(err, IsNil) 35 + 36 + util.WriteFile(fs, "foo", []byte("foo"), 0644) 37 + 38 + _, err = w.Add("foo") 33 39 c.Assert(err, IsNil) 34 40 35 41 hash, err := w.Commit("foo", &CommitOptions{}) ··· 63 69 c.Assert(err, IsNil) 64 70 65 71 assertStorageStatus(c, r, 1, 1, 1, expected) 72 + } 73 + 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) 66 90 } 67 91 68 92 func (s *WorktreeSuite) TestCommitParent(c *C) {
+33 -3
worktree_test.go
··· 3 3 import ( 4 4 "bytes" 5 5 "context" 6 - "errors" 7 6 "io" 8 7 "io/ioutil" 9 8 "os" ··· 2167 2166 } 2168 2167 2169 2168 func (s *WorktreeSuite) TestAddAndCommit(c *C) { 2169 + expectedFiles := 2 2170 + 2170 2171 dir, clean := s.TemporalDir() 2171 2172 defer clean() 2172 2173 ··· 2175 2176 2176 2177 w, err := repo.Worktree() 2177 2178 c.Assert(err, IsNil) 2179 + 2180 + os.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0o644) 2181 + os.WriteFile(filepath.Join(dir, "bar"), []byte("foo"), 0o644) 2178 2182 2179 2183 _, err = w.Add(".") 2180 2184 c.Assert(err, IsNil) 2181 2185 2182 - w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{ 2186 + _, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{ 2183 2187 Name: "foo", 2184 2188 Email: "foo@foo.foo", 2185 2189 When: time.Now(), 2186 2190 }}) 2191 + c.Assert(err, IsNil) 2187 2192 2188 2193 iter, err := w.r.Log(&LogOptions{}) 2189 2194 c.Assert(err, IsNil) 2195 + 2196 + filesFound := 0 2190 2197 err = iter.ForEach(func(c *object.Commit) error { 2191 2198 files, err := c.Files() 2192 2199 if err != nil { ··· 2194 2201 } 2195 2202 2196 2203 err = files.ForEach(func(f *object.File) error { 2197 - return errors.New("Expected no files, got at least 1") 2204 + filesFound++ 2205 + return nil 2198 2206 }) 2199 2207 return err 2200 2208 }) 2201 2209 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) 2202 2232 } 2203 2233 2204 2234 func (s *WorktreeSuite) TestLinkedWorktree(c *C) {