1package git 2 3import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "strings" 9 "time" 10 11 "github.com/go-git/go-git/v5/plumbing" 12 "github.com/go-git/go-git/v5/plumbing/cache" 13 "github.com/go-git/go-git/v5/plumbing/object" 14 "github.com/go-git/go-git/v5/plumbing/storer" 15 "github.com/go-git/go-git/v5/storage/filesystem" 16 "github.com/go-git/go-git/v5/storage/memory" 17 18 "github.com/go-git/go-billy/v5/memfs" 19 "github.com/go-git/go-billy/v5/osfs" 20 "github.com/go-git/go-billy/v5/util" 21 "golang.org/x/crypto/openpgp" 22 "golang.org/x/crypto/openpgp/armor" 23 "golang.org/x/crypto/openpgp/errors" 24 . "gopkg.in/check.v1" 25) 26 27func (s *WorktreeSuite) TestCommitEmptyOptions(c *C) { 28 r, err := Init(memory.NewStorage(), memfs.New()) 29 c.Assert(err, IsNil) 30 31 w, err := r.Worktree() 32 c.Assert(err, IsNil) 33 34 hash, err := w.Commit("foo", &CommitOptions{}) 35 c.Assert(err, IsNil) 36 c.Assert(hash.IsZero(), Equals, false) 37 38 commit, err := r.CommitObject(hash) 39 c.Assert(err, IsNil) 40 c.Assert(commit.Author.Name, Not(Equals), "") 41} 42 43func (s *WorktreeSuite) TestCommitInitial(c *C) { 44 expected := plumbing.NewHash("98c4ac7c29c913f7461eae06e024dc18e80d23a4") 45 46 fs := memfs.New() 47 storage := memory.NewStorage() 48 49 r, err := Init(storage, fs) 50 c.Assert(err, IsNil) 51 52 w, err := r.Worktree() 53 c.Assert(err, IsNil) 54 55 util.WriteFile(fs, "foo", []byte("foo"), 0644) 56 57 _, err = w.Add("foo") 58 c.Assert(err, IsNil) 59 60 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 61 c.Assert(hash, Equals, expected) 62 c.Assert(err, IsNil) 63 64 assertStorageStatus(c, r, 1, 1, 1, expected) 65} 66 67func (s *WorktreeSuite) TestCommitParent(c *C) { 68 expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22") 69 70 fs := memfs.New() 71 w := &Worktree{ 72 r: s.Repository, 73 Filesystem: fs, 74 } 75 76 err := w.Checkout(&CheckoutOptions{}) 77 c.Assert(err, IsNil) 78 79 util.WriteFile(fs, "foo", []byte("foo"), 0644) 80 81 _, err = w.Add("foo") 82 c.Assert(err, IsNil) 83 84 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 85 c.Assert(hash, Equals, expected) 86 c.Assert(err, IsNil) 87 88 assertStorageStatus(c, s.Repository, 13, 11, 10, expected) 89} 90 91func (s *WorktreeSuite) TestCommitAll(c *C) { 92 expected := plumbing.NewHash("aede6f8c9c1c7ec9ca8d287c64b8ed151276fa28") 93 94 fs := memfs.New() 95 w := &Worktree{ 96 r: s.Repository, 97 Filesystem: fs, 98 } 99 100 err := w.Checkout(&CheckoutOptions{}) 101 c.Assert(err, IsNil) 102 103 util.WriteFile(fs, "LICENSE", []byte("foo"), 0644) 104 util.WriteFile(fs, "foo", []byte("foo"), 0644) 105 106 hash, err := w.Commit("foo\n", &CommitOptions{ 107 All: true, 108 Author: defaultSignature(), 109 }) 110 111 c.Assert(hash, Equals, expected) 112 c.Assert(err, IsNil) 113 114 assertStorageStatus(c, s.Repository, 13, 11, 10, expected) 115} 116 117func (s *WorktreeSuite) TestRemoveAndCommitAll(c *C) { 118 expected := plumbing.NewHash("907cd576c6ced2ecd3dab34a72bf9cf65944b9a9") 119 120 fs := memfs.New() 121 w := &Worktree{ 122 r: s.Repository, 123 Filesystem: fs, 124 } 125 126 err := w.Checkout(&CheckoutOptions{}) 127 c.Assert(err, IsNil) 128 129 util.WriteFile(fs, "foo", []byte("foo"), 0644) 130 _, err = w.Add("foo") 131 c.Assert(err, IsNil) 132 133 _, errFirst := w.Commit("Add in Repo\n", &CommitOptions{ 134 Author: defaultSignature(), 135 }) 136 c.Assert(errFirst, IsNil) 137 138 errRemove := fs.Remove("foo") 139 c.Assert(errRemove, IsNil) 140 141 hash, errSecond := w.Commit("Remove foo\n", &CommitOptions{ 142 All: true, 143 Author: defaultSignature(), 144 }) 145 c.Assert(errSecond, IsNil) 146 147 c.Assert(hash, Equals, expected) 148 c.Assert(err, IsNil) 149 150 assertStorageStatus(c, s.Repository, 13, 11, 11, expected) 151} 152 153func (s *WorktreeSuite) TestCommitSign(c *C) { 154 fs := memfs.New() 155 storage := memory.NewStorage() 156 157 r, err := Init(storage, fs) 158 c.Assert(err, IsNil) 159 160 w, err := r.Worktree() 161 c.Assert(err, IsNil) 162 163 util.WriteFile(fs, "foo", []byte("foo"), 0644) 164 165 _, err = w.Add("foo") 166 c.Assert(err, IsNil) 167 168 key := commitSignKey(c, true) 169 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key}) 170 c.Assert(err, IsNil) 171 172 // Verify the commit. 173 pks := new(bytes.Buffer) 174 pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil) 175 c.Assert(err, IsNil) 176 177 err = key.Serialize(pkw) 178 c.Assert(err, IsNil) 179 err = pkw.Close() 180 c.Assert(err, IsNil) 181 182 expectedCommit, err := r.CommitObject(hash) 183 c.Assert(err, IsNil) 184 actual, err := expectedCommit.Verify(pks.String()) 185 c.Assert(err, IsNil) 186 c.Assert(actual.PrimaryKey, DeepEquals, key.PrimaryKey) 187} 188 189func (s *WorktreeSuite) TestCommitSignBadKey(c *C) { 190 fs := memfs.New() 191 storage := memory.NewStorage() 192 193 r, err := Init(storage, fs) 194 c.Assert(err, IsNil) 195 196 w, err := r.Worktree() 197 c.Assert(err, IsNil) 198 199 util.WriteFile(fs, "foo", []byte("foo"), 0644) 200 201 _, err = w.Add("foo") 202 c.Assert(err, IsNil) 203 204 key := commitSignKey(c, false) 205 _, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key}) 206 c.Assert(err, Equals, errors.InvalidArgumentError("signing key is encrypted")) 207} 208 209func (s *WorktreeSuite) TestCommitTreeSort(c *C) { 210 path, err := ioutil.TempDir(os.TempDir(), "test-commit-tree-sort") 211 c.Assert(err, IsNil) 212 fs := osfs.New(path) 213 st := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 214 r, err := Init(st, nil) 215 c.Assert(err, IsNil) 216 217 r, _ = Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 218 URL: path, 219 }) 220 221 w, err := r.Worktree() 222 c.Assert(err, IsNil) 223 224 mfs := w.Filesystem 225 226 err = mfs.MkdirAll("delta", 0755) 227 c.Assert(err, IsNil) 228 229 for _, p := range []string{"delta_last", "Gamma", "delta/middle", "Beta", "delta-first", "alpha"} { 230 util.WriteFile(mfs, p, []byte("foo"), 0644) 231 _, err = w.Add(p) 232 c.Assert(err, IsNil) 233 } 234 235 _, err = w.Commit("foo\n", &CommitOptions{ 236 All: true, 237 Author: defaultSignature(), 238 }) 239 c.Assert(err, IsNil) 240 241 err = r.Push(&PushOptions{}) 242 c.Assert(err, IsNil) 243 244 cmd := exec.Command("git", "fsck") 245 cmd.Dir = path 246 cmd.Env = os.Environ() 247 buf := &bytes.Buffer{} 248 cmd.Stderr = buf 249 cmd.Stdout = buf 250 251 err = cmd.Run() 252 253 c.Assert(err, IsNil, Commentf("%s", buf.Bytes())) 254} 255 256func assertStorageStatus( 257 c *C, r *Repository, 258 treesCount, blobCount, commitCount int, head plumbing.Hash, 259) { 260 trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject) 261 c.Assert(err, IsNil) 262 blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject) 263 c.Assert(err, IsNil) 264 commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject) 265 c.Assert(err, IsNil) 266 267 c.Assert(lenIterEncodedObjects(trees), Equals, treesCount) 268 c.Assert(lenIterEncodedObjects(blobs), Equals, blobCount) 269 c.Assert(lenIterEncodedObjects(commits), Equals, commitCount) 270 271 ref, err := r.Head() 272 c.Assert(err, IsNil) 273 c.Assert(ref.Hash(), Equals, head) 274} 275 276func lenIterEncodedObjects(iter storer.EncodedObjectIter) int { 277 count := 0 278 iter.ForEach(func(plumbing.EncodedObject) error { 279 count++ 280 return nil 281 }) 282 283 return count 284} 285 286func defaultSignature() *object.Signature { 287 when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200") 288 return &object.Signature{ 289 Name: "foo", 290 Email: "foo@foo.foo", 291 When: when, 292 } 293} 294 295func commitSignKey(c *C, decrypt bool) *openpgp.Entity { 296 s := strings.NewReader(armoredKeyRing) 297 es, err := openpgp.ReadArmoredKeyRing(s) 298 c.Assert(err, IsNil) 299 300 c.Assert(es, HasLen, 1) 301 c.Assert(es[0].Identities, HasLen, 1) 302 _, ok := es[0].Identities["foo bar <foo@foo.foo>"] 303 c.Assert(ok, Equals, true) 304 305 key := es[0] 306 if decrypt { 307 err = key.PrivateKey.Decrypt([]byte(keyPassphrase)) 308 c.Assert(err, IsNil) 309 } 310 311 return key 312} 313 314const armoredKeyRing = ` 315-----BEGIN PGP PRIVATE KEY BLOCK----- 316 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 369=gIM9 370-----END PGP PRIVATE KEY BLOCK----- 371` 372 373const keyPassphrase = "abcdef0123456789" 374