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