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}