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