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