fork of go-git with some jj specific features
at heads/v5 23 kB view raw
1package git 2 3import ( 4 "bytes" 5 "fmt" 6 "log" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "testing" 13 "time" 14 15 fixtures "github.com/go-git/go-git-fixtures/v4" 16 "github.com/go-git/go-git/v5/plumbing" 17 "github.com/go-git/go-git/v5/plumbing/cache" 18 "github.com/go-git/go-git/v5/plumbing/object" 19 "github.com/go-git/go-git/v5/plumbing/storer" 20 "github.com/go-git/go-git/v5/storage/filesystem" 21 "github.com/go-git/go-git/v5/storage/memory" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 25 "github.com/ProtonMail/go-crypto/openpgp" 26 "github.com/ProtonMail/go-crypto/openpgp/armor" 27 "github.com/ProtonMail/go-crypto/openpgp/errors" 28 "github.com/go-git/go-billy/v5/memfs" 29 "github.com/go-git/go-billy/v5/util" 30) 31 32func (s *WorktreeSuite) TestCommitEmptyOptions() { 33 fs := memfs.New() 34 r, err := Init(memory.NewStorage(), fs) 35 s.NoError(err) 36 37 w, err := r.Worktree() 38 s.NoError(err) 39 40 util.WriteFile(fs, "foo", []byte("foo"), 0644) 41 42 _, err = w.Add("foo") 43 s.NoError(err) 44 45 hash, err := w.Commit("foo", &CommitOptions{}) 46 s.NoError(err) 47 s.False(hash.IsZero()) 48 49 commit, err := r.CommitObject(hash) 50 s.NoError(err) 51 s.NotEqual("", commit.Author.Name) 52} 53 54func (s *WorktreeSuite) TestCommitInitial() { 55 expected := plumbing.NewHash("98c4ac7c29c913f7461eae06e024dc18e80d23a4") 56 57 fs := memfs.New() 58 storage := memory.NewStorage() 59 60 r, err := Init(storage, fs) 61 s.NoError(err) 62 63 w, err := r.Worktree() 64 s.NoError(err) 65 66 util.WriteFile(fs, "foo", []byte("foo"), 0644) 67 68 _, err = w.Add("foo") 69 s.NoError(err) 70 71 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 72 s.Equal(expected, hash) 73 s.NoError(err) 74 75 assertStorageStatus(s, r, 1, 1, 1, expected) 76} 77 78func (s *WorktreeSuite) TestNothingToCommit() { 79 expected := plumbing.NewHash("838ea833ce893e8555907e5ef224aa076f5e274a") 80 81 r, err := Init(memory.NewStorage(), memfs.New()) 82 s.NoError(err) 83 84 w, err := r.Worktree() 85 s.NoError(err) 86 87 hash, err := w.Commit("failed empty commit\n", &CommitOptions{Author: defaultSignature()}) 88 s.Equal(plumbing.ZeroHash, hash) 89 s.ErrorIs(err, ErrEmptyCommit) 90 91 hash, err = w.Commit("enable empty commits\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true}) 92 s.Equal(expected, hash) 93 s.NoError(err) 94} 95 96func (s *WorktreeSuite) TestNothingToCommitNonEmptyRepo() { 97 fs := memfs.New() 98 r, err := Init(memory.NewStorage(), fs) 99 s.NoError(err) 100 101 w, err := r.Worktree() 102 s.NoError(err) 103 104 err = util.WriteFile(fs, "foo", []byte("foo"), 0644) 105 s.NoError(err) 106 107 w.Add("foo") 108 _, err = w.Commit("previous commit\n", &CommitOptions{Author: defaultSignature()}) 109 s.NoError(err) 110 111 hash, err := w.Commit("failed empty commit\n", &CommitOptions{Author: defaultSignature()}) 112 s.Equal(plumbing.ZeroHash, hash) 113 s.ErrorIs(err, ErrEmptyCommit) 114 115 _, err = w.Commit("enable empty commits\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true}) 116 s.NoError(err) 117} 118 119func (s *WorktreeSuite) TestRemoveAndCommitToMakeEmptyRepo() { 120 fs := memfs.New() 121 r, err := Init(memory.NewStorage(), fs) 122 s.NoError(err) 123 124 w, err := r.Worktree() 125 s.NoError(err) 126 127 err = util.WriteFile(fs, "foo", []byte("foo"), 0644) 128 s.NoError(err) 129 130 _, err = w.Add("foo") 131 s.NoError(err) 132 133 _, err = w.Commit("Add in Repo\n", &CommitOptions{Author: defaultSignature()}) 134 s.NoError(err) 135 136 err = fs.Remove("foo") 137 s.NoError(err) 138 139 _, err = w.Add("foo") 140 s.NoError(err) 141 142 _, err = w.Commit("Remove foo\n", &CommitOptions{Author: defaultSignature()}) 143 s.NoError(err) 144} 145 146func (s *WorktreeSuite) TestCommitParent() { 147 expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22") 148 149 fs := memfs.New() 150 w := &Worktree{ 151 r: s.Repository, 152 Filesystem: fs, 153 } 154 155 err := w.Checkout(&CheckoutOptions{}) 156 s.NoError(err) 157 158 err = util.WriteFile(fs, "foo", []byte("foo"), 0644) 159 s.NoError(err) 160 161 _, err = w.Add("foo") 162 s.NoError(err) 163 164 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 165 s.Equal(expected, hash) 166 s.NoError(err) 167 168 assertStorageStatus(s, s.Repository, 13, 11, 10, expected) 169} 170 171func (s *WorktreeSuite) TestCommitAmendWithoutChanges() { 172 fs := memfs.New() 173 w := &Worktree{ 174 r: s.Repository, 175 Filesystem: fs, 176 } 177 178 err := w.Checkout(&CheckoutOptions{}) 179 s.NoError(err) 180 181 err = util.WriteFile(fs, "foo", []byte("foo"), 0644) 182 s.NoError(err) 183 184 _, err = w.Add("foo") 185 s.NoError(err) 186 187 prevHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 188 s.NoError(err) 189 190 amendedHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), Amend: true}) 191 s.NoError(err) 192 193 headRef, err := w.r.Head() 194 s.NoError(err) 195 196 s.Equal(headRef.Hash(), amendedHash) 197 s.Equal(prevHash, amendedHash) 198 199 commit, err := w.r.CommitObject(headRef.Hash()) 200 s.NoError(err) 201 s.Equal("foo\n", commit.Message) 202 203 assertStorageStatus(s, s.Repository, 13, 11, 10, amendedHash) 204} 205 206func (s *WorktreeSuite) TestCommitAmendWithChanges() { 207 fs := memfs.New() 208 w := &Worktree{ 209 r: s.Repository, 210 Filesystem: fs, 211 } 212 213 err := w.Checkout(&CheckoutOptions{}) 214 s.NoError(err) 215 216 util.WriteFile(fs, "foo", []byte("foo"), 0644) 217 218 _, err = w.Add("foo") 219 s.NoError(err) 220 221 _, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 222 s.NoError(err) 223 224 util.WriteFile(fs, "bar", []byte("bar"), 0644) 225 226 _, err = w.Add("bar") 227 s.NoError(err) 228 229 amendedHash, err := w.Commit("bar\n", &CommitOptions{Amend: true}) 230 s.NoError(err) 231 232 headRef, err := w.r.Head() 233 s.NoError(err) 234 235 s.Equal(headRef.Hash(), amendedHash) 236 237 commit, err := w.r.CommitObject(headRef.Hash()) 238 s.NoError(err) 239 s.Equal("bar\n", commit.Message) 240 s.Equal(1, commit.NumParents()) 241 242 stats, err := commit.Stats() 243 s.NoError(err) 244 s.Len(stats, 2) 245 s.Equal(object.FileStat{ 246 Name: "bar", 247 Addition: 1, 248 }, stats[0]) 249 s.Equal(object.FileStat{ 250 Name: "foo", 251 Addition: 1, 252 }, stats[1]) 253 254 assertStorageStatus(s, s.Repository, 14, 12, 11, amendedHash) 255} 256 257func (s *WorktreeSuite) TestCommitAmendNothingToCommit() { 258 fs := memfs.New() 259 w := &Worktree{ 260 r: s.Repository, 261 Filesystem: fs, 262 } 263 264 err := w.Checkout(&CheckoutOptions{}) 265 s.NoError(err) 266 267 err = util.WriteFile(fs, "foo", []byte("foo"), 0644) 268 s.NoError(err) 269 270 _, err = w.Add("foo") 271 s.NoError(err) 272 273 prevHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()}) 274 s.NoError(err) 275 276 _, err = w.Commit("bar\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true}) 277 s.NoError(err) 278 279 amendedHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), Amend: true}) 280 s.T().Log(prevHash, amendedHash) 281 s.ErrorIs(err, ErrEmptyCommit) 282 s.Equal(plumbing.ZeroHash, amendedHash) 283} 284 285func TestCount(t *testing.T) { 286 f := fixtures.Basic().One() 287 r := NewRepositoryWithEmptyWorktree(f) 288 289 iter, err := r.CommitObjects() 290 require.NoError(t, err) 291 292 count := 0 293 iter.ForEach(func(c *object.Commit) error { 294 count++ 295 return nil 296 }) 297 assert.Equal(t, 9, count, "commits mismatch") 298 299 trees, err := r.TreeObjects() 300 require.NoError(t, err) 301 302 count = 0 303 trees.ForEach(func(c *object.Tree) error { 304 count++ 305 return nil 306 }) 307 assert.Equal(t, 12, count, "trees mismatch") 308 309 blobs, err := r.BlobObjects() 310 require.NoError(t, err) 311 312 count = 0 313 blobs.ForEach(func(c *object.Blob) error { 314 count++ 315 return nil 316 }) 317 assert.Equal(t, 10, count, "blobs mismatch") 318 319 objects, err := r.Objects() 320 require.NoError(t, err) 321 322 count = 0 323 objects.ForEach(func(c object.Object) error { 324 count++ 325 return nil 326 }) 327 assert.Equal(t, 31, count, "objects mismatch") 328} 329 330func TestAddAndCommitWithSkipStatus(t *testing.T) { 331 expected := plumbing.NewHash("375a3808ffde7f129cdd3c8c252fd0fe37cfd13b") 332 333 f := fixtures.Basic().One() 334 fs := memfs.New() 335 r := NewRepositoryWithEmptyWorktree(f) 336 w := &Worktree{ 337 r: r, 338 Filesystem: fs, 339 } 340 341 err := w.Checkout(&CheckoutOptions{}) 342 require.NoError(t, err) 343 344 util.WriteFile(fs, "LICENSE", []byte("foo"), 0644) 345 util.WriteFile(fs, "foo", []byte("foo"), 0644) 346 347 err = w.AddWithOptions(&AddOptions{ 348 Path: "foo", 349 SkipStatus: true, 350 }) 351 require.NoError(t, err) 352 353 hash, err := w.Commit("commit foo only\n", &CommitOptions{ 354 Author: defaultSignature(), 355 }) 356 357 assert.Equal(t, expected.String(), hash.String()) 358 require.NoError(t, err) 359 360 assertStorage(t, r, 13, 11, 10, expected) 361} 362 363func assertStorage( 364 t *testing.T, r *Repository, 365 treesCount, blobCount, commitCount int, head plumbing.Hash, 366) { 367 trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject) 368 require.NoError(t, err) 369 blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject) 370 require.NoError(t, err) 371 commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject) 372 require.NoError(t, err) 373 374 assert.Equal(t, treesCount, lenIterEncodedObjects(trees), "trees count mismatch") 375 assert.Equal(t, blobCount, lenIterEncodedObjects(blobs), "blobs count mismatch") 376 assert.Equal(t, commitCount, lenIterEncodedObjects(commits), "commits count mismatch") 377 378 ref, err := r.Head() 379 require.NoError(t, err) 380 assert.Equal(t, head.String(), ref.Hash().String()) 381} 382 383func (s *WorktreeSuite) TestAddAndCommitWithSkipStatusPathNotModified() { 384 expected := plumbing.NewHash("375a3808ffde7f129cdd3c8c252fd0fe37cfd13b") 385 expected2 := plumbing.NewHash("8691273baf8f6ee2cccfc05e910552c04d02d472") 386 387 fs := memfs.New() 388 w := &Worktree{ 389 r: s.Repository, 390 Filesystem: fs, 391 } 392 393 err := w.Checkout(&CheckoutOptions{}) 394 s.NoError(err) 395 396 util.WriteFile(fs, "foo", []byte("foo"), 0644) 397 398 status, err := w.Status() 399 s.NoError(err) 400 foo := status.File("foo") 401 s.Equal(Untracked, foo.Staging) 402 s.Equal(Untracked, foo.Worktree) 403 404 err = w.AddWithOptions(&AddOptions{ 405 Path: "foo", 406 SkipStatus: true, 407 }) 408 s.NoError(err) 409 410 status, err = w.Status() 411 s.NoError(err) 412 foo = status.File("foo") 413 s.Equal(Added, foo.Staging) 414 s.Equal(Unmodified, foo.Worktree) 415 416 hash, err := w.Commit("commit foo only\n", &CommitOptions{All: true, 417 Author: defaultSignature(), 418 }) 419 s.Equal(expected, hash) 420 s.NoError(err) 421 422 commit1, err := w.r.CommitObject(hash) 423 s.NoError(err) 424 425 status, err = w.Status() 426 s.NoError(err) 427 foo = status.File("foo") 428 s.Equal(Untracked, foo.Staging) 429 s.Equal(Untracked, foo.Worktree) 430 431 assertStorageStatus(s, s.Repository, 13, 11, 10, expected) 432 433 err = w.AddWithOptions(&AddOptions{ 434 Path: "foo", 435 SkipStatus: true, 436 }) 437 s.NoError(err) 438 439 status, err = w.Status() 440 s.NoError(err) 441 foo = status.File("foo") 442 s.Equal(Untracked, foo.Staging) 443 s.Equal(Untracked, foo.Worktree) 444 445 hash, err = w.Commit("commit with no changes\n", &CommitOptions{ 446 Author: defaultSignature(), 447 AllowEmptyCommits: true, 448 }) 449 s.Equal(expected2, hash) 450 s.NoError(err) 451 452 commit2, err := w.r.CommitObject(hash) 453 s.NoError(err) 454 455 status, err = w.Status() 456 s.NoError(err) 457 foo = status.File("foo") 458 s.Equal(Untracked, foo.Staging) 459 s.Equal(Untracked, foo.Worktree) 460 461 patch, err := commit2.Patch(commit1) 462 s.NoError(err) 463 files := patch.FilePatches() 464 s.Nil(files) 465 466 assertStorageStatus(s, s.Repository, 13, 11, 11, expected2) 467} 468 469func (s *WorktreeSuite) TestCommitAll() { 470 expected := plumbing.NewHash("aede6f8c9c1c7ec9ca8d287c64b8ed151276fa28") 471 472 fs := memfs.New() 473 w := &Worktree{ 474 r: s.Repository, 475 Filesystem: fs, 476 } 477 478 err := w.Checkout(&CheckoutOptions{}) 479 s.NoError(err) 480 481 util.WriteFile(fs, "LICENSE", []byte("foo"), 0644) 482 util.WriteFile(fs, "foo", []byte("foo"), 0644) 483 484 hash, err := w.Commit("foo\n", &CommitOptions{ 485 All: true, 486 Author: defaultSignature(), 487 }) 488 489 s.Equal(expected, hash) 490 s.NoError(err) 491 492 assertStorageStatus(s, s.Repository, 13, 11, 10, expected) 493} 494 495func (s *WorktreeSuite) TestRemoveAndCommitAll() { 496 expected := plumbing.NewHash("907cd576c6ced2ecd3dab34a72bf9cf65944b9a9") 497 498 fs := memfs.New() 499 w := &Worktree{ 500 r: s.Repository, 501 Filesystem: fs, 502 } 503 504 err := w.Checkout(&CheckoutOptions{}) 505 s.NoError(err) 506 507 util.WriteFile(fs, "foo", []byte("foo"), 0644) 508 _, err = w.Add("foo") 509 s.NoError(err) 510 511 _, errFirst := w.Commit("Add in Repo\n", &CommitOptions{ 512 Author: defaultSignature(), 513 }) 514 s.Nil(errFirst) 515 516 errRemove := fs.Remove("foo") 517 s.Nil(errRemove) 518 519 hash, errSecond := w.Commit("Remove foo\n", &CommitOptions{ 520 All: true, 521 Author: defaultSignature(), 522 }) 523 s.Nil(errSecond) 524 525 s.Equal(expected, hash) 526 s.NoError(err) 527 528 assertStorageStatus(s, s.Repository, 13, 11, 11, expected) 529} 530 531func (s *WorktreeSuite) TestCommitSign() { 532 fs := memfs.New() 533 storage := memory.NewStorage() 534 535 r, err := Init(storage, fs) 536 s.NoError(err) 537 538 w, err := r.Worktree() 539 s.NoError(err) 540 541 util.WriteFile(fs, "foo", []byte("foo"), 0644) 542 543 _, err = w.Add("foo") 544 s.NoError(err) 545 546 key := commitSignKey(s.T(), true) 547 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key}) 548 s.NoError(err) 549 550 // Verify the commit. 551 pks := new(bytes.Buffer) 552 pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil) 553 s.NoError(err) 554 555 err = key.Serialize(pkw) 556 s.NoError(err) 557 err = pkw.Close() 558 s.NoError(err) 559 560 expectedCommit, err := r.CommitObject(hash) 561 s.NoError(err) 562 actual, err := expectedCommit.Verify(pks.String()) 563 s.NoError(err) 564 s.Equal(key.PrimaryKey, actual.PrimaryKey) 565} 566 567func (s *WorktreeSuite) TestCommitSignBadKey() { 568 fs := memfs.New() 569 storage := memory.NewStorage() 570 571 r, err := Init(storage, fs) 572 s.NoError(err) 573 574 w, err := r.Worktree() 575 s.NoError(err) 576 577 util.WriteFile(fs, "foo", []byte("foo"), 0644) 578 579 _, err = w.Add("foo") 580 s.NoError(err) 581 582 key := commitSignKey(s.T(), false) 583 _, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key}) 584 s.ErrorIs(err, errors.InvalidArgumentError("signing key is encrypted")) 585} 586 587func (s *WorktreeSuite) TestCommitTreeSort() { 588 fs := s.TemporalFilesystem() 589 590 st := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) 591 _, err := Init(st, nil) 592 s.NoError(err) 593 594 r, _ := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 595 URL: fs.Root(), 596 }) 597 598 w, err := r.Worktree() 599 s.NoError(err) 600 601 mfs := w.Filesystem 602 603 err = mfs.MkdirAll("delta", 0755) 604 s.NoError(err) 605 606 for _, p := range []string{"delta_last", "Gamma", "delta/middle", "Beta", "delta-first", "alpha"} { 607 util.WriteFile(mfs, p, []byte("foo"), 0644) 608 _, err = w.Add(p) 609 s.NoError(err) 610 } 611 612 _, err = w.Commit("foo\n", &CommitOptions{ 613 All: true, 614 Author: defaultSignature(), 615 }) 616 s.NoError(err) 617 618 err = r.Push(&PushOptions{}) 619 s.NoError(err) 620 621 cmd := exec.Command("git", "fsck") 622 cmd.Dir = fs.Root() 623 cmd.Env = os.Environ() 624 buf := &bytes.Buffer{} 625 cmd.Stderr = buf 626 cmd.Stdout = buf 627 628 err = cmd.Run() 629 630 s.NoError(err, fmt.Sprintf("%s", buf.Bytes())) 631} 632 633// https://github.com/go-git/go-git/pull/224 634func (s *WorktreeSuite) TestJustStoreObjectsNotAlreadyStored() { 635 fs := s.TemporalFilesystem() 636 637 fsDotgit, err := fs.Chroot(".git") // real fs to get modified timestamps 638 s.NoError(err) 639 storage := filesystem.NewStorage(fsDotgit, cache.NewObjectLRUDefault()) 640 641 r, err := Init(storage, fs) 642 s.NoError(err) 643 644 w, err := r.Worktree() 645 s.NoError(err) 646 647 // Step 1: Write LICENSE 648 util.WriteFile(fs, "LICENSE", []byte("license"), 0644) 649 hLicense, err := w.Add("LICENSE") 650 s.NoError(err) 651 s.Equal(plumbing.NewHash("0484eba0d41636ba71fa612c78559cd6c3006cde"), hLicense) 652 653 hash, err := w.Commit("commit 1\n", &CommitOptions{ 654 All: true, 655 Author: defaultSignature(), 656 }) 657 s.NoError(err) 658 s.Equal(plumbing.NewHash("7a7faee4630d2664a6869677cc8ab614f3fd4a18"), hash) 659 660 infoLicense, err := fsDotgit.Stat(filepath.Join("objects", "04", "84eba0d41636ba71fa612c78559cd6c3006cde")) 661 s.NoError(err) // checking objects file exists 662 663 // Step 2: Write foo. 664 time.Sleep(5 * time.Millisecond) // uncool, but we need to get different timestamps... 665 util.WriteFile(fs, "foo", []byte("foo"), 0644) 666 hFoo, err := w.Add("foo") 667 s.NoError(err) 668 s.Equal(plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"), hFoo) 669 670 hash, err = w.Commit("commit 2\n", &CommitOptions{ 671 All: true, 672 Author: defaultSignature(), 673 }) 674 s.NoError(err) 675 s.Equal(plumbing.NewHash("97c0c5177e6ac57d10e8ea0017f2d39b91e2b364"), hash) 676 677 // Step 3: Check 678 // There is no need to overwrite the object of LICENSE, because its content 679 // was not changed. Just a write on the object of foo is required. This behaviour 680 // is fixed by #224 and tested by comparing the timestamps of the stored objects. 681 infoFoo, err := fsDotgit.Stat(filepath.Join("objects", "19", "102815663d23f8b75a47e7a01965dcdc96468c")) 682 s.NoError(err) // checking objects file exists 683 s.True(infoLicense.ModTime().Before(infoFoo.ModTime())) // object of foo has another/greaterThan timestamp than LICENSE 684 685 infoLicenseSecond, err := fsDotgit.Stat(filepath.Join("objects", "04", "84eba0d41636ba71fa612c78559cd6c3006cde")) 686 s.NoError(err) 687 688 log.Printf("comparing mod time: %v == %v on %v (%v)", infoLicenseSecond.ModTime(), infoLicense.ModTime(), runtime.GOOS, runtime.GOARCH) 689 s.Equal(infoLicense.ModTime(), infoLicenseSecond.ModTime()) // object of LICENSE should have the same timestamp because no additional write operation was performed 690} 691 692func (s *WorktreeSuite) TestCommitInvalidCharactersInAuthorInfos() { 693 f := fixtures.Basic().One() 694 s.Repository = NewRepositoryWithEmptyWorktree(f) 695 696 expected := plumbing.NewHash("e8eecef2524c3a37cf0f0996603162f81e0373f1") 697 698 fs := memfs.New() 699 storage := memory.NewStorage() 700 701 r, err := Init(storage, fs) 702 s.NoError(err) 703 704 w, err := r.Worktree() 705 s.NoError(err) 706 707 util.WriteFile(fs, "foo", []byte("foo"), 0644) 708 709 _, err = w.Add("foo") 710 s.NoError(err) 711 712 hash, err := w.Commit("foo\n", &CommitOptions{Author: invalidSignature()}) 713 s.Equal(expected, hash) 714 s.NoError(err) 715 716 assertStorageStatus(s, r, 1, 1, 1, expected) 717 718 // Check HEAD commit contains author informations with '<', '>' and '\n' stripped 719 lr, err := r.Log(&LogOptions{}) 720 s.NoError(err) 721 722 commit, err := lr.Next() 723 s.NoError(err) 724 725 s.Equal("foo bad", commit.Author.Name) 726 s.Equal("badfoo@foo.foo", commit.Author.Email) 727 728} 729 730func assertStorageStatus( 731 s *WorktreeSuite, r *Repository, 732 treesCount, blobCount, commitCount int, head plumbing.Hash, 733) { 734 trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject) 735 s.NoError(err) 736 blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject) 737 s.NoError(err) 738 commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject) 739 s.NoError(err) 740 741 s.Equal(treesCount, lenIterEncodedObjects(trees)) 742 s.Equal(blobCount, lenIterEncodedObjects(blobs)) 743 s.Equal(commitCount, lenIterEncodedObjects(commits)) 744 745 ref, err := r.Head() 746 s.NoError(err) 747 s.Equal(head, ref.Hash()) 748} 749 750func lenIterEncodedObjects(iter storer.EncodedObjectIter) int { 751 count := 0 752 iter.ForEach(func(plumbing.EncodedObject) error { 753 count++ 754 return nil 755 }) 756 757 return count 758} 759 760func defaultSignature() *object.Signature { 761 when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200") 762 return &object.Signature{ 763 Name: "foo", 764 Email: "foo@foo.foo", 765 When: when, 766 } 767} 768 769func invalidSignature() *object.Signature { 770 when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200") 771 return &object.Signature{ 772 Name: "foo <bad>\n", 773 Email: "<bad>\nfoo@foo.foo", 774 When: when, 775 } 776} 777 778func commitSignKey(t *testing.T, decrypt bool) *openpgp.Entity { 779 s := strings.NewReader(armoredKeyRing) 780 es, err := openpgp.ReadArmoredKeyRing(s) 781 assert.NoError(t, err) 782 783 assert.Len(t, es, 1) 784 assert.Len(t, es[0].Identities, 1) 785 _, ok := es[0].Identities["foo bar <foo@foo.foo>"] 786 assert.True(t, ok) 787 788 key := es[0] 789 if decrypt { 790 err = key.PrivateKey.Decrypt([]byte(keyPassphrase)) 791 assert.NoError(t, err) 792 } 793 794 return key 795} 796 797const armoredKeyRing = ` 798-----BEGIN PGP PRIVATE KEY BLOCK----- 799 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 852=gIM9 853-----END PGP PRIVATE KEY BLOCK----- 854` 855 856const keyPassphrase = "abcdef0123456789"