fork of go-git with some jj specific features
at v5.13.2 85 kB view raw
1package git 2 3import ( 4 "bytes" 5 "context" 6 "errors" 7 "io" 8 "os" 9 "path/filepath" 10 "regexp" 11 "runtime" 12 "strings" 13 "testing" 14 "time" 15 16 fixtures "github.com/go-git/go-git-fixtures/v4" 17 "github.com/go-git/go-git/v5/config" 18 "github.com/go-git/go-git/v5/plumbing" 19 "github.com/go-git/go-git/v5/plumbing/cache" 20 "github.com/go-git/go-git/v5/plumbing/filemode" 21 "github.com/go-git/go-git/v5/plumbing/format/gitignore" 22 "github.com/go-git/go-git/v5/plumbing/format/index" 23 "github.com/go-git/go-git/v5/plumbing/object" 24 "github.com/go-git/go-git/v5/storage/filesystem" 25 "github.com/go-git/go-git/v5/storage/memory" 26 "github.com/stretchr/testify/assert" 27 28 "github.com/go-git/go-billy/v5" 29 "github.com/go-git/go-billy/v5/memfs" 30 "github.com/go-git/go-billy/v5/osfs" 31 "github.com/go-git/go-billy/v5/util" 32 "golang.org/x/text/unicode/norm" 33 . "gopkg.in/check.v1" 34) 35 36func defaultTestCommitOptions() *CommitOptions { 37 return &CommitOptions{ 38 Author: &object.Signature{Name: "testuser", Email: "testemail"}, 39 } 40} 41 42type WorktreeSuite struct { 43 BaseSuite 44} 45 46var _ = Suite(&WorktreeSuite{}) 47 48func (s *WorktreeSuite) SetUpTest(c *C) { 49 f := fixtures.Basic().One() 50 s.Repository = s.NewRepositoryWithEmptyWorktree(f) 51} 52 53func (s *WorktreeSuite) TestPullCheckout(c *C) { 54 fs := memfs.New() 55 r, _ := Init(memory.NewStorage(), fs) 56 r.CreateRemote(&config.RemoteConfig{ 57 Name: DefaultRemoteName, 58 URLs: []string{s.GetBasicLocalRepositoryURL()}, 59 }) 60 61 w, err := r.Worktree() 62 c.Assert(err, IsNil) 63 64 err = w.Pull(&PullOptions{}) 65 c.Assert(err, IsNil) 66 67 fi, err := fs.ReadDir("") 68 c.Assert(err, IsNil) 69 c.Assert(fi, HasLen, 8) 70} 71 72func (s *WorktreeSuite) TestPullFastForward(c *C) { 73 url := c.MkDir() 74 75 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 76 77 server, err := PlainClone(url, false, &CloneOptions{ 78 URL: path, 79 }) 80 c.Assert(err, IsNil) 81 82 dir := c.MkDir() 83 84 r, err := PlainClone(dir, false, &CloneOptions{ 85 URL: url, 86 }) 87 c.Assert(err, IsNil) 88 89 w, err := server.Worktree() 90 c.Assert(err, IsNil) 91 err = os.WriteFile(filepath.Join(url, "foo"), []byte("foo"), 0755) 92 c.Assert(err, IsNil) 93 w.Add("foo") 94 hash, err := w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 95 c.Assert(err, IsNil) 96 97 w, err = r.Worktree() 98 c.Assert(err, IsNil) 99 100 err = w.Pull(&PullOptions{}) 101 c.Assert(err, IsNil) 102 103 head, err := r.Head() 104 c.Assert(err, IsNil) 105 c.Assert(head.Hash(), Equals, hash) 106} 107 108func (s *WorktreeSuite) TestPullNonFastForward(c *C) { 109 url := c.MkDir() 110 111 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 112 113 server, err := PlainClone(url, false, &CloneOptions{ 114 URL: path, 115 }) 116 c.Assert(err, IsNil) 117 118 dir := c.MkDir() 119 120 r, err := PlainClone(dir, false, &CloneOptions{ 121 URL: url, 122 }) 123 c.Assert(err, IsNil) 124 125 w, err := server.Worktree() 126 c.Assert(err, IsNil) 127 err = os.WriteFile(filepath.Join(url, "foo"), []byte("foo"), 0755) 128 c.Assert(err, IsNil) 129 w.Add("foo") 130 _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 131 c.Assert(err, IsNil) 132 133 w, err = r.Worktree() 134 c.Assert(err, IsNil) 135 err = os.WriteFile(filepath.Join(dir, "bar"), []byte("bar"), 0755) 136 c.Assert(err, IsNil) 137 w.Add("bar") 138 _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()}) 139 c.Assert(err, IsNil) 140 141 err = w.Pull(&PullOptions{}) 142 c.Assert(err, Equals, ErrNonFastForwardUpdate) 143} 144 145func (s *WorktreeSuite) TestPullUpdateReferencesIfNeeded(c *C) { 146 r, _ := Init(memory.NewStorage(), memfs.New()) 147 r.CreateRemote(&config.RemoteConfig{ 148 Name: DefaultRemoteName, 149 URLs: []string{s.GetBasicLocalRepositoryURL()}, 150 }) 151 152 err := r.Fetch(&FetchOptions{}) 153 c.Assert(err, IsNil) 154 155 _, err = r.Reference("refs/heads/master", false) 156 c.Assert(err, NotNil) 157 158 w, err := r.Worktree() 159 c.Assert(err, IsNil) 160 161 err = w.Pull(&PullOptions{}) 162 c.Assert(err, IsNil) 163 164 head, err := r.Reference(plumbing.HEAD, true) 165 c.Assert(err, IsNil) 166 c.Assert(head.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 167 168 branch, err := r.Reference("refs/heads/master", false) 169 c.Assert(err, IsNil) 170 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 171 172 err = w.Pull(&PullOptions{}) 173 c.Assert(err, Equals, NoErrAlreadyUpToDate) 174} 175 176func (s *WorktreeSuite) TestPullInSingleBranch(c *C) { 177 r, _ := Init(memory.NewStorage(), memfs.New()) 178 err := r.clone(context.Background(), &CloneOptions{ 179 URL: s.GetBasicLocalRepositoryURL(), 180 SingleBranch: true, 181 }) 182 183 c.Assert(err, IsNil) 184 185 w, err := r.Worktree() 186 c.Assert(err, IsNil) 187 188 err = w.Pull(&PullOptions{}) 189 c.Assert(err, Equals, NoErrAlreadyUpToDate) 190 191 branch, err := r.Reference("refs/heads/master", false) 192 c.Assert(err, IsNil) 193 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 194 195 _, err = r.Reference("refs/remotes/foo/branch", false) 196 c.Assert(err, NotNil) 197 198 storage := r.Storer.(*memory.Storage) 199 c.Assert(storage.Objects, HasLen, 28) 200} 201 202func (s *WorktreeSuite) TestPullProgress(c *C) { 203 r, _ := Init(memory.NewStorage(), memfs.New()) 204 205 r.CreateRemote(&config.RemoteConfig{ 206 Name: DefaultRemoteName, 207 URLs: []string{s.GetBasicLocalRepositoryURL()}, 208 }) 209 210 w, err := r.Worktree() 211 c.Assert(err, IsNil) 212 213 buf := bytes.NewBuffer(nil) 214 err = w.Pull(&PullOptions{ 215 Progress: buf, 216 }) 217 218 c.Assert(err, IsNil) 219 c.Assert(buf.Len(), Not(Equals), 0) 220} 221 222func (s *WorktreeSuite) TestPullProgressWithRecursion(c *C) { 223 if testing.Short() { 224 c.Skip("skipping test in short mode.") 225 } 226 227 path := fixtures.ByTag("submodule").One().Worktree().Root() 228 229 dir := c.MkDir() 230 231 r, _ := PlainInit(dir, false) 232 r.CreateRemote(&config.RemoteConfig{ 233 Name: DefaultRemoteName, 234 URLs: []string{path}, 235 }) 236 237 w, err := r.Worktree() 238 c.Assert(err, IsNil) 239 240 err = w.Pull(&PullOptions{ 241 RecurseSubmodules: DefaultSubmoduleRecursionDepth, 242 }) 243 c.Assert(err, IsNil) 244 245 cfg, err := r.Config() 246 c.Assert(err, IsNil) 247 c.Assert(cfg.Submodules, HasLen, 2) 248} 249 250func (s *RepositorySuite) TestPullAdd(c *C) { 251 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 252 253 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 254 URL: filepath.Join(path, ".git"), 255 }) 256 257 c.Assert(err, IsNil) 258 259 storage := r.Storer.(*memory.Storage) 260 c.Assert(storage.Objects, HasLen, 28) 261 262 branch, err := r.Reference("refs/heads/master", false) 263 c.Assert(err, IsNil) 264 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 265 266 ExecuteOnPath(c, path, 267 "touch foo", 268 "git add foo", 269 "git commit --no-gpg-sign -m foo foo", 270 ) 271 272 w, err := r.Worktree() 273 c.Assert(err, IsNil) 274 275 err = w.Pull(&PullOptions{RemoteName: "origin"}) 276 c.Assert(err, IsNil) 277 278 // the commit command has introduced a new commit, tree and blob 279 c.Assert(storage.Objects, HasLen, 31) 280 281 branch, err = r.Reference("refs/heads/master", false) 282 c.Assert(err, IsNil) 283 c.Assert(branch.Hash().String(), Not(Equals), "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 284} 285 286func (s *WorktreeSuite) TestPullAlreadyUptodate(c *C) { 287 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 288 289 fs := memfs.New() 290 r, err := Clone(memory.NewStorage(), fs, &CloneOptions{ 291 URL: filepath.Join(path, ".git"), 292 }) 293 294 c.Assert(err, IsNil) 295 296 w, err := r.Worktree() 297 c.Assert(err, IsNil) 298 err = util.WriteFile(fs, "bar", []byte("bar"), 0755) 299 c.Assert(err, IsNil) 300 w.Add("bar") 301 _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()}) 302 c.Assert(err, IsNil) 303 304 err = w.Pull(&PullOptions{}) 305 c.Assert(err, Equals, NoErrAlreadyUpToDate) 306} 307 308func (s *WorktreeSuite) TestPullDepth(c *C) { 309 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 310 URL: fixtures.Basic().One().URL, 311 Depth: 1, 312 }) 313 314 c.Assert(err, IsNil) 315 316 w, err := r.Worktree() 317 c.Assert(err, IsNil) 318 err = w.Pull(&PullOptions{}) 319 c.Assert(err, Equals, nil) 320} 321 322func (s *WorktreeSuite) TestPullAfterShallowClone(c *C) { 323 tempDir := c.MkDir() 324 remoteURL := filepath.Join(tempDir, "remote") 325 repoDir := filepath.Join(tempDir, "repo") 326 327 remote, err := PlainInit(remoteURL, false) 328 c.Assert(err, IsNil) 329 c.Assert(remote, NotNil) 330 331 _ = CommitNewFile(c, remote, "File1") 332 _ = CommitNewFile(c, remote, "File2") 333 334 repo, err := PlainClone(repoDir, false, &CloneOptions{ 335 URL: remoteURL, 336 Depth: 1, 337 Tags: NoTags, 338 SingleBranch: true, 339 ReferenceName: "master", 340 }) 341 c.Assert(err, IsNil) 342 343 _ = CommitNewFile(c, remote, "File3") 344 _ = CommitNewFile(c, remote, "File4") 345 346 w, err := repo.Worktree() 347 c.Assert(err, IsNil) 348 349 err = w.Pull(&PullOptions{ 350 RemoteName: DefaultRemoteName, 351 SingleBranch: true, 352 ReferenceName: plumbing.NewBranchReferenceName("master"), 353 }) 354 c.Assert(err, IsNil) 355} 356 357func (s *WorktreeSuite) TestCheckout(c *C) { 358 fs := memfs.New() 359 w := &Worktree{ 360 r: s.Repository, 361 Filesystem: fs, 362 } 363 364 err := w.Checkout(&CheckoutOptions{ 365 Force: true, 366 }) 367 c.Assert(err, IsNil) 368 369 entries, err := fs.ReadDir("/") 370 c.Assert(err, IsNil) 371 372 c.Assert(entries, HasLen, 8) 373 ch, err := fs.Open("CHANGELOG") 374 c.Assert(err, IsNil) 375 376 content, err := io.ReadAll(ch) 377 c.Assert(err, IsNil) 378 c.Assert(string(content), Equals, "Initial changelog\n") 379 380 idx, err := s.Repository.Storer.Index() 381 c.Assert(err, IsNil) 382 c.Assert(idx.Entries, HasLen, 9) 383} 384 385func (s *WorktreeSuite) TestCheckoutForce(c *C) { 386 w := &Worktree{ 387 r: s.Repository, 388 Filesystem: memfs.New(), 389 } 390 391 err := w.Checkout(&CheckoutOptions{}) 392 c.Assert(err, IsNil) 393 394 w.Filesystem = memfs.New() 395 396 err = w.Checkout(&CheckoutOptions{ 397 Force: true, 398 }) 399 c.Assert(err, IsNil) 400 401 entries, err := w.Filesystem.ReadDir("/") 402 c.Assert(err, IsNil) 403 c.Assert(entries, HasLen, 8) 404} 405 406func (s *WorktreeSuite) TestCheckoutKeep(c *C) { 407 w := &Worktree{ 408 r: s.Repository, 409 Filesystem: memfs.New(), 410 } 411 412 err := w.Checkout(&CheckoutOptions{ 413 Force: true, 414 }) 415 c.Assert(err, IsNil) 416 417 // Create a new branch and create a new file. 418 err = w.Checkout(&CheckoutOptions{ 419 Branch: plumbing.NewBranchReferenceName("new-branch"), 420 Create: true, 421 }) 422 c.Assert(err, IsNil) 423 424 w.Filesystem = memfs.New() 425 f, err := w.Filesystem.Create("new-file.txt") 426 c.Assert(err, IsNil) 427 _, err = f.Write([]byte("DUMMY")) 428 c.Assert(err, IsNil) 429 c.Assert(f.Close(), IsNil) 430 431 // Add the file to staging. 432 _, err = w.Add("new-file.txt") 433 c.Assert(err, IsNil) 434 435 // Switch branch to master, and verify that the new file was kept in staging. 436 err = w.Checkout(&CheckoutOptions{ 437 Keep: true, 438 }) 439 c.Assert(err, IsNil) 440 441 fi, err := w.Filesystem.Stat("new-file.txt") 442 c.Assert(err, IsNil) 443 c.Assert(fi.Size(), Equals, int64(5)) 444} 445 446func (s *WorktreeSuite) TestCheckoutSymlink(c *C) { 447 if runtime.GOOS == "windows" { 448 c.Skip("git doesn't support symlinks by default in windows") 449 } 450 451 dir := c.MkDir() 452 453 r, err := PlainInit(dir, false) 454 c.Assert(err, IsNil) 455 456 w, err := r.Worktree() 457 c.Assert(err, IsNil) 458 459 w.Filesystem.Symlink("not-exists", "bar") 460 w.Add("bar") 461 w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 462 463 r.Storer.SetIndex(&index.Index{Version: 2}) 464 w.Filesystem = osfs.New(filepath.Join(dir, "worktree-empty")) 465 466 err = w.Checkout(&CheckoutOptions{}) 467 c.Assert(err, IsNil) 468 469 status, err := w.Status() 470 c.Assert(err, IsNil) 471 c.Assert(status.IsClean(), Equals, true) 472 473 target, err := w.Filesystem.Readlink("bar") 474 c.Assert(target, Equals, "not-exists") 475 c.Assert(err, IsNil) 476} 477 478func (s *WorktreeSuite) TestCheckoutSparse(c *C) { 479 fs := memfs.New() 480 r, err := Clone(memory.NewStorage(), fs, &CloneOptions{ 481 URL: s.GetBasicLocalRepositoryURL(), 482 NoCheckout: true, 483 }) 484 c.Assert(err, IsNil) 485 486 w, err := r.Worktree() 487 c.Assert(err, IsNil) 488 489 sparseCheckoutDirectories := []string{"go", "json", "php"} 490 c.Assert(w.Checkout(&CheckoutOptions{ 491 SparseCheckoutDirectories: sparseCheckoutDirectories, 492 }), IsNil) 493 494 fis, err := fs.ReadDir("/") 495 c.Assert(err, IsNil) 496 497 for _, fi := range fis { 498 c.Assert(fi.IsDir(), Equals, true) 499 var oneOfSparseCheckoutDirs bool 500 501 for _, sparseCheckoutDirectory := range sparseCheckoutDirectories { 502 if strings.HasPrefix(fi.Name(), sparseCheckoutDirectory) { 503 oneOfSparseCheckoutDirs = true 504 } 505 } 506 c.Assert(oneOfSparseCheckoutDirs, Equals, true) 507 } 508} 509 510func (s *WorktreeSuite) TestFilenameNormalization(c *C) { 511 if runtime.GOOS == "windows" { 512 c.Skip("windows paths may contain non utf-8 sequences") 513 } 514 515 url := c.MkDir() 516 517 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 518 519 server, err := PlainClone(url, false, &CloneOptions{ 520 URL: path, 521 }) 522 c.Assert(err, IsNil) 523 524 filename := "페" 525 526 w, err := server.Worktree() 527 c.Assert(err, IsNil) 528 529 writeFile := func(path string) { 530 err := util.WriteFile(w.Filesystem, path, []byte("foo"), 0755) 531 c.Assert(err, IsNil) 532 } 533 534 writeFile(filename) 535 origHash, err := w.Add(filename) 536 c.Assert(err, IsNil) 537 _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 538 c.Assert(err, IsNil) 539 540 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 541 URL: url, 542 }) 543 c.Assert(err, IsNil) 544 545 w, err = r.Worktree() 546 c.Assert(err, IsNil) 547 548 status, err := w.Status() 549 c.Assert(err, IsNil) 550 c.Assert(status.IsClean(), Equals, true) 551 552 err = w.Filesystem.Remove(filename) 553 c.Assert(err, IsNil) 554 555 modFilename := norm.NFKD.String(filename) 556 writeFile(modFilename) 557 558 _, err = w.Add(filename) 559 c.Assert(err, IsNil) 560 modHash, err := w.Add(modFilename) 561 c.Assert(err, IsNil) 562 // At this point we've got two files with the same content. 563 // Hence their hashes must be the same. 564 c.Assert(origHash == modHash, Equals, true) 565 566 status, err = w.Status() 567 c.Assert(err, IsNil) 568 // However, their names are different and the work tree is still dirty. 569 c.Assert(status.IsClean(), Equals, false) 570 571 // Revert back the deletion of the first file. 572 writeFile(filename) 573 _, err = w.Add(filename) 574 c.Assert(err, IsNil) 575 576 status, err = w.Status() 577 c.Assert(err, IsNil) 578 // Still dirty - the second file is added. 579 c.Assert(status.IsClean(), Equals, false) 580 581 _, err = w.Remove(modFilename) 582 c.Assert(err, IsNil) 583 584 status, err = w.Status() 585 c.Assert(err, IsNil) 586 c.Assert(status.IsClean(), Equals, true) 587} 588 589func (s *WorktreeSuite) TestCheckoutSubmodule(c *C) { 590 url := "https://github.com/git-fixtures/submodule.git" 591 r := s.NewRepositoryWithEmptyWorktree(fixtures.ByURL(url).One()) 592 593 w, err := r.Worktree() 594 c.Assert(err, IsNil) 595 596 err = w.Checkout(&CheckoutOptions{}) 597 c.Assert(err, IsNil) 598 599 status, err := w.Status() 600 c.Assert(err, IsNil) 601 c.Assert(status.IsClean(), Equals, true) 602} 603 604func (s *WorktreeSuite) TestCheckoutSubmoduleInitialized(c *C) { 605 url := "https://github.com/git-fixtures/submodule.git" 606 r := s.NewRepository(fixtures.ByURL(url).One()) 607 608 w, err := r.Worktree() 609 c.Assert(err, IsNil) 610 611 sub, err := w.Submodules() 612 c.Assert(err, IsNil) 613 614 err = sub.Update(&SubmoduleUpdateOptions{Init: true}) 615 c.Assert(err, IsNil) 616 617 status, err := w.Status() 618 c.Assert(err, IsNil) 619 c.Assert(status.IsClean(), Equals, true) 620} 621 622func (s *WorktreeSuite) TestCheckoutRelativePathSubmoduleInitialized(c *C) { 623 url := "https://github.com/git-fixtures/submodule.git" 624 r := s.NewRepository(fixtures.ByURL(url).One()) 625 626 // modify the .gitmodules from original one 627 file, err := r.wt.OpenFile(".gitmodules", os.O_WRONLY|os.O_TRUNC, 0666) 628 c.Assert(err, IsNil) 629 630 n, err := io.WriteString(file, `[submodule "basic"] 631 path = basic 632 url = ../basic.git 633[submodule "itself"] 634 path = itself 635 url = ../submodule.git`) 636 c.Assert(err, IsNil) 637 c.Assert(n, Not(Equals), 0) 638 639 w, err := r.Worktree() 640 c.Assert(err, IsNil) 641 642 w.Add(".gitmodules") 643 w.Commit("test", &CommitOptions{}) 644 645 // test submodule path 646 modules, err := w.readGitmodulesFile() 647 c.Assert(err, IsNil) 648 649 c.Assert(modules.Submodules["basic"].URL, Equals, "../basic.git") 650 c.Assert(modules.Submodules["itself"].URL, Equals, "../submodule.git") 651 652 basicSubmodule, err := w.Submodule("basic") 653 c.Assert(err, IsNil) 654 basicRepo, err := basicSubmodule.Repository() 655 c.Assert(err, IsNil) 656 basicRemotes, err := basicRepo.Remotes() 657 c.Assert(err, IsNil) 658 c.Assert(basicRemotes[0].Config().URLs[0], Equals, "https://github.com/git-fixtures/basic.git") 659 660 itselfSubmodule, err := w.Submodule("itself") 661 c.Assert(err, IsNil) 662 itselfRepo, err := itselfSubmodule.Repository() 663 c.Assert(err, IsNil) 664 itselfRemotes, err := itselfRepo.Remotes() 665 c.Assert(err, IsNil) 666 c.Assert(itselfRemotes[0].Config().URLs[0], Equals, "https://github.com/git-fixtures/submodule.git") 667 668 sub, err := w.Submodules() 669 c.Assert(err, IsNil) 670 671 err = sub.Update(&SubmoduleUpdateOptions{Init: true, RecurseSubmodules: DefaultSubmoduleRecursionDepth}) 672 c.Assert(err, IsNil) 673 674 status, err := w.Status() 675 c.Assert(err, IsNil) 676 c.Assert(status.IsClean(), Equals, true) 677} 678 679func (s *WorktreeSuite) TestCheckoutIndexMem(c *C) { 680 fs := memfs.New() 681 w := &Worktree{ 682 r: s.Repository, 683 Filesystem: fs, 684 } 685 686 err := w.Checkout(&CheckoutOptions{}) 687 c.Assert(err, IsNil) 688 689 idx, err := s.Repository.Storer.Index() 690 c.Assert(err, IsNil) 691 c.Assert(idx.Entries, HasLen, 9) 692 c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") 693 c.Assert(idx.Entries[0].Name, Equals, ".gitignore") 694 c.Assert(idx.Entries[0].Mode, Equals, filemode.Regular) 695 c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false) 696 c.Assert(idx.Entries[0].Size, Equals, uint32(189)) 697 698 // ctime, dev, inode, uid and gid are not supported on memfs fs 699 c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, true) 700 c.Assert(idx.Entries[0].Dev, Equals, uint32(0)) 701 c.Assert(idx.Entries[0].Inode, Equals, uint32(0)) 702 c.Assert(idx.Entries[0].UID, Equals, uint32(0)) 703 c.Assert(idx.Entries[0].GID, Equals, uint32(0)) 704} 705 706func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) { 707 fs := s.TemporalFilesystem(c) 708 709 w := &Worktree{ 710 r: s.Repository, 711 Filesystem: fs, 712 } 713 714 err := w.Checkout(&CheckoutOptions{}) 715 c.Assert(err, IsNil) 716 717 idx, err := s.Repository.Storer.Index() 718 c.Assert(err, IsNil) 719 c.Assert(idx.Entries, HasLen, 9) 720 c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") 721 c.Assert(idx.Entries[0].Name, Equals, ".gitignore") 722 c.Assert(idx.Entries[0].Mode, Equals, filemode.Regular) 723 c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false) 724 c.Assert(idx.Entries[0].Size, Equals, uint32(189)) 725 726 c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, false) 727 if runtime.GOOS != "windows" { 728 c.Assert(idx.Entries[0].Dev, Not(Equals), uint32(0)) 729 c.Assert(idx.Entries[0].Inode, Not(Equals), uint32(0)) 730 c.Assert(idx.Entries[0].UID, Not(Equals), uint32(0)) 731 c.Assert(idx.Entries[0].GID, Not(Equals), uint32(0)) 732 } 733} 734 735func (s *WorktreeSuite) TestCheckoutBranch(c *C) { 736 w := &Worktree{ 737 r: s.Repository, 738 Filesystem: memfs.New(), 739 } 740 741 err := w.Checkout(&CheckoutOptions{ 742 Branch: "refs/heads/branch", 743 }) 744 c.Assert(err, IsNil) 745 746 head, err := w.r.Head() 747 c.Assert(err, IsNil) 748 c.Assert(head.Name().String(), Equals, "refs/heads/branch") 749 750 status, err := w.Status() 751 c.Assert(err, IsNil) 752 c.Assert(status.IsClean(), Equals, true) 753} 754 755func (s *WorktreeSuite) TestCheckoutCreateWithHash(c *C) { 756 w := &Worktree{ 757 r: s.Repository, 758 Filesystem: memfs.New(), 759 } 760 761 err := w.Checkout(&CheckoutOptions{ 762 Create: true, 763 Branch: "refs/heads/foo", 764 Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), 765 }) 766 c.Assert(err, IsNil) 767 768 head, err := w.r.Head() 769 c.Assert(err, IsNil) 770 c.Assert(head.Name().String(), Equals, "refs/heads/foo") 771 c.Assert(head.Hash(), Equals, plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")) 772 773 status, err := w.Status() 774 c.Assert(err, IsNil) 775 c.Assert(status.IsClean(), Equals, true) 776} 777 778func (s *WorktreeSuite) TestCheckoutCreate(c *C) { 779 w := &Worktree{ 780 r: s.Repository, 781 Filesystem: memfs.New(), 782 } 783 784 err := w.Checkout(&CheckoutOptions{ 785 Create: true, 786 Branch: "refs/heads/foo", 787 }) 788 c.Assert(err, IsNil) 789 790 head, err := w.r.Head() 791 c.Assert(err, IsNil) 792 c.Assert(head.Name().String(), Equals, "refs/heads/foo") 793 c.Assert(head.Hash(), Equals, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) 794 795 status, err := w.Status() 796 c.Assert(err, IsNil) 797 c.Assert(status.IsClean(), Equals, true) 798} 799 800func (s *WorktreeSuite) TestCheckoutBranchAndHash(c *C) { 801 w := &Worktree{ 802 r: s.Repository, 803 Filesystem: memfs.New(), 804 } 805 806 err := w.Checkout(&CheckoutOptions{ 807 Branch: "refs/heads/foo", 808 Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), 809 }) 810 811 c.Assert(err, Equals, ErrBranchHashExclusive) 812} 813 814func (s *WorktreeSuite) TestCheckoutCreateMissingBranch(c *C) { 815 w := &Worktree{ 816 r: s.Repository, 817 Filesystem: memfs.New(), 818 } 819 820 err := w.Checkout(&CheckoutOptions{ 821 Create: true, 822 }) 823 824 c.Assert(err, Equals, ErrCreateRequiresBranch) 825} 826 827func (s *WorktreeSuite) TestCheckoutCreateInvalidBranch(c *C) { 828 w := &Worktree{ 829 r: s.Repository, 830 Filesystem: memfs.New(), 831 } 832 833 for _, name := range []plumbing.ReferenceName{ 834 "foo", 835 "-", 836 "-foo", 837 "refs/heads//", 838 "refs/heads/..", 839 "refs/heads/a..b", 840 "refs/heads/.", 841 } { 842 err := w.Checkout(&CheckoutOptions{ 843 Create: true, 844 Branch: name, 845 }) 846 847 c.Assert(err, Equals, plumbing.ErrInvalidReferenceName) 848 } 849} 850 851func (s *WorktreeSuite) TestCheckoutTag(c *C) { 852 f := fixtures.ByTag("tags").One() 853 r := s.NewRepositoryWithEmptyWorktree(f) 854 w, err := r.Worktree() 855 c.Assert(err, IsNil) 856 857 err = w.Checkout(&CheckoutOptions{}) 858 c.Assert(err, IsNil) 859 head, err := w.r.Head() 860 c.Assert(err, IsNil) 861 c.Assert(head.Name().String(), Equals, "refs/heads/master") 862 863 status, err := w.Status() 864 c.Assert(err, IsNil) 865 c.Assert(status.IsClean(), Equals, true) 866 867 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/lightweight-tag"}) 868 c.Assert(err, IsNil) 869 head, err = w.r.Head() 870 c.Assert(err, IsNil) 871 c.Assert(head.Name().String(), Equals, "HEAD") 872 c.Assert(head.Hash().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f") 873 874 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/commit-tag"}) 875 c.Assert(err, IsNil) 876 head, err = w.r.Head() 877 c.Assert(err, IsNil) 878 c.Assert(head.Name().String(), Equals, "HEAD") 879 c.Assert(head.Hash().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f") 880 881 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/tree-tag"}) 882 c.Assert(err, NotNil) 883 head, err = w.r.Head() 884 c.Assert(err, IsNil) 885 c.Assert(head.Name().String(), Equals, "HEAD") 886} 887 888func (s *WorktreeSuite) TestCheckoutTagHash(c *C) { 889 f := fixtures.ByTag("tags").One() 890 r := s.NewRepositoryWithEmptyWorktree(f) 891 w, err := r.Worktree() 892 c.Assert(err, IsNil) 893 894 for _, hash := range []string{ 895 "b742a2a9fa0afcfa9a6fad080980fbc26b007c69", // annotated tag 896 "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc", // commit tag 897 "f7b877701fbf855b44c0a9e86f3fdce2c298b07f", // lightweight tag 898 } { 899 err = w.Checkout(&CheckoutOptions{ 900 Hash: plumbing.NewHash(hash), 901 }) 902 c.Assert(err, IsNil) 903 head, err := w.r.Head() 904 c.Assert(err, IsNil) 905 c.Assert(head.Name().String(), Equals, "HEAD") 906 907 status, err := w.Status() 908 c.Assert(err, IsNil) 909 c.Assert(status.IsClean(), Equals, true) 910 } 911 912 for _, hash := range []string{ 913 "fe6cb94756faa81e5ed9240f9191b833db5f40ae", // blob tag 914 "152175bf7e5580299fa1f0ba41ef6474cc043b70", // tree tag 915 } { 916 err = w.Checkout(&CheckoutOptions{ 917 Hash: plumbing.NewHash(hash), 918 }) 919 c.Assert(err, NotNil) 920 } 921} 922 923func (s *WorktreeSuite) TestCheckoutBisect(c *C) { 924 if testing.Short() { 925 c.Skip("skipping test in short mode.") 926 } 927 928 s.testCheckoutBisect(c, "https://github.com/src-d/go-git.git") 929} 930 931func (s *WorktreeSuite) TestCheckoutBisectSubmodules(c *C) { 932 s.testCheckoutBisect(c, "https://github.com/git-fixtures/submodule.git") 933} 934 935// TestCheckoutBisect simulates a git bisect going through the git history and 936// checking every commit over the previous commit 937func (s *WorktreeSuite) testCheckoutBisect(c *C, url string) { 938 f := fixtures.ByURL(url).One() 939 r := s.NewRepositoryWithEmptyWorktree(f) 940 941 w, err := r.Worktree() 942 c.Assert(err, IsNil) 943 944 iter, err := w.r.Log(&LogOptions{}) 945 c.Assert(err, IsNil) 946 947 iter.ForEach(func(commit *object.Commit) error { 948 err := w.Checkout(&CheckoutOptions{Hash: commit.Hash}) 949 c.Assert(err, IsNil) 950 951 status, err := w.Status() 952 c.Assert(err, IsNil) 953 c.Assert(status.IsClean(), Equals, true) 954 955 return nil 956 }) 957} 958 959func (s *WorktreeSuite) TestStatus(c *C) { 960 fs := memfs.New() 961 w := &Worktree{ 962 r: s.Repository, 963 Filesystem: fs, 964 } 965 966 status, err := w.Status() 967 c.Assert(err, IsNil) 968 969 c.Assert(status.IsClean(), Equals, false) 970 c.Assert(status, HasLen, 9) 971} 972 973func (s *WorktreeSuite) TestStatusEmpty(c *C) { 974 fs := memfs.New() 975 storage := memory.NewStorage() 976 977 r, err := Init(storage, fs) 978 c.Assert(err, IsNil) 979 980 w, err := r.Worktree() 981 c.Assert(err, IsNil) 982 983 status, err := w.Status() 984 c.Assert(err, IsNil) 985 c.Assert(status.IsClean(), Equals, true) 986 c.Assert(status, NotNil) 987} 988 989func (s *WorktreeSuite) TestStatusCheckedInBeforeIgnored(c *C) { 990 fs := memfs.New() 991 storage := memory.NewStorage() 992 993 r, err := Init(storage, fs) 994 c.Assert(err, IsNil) 995 996 w, err := r.Worktree() 997 c.Assert(err, IsNil) 998 999 err = util.WriteFile(fs, "fileToIgnore", []byte("Initial data"), 0755) 1000 c.Assert(err, IsNil) 1001 _, err = w.Add("fileToIgnore") 1002 c.Assert(err, IsNil) 1003 1004 _, err = w.Commit("Added file that will be ignored later", defaultTestCommitOptions()) 1005 c.Assert(err, IsNil) 1006 1007 err = util.WriteFile(fs, ".gitignore", []byte("fileToIgnore\nsecondIgnoredFile"), 0755) 1008 c.Assert(err, IsNil) 1009 _, err = w.Add(".gitignore") 1010 c.Assert(err, IsNil) 1011 _, err = w.Commit("Added .gitignore", defaultTestCommitOptions()) 1012 c.Assert(err, IsNil) 1013 status, err := w.Status() 1014 c.Assert(err, IsNil) 1015 c.Assert(status.IsClean(), Equals, true) 1016 c.Assert(status, NotNil) 1017 1018 err = util.WriteFile(fs, "secondIgnoredFile", []byte("Should be completely ignored"), 0755) 1019 c.Assert(err, IsNil) 1020 status = nil 1021 status, err = w.Status() 1022 c.Assert(err, IsNil) 1023 c.Assert(status.IsClean(), Equals, true) 1024 c.Assert(status, NotNil) 1025 1026 err = util.WriteFile(fs, "fileToIgnore", []byte("Updated data"), 0755) 1027 c.Assert(err, IsNil) 1028 status = nil 1029 status, err = w.Status() 1030 c.Assert(err, IsNil) 1031 c.Assert(status.IsClean(), Equals, false) 1032 c.Assert(status, NotNil) 1033} 1034 1035func (s *WorktreeSuite) TestStatusEmptyDirty(c *C) { 1036 fs := memfs.New() 1037 err := util.WriteFile(fs, "foo", []byte("foo"), 0755) 1038 c.Assert(err, IsNil) 1039 1040 storage := memory.NewStorage() 1041 1042 r, err := Init(storage, fs) 1043 c.Assert(err, IsNil) 1044 1045 w, err := r.Worktree() 1046 c.Assert(err, IsNil) 1047 1048 status, err := w.Status() 1049 c.Assert(err, IsNil) 1050 c.Assert(status.IsClean(), Equals, false) 1051 c.Assert(status, HasLen, 1) 1052} 1053 1054func (s *WorktreeSuite) TestStatusUnmodified(c *C) { 1055 fs := memfs.New() 1056 w := &Worktree{ 1057 r: s.Repository, 1058 Filesystem: fs, 1059 } 1060 1061 err := w.Checkout(&CheckoutOptions{Force: true}) 1062 c.Assert(err, IsNil) 1063 1064 status, err := w.StatusWithOptions(StatusOptions{Strategy: Preload}) 1065 c.Assert(err, IsNil) 1066 c.Assert(status.IsClean(), Equals, true) 1067 c.Assert(status.IsUntracked("LICENSE"), Equals, false) 1068 1069 c.Assert(status.File("LICENSE").Staging, Equals, Unmodified) 1070 c.Assert(status.File("LICENSE").Worktree, Equals, Unmodified) 1071 1072 status, err = w.StatusWithOptions(StatusOptions{Strategy: Empty}) 1073 c.Assert(err, IsNil) 1074 c.Assert(status.IsClean(), Equals, true) 1075 c.Assert(status.IsUntracked("LICENSE"), Equals, false) 1076 1077 c.Assert(status.File("LICENSE").Staging, Equals, Untracked) 1078 c.Assert(status.File("LICENSE").Worktree, Equals, Untracked) 1079} 1080 1081func (s *WorktreeSuite) TestReset(c *C) { 1082 fs := memfs.New() 1083 w := &Worktree{ 1084 r: s.Repository, 1085 Filesystem: fs, 1086 } 1087 1088 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 1089 1090 err := w.Checkout(&CheckoutOptions{}) 1091 c.Assert(err, IsNil) 1092 1093 branch, err := w.r.Reference(plumbing.Master, false) 1094 c.Assert(err, IsNil) 1095 c.Assert(branch.Hash(), Not(Equals), commit) 1096 1097 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commit}) 1098 c.Assert(err, IsNil) 1099 1100 branch, err = w.r.Reference(plumbing.Master, false) 1101 c.Assert(err, IsNil) 1102 c.Assert(branch.Hash(), Equals, commit) 1103 1104 status, err := w.Status() 1105 c.Assert(err, IsNil) 1106 c.Assert(status.IsClean(), Equals, true) 1107} 1108 1109func (s *WorktreeSuite) TestResetWithUntracked(c *C) { 1110 fs := memfs.New() 1111 w := &Worktree{ 1112 r: s.Repository, 1113 Filesystem: fs, 1114 } 1115 1116 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 1117 1118 err := w.Checkout(&CheckoutOptions{}) 1119 c.Assert(err, IsNil) 1120 1121 err = util.WriteFile(fs, "foo", nil, 0755) 1122 c.Assert(err, IsNil) 1123 1124 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commit}) 1125 c.Assert(err, IsNil) 1126 1127 status, err := w.Status() 1128 c.Assert(err, IsNil) 1129 c.Assert(status.IsClean(), Equals, true) 1130} 1131 1132func (s *WorktreeSuite) TestResetSoft(c *C) { 1133 fs := memfs.New() 1134 w := &Worktree{ 1135 r: s.Repository, 1136 Filesystem: fs, 1137 } 1138 1139 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 1140 1141 err := w.Checkout(&CheckoutOptions{}) 1142 c.Assert(err, IsNil) 1143 1144 err = w.Reset(&ResetOptions{Mode: SoftReset, Commit: commit}) 1145 c.Assert(err, IsNil) 1146 1147 branch, err := w.r.Reference(plumbing.Master, false) 1148 c.Assert(err, IsNil) 1149 c.Assert(branch.Hash(), Equals, commit) 1150 1151 status, err := w.Status() 1152 c.Assert(err, IsNil) 1153 c.Assert(status.IsClean(), Equals, false) 1154 c.Assert(status.File("CHANGELOG").Staging, Equals, Added) 1155} 1156 1157func (s *WorktreeSuite) TestResetMixed(c *C) { 1158 fs := memfs.New() 1159 w := &Worktree{ 1160 r: s.Repository, 1161 Filesystem: fs, 1162 } 1163 1164 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 1165 1166 err := w.Checkout(&CheckoutOptions{}) 1167 c.Assert(err, IsNil) 1168 1169 err = w.Reset(&ResetOptions{Mode: MixedReset, Commit: commit}) 1170 c.Assert(err, IsNil) 1171 1172 branch, err := w.r.Reference(plumbing.Master, false) 1173 c.Assert(err, IsNil) 1174 c.Assert(branch.Hash(), Equals, commit) 1175 1176 status, err := w.Status() 1177 c.Assert(err, IsNil) 1178 c.Assert(status.IsClean(), Equals, false) 1179 c.Assert(status.File("CHANGELOG").Staging, Equals, Untracked) 1180} 1181 1182func (s *WorktreeSuite) TestResetMerge(c *C) { 1183 fs := memfs.New() 1184 w := &Worktree{ 1185 r: s.Repository, 1186 Filesystem: fs, 1187 } 1188 1189 commitA := plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294") 1190 commitB := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 1191 1192 err := w.Checkout(&CheckoutOptions{}) 1193 c.Assert(err, IsNil) 1194 1195 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commitA}) 1196 c.Assert(err, IsNil) 1197 1198 branch, err := w.r.Reference(plumbing.Master, false) 1199 c.Assert(err, IsNil) 1200 c.Assert(branch.Hash(), Equals, commitA) 1201 1202 f, err := fs.Create(".gitignore") 1203 c.Assert(err, IsNil) 1204 _, err = f.Write([]byte("foo")) 1205 c.Assert(err, IsNil) 1206 err = f.Close() 1207 c.Assert(err, IsNil) 1208 1209 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commitB}) 1210 c.Assert(err, Equals, ErrUnstagedChanges) 1211 1212 branch, err = w.r.Reference(plumbing.Master, false) 1213 c.Assert(err, IsNil) 1214 c.Assert(branch.Hash(), Equals, commitA) 1215} 1216 1217func (s *WorktreeSuite) TestResetHard(c *C) { 1218 fs := memfs.New() 1219 w := &Worktree{ 1220 r: s.Repository, 1221 Filesystem: fs, 1222 } 1223 1224 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 1225 1226 err := w.Checkout(&CheckoutOptions{}) 1227 c.Assert(err, IsNil) 1228 1229 f, err := fs.Create(".gitignore") 1230 c.Assert(err, IsNil) 1231 _, err = f.Write([]byte("foo")) 1232 c.Assert(err, IsNil) 1233 err = f.Close() 1234 c.Assert(err, IsNil) 1235 1236 err = w.Reset(&ResetOptions{Mode: HardReset, Commit: commit}) 1237 c.Assert(err, IsNil) 1238 1239 branch, err := w.r.Reference(plumbing.Master, false) 1240 c.Assert(err, IsNil) 1241 c.Assert(branch.Hash(), Equals, commit) 1242} 1243 1244func (s *WorktreeSuite) TestResetHardSubFolders(c *C) { 1245 fs := memfs.New() 1246 w := &Worktree{ 1247 r: s.Repository, 1248 Filesystem: fs, 1249 } 1250 1251 err := w.Checkout(&CheckoutOptions{}) 1252 c.Assert(err, IsNil) 1253 1254 err = fs.MkdirAll("dir", os.ModePerm) 1255 c.Assert(err, IsNil) 1256 tf, err := fs.Create("./dir/testfile.txt") 1257 c.Assert(err, IsNil) 1258 _, err = tf.Write([]byte("testfile content")) 1259 c.Assert(err, IsNil) 1260 err = tf.Close() 1261 c.Assert(err, IsNil) 1262 _, err = w.Add("dir/testfile.txt") 1263 c.Assert(err, IsNil) 1264 _, err = w.Commit("testcommit", &CommitOptions{Author: &object.Signature{Name: "name", Email: "email"}}) 1265 c.Assert(err, IsNil) 1266 1267 err = fs.Remove("dir/testfile.txt") 1268 c.Assert(err, IsNil) 1269 1270 status, err := w.Status() 1271 c.Assert(err, IsNil) 1272 c.Assert(status.IsClean(), Equals, false) 1273 1274 err = w.Reset(&ResetOptions{Files: []string{"dir/testfile.txt"}, Mode: HardReset}) 1275 c.Assert(err, IsNil) 1276 1277 status, err = w.Status() 1278 c.Assert(err, IsNil) 1279 c.Assert(status.IsClean(), Equals, true) 1280} 1281 1282func (s *WorktreeSuite) TestResetHardWithGitIgnore(c *C) { 1283 fs := memfs.New() 1284 w := &Worktree{ 1285 r: s.Repository, 1286 Filesystem: fs, 1287 } 1288 1289 err := w.Checkout(&CheckoutOptions{}) 1290 c.Assert(err, IsNil) 1291 1292 tf, err := fs.Create("newTestFile.txt") 1293 c.Assert(err, IsNil) 1294 _, err = tf.Write([]byte("testfile content")) 1295 c.Assert(err, IsNil) 1296 err = tf.Close() 1297 c.Assert(err, IsNil) 1298 _, err = w.Add("newTestFile.txt") 1299 c.Assert(err, IsNil) 1300 _, err = w.Commit("testcommit", &CommitOptions{Author: &object.Signature{Name: "name", Email: "email"}}) 1301 c.Assert(err, IsNil) 1302 1303 err = fs.Remove("newTestFile.txt") 1304 c.Assert(err, IsNil) 1305 f, err := fs.Create(".gitignore") 1306 c.Assert(err, IsNil) 1307 _, err = f.Write([]byte("foo\n")) 1308 c.Assert(err, IsNil) 1309 _, err = f.Write([]byte("newTestFile.txt\n")) 1310 c.Assert(err, IsNil) 1311 err = f.Close() 1312 c.Assert(err, IsNil) 1313 1314 status, err := w.Status() 1315 c.Assert(err, IsNil) 1316 c.Assert(status.IsClean(), Equals, false) 1317 1318 err = w.Reset(&ResetOptions{Mode: HardReset}) 1319 c.Assert(err, IsNil) 1320 1321 status, err = w.Status() 1322 c.Assert(err, IsNil) 1323 c.Assert(status.IsClean(), Equals, true) 1324} 1325 1326func (s *WorktreeSuite) TestResetSparsely(c *C) { 1327 fs := memfs.New() 1328 w := &Worktree{ 1329 r: s.Repository, 1330 Filesystem: fs, 1331 } 1332 1333 sparseResetDirs := []string{"php"} 1334 1335 err := w.ResetSparsely(&ResetOptions{Mode: HardReset}, sparseResetDirs) 1336 c.Assert(err, IsNil) 1337 1338 files, err := fs.ReadDir("/") 1339 c.Assert(err, IsNil) 1340 c.Assert(files, HasLen, 1) 1341 c.Assert(files[0].Name(), Equals, "php") 1342 1343 files, err = fs.ReadDir("/php") 1344 c.Assert(err, IsNil) 1345 c.Assert(files, HasLen, 1) 1346 c.Assert(files[0].Name(), Equals, "crappy.php") 1347} 1348 1349func (s *WorktreeSuite) TestStatusAfterCheckout(c *C) { 1350 fs := memfs.New() 1351 w := &Worktree{ 1352 r: s.Repository, 1353 Filesystem: fs, 1354 } 1355 1356 err := w.Checkout(&CheckoutOptions{Force: true}) 1357 c.Assert(err, IsNil) 1358 1359 status, err := w.Status() 1360 c.Assert(err, IsNil) 1361 c.Assert(status.IsClean(), Equals, true) 1362 1363} 1364 1365func (s *WorktreeSuite) TestStatusModified(c *C) { 1366 fs := s.TemporalFilesystem(c) 1367 1368 w := &Worktree{ 1369 r: s.Repository, 1370 Filesystem: fs, 1371 } 1372 1373 err := w.Checkout(&CheckoutOptions{}) 1374 c.Assert(err, IsNil) 1375 1376 f, err := fs.Create(".gitignore") 1377 c.Assert(err, IsNil) 1378 _, err = f.Write([]byte("foo")) 1379 c.Assert(err, IsNil) 1380 err = f.Close() 1381 c.Assert(err, IsNil) 1382 1383 status, err := w.Status() 1384 c.Assert(err, IsNil) 1385 c.Assert(status.IsClean(), Equals, false) 1386 c.Assert(status.File(".gitignore").Worktree, Equals, Modified) 1387} 1388 1389func (s *WorktreeSuite) TestStatusIgnored(c *C) { 1390 fs := memfs.New() 1391 w := &Worktree{ 1392 r: s.Repository, 1393 Filesystem: fs, 1394 } 1395 1396 w.Checkout(&CheckoutOptions{}) 1397 1398 fs.MkdirAll("another", os.ModePerm) 1399 f, _ := fs.Create("another/file") 1400 f.Close() 1401 fs.MkdirAll("vendor/github.com", os.ModePerm) 1402 f, _ = fs.Create("vendor/github.com/file") 1403 f.Close() 1404 fs.MkdirAll("vendor/gopkg.in", os.ModePerm) 1405 f, _ = fs.Create("vendor/gopkg.in/file") 1406 f.Close() 1407 1408 status, _ := w.Status() 1409 c.Assert(len(status), Equals, 3) 1410 _, ok := status["another/file"] 1411 c.Assert(ok, Equals, true) 1412 _, ok = status["vendor/github.com/file"] 1413 c.Assert(ok, Equals, true) 1414 _, ok = status["vendor/gopkg.in/file"] 1415 c.Assert(ok, Equals, true) 1416 1417 f, _ = fs.Create(".gitignore") 1418 f.Write([]byte("vendor/g*/")) 1419 f.Close() 1420 f, _ = fs.Create("vendor/.gitignore") 1421 f.Write([]byte("!github.com/\n")) 1422 f.Close() 1423 1424 status, _ = w.Status() 1425 c.Assert(len(status), Equals, 4) 1426 _, ok = status[".gitignore"] 1427 c.Assert(ok, Equals, true) 1428 _, ok = status["another/file"] 1429 c.Assert(ok, Equals, true) 1430 _, ok = status["vendor/.gitignore"] 1431 c.Assert(ok, Equals, true) 1432 _, ok = status["vendor/github.com/file"] 1433 c.Assert(ok, Equals, true) 1434} 1435 1436func (s *WorktreeSuite) TestStatusUntracked(c *C) { 1437 fs := memfs.New() 1438 w := &Worktree{ 1439 r: s.Repository, 1440 Filesystem: fs, 1441 } 1442 1443 err := w.Checkout(&CheckoutOptions{Force: true}) 1444 c.Assert(err, IsNil) 1445 1446 f, err := w.Filesystem.Create("foo") 1447 c.Assert(err, IsNil) 1448 c.Assert(f.Close(), IsNil) 1449 1450 status, err := w.Status() 1451 c.Assert(err, IsNil) 1452 c.Assert(status.File("foo").Staging, Equals, Untracked) 1453 c.Assert(status.File("foo").Worktree, Equals, Untracked) 1454} 1455 1456func (s *WorktreeSuite) TestStatusDeleted(c *C) { 1457 fs := s.TemporalFilesystem(c) 1458 1459 w := &Worktree{ 1460 r: s.Repository, 1461 Filesystem: fs, 1462 } 1463 1464 err := w.Checkout(&CheckoutOptions{}) 1465 c.Assert(err, IsNil) 1466 1467 err = fs.Remove(".gitignore") 1468 c.Assert(err, IsNil) 1469 1470 status, err := w.Status() 1471 c.Assert(err, IsNil) 1472 c.Assert(status.IsClean(), Equals, false) 1473 c.Assert(status.File(".gitignore").Worktree, Equals, Deleted) 1474} 1475 1476func (s *WorktreeSuite) TestSubmodule(c *C) { 1477 path := fixtures.ByTag("submodule").One().Worktree().Root() 1478 r, err := PlainOpen(path) 1479 c.Assert(err, IsNil) 1480 1481 w, err := r.Worktree() 1482 c.Assert(err, IsNil) 1483 1484 m, err := w.Submodule("basic") 1485 c.Assert(err, IsNil) 1486 1487 c.Assert(m.Config().Name, Equals, "basic") 1488} 1489 1490func (s *WorktreeSuite) TestSubmodules(c *C) { 1491 path := fixtures.ByTag("submodule").One().Worktree().Root() 1492 r, err := PlainOpen(path) 1493 c.Assert(err, IsNil) 1494 1495 w, err := r.Worktree() 1496 c.Assert(err, IsNil) 1497 1498 l, err := w.Submodules() 1499 c.Assert(err, IsNil) 1500 1501 c.Assert(l, HasLen, 2) 1502} 1503 1504func (s *WorktreeSuite) TestAddUntracked(c *C) { 1505 fs := memfs.New() 1506 w := &Worktree{ 1507 r: s.Repository, 1508 Filesystem: fs, 1509 } 1510 1511 err := w.Checkout(&CheckoutOptions{Force: true}) 1512 c.Assert(err, IsNil) 1513 1514 idx, err := w.r.Storer.Index() 1515 c.Assert(err, IsNil) 1516 c.Assert(idx.Entries, HasLen, 9) 1517 1518 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755) 1519 c.Assert(err, IsNil) 1520 1521 hash, err := w.Add("foo") 1522 c.Assert(hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5") 1523 c.Assert(err, IsNil) 1524 1525 idx, err = w.r.Storer.Index() 1526 c.Assert(err, IsNil) 1527 c.Assert(idx.Entries, HasLen, 10) 1528 1529 e, err := idx.Entry("foo") 1530 c.Assert(err, IsNil) 1531 c.Assert(e.Hash, Equals, hash) 1532 c.Assert(e.Mode, Equals, filemode.Executable) 1533 1534 status, err := w.Status() 1535 c.Assert(err, IsNil) 1536 c.Assert(status, HasLen, 1) 1537 1538 file := status.File("foo") 1539 c.Assert(file.Staging, Equals, Added) 1540 c.Assert(file.Worktree, Equals, Unmodified) 1541 1542 obj, err := w.r.Storer.EncodedObject(plumbing.BlobObject, hash) 1543 c.Assert(err, IsNil) 1544 c.Assert(obj, NotNil) 1545 c.Assert(obj.Size(), Equals, int64(3)) 1546} 1547 1548func (s *WorktreeSuite) TestIgnored(c *C) { 1549 fs := memfs.New() 1550 w := &Worktree{ 1551 r: s.Repository, 1552 Filesystem: fs, 1553 } 1554 1555 w.Excludes = make([]gitignore.Pattern, 0) 1556 w.Excludes = append(w.Excludes, gitignore.ParsePattern("foo", nil)) 1557 1558 err := w.Checkout(&CheckoutOptions{Force: true}) 1559 c.Assert(err, IsNil) 1560 1561 idx, err := w.r.Storer.Index() 1562 c.Assert(err, IsNil) 1563 c.Assert(idx.Entries, HasLen, 9) 1564 1565 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755) 1566 c.Assert(err, IsNil) 1567 1568 status, err := w.Status() 1569 c.Assert(err, IsNil) 1570 c.Assert(status, HasLen, 0) 1571 1572 file := status.File("foo") 1573 c.Assert(file.Staging, Equals, Untracked) 1574 c.Assert(file.Worktree, Equals, Untracked) 1575} 1576 1577func (s *WorktreeSuite) TestExcludedNoGitignore(c *C) { 1578 f := fixtures.ByTag("empty").One() 1579 r := s.NewRepository(f) 1580 1581 fs := memfs.New() 1582 w := &Worktree{ 1583 r: r, 1584 Filesystem: fs, 1585 } 1586 1587 _, err := fs.Open(".gitignore") 1588 c.Assert(err, Equals, os.ErrNotExist) 1589 1590 w.Excludes = make([]gitignore.Pattern, 0) 1591 w.Excludes = append(w.Excludes, gitignore.ParsePattern("foo", nil)) 1592 1593 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755) 1594 c.Assert(err, IsNil) 1595 1596 status, err := w.Status() 1597 c.Assert(err, IsNil) 1598 c.Assert(status, HasLen, 0) 1599 1600 file := status.File("foo") 1601 c.Assert(file.Staging, Equals, Untracked) 1602 c.Assert(file.Worktree, Equals, Untracked) 1603} 1604 1605func (s *WorktreeSuite) TestAddModified(c *C) { 1606 fs := memfs.New() 1607 w := &Worktree{ 1608 r: s.Repository, 1609 Filesystem: fs, 1610 } 1611 1612 err := w.Checkout(&CheckoutOptions{Force: true}) 1613 c.Assert(err, IsNil) 1614 1615 idx, err := w.r.Storer.Index() 1616 c.Assert(err, IsNil) 1617 c.Assert(idx.Entries, HasLen, 9) 1618 1619 err = util.WriteFile(w.Filesystem, "LICENSE", []byte("FOO"), 0644) 1620 c.Assert(err, IsNil) 1621 1622 hash, err := w.Add("LICENSE") 1623 c.Assert(err, IsNil) 1624 c.Assert(hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5") 1625 1626 idx, err = w.r.Storer.Index() 1627 c.Assert(err, IsNil) 1628 c.Assert(idx.Entries, HasLen, 9) 1629 1630 e, err := idx.Entry("LICENSE") 1631 c.Assert(err, IsNil) 1632 c.Assert(e.Hash, Equals, hash) 1633 c.Assert(e.Mode, Equals, filemode.Regular) 1634 1635 status, err := w.Status() 1636 c.Assert(err, IsNil) 1637 c.Assert(status, HasLen, 1) 1638 1639 file := status.File("LICENSE") 1640 c.Assert(file.Staging, Equals, Modified) 1641 c.Assert(file.Worktree, Equals, Unmodified) 1642} 1643 1644func (s *WorktreeSuite) TestAddUnmodified(c *C) { 1645 fs := memfs.New() 1646 w := &Worktree{ 1647 r: s.Repository, 1648 Filesystem: fs, 1649 } 1650 1651 err := w.Checkout(&CheckoutOptions{Force: true}) 1652 c.Assert(err, IsNil) 1653 1654 hash, err := w.Add("LICENSE") 1655 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 1656 c.Assert(err, IsNil) 1657} 1658 1659func (s *WorktreeSuite) TestAddRemoved(c *C) { 1660 fs := memfs.New() 1661 w := &Worktree{ 1662 r: s.Repository, 1663 Filesystem: fs, 1664 } 1665 1666 err := w.Checkout(&CheckoutOptions{Force: true}) 1667 c.Assert(err, IsNil) 1668 1669 idx, err := w.r.Storer.Index() 1670 c.Assert(err, IsNil) 1671 c.Assert(idx.Entries, HasLen, 9) 1672 1673 err = w.Filesystem.Remove("LICENSE") 1674 c.Assert(err, IsNil) 1675 1676 hash, err := w.Add("LICENSE") 1677 c.Assert(err, IsNil) 1678 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 1679 1680 e, err := idx.Entry("LICENSE") 1681 c.Assert(err, IsNil) 1682 c.Assert(e.Hash, Equals, hash) 1683 c.Assert(e.Mode, Equals, filemode.Regular) 1684 1685 status, err := w.Status() 1686 c.Assert(err, IsNil) 1687 c.Assert(status, HasLen, 1) 1688 1689 file := status.File("LICENSE") 1690 c.Assert(file.Staging, Equals, Deleted) 1691} 1692 1693func (s *WorktreeSuite) TestAddRemovedInDirectory(c *C) { 1694 fs := memfs.New() 1695 w := &Worktree{ 1696 r: s.Repository, 1697 Filesystem: fs, 1698 } 1699 1700 err := w.Checkout(&CheckoutOptions{Force: true}) 1701 c.Assert(err, IsNil) 1702 1703 idx, err := w.r.Storer.Index() 1704 c.Assert(err, IsNil) 1705 c.Assert(idx.Entries, HasLen, 9) 1706 1707 err = w.Filesystem.Remove("go/example.go") 1708 c.Assert(err, IsNil) 1709 1710 err = w.Filesystem.Remove("json/short.json") 1711 c.Assert(err, IsNil) 1712 1713 hash, err := w.Add("go") 1714 c.Assert(err, IsNil) 1715 c.Assert(hash.IsZero(), Equals, true) 1716 1717 e, err := idx.Entry("go/example.go") 1718 c.Assert(err, IsNil) 1719 c.Assert(e.Hash, Equals, plumbing.NewHash("880cd14280f4b9b6ed3986d6671f907d7cc2a198")) 1720 c.Assert(e.Mode, Equals, filemode.Regular) 1721 1722 e, err = idx.Entry("json/short.json") 1723 c.Assert(err, IsNil) 1724 c.Assert(e.Hash, Equals, plumbing.NewHash("c8f1d8c61f9da76f4cb49fd86322b6e685dba956")) 1725 c.Assert(e.Mode, Equals, filemode.Regular) 1726 1727 status, err := w.Status() 1728 c.Assert(err, IsNil) 1729 c.Assert(status, HasLen, 2) 1730 1731 file := status.File("go/example.go") 1732 c.Assert(file.Staging, Equals, Deleted) 1733 1734 file = status.File("json/short.json") 1735 c.Assert(file.Staging, Equals, Unmodified) 1736} 1737 1738func (s *WorktreeSuite) TestAddRemovedInDirectoryWithTrailingSlash(c *C) { 1739 fs := memfs.New() 1740 w := &Worktree{ 1741 r: s.Repository, 1742 Filesystem: fs, 1743 } 1744 1745 err := w.Checkout(&CheckoutOptions{Force: true}) 1746 c.Assert(err, IsNil) 1747 1748 idx, err := w.r.Storer.Index() 1749 c.Assert(err, IsNil) 1750 c.Assert(idx.Entries, HasLen, 9) 1751 1752 err = w.Filesystem.Remove("go/example.go") 1753 c.Assert(err, IsNil) 1754 1755 err = w.Filesystem.Remove("json/short.json") 1756 c.Assert(err, IsNil) 1757 1758 hash, err := w.Add("go/") 1759 c.Assert(err, IsNil) 1760 c.Assert(hash.IsZero(), Equals, true) 1761 1762 e, err := idx.Entry("go/example.go") 1763 c.Assert(err, IsNil) 1764 c.Assert(e.Hash, Equals, plumbing.NewHash("880cd14280f4b9b6ed3986d6671f907d7cc2a198")) 1765 c.Assert(e.Mode, Equals, filemode.Regular) 1766 1767 e, err = idx.Entry("json/short.json") 1768 c.Assert(err, IsNil) 1769 c.Assert(e.Hash, Equals, plumbing.NewHash("c8f1d8c61f9da76f4cb49fd86322b6e685dba956")) 1770 c.Assert(e.Mode, Equals, filemode.Regular) 1771 1772 status, err := w.Status() 1773 c.Assert(err, IsNil) 1774 c.Assert(status, HasLen, 2) 1775 1776 file := status.File("go/example.go") 1777 c.Assert(file.Staging, Equals, Deleted) 1778 1779 file = status.File("json/short.json") 1780 c.Assert(file.Staging, Equals, Unmodified) 1781} 1782 1783func (s *WorktreeSuite) TestAddRemovedInDirectoryDot(c *C) { 1784 fs := memfs.New() 1785 w := &Worktree{ 1786 r: s.Repository, 1787 Filesystem: fs, 1788 } 1789 1790 err := w.Checkout(&CheckoutOptions{Force: true}) 1791 c.Assert(err, IsNil) 1792 1793 idx, err := w.r.Storer.Index() 1794 c.Assert(err, IsNil) 1795 c.Assert(idx.Entries, HasLen, 9) 1796 1797 err = w.Filesystem.Remove("go/example.go") 1798 c.Assert(err, IsNil) 1799 1800 err = w.Filesystem.Remove("json/short.json") 1801 c.Assert(err, IsNil) 1802 1803 hash, err := w.Add(".") 1804 c.Assert(err, IsNil) 1805 c.Assert(hash.IsZero(), Equals, true) 1806 1807 e, err := idx.Entry("go/example.go") 1808 c.Assert(err, IsNil) 1809 c.Assert(e.Hash, Equals, plumbing.NewHash("880cd14280f4b9b6ed3986d6671f907d7cc2a198")) 1810 c.Assert(e.Mode, Equals, filemode.Regular) 1811 1812 e, err = idx.Entry("json/short.json") 1813 c.Assert(err, IsNil) 1814 c.Assert(e.Hash, Equals, plumbing.NewHash("c8f1d8c61f9da76f4cb49fd86322b6e685dba956")) 1815 c.Assert(e.Mode, Equals, filemode.Regular) 1816 1817 status, err := w.Status() 1818 c.Assert(err, IsNil) 1819 c.Assert(status, HasLen, 2) 1820 1821 file := status.File("go/example.go") 1822 c.Assert(file.Staging, Equals, Deleted) 1823 1824 file = status.File("json/short.json") 1825 c.Assert(file.Staging, Equals, Deleted) 1826} 1827 1828func (s *WorktreeSuite) TestAddSymlink(c *C) { 1829 dir := c.MkDir() 1830 1831 r, err := PlainInit(dir, false) 1832 c.Assert(err, IsNil) 1833 err = util.WriteFile(r.wt, "foo", []byte("qux"), 0644) 1834 c.Assert(err, IsNil) 1835 err = r.wt.Symlink("foo", "bar") 1836 c.Assert(err, IsNil) 1837 1838 w, err := r.Worktree() 1839 c.Assert(err, IsNil) 1840 h, err := w.Add("foo") 1841 c.Assert(err, IsNil) 1842 c.Assert(h, Not(Equals), plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c")) 1843 1844 h, err = w.Add("bar") 1845 c.Assert(err, IsNil) 1846 c.Assert(h, Equals, plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c")) 1847 1848 obj, err := w.r.Storer.EncodedObject(plumbing.BlobObject, h) 1849 c.Assert(err, IsNil) 1850 c.Assert(obj, NotNil) 1851 c.Assert(obj.Size(), Equals, int64(3)) 1852} 1853 1854func (s *WorktreeSuite) TestAddDirectory(c *C) { 1855 fs := memfs.New() 1856 w := &Worktree{ 1857 r: s.Repository, 1858 Filesystem: fs, 1859 } 1860 1861 err := w.Checkout(&CheckoutOptions{Force: true}) 1862 c.Assert(err, IsNil) 1863 1864 idx, err := w.r.Storer.Index() 1865 c.Assert(err, IsNil) 1866 c.Assert(idx.Entries, HasLen, 9) 1867 1868 err = util.WriteFile(w.Filesystem, "qux/foo", []byte("FOO"), 0755) 1869 c.Assert(err, IsNil) 1870 err = util.WriteFile(w.Filesystem, "qux/baz/bar", []byte("BAR"), 0755) 1871 c.Assert(err, IsNil) 1872 1873 h, err := w.Add("qux") 1874 c.Assert(err, IsNil) 1875 c.Assert(h.IsZero(), Equals, true) 1876 1877 idx, err = w.r.Storer.Index() 1878 c.Assert(err, IsNil) 1879 c.Assert(idx.Entries, HasLen, 11) 1880 1881 e, err := idx.Entry("qux/foo") 1882 c.Assert(err, IsNil) 1883 c.Assert(e.Mode, Equals, filemode.Executable) 1884 1885 e, err = idx.Entry("qux/baz/bar") 1886 c.Assert(err, IsNil) 1887 c.Assert(e.Mode, Equals, filemode.Executable) 1888 1889 status, err := w.Status() 1890 c.Assert(err, IsNil) 1891 c.Assert(status, HasLen, 2) 1892 1893 file := status.File("qux/foo") 1894 c.Assert(file.Staging, Equals, Added) 1895 c.Assert(file.Worktree, Equals, Unmodified) 1896 1897 file = status.File("qux/baz/bar") 1898 c.Assert(file.Staging, Equals, Added) 1899 c.Assert(file.Worktree, Equals, Unmodified) 1900} 1901 1902func (s *WorktreeSuite) TestAddDirectoryErrorNotFound(c *C) { 1903 r, _ := Init(memory.NewStorage(), memfs.New()) 1904 w, _ := r.Worktree() 1905 1906 h, err := w.Add("foo") 1907 c.Assert(err, NotNil) 1908 c.Assert(h.IsZero(), Equals, true) 1909} 1910 1911func (s *WorktreeSuite) TestAddAll(c *C) { 1912 fs := memfs.New() 1913 w := &Worktree{ 1914 r: s.Repository, 1915 Filesystem: fs, 1916 } 1917 1918 err := w.Checkout(&CheckoutOptions{Force: true}) 1919 c.Assert(err, IsNil) 1920 1921 idx, err := w.r.Storer.Index() 1922 c.Assert(err, IsNil) 1923 c.Assert(idx.Entries, HasLen, 9) 1924 1925 err = util.WriteFile(w.Filesystem, "file1", []byte("file1"), 0644) 1926 c.Assert(err, IsNil) 1927 1928 err = util.WriteFile(w.Filesystem, "file2", []byte("file2"), 0644) 1929 c.Assert(err, IsNil) 1930 1931 err = util.WriteFile(w.Filesystem, "file3", []byte("ignore me"), 0644) 1932 c.Assert(err, IsNil) 1933 1934 w.Excludes = make([]gitignore.Pattern, 0) 1935 w.Excludes = append(w.Excludes, gitignore.ParsePattern("file3", nil)) 1936 1937 err = w.AddWithOptions(&AddOptions{All: true}) 1938 c.Assert(err, IsNil) 1939 1940 idx, err = w.r.Storer.Index() 1941 c.Assert(err, IsNil) 1942 c.Assert(idx.Entries, HasLen, 11) 1943 1944 status, err := w.Status() 1945 c.Assert(err, IsNil) 1946 c.Assert(status, HasLen, 2) 1947 1948 file1 := status.File("file1") 1949 c.Assert(file1.Staging, Equals, Added) 1950 file2 := status.File("file2") 1951 c.Assert(file2.Staging, Equals, Added) 1952 file3 := status.File("file3") 1953 c.Assert(file3.Staging, Equals, Untracked) 1954 c.Assert(file3.Worktree, Equals, Untracked) 1955} 1956 1957func (s *WorktreeSuite) TestAddGlob(c *C) { 1958 fs := memfs.New() 1959 w := &Worktree{ 1960 r: s.Repository, 1961 Filesystem: fs, 1962 } 1963 1964 err := w.Checkout(&CheckoutOptions{Force: true}) 1965 c.Assert(err, IsNil) 1966 1967 idx, err := w.r.Storer.Index() 1968 c.Assert(err, IsNil) 1969 c.Assert(idx.Entries, HasLen, 9) 1970 1971 err = util.WriteFile(w.Filesystem, "qux/qux", []byte("QUX"), 0755) 1972 c.Assert(err, IsNil) 1973 err = util.WriteFile(w.Filesystem, "qux/baz", []byte("BAZ"), 0755) 1974 c.Assert(err, IsNil) 1975 err = util.WriteFile(w.Filesystem, "qux/bar/baz", []byte("BAZ"), 0755) 1976 c.Assert(err, IsNil) 1977 1978 err = w.AddWithOptions(&AddOptions{Glob: w.Filesystem.Join("qux", "b*")}) 1979 c.Assert(err, IsNil) 1980 1981 idx, err = w.r.Storer.Index() 1982 c.Assert(err, IsNil) 1983 c.Assert(idx.Entries, HasLen, 11) 1984 1985 e, err := idx.Entry("qux/baz") 1986 c.Assert(err, IsNil) 1987 c.Assert(e.Mode, Equals, filemode.Executable) 1988 1989 e, err = idx.Entry("qux/bar/baz") 1990 c.Assert(err, IsNil) 1991 c.Assert(e.Mode, Equals, filemode.Executable) 1992 1993 status, err := w.Status() 1994 c.Assert(err, IsNil) 1995 c.Assert(status, HasLen, 3) 1996 1997 file := status.File("qux/qux") 1998 c.Assert(file.Staging, Equals, Untracked) 1999 c.Assert(file.Worktree, Equals, Untracked) 2000 2001 file = status.File("qux/baz") 2002 c.Assert(file.Staging, Equals, Added) 2003 c.Assert(file.Worktree, Equals, Unmodified) 2004 2005 file = status.File("qux/bar/baz") 2006 c.Assert(file.Staging, Equals, Added) 2007 c.Assert(file.Worktree, Equals, Unmodified) 2008} 2009 2010func (s *WorktreeSuite) TestAddFilenameStartingWithDot(c *C) { 2011 fs := memfs.New() 2012 w := &Worktree{ 2013 r: s.Repository, 2014 Filesystem: fs, 2015 } 2016 2017 err := w.Checkout(&CheckoutOptions{Force: true}) 2018 c.Assert(err, IsNil) 2019 2020 idx, err := w.r.Storer.Index() 2021 c.Assert(err, IsNil) 2022 c.Assert(idx.Entries, HasLen, 9) 2023 2024 err = util.WriteFile(w.Filesystem, "qux", []byte("QUX"), 0o755) 2025 c.Assert(err, IsNil) 2026 err = util.WriteFile(w.Filesystem, "baz", []byte("BAZ"), 0o755) 2027 c.Assert(err, IsNil) 2028 err = util.WriteFile(w.Filesystem, "foo/bar/baz", []byte("BAZ"), 0o755) 2029 c.Assert(err, IsNil) 2030 2031 _, err = w.Add("./qux") 2032 c.Assert(err, IsNil) 2033 2034 _, err = w.Add("./baz") 2035 c.Assert(err, IsNil) 2036 2037 _, err = w.Add("foo/bar/../bar/./baz") 2038 c.Assert(err, IsNil) 2039 2040 idx, err = w.r.Storer.Index() 2041 c.Assert(err, IsNil) 2042 c.Assert(idx.Entries, HasLen, 12) 2043 2044 e, err := idx.Entry("qux") 2045 c.Assert(err, IsNil) 2046 c.Assert(e.Mode, Equals, filemode.Executable) 2047 2048 e, err = idx.Entry("baz") 2049 c.Assert(err, IsNil) 2050 c.Assert(e.Mode, Equals, filemode.Executable) 2051 2052 status, err := w.Status() 2053 c.Assert(err, IsNil) 2054 c.Assert(status, HasLen, 3) 2055 2056 file := status.File("qux") 2057 c.Assert(file.Staging, Equals, Added) 2058 c.Assert(file.Worktree, Equals, Unmodified) 2059 2060 file = status.File("baz") 2061 c.Assert(file.Staging, Equals, Added) 2062 c.Assert(file.Worktree, Equals, Unmodified) 2063 2064 file = status.File("foo/bar/baz") 2065 c.Assert(file.Staging, Equals, Added) 2066 c.Assert(file.Worktree, Equals, Unmodified) 2067 2068} 2069 2070func (s *WorktreeSuite) TestAddGlobErrorNoMatches(c *C) { 2071 r, _ := Init(memory.NewStorage(), memfs.New()) 2072 w, _ := r.Worktree() 2073 2074 err := w.AddGlob("foo") 2075 c.Assert(err, Equals, ErrGlobNoMatches) 2076} 2077 2078func (s *WorktreeSuite) TestAddSkipStatusAddedPath(c *C) { 2079 fs := memfs.New() 2080 w := &Worktree{ 2081 r: s.Repository, 2082 Filesystem: fs, 2083 } 2084 2085 err := w.Checkout(&CheckoutOptions{Force: true}) 2086 c.Assert(err, IsNil) 2087 2088 idx, err := w.r.Storer.Index() 2089 c.Assert(err, IsNil) 2090 c.Assert(idx.Entries, HasLen, 9) 2091 2092 err = util.WriteFile(w.Filesystem, "file1", []byte("file1"), 0644) 2093 c.Assert(err, IsNil) 2094 2095 err = w.AddWithOptions(&AddOptions{Path: "file1", SkipStatus: true}) 2096 c.Assert(err, IsNil) 2097 2098 idx, err = w.r.Storer.Index() 2099 c.Assert(err, IsNil) 2100 c.Assert(idx.Entries, HasLen, 10) 2101 2102 e, err := idx.Entry("file1") 2103 c.Assert(err, IsNil) 2104 c.Assert(e.Mode, Equals, filemode.Regular) 2105 2106 status, err := w.Status() 2107 c.Assert(err, IsNil) 2108 c.Assert(status, HasLen, 1) 2109 2110 file := status.File("file1") 2111 c.Assert(file.Staging, Equals, Added) 2112 c.Assert(file.Worktree, Equals, Unmodified) 2113} 2114 2115func (s *WorktreeSuite) TestAddSkipStatusModifiedPath(c *C) { 2116 fs := memfs.New() 2117 w := &Worktree{ 2118 r: s.Repository, 2119 Filesystem: fs, 2120 } 2121 2122 err := w.Checkout(&CheckoutOptions{Force: true}) 2123 c.Assert(err, IsNil) 2124 2125 idx, err := w.r.Storer.Index() 2126 c.Assert(err, IsNil) 2127 c.Assert(idx.Entries, HasLen, 9) 2128 2129 err = util.WriteFile(w.Filesystem, "LICENSE", []byte("file1"), 0644) 2130 c.Assert(err, IsNil) 2131 2132 err = w.AddWithOptions(&AddOptions{Path: "LICENSE", SkipStatus: true}) 2133 c.Assert(err, IsNil) 2134 2135 idx, err = w.r.Storer.Index() 2136 c.Assert(err, IsNil) 2137 c.Assert(idx.Entries, HasLen, 9) 2138 2139 e, err := idx.Entry("LICENSE") 2140 c.Assert(err, IsNil) 2141 c.Assert(e.Mode, Equals, filemode.Regular) 2142 2143 status, err := w.Status() 2144 c.Assert(err, IsNil) 2145 c.Assert(status, HasLen, 1) 2146 2147 file := status.File("LICENSE") 2148 c.Assert(file.Staging, Equals, Modified) 2149 c.Assert(file.Worktree, Equals, Unmodified) 2150} 2151 2152func (s *WorktreeSuite) TestAddSkipStatusNonModifiedPath(c *C) { 2153 fs := memfs.New() 2154 w := &Worktree{ 2155 r: s.Repository, 2156 Filesystem: fs, 2157 } 2158 2159 err := w.Checkout(&CheckoutOptions{Force: true}) 2160 c.Assert(err, IsNil) 2161 2162 idx, err := w.r.Storer.Index() 2163 c.Assert(err, IsNil) 2164 c.Assert(idx.Entries, HasLen, 9) 2165 2166 err = w.AddWithOptions(&AddOptions{Path: "LICENSE", SkipStatus: true}) 2167 c.Assert(err, IsNil) 2168 2169 idx, err = w.r.Storer.Index() 2170 c.Assert(err, IsNil) 2171 c.Assert(idx.Entries, HasLen, 9) 2172 2173 e, err := idx.Entry("LICENSE") 2174 c.Assert(err, IsNil) 2175 c.Assert(e.Mode, Equals, filemode.Regular) 2176 2177 status, err := w.Status() 2178 c.Assert(err, IsNil) 2179 c.Assert(status, HasLen, 0) 2180 2181 file := status.File("LICENSE") 2182 c.Assert(file.Staging, Equals, Untracked) 2183 c.Assert(file.Worktree, Equals, Untracked) 2184} 2185 2186func (s *WorktreeSuite) TestAddSkipStatusWithIgnoredPath(c *C) { 2187 fs := memfs.New() 2188 w := &Worktree{ 2189 r: s.Repository, 2190 Filesystem: fs, 2191 } 2192 2193 err := w.Checkout(&CheckoutOptions{Force: true}) 2194 c.Assert(err, IsNil) 2195 2196 idx, err := w.r.Storer.Index() 2197 c.Assert(err, IsNil) 2198 c.Assert(idx.Entries, HasLen, 9) 2199 2200 err = util.WriteFile(fs, ".gitignore", []byte("fileToIgnore\n"), 0755) 2201 c.Assert(err, IsNil) 2202 _, err = w.Add(".gitignore") 2203 c.Assert(err, IsNil) 2204 _, err = w.Commit("Added .gitignore", defaultTestCommitOptions()) 2205 c.Assert(err, IsNil) 2206 2207 err = util.WriteFile(fs, "fileToIgnore", []byte("file to ignore"), 0644) 2208 c.Assert(err, IsNil) 2209 2210 status, err := w.Status() 2211 c.Assert(err, IsNil) 2212 c.Assert(status, HasLen, 0) 2213 2214 file := status.File("fileToIgnore") 2215 c.Assert(file.Staging, Equals, Untracked) 2216 c.Assert(file.Worktree, Equals, Untracked) 2217 2218 err = w.AddWithOptions(&AddOptions{Path: "fileToIgnore", SkipStatus: true}) 2219 c.Assert(err, IsNil) 2220 2221 idx, err = w.r.Storer.Index() 2222 c.Assert(err, IsNil) 2223 c.Assert(idx.Entries, HasLen, 10) 2224 2225 e, err := idx.Entry("fileToIgnore") 2226 c.Assert(err, IsNil) 2227 c.Assert(e.Mode, Equals, filemode.Regular) 2228 2229 status, err = w.Status() 2230 c.Assert(err, IsNil) 2231 c.Assert(status, HasLen, 1) 2232 2233 file = status.File("fileToIgnore") 2234 c.Assert(file.Staging, Equals, Added) 2235 c.Assert(file.Worktree, Equals, Unmodified) 2236} 2237 2238func (s *WorktreeSuite) TestRemove(c *C) { 2239 fs := memfs.New() 2240 w := &Worktree{ 2241 r: s.Repository, 2242 Filesystem: fs, 2243 } 2244 2245 err := w.Checkout(&CheckoutOptions{Force: true}) 2246 c.Assert(err, IsNil) 2247 2248 hash, err := w.Remove("LICENSE") 2249 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 2250 c.Assert(err, IsNil) 2251 2252 status, err := w.Status() 2253 c.Assert(err, IsNil) 2254 c.Assert(status, HasLen, 1) 2255 c.Assert(status.File("LICENSE").Staging, Equals, Deleted) 2256} 2257 2258func (s *WorktreeSuite) TestRemoveNotExistentEntry(c *C) { 2259 fs := memfs.New() 2260 w := &Worktree{ 2261 r: s.Repository, 2262 Filesystem: fs, 2263 } 2264 2265 err := w.Checkout(&CheckoutOptions{Force: true}) 2266 c.Assert(err, IsNil) 2267 2268 hash, err := w.Remove("not-exists") 2269 c.Assert(hash.IsZero(), Equals, true) 2270 c.Assert(err, NotNil) 2271} 2272 2273func (s *WorktreeSuite) TestRemoveDirectory(c *C) { 2274 fs := memfs.New() 2275 w := &Worktree{ 2276 r: s.Repository, 2277 Filesystem: fs, 2278 } 2279 2280 err := w.Checkout(&CheckoutOptions{Force: true}) 2281 c.Assert(err, IsNil) 2282 2283 hash, err := w.Remove("json") 2284 c.Assert(hash.IsZero(), Equals, true) 2285 c.Assert(err, IsNil) 2286 2287 status, err := w.Status() 2288 c.Assert(err, IsNil) 2289 c.Assert(status, HasLen, 2) 2290 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 2291 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 2292 2293 _, err = w.Filesystem.Stat("json") 2294 c.Assert(os.IsNotExist(err), Equals, true) 2295} 2296 2297func (s *WorktreeSuite) TestRemoveDirectoryUntracked(c *C) { 2298 fs := memfs.New() 2299 w := &Worktree{ 2300 r: s.Repository, 2301 Filesystem: fs, 2302 } 2303 2304 err := w.Checkout(&CheckoutOptions{Force: true}) 2305 c.Assert(err, IsNil) 2306 2307 err = util.WriteFile(w.Filesystem, "json/foo", []byte("FOO"), 0755) 2308 c.Assert(err, IsNil) 2309 2310 hash, err := w.Remove("json") 2311 c.Assert(hash.IsZero(), Equals, true) 2312 c.Assert(err, IsNil) 2313 2314 status, err := w.Status() 2315 c.Assert(err, IsNil) 2316 c.Assert(status, HasLen, 3) 2317 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 2318 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 2319 c.Assert(status.File("json/foo").Staging, Equals, Untracked) 2320 2321 _, err = w.Filesystem.Stat("json") 2322 c.Assert(err, IsNil) 2323} 2324 2325func (s *WorktreeSuite) TestRemoveDeletedFromWorktree(c *C) { 2326 fs := memfs.New() 2327 w := &Worktree{ 2328 r: s.Repository, 2329 Filesystem: fs, 2330 } 2331 2332 err := w.Checkout(&CheckoutOptions{Force: true}) 2333 c.Assert(err, IsNil) 2334 2335 err = fs.Remove("LICENSE") 2336 c.Assert(err, IsNil) 2337 2338 hash, err := w.Remove("LICENSE") 2339 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 2340 c.Assert(err, IsNil) 2341 2342 status, err := w.Status() 2343 c.Assert(err, IsNil) 2344 c.Assert(status, HasLen, 1) 2345 c.Assert(status.File("LICENSE").Staging, Equals, Deleted) 2346} 2347 2348func (s *WorktreeSuite) TestRemoveGlob(c *C) { 2349 fs := memfs.New() 2350 w := &Worktree{ 2351 r: s.Repository, 2352 Filesystem: fs, 2353 } 2354 2355 err := w.Checkout(&CheckoutOptions{Force: true}) 2356 c.Assert(err, IsNil) 2357 2358 err = w.RemoveGlob(w.Filesystem.Join("json", "l*")) 2359 c.Assert(err, IsNil) 2360 2361 status, err := w.Status() 2362 c.Assert(err, IsNil) 2363 c.Assert(status, HasLen, 1) 2364 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 2365} 2366 2367func (s *WorktreeSuite) TestRemoveGlobDirectory(c *C) { 2368 fs := memfs.New() 2369 w := &Worktree{ 2370 r: s.Repository, 2371 Filesystem: fs, 2372 } 2373 2374 err := w.Checkout(&CheckoutOptions{Force: true}) 2375 c.Assert(err, IsNil) 2376 2377 err = w.RemoveGlob("js*") 2378 c.Assert(err, IsNil) 2379 2380 status, err := w.Status() 2381 c.Assert(err, IsNil) 2382 c.Assert(status, HasLen, 2) 2383 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 2384 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 2385 2386 _, err = w.Filesystem.Stat("json") 2387 c.Assert(os.IsNotExist(err), Equals, true) 2388} 2389 2390func (s *WorktreeSuite) TestRemoveGlobDirectoryDeleted(c *C) { 2391 fs := memfs.New() 2392 w := &Worktree{ 2393 r: s.Repository, 2394 Filesystem: fs, 2395 } 2396 2397 err := w.Checkout(&CheckoutOptions{Force: true}) 2398 c.Assert(err, IsNil) 2399 2400 err = fs.Remove("json/short.json") 2401 c.Assert(err, IsNil) 2402 2403 err = util.WriteFile(w.Filesystem, "json/foo", []byte("FOO"), 0755) 2404 c.Assert(err, IsNil) 2405 2406 err = w.RemoveGlob("js*") 2407 c.Assert(err, IsNil) 2408 2409 status, err := w.Status() 2410 c.Assert(err, IsNil) 2411 c.Assert(status, HasLen, 3) 2412 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 2413 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 2414} 2415 2416func (s *WorktreeSuite) TestMove(c *C) { 2417 fs := memfs.New() 2418 w := &Worktree{ 2419 r: s.Repository, 2420 Filesystem: fs, 2421 } 2422 2423 err := w.Checkout(&CheckoutOptions{Force: true}) 2424 c.Assert(err, IsNil) 2425 2426 hash, err := w.Move("LICENSE", "foo") 2427 c.Check(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 2428 c.Assert(err, IsNil) 2429 2430 status, err := w.Status() 2431 c.Assert(err, IsNil) 2432 c.Assert(status, HasLen, 2) 2433 c.Assert(status.File("LICENSE").Staging, Equals, Deleted) 2434 c.Assert(status.File("foo").Staging, Equals, Added) 2435 2436} 2437 2438func (s *WorktreeSuite) TestMoveNotExistentEntry(c *C) { 2439 fs := memfs.New() 2440 w := &Worktree{ 2441 r: s.Repository, 2442 Filesystem: fs, 2443 } 2444 2445 err := w.Checkout(&CheckoutOptions{Force: true}) 2446 c.Assert(err, IsNil) 2447 2448 hash, err := w.Move("not-exists", "foo") 2449 c.Assert(hash.IsZero(), Equals, true) 2450 c.Assert(err, NotNil) 2451} 2452 2453func (s *WorktreeSuite) TestMoveToExistent(c *C) { 2454 fs := memfs.New() 2455 w := &Worktree{ 2456 r: s.Repository, 2457 Filesystem: fs, 2458 } 2459 2460 err := w.Checkout(&CheckoutOptions{Force: true}) 2461 c.Assert(err, IsNil) 2462 2463 hash, err := w.Move(".gitignore", "LICENSE") 2464 c.Assert(hash.IsZero(), Equals, true) 2465 c.Assert(err, Equals, ErrDestinationExists) 2466} 2467 2468func (s *WorktreeSuite) TestClean(c *C) { 2469 fs := fixtures.ByTag("dirty").One().Worktree() 2470 2471 // Open the repo. 2472 fs, err := fs.Chroot("repo") 2473 c.Assert(err, IsNil) 2474 r, err := PlainOpen(fs.Root()) 2475 c.Assert(err, IsNil) 2476 2477 wt, err := r.Worktree() 2478 c.Assert(err, IsNil) 2479 2480 // Status before cleaning. 2481 status, err := wt.Status() 2482 c.Assert(err, IsNil) 2483 c.Assert(len(status), Equals, 2) 2484 2485 err = wt.Clean(&CleanOptions{}) 2486 c.Assert(err, IsNil) 2487 2488 // Status after cleaning. 2489 status, err = wt.Status() 2490 c.Assert(err, IsNil) 2491 2492 c.Assert(len(status), Equals, 1) 2493 2494 fi, err := fs.Lstat("pkgA") 2495 c.Assert(err, IsNil) 2496 c.Assert(fi.IsDir(), Equals, true) 2497 2498 // Clean with Dir: true. 2499 err = wt.Clean(&CleanOptions{Dir: true}) 2500 c.Assert(err, IsNil) 2501 2502 status, err = wt.Status() 2503 c.Assert(err, IsNil) 2504 2505 c.Assert(len(status), Equals, 0) 2506 2507 // An empty dir should be deleted, as well. 2508 _, err = fs.Lstat("pkgA") 2509 c.Assert(err, ErrorMatches, ".*(no such file or directory.*|.*file does not exist)*.") 2510} 2511 2512func (s *WorktreeSuite) TestCleanBare(c *C) { 2513 storer := memory.NewStorage() 2514 2515 r, err := Init(storer, nil) 2516 c.Assert(err, IsNil) 2517 c.Assert(r, NotNil) 2518 2519 wtfs := memfs.New() 2520 2521 err = wtfs.MkdirAll("worktree", os.ModePerm) 2522 c.Assert(err, IsNil) 2523 2524 wtfs, err = wtfs.Chroot("worktree") 2525 c.Assert(err, IsNil) 2526 2527 r, err = Open(storer, wtfs) 2528 c.Assert(err, IsNil) 2529 2530 wt, err := r.Worktree() 2531 c.Assert(err, IsNil) 2532 2533 _, err = wt.Filesystem.Lstat(".") 2534 c.Assert(err, IsNil) 2535 2536 // Clean with Dir: true. 2537 err = wt.Clean(&CleanOptions{Dir: true}) 2538 c.Assert(err, IsNil) 2539 2540 // Root worktree directory must remain after cleaning 2541 _, err = wt.Filesystem.Lstat(".") 2542 c.Assert(err, IsNil) 2543} 2544 2545func TestAlternatesRepo(t *testing.T) { 2546 fs := fixtures.ByTag("alternates").One().Worktree() 2547 2548 // Open 1st repo. 2549 rep1fs, err := fs.Chroot("rep1") 2550 assert.NoError(t, err) 2551 rep1, err := PlainOpen(rep1fs.Root()) 2552 assert.NoError(t, err) 2553 2554 // Open 2nd repo. 2555 rep2fs, err := fs.Chroot("rep2") 2556 assert.NoError(t, err) 2557 d, _ := rep2fs.Chroot(GitDirName) 2558 storer := filesystem.NewStorageWithOptions(d, 2559 cache.NewObjectLRUDefault(), filesystem.Options{ 2560 AlternatesFS: fs, 2561 }) 2562 rep2, err := Open(storer, rep2fs) 2563 2564 assert.NoError(t, err) 2565 2566 // Get the HEAD commit from the main repo. 2567 h, err := rep1.Head() 2568 assert.NoError(t, err) 2569 commit1, err := rep1.CommitObject(h.Hash()) 2570 assert.NoError(t, err) 2571 2572 // Get the HEAD commit from the shared repo. 2573 h, err = rep2.Head() 2574 assert.NoError(t, err) 2575 commit2, err := rep2.CommitObject(h.Hash()) 2576 assert.NoError(t, err) 2577 2578 assert.Equal(t, commit1.String(), commit2.String()) 2579} 2580 2581func (s *WorktreeSuite) TestGrep(c *C) { 2582 cases := []struct { 2583 name string 2584 options GrepOptions 2585 wantResult []GrepResult 2586 dontWantResult []GrepResult 2587 wantError error 2588 }{ 2589 { 2590 name: "basic word match", 2591 options: GrepOptions{ 2592 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 2593 }, 2594 wantResult: []GrepResult{ 2595 { 2596 FileName: "go/example.go", 2597 LineNumber: 3, 2598 Content: "import (", 2599 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2600 }, 2601 { 2602 FileName: "vendor/foo.go", 2603 LineNumber: 3, 2604 Content: "import \"fmt\"", 2605 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2606 }, 2607 }, 2608 }, { 2609 name: "case insensitive match", 2610 options: GrepOptions{ 2611 Patterns: []*regexp.Regexp{regexp.MustCompile(`(?i)IMport`)}, 2612 }, 2613 wantResult: []GrepResult{ 2614 { 2615 FileName: "go/example.go", 2616 LineNumber: 3, 2617 Content: "import (", 2618 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2619 }, 2620 { 2621 FileName: "vendor/foo.go", 2622 LineNumber: 3, 2623 Content: "import \"fmt\"", 2624 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2625 }, 2626 }, 2627 }, { 2628 name: "invert match", 2629 options: GrepOptions{ 2630 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 2631 InvertMatch: true, 2632 }, 2633 dontWantResult: []GrepResult{ 2634 { 2635 FileName: "go/example.go", 2636 LineNumber: 3, 2637 Content: "import (", 2638 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2639 }, 2640 { 2641 FileName: "vendor/foo.go", 2642 LineNumber: 3, 2643 Content: "import \"fmt\"", 2644 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2645 }, 2646 }, 2647 }, { 2648 name: "match at a given commit hash", 2649 options: GrepOptions{ 2650 Patterns: []*regexp.Regexp{regexp.MustCompile("The MIT License")}, 2651 CommitHash: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"), 2652 }, 2653 wantResult: []GrepResult{ 2654 { 2655 FileName: "LICENSE", 2656 LineNumber: 1, 2657 Content: "The MIT License (MIT)", 2658 TreeName: "b029517f6300c2da0f4b651b8642506cd6aaf45d", 2659 }, 2660 }, 2661 dontWantResult: []GrepResult{ 2662 { 2663 FileName: "go/example.go", 2664 LineNumber: 3, 2665 Content: "import (", 2666 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2667 }, 2668 }, 2669 }, { 2670 name: "match for a given pathspec", 2671 options: GrepOptions{ 2672 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 2673 PathSpecs: []*regexp.Regexp{regexp.MustCompile("go/")}, 2674 }, 2675 wantResult: []GrepResult{ 2676 { 2677 FileName: "go/example.go", 2678 LineNumber: 3, 2679 Content: "import (", 2680 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2681 }, 2682 }, 2683 dontWantResult: []GrepResult{ 2684 { 2685 FileName: "vendor/foo.go", 2686 LineNumber: 3, 2687 Content: "import \"fmt\"", 2688 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2689 }, 2690 }, 2691 }, { 2692 name: "match at a given reference name", 2693 options: GrepOptions{ 2694 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 2695 ReferenceName: "refs/heads/master", 2696 }, 2697 wantResult: []GrepResult{ 2698 { 2699 FileName: "go/example.go", 2700 LineNumber: 3, 2701 Content: "import (", 2702 TreeName: "refs/heads/master", 2703 }, 2704 }, 2705 }, { 2706 name: "ambiguous options", 2707 options: GrepOptions{ 2708 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 2709 CommitHash: plumbing.NewHash("2d55a722f3c3ecc36da919dfd8b6de38352f3507"), 2710 ReferenceName: "somereferencename", 2711 }, 2712 wantError: ErrHashOrReference, 2713 }, { 2714 name: "multiple patterns", 2715 options: GrepOptions{ 2716 Patterns: []*regexp.Regexp{ 2717 regexp.MustCompile("import"), 2718 regexp.MustCompile("License"), 2719 }, 2720 }, 2721 wantResult: []GrepResult{ 2722 { 2723 FileName: "go/example.go", 2724 LineNumber: 3, 2725 Content: "import (", 2726 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2727 }, 2728 { 2729 FileName: "vendor/foo.go", 2730 LineNumber: 3, 2731 Content: "import \"fmt\"", 2732 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2733 }, 2734 { 2735 FileName: "LICENSE", 2736 LineNumber: 1, 2737 Content: "The MIT License (MIT)", 2738 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2739 }, 2740 }, 2741 }, { 2742 name: "multiple pathspecs", 2743 options: GrepOptions{ 2744 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 2745 PathSpecs: []*regexp.Regexp{ 2746 regexp.MustCompile("go/"), 2747 regexp.MustCompile("vendor/"), 2748 }, 2749 }, 2750 wantResult: []GrepResult{ 2751 { 2752 FileName: "go/example.go", 2753 LineNumber: 3, 2754 Content: "import (", 2755 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2756 }, 2757 { 2758 FileName: "vendor/foo.go", 2759 LineNumber: 3, 2760 Content: "import \"fmt\"", 2761 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2762 }, 2763 }, 2764 }, 2765 } 2766 2767 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 2768 2769 dir := c.MkDir() 2770 2771 server, err := PlainClone(dir, false, &CloneOptions{ 2772 URL: path, 2773 }) 2774 c.Assert(err, IsNil) 2775 2776 w, err := server.Worktree() 2777 c.Assert(err, IsNil) 2778 2779 for _, tc := range cases { 2780 gr, err := w.Grep(&tc.options) 2781 if tc.wantError != nil { 2782 c.Assert(err, Equals, tc.wantError) 2783 } else { 2784 c.Assert(err, IsNil) 2785 } 2786 2787 // Iterate through the results and check if the wanted result is present 2788 // in the got result. 2789 for _, wantResult := range tc.wantResult { 2790 found := false 2791 for _, gotResult := range gr { 2792 if wantResult == gotResult { 2793 found = true 2794 break 2795 } 2796 } 2797 if !found { 2798 c.Errorf("unexpected grep results for %q, expected result to contain: %v", tc.name, wantResult) 2799 } 2800 } 2801 2802 // Iterate through the results and check if the not wanted result is 2803 // present in the got result. 2804 for _, dontWantResult := range tc.dontWantResult { 2805 found := false 2806 for _, gotResult := range gr { 2807 if dontWantResult == gotResult { 2808 found = true 2809 break 2810 } 2811 } 2812 if found { 2813 c.Errorf("unexpected grep results for %q, expected result to NOT contain: %v", tc.name, dontWantResult) 2814 } 2815 } 2816 } 2817} 2818 2819func (s *WorktreeSuite) TestGrepBare(c *C) { 2820 cases := []struct { 2821 name string 2822 options GrepOptions 2823 wantResult []GrepResult 2824 dontWantResult []GrepResult 2825 wantError error 2826 }{ 2827 { 2828 name: "basic word match", 2829 options: GrepOptions{ 2830 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 2831 CommitHash: plumbing.ZeroHash, 2832 }, 2833 wantResult: []GrepResult{ 2834 { 2835 FileName: "go/example.go", 2836 LineNumber: 3, 2837 Content: "import (", 2838 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2839 }, 2840 { 2841 FileName: "vendor/foo.go", 2842 LineNumber: 3, 2843 Content: "import \"fmt\"", 2844 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 2845 }, 2846 }, 2847 }, 2848 } 2849 2850 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 2851 2852 dir := c.MkDir() 2853 2854 r, err := PlainClone(dir, true, &CloneOptions{ 2855 URL: path, 2856 }) 2857 c.Assert(err, IsNil) 2858 2859 for _, tc := range cases { 2860 gr, err := r.Grep(&tc.options) 2861 if tc.wantError != nil { 2862 c.Assert(err, Equals, tc.wantError) 2863 } else { 2864 c.Assert(err, IsNil) 2865 } 2866 2867 // Iterate through the results and check if the wanted result is present 2868 // in the got result. 2869 for _, wantResult := range tc.wantResult { 2870 found := false 2871 for _, gotResult := range gr { 2872 if wantResult == gotResult { 2873 found = true 2874 break 2875 } 2876 } 2877 if !found { 2878 c.Errorf("unexpected grep results for %q, expected result to contain: %v", tc.name, wantResult) 2879 } 2880 } 2881 2882 // Iterate through the results and check if the not wanted result is 2883 // present in the got result. 2884 for _, dontWantResult := range tc.dontWantResult { 2885 found := false 2886 for _, gotResult := range gr { 2887 if dontWantResult == gotResult { 2888 found = true 2889 break 2890 } 2891 } 2892 if found { 2893 c.Errorf("unexpected grep results for %q, expected result to NOT contain: %v", tc.name, dontWantResult) 2894 } 2895 } 2896 } 2897} 2898 2899func (s *WorktreeSuite) TestResetLingeringDirectories(c *C) { 2900 dir := c.MkDir() 2901 2902 commitOpts := &CommitOptions{Author: &object.Signature{ 2903 Name: "foo", 2904 Email: "foo@foo.foo", 2905 When: time.Now(), 2906 }} 2907 2908 repo, err := PlainInit(dir, false) 2909 c.Assert(err, IsNil) 2910 2911 w, err := repo.Worktree() 2912 c.Assert(err, IsNil) 2913 2914 os.WriteFile(filepath.Join(dir, "README"), []byte("placeholder"), 0o644) 2915 2916 _, err = w.Add(".") 2917 c.Assert(err, IsNil) 2918 2919 initialHash, err := w.Commit("Initial commit", commitOpts) 2920 c.Assert(err, IsNil) 2921 2922 os.MkdirAll(filepath.Join(dir, "a", "b"), 0o755) 2923 os.WriteFile(filepath.Join(dir, "a", "b", "1"), []byte("1"), 0o644) 2924 2925 _, err = w.Add(".") 2926 c.Assert(err, IsNil) 2927 2928 _, err = w.Commit("Add file in nested sub-directories", commitOpts) 2929 c.Assert(err, IsNil) 2930 2931 // reset to initial commit, which should remove a/b/1, a/b, and a 2932 err = w.Reset(&ResetOptions{ 2933 Commit: initialHash, 2934 Mode: HardReset, 2935 }) 2936 c.Assert(err, IsNil) 2937 2938 _, err = os.Stat(filepath.Join(dir, "a", "b", "1")) 2939 c.Assert(errors.Is(err, os.ErrNotExist), Equals, true) 2940 2941 _, err = os.Stat(filepath.Join(dir, "a", "b")) 2942 c.Assert(errors.Is(err, os.ErrNotExist), Equals, true) 2943 2944 _, err = os.Stat(filepath.Join(dir, "a")) 2945 c.Assert(errors.Is(err, os.ErrNotExist), Equals, true) 2946} 2947 2948func (s *WorktreeSuite) TestAddAndCommit(c *C) { 2949 expectedFiles := 2 2950 2951 dir := c.MkDir() 2952 2953 repo, err := PlainInit(dir, false) 2954 c.Assert(err, IsNil) 2955 2956 w, err := repo.Worktree() 2957 c.Assert(err, IsNil) 2958 2959 os.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0o644) 2960 os.WriteFile(filepath.Join(dir, "bar"), []byte("foo"), 0o644) 2961 2962 _, err = w.Add(".") 2963 c.Assert(err, IsNil) 2964 2965 _, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{ 2966 Name: "foo", 2967 Email: "foo@foo.foo", 2968 When: time.Now(), 2969 }}) 2970 c.Assert(err, IsNil) 2971 2972 iter, err := w.r.Log(&LogOptions{}) 2973 c.Assert(err, IsNil) 2974 2975 filesFound := 0 2976 err = iter.ForEach(func(c *object.Commit) error { 2977 files, err := c.Files() 2978 if err != nil { 2979 return err 2980 } 2981 2982 err = files.ForEach(func(f *object.File) error { 2983 filesFound++ 2984 return nil 2985 }) 2986 return err 2987 }) 2988 c.Assert(err, IsNil) 2989 c.Assert(filesFound, Equals, expectedFiles) 2990} 2991 2992func (s *WorktreeSuite) TestAddAndCommitEmpty(c *C) { 2993 dir := c.MkDir() 2994 2995 repo, err := PlainInit(dir, false) 2996 c.Assert(err, IsNil) 2997 2998 w, err := repo.Worktree() 2999 c.Assert(err, IsNil) 3000 3001 _, err = w.Add(".") 3002 c.Assert(err, IsNil) 3003 3004 _, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{ 3005 Name: "foo", 3006 Email: "foo@foo.foo", 3007 When: time.Now(), 3008 }}) 3009 c.Assert(err, Equals, ErrEmptyCommit) 3010} 3011 3012func (s *WorktreeSuite) TestLinkedWorktree(c *C) { 3013 fs := fixtures.ByTag("linked-worktree").One().Worktree() 3014 3015 // Open main repo. 3016 { 3017 fs, err := fs.Chroot("main") 3018 c.Assert(err, IsNil) 3019 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 3020 c.Assert(err, IsNil) 3021 3022 wt, err := repo.Worktree() 3023 c.Assert(err, IsNil) 3024 3025 status, err := wt.Status() 3026 c.Assert(err, IsNil) 3027 c.Assert(len(status), Equals, 2) // 2 files 3028 3029 head, err := repo.Head() 3030 c.Assert(err, IsNil) 3031 c.Assert(string(head.Name()), Equals, "refs/heads/master") 3032 } 3033 3034 // Open linked-worktree #1. 3035 { 3036 fs, err := fs.Chroot("linked-worktree-1") 3037 c.Assert(err, IsNil) 3038 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 3039 c.Assert(err, IsNil) 3040 3041 wt, err := repo.Worktree() 3042 c.Assert(err, IsNil) 3043 3044 status, err := wt.Status() 3045 c.Assert(err, IsNil) 3046 c.Assert(len(status), Equals, 3) // 3 files 3047 3048 _, ok := status["linked-worktree-1-unique-file.txt"] 3049 c.Assert(ok, Equals, true) 3050 3051 head, err := repo.Head() 3052 c.Assert(err, IsNil) 3053 c.Assert(string(head.Name()), Equals, "refs/heads/linked-worktree-1") 3054 } 3055 3056 // Open linked-worktree #2. 3057 { 3058 fs, err := fs.Chroot("linked-worktree-2") 3059 c.Assert(err, IsNil) 3060 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 3061 c.Assert(err, IsNil) 3062 3063 wt, err := repo.Worktree() 3064 c.Assert(err, IsNil) 3065 3066 status, err := wt.Status() 3067 c.Assert(err, IsNil) 3068 c.Assert(len(status), Equals, 3) // 3 files 3069 3070 _, ok := status["linked-worktree-2-unique-file.txt"] 3071 c.Assert(ok, Equals, true) 3072 3073 head, err := repo.Head() 3074 c.Assert(err, IsNil) 3075 c.Assert(string(head.Name()), Equals, "refs/heads/branch-with-different-name") 3076 } 3077 3078 // Open linked-worktree #2. 3079 { 3080 fs, err := fs.Chroot("linked-worktree-invalid-commondir") 3081 c.Assert(err, IsNil) 3082 _, err = PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 3083 c.Assert(err, Equals, ErrRepositoryIncomplete) 3084 } 3085} 3086 3087func TestValidPath(t *testing.T) { 3088 type testcase struct { 3089 path string 3090 wantErr bool 3091 } 3092 3093 tests := []testcase{ 3094 {".git", true}, 3095 {".git/b", true}, 3096 {".git\\b", true}, 3097 {"git~1", true}, 3098 {"a/../b", true}, 3099 {"a\\..\\b", true}, 3100 {"/", true}, 3101 {"", true}, 3102 {".gitmodules", false}, 3103 {".gitignore", false}, 3104 {"a..b", false}, 3105 {".", false}, 3106 {"a/.git", false}, 3107 {"a\\.git", false}, 3108 {"a/.git/b", false}, 3109 {"a\\.git\\b", false}, 3110 } 3111 3112 if runtime.GOOS == "windows" { 3113 tests = append(tests, []testcase{ 3114 {"\\\\a\\b", true}, 3115 {"C:\\a\\b", true}, 3116 {".git . . .", true}, 3117 {".git . . ", true}, 3118 {".git ", true}, 3119 {".git.", true}, 3120 {".git::$INDEX_ALLOCATION", true}, 3121 }...) 3122 } 3123 3124 for _, tc := range tests { 3125 t.Run(tc.path, func(t *testing.T) { 3126 err := validPath(tc.path) 3127 if tc.wantErr { 3128 assert.Error(t, err) 3129 } else { 3130 assert.NoError(t, err) 3131 } 3132 }) 3133 } 3134} 3135 3136func TestWindowsValidPath(t *testing.T) { 3137 tests := []struct { 3138 path string 3139 want bool 3140 }{ 3141 {".git", false}, 3142 {".git . . .", false}, 3143 {".git ", false}, 3144 {".git ", false}, 3145 {".git . .", false}, 3146 {".git . .", false}, 3147 {".git::$INDEX_ALLOCATION", false}, 3148 {".git:", false}, 3149 {"a", true}, 3150 {"a\\b", true}, 3151 {"a/b", true}, 3152 {".gitm", true}, 3153 } 3154 3155 for _, tc := range tests { 3156 t.Run(tc.path, func(t *testing.T) { 3157 got := windowsValidPath(tc.path) 3158 assert.Equal(t, tc.want, got) 3159 }) 3160 } 3161} 3162 3163var statusCodeNames = map[StatusCode]string{ 3164 Unmodified: "Unmodified", 3165 Untracked: "Untracked", 3166 Modified: "Modified", 3167 Added: "Added", 3168 Deleted: "Deleted", 3169 Renamed: "Renamed", 3170 Copied: "Copied", 3171 UpdatedButUnmerged: "UpdatedButUnmerged", 3172} 3173 3174func setupForRestore(c *C, s *WorktreeSuite) (fs billy.Filesystem, w *Worktree, names []string) { 3175 fs = memfs.New() 3176 w = &Worktree{ 3177 r: s.Repository, 3178 Filesystem: fs, 3179 } 3180 3181 err := w.Checkout(&CheckoutOptions{}) 3182 c.Assert(err, IsNil) 3183 3184 names = []string{"foo", "CHANGELOG", "LICENSE", "binary.jpg"} 3185 verifyStatus(c, "Checkout", w, names, []FileStatus{ 3186 {Worktree: Untracked, Staging: Untracked}, 3187 {Worktree: Untracked, Staging: Untracked}, 3188 {Worktree: Untracked, Staging: Untracked}, 3189 {Worktree: Untracked, Staging: Untracked}, 3190 }) 3191 3192 // Touch of bunch of files including create a new file and delete an exsiting file 3193 for _, name := range names { 3194 err = util.WriteFile(fs, name, []byte("Foo Bar"), 0755) 3195 c.Assert(err, IsNil) 3196 } 3197 err = util.RemoveAll(fs, names[3]) 3198 c.Assert(err, IsNil) 3199 3200 // Confirm the status after doing the edits without staging anything 3201 verifyStatus(c, "Edits", w, names, []FileStatus{ 3202 {Worktree: Untracked, Staging: Untracked}, 3203 {Worktree: Modified, Staging: Unmodified}, 3204 {Worktree: Modified, Staging: Unmodified}, 3205 {Worktree: Deleted, Staging: Unmodified}, 3206 }) 3207 3208 // Stage all files and verify the updated status 3209 for _, name := range names { 3210 _, err = w.Add(name) 3211 c.Assert(err, IsNil) 3212 } 3213 verifyStatus(c, "Staged", w, names, []FileStatus{ 3214 {Worktree: Unmodified, Staging: Added}, 3215 {Worktree: Unmodified, Staging: Modified}, 3216 {Worktree: Unmodified, Staging: Modified}, 3217 {Worktree: Unmodified, Staging: Deleted}, 3218 }) 3219 3220 // Add secondary changes to a file to make sure we only restore the staged file 3221 err = util.WriteFile(fs, names[1], []byte("Foo Bar:11"), 0755) 3222 c.Assert(err, IsNil) 3223 err = util.WriteFile(fs, names[2], []byte("Foo Bar:22"), 0755) 3224 c.Assert(err, IsNil) 3225 3226 verifyStatus(c, "Secondary Edits", w, names, []FileStatus{ 3227 {Worktree: Unmodified, Staging: Added}, 3228 {Worktree: Modified, Staging: Modified}, 3229 {Worktree: Modified, Staging: Modified}, 3230 {Worktree: Unmodified, Staging: Deleted}, 3231 }) 3232 3233 return 3234} 3235 3236func verifyStatus(c *C, marker string, w *Worktree, files []string, statuses []FileStatus) { 3237 c.Assert(len(files), Equals, len(statuses)) 3238 3239 status, err := w.Status() 3240 c.Assert(err, IsNil) 3241 3242 for i, file := range files { 3243 current := status.File(file) 3244 expected := statuses[i] 3245 c.Assert(current.Worktree, Equals, expected.Worktree, Commentf("%s - [%d] : %s Worktree %s != %s", marker, i, file, statusCodeNames[current.Worktree], statusCodeNames[expected.Worktree])) 3246 c.Assert(current.Staging, Equals, expected.Staging, Commentf("%s - [%d] : %s Staging %s != %s", marker, i, file, statusCodeNames[current.Staging], statusCodeNames[expected.Staging])) 3247 } 3248} 3249 3250func (s *WorktreeSuite) TestRestoreStaged(c *C) { 3251 fs, w, names := setupForRestore(c, s) 3252 3253 // Attempt without files should throw an error like the git restore --staged 3254 opts := RestoreOptions{Staged: true} 3255 err := w.Restore(&opts) 3256 c.Assert(err, Equals, ErrNoRestorePaths) 3257 3258 // Restore Staged files in 2 groups and confirm status 3259 opts.Files = []string{names[0], "./" + names[1]} 3260 err = w.Restore(&opts) 3261 c.Assert(err, IsNil) 3262 verifyStatus(c, "Restored First", w, names, []FileStatus{ 3263 {Worktree: Untracked, Staging: Untracked}, 3264 {Worktree: Modified, Staging: Unmodified}, 3265 {Worktree: Modified, Staging: Modified}, 3266 {Worktree: Unmodified, Staging: Deleted}, 3267 }) 3268 3269 // Make sure the restore didn't overwrite our secondary changes 3270 contents, err := util.ReadFile(fs, names[1]) 3271 c.Assert(err, IsNil) 3272 c.Assert(string(contents), Equals, "Foo Bar:11") 3273 3274 opts.Files = []string{"./" + names[2], names[3]} 3275 err = w.Restore(&opts) 3276 c.Assert(err, IsNil) 3277 verifyStatus(c, "Restored Second", w, names, []FileStatus{ 3278 {Worktree: Untracked, Staging: Untracked}, 3279 {Worktree: Modified, Staging: Unmodified}, 3280 {Worktree: Modified, Staging: Unmodified}, 3281 {Worktree: Deleted, Staging: Unmodified}, 3282 }) 3283 3284 // Make sure the restore didn't overwrite our secondary changes 3285 contents, err = util.ReadFile(fs, names[2]) 3286 c.Assert(err, IsNil) 3287 c.Assert(string(contents), Equals, "Foo Bar:22") 3288} 3289 3290func (s *WorktreeSuite) TestRestoreWorktree(c *C) { 3291 _, w, names := setupForRestore(c, s) 3292 3293 // Attempt without files should throw an error like the git restore 3294 opts := RestoreOptions{} 3295 err := w.Restore(&opts) 3296 c.Assert(err, Equals, ErrNoRestorePaths) 3297 3298 opts.Files = []string{names[0], names[1]} 3299 err = w.Restore(&opts) 3300 c.Assert(err, Equals, ErrRestoreWorktreeOnlyNotSupported) 3301} 3302 3303func (s *WorktreeSuite) TestRestoreBoth(c *C) { 3304 _, w, names := setupForRestore(c, s) 3305 3306 // Attempt without files should throw an error like the git restore --staged --worktree 3307 opts := RestoreOptions{Staged: true, Worktree: true} 3308 err := w.Restore(&opts) 3309 c.Assert(err, Equals, ErrNoRestorePaths) 3310 3311 // Restore Staged files in 2 groups and confirm status 3312 opts.Files = []string{names[0], names[1]} 3313 err = w.Restore(&opts) 3314 c.Assert(err, IsNil) 3315 verifyStatus(c, "Restored First", w, names, []FileStatus{ 3316 {Worktree: Untracked, Staging: Untracked}, 3317 {Worktree: Untracked, Staging: Untracked}, 3318 {Worktree: Modified, Staging: Modified}, 3319 {Worktree: Unmodified, Staging: Deleted}, 3320 }) 3321 3322 opts.Files = []string{names[2], names[3]} 3323 err = w.Restore(&opts) 3324 c.Assert(err, IsNil) 3325 verifyStatus(c, "Restored Second", w, names, []FileStatus{ 3326 {Worktree: Untracked, Staging: Untracked}, 3327 {Worktree: Untracked, Staging: Untracked}, 3328 {Worktree: Untracked, Staging: Untracked}, 3329 {Worktree: Untracked, Staging: Untracked}, 3330 }) 3331} 3332 3333func TestFilePermissions(t *testing.T) { 3334 3335 // Initialize an in memory repository 3336 remoteUrl := t.TempDir() 3337 3338 inMemoryFs := memfs.New() 3339 remoteFs := osfs.New(remoteUrl) 3340 remoteStorage := filesystem.NewStorage(remoteFs, cache.NewObjectLRUDefault()) 3341 3342 remoteRepository, err := Init(remoteStorage, inMemoryFs) 3343 assert.NoError(t, err) 3344 3345 err = util.WriteFile(inMemoryFs, "fileWithExecuteBit", []byte("Initial data"), 0755) 3346 assert.NoError(t, err) 3347 3348 err = util.WriteFile(inMemoryFs, "regularFile", []byte("Initial data"), 0644) 3349 assert.NoError(t, err) 3350 3351 remoteWorktree, err := remoteRepository.Worktree() 3352 assert.NoError(t, err) 3353 3354 _, err = remoteWorktree.Add("fileWithExecuteBit") 3355 assert.NoError(t, err) 3356 3357 _, err = remoteWorktree.Add("regularFile") 3358 assert.NoError(t, err) 3359 3360 _, err = remoteWorktree.Commit("my commit", &CommitOptions{}) 3361 assert.NoError(t, err) 3362 3363 worktreePath := t.TempDir() 3364 3365 localRepo, err := PlainClone(worktreePath, false, &CloneOptions{URL: remoteUrl}) 3366 assert.NoError(t, err) 3367 3368 localWorktree, err := localRepo.Worktree() 3369 assert.NoError(t, err) 3370 3371 idx, err := localWorktree.r.Storer.Index() 3372 assert.NoError(t, err) 3373 3374 expectedEntries := []index.Entry{ 3375 { 3376 Name: "fileWithExecuteBit", 3377 Mode: filemode.Executable, 3378 }, 3379 { 3380 Name: "regularFile", 3381 Mode: filemode.Regular, 3382 }, 3383 } 3384 3385 assert.Len(t, idx.Entries, len(expectedEntries)) 3386 3387 for i, expectedEntry := range expectedEntries { 3388 assert.Equal(t, expectedEntry.Name, idx.Entries[i].Name) 3389 assert.Equal(t, expectedEntry.Mode, idx.Entries[i].Mode) 3390 } 3391 3392}