1package git 2 3import ( 4 "bytes" 5 "context" 6 "errors" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "regexp" 11 "runtime" 12 "testing" 13 "time" 14 15 fixtures "github.com/go-git/go-git-fixtures/v4" 16 "github.com/go-git/go-git/v5/config" 17 "github.com/go-git/go-git/v5/plumbing" 18 "github.com/go-git/go-git/v5/plumbing/filemode" 19 "github.com/go-git/go-git/v5/plumbing/format/gitignore" 20 "github.com/go-git/go-git/v5/plumbing/format/index" 21 "github.com/go-git/go-git/v5/plumbing/object" 22 "github.com/go-git/go-git/v5/storage/memory" 23 24 "github.com/go-git/go-billy/v5/memfs" 25 "github.com/go-git/go-billy/v5/osfs" 26 "github.com/go-git/go-billy/v5/util" 27 "golang.org/x/text/unicode/norm" 28 . "gopkg.in/check.v1" 29) 30 31type WorktreeSuite struct { 32 BaseSuite 33} 34 35var _ = Suite(&WorktreeSuite{}) 36 37func (s *WorktreeSuite) SetUpTest(c *C) { 38 f := fixtures.Basic().One() 39 s.Repository = s.NewRepositoryWithEmptyWorktree(f) 40} 41 42func (s *WorktreeSuite) TestPullCheckout(c *C) { 43 fs := memfs.New() 44 r, _ := Init(memory.NewStorage(), fs) 45 r.CreateRemote(&config.RemoteConfig{ 46 Name: DefaultRemoteName, 47 URLs: []string{s.GetBasicLocalRepositoryURL()}, 48 }) 49 50 w, err := r.Worktree() 51 c.Assert(err, IsNil) 52 53 err = w.Pull(&PullOptions{}) 54 c.Assert(err, IsNil) 55 56 fi, err := fs.ReadDir("") 57 c.Assert(err, IsNil) 58 c.Assert(fi, HasLen, 8) 59} 60 61func (s *WorktreeSuite) TestPullFastForward(c *C) { 62 url := c.MkDir() 63 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 64 65 server, err := PlainClone(url, false, &CloneOptions{ 66 URL: path, 67 }) 68 c.Assert(err, IsNil) 69 70 r, err := PlainClone(c.MkDir(), false, &CloneOptions{ 71 URL: url, 72 }) 73 c.Assert(err, IsNil) 74 75 w, err := server.Worktree() 76 c.Assert(err, IsNil) 77 err = ioutil.WriteFile(filepath.Join(path, "foo"), []byte("foo"), 0755) 78 c.Assert(err, IsNil) 79 hash, err := w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 80 c.Assert(err, IsNil) 81 82 w, err = r.Worktree() 83 c.Assert(err, IsNil) 84 85 err = w.Pull(&PullOptions{}) 86 c.Assert(err, IsNil) 87 88 head, err := r.Head() 89 c.Assert(err, IsNil) 90 c.Assert(head.Hash(), Equals, hash) 91} 92 93func (s *WorktreeSuite) TestPullNonFastForward(c *C) { 94 url := c.MkDir() 95 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 96 97 server, err := PlainClone(url, false, &CloneOptions{ 98 URL: path, 99 }) 100 c.Assert(err, IsNil) 101 102 r, err := PlainClone(c.MkDir(), false, &CloneOptions{ 103 URL: url, 104 }) 105 c.Assert(err, IsNil) 106 107 w, err := server.Worktree() 108 c.Assert(err, IsNil) 109 err = ioutil.WriteFile(filepath.Join(path, "foo"), []byte("foo"), 0755) 110 c.Assert(err, IsNil) 111 _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 112 c.Assert(err, IsNil) 113 114 w, err = r.Worktree() 115 c.Assert(err, IsNil) 116 err = ioutil.WriteFile(filepath.Join(path, "bar"), []byte("bar"), 0755) 117 c.Assert(err, IsNil) 118 _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()}) 119 c.Assert(err, IsNil) 120 121 err = w.Pull(&PullOptions{}) 122 c.Assert(err, Equals, ErrNonFastForwardUpdate) 123} 124 125func (s *WorktreeSuite) TestPullUpdateReferencesIfNeeded(c *C) { 126 r, _ := Init(memory.NewStorage(), memfs.New()) 127 r.CreateRemote(&config.RemoteConfig{ 128 Name: DefaultRemoteName, 129 URLs: []string{s.GetBasicLocalRepositoryURL()}, 130 }) 131 132 err := r.Fetch(&FetchOptions{}) 133 c.Assert(err, IsNil) 134 135 _, err = r.Reference("refs/heads/master", false) 136 c.Assert(err, NotNil) 137 138 w, err := r.Worktree() 139 c.Assert(err, IsNil) 140 141 err = w.Pull(&PullOptions{}) 142 c.Assert(err, IsNil) 143 144 head, err := r.Reference(plumbing.HEAD, true) 145 c.Assert(err, IsNil) 146 c.Assert(head.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 147 148 branch, err := r.Reference("refs/heads/master", false) 149 c.Assert(err, IsNil) 150 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 151 152 err = w.Pull(&PullOptions{}) 153 c.Assert(err, Equals, NoErrAlreadyUpToDate) 154} 155 156func (s *WorktreeSuite) TestPullInSingleBranch(c *C) { 157 r, _ := Init(memory.NewStorage(), memfs.New()) 158 err := r.clone(context.Background(), &CloneOptions{ 159 URL: s.GetBasicLocalRepositoryURL(), 160 SingleBranch: true, 161 }) 162 163 c.Assert(err, IsNil) 164 165 w, err := r.Worktree() 166 c.Assert(err, IsNil) 167 168 err = w.Pull(&PullOptions{}) 169 c.Assert(err, Equals, NoErrAlreadyUpToDate) 170 171 branch, err := r.Reference("refs/heads/master", false) 172 c.Assert(err, IsNil) 173 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 174 175 branch, err = r.Reference("refs/remotes/foo/branch", false) 176 c.Assert(err, NotNil) 177 178 storage := r.Storer.(*memory.Storage) 179 c.Assert(storage.Objects, HasLen, 28) 180} 181 182func (s *WorktreeSuite) TestPullProgress(c *C) { 183 r, _ := Init(memory.NewStorage(), memfs.New()) 184 185 r.CreateRemote(&config.RemoteConfig{ 186 Name: DefaultRemoteName, 187 URLs: []string{s.GetBasicLocalRepositoryURL()}, 188 }) 189 190 w, err := r.Worktree() 191 c.Assert(err, IsNil) 192 193 buf := bytes.NewBuffer(nil) 194 err = w.Pull(&PullOptions{ 195 Progress: buf, 196 }) 197 198 c.Assert(err, IsNil) 199 c.Assert(buf.Len(), Not(Equals), 0) 200} 201 202func (s *WorktreeSuite) TestPullProgressWithRecursion(c *C) { 203 if testing.Short() { 204 c.Skip("skipping test in short mode.") 205 } 206 207 path := fixtures.ByTag("submodule").One().Worktree().Root() 208 209 dir, err := ioutil.TempDir("", "plain-clone-submodule") 210 c.Assert(err, IsNil) 211 defer os.RemoveAll(dir) 212 213 r, _ := PlainInit(dir, false) 214 r.CreateRemote(&config.RemoteConfig{ 215 Name: DefaultRemoteName, 216 URLs: []string{path}, 217 }) 218 219 w, err := r.Worktree() 220 c.Assert(err, IsNil) 221 222 err = w.Pull(&PullOptions{ 223 RecurseSubmodules: DefaultSubmoduleRecursionDepth, 224 }) 225 c.Assert(err, IsNil) 226 227 cfg, err := r.Config() 228 c.Assert(err, IsNil) 229 c.Assert(cfg.Submodules, HasLen, 2) 230} 231 232func (s *RepositorySuite) TestPullAdd(c *C) { 233 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 234 235 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 236 URL: filepath.Join(path, ".git"), 237 }) 238 239 c.Assert(err, IsNil) 240 241 storage := r.Storer.(*memory.Storage) 242 c.Assert(storage.Objects, HasLen, 28) 243 244 branch, err := r.Reference("refs/heads/master", false) 245 c.Assert(err, IsNil) 246 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 247 248 ExecuteOnPath(c, path, 249 "touch foo", 250 "git add foo", 251 "git commit -m foo foo", 252 ) 253 254 w, err := r.Worktree() 255 c.Assert(err, IsNil) 256 257 err = w.Pull(&PullOptions{RemoteName: "origin"}) 258 c.Assert(err, IsNil) 259 260 // the commit command has introduced a new commit, tree and blob 261 c.Assert(storage.Objects, HasLen, 31) 262 263 branch, err = r.Reference("refs/heads/master", false) 264 c.Assert(err, IsNil) 265 c.Assert(branch.Hash().String(), Not(Equals), "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") 266} 267 268func (s *WorktreeSuite) TestPullAlreadyUptodate(c *C) { 269 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 270 271 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 272 URL: filepath.Join(path, ".git"), 273 }) 274 275 c.Assert(err, IsNil) 276 277 w, err := r.Worktree() 278 c.Assert(err, IsNil) 279 err = ioutil.WriteFile(filepath.Join(path, "bar"), []byte("bar"), 0755) 280 c.Assert(err, IsNil) 281 _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()}) 282 c.Assert(err, IsNil) 283 284 err = w.Pull(&PullOptions{}) 285 c.Assert(err, Equals, NoErrAlreadyUpToDate) 286} 287 288func (s *WorktreeSuite) TestCheckout(c *C) { 289 fs := memfs.New() 290 w := &Worktree{ 291 r: s.Repository, 292 Filesystem: fs, 293 } 294 295 err := w.Checkout(&CheckoutOptions{ 296 Force: true, 297 }) 298 c.Assert(err, IsNil) 299 300 entries, err := fs.ReadDir("/") 301 c.Assert(err, IsNil) 302 303 c.Assert(entries, HasLen, 8) 304 ch, err := fs.Open("CHANGELOG") 305 c.Assert(err, IsNil) 306 307 content, err := ioutil.ReadAll(ch) 308 c.Assert(err, IsNil) 309 c.Assert(string(content), Equals, "Initial changelog\n") 310 311 idx, err := s.Repository.Storer.Index() 312 c.Assert(err, IsNil) 313 c.Assert(idx.Entries, HasLen, 9) 314} 315 316func (s *WorktreeSuite) TestCheckoutForce(c *C) { 317 w := &Worktree{ 318 r: s.Repository, 319 Filesystem: memfs.New(), 320 } 321 322 err := w.Checkout(&CheckoutOptions{}) 323 c.Assert(err, IsNil) 324 325 w.Filesystem = memfs.New() 326 327 err = w.Checkout(&CheckoutOptions{ 328 Force: true, 329 }) 330 c.Assert(err, IsNil) 331 332 entries, err := w.Filesystem.ReadDir("/") 333 c.Assert(err, IsNil) 334 c.Assert(entries, HasLen, 8) 335} 336 337func (s *WorktreeSuite) TestCheckoutKeep(c *C) { 338 w := &Worktree{ 339 r: s.Repository, 340 Filesystem: memfs.New(), 341 } 342 343 err := w.Checkout(&CheckoutOptions{ 344 Force: true, 345 }) 346 c.Assert(err, IsNil) 347 348 // Create a new branch and create a new file. 349 err = w.Checkout(&CheckoutOptions{ 350 Branch: plumbing.NewBranchReferenceName("new-branch"), 351 Create: true, 352 }) 353 c.Assert(err, IsNil) 354 355 w.Filesystem = memfs.New() 356 f, err := w.Filesystem.Create("new-file.txt") 357 c.Assert(err, IsNil) 358 _, err = f.Write([]byte("DUMMY")) 359 c.Assert(err, IsNil) 360 c.Assert(f.Close(), IsNil) 361 362 // Add the file to staging. 363 _, err = w.Add("new-file.txt") 364 c.Assert(err, IsNil) 365 366 // Switch branch to master, and verify that the new file was kept in staging. 367 err = w.Checkout(&CheckoutOptions{ 368 Keep: true, 369 }) 370 c.Assert(err, IsNil) 371 372 fi, err := w.Filesystem.Stat("new-file.txt") 373 c.Assert(err, IsNil) 374 c.Assert(fi.Size(), Equals, int64(5)) 375} 376 377func (s *WorktreeSuite) TestCheckoutSymlink(c *C) { 378 if runtime.GOOS == "windows" { 379 c.Skip("git doesn't support symlinks by default in windows") 380 } 381 382 dir, err := ioutil.TempDir("", "checkout") 383 c.Assert(err, IsNil) 384 defer os.RemoveAll(dir) 385 386 r, err := PlainInit(dir, false) 387 c.Assert(err, IsNil) 388 389 w, err := r.Worktree() 390 c.Assert(err, IsNil) 391 392 w.Filesystem.Symlink("not-exists", "bar") 393 w.Add("bar") 394 w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 395 396 r.Storer.SetIndex(&index.Index{Version: 2}) 397 w.Filesystem = osfs.New(filepath.Join(dir, "worktree-empty")) 398 399 err = w.Checkout(&CheckoutOptions{}) 400 c.Assert(err, IsNil) 401 402 status, err := w.Status() 403 c.Assert(err, IsNil) 404 c.Assert(status.IsClean(), Equals, true) 405 406 target, err := w.Filesystem.Readlink("bar") 407 c.Assert(target, Equals, "not-exists") 408 c.Assert(err, IsNil) 409} 410 411func (s *WorktreeSuite) TestFilenameNormalization(c *C) { 412 if runtime.GOOS == "windows" { 413 c.Skip("windows paths may contain non utf-8 sequences") 414 } 415 416 url := c.MkDir() 417 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 418 419 server, err := PlainClone(url, false, &CloneOptions{ 420 URL: path, 421 }) 422 c.Assert(err, IsNil) 423 424 filename := "페" 425 426 w, err := server.Worktree() 427 c.Assert(err, IsNil) 428 429 writeFile := func(path string) { 430 err := util.WriteFile(w.Filesystem, path, []byte("foo"), 0755) 431 c.Assert(err, IsNil) 432 } 433 434 writeFile(filename) 435 origHash, err := w.Add(filename) 436 c.Assert(err, IsNil) 437 _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()}) 438 c.Assert(err, IsNil) 439 440 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ 441 URL: url, 442 }) 443 c.Assert(err, IsNil) 444 445 w, err = r.Worktree() 446 c.Assert(err, IsNil) 447 448 status, err := w.Status() 449 c.Assert(err, IsNil) 450 c.Assert(status.IsClean(), Equals, true) 451 452 err = w.Filesystem.Remove(filename) 453 c.Assert(err, IsNil) 454 455 modFilename := norm.NFKD.String(filename) 456 writeFile(modFilename) 457 458 _, err = w.Add(filename) 459 c.Assert(err, IsNil) 460 modHash, err := w.Add(modFilename) 461 c.Assert(err, IsNil) 462 // At this point we've got two files with the same content. 463 // Hence their hashes must be the same. 464 c.Assert(origHash == modHash, Equals, true) 465 466 status, err = w.Status() 467 c.Assert(err, IsNil) 468 // However, their names are different and the work tree is still dirty. 469 c.Assert(status.IsClean(), Equals, false) 470 471 // Revert back the deletion of the first file. 472 writeFile(filename) 473 _, err = w.Add(filename) 474 c.Assert(err, IsNil) 475 476 status, err = w.Status() 477 c.Assert(err, IsNil) 478 // Still dirty - the second file is added. 479 c.Assert(status.IsClean(), Equals, false) 480 481 _, err = w.Remove(modFilename) 482 c.Assert(err, IsNil) 483 484 status, err = w.Status() 485 c.Assert(err, IsNil) 486 c.Assert(status.IsClean(), Equals, true) 487} 488 489func (s *WorktreeSuite) TestCheckoutSubmodule(c *C) { 490 url := "https://github.com/git-fixtures/submodule.git" 491 r := s.NewRepositoryWithEmptyWorktree(fixtures.ByURL(url).One()) 492 493 w, err := r.Worktree() 494 c.Assert(err, IsNil) 495 496 err = w.Checkout(&CheckoutOptions{}) 497 c.Assert(err, IsNil) 498 499 status, err := w.Status() 500 c.Assert(err, IsNil) 501 c.Assert(status.IsClean(), Equals, true) 502} 503 504func (s *WorktreeSuite) TestCheckoutSubmoduleInitialized(c *C) { 505 url := "https://github.com/git-fixtures/submodule.git" 506 r := s.NewRepository(fixtures.ByURL(url).One()) 507 508 w, err := r.Worktree() 509 c.Assert(err, IsNil) 510 511 sub, err := w.Submodules() 512 c.Assert(err, IsNil) 513 514 err = sub.Update(&SubmoduleUpdateOptions{Init: true}) 515 c.Assert(err, IsNil) 516 517 status, err := w.Status() 518 c.Assert(err, IsNil) 519 c.Assert(status.IsClean(), Equals, true) 520} 521 522func (s *WorktreeSuite) TestCheckoutIndexMem(c *C) { 523 fs := memfs.New() 524 w := &Worktree{ 525 r: s.Repository, 526 Filesystem: fs, 527 } 528 529 err := w.Checkout(&CheckoutOptions{}) 530 c.Assert(err, IsNil) 531 532 idx, err := s.Repository.Storer.Index() 533 c.Assert(err, IsNil) 534 c.Assert(idx.Entries, HasLen, 9) 535 c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") 536 c.Assert(idx.Entries[0].Name, Equals, ".gitignore") 537 c.Assert(idx.Entries[0].Mode, Equals, filemode.Regular) 538 c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false) 539 c.Assert(idx.Entries[0].Size, Equals, uint32(189)) 540 541 // ctime, dev, inode, uid and gid are not supported on memfs fs 542 c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, true) 543 c.Assert(idx.Entries[0].Dev, Equals, uint32(0)) 544 c.Assert(idx.Entries[0].Inode, Equals, uint32(0)) 545 c.Assert(idx.Entries[0].UID, Equals, uint32(0)) 546 c.Assert(idx.Entries[0].GID, Equals, uint32(0)) 547} 548 549func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) { 550 dir, err := ioutil.TempDir("", "checkout") 551 c.Assert(err, IsNil) 552 defer os.RemoveAll(dir) 553 554 fs := osfs.New(filepath.Join(dir, "worktree")) 555 w := &Worktree{ 556 r: s.Repository, 557 Filesystem: fs, 558 } 559 560 err = w.Checkout(&CheckoutOptions{}) 561 c.Assert(err, IsNil) 562 563 idx, err := s.Repository.Storer.Index() 564 c.Assert(err, IsNil) 565 c.Assert(idx.Entries, HasLen, 9) 566 c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") 567 c.Assert(idx.Entries[0].Name, Equals, ".gitignore") 568 c.Assert(idx.Entries[0].Mode, Equals, filemode.Regular) 569 c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false) 570 c.Assert(idx.Entries[0].Size, Equals, uint32(189)) 571 572 c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, false) 573 if runtime.GOOS != "windows" { 574 c.Assert(idx.Entries[0].Dev, Not(Equals), uint32(0)) 575 c.Assert(idx.Entries[0].Inode, Not(Equals), uint32(0)) 576 c.Assert(idx.Entries[0].UID, Not(Equals), uint32(0)) 577 c.Assert(idx.Entries[0].GID, Not(Equals), uint32(0)) 578 } 579} 580 581func (s *WorktreeSuite) TestCheckoutBranch(c *C) { 582 w := &Worktree{ 583 r: s.Repository, 584 Filesystem: memfs.New(), 585 } 586 587 err := w.Checkout(&CheckoutOptions{ 588 Branch: "refs/heads/branch", 589 }) 590 c.Assert(err, IsNil) 591 592 head, err := w.r.Head() 593 c.Assert(err, IsNil) 594 c.Assert(head.Name().String(), Equals, "refs/heads/branch") 595 596 status, err := w.Status() 597 c.Assert(err, IsNil) 598 c.Assert(status.IsClean(), Equals, true) 599} 600 601func (s *WorktreeSuite) TestCheckoutCreateWithHash(c *C) { 602 w := &Worktree{ 603 r: s.Repository, 604 Filesystem: memfs.New(), 605 } 606 607 err := w.Checkout(&CheckoutOptions{ 608 Create: true, 609 Branch: "refs/heads/foo", 610 Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), 611 }) 612 c.Assert(err, IsNil) 613 614 head, err := w.r.Head() 615 c.Assert(err, IsNil) 616 c.Assert(head.Name().String(), Equals, "refs/heads/foo") 617 c.Assert(head.Hash(), Equals, plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")) 618 619 status, err := w.Status() 620 c.Assert(err, IsNil) 621 c.Assert(status.IsClean(), Equals, true) 622} 623 624func (s *WorktreeSuite) TestCheckoutCreate(c *C) { 625 w := &Worktree{ 626 r: s.Repository, 627 Filesystem: memfs.New(), 628 } 629 630 err := w.Checkout(&CheckoutOptions{ 631 Create: true, 632 Branch: "refs/heads/foo", 633 }) 634 c.Assert(err, IsNil) 635 636 head, err := w.r.Head() 637 c.Assert(err, IsNil) 638 c.Assert(head.Name().String(), Equals, "refs/heads/foo") 639 c.Assert(head.Hash(), Equals, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) 640 641 status, err := w.Status() 642 c.Assert(err, IsNil) 643 c.Assert(status.IsClean(), Equals, true) 644} 645 646func (s *WorktreeSuite) TestCheckoutBranchAndHash(c *C) { 647 w := &Worktree{ 648 r: s.Repository, 649 Filesystem: memfs.New(), 650 } 651 652 err := w.Checkout(&CheckoutOptions{ 653 Branch: "refs/heads/foo", 654 Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), 655 }) 656 657 c.Assert(err, Equals, ErrBranchHashExclusive) 658} 659 660func (s *WorktreeSuite) TestCheckoutCreateMissingBranch(c *C) { 661 w := &Worktree{ 662 r: s.Repository, 663 Filesystem: memfs.New(), 664 } 665 666 err := w.Checkout(&CheckoutOptions{ 667 Create: true, 668 }) 669 670 c.Assert(err, Equals, ErrCreateRequiresBranch) 671} 672 673func (s *WorktreeSuite) TestCheckoutTag(c *C) { 674 f := fixtures.ByTag("tags").One() 675 r := s.NewRepositoryWithEmptyWorktree(f) 676 w, err := r.Worktree() 677 c.Assert(err, IsNil) 678 679 err = w.Checkout(&CheckoutOptions{}) 680 c.Assert(err, IsNil) 681 head, err := w.r.Head() 682 c.Assert(err, IsNil) 683 c.Assert(head.Name().String(), Equals, "refs/heads/master") 684 685 status, err := w.Status() 686 c.Assert(err, IsNil) 687 c.Assert(status.IsClean(), Equals, true) 688 689 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/lightweight-tag"}) 690 c.Assert(err, IsNil) 691 head, err = w.r.Head() 692 c.Assert(err, IsNil) 693 c.Assert(head.Name().String(), Equals, "HEAD") 694 c.Assert(head.Hash().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f") 695 696 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/commit-tag"}) 697 c.Assert(err, IsNil) 698 head, err = w.r.Head() 699 c.Assert(err, IsNil) 700 c.Assert(head.Name().String(), Equals, "HEAD") 701 c.Assert(head.Hash().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f") 702 703 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/tree-tag"}) 704 c.Assert(err, NotNil) 705 head, err = w.r.Head() 706 c.Assert(err, IsNil) 707 c.Assert(head.Name().String(), Equals, "HEAD") 708} 709 710func (s *WorktreeSuite) TestCheckoutBisect(c *C) { 711 if testing.Short() { 712 c.Skip("skipping test in short mode.") 713 } 714 715 s.testCheckoutBisect(c, "https://github.com/src-d/go-git.git") 716} 717 718func (s *WorktreeSuite) TestCheckoutBisectSubmodules(c *C) { 719 s.testCheckoutBisect(c, "https://github.com/git-fixtures/submodule.git") 720} 721 722// TestCheckoutBisect simulates a git bisect going through the git history and 723// checking every commit over the previous commit 724func (s *WorktreeSuite) testCheckoutBisect(c *C, url string) { 725 f := fixtures.ByURL(url).One() 726 r := s.NewRepositoryWithEmptyWorktree(f) 727 728 w, err := r.Worktree() 729 c.Assert(err, IsNil) 730 731 iter, err := w.r.Log(&LogOptions{}) 732 c.Assert(err, IsNil) 733 734 iter.ForEach(func(commit *object.Commit) error { 735 err := w.Checkout(&CheckoutOptions{Hash: commit.Hash}) 736 c.Assert(err, IsNil) 737 738 status, err := w.Status() 739 c.Assert(err, IsNil) 740 c.Assert(status.IsClean(), Equals, true) 741 742 return nil 743 }) 744} 745 746func (s *WorktreeSuite) TestStatus(c *C) { 747 fs := memfs.New() 748 w := &Worktree{ 749 r: s.Repository, 750 Filesystem: fs, 751 } 752 753 status, err := w.Status() 754 c.Assert(err, IsNil) 755 756 c.Assert(status.IsClean(), Equals, false) 757 c.Assert(status, HasLen, 9) 758} 759 760func (s *WorktreeSuite) TestStatusEmpty(c *C) { 761 fs := memfs.New() 762 storage := memory.NewStorage() 763 764 r, err := Init(storage, fs) 765 c.Assert(err, IsNil) 766 767 w, err := r.Worktree() 768 c.Assert(err, IsNil) 769 770 status, err := w.Status() 771 c.Assert(err, IsNil) 772 c.Assert(status.IsClean(), Equals, true) 773 c.Assert(status, NotNil) 774} 775 776func (s *WorktreeSuite) TestStatusEmptyDirty(c *C) { 777 fs := memfs.New() 778 err := util.WriteFile(fs, "foo", []byte("foo"), 0755) 779 c.Assert(err, IsNil) 780 781 storage := memory.NewStorage() 782 783 r, err := Init(storage, fs) 784 c.Assert(err, IsNil) 785 786 w, err := r.Worktree() 787 c.Assert(err, IsNil) 788 789 status, err := w.Status() 790 c.Assert(err, IsNil) 791 c.Assert(status.IsClean(), Equals, false) 792 c.Assert(status, HasLen, 1) 793} 794 795func (s *WorktreeSuite) TestReset(c *C) { 796 fs := memfs.New() 797 w := &Worktree{ 798 r: s.Repository, 799 Filesystem: fs, 800 } 801 802 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 803 804 err := w.Checkout(&CheckoutOptions{}) 805 c.Assert(err, IsNil) 806 807 branch, err := w.r.Reference(plumbing.Master, false) 808 c.Assert(err, IsNil) 809 c.Assert(branch.Hash(), Not(Equals), commit) 810 811 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commit}) 812 c.Assert(err, IsNil) 813 814 branch, err = w.r.Reference(plumbing.Master, false) 815 c.Assert(err, IsNil) 816 c.Assert(branch.Hash(), Equals, commit) 817 818 status, err := w.Status() 819 c.Assert(err, IsNil) 820 c.Assert(status.IsClean(), Equals, true) 821} 822 823func (s *WorktreeSuite) TestResetWithUntracked(c *C) { 824 fs := memfs.New() 825 w := &Worktree{ 826 r: s.Repository, 827 Filesystem: fs, 828 } 829 830 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 831 832 err := w.Checkout(&CheckoutOptions{}) 833 c.Assert(err, IsNil) 834 835 err = util.WriteFile(fs, "foo", nil, 0755) 836 c.Assert(err, IsNil) 837 838 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commit}) 839 c.Assert(err, IsNil) 840 841 status, err := w.Status() 842 c.Assert(err, IsNil) 843 c.Assert(status.IsClean(), Equals, true) 844} 845 846func (s *WorktreeSuite) TestResetSoft(c *C) { 847 fs := memfs.New() 848 w := &Worktree{ 849 r: s.Repository, 850 Filesystem: fs, 851 } 852 853 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 854 855 err := w.Checkout(&CheckoutOptions{}) 856 c.Assert(err, IsNil) 857 858 err = w.Reset(&ResetOptions{Mode: SoftReset, Commit: commit}) 859 c.Assert(err, IsNil) 860 861 branch, err := w.r.Reference(plumbing.Master, false) 862 c.Assert(err, IsNil) 863 c.Assert(branch.Hash(), Equals, commit) 864 865 status, err := w.Status() 866 c.Assert(err, IsNil) 867 c.Assert(status.IsClean(), Equals, false) 868 c.Assert(status.File("CHANGELOG").Staging, Equals, Added) 869} 870 871func (s *WorktreeSuite) TestResetMixed(c *C) { 872 fs := memfs.New() 873 w := &Worktree{ 874 r: s.Repository, 875 Filesystem: fs, 876 } 877 878 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 879 880 err := w.Checkout(&CheckoutOptions{}) 881 c.Assert(err, IsNil) 882 883 err = w.Reset(&ResetOptions{Mode: MixedReset, Commit: commit}) 884 c.Assert(err, IsNil) 885 886 branch, err := w.r.Reference(plumbing.Master, false) 887 c.Assert(err, IsNil) 888 c.Assert(branch.Hash(), Equals, commit) 889 890 status, err := w.Status() 891 c.Assert(err, IsNil) 892 c.Assert(status.IsClean(), Equals, false) 893 c.Assert(status.File("CHANGELOG").Staging, Equals, Untracked) 894} 895 896func (s *WorktreeSuite) TestResetMerge(c *C) { 897 fs := memfs.New() 898 w := &Worktree{ 899 r: s.Repository, 900 Filesystem: fs, 901 } 902 903 commitA := plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294") 904 commitB := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 905 906 err := w.Checkout(&CheckoutOptions{}) 907 c.Assert(err, IsNil) 908 909 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commitA}) 910 c.Assert(err, IsNil) 911 912 branch, err := w.r.Reference(plumbing.Master, false) 913 c.Assert(err, IsNil) 914 c.Assert(branch.Hash(), Equals, commitA) 915 916 f, err := fs.Create(".gitignore") 917 c.Assert(err, IsNil) 918 _, err = f.Write([]byte("foo")) 919 c.Assert(err, IsNil) 920 err = f.Close() 921 c.Assert(err, IsNil) 922 923 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commitB}) 924 c.Assert(err, Equals, ErrUnstagedChanges) 925 926 branch, err = w.r.Reference(plumbing.Master, false) 927 c.Assert(err, IsNil) 928 c.Assert(branch.Hash(), Equals, commitA) 929} 930 931func (s *WorktreeSuite) TestResetHard(c *C) { 932 fs := memfs.New() 933 w := &Worktree{ 934 r: s.Repository, 935 Filesystem: fs, 936 } 937 938 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9") 939 940 err := w.Checkout(&CheckoutOptions{}) 941 c.Assert(err, IsNil) 942 943 f, err := fs.Create(".gitignore") 944 c.Assert(err, IsNil) 945 _, err = f.Write([]byte("foo")) 946 c.Assert(err, IsNil) 947 err = f.Close() 948 c.Assert(err, IsNil) 949 950 err = w.Reset(&ResetOptions{Mode: HardReset, Commit: commit}) 951 c.Assert(err, IsNil) 952 953 branch, err := w.r.Reference(plumbing.Master, false) 954 c.Assert(err, IsNil) 955 c.Assert(branch.Hash(), Equals, commit) 956} 957 958func (s *WorktreeSuite) TestStatusAfterCheckout(c *C) { 959 fs := memfs.New() 960 w := &Worktree{ 961 r: s.Repository, 962 Filesystem: fs, 963 } 964 965 err := w.Checkout(&CheckoutOptions{Force: true}) 966 c.Assert(err, IsNil) 967 968 status, err := w.Status() 969 c.Assert(err, IsNil) 970 c.Assert(status.IsClean(), Equals, true) 971 972} 973 974func (s *WorktreeSuite) TestStatusModified(c *C) { 975 dir, err := ioutil.TempDir("", "status") 976 c.Assert(err, IsNil) 977 defer os.RemoveAll(dir) 978 979 fs := osfs.New(filepath.Join(dir, "worktree")) 980 w := &Worktree{ 981 r: s.Repository, 982 Filesystem: fs, 983 } 984 985 err = w.Checkout(&CheckoutOptions{}) 986 c.Assert(err, IsNil) 987 988 f, err := fs.Create(".gitignore") 989 c.Assert(err, IsNil) 990 _, err = f.Write([]byte("foo")) 991 c.Assert(err, IsNil) 992 err = f.Close() 993 c.Assert(err, IsNil) 994 995 status, err := w.Status() 996 c.Assert(err, IsNil) 997 c.Assert(status.IsClean(), Equals, false) 998 c.Assert(status.File(".gitignore").Worktree, Equals, Modified) 999} 1000 1001func (s *WorktreeSuite) TestStatusIgnored(c *C) { 1002 fs := memfs.New() 1003 w := &Worktree{ 1004 r: s.Repository, 1005 Filesystem: fs, 1006 } 1007 1008 w.Checkout(&CheckoutOptions{}) 1009 1010 fs.MkdirAll("another", os.ModePerm) 1011 f, _ := fs.Create("another/file") 1012 f.Close() 1013 fs.MkdirAll("vendor/github.com", os.ModePerm) 1014 f, _ = fs.Create("vendor/github.com/file") 1015 f.Close() 1016 fs.MkdirAll("vendor/gopkg.in", os.ModePerm) 1017 f, _ = fs.Create("vendor/gopkg.in/file") 1018 f.Close() 1019 1020 status, _ := w.Status() 1021 c.Assert(len(status), Equals, 3) 1022 _, ok := status["another/file"] 1023 c.Assert(ok, Equals, true) 1024 _, ok = status["vendor/github.com/file"] 1025 c.Assert(ok, Equals, true) 1026 _, ok = status["vendor/gopkg.in/file"] 1027 c.Assert(ok, Equals, true) 1028 1029 f, _ = fs.Create(".gitignore") 1030 f.Write([]byte("vendor/g*/")) 1031 f.Close() 1032 f, _ = fs.Create("vendor/.gitignore") 1033 f.Write([]byte("!github.com/\n")) 1034 f.Close() 1035 1036 status, _ = w.Status() 1037 c.Assert(len(status), Equals, 4) 1038 _, ok = status[".gitignore"] 1039 c.Assert(ok, Equals, true) 1040 _, ok = status["another/file"] 1041 c.Assert(ok, Equals, true) 1042 _, ok = status["vendor/.gitignore"] 1043 c.Assert(ok, Equals, true) 1044 _, ok = status["vendor/github.com/file"] 1045 c.Assert(ok, Equals, true) 1046} 1047 1048func (s *WorktreeSuite) TestStatusUntracked(c *C) { 1049 fs := memfs.New() 1050 w := &Worktree{ 1051 r: s.Repository, 1052 Filesystem: fs, 1053 } 1054 1055 err := w.Checkout(&CheckoutOptions{Force: true}) 1056 c.Assert(err, IsNil) 1057 1058 f, err := w.Filesystem.Create("foo") 1059 c.Assert(err, IsNil) 1060 c.Assert(f.Close(), IsNil) 1061 1062 status, err := w.Status() 1063 c.Assert(err, IsNil) 1064 c.Assert(status.File("foo").Staging, Equals, Untracked) 1065 c.Assert(status.File("foo").Worktree, Equals, Untracked) 1066} 1067 1068func (s *WorktreeSuite) TestStatusDeleted(c *C) { 1069 dir, err := ioutil.TempDir("", "status") 1070 c.Assert(err, IsNil) 1071 defer os.RemoveAll(dir) 1072 1073 fs := osfs.New(filepath.Join(dir, "worktree")) 1074 w := &Worktree{ 1075 r: s.Repository, 1076 Filesystem: fs, 1077 } 1078 1079 err = w.Checkout(&CheckoutOptions{}) 1080 c.Assert(err, IsNil) 1081 1082 err = fs.Remove(".gitignore") 1083 c.Assert(err, IsNil) 1084 1085 status, err := w.Status() 1086 c.Assert(err, IsNil) 1087 c.Assert(status.IsClean(), Equals, false) 1088 c.Assert(status.File(".gitignore").Worktree, Equals, Deleted) 1089} 1090 1091func (s *WorktreeSuite) TestSubmodule(c *C) { 1092 path := fixtures.ByTag("submodule").One().Worktree().Root() 1093 r, err := PlainOpen(path) 1094 c.Assert(err, IsNil) 1095 1096 w, err := r.Worktree() 1097 c.Assert(err, IsNil) 1098 1099 m, err := w.Submodule("basic") 1100 c.Assert(err, IsNil) 1101 1102 c.Assert(m.Config().Name, Equals, "basic") 1103} 1104 1105func (s *WorktreeSuite) TestSubmodules(c *C) { 1106 path := fixtures.ByTag("submodule").One().Worktree().Root() 1107 r, err := PlainOpen(path) 1108 c.Assert(err, IsNil) 1109 1110 w, err := r.Worktree() 1111 c.Assert(err, IsNil) 1112 1113 l, err := w.Submodules() 1114 c.Assert(err, IsNil) 1115 1116 c.Assert(l, HasLen, 2) 1117} 1118 1119func (s *WorktreeSuite) TestAddUntracked(c *C) { 1120 fs := memfs.New() 1121 w := &Worktree{ 1122 r: s.Repository, 1123 Filesystem: fs, 1124 } 1125 1126 err := w.Checkout(&CheckoutOptions{Force: true}) 1127 c.Assert(err, IsNil) 1128 1129 idx, err := w.r.Storer.Index() 1130 c.Assert(err, IsNil) 1131 c.Assert(idx.Entries, HasLen, 9) 1132 1133 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755) 1134 c.Assert(err, IsNil) 1135 1136 hash, err := w.Add("foo") 1137 c.Assert(hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5") 1138 c.Assert(err, IsNil) 1139 1140 idx, err = w.r.Storer.Index() 1141 c.Assert(err, IsNil) 1142 c.Assert(idx.Entries, HasLen, 10) 1143 1144 e, err := idx.Entry("foo") 1145 c.Assert(err, IsNil) 1146 c.Assert(e.Hash, Equals, hash) 1147 c.Assert(e.Mode, Equals, filemode.Executable) 1148 1149 status, err := w.Status() 1150 c.Assert(err, IsNil) 1151 c.Assert(status, HasLen, 1) 1152 1153 file := status.File("foo") 1154 c.Assert(file.Staging, Equals, Added) 1155 c.Assert(file.Worktree, Equals, Unmodified) 1156 1157 obj, err := w.r.Storer.EncodedObject(plumbing.BlobObject, hash) 1158 c.Assert(err, IsNil) 1159 c.Assert(obj, NotNil) 1160 c.Assert(obj.Size(), Equals, int64(3)) 1161} 1162 1163func (s *WorktreeSuite) TestIgnored(c *C) { 1164 fs := memfs.New() 1165 w := &Worktree{ 1166 r: s.Repository, 1167 Filesystem: fs, 1168 } 1169 1170 w.Excludes = make([]gitignore.Pattern, 0) 1171 w.Excludes = append(w.Excludes, gitignore.ParsePattern("foo", nil)) 1172 1173 err := w.Checkout(&CheckoutOptions{Force: true}) 1174 c.Assert(err, IsNil) 1175 1176 idx, err := w.r.Storer.Index() 1177 c.Assert(err, IsNil) 1178 c.Assert(idx.Entries, HasLen, 9) 1179 1180 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755) 1181 c.Assert(err, IsNil) 1182 1183 status, err := w.Status() 1184 c.Assert(err, IsNil) 1185 c.Assert(status, HasLen, 0) 1186 1187 file := status.File("foo") 1188 c.Assert(file.Staging, Equals, Untracked) 1189 c.Assert(file.Worktree, Equals, Untracked) 1190} 1191 1192func (s *WorktreeSuite) TestExcludedNoGitignore(c *C) { 1193 f := fixtures.ByTag("empty").One() 1194 r := s.NewRepository(f) 1195 1196 fs := memfs.New() 1197 w := &Worktree{ 1198 r: r, 1199 Filesystem: fs, 1200 } 1201 1202 _, err := fs.Open(".gitignore") 1203 c.Assert(err, Equals, os.ErrNotExist) 1204 1205 w.Excludes = make([]gitignore.Pattern, 0) 1206 w.Excludes = append(w.Excludes, gitignore.ParsePattern("foo", nil)) 1207 1208 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755) 1209 c.Assert(err, IsNil) 1210 1211 status, err := w.Status() 1212 c.Assert(err, IsNil) 1213 c.Assert(status, HasLen, 0) 1214 1215 file := status.File("foo") 1216 c.Assert(file.Staging, Equals, Untracked) 1217 c.Assert(file.Worktree, Equals, Untracked) 1218} 1219 1220func (s *WorktreeSuite) TestAddModified(c *C) { 1221 fs := memfs.New() 1222 w := &Worktree{ 1223 r: s.Repository, 1224 Filesystem: fs, 1225 } 1226 1227 err := w.Checkout(&CheckoutOptions{Force: true}) 1228 c.Assert(err, IsNil) 1229 1230 idx, err := w.r.Storer.Index() 1231 c.Assert(err, IsNil) 1232 c.Assert(idx.Entries, HasLen, 9) 1233 1234 err = util.WriteFile(w.Filesystem, "LICENSE", []byte("FOO"), 0644) 1235 c.Assert(err, IsNil) 1236 1237 hash, err := w.Add("LICENSE") 1238 c.Assert(err, IsNil) 1239 c.Assert(hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5") 1240 1241 idx, err = w.r.Storer.Index() 1242 c.Assert(err, IsNil) 1243 c.Assert(idx.Entries, HasLen, 9) 1244 1245 e, err := idx.Entry("LICENSE") 1246 c.Assert(err, IsNil) 1247 c.Assert(e.Hash, Equals, hash) 1248 c.Assert(e.Mode, Equals, filemode.Regular) 1249 1250 status, err := w.Status() 1251 c.Assert(err, IsNil) 1252 c.Assert(status, HasLen, 1) 1253 1254 file := status.File("LICENSE") 1255 c.Assert(file.Staging, Equals, Modified) 1256 c.Assert(file.Worktree, Equals, Unmodified) 1257} 1258 1259func (s *WorktreeSuite) TestAddUnmodified(c *C) { 1260 fs := memfs.New() 1261 w := &Worktree{ 1262 r: s.Repository, 1263 Filesystem: fs, 1264 } 1265 1266 err := w.Checkout(&CheckoutOptions{Force: true}) 1267 c.Assert(err, IsNil) 1268 1269 hash, err := w.Add("LICENSE") 1270 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 1271 c.Assert(err, IsNil) 1272} 1273 1274func (s *WorktreeSuite) TestAddRemoved(c *C) { 1275 fs := memfs.New() 1276 w := &Worktree{ 1277 r: s.Repository, 1278 Filesystem: fs, 1279 } 1280 1281 err := w.Checkout(&CheckoutOptions{Force: true}) 1282 c.Assert(err, IsNil) 1283 1284 idx, err := w.r.Storer.Index() 1285 c.Assert(err, IsNil) 1286 c.Assert(idx.Entries, HasLen, 9) 1287 1288 err = w.Filesystem.Remove("LICENSE") 1289 c.Assert(err, IsNil) 1290 1291 hash, err := w.Add("LICENSE") 1292 c.Assert(err, IsNil) 1293 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 1294 1295 e, err := idx.Entry("LICENSE") 1296 c.Assert(err, IsNil) 1297 c.Assert(e.Hash, Equals, hash) 1298 c.Assert(e.Mode, Equals, filemode.Regular) 1299 1300 status, err := w.Status() 1301 c.Assert(err, IsNil) 1302 c.Assert(status, HasLen, 1) 1303 1304 file := status.File("LICENSE") 1305 c.Assert(file.Staging, Equals, Deleted) 1306} 1307 1308func (s *WorktreeSuite) TestAddSymlink(c *C) { 1309 dir, err := ioutil.TempDir("", "checkout") 1310 c.Assert(err, IsNil) 1311 defer os.RemoveAll(dir) 1312 1313 r, err := PlainInit(dir, false) 1314 c.Assert(err, IsNil) 1315 err = util.WriteFile(r.wt, "foo", []byte("qux"), 0644) 1316 c.Assert(err, IsNil) 1317 err = r.wt.Symlink("foo", "bar") 1318 c.Assert(err, IsNil) 1319 1320 w, err := r.Worktree() 1321 c.Assert(err, IsNil) 1322 h, err := w.Add("foo") 1323 c.Assert(err, IsNil) 1324 c.Assert(h, Not(Equals), plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c")) 1325 1326 h, err = w.Add("bar") 1327 c.Assert(err, IsNil) 1328 c.Assert(h, Equals, plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c")) 1329 1330 obj, err := w.r.Storer.EncodedObject(plumbing.BlobObject, h) 1331 c.Assert(err, IsNil) 1332 c.Assert(obj, NotNil) 1333 c.Assert(obj.Size(), Equals, int64(3)) 1334} 1335 1336func (s *WorktreeSuite) TestAddDirectory(c *C) { 1337 fs := memfs.New() 1338 w := &Worktree{ 1339 r: s.Repository, 1340 Filesystem: fs, 1341 } 1342 1343 err := w.Checkout(&CheckoutOptions{Force: true}) 1344 c.Assert(err, IsNil) 1345 1346 idx, err := w.r.Storer.Index() 1347 c.Assert(err, IsNil) 1348 c.Assert(idx.Entries, HasLen, 9) 1349 1350 err = util.WriteFile(w.Filesystem, "qux/foo", []byte("FOO"), 0755) 1351 c.Assert(err, IsNil) 1352 err = util.WriteFile(w.Filesystem, "qux/baz/bar", []byte("BAR"), 0755) 1353 c.Assert(err, IsNil) 1354 1355 h, err := w.Add("qux") 1356 c.Assert(err, IsNil) 1357 c.Assert(h.IsZero(), Equals, true) 1358 1359 idx, err = w.r.Storer.Index() 1360 c.Assert(err, IsNil) 1361 c.Assert(idx.Entries, HasLen, 11) 1362 1363 e, err := idx.Entry("qux/foo") 1364 c.Assert(err, IsNil) 1365 c.Assert(e.Mode, Equals, filemode.Executable) 1366 1367 e, err = idx.Entry("qux/baz/bar") 1368 c.Assert(err, IsNil) 1369 c.Assert(e.Mode, Equals, filemode.Executable) 1370 1371 status, err := w.Status() 1372 c.Assert(err, IsNil) 1373 c.Assert(status, HasLen, 2) 1374 1375 file := status.File("qux/foo") 1376 c.Assert(file.Staging, Equals, Added) 1377 c.Assert(file.Worktree, Equals, Unmodified) 1378 1379 file = status.File("qux/baz/bar") 1380 c.Assert(file.Staging, Equals, Added) 1381 c.Assert(file.Worktree, Equals, Unmodified) 1382} 1383 1384func (s *WorktreeSuite) TestAddDirectoryErrorNotFound(c *C) { 1385 r, _ := Init(memory.NewStorage(), memfs.New()) 1386 w, _ := r.Worktree() 1387 1388 h, err := w.Add("foo") 1389 c.Assert(err, NotNil) 1390 c.Assert(h.IsZero(), Equals, true) 1391} 1392 1393func (s *WorktreeSuite) TestAddAll(c *C) { 1394 fs := memfs.New() 1395 w := &Worktree{ 1396 r: s.Repository, 1397 Filesystem: fs, 1398 } 1399 1400 err := w.Checkout(&CheckoutOptions{Force: true}) 1401 c.Assert(err, IsNil) 1402 1403 idx, err := w.r.Storer.Index() 1404 c.Assert(err, IsNil) 1405 c.Assert(idx.Entries, HasLen, 9) 1406 1407 err = util.WriteFile(w.Filesystem, "file1", []byte("file1"), 0644) 1408 c.Assert(err, IsNil) 1409 1410 err = util.WriteFile(w.Filesystem, "file2", []byte("file2"), 0644) 1411 c.Assert(err, IsNil) 1412 1413 err = util.WriteFile(w.Filesystem, "file3", []byte("ignore me"), 0644) 1414 c.Assert(err, IsNil) 1415 1416 w.Excludes = make([]gitignore.Pattern, 0) 1417 w.Excludes = append(w.Excludes, gitignore.ParsePattern("file3", nil)) 1418 1419 err = w.AddWithOptions(&AddOptions{All: true}) 1420 c.Assert(err, IsNil) 1421 1422 idx, err = w.r.Storer.Index() 1423 c.Assert(err, IsNil) 1424 c.Assert(idx.Entries, HasLen, 11) 1425 1426 status, err := w.Status() 1427 c.Assert(err, IsNil) 1428 c.Assert(status, HasLen, 2) 1429 1430 file1 := status.File("file1") 1431 c.Assert(file1.Staging, Equals, Added) 1432 file2 := status.File("file2") 1433 c.Assert(file2.Staging, Equals, Added) 1434 file3 := status.File("file3") 1435 c.Assert(file3.Staging, Equals, Untracked) 1436 c.Assert(file3.Worktree, Equals, Untracked) 1437} 1438 1439func (s *WorktreeSuite) TestAddGlob(c *C) { 1440 fs := memfs.New() 1441 w := &Worktree{ 1442 r: s.Repository, 1443 Filesystem: fs, 1444 } 1445 1446 err := w.Checkout(&CheckoutOptions{Force: true}) 1447 c.Assert(err, IsNil) 1448 1449 idx, err := w.r.Storer.Index() 1450 c.Assert(err, IsNil) 1451 c.Assert(idx.Entries, HasLen, 9) 1452 1453 err = util.WriteFile(w.Filesystem, "qux/qux", []byte("QUX"), 0755) 1454 c.Assert(err, IsNil) 1455 err = util.WriteFile(w.Filesystem, "qux/baz", []byte("BAZ"), 0755) 1456 c.Assert(err, IsNil) 1457 err = util.WriteFile(w.Filesystem, "qux/bar/baz", []byte("BAZ"), 0755) 1458 c.Assert(err, IsNil) 1459 1460 err = w.AddWithOptions(&AddOptions{Glob: w.Filesystem.Join("qux", "b*")}) 1461 c.Assert(err, IsNil) 1462 1463 idx, err = w.r.Storer.Index() 1464 c.Assert(err, IsNil) 1465 c.Assert(idx.Entries, HasLen, 11) 1466 1467 e, err := idx.Entry("qux/baz") 1468 c.Assert(err, IsNil) 1469 c.Assert(e.Mode, Equals, filemode.Executable) 1470 1471 e, err = idx.Entry("qux/bar/baz") 1472 c.Assert(err, IsNil) 1473 c.Assert(e.Mode, Equals, filemode.Executable) 1474 1475 status, err := w.Status() 1476 c.Assert(err, IsNil) 1477 c.Assert(status, HasLen, 3) 1478 1479 file := status.File("qux/qux") 1480 c.Assert(file.Staging, Equals, Untracked) 1481 c.Assert(file.Worktree, Equals, Untracked) 1482 1483 file = status.File("qux/baz") 1484 c.Assert(file.Staging, Equals, Added) 1485 c.Assert(file.Worktree, Equals, Unmodified) 1486 1487 file = status.File("qux/bar/baz") 1488 c.Assert(file.Staging, Equals, Added) 1489 c.Assert(file.Worktree, Equals, Unmodified) 1490} 1491 1492func (s *WorktreeSuite) TestAddGlobErrorNoMatches(c *C) { 1493 r, _ := Init(memory.NewStorage(), memfs.New()) 1494 w, _ := r.Worktree() 1495 1496 err := w.AddGlob("foo") 1497 c.Assert(err, Equals, ErrGlobNoMatches) 1498} 1499 1500func (s *WorktreeSuite) TestRemove(c *C) { 1501 fs := memfs.New() 1502 w := &Worktree{ 1503 r: s.Repository, 1504 Filesystem: fs, 1505 } 1506 1507 err := w.Checkout(&CheckoutOptions{Force: true}) 1508 c.Assert(err, IsNil) 1509 1510 hash, err := w.Remove("LICENSE") 1511 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 1512 c.Assert(err, IsNil) 1513 1514 status, err := w.Status() 1515 c.Assert(err, IsNil) 1516 c.Assert(status, HasLen, 1) 1517 c.Assert(status.File("LICENSE").Staging, Equals, Deleted) 1518} 1519 1520func (s *WorktreeSuite) TestRemoveNotExistentEntry(c *C) { 1521 fs := memfs.New() 1522 w := &Worktree{ 1523 r: s.Repository, 1524 Filesystem: fs, 1525 } 1526 1527 err := w.Checkout(&CheckoutOptions{Force: true}) 1528 c.Assert(err, IsNil) 1529 1530 hash, err := w.Remove("not-exists") 1531 c.Assert(hash.IsZero(), Equals, true) 1532 c.Assert(err, NotNil) 1533} 1534 1535func (s *WorktreeSuite) TestRemoveDirectory(c *C) { 1536 fs := memfs.New() 1537 w := &Worktree{ 1538 r: s.Repository, 1539 Filesystem: fs, 1540 } 1541 1542 err := w.Checkout(&CheckoutOptions{Force: true}) 1543 c.Assert(err, IsNil) 1544 1545 hash, err := w.Remove("json") 1546 c.Assert(hash.IsZero(), Equals, true) 1547 c.Assert(err, IsNil) 1548 1549 status, err := w.Status() 1550 c.Assert(err, IsNil) 1551 c.Assert(status, HasLen, 2) 1552 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 1553 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 1554 1555 _, err = w.Filesystem.Stat("json") 1556 c.Assert(os.IsNotExist(err), Equals, true) 1557} 1558 1559func (s *WorktreeSuite) TestRemoveDirectoryUntracked(c *C) { 1560 fs := memfs.New() 1561 w := &Worktree{ 1562 r: s.Repository, 1563 Filesystem: fs, 1564 } 1565 1566 err := w.Checkout(&CheckoutOptions{Force: true}) 1567 c.Assert(err, IsNil) 1568 1569 err = util.WriteFile(w.Filesystem, "json/foo", []byte("FOO"), 0755) 1570 c.Assert(err, IsNil) 1571 1572 hash, err := w.Remove("json") 1573 c.Assert(hash.IsZero(), Equals, true) 1574 c.Assert(err, IsNil) 1575 1576 status, err := w.Status() 1577 c.Assert(err, IsNil) 1578 c.Assert(status, HasLen, 3) 1579 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 1580 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 1581 c.Assert(status.File("json/foo").Staging, Equals, Untracked) 1582 1583 _, err = w.Filesystem.Stat("json") 1584 c.Assert(err, IsNil) 1585} 1586 1587func (s *WorktreeSuite) TestRemoveDeletedFromWorktree(c *C) { 1588 fs := memfs.New() 1589 w := &Worktree{ 1590 r: s.Repository, 1591 Filesystem: fs, 1592 } 1593 1594 err := w.Checkout(&CheckoutOptions{Force: true}) 1595 c.Assert(err, IsNil) 1596 1597 err = fs.Remove("LICENSE") 1598 c.Assert(err, IsNil) 1599 1600 hash, err := w.Remove("LICENSE") 1601 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 1602 c.Assert(err, IsNil) 1603 1604 status, err := w.Status() 1605 c.Assert(err, IsNil) 1606 c.Assert(status, HasLen, 1) 1607 c.Assert(status.File("LICENSE").Staging, Equals, Deleted) 1608} 1609 1610func (s *WorktreeSuite) TestRemoveGlob(c *C) { 1611 fs := memfs.New() 1612 w := &Worktree{ 1613 r: s.Repository, 1614 Filesystem: fs, 1615 } 1616 1617 err := w.Checkout(&CheckoutOptions{Force: true}) 1618 c.Assert(err, IsNil) 1619 1620 err = w.RemoveGlob(w.Filesystem.Join("json", "l*")) 1621 c.Assert(err, IsNil) 1622 1623 status, err := w.Status() 1624 c.Assert(err, IsNil) 1625 c.Assert(status, HasLen, 1) 1626 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 1627} 1628 1629func (s *WorktreeSuite) TestRemoveGlobDirectory(c *C) { 1630 fs := memfs.New() 1631 w := &Worktree{ 1632 r: s.Repository, 1633 Filesystem: fs, 1634 } 1635 1636 err := w.Checkout(&CheckoutOptions{Force: true}) 1637 c.Assert(err, IsNil) 1638 1639 err = w.RemoveGlob("js*") 1640 c.Assert(err, IsNil) 1641 1642 status, err := w.Status() 1643 c.Assert(err, IsNil) 1644 c.Assert(status, HasLen, 2) 1645 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 1646 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 1647 1648 _, err = w.Filesystem.Stat("json") 1649 c.Assert(os.IsNotExist(err), Equals, true) 1650} 1651 1652func (s *WorktreeSuite) TestRemoveGlobDirectoryDeleted(c *C) { 1653 fs := memfs.New() 1654 w := &Worktree{ 1655 r: s.Repository, 1656 Filesystem: fs, 1657 } 1658 1659 err := w.Checkout(&CheckoutOptions{Force: true}) 1660 c.Assert(err, IsNil) 1661 1662 err = fs.Remove("json/short.json") 1663 c.Assert(err, IsNil) 1664 1665 err = util.WriteFile(w.Filesystem, "json/foo", []byte("FOO"), 0755) 1666 c.Assert(err, IsNil) 1667 1668 err = w.RemoveGlob("js*") 1669 c.Assert(err, IsNil) 1670 1671 status, err := w.Status() 1672 c.Assert(err, IsNil) 1673 c.Assert(status, HasLen, 3) 1674 c.Assert(status.File("json/short.json").Staging, Equals, Deleted) 1675 c.Assert(status.File("json/long.json").Staging, Equals, Deleted) 1676} 1677 1678func (s *WorktreeSuite) TestMove(c *C) { 1679 fs := memfs.New() 1680 w := &Worktree{ 1681 r: s.Repository, 1682 Filesystem: fs, 1683 } 1684 1685 err := w.Checkout(&CheckoutOptions{Force: true}) 1686 c.Assert(err, IsNil) 1687 1688 hash, err := w.Move("LICENSE", "foo") 1689 c.Check(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f") 1690 c.Assert(err, IsNil) 1691 1692 status, err := w.Status() 1693 c.Assert(err, IsNil) 1694 c.Assert(status, HasLen, 2) 1695 c.Assert(status.File("LICENSE").Staging, Equals, Deleted) 1696 c.Assert(status.File("foo").Staging, Equals, Added) 1697 1698} 1699 1700func (s *WorktreeSuite) TestMoveNotExistentEntry(c *C) { 1701 fs := memfs.New() 1702 w := &Worktree{ 1703 r: s.Repository, 1704 Filesystem: fs, 1705 } 1706 1707 err := w.Checkout(&CheckoutOptions{Force: true}) 1708 c.Assert(err, IsNil) 1709 1710 hash, err := w.Move("not-exists", "foo") 1711 c.Assert(hash.IsZero(), Equals, true) 1712 c.Assert(err, NotNil) 1713} 1714 1715func (s *WorktreeSuite) TestMoveToExistent(c *C) { 1716 fs := memfs.New() 1717 w := &Worktree{ 1718 r: s.Repository, 1719 Filesystem: fs, 1720 } 1721 1722 err := w.Checkout(&CheckoutOptions{Force: true}) 1723 c.Assert(err, IsNil) 1724 1725 hash, err := w.Move(".gitignore", "LICENSE") 1726 c.Assert(hash.IsZero(), Equals, true) 1727 c.Assert(err, Equals, ErrDestinationExists) 1728} 1729 1730func (s *WorktreeSuite) TestClean(c *C) { 1731 fs := fixtures.ByTag("dirty").One().Worktree() 1732 1733 // Open the repo. 1734 fs, err := fs.Chroot("repo") 1735 c.Assert(err, IsNil) 1736 r, err := PlainOpen(fs.Root()) 1737 c.Assert(err, IsNil) 1738 1739 wt, err := r.Worktree() 1740 c.Assert(err, IsNil) 1741 1742 // Status before cleaning. 1743 status, err := wt.Status() 1744 c.Assert(err, IsNil) 1745 c.Assert(len(status), Equals, 2) 1746 1747 err = wt.Clean(&CleanOptions{}) 1748 c.Assert(err, IsNil) 1749 1750 // Status after cleaning. 1751 status, err = wt.Status() 1752 c.Assert(err, IsNil) 1753 1754 c.Assert(len(status), Equals, 1) 1755 1756 fi, err := fs.Lstat("pkgA") 1757 c.Assert(err, IsNil) 1758 c.Assert(fi.IsDir(), Equals, true) 1759 1760 // Clean with Dir: true. 1761 err = wt.Clean(&CleanOptions{Dir: true}) 1762 c.Assert(err, IsNil) 1763 1764 status, err = wt.Status() 1765 c.Assert(err, IsNil) 1766 1767 c.Assert(len(status), Equals, 0) 1768 1769 // An empty dir should be deleted, as well. 1770 _, err = fs.Lstat("pkgA") 1771 c.Assert(err, ErrorMatches, ".*(no such file or directory.*|.*file does not exist)*.") 1772 1773} 1774 1775func (s *WorktreeSuite) TestAlternatesRepo(c *C) { 1776 fs := fixtures.ByTag("alternates").One().Worktree() 1777 1778 // Open 1st repo. 1779 rep1fs, err := fs.Chroot("rep1") 1780 c.Assert(err, IsNil) 1781 rep1, err := PlainOpen(rep1fs.Root()) 1782 c.Assert(err, IsNil) 1783 1784 // Open 2nd repo. 1785 rep2fs, err := fs.Chroot("rep2") 1786 c.Assert(err, IsNil) 1787 rep2, err := PlainOpen(rep2fs.Root()) 1788 c.Assert(err, IsNil) 1789 1790 // Get the HEAD commit from the main repo. 1791 h, err := rep1.Head() 1792 c.Assert(err, IsNil) 1793 commit1, err := rep1.CommitObject(h.Hash()) 1794 c.Assert(err, IsNil) 1795 1796 // Get the HEAD commit from the shared repo. 1797 h, err = rep2.Head() 1798 c.Assert(err, IsNil) 1799 commit2, err := rep2.CommitObject(h.Hash()) 1800 c.Assert(err, IsNil) 1801 1802 c.Assert(commit1.String(), Equals, commit2.String()) 1803} 1804 1805func (s *WorktreeSuite) TestGrep(c *C) { 1806 cases := []struct { 1807 name string 1808 options GrepOptions 1809 wantResult []GrepResult 1810 dontWantResult []GrepResult 1811 wantError error 1812 }{ 1813 { 1814 name: "basic word match", 1815 options: GrepOptions{ 1816 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 1817 }, 1818 wantResult: []GrepResult{ 1819 { 1820 FileName: "go/example.go", 1821 LineNumber: 3, 1822 Content: "import (", 1823 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1824 }, 1825 { 1826 FileName: "vendor/foo.go", 1827 LineNumber: 3, 1828 Content: "import \"fmt\"", 1829 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1830 }, 1831 }, 1832 }, { 1833 name: "case insensitive match", 1834 options: GrepOptions{ 1835 Patterns: []*regexp.Regexp{regexp.MustCompile(`(?i)IMport`)}, 1836 }, 1837 wantResult: []GrepResult{ 1838 { 1839 FileName: "go/example.go", 1840 LineNumber: 3, 1841 Content: "import (", 1842 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1843 }, 1844 { 1845 FileName: "vendor/foo.go", 1846 LineNumber: 3, 1847 Content: "import \"fmt\"", 1848 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1849 }, 1850 }, 1851 }, { 1852 name: "invert match", 1853 options: GrepOptions{ 1854 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 1855 InvertMatch: true, 1856 }, 1857 dontWantResult: []GrepResult{ 1858 { 1859 FileName: "go/example.go", 1860 LineNumber: 3, 1861 Content: "import (", 1862 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1863 }, 1864 { 1865 FileName: "vendor/foo.go", 1866 LineNumber: 3, 1867 Content: "import \"fmt\"", 1868 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1869 }, 1870 }, 1871 }, { 1872 name: "match at a given commit hash", 1873 options: GrepOptions{ 1874 Patterns: []*regexp.Regexp{regexp.MustCompile("The MIT License")}, 1875 CommitHash: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"), 1876 }, 1877 wantResult: []GrepResult{ 1878 { 1879 FileName: "LICENSE", 1880 LineNumber: 1, 1881 Content: "The MIT License (MIT)", 1882 TreeName: "b029517f6300c2da0f4b651b8642506cd6aaf45d", 1883 }, 1884 }, 1885 dontWantResult: []GrepResult{ 1886 { 1887 FileName: "go/example.go", 1888 LineNumber: 3, 1889 Content: "import (", 1890 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1891 }, 1892 }, 1893 }, { 1894 name: "match for a given pathspec", 1895 options: GrepOptions{ 1896 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 1897 PathSpecs: []*regexp.Regexp{regexp.MustCompile("go/")}, 1898 }, 1899 wantResult: []GrepResult{ 1900 { 1901 FileName: "go/example.go", 1902 LineNumber: 3, 1903 Content: "import (", 1904 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1905 }, 1906 }, 1907 dontWantResult: []GrepResult{ 1908 { 1909 FileName: "vendor/foo.go", 1910 LineNumber: 3, 1911 Content: "import \"fmt\"", 1912 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1913 }, 1914 }, 1915 }, { 1916 name: "match at a given reference name", 1917 options: GrepOptions{ 1918 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 1919 ReferenceName: "refs/heads/master", 1920 }, 1921 wantResult: []GrepResult{ 1922 { 1923 FileName: "go/example.go", 1924 LineNumber: 3, 1925 Content: "import (", 1926 TreeName: "refs/heads/master", 1927 }, 1928 }, 1929 }, { 1930 name: "ambiguous options", 1931 options: GrepOptions{ 1932 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 1933 CommitHash: plumbing.NewHash("2d55a722f3c3ecc36da919dfd8b6de38352f3507"), 1934 ReferenceName: "somereferencename", 1935 }, 1936 wantError: ErrHashOrReference, 1937 }, { 1938 name: "multiple patterns", 1939 options: GrepOptions{ 1940 Patterns: []*regexp.Regexp{ 1941 regexp.MustCompile("import"), 1942 regexp.MustCompile("License"), 1943 }, 1944 }, 1945 wantResult: []GrepResult{ 1946 { 1947 FileName: "go/example.go", 1948 LineNumber: 3, 1949 Content: "import (", 1950 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1951 }, 1952 { 1953 FileName: "vendor/foo.go", 1954 LineNumber: 3, 1955 Content: "import \"fmt\"", 1956 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1957 }, 1958 { 1959 FileName: "LICENSE", 1960 LineNumber: 1, 1961 Content: "The MIT License (MIT)", 1962 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1963 }, 1964 }, 1965 }, { 1966 name: "multiple pathspecs", 1967 options: GrepOptions{ 1968 Patterns: []*regexp.Regexp{regexp.MustCompile("import")}, 1969 PathSpecs: []*regexp.Regexp{ 1970 regexp.MustCompile("go/"), 1971 regexp.MustCompile("vendor/"), 1972 }, 1973 }, 1974 wantResult: []GrepResult{ 1975 { 1976 FileName: "go/example.go", 1977 LineNumber: 3, 1978 Content: "import (", 1979 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1980 }, 1981 { 1982 FileName: "vendor/foo.go", 1983 LineNumber: 3, 1984 Content: "import \"fmt\"", 1985 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 1986 }, 1987 }, 1988 }, 1989 } 1990 1991 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() 1992 server, err := PlainClone(c.MkDir(), false, &CloneOptions{ 1993 URL: path, 1994 }) 1995 c.Assert(err, IsNil) 1996 1997 w, err := server.Worktree() 1998 c.Assert(err, IsNil) 1999 2000 for _, tc := range cases { 2001 gr, err := w.Grep(&tc.options) 2002 if tc.wantError != nil { 2003 c.Assert(err, Equals, tc.wantError) 2004 } else { 2005 c.Assert(err, IsNil) 2006 } 2007 2008 // Iterate through the results and check if the wanted result is present 2009 // in the got result. 2010 for _, wantResult := range tc.wantResult { 2011 found := false 2012 for _, gotResult := range gr { 2013 if wantResult == gotResult { 2014 found = true 2015 break 2016 } 2017 } 2018 if !found { 2019 c.Errorf("unexpected grep results for %q, expected result to contain: %v", tc.name, wantResult) 2020 } 2021 } 2022 2023 // Iterate through the results and check if the not wanted result is 2024 // present in the got result. 2025 for _, dontWantResult := range tc.dontWantResult { 2026 found := false 2027 for _, gotResult := range gr { 2028 if dontWantResult == gotResult { 2029 found = true 2030 break 2031 } 2032 } 2033 if found { 2034 c.Errorf("unexpected grep results for %q, expected result to NOT contain: %v", tc.name, dontWantResult) 2035 } 2036 } 2037 } 2038} 2039 2040func (s *WorktreeSuite) TestAddAndCommit(c *C) { 2041 dir, err := ioutil.TempDir("", "plain-repo") 2042 c.Assert(err, IsNil) 2043 defer os.RemoveAll(dir) 2044 2045 repo, err := PlainInit(dir, false) 2046 c.Assert(err, IsNil) 2047 2048 w, err := repo.Worktree() 2049 c.Assert(err, IsNil) 2050 2051 _, err = w.Add(".") 2052 c.Assert(err, IsNil) 2053 2054 w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{ 2055 Name: "foo", 2056 Email: "foo@foo.foo", 2057 When: time.Now(), 2058 }}) 2059 2060 iter, err := w.r.Log(&LogOptions{}) 2061 c.Assert(err, IsNil) 2062 err = iter.ForEach(func(c *object.Commit) error { 2063 files, err := c.Files() 2064 if err != nil { 2065 return err 2066 } 2067 2068 err = files.ForEach(func(f *object.File) error { 2069 return errors.New("Expected no files, got at least 1") 2070 }) 2071 return err 2072 }) 2073 c.Assert(err, IsNil) 2074} 2075 2076func (s *WorktreeSuite) TestLinkedWorktree(c *C) { 2077 fs := fixtures.ByTag("linked-worktree").One().Worktree() 2078 2079 // Open main repo. 2080 { 2081 fs, err := fs.Chroot("main") 2082 c.Assert(err, IsNil) 2083 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 2084 c.Assert(err, IsNil) 2085 2086 wt, err := repo.Worktree() 2087 c.Assert(err, IsNil) 2088 2089 status, err := wt.Status() 2090 c.Assert(err, IsNil) 2091 c.Assert(len(status), Equals, 2) // 2 files 2092 2093 head, err := repo.Head() 2094 c.Assert(err, IsNil) 2095 c.Assert(string(head.Name()), Equals, "refs/heads/master") 2096 } 2097 2098 // Open linked-worktree #1. 2099 { 2100 fs, err := fs.Chroot("linked-worktree-1") 2101 c.Assert(err, IsNil) 2102 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 2103 c.Assert(err, IsNil) 2104 2105 wt, err := repo.Worktree() 2106 c.Assert(err, IsNil) 2107 2108 status, err := wt.Status() 2109 c.Assert(err, IsNil) 2110 c.Assert(len(status), Equals, 3) // 3 files 2111 2112 _, ok := status["linked-worktree-1-unique-file.txt"] 2113 c.Assert(ok, Equals, true) 2114 2115 head, err := repo.Head() 2116 c.Assert(err, IsNil) 2117 c.Assert(string(head.Name()), Equals, "refs/heads/linked-worktree-1") 2118 } 2119 2120 // Open linked-worktree #2. 2121 { 2122 fs, err := fs.Chroot("linked-worktree-2") 2123 c.Assert(err, IsNil) 2124 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 2125 c.Assert(err, IsNil) 2126 2127 wt, err := repo.Worktree() 2128 c.Assert(err, IsNil) 2129 2130 status, err := wt.Status() 2131 c.Assert(err, IsNil) 2132 c.Assert(len(status), Equals, 3) // 3 files 2133 2134 _, ok := status["linked-worktree-2-unique-file.txt"] 2135 c.Assert(ok, Equals, true) 2136 2137 head, err := repo.Head() 2138 c.Assert(err, IsNil) 2139 c.Assert(string(head.Name()), Equals, "refs/heads/branch-with-different-name") 2140 } 2141 2142 // Open linked-worktree #2. 2143 { 2144 fs, err := fs.Chroot("linked-worktree-invalid-commondir") 2145 c.Assert(err, IsNil) 2146 _, err = PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true}) 2147 c.Assert(err, Equals, ErrRepositoryIncomplete) 2148 } 2149} 2150