fork of go-git with some jj specific features
at v5.5.2 14 kB view raw
1package git 2 3import ( 4 "bytes" 5 "log" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 "strings" 11 "time" 12 13 "github.com/go-git/go-git/v5/plumbing" 14 "github.com/go-git/go-git/v5/plumbing/cache" 15 "github.com/go-git/go-git/v5/plumbing/object" 16 "github.com/go-git/go-git/v5/plumbing/storer" 17 "github.com/go-git/go-git/v5/storage/filesystem" 18 "github.com/go-git/go-git/v5/storage/memory" 19 20 "github.com/ProtonMail/go-crypto/openpgp" 21 "github.com/ProtonMail/go-crypto/openpgp/armor" 22 "github.com/ProtonMail/go-crypto/openpgp/errors" 23 "github.com/go-git/go-billy/v5/memfs" 24 "github.com/go-git/go-billy/v5/util" 25 . "gopkg.in/check.v1" 26) 27 28func (s *WorktreeSuite) TestCommitEmptyOptions(c *C) { 29 fs := memfs.New() 30 r, err := Init(memory.NewStorage(), fs) 31 c.Assert(err, IsNil) 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") 39 c.Assert(err, IsNil) 40 41 hash, err := w.Commit("foo", &CommitOptions{}) 42 c.Assert(err, IsNil) 43 c.Assert(hash.IsZero(), Equals, false) 44 45 commit, err := r.CommitObject(hash) 46 c.Assert(err, IsNil) 47 c.Assert(commit.Author.Name, Not(Equals), "") 48} 49 50func (s *WorktreeSuite) TestCommitInitial(c *C) { 51 expected := plumbing.NewHash("98c4ac7c29c913f7461eae06e024dc18e80d23a4") 52 53 fs := memfs.New() 54 storage := memory.NewStorage() 55 56 r, err := Init(storage, fs) 57 c.Assert(err, IsNil) 58 59 w, err := r.Worktree() 60 c.Assert(err, IsNil) 61 62 util.WriteFile(fs, "foo", []byte("foo"), 0644) 63 64 _, err = w.Add("foo") 65 c.Assert(err, IsNil) 66 67 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 68 c.Assert(hash, Equals, expected) 69 c.Assert(err, IsNil) 70 71 assertStorageStatus(c, r, 1, 1, 1, expected) 72} 73 74func (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 92func (s *WorktreeSuite) TestCommitParent(c *C) { 93 expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22") 94 95 fs := memfs.New() 96 w := &Worktree{ 97 r: s.Repository, 98 Filesystem: fs, 99 } 100 101 err := w.Checkout(&CheckoutOptions{}) 102 c.Assert(err, IsNil) 103 104 util.WriteFile(fs, "foo", []byte("foo"), 0644) 105 106 _, err = w.Add("foo") 107 c.Assert(err, IsNil) 108 109 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 110 c.Assert(hash, Equals, expected) 111 c.Assert(err, IsNil) 112 113 assertStorageStatus(c, s.Repository, 13, 11, 10, expected) 114} 115 116func (s *WorktreeSuite) TestCommitAll(c *C) { 117 expected := plumbing.NewHash("aede6f8c9c1c7ec9ca8d287c64b8ed151276fa28") 118 119 fs := memfs.New() 120 w := &Worktree{ 121 r: s.Repository, 122 Filesystem: fs, 123 } 124 125 err := w.Checkout(&CheckoutOptions{}) 126 c.Assert(err, IsNil) 127 128 util.WriteFile(fs, "LICENSE", []byte("foo"), 0644) 129 util.WriteFile(fs, "foo", []byte("foo"), 0644) 130 131 hash, err := w.Commit("foo\n", &CommitOptions{ 132 All: true, 133 Author: defaultSignature(), 134 }) 135 136 c.Assert(hash, Equals, expected) 137 c.Assert(err, IsNil) 138 139 assertStorageStatus(c, s.Repository, 13, 11, 10, expected) 140} 141 142func (s *WorktreeSuite) TestRemoveAndCommitAll(c *C) { 143 expected := plumbing.NewHash("907cd576c6ced2ecd3dab34a72bf9cf65944b9a9") 144 145 fs := memfs.New() 146 w := &Worktree{ 147 r: s.Repository, 148 Filesystem: fs, 149 } 150 151 err := w.Checkout(&CheckoutOptions{}) 152 c.Assert(err, IsNil) 153 154 util.WriteFile(fs, "foo", []byte("foo"), 0644) 155 _, err = w.Add("foo") 156 c.Assert(err, IsNil) 157 158 _, errFirst := w.Commit("Add in Repo\n", &CommitOptions{ 159 Author: defaultSignature(), 160 }) 161 c.Assert(errFirst, IsNil) 162 163 errRemove := fs.Remove("foo") 164 c.Assert(errRemove, IsNil) 165 166 hash, errSecond := w.Commit("Remove foo\n", &CommitOptions{ 167 All: true, 168 Author: defaultSignature(), 169 }) 170 c.Assert(errSecond, IsNil) 171 172 c.Assert(hash, Equals, expected) 173 c.Assert(err, IsNil) 174 175 assertStorageStatus(c, s.Repository, 13, 11, 11, expected) 176} 177 178func (s *WorktreeSuite) TestCommitSign(c *C) { 179 fs := memfs.New() 180 storage := memory.NewStorage() 181 182 r, err := Init(storage, fs) 183 c.Assert(err, IsNil) 184 185 w, err := r.Worktree() 186 c.Assert(err, IsNil) 187 188 util.WriteFile(fs, "foo", []byte("foo"), 0644) 189 190 _, err = w.Add("foo") 191 c.Assert(err, IsNil) 192 193 key := commitSignKey(c, true) 194 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key}) 195 c.Assert(err, IsNil) 196 197 // Verify the commit. 198 pks := new(bytes.Buffer) 199 pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil) 200 c.Assert(err, IsNil) 201 202 err = key.Serialize(pkw) 203 c.Assert(err, IsNil) 204 err = pkw.Close() 205 c.Assert(err, IsNil) 206 207 expectedCommit, err := r.CommitObject(hash) 208 c.Assert(err, IsNil) 209 actual, err := expectedCommit.Verify(pks.String()) 210 c.Assert(err, IsNil) 211 c.Assert(actual.PrimaryKey, DeepEquals, key.PrimaryKey) 212} 213 214func (s *WorktreeSuite) TestCommitSignBadKey(c *C) { 215 fs := memfs.New() 216 storage := memory.NewStorage() 217 218 r, err := Init(storage, fs) 219 c.Assert(err, IsNil) 220 221 w, err := r.Worktree() 222 c.Assert(err, IsNil) 223 224 util.WriteFile(fs, "foo", []byte("foo"), 0644) 225 226 _, err = w.Add("foo") 227 c.Assert(err, IsNil) 228 229 key := commitSignKey(c, false) 230 _, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key}) 231 c.Assert(err, Equals, errors.InvalidArgumentError("signing key is encrypted")) 232} 233 234func (s *WorktreeSuite) TestCommitTreeSort(c *C) { 235 fs, clean := s.TemporalFilesystem() 236 defer clean() 237 238 st := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 239 _, err := Init(st, nil) 240 c.Assert(err, IsNil) 241 242 r, _ := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 243 URL: fs.Root(), 244 }) 245 246 w, err := r.Worktree() 247 c.Assert(err, IsNil) 248 249 mfs := w.Filesystem 250 251 err = mfs.MkdirAll("delta", 0755) 252 c.Assert(err, IsNil) 253 254 for _, p := range []string{"delta_last", "Gamma", "delta/middle", "Beta", "delta-first", "alpha"} { 255 util.WriteFile(mfs, p, []byte("foo"), 0644) 256 _, err = w.Add(p) 257 c.Assert(err, IsNil) 258 } 259 260 _, err = w.Commit("foo\n", &CommitOptions{ 261 All: true, 262 Author: defaultSignature(), 263 }) 264 c.Assert(err, IsNil) 265 266 err = r.Push(&PushOptions{}) 267 c.Assert(err, IsNil) 268 269 cmd := exec.Command("git", "fsck") 270 cmd.Dir = fs.Root() 271 cmd.Env = os.Environ() 272 buf := &bytes.Buffer{} 273 cmd.Stderr = buf 274 cmd.Stdout = buf 275 276 err = cmd.Run() 277 278 c.Assert(err, IsNil, Commentf("%s", buf.Bytes())) 279} 280 281// https://github.com/go-git/go-git/pull/224 282func (s *WorktreeSuite) TestJustStoreObjectsNotAlreadyStored(c *C) { 283 fs, clean := s.TemporalFilesystem() 284 defer clean() 285 286 fsDotgit, err := fs.Chroot(".git") // real fs to get modified timestamps 287 c.Assert(err, IsNil) 288 storage := filesystem.NewStorage(fsDotgit, cache.NewObjectLRUDefault()) 289 290 r, err := Init(storage, fs) 291 c.Assert(err, IsNil) 292 293 w, err := r.Worktree() 294 c.Assert(err, IsNil) 295 296 // Step 1: Write LICENSE 297 util.WriteFile(fs, "LICENSE", []byte("license"), 0644) 298 hLicense, err := w.Add("LICENSE") 299 c.Assert(err, IsNil) 300 c.Assert(hLicense, Equals, plumbing.NewHash("0484eba0d41636ba71fa612c78559cd6c3006cde")) 301 302 hash, err := w.Commit("commit 1\n", &CommitOptions{ 303 All: true, 304 Author: defaultSignature(), 305 }) 306 c.Assert(err, IsNil) 307 c.Assert(hash, Equals, plumbing.NewHash("7a7faee4630d2664a6869677cc8ab614f3fd4a18")) 308 309 infoLicense, err := fsDotgit.Stat(filepath.Join("objects", "04", "84eba0d41636ba71fa612c78559cd6c3006cde")) 310 c.Assert(err, IsNil) // checking objects file exists 311 312 // Step 2: Write foo. 313 time.Sleep(5 * time.Millisecond) // uncool, but we need to get different timestamps... 314 util.WriteFile(fs, "foo", []byte("foo"), 0644) 315 hFoo, err := w.Add("foo") 316 c.Assert(err, IsNil) 317 c.Assert(hFoo, Equals, plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c")) 318 319 hash, err = w.Commit("commit 2\n", &CommitOptions{ 320 All: true, 321 Author: defaultSignature(), 322 }) 323 c.Assert(err, IsNil) 324 c.Assert(hash, Equals, plumbing.NewHash("97c0c5177e6ac57d10e8ea0017f2d39b91e2b364")) 325 326 // Step 3: Check 327 // There is no need to overwrite the object of LICENSE, because its content 328 // was not changed. Just a write on the object of foo is required. This behaviour 329 // is fixed by #224 and tested by comparing the timestamps of the stored objects. 330 infoFoo, err := fsDotgit.Stat(filepath.Join("objects", "19", "102815663d23f8b75a47e7a01965dcdc96468c")) 331 c.Assert(err, IsNil) // checking objects file exists 332 c.Assert(infoLicense.ModTime().Before(infoFoo.ModTime()), Equals, true) // object of foo has another/greaterThan timestamp than LICENSE 333 334 infoLicenseSecond, err := fsDotgit.Stat(filepath.Join("objects", "04", "84eba0d41636ba71fa612c78559cd6c3006cde")) 335 c.Assert(err, IsNil) 336 337 log.Printf("comparing mod time: %v == %v on %v (%v)", infoLicenseSecond.ModTime(), infoLicense.ModTime(), runtime.GOOS, runtime.GOARCH) 338 c.Assert(infoLicenseSecond.ModTime(), Equals, infoLicense.ModTime()) // object of LICENSE should have the same timestamp because no additional write operation was performed 339} 340 341func assertStorageStatus( 342 c *C, r *Repository, 343 treesCount, blobCount, commitCount int, head plumbing.Hash, 344) { 345 trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject) 346 c.Assert(err, IsNil) 347 blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject) 348 c.Assert(err, IsNil) 349 commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject) 350 c.Assert(err, IsNil) 351 352 c.Assert(lenIterEncodedObjects(trees), Equals, treesCount) 353 c.Assert(lenIterEncodedObjects(blobs), Equals, blobCount) 354 c.Assert(lenIterEncodedObjects(commits), Equals, commitCount) 355 356 ref, err := r.Head() 357 c.Assert(err, IsNil) 358 c.Assert(ref.Hash(), Equals, head) 359} 360 361func lenIterEncodedObjects(iter storer.EncodedObjectIter) int { 362 count := 0 363 iter.ForEach(func(plumbing.EncodedObject) error { 364 count++ 365 return nil 366 }) 367 368 return count 369} 370 371func defaultSignature() *object.Signature { 372 when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200") 373 return &object.Signature{ 374 Name: "foo", 375 Email: "foo@foo.foo", 376 When: when, 377 } 378} 379 380func commitSignKey(c *C, decrypt bool) *openpgp.Entity { 381 s := strings.NewReader(armoredKeyRing) 382 es, err := openpgp.ReadArmoredKeyRing(s) 383 c.Assert(err, IsNil) 384 385 c.Assert(es, HasLen, 1) 386 c.Assert(es[0].Identities, HasLen, 1) 387 _, ok := es[0].Identities["foo bar <foo@foo.foo>"] 388 c.Assert(ok, Equals, true) 389 390 key := es[0] 391 if decrypt { 392 err = key.PrivateKey.Decrypt([]byte(keyPassphrase)) 393 c.Assert(err, IsNil) 394 } 395 396 return key 397} 398 399const armoredKeyRing = ` 400-----BEGIN PGP PRIVATE KEY BLOCK----- 401 402lQdGBFt89QIBEAC8du0Purt9yeFuLlBYHcexnZvcbaci2pY+Ejn1VnxM7caFxRX/ 403b2weZi9E6+I0F+K/hKIaidPdcbK92UCL0Vp6F3izjqategZ7o44vlK/HfWFME4wv 404sou6lnig9ovA73HRyzngi3CmqWxSdg8lL0kIJLNzlvCFEd4Z34BnEkagklQJRymo 4050WnmLJjSnZFT5Nk7q5jrcR7ApbD98cakvgivDlUBPJCk2JFPWheCkouWPHMvLXQz 406bZXW5RFz4lJsMUWa/S3ofvIOnjG5Etnil3IA4uksS8fSDkGus998mBvUwzqX7xBh 407dK17ZEbxDdO4PuVJDkjvq618rMu8FVk5yVd59rUketSnGrehd/+vdh6qtgQC4tu1 408RldbUVAuKZGg79H61nWnvrDZmbw4eoqCEuv1+aZsM9ElSC5Ps2J0rtpHRyBndKn+ 4098Jlc/KTH04/O+FAhEv0IgMTFEm3iAq8udBhRBgu6Y4gJyn4tqy6+6ZjPUNos8GOG 410+ZJPdrgHHHfQged1ygeceN6W2AwQRet/B3/rieHf2V93uHJy/DjYUEuBhPm9nxqi 411R6ILUr97Sj2EsvLyfQO9pFpIctoNKEJmDx/C9tkFMNNlQhpsBitSdR2/wancw9ND 412iWV/J9roUdC0qns7eNSbiFe3Len8Xir7srnjAFgbGvOu9jDBUuiKGT5F3wARAQAB 413/gcDAl+0SktmjrUW8uwpvru6GeIeo5kc4rXuD7iIxH6nDl3nmjZMX7qWvp+pRTHH 4140hEDH44899PDvzclBN3ouehfFUbJ+DBy8umBiLqF8Mu2PrKjdmyv3BvnbTkqPM3m 4152Su7WmUDBhG00X07lfl8fTpZJG80onEGzGynryP/xVm4ymzoHyYGksntXLYr2HJ5 416aV6L7sL2/STsaaOVHoa/oEmVBo1+NRsTxRRUcFVLs3g0OIi6ZCeSevBdavMwf9Iv 417b5Bs/e0+GLpP71XzFpdrGcL6oGjZH/dgdeypzbGA+FHtQJqynN3qEE9eCc9cfTGL 4182zN2OtnMA28NtPVN4SnSxQIDvycWx68NZjfwLOK+gswfKpimp+6xMWSnNIRDyU9M 419w0hdNPMK9JAxm/MlnkR7x6ysX/8vrVVFl9gWOmxzJ5L4kvfMsHcV5ZFRP8OnVA6a 420NFBWIBGXF1uQC4qrXup/xKyWJOoH++cMo2cjPT3+3oifZgdBydVfHXjS9aQ/S3Sa 421A6henWyx/qeBGPVRuXWdXIOKDboOPK8JwQaGd6yazKkH9c5tDohmQHzZ6ho0gyAt 422dh+g9ZyiZVpjc6excfK/DP/RdUOYKw3Ur9652hKephvYZzHvPjTbqVkhS7JjZkVY 423rukQ64d5T0pE1B4y+If4hLFXMNQtfo0TIsATNA69jop+KFnJpLzAB+Ee33EA/HUl 424YC5EJCJaXt6kdtYFac0HvVWiz5ZuMhdtzpJfvOe+Olp/xR9nIPW3XZojQoHIZKwu 425gXeZeVMvfeoq+ymKAKNH5Np4WaUDF7Wh9VLl045jGyF5viyy61ivC0eyAzp5W1uy 426gJBZwafVma5MhmZUS2dFs0hBwBrKRzZZhN65VvfSYw6CnXp83ryUjReDvrLmqZDM 427FNpSMDKRk1+k9Wwi3m+fzLAvlxoHscJ5Any7ApsvBRbyehP8MAAG7UV3jImugTLi 428yN6FKVwziQXiC4/97oKbA1YYNjTT7Qw9gWTXvLRspn4f9997brcA9dm0M0seTjLa 429lc5hTJwJQdvPPI2klf+YgPvsD6nrP1moeWBb8irICqG1/BoE0JHPS+bqJ1J+m1iV 430kRV/+4pV2bLlXKqg1LEvqANW+1P1eM2nbbVB7EQn8ZOPIKMoCLoC1QWUPNfnemsW 431U5ynAbhsbm16PDJql0ApEgUCEDfsXTu1ui6SIO3bs/gWyD9HEmnfaYMYDKF+j+0r 432jXd4GnCxb+Yu3wV5WyewOHouzC+++h/3WcDLkOYZ9pcIbA86qT+v6b9MuTAU0D3c 433wlDv8r5J59zOcXl4HpMb2BY5F9dZn8hjgeVJRhJdij9x1TQ8qlVasSi4Eq8SiPmZ 434PZz33Pk6yn2caQ6wd47A79LXCbFQqJqA5aA6oS4DOpENGS5fh7WUZq/MTcmm9GsG 435w2gHxocASK9RCUYgZFWVYgLDuviMMWvc/2TJcTMxdF0Amu3erYAD90smFs0g/6fZ 4364pRLnKFuifwAMGMOx7jbW5tmOaSPx6XkuYvkDJeLMHoN3z/8bZEG5VpayypwFGyV 437bk/YIUWg/KM/43juDPdTvab9tZzYIjxC6on7dtYIAGjZis97XZou3KYKTaMe1VY6 438IhrnVzJ0JAHpd1prf9NUz96e1vjGdn3I61JgjNp5sWklIJEZzvaD28Eovf/LH1BO 439gYFFCvsWXaRoPHNQ5a9m7CROkLeHUFgRu5uriqHxxQHgogDznc8/3fnvDAHNpNb6 440Jnk4zaeVR3tTyIjiNM+wxUFPDNFpJWmQbSDCcPVYTbpznzVRnhqrw7q0FWZvbyBi 441YXIgPGZvb0Bmb28uZm9vPokCVAQTAQgAPgIbAwULCQgHAgYVCAkKCwIEFgIDAQIe 442AQIXgBYhBJOhf/AeVDKFRgh8jgKTlUAu/M1TBQJbfPU4BQkSzAM2AAoJEAKTlUAu 443/M1TVTIQALA6ocNc2fXz1loLykMxlfnX/XxiyNDOUPDZkrZtscqqWPYaWvJK3OiD 44432bdVEbftnAiFvJYkinrCXLEmwwf5wyOxKFmCHwwKhH0UYt60yF4WwlOVNstGSAy 445RkPMEEmVfMXS9K1nzKv/9A5YsqMQob7sN5CMN66Vrm0RKSvOF/NhhM9v8fC0QSU2 446GZNO0tnRfaS4wMnFr5L4FuDST+14F5sJT7ZEJz7HfbxXKLvvWbvqLlCYHJOdz56s 447X/eKde8eT9/LSzcmgsd7rGS2np5901kubww5jllUl1CFnk3Mdg9FTJl5u9Epuhnn 448823Jpdy1ZNbyLqZ266Z/q2HepDA7P/GqIXgWdHjwG2y1YAC4JIkA4RBbesQwqAXs 4496cX5gqRFRl5iDGEP5zclS0y5mWi/J8bLYxMYfqxs9EZtHd9DumWISi87804TEzYa 450WDijMlW7PR8QRW0vdmtYOhJZOlTnomLQx2v27iqpVXRh12J1aYVBFC+IvG1vhCf9 451FL3LzAHHEGlIoDaKJMd+Wg/Lm/f1PqqQx3lWIh9hhKh5Qx6hcuJH669JOWuEdxfo 4521so50aItG+tdDKqXflmOi7grrUURchYYKteaW2fC2SQgzDClprALI7aj9s/lDrEN 453CgLH6twOqdSFWqB/4ASDMsNeLeKX3WOYKYYMlE01cj3T1m6dpRUO 454=gIM9 455-----END PGP PRIVATE KEY BLOCK----- 456` 457 458const keyPassphrase = "abcdef0123456789"