1package git
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "io"
8 "os"
9 "path/filepath"
10 "regexp"
11 "runtime"
12 "strings"
13 "testing"
14 "time"
15
16 fixtures "github.com/go-git/go-git-fixtures/v4"
17 "github.com/go-git/go-git/v5/config"
18 "github.com/go-git/go-git/v5/plumbing"
19 "github.com/go-git/go-git/v5/plumbing/cache"
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/filesystem"
25 "github.com/go-git/go-git/v5/storage/memory"
26 "github.com/stretchr/testify/assert"
27
28 "github.com/go-git/go-billy/v5"
29 "github.com/go-git/go-billy/v5/memfs"
30 "github.com/go-git/go-billy/v5/osfs"
31 "github.com/go-git/go-billy/v5/util"
32 "golang.org/x/text/unicode/norm"
33 . "gopkg.in/check.v1"
34)
35
36func defaultTestCommitOptions() *CommitOptions {
37 return &CommitOptions{
38 Author: &object.Signature{Name: "testuser", Email: "testemail"},
39 }
40}
41
42type WorktreeSuite struct {
43 BaseSuite
44}
45
46var _ = Suite(&WorktreeSuite{})
47
48func (s *WorktreeSuite) SetUpTest(c *C) {
49 f := fixtures.Basic().One()
50 s.Repository = s.NewRepositoryWithEmptyWorktree(f)
51}
52
53func (s *WorktreeSuite) TestPullCheckout(c *C) {
54 fs := memfs.New()
55 r, _ := Init(memory.NewStorage(), fs)
56 r.CreateRemote(&config.RemoteConfig{
57 Name: DefaultRemoteName,
58 URLs: []string{s.GetBasicLocalRepositoryURL()},
59 })
60
61 w, err := r.Worktree()
62 c.Assert(err, IsNil)
63
64 err = w.Pull(&PullOptions{})
65 c.Assert(err, IsNil)
66
67 fi, err := fs.ReadDir("")
68 c.Assert(err, IsNil)
69 c.Assert(fi, HasLen, 8)
70}
71
72func (s *WorktreeSuite) TestPullFastForward(c *C) {
73 url := c.MkDir()
74
75 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
76
77 server, err := PlainClone(url, false, &CloneOptions{
78 URL: path,
79 })
80 c.Assert(err, IsNil)
81
82 dir := c.MkDir()
83
84 r, err := PlainClone(dir, false, &CloneOptions{
85 URL: url,
86 })
87 c.Assert(err, IsNil)
88
89 w, err := server.Worktree()
90 c.Assert(err, IsNil)
91 err = os.WriteFile(filepath.Join(url, "foo"), []byte("foo"), 0755)
92 c.Assert(err, IsNil)
93 w.Add("foo")
94 hash, err := w.Commit("foo", &CommitOptions{Author: defaultSignature()})
95 c.Assert(err, IsNil)
96
97 w, err = r.Worktree()
98 c.Assert(err, IsNil)
99
100 err = w.Pull(&PullOptions{})
101 c.Assert(err, IsNil)
102
103 head, err := r.Head()
104 c.Assert(err, IsNil)
105 c.Assert(head.Hash(), Equals, hash)
106}
107
108func (s *WorktreeSuite) TestPullNonFastForward(c *C) {
109 url := c.MkDir()
110
111 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
112
113 server, err := PlainClone(url, false, &CloneOptions{
114 URL: path,
115 })
116 c.Assert(err, IsNil)
117
118 dir := c.MkDir()
119
120 r, err := PlainClone(dir, false, &CloneOptions{
121 URL: url,
122 })
123 c.Assert(err, IsNil)
124
125 w, err := server.Worktree()
126 c.Assert(err, IsNil)
127 err = os.WriteFile(filepath.Join(url, "foo"), []byte("foo"), 0755)
128 c.Assert(err, IsNil)
129 w.Add("foo")
130 _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()})
131 c.Assert(err, IsNil)
132
133 w, err = r.Worktree()
134 c.Assert(err, IsNil)
135 err = os.WriteFile(filepath.Join(dir, "bar"), []byte("bar"), 0755)
136 c.Assert(err, IsNil)
137 w.Add("bar")
138 _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()})
139 c.Assert(err, IsNil)
140
141 err = w.Pull(&PullOptions{})
142 c.Assert(err, Equals, ErrNonFastForwardUpdate)
143}
144
145func (s *WorktreeSuite) TestPullUpdateReferencesIfNeeded(c *C) {
146 r, _ := Init(memory.NewStorage(), memfs.New())
147 r.CreateRemote(&config.RemoteConfig{
148 Name: DefaultRemoteName,
149 URLs: []string{s.GetBasicLocalRepositoryURL()},
150 })
151
152 err := r.Fetch(&FetchOptions{})
153 c.Assert(err, IsNil)
154
155 _, err = r.Reference("refs/heads/master", false)
156 c.Assert(err, NotNil)
157
158 w, err := r.Worktree()
159 c.Assert(err, IsNil)
160
161 err = w.Pull(&PullOptions{})
162 c.Assert(err, IsNil)
163
164 head, err := r.Reference(plumbing.HEAD, true)
165 c.Assert(err, IsNil)
166 c.Assert(head.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
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 err = w.Pull(&PullOptions{})
173 c.Assert(err, Equals, NoErrAlreadyUpToDate)
174}
175
176func (s *WorktreeSuite) TestPullInSingleBranch(c *C) {
177 r, _ := Init(memory.NewStorage(), memfs.New())
178 err := r.clone(context.Background(), &CloneOptions{
179 URL: s.GetBasicLocalRepositoryURL(),
180 SingleBranch: true,
181 })
182
183 c.Assert(err, IsNil)
184
185 w, err := r.Worktree()
186 c.Assert(err, IsNil)
187
188 err = w.Pull(&PullOptions{})
189 c.Assert(err, Equals, NoErrAlreadyUpToDate)
190
191 branch, err := r.Reference("refs/heads/master", false)
192 c.Assert(err, IsNil)
193 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
194
195 _, err = r.Reference("refs/remotes/foo/branch", false)
196 c.Assert(err, NotNil)
197
198 storage := r.Storer.(*memory.Storage)
199 c.Assert(storage.Objects, HasLen, 28)
200}
201
202func (s *WorktreeSuite) TestPullProgress(c *C) {
203 r, _ := Init(memory.NewStorage(), memfs.New())
204
205 r.CreateRemote(&config.RemoteConfig{
206 Name: DefaultRemoteName,
207 URLs: []string{s.GetBasicLocalRepositoryURL()},
208 })
209
210 w, err := r.Worktree()
211 c.Assert(err, IsNil)
212
213 buf := bytes.NewBuffer(nil)
214 err = w.Pull(&PullOptions{
215 Progress: buf,
216 })
217
218 c.Assert(err, IsNil)
219 c.Assert(buf.Len(), Not(Equals), 0)
220}
221
222func (s *WorktreeSuite) TestPullProgressWithRecursion(c *C) {
223 if testing.Short() {
224 c.Skip("skipping test in short mode.")
225 }
226
227 path := fixtures.ByTag("submodule").One().Worktree().Root()
228
229 dir := c.MkDir()
230
231 r, _ := PlainInit(dir, false)
232 r.CreateRemote(&config.RemoteConfig{
233 Name: DefaultRemoteName,
234 URLs: []string{path},
235 })
236
237 w, err := r.Worktree()
238 c.Assert(err, IsNil)
239
240 err = w.Pull(&PullOptions{
241 RecurseSubmodules: DefaultSubmoduleRecursionDepth,
242 })
243 c.Assert(err, IsNil)
244
245 cfg, err := r.Config()
246 c.Assert(err, IsNil)
247 c.Assert(cfg.Submodules, HasLen, 2)
248}
249
250func (s *RepositorySuite) TestPullAdd(c *C) {
251 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
252
253 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{
254 URL: filepath.Join(path, ".git"),
255 })
256
257 c.Assert(err, IsNil)
258
259 storage := r.Storer.(*memory.Storage)
260 c.Assert(storage.Objects, HasLen, 28)
261
262 branch, err := r.Reference("refs/heads/master", false)
263 c.Assert(err, IsNil)
264 c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
265
266 ExecuteOnPath(c, path,
267 "touch foo",
268 "git add foo",
269 "git commit --no-gpg-sign -m foo foo",
270 )
271
272 w, err := r.Worktree()
273 c.Assert(err, IsNil)
274
275 err = w.Pull(&PullOptions{RemoteName: "origin"})
276 c.Assert(err, IsNil)
277
278 // the commit command has introduced a new commit, tree and blob
279 c.Assert(storage.Objects, HasLen, 31)
280
281 branch, err = r.Reference("refs/heads/master", false)
282 c.Assert(err, IsNil)
283 c.Assert(branch.Hash().String(), Not(Equals), "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
284}
285
286func (s *WorktreeSuite) TestPullAlreadyUptodate(c *C) {
287 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
288
289 fs := memfs.New()
290 r, err := Clone(memory.NewStorage(), fs, &CloneOptions{
291 URL: filepath.Join(path, ".git"),
292 })
293
294 c.Assert(err, IsNil)
295
296 w, err := r.Worktree()
297 c.Assert(err, IsNil)
298 err = util.WriteFile(fs, "bar", []byte("bar"), 0755)
299 c.Assert(err, IsNil)
300 w.Add("bar")
301 _, err = w.Commit("bar", &CommitOptions{Author: defaultSignature()})
302 c.Assert(err, IsNil)
303
304 err = w.Pull(&PullOptions{})
305 c.Assert(err, Equals, NoErrAlreadyUpToDate)
306}
307
308func (s *WorktreeSuite) TestPullDepth(c *C) {
309 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{
310 URL: fixtures.Basic().One().URL,
311 Depth: 1,
312 })
313
314 c.Assert(err, IsNil)
315
316 w, err := r.Worktree()
317 c.Assert(err, IsNil)
318 err = w.Pull(&PullOptions{})
319 c.Assert(err, Equals, nil)
320}
321
322func (s *WorktreeSuite) TestPullAfterShallowClone(c *C) {
323 tempDir := c.MkDir()
324 remoteURL := filepath.Join(tempDir, "remote")
325 repoDir := filepath.Join(tempDir, "repo")
326
327 remote, err := PlainInit(remoteURL, false)
328 c.Assert(err, IsNil)
329 c.Assert(remote, NotNil)
330
331 _ = CommitNewFile(c, remote, "File1")
332 _ = CommitNewFile(c, remote, "File2")
333
334 repo, err := PlainClone(repoDir, false, &CloneOptions{
335 URL: remoteURL,
336 Depth: 1,
337 Tags: NoTags,
338 SingleBranch: true,
339 ReferenceName: "master",
340 })
341 c.Assert(err, IsNil)
342
343 _ = CommitNewFile(c, remote, "File3")
344 _ = CommitNewFile(c, remote, "File4")
345
346 w, err := repo.Worktree()
347 c.Assert(err, IsNil)
348
349 err = w.Pull(&PullOptions{
350 RemoteName: DefaultRemoteName,
351 SingleBranch: true,
352 ReferenceName: plumbing.NewBranchReferenceName("master"),
353 })
354 c.Assert(err, IsNil)
355}
356
357func (s *WorktreeSuite) TestCheckout(c *C) {
358 fs := memfs.New()
359 w := &Worktree{
360 r: s.Repository,
361 Filesystem: fs,
362 }
363
364 err := w.Checkout(&CheckoutOptions{
365 Force: true,
366 })
367 c.Assert(err, IsNil)
368
369 entries, err := fs.ReadDir("/")
370 c.Assert(err, IsNil)
371
372 c.Assert(entries, HasLen, 8)
373 ch, err := fs.Open("CHANGELOG")
374 c.Assert(err, IsNil)
375
376 content, err := io.ReadAll(ch)
377 c.Assert(err, IsNil)
378 c.Assert(string(content), Equals, "Initial changelog\n")
379
380 idx, err := s.Repository.Storer.Index()
381 c.Assert(err, IsNil)
382 c.Assert(idx.Entries, HasLen, 9)
383}
384
385func (s *WorktreeSuite) TestCheckoutForce(c *C) {
386 w := &Worktree{
387 r: s.Repository,
388 Filesystem: memfs.New(),
389 }
390
391 err := w.Checkout(&CheckoutOptions{})
392 c.Assert(err, IsNil)
393
394 w.Filesystem = memfs.New()
395
396 err = w.Checkout(&CheckoutOptions{
397 Force: true,
398 })
399 c.Assert(err, IsNil)
400
401 entries, err := w.Filesystem.ReadDir("/")
402 c.Assert(err, IsNil)
403 c.Assert(entries, HasLen, 8)
404}
405
406func (s *WorktreeSuite) TestCheckoutKeep(c *C) {
407 w := &Worktree{
408 r: s.Repository,
409 Filesystem: memfs.New(),
410 }
411
412 err := w.Checkout(&CheckoutOptions{
413 Force: true,
414 })
415 c.Assert(err, IsNil)
416
417 // Create a new branch and create a new file.
418 err = w.Checkout(&CheckoutOptions{
419 Branch: plumbing.NewBranchReferenceName("new-branch"),
420 Create: true,
421 })
422 c.Assert(err, IsNil)
423
424 w.Filesystem = memfs.New()
425 f, err := w.Filesystem.Create("new-file.txt")
426 c.Assert(err, IsNil)
427 _, err = f.Write([]byte("DUMMY"))
428 c.Assert(err, IsNil)
429 c.Assert(f.Close(), IsNil)
430
431 // Add the file to staging.
432 _, err = w.Add("new-file.txt")
433 c.Assert(err, IsNil)
434
435 // Switch branch to master, and verify that the new file was kept in staging.
436 err = w.Checkout(&CheckoutOptions{
437 Keep: true,
438 })
439 c.Assert(err, IsNil)
440
441 fi, err := w.Filesystem.Stat("new-file.txt")
442 c.Assert(err, IsNil)
443 c.Assert(fi.Size(), Equals, int64(5))
444}
445
446func (s *WorktreeSuite) TestCheckoutSymlink(c *C) {
447 if runtime.GOOS == "windows" {
448 c.Skip("git doesn't support symlinks by default in windows")
449 }
450
451 dir := c.MkDir()
452
453 r, err := PlainInit(dir, false)
454 c.Assert(err, IsNil)
455
456 w, err := r.Worktree()
457 c.Assert(err, IsNil)
458
459 w.Filesystem.Symlink("not-exists", "bar")
460 w.Add("bar")
461 w.Commit("foo", &CommitOptions{Author: defaultSignature()})
462
463 r.Storer.SetIndex(&index.Index{Version: 2})
464 w.Filesystem = osfs.New(filepath.Join(dir, "worktree-empty"))
465
466 err = w.Checkout(&CheckoutOptions{})
467 c.Assert(err, IsNil)
468
469 status, err := w.Status()
470 c.Assert(err, IsNil)
471 c.Assert(status.IsClean(), Equals, true)
472
473 target, err := w.Filesystem.Readlink("bar")
474 c.Assert(target, Equals, "not-exists")
475 c.Assert(err, IsNil)
476}
477
478func (s *WorktreeSuite) TestCheckoutSparse(c *C) {
479 fs := memfs.New()
480 r, err := Clone(memory.NewStorage(), fs, &CloneOptions{
481 URL: s.GetBasicLocalRepositoryURL(),
482 NoCheckout: true,
483 })
484 c.Assert(err, IsNil)
485
486 w, err := r.Worktree()
487 c.Assert(err, IsNil)
488
489 sparseCheckoutDirectories := []string{"go", "json", "php"}
490 c.Assert(w.Checkout(&CheckoutOptions{
491 SparseCheckoutDirectories: sparseCheckoutDirectories,
492 }), IsNil)
493
494 fis, err := fs.ReadDir("/")
495 c.Assert(err, IsNil)
496
497 for _, fi := range fis {
498 c.Assert(fi.IsDir(), Equals, true)
499 var oneOfSparseCheckoutDirs bool
500
501 for _, sparseCheckoutDirectory := range sparseCheckoutDirectories {
502 if strings.HasPrefix(fi.Name(), sparseCheckoutDirectory) {
503 oneOfSparseCheckoutDirs = true
504 }
505 }
506 c.Assert(oneOfSparseCheckoutDirs, Equals, true)
507 }
508}
509
510func (s *WorktreeSuite) TestFilenameNormalization(c *C) {
511 if runtime.GOOS == "windows" {
512 c.Skip("windows paths may contain non utf-8 sequences")
513 }
514
515 url := c.MkDir()
516
517 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
518
519 server, err := PlainClone(url, false, &CloneOptions{
520 URL: path,
521 })
522 c.Assert(err, IsNil)
523
524 filename := "페"
525
526 w, err := server.Worktree()
527 c.Assert(err, IsNil)
528
529 writeFile := func(path string) {
530 err := util.WriteFile(w.Filesystem, path, []byte("foo"), 0755)
531 c.Assert(err, IsNil)
532 }
533
534 writeFile(filename)
535 origHash, err := w.Add(filename)
536 c.Assert(err, IsNil)
537 _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()})
538 c.Assert(err, IsNil)
539
540 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{
541 URL: url,
542 })
543 c.Assert(err, IsNil)
544
545 w, err = r.Worktree()
546 c.Assert(err, IsNil)
547
548 status, err := w.Status()
549 c.Assert(err, IsNil)
550 c.Assert(status.IsClean(), Equals, true)
551
552 err = w.Filesystem.Remove(filename)
553 c.Assert(err, IsNil)
554
555 modFilename := norm.NFKD.String(filename)
556 writeFile(modFilename)
557
558 _, err = w.Add(filename)
559 c.Assert(err, IsNil)
560 modHash, err := w.Add(modFilename)
561 c.Assert(err, IsNil)
562 // At this point we've got two files with the same content.
563 // Hence their hashes must be the same.
564 c.Assert(origHash == modHash, Equals, true)
565
566 status, err = w.Status()
567 c.Assert(err, IsNil)
568 // However, their names are different and the work tree is still dirty.
569 c.Assert(status.IsClean(), Equals, false)
570
571 // Revert back the deletion of the first file.
572 writeFile(filename)
573 _, err = w.Add(filename)
574 c.Assert(err, IsNil)
575
576 status, err = w.Status()
577 c.Assert(err, IsNil)
578 // Still dirty - the second file is added.
579 c.Assert(status.IsClean(), Equals, false)
580
581 _, err = w.Remove(modFilename)
582 c.Assert(err, IsNil)
583
584 status, err = w.Status()
585 c.Assert(err, IsNil)
586 c.Assert(status.IsClean(), Equals, true)
587}
588
589func (s *WorktreeSuite) TestCheckoutSubmodule(c *C) {
590 url := "https://github.com/git-fixtures/submodule.git"
591 r := s.NewRepositoryWithEmptyWorktree(fixtures.ByURL(url).One())
592
593 w, err := r.Worktree()
594 c.Assert(err, IsNil)
595
596 err = w.Checkout(&CheckoutOptions{})
597 c.Assert(err, IsNil)
598
599 status, err := w.Status()
600 c.Assert(err, IsNil)
601 c.Assert(status.IsClean(), Equals, true)
602}
603
604func (s *WorktreeSuite) TestCheckoutSubmoduleInitialized(c *C) {
605 url := "https://github.com/git-fixtures/submodule.git"
606 r := s.NewRepository(fixtures.ByURL(url).One())
607
608 w, err := r.Worktree()
609 c.Assert(err, IsNil)
610
611 sub, err := w.Submodules()
612 c.Assert(err, IsNil)
613
614 err = sub.Update(&SubmoduleUpdateOptions{Init: true})
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) TestCheckoutRelativePathSubmoduleInitialized(c *C) {
623 url := "https://github.com/git-fixtures/submodule.git"
624 r := s.NewRepository(fixtures.ByURL(url).One())
625
626 // modify the .gitmodules from original one
627 file, err := r.wt.OpenFile(".gitmodules", os.O_WRONLY|os.O_TRUNC, 0666)
628 c.Assert(err, IsNil)
629
630 n, err := io.WriteString(file, `[submodule "basic"]
631 path = basic
632 url = ../basic.git
633[submodule "itself"]
634 path = itself
635 url = ../submodule.git`)
636 c.Assert(err, IsNil)
637 c.Assert(n, Not(Equals), 0)
638
639 w, err := r.Worktree()
640 c.Assert(err, IsNil)
641
642 w.Add(".gitmodules")
643 w.Commit("test", &CommitOptions{})
644
645 // test submodule path
646 modules, err := w.readGitmodulesFile()
647 c.Assert(err, IsNil)
648
649 c.Assert(modules.Submodules["basic"].URL, Equals, "../basic.git")
650 c.Assert(modules.Submodules["itself"].URL, Equals, "../submodule.git")
651
652 basicSubmodule, err := w.Submodule("basic")
653 c.Assert(err, IsNil)
654 basicRepo, err := basicSubmodule.Repository()
655 c.Assert(err, IsNil)
656 basicRemotes, err := basicRepo.Remotes()
657 c.Assert(err, IsNil)
658 c.Assert(basicRemotes[0].Config().URLs[0], Equals, "https://github.com/git-fixtures/basic.git")
659
660 itselfSubmodule, err := w.Submodule("itself")
661 c.Assert(err, IsNil)
662 itselfRepo, err := itselfSubmodule.Repository()
663 c.Assert(err, IsNil)
664 itselfRemotes, err := itselfRepo.Remotes()
665 c.Assert(err, IsNil)
666 c.Assert(itselfRemotes[0].Config().URLs[0], Equals, "https://github.com/git-fixtures/submodule.git")
667
668 sub, err := w.Submodules()
669 c.Assert(err, IsNil)
670
671 err = sub.Update(&SubmoduleUpdateOptions{Init: true, RecurseSubmodules: DefaultSubmoduleRecursionDepth})
672 c.Assert(err, IsNil)
673
674 status, err := w.Status()
675 c.Assert(err, IsNil)
676 c.Assert(status.IsClean(), Equals, true)
677}
678
679func (s *WorktreeSuite) TestCheckoutIndexMem(c *C) {
680 fs := memfs.New()
681 w := &Worktree{
682 r: s.Repository,
683 Filesystem: fs,
684 }
685
686 err := w.Checkout(&CheckoutOptions{})
687 c.Assert(err, IsNil)
688
689 idx, err := s.Repository.Storer.Index()
690 c.Assert(err, IsNil)
691 c.Assert(idx.Entries, HasLen, 9)
692 c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
693 c.Assert(idx.Entries[0].Name, Equals, ".gitignore")
694 c.Assert(idx.Entries[0].Mode, Equals, filemode.Regular)
695 c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false)
696 c.Assert(idx.Entries[0].Size, Equals, uint32(189))
697
698 // ctime, dev, inode, uid and gid are not supported on memfs fs
699 c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, true)
700 c.Assert(idx.Entries[0].Dev, Equals, uint32(0))
701 c.Assert(idx.Entries[0].Inode, Equals, uint32(0))
702 c.Assert(idx.Entries[0].UID, Equals, uint32(0))
703 c.Assert(idx.Entries[0].GID, Equals, uint32(0))
704}
705
706func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) {
707 fs := s.TemporalFilesystem(c)
708
709 w := &Worktree{
710 r: s.Repository,
711 Filesystem: fs,
712 }
713
714 err := w.Checkout(&CheckoutOptions{})
715 c.Assert(err, IsNil)
716
717 idx, err := s.Repository.Storer.Index()
718 c.Assert(err, IsNil)
719 c.Assert(idx.Entries, HasLen, 9)
720 c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
721 c.Assert(idx.Entries[0].Name, Equals, ".gitignore")
722 c.Assert(idx.Entries[0].Mode, Equals, filemode.Regular)
723 c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false)
724 c.Assert(idx.Entries[0].Size, Equals, uint32(189))
725
726 c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, false)
727 if runtime.GOOS != "windows" {
728 c.Assert(idx.Entries[0].Dev, Not(Equals), uint32(0))
729 c.Assert(idx.Entries[0].Inode, Not(Equals), uint32(0))
730 c.Assert(idx.Entries[0].UID, Not(Equals), uint32(0))
731 c.Assert(idx.Entries[0].GID, Not(Equals), uint32(0))
732 }
733}
734
735func (s *WorktreeSuite) TestCheckoutBranch(c *C) {
736 w := &Worktree{
737 r: s.Repository,
738 Filesystem: memfs.New(),
739 }
740
741 err := w.Checkout(&CheckoutOptions{
742 Branch: "refs/heads/branch",
743 })
744 c.Assert(err, IsNil)
745
746 head, err := w.r.Head()
747 c.Assert(err, IsNil)
748 c.Assert(head.Name().String(), Equals, "refs/heads/branch")
749
750 status, err := w.Status()
751 c.Assert(err, IsNil)
752 c.Assert(status.IsClean(), Equals, true)
753}
754
755func (s *WorktreeSuite) TestCheckoutCreateWithHash(c *C) {
756 w := &Worktree{
757 r: s.Repository,
758 Filesystem: memfs.New(),
759 }
760
761 err := w.Checkout(&CheckoutOptions{
762 Create: true,
763 Branch: "refs/heads/foo",
764 Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
765 })
766 c.Assert(err, IsNil)
767
768 head, err := w.r.Head()
769 c.Assert(err, IsNil)
770 c.Assert(head.Name().String(), Equals, "refs/heads/foo")
771 c.Assert(head.Hash(), Equals, plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"))
772
773 status, err := w.Status()
774 c.Assert(err, IsNil)
775 c.Assert(status.IsClean(), Equals, true)
776}
777
778func (s *WorktreeSuite) TestCheckoutCreate(c *C) {
779 w := &Worktree{
780 r: s.Repository,
781 Filesystem: memfs.New(),
782 }
783
784 err := w.Checkout(&CheckoutOptions{
785 Create: true,
786 Branch: "refs/heads/foo",
787 })
788 c.Assert(err, IsNil)
789
790 head, err := w.r.Head()
791 c.Assert(err, IsNil)
792 c.Assert(head.Name().String(), Equals, "refs/heads/foo")
793 c.Assert(head.Hash(), Equals, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
794
795 status, err := w.Status()
796 c.Assert(err, IsNil)
797 c.Assert(status.IsClean(), Equals, true)
798}
799
800func (s *WorktreeSuite) TestCheckoutBranchAndHash(c *C) {
801 w := &Worktree{
802 r: s.Repository,
803 Filesystem: memfs.New(),
804 }
805
806 err := w.Checkout(&CheckoutOptions{
807 Branch: "refs/heads/foo",
808 Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
809 })
810
811 c.Assert(err, Equals, ErrBranchHashExclusive)
812}
813
814func (s *WorktreeSuite) TestCheckoutCreateMissingBranch(c *C) {
815 w := &Worktree{
816 r: s.Repository,
817 Filesystem: memfs.New(),
818 }
819
820 err := w.Checkout(&CheckoutOptions{
821 Create: true,
822 })
823
824 c.Assert(err, Equals, ErrCreateRequiresBranch)
825}
826
827func (s *WorktreeSuite) TestCheckoutCreateInvalidBranch(c *C) {
828 w := &Worktree{
829 r: s.Repository,
830 Filesystem: memfs.New(),
831 }
832
833 for _, name := range []plumbing.ReferenceName{
834 "foo",
835 "-",
836 "-foo",
837 "refs/heads//",
838 "refs/heads/..",
839 "refs/heads/a..b",
840 "refs/heads/.",
841 } {
842 err := w.Checkout(&CheckoutOptions{
843 Create: true,
844 Branch: name,
845 })
846
847 c.Assert(err, Equals, plumbing.ErrInvalidReferenceName)
848 }
849}
850
851func (s *WorktreeSuite) TestCheckoutTag(c *C) {
852 f := fixtures.ByTag("tags").One()
853 r := s.NewRepositoryWithEmptyWorktree(f)
854 w, err := r.Worktree()
855 c.Assert(err, IsNil)
856
857 err = w.Checkout(&CheckoutOptions{})
858 c.Assert(err, IsNil)
859 head, err := w.r.Head()
860 c.Assert(err, IsNil)
861 c.Assert(head.Name().String(), Equals, "refs/heads/master")
862
863 status, err := w.Status()
864 c.Assert(err, IsNil)
865 c.Assert(status.IsClean(), Equals, true)
866
867 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/lightweight-tag"})
868 c.Assert(err, IsNil)
869 head, err = w.r.Head()
870 c.Assert(err, IsNil)
871 c.Assert(head.Name().String(), Equals, "HEAD")
872 c.Assert(head.Hash().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
873
874 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/commit-tag"})
875 c.Assert(err, IsNil)
876 head, err = w.r.Head()
877 c.Assert(err, IsNil)
878 c.Assert(head.Name().String(), Equals, "HEAD")
879 c.Assert(head.Hash().String(), Equals, "f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
880
881 err = w.Checkout(&CheckoutOptions{Branch: "refs/tags/tree-tag"})
882 c.Assert(err, NotNil)
883 head, err = w.r.Head()
884 c.Assert(err, IsNil)
885 c.Assert(head.Name().String(), Equals, "HEAD")
886}
887
888func (s *WorktreeSuite) TestCheckoutTagHash(c *C) {
889 f := fixtures.ByTag("tags").One()
890 r := s.NewRepositoryWithEmptyWorktree(f)
891 w, err := r.Worktree()
892 c.Assert(err, IsNil)
893
894 for _, hash := range []string{
895 "b742a2a9fa0afcfa9a6fad080980fbc26b007c69", // annotated tag
896 "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc", // commit tag
897 "f7b877701fbf855b44c0a9e86f3fdce2c298b07f", // lightweight tag
898 } {
899 err = w.Checkout(&CheckoutOptions{
900 Hash: plumbing.NewHash(hash),
901 })
902 c.Assert(err, IsNil)
903 head, err := w.r.Head()
904 c.Assert(err, IsNil)
905 c.Assert(head.Name().String(), Equals, "HEAD")
906
907 status, err := w.Status()
908 c.Assert(err, IsNil)
909 c.Assert(status.IsClean(), Equals, true)
910 }
911
912 for _, hash := range []string{
913 "fe6cb94756faa81e5ed9240f9191b833db5f40ae", // blob tag
914 "152175bf7e5580299fa1f0ba41ef6474cc043b70", // tree tag
915 } {
916 err = w.Checkout(&CheckoutOptions{
917 Hash: plumbing.NewHash(hash),
918 })
919 c.Assert(err, NotNil)
920 }
921}
922
923func (s *WorktreeSuite) TestCheckoutBisect(c *C) {
924 if testing.Short() {
925 c.Skip("skipping test in short mode.")
926 }
927
928 s.testCheckoutBisect(c, "https://github.com/src-d/go-git.git")
929}
930
931func (s *WorktreeSuite) TestCheckoutBisectSubmodules(c *C) {
932 s.testCheckoutBisect(c, "https://github.com/git-fixtures/submodule.git")
933}
934
935// TestCheckoutBisect simulates a git bisect going through the git history and
936// checking every commit over the previous commit
937func (s *WorktreeSuite) testCheckoutBisect(c *C, url string) {
938 f := fixtures.ByURL(url).One()
939 r := s.NewRepositoryWithEmptyWorktree(f)
940
941 w, err := r.Worktree()
942 c.Assert(err, IsNil)
943
944 iter, err := w.r.Log(&LogOptions{})
945 c.Assert(err, IsNil)
946
947 iter.ForEach(func(commit *object.Commit) error {
948 err := w.Checkout(&CheckoutOptions{Hash: commit.Hash})
949 c.Assert(err, IsNil)
950
951 status, err := w.Status()
952 c.Assert(err, IsNil)
953 c.Assert(status.IsClean(), Equals, true)
954
955 return nil
956 })
957}
958
959func (s *WorktreeSuite) TestStatus(c *C) {
960 fs := memfs.New()
961 w := &Worktree{
962 r: s.Repository,
963 Filesystem: fs,
964 }
965
966 status, err := w.Status()
967 c.Assert(err, IsNil)
968
969 c.Assert(status.IsClean(), Equals, false)
970 c.Assert(status, HasLen, 9)
971}
972
973func (s *WorktreeSuite) TestStatusEmpty(c *C) {
974 fs := memfs.New()
975 storage := memory.NewStorage()
976
977 r, err := Init(storage, fs)
978 c.Assert(err, IsNil)
979
980 w, err := r.Worktree()
981 c.Assert(err, IsNil)
982
983 status, err := w.Status()
984 c.Assert(err, IsNil)
985 c.Assert(status.IsClean(), Equals, true)
986 c.Assert(status, NotNil)
987}
988
989func (s *WorktreeSuite) TestStatusCheckedInBeforeIgnored(c *C) {
990 fs := memfs.New()
991 storage := memory.NewStorage()
992
993 r, err := Init(storage, fs)
994 c.Assert(err, IsNil)
995
996 w, err := r.Worktree()
997 c.Assert(err, IsNil)
998
999 err = util.WriteFile(fs, "fileToIgnore", []byte("Initial data"), 0755)
1000 c.Assert(err, IsNil)
1001 _, err = w.Add("fileToIgnore")
1002 c.Assert(err, IsNil)
1003
1004 _, err = w.Commit("Added file that will be ignored later", defaultTestCommitOptions())
1005 c.Assert(err, IsNil)
1006
1007 err = util.WriteFile(fs, ".gitignore", []byte("fileToIgnore\nsecondIgnoredFile"), 0755)
1008 c.Assert(err, IsNil)
1009 _, err = w.Add(".gitignore")
1010 c.Assert(err, IsNil)
1011 _, err = w.Commit("Added .gitignore", defaultTestCommitOptions())
1012 c.Assert(err, IsNil)
1013 status, err := w.Status()
1014 c.Assert(err, IsNil)
1015 c.Assert(status.IsClean(), Equals, true)
1016 c.Assert(status, NotNil)
1017
1018 err = util.WriteFile(fs, "secondIgnoredFile", []byte("Should be completely ignored"), 0755)
1019 c.Assert(err, IsNil)
1020 status = nil
1021 status, err = w.Status()
1022 c.Assert(err, IsNil)
1023 c.Assert(status.IsClean(), Equals, true)
1024 c.Assert(status, NotNil)
1025
1026 err = util.WriteFile(fs, "fileToIgnore", []byte("Updated data"), 0755)
1027 c.Assert(err, IsNil)
1028 status = nil
1029 status, err = w.Status()
1030 c.Assert(err, IsNil)
1031 c.Assert(status.IsClean(), Equals, false)
1032 c.Assert(status, NotNil)
1033}
1034
1035func (s *WorktreeSuite) TestStatusEmptyDirty(c *C) {
1036 fs := memfs.New()
1037 err := util.WriteFile(fs, "foo", []byte("foo"), 0755)
1038 c.Assert(err, IsNil)
1039
1040 storage := memory.NewStorage()
1041
1042 r, err := Init(storage, fs)
1043 c.Assert(err, IsNil)
1044
1045 w, err := r.Worktree()
1046 c.Assert(err, IsNil)
1047
1048 status, err := w.Status()
1049 c.Assert(err, IsNil)
1050 c.Assert(status.IsClean(), Equals, false)
1051 c.Assert(status, HasLen, 1)
1052}
1053
1054func (s *WorktreeSuite) TestStatusUnmodified(c *C) {
1055 fs := memfs.New()
1056 w := &Worktree{
1057 r: s.Repository,
1058 Filesystem: fs,
1059 }
1060
1061 err := w.Checkout(&CheckoutOptions{Force: true})
1062 c.Assert(err, IsNil)
1063
1064 status, err := w.StatusWithOptions(StatusOptions{Strategy: Preload})
1065 c.Assert(err, IsNil)
1066 c.Assert(status.IsClean(), Equals, true)
1067 c.Assert(status.IsUntracked("LICENSE"), Equals, false)
1068
1069 c.Assert(status.File("LICENSE").Staging, Equals, Unmodified)
1070 c.Assert(status.File("LICENSE").Worktree, Equals, Unmodified)
1071
1072 status, err = w.StatusWithOptions(StatusOptions{Strategy: Empty})
1073 c.Assert(err, IsNil)
1074 c.Assert(status.IsClean(), Equals, true)
1075 c.Assert(status.IsUntracked("LICENSE"), Equals, false)
1076
1077 c.Assert(status.File("LICENSE").Staging, Equals, Untracked)
1078 c.Assert(status.File("LICENSE").Worktree, Equals, Untracked)
1079}
1080
1081func (s *WorktreeSuite) TestReset(c *C) {
1082 fs := memfs.New()
1083 w := &Worktree{
1084 r: s.Repository,
1085 Filesystem: fs,
1086 }
1087
1088 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")
1089
1090 err := w.Checkout(&CheckoutOptions{})
1091 c.Assert(err, IsNil)
1092
1093 branch, err := w.r.Reference(plumbing.Master, false)
1094 c.Assert(err, IsNil)
1095 c.Assert(branch.Hash(), Not(Equals), commit)
1096
1097 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commit})
1098 c.Assert(err, IsNil)
1099
1100 branch, err = w.r.Reference(plumbing.Master, false)
1101 c.Assert(err, IsNil)
1102 c.Assert(branch.Hash(), Equals, commit)
1103
1104 status, err := w.Status()
1105 c.Assert(err, IsNil)
1106 c.Assert(status.IsClean(), Equals, true)
1107}
1108
1109func (s *WorktreeSuite) TestResetWithUntracked(c *C) {
1110 fs := memfs.New()
1111 w := &Worktree{
1112 r: s.Repository,
1113 Filesystem: fs,
1114 }
1115
1116 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")
1117
1118 err := w.Checkout(&CheckoutOptions{})
1119 c.Assert(err, IsNil)
1120
1121 err = util.WriteFile(fs, "foo", nil, 0755)
1122 c.Assert(err, IsNil)
1123
1124 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commit})
1125 c.Assert(err, IsNil)
1126
1127 status, err := w.Status()
1128 c.Assert(err, IsNil)
1129 c.Assert(status.IsClean(), Equals, true)
1130}
1131
1132func (s *WorktreeSuite) TestResetSoft(c *C) {
1133 fs := memfs.New()
1134 w := &Worktree{
1135 r: s.Repository,
1136 Filesystem: fs,
1137 }
1138
1139 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")
1140
1141 err := w.Checkout(&CheckoutOptions{})
1142 c.Assert(err, IsNil)
1143
1144 err = w.Reset(&ResetOptions{Mode: SoftReset, Commit: commit})
1145 c.Assert(err, IsNil)
1146
1147 branch, err := w.r.Reference(plumbing.Master, false)
1148 c.Assert(err, IsNil)
1149 c.Assert(branch.Hash(), Equals, commit)
1150
1151 status, err := w.Status()
1152 c.Assert(err, IsNil)
1153 c.Assert(status.IsClean(), Equals, false)
1154 c.Assert(status.File("CHANGELOG").Staging, Equals, Added)
1155}
1156
1157func (s *WorktreeSuite) TestResetMixed(c *C) {
1158 fs := memfs.New()
1159 w := &Worktree{
1160 r: s.Repository,
1161 Filesystem: fs,
1162 }
1163
1164 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")
1165
1166 err := w.Checkout(&CheckoutOptions{})
1167 c.Assert(err, IsNil)
1168
1169 err = w.Reset(&ResetOptions{Mode: MixedReset, Commit: commit})
1170 c.Assert(err, IsNil)
1171
1172 branch, err := w.r.Reference(plumbing.Master, false)
1173 c.Assert(err, IsNil)
1174 c.Assert(branch.Hash(), Equals, commit)
1175
1176 status, err := w.Status()
1177 c.Assert(err, IsNil)
1178 c.Assert(status.IsClean(), Equals, false)
1179 c.Assert(status.File("CHANGELOG").Staging, Equals, Untracked)
1180}
1181
1182func (s *WorktreeSuite) TestResetMerge(c *C) {
1183 fs := memfs.New()
1184 w := &Worktree{
1185 r: s.Repository,
1186 Filesystem: fs,
1187 }
1188
1189 commitA := plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294")
1190 commitB := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")
1191
1192 err := w.Checkout(&CheckoutOptions{})
1193 c.Assert(err, IsNil)
1194
1195 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commitA})
1196 c.Assert(err, IsNil)
1197
1198 branch, err := w.r.Reference(plumbing.Master, false)
1199 c.Assert(err, IsNil)
1200 c.Assert(branch.Hash(), Equals, commitA)
1201
1202 f, err := fs.Create(".gitignore")
1203 c.Assert(err, IsNil)
1204 _, err = f.Write([]byte("foo"))
1205 c.Assert(err, IsNil)
1206 err = f.Close()
1207 c.Assert(err, IsNil)
1208
1209 err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commitB})
1210 c.Assert(err, Equals, ErrUnstagedChanges)
1211
1212 branch, err = w.r.Reference(plumbing.Master, false)
1213 c.Assert(err, IsNil)
1214 c.Assert(branch.Hash(), Equals, commitA)
1215}
1216
1217func (s *WorktreeSuite) TestResetHard(c *C) {
1218 fs := memfs.New()
1219 w := &Worktree{
1220 r: s.Repository,
1221 Filesystem: fs,
1222 }
1223
1224 commit := plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")
1225
1226 err := w.Checkout(&CheckoutOptions{})
1227 c.Assert(err, IsNil)
1228
1229 f, err := fs.Create(".gitignore")
1230 c.Assert(err, IsNil)
1231 _, err = f.Write([]byte("foo"))
1232 c.Assert(err, IsNil)
1233 err = f.Close()
1234 c.Assert(err, IsNil)
1235
1236 err = w.Reset(&ResetOptions{Mode: HardReset, Commit: commit})
1237 c.Assert(err, IsNil)
1238
1239 branch, err := w.r.Reference(plumbing.Master, false)
1240 c.Assert(err, IsNil)
1241 c.Assert(branch.Hash(), Equals, commit)
1242}
1243
1244func (s *WorktreeSuite) TestResetHardSubFolders(c *C) {
1245 fs := memfs.New()
1246 w := &Worktree{
1247 r: s.Repository,
1248 Filesystem: fs,
1249 }
1250
1251 err := w.Checkout(&CheckoutOptions{})
1252 c.Assert(err, IsNil)
1253
1254 err = fs.MkdirAll("dir", os.ModePerm)
1255 c.Assert(err, IsNil)
1256 tf, err := fs.Create("./dir/testfile.txt")
1257 c.Assert(err, IsNil)
1258 _, err = tf.Write([]byte("testfile content"))
1259 c.Assert(err, IsNil)
1260 err = tf.Close()
1261 c.Assert(err, IsNil)
1262 _, err = w.Add("dir/testfile.txt")
1263 c.Assert(err, IsNil)
1264 _, err = w.Commit("testcommit", &CommitOptions{Author: &object.Signature{Name: "name", Email: "email"}})
1265 c.Assert(err, IsNil)
1266
1267 err = fs.Remove("dir/testfile.txt")
1268 c.Assert(err, IsNil)
1269
1270 status, err := w.Status()
1271 c.Assert(err, IsNil)
1272 c.Assert(status.IsClean(), Equals, false)
1273
1274 err = w.Reset(&ResetOptions{Files: []string{"dir/testfile.txt"}, Mode: HardReset})
1275 c.Assert(err, IsNil)
1276
1277 status, err = w.Status()
1278 c.Assert(err, IsNil)
1279 c.Assert(status.IsClean(), Equals, true)
1280}
1281
1282func (s *WorktreeSuite) TestResetHardWithGitIgnore(c *C) {
1283 fs := memfs.New()
1284 w := &Worktree{
1285 r: s.Repository,
1286 Filesystem: fs,
1287 }
1288
1289 err := w.Checkout(&CheckoutOptions{})
1290 c.Assert(err, IsNil)
1291
1292 tf, err := fs.Create("newTestFile.txt")
1293 c.Assert(err, IsNil)
1294 _, err = tf.Write([]byte("testfile content"))
1295 c.Assert(err, IsNil)
1296 err = tf.Close()
1297 c.Assert(err, IsNil)
1298 _, err = w.Add("newTestFile.txt")
1299 c.Assert(err, IsNil)
1300 _, err = w.Commit("testcommit", &CommitOptions{Author: &object.Signature{Name: "name", Email: "email"}})
1301 c.Assert(err, IsNil)
1302
1303 err = fs.Remove("newTestFile.txt")
1304 c.Assert(err, IsNil)
1305 f, err := fs.Create(".gitignore")
1306 c.Assert(err, IsNil)
1307 _, err = f.Write([]byte("foo\n"))
1308 c.Assert(err, IsNil)
1309 _, err = f.Write([]byte("newTestFile.txt\n"))
1310 c.Assert(err, IsNil)
1311 err = f.Close()
1312 c.Assert(err, IsNil)
1313
1314 status, err := w.Status()
1315 c.Assert(err, IsNil)
1316 c.Assert(status.IsClean(), Equals, false)
1317
1318 err = w.Reset(&ResetOptions{Mode: HardReset})
1319 c.Assert(err, IsNil)
1320
1321 status, err = w.Status()
1322 c.Assert(err, IsNil)
1323 c.Assert(status.IsClean(), Equals, true)
1324}
1325
1326func (s *WorktreeSuite) TestResetSparsely(c *C) {
1327 fs := memfs.New()
1328 w := &Worktree{
1329 r: s.Repository,
1330 Filesystem: fs,
1331 }
1332
1333 sparseResetDirs := []string{"php"}
1334
1335 err := w.ResetSparsely(&ResetOptions{Mode: HardReset}, sparseResetDirs)
1336 c.Assert(err, IsNil)
1337
1338 files, err := fs.ReadDir("/")
1339 c.Assert(err, IsNil)
1340 c.Assert(files, HasLen, 1)
1341 c.Assert(files[0].Name(), Equals, "php")
1342
1343 files, err = fs.ReadDir("/php")
1344 c.Assert(err, IsNil)
1345 c.Assert(files, HasLen, 1)
1346 c.Assert(files[0].Name(), Equals, "crappy.php")
1347}
1348
1349func (s *WorktreeSuite) TestStatusAfterCheckout(c *C) {
1350 fs := memfs.New()
1351 w := &Worktree{
1352 r: s.Repository,
1353 Filesystem: fs,
1354 }
1355
1356 err := w.Checkout(&CheckoutOptions{Force: true})
1357 c.Assert(err, IsNil)
1358
1359 status, err := w.Status()
1360 c.Assert(err, IsNil)
1361 c.Assert(status.IsClean(), Equals, true)
1362
1363}
1364
1365func (s *WorktreeSuite) TestStatusModified(c *C) {
1366 fs := s.TemporalFilesystem(c)
1367
1368 w := &Worktree{
1369 r: s.Repository,
1370 Filesystem: fs,
1371 }
1372
1373 err := w.Checkout(&CheckoutOptions{})
1374 c.Assert(err, IsNil)
1375
1376 f, err := fs.Create(".gitignore")
1377 c.Assert(err, IsNil)
1378 _, err = f.Write([]byte("foo"))
1379 c.Assert(err, IsNil)
1380 err = f.Close()
1381 c.Assert(err, IsNil)
1382
1383 status, err := w.Status()
1384 c.Assert(err, IsNil)
1385 c.Assert(status.IsClean(), Equals, false)
1386 c.Assert(status.File(".gitignore").Worktree, Equals, Modified)
1387}
1388
1389func (s *WorktreeSuite) TestStatusIgnored(c *C) {
1390 fs := memfs.New()
1391 w := &Worktree{
1392 r: s.Repository,
1393 Filesystem: fs,
1394 }
1395
1396 w.Checkout(&CheckoutOptions{})
1397
1398 fs.MkdirAll("another", os.ModePerm)
1399 f, _ := fs.Create("another/file")
1400 f.Close()
1401 fs.MkdirAll("vendor/github.com", os.ModePerm)
1402 f, _ = fs.Create("vendor/github.com/file")
1403 f.Close()
1404 fs.MkdirAll("vendor/gopkg.in", os.ModePerm)
1405 f, _ = fs.Create("vendor/gopkg.in/file")
1406 f.Close()
1407
1408 status, _ := w.Status()
1409 c.Assert(len(status), Equals, 3)
1410 _, ok := status["another/file"]
1411 c.Assert(ok, Equals, true)
1412 _, ok = status["vendor/github.com/file"]
1413 c.Assert(ok, Equals, true)
1414 _, ok = status["vendor/gopkg.in/file"]
1415 c.Assert(ok, Equals, true)
1416
1417 f, _ = fs.Create(".gitignore")
1418 f.Write([]byte("vendor/g*/"))
1419 f.Close()
1420 f, _ = fs.Create("vendor/.gitignore")
1421 f.Write([]byte("!github.com/\n"))
1422 f.Close()
1423
1424 status, _ = w.Status()
1425 c.Assert(len(status), Equals, 4)
1426 _, ok = status[".gitignore"]
1427 c.Assert(ok, Equals, true)
1428 _, ok = status["another/file"]
1429 c.Assert(ok, Equals, true)
1430 _, ok = status["vendor/.gitignore"]
1431 c.Assert(ok, Equals, true)
1432 _, ok = status["vendor/github.com/file"]
1433 c.Assert(ok, Equals, true)
1434}
1435
1436func (s *WorktreeSuite) TestStatusUntracked(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 f, err := w.Filesystem.Create("foo")
1447 c.Assert(err, IsNil)
1448 c.Assert(f.Close(), IsNil)
1449
1450 status, err := w.Status()
1451 c.Assert(err, IsNil)
1452 c.Assert(status.File("foo").Staging, Equals, Untracked)
1453 c.Assert(status.File("foo").Worktree, Equals, Untracked)
1454}
1455
1456func (s *WorktreeSuite) TestStatusDeleted(c *C) {
1457 fs := s.TemporalFilesystem(c)
1458
1459 w := &Worktree{
1460 r: s.Repository,
1461 Filesystem: fs,
1462 }
1463
1464 err := w.Checkout(&CheckoutOptions{})
1465 c.Assert(err, IsNil)
1466
1467 err = fs.Remove(".gitignore")
1468 c.Assert(err, IsNil)
1469
1470 status, err := w.Status()
1471 c.Assert(err, IsNil)
1472 c.Assert(status.IsClean(), Equals, false)
1473 c.Assert(status.File(".gitignore").Worktree, Equals, Deleted)
1474}
1475
1476func (s *WorktreeSuite) TestSubmodule(c *C) {
1477 path := fixtures.ByTag("submodule").One().Worktree().Root()
1478 r, err := PlainOpen(path)
1479 c.Assert(err, IsNil)
1480
1481 w, err := r.Worktree()
1482 c.Assert(err, IsNil)
1483
1484 m, err := w.Submodule("basic")
1485 c.Assert(err, IsNil)
1486
1487 c.Assert(m.Config().Name, Equals, "basic")
1488}
1489
1490func (s *WorktreeSuite) TestSubmodules(c *C) {
1491 path := fixtures.ByTag("submodule").One().Worktree().Root()
1492 r, err := PlainOpen(path)
1493 c.Assert(err, IsNil)
1494
1495 w, err := r.Worktree()
1496 c.Assert(err, IsNil)
1497
1498 l, err := w.Submodules()
1499 c.Assert(err, IsNil)
1500
1501 c.Assert(l, HasLen, 2)
1502}
1503
1504func (s *WorktreeSuite) TestAddUntracked(c *C) {
1505 fs := memfs.New()
1506 w := &Worktree{
1507 r: s.Repository,
1508 Filesystem: fs,
1509 }
1510
1511 err := w.Checkout(&CheckoutOptions{Force: true})
1512 c.Assert(err, IsNil)
1513
1514 idx, err := w.r.Storer.Index()
1515 c.Assert(err, IsNil)
1516 c.Assert(idx.Entries, HasLen, 9)
1517
1518 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755)
1519 c.Assert(err, IsNil)
1520
1521 hash, err := w.Add("foo")
1522 c.Assert(hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5")
1523 c.Assert(err, IsNil)
1524
1525 idx, err = w.r.Storer.Index()
1526 c.Assert(err, IsNil)
1527 c.Assert(idx.Entries, HasLen, 10)
1528
1529 e, err := idx.Entry("foo")
1530 c.Assert(err, IsNil)
1531 c.Assert(e.Hash, Equals, hash)
1532 c.Assert(e.Mode, Equals, filemode.Executable)
1533
1534 status, err := w.Status()
1535 c.Assert(err, IsNil)
1536 c.Assert(status, HasLen, 1)
1537
1538 file := status.File("foo")
1539 c.Assert(file.Staging, Equals, Added)
1540 c.Assert(file.Worktree, Equals, Unmodified)
1541
1542 obj, err := w.r.Storer.EncodedObject(plumbing.BlobObject, hash)
1543 c.Assert(err, IsNil)
1544 c.Assert(obj, NotNil)
1545 c.Assert(obj.Size(), Equals, int64(3))
1546}
1547
1548func (s *WorktreeSuite) TestIgnored(c *C) {
1549 fs := memfs.New()
1550 w := &Worktree{
1551 r: s.Repository,
1552 Filesystem: fs,
1553 }
1554
1555 w.Excludes = make([]gitignore.Pattern, 0)
1556 w.Excludes = append(w.Excludes, gitignore.ParsePattern("foo", nil))
1557
1558 err := w.Checkout(&CheckoutOptions{Force: true})
1559 c.Assert(err, IsNil)
1560
1561 idx, err := w.r.Storer.Index()
1562 c.Assert(err, IsNil)
1563 c.Assert(idx.Entries, HasLen, 9)
1564
1565 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755)
1566 c.Assert(err, IsNil)
1567
1568 status, err := w.Status()
1569 c.Assert(err, IsNil)
1570 c.Assert(status, HasLen, 0)
1571
1572 file := status.File("foo")
1573 c.Assert(file.Staging, Equals, Untracked)
1574 c.Assert(file.Worktree, Equals, Untracked)
1575}
1576
1577func (s *WorktreeSuite) TestExcludedNoGitignore(c *C) {
1578 f := fixtures.ByTag("empty").One()
1579 r := s.NewRepository(f)
1580
1581 fs := memfs.New()
1582 w := &Worktree{
1583 r: r,
1584 Filesystem: fs,
1585 }
1586
1587 _, err := fs.Open(".gitignore")
1588 c.Assert(err, Equals, os.ErrNotExist)
1589
1590 w.Excludes = make([]gitignore.Pattern, 0)
1591 w.Excludes = append(w.Excludes, gitignore.ParsePattern("foo", nil))
1592
1593 err = util.WriteFile(w.Filesystem, "foo", []byte("FOO"), 0755)
1594 c.Assert(err, IsNil)
1595
1596 status, err := w.Status()
1597 c.Assert(err, IsNil)
1598 c.Assert(status, HasLen, 0)
1599
1600 file := status.File("foo")
1601 c.Assert(file.Staging, Equals, Untracked)
1602 c.Assert(file.Worktree, Equals, Untracked)
1603}
1604
1605func (s *WorktreeSuite) TestAddModified(c *C) {
1606 fs := memfs.New()
1607 w := &Worktree{
1608 r: s.Repository,
1609 Filesystem: fs,
1610 }
1611
1612 err := w.Checkout(&CheckoutOptions{Force: true})
1613 c.Assert(err, IsNil)
1614
1615 idx, err := w.r.Storer.Index()
1616 c.Assert(err, IsNil)
1617 c.Assert(idx.Entries, HasLen, 9)
1618
1619 err = util.WriteFile(w.Filesystem, "LICENSE", []byte("FOO"), 0644)
1620 c.Assert(err, IsNil)
1621
1622 hash, err := w.Add("LICENSE")
1623 c.Assert(err, IsNil)
1624 c.Assert(hash.String(), Equals, "d96c7efbfec2814ae0301ad054dc8d9fc416c9b5")
1625
1626 idx, err = w.r.Storer.Index()
1627 c.Assert(err, IsNil)
1628 c.Assert(idx.Entries, HasLen, 9)
1629
1630 e, err := idx.Entry("LICENSE")
1631 c.Assert(err, IsNil)
1632 c.Assert(e.Hash, Equals, hash)
1633 c.Assert(e.Mode, Equals, filemode.Regular)
1634
1635 status, err := w.Status()
1636 c.Assert(err, IsNil)
1637 c.Assert(status, HasLen, 1)
1638
1639 file := status.File("LICENSE")
1640 c.Assert(file.Staging, Equals, Modified)
1641 c.Assert(file.Worktree, Equals, Unmodified)
1642}
1643
1644func (s *WorktreeSuite) TestAddUnmodified(c *C) {
1645 fs := memfs.New()
1646 w := &Worktree{
1647 r: s.Repository,
1648 Filesystem: fs,
1649 }
1650
1651 err := w.Checkout(&CheckoutOptions{Force: true})
1652 c.Assert(err, IsNil)
1653
1654 hash, err := w.Add("LICENSE")
1655 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
1656 c.Assert(err, IsNil)
1657}
1658
1659func (s *WorktreeSuite) TestAddRemoved(c *C) {
1660 fs := memfs.New()
1661 w := &Worktree{
1662 r: s.Repository,
1663 Filesystem: fs,
1664 }
1665
1666 err := w.Checkout(&CheckoutOptions{Force: true})
1667 c.Assert(err, IsNil)
1668
1669 idx, err := w.r.Storer.Index()
1670 c.Assert(err, IsNil)
1671 c.Assert(idx.Entries, HasLen, 9)
1672
1673 err = w.Filesystem.Remove("LICENSE")
1674 c.Assert(err, IsNil)
1675
1676 hash, err := w.Add("LICENSE")
1677 c.Assert(err, IsNil)
1678 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
1679
1680 e, err := idx.Entry("LICENSE")
1681 c.Assert(err, IsNil)
1682 c.Assert(e.Hash, Equals, hash)
1683 c.Assert(e.Mode, Equals, filemode.Regular)
1684
1685 status, err := w.Status()
1686 c.Assert(err, IsNil)
1687 c.Assert(status, HasLen, 1)
1688
1689 file := status.File("LICENSE")
1690 c.Assert(file.Staging, Equals, Deleted)
1691}
1692
1693func (s *WorktreeSuite) TestAddRemovedInDirectory(c *C) {
1694 fs := memfs.New()
1695 w := &Worktree{
1696 r: s.Repository,
1697 Filesystem: fs,
1698 }
1699
1700 err := w.Checkout(&CheckoutOptions{Force: true})
1701 c.Assert(err, IsNil)
1702
1703 idx, err := w.r.Storer.Index()
1704 c.Assert(err, IsNil)
1705 c.Assert(idx.Entries, HasLen, 9)
1706
1707 err = w.Filesystem.Remove("go/example.go")
1708 c.Assert(err, IsNil)
1709
1710 err = w.Filesystem.Remove("json/short.json")
1711 c.Assert(err, IsNil)
1712
1713 hash, err := w.Add("go")
1714 c.Assert(err, IsNil)
1715 c.Assert(hash.IsZero(), Equals, true)
1716
1717 e, err := idx.Entry("go/example.go")
1718 c.Assert(err, IsNil)
1719 c.Assert(e.Hash, Equals, plumbing.NewHash("880cd14280f4b9b6ed3986d6671f907d7cc2a198"))
1720 c.Assert(e.Mode, Equals, filemode.Regular)
1721
1722 e, err = idx.Entry("json/short.json")
1723 c.Assert(err, IsNil)
1724 c.Assert(e.Hash, Equals, plumbing.NewHash("c8f1d8c61f9da76f4cb49fd86322b6e685dba956"))
1725 c.Assert(e.Mode, Equals, filemode.Regular)
1726
1727 status, err := w.Status()
1728 c.Assert(err, IsNil)
1729 c.Assert(status, HasLen, 2)
1730
1731 file := status.File("go/example.go")
1732 c.Assert(file.Staging, Equals, Deleted)
1733
1734 file = status.File("json/short.json")
1735 c.Assert(file.Staging, Equals, Unmodified)
1736}
1737
1738func (s *WorktreeSuite) TestAddRemovedInDirectoryWithTrailingSlash(c *C) {
1739 fs := memfs.New()
1740 w := &Worktree{
1741 r: s.Repository,
1742 Filesystem: fs,
1743 }
1744
1745 err := w.Checkout(&CheckoutOptions{Force: true})
1746 c.Assert(err, IsNil)
1747
1748 idx, err := w.r.Storer.Index()
1749 c.Assert(err, IsNil)
1750 c.Assert(idx.Entries, HasLen, 9)
1751
1752 err = w.Filesystem.Remove("go/example.go")
1753 c.Assert(err, IsNil)
1754
1755 err = w.Filesystem.Remove("json/short.json")
1756 c.Assert(err, IsNil)
1757
1758 hash, err := w.Add("go/")
1759 c.Assert(err, IsNil)
1760 c.Assert(hash.IsZero(), Equals, true)
1761
1762 e, err := idx.Entry("go/example.go")
1763 c.Assert(err, IsNil)
1764 c.Assert(e.Hash, Equals, plumbing.NewHash("880cd14280f4b9b6ed3986d6671f907d7cc2a198"))
1765 c.Assert(e.Mode, Equals, filemode.Regular)
1766
1767 e, err = idx.Entry("json/short.json")
1768 c.Assert(err, IsNil)
1769 c.Assert(e.Hash, Equals, plumbing.NewHash("c8f1d8c61f9da76f4cb49fd86322b6e685dba956"))
1770 c.Assert(e.Mode, Equals, filemode.Regular)
1771
1772 status, err := w.Status()
1773 c.Assert(err, IsNil)
1774 c.Assert(status, HasLen, 2)
1775
1776 file := status.File("go/example.go")
1777 c.Assert(file.Staging, Equals, Deleted)
1778
1779 file = status.File("json/short.json")
1780 c.Assert(file.Staging, Equals, Unmodified)
1781}
1782
1783func (s *WorktreeSuite) TestAddRemovedInDirectoryDot(c *C) {
1784 fs := memfs.New()
1785 w := &Worktree{
1786 r: s.Repository,
1787 Filesystem: fs,
1788 }
1789
1790 err := w.Checkout(&CheckoutOptions{Force: true})
1791 c.Assert(err, IsNil)
1792
1793 idx, err := w.r.Storer.Index()
1794 c.Assert(err, IsNil)
1795 c.Assert(idx.Entries, HasLen, 9)
1796
1797 err = w.Filesystem.Remove("go/example.go")
1798 c.Assert(err, IsNil)
1799
1800 err = w.Filesystem.Remove("json/short.json")
1801 c.Assert(err, IsNil)
1802
1803 hash, err := w.Add(".")
1804 c.Assert(err, IsNil)
1805 c.Assert(hash.IsZero(), Equals, true)
1806
1807 e, err := idx.Entry("go/example.go")
1808 c.Assert(err, IsNil)
1809 c.Assert(e.Hash, Equals, plumbing.NewHash("880cd14280f4b9b6ed3986d6671f907d7cc2a198"))
1810 c.Assert(e.Mode, Equals, filemode.Regular)
1811
1812 e, err = idx.Entry("json/short.json")
1813 c.Assert(err, IsNil)
1814 c.Assert(e.Hash, Equals, plumbing.NewHash("c8f1d8c61f9da76f4cb49fd86322b6e685dba956"))
1815 c.Assert(e.Mode, Equals, filemode.Regular)
1816
1817 status, err := w.Status()
1818 c.Assert(err, IsNil)
1819 c.Assert(status, HasLen, 2)
1820
1821 file := status.File("go/example.go")
1822 c.Assert(file.Staging, Equals, Deleted)
1823
1824 file = status.File("json/short.json")
1825 c.Assert(file.Staging, Equals, Deleted)
1826}
1827
1828func (s *WorktreeSuite) TestAddSymlink(c *C) {
1829 dir := c.MkDir()
1830
1831 r, err := PlainInit(dir, false)
1832 c.Assert(err, IsNil)
1833 err = util.WriteFile(r.wt, "foo", []byte("qux"), 0644)
1834 c.Assert(err, IsNil)
1835 err = r.wt.Symlink("foo", "bar")
1836 c.Assert(err, IsNil)
1837
1838 w, err := r.Worktree()
1839 c.Assert(err, IsNil)
1840 h, err := w.Add("foo")
1841 c.Assert(err, IsNil)
1842 c.Assert(h, Not(Equals), plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"))
1843
1844 h, err = w.Add("bar")
1845 c.Assert(err, IsNil)
1846 c.Assert(h, Equals, plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"))
1847
1848 obj, err := w.r.Storer.EncodedObject(plumbing.BlobObject, h)
1849 c.Assert(err, IsNil)
1850 c.Assert(obj, NotNil)
1851 c.Assert(obj.Size(), Equals, int64(3))
1852}
1853
1854func (s *WorktreeSuite) TestAddDirectory(c *C) {
1855 fs := memfs.New()
1856 w := &Worktree{
1857 r: s.Repository,
1858 Filesystem: fs,
1859 }
1860
1861 err := w.Checkout(&CheckoutOptions{Force: true})
1862 c.Assert(err, IsNil)
1863
1864 idx, err := w.r.Storer.Index()
1865 c.Assert(err, IsNil)
1866 c.Assert(idx.Entries, HasLen, 9)
1867
1868 err = util.WriteFile(w.Filesystem, "qux/foo", []byte("FOO"), 0755)
1869 c.Assert(err, IsNil)
1870 err = util.WriteFile(w.Filesystem, "qux/baz/bar", []byte("BAR"), 0755)
1871 c.Assert(err, IsNil)
1872
1873 h, err := w.Add("qux")
1874 c.Assert(err, IsNil)
1875 c.Assert(h.IsZero(), Equals, true)
1876
1877 idx, err = w.r.Storer.Index()
1878 c.Assert(err, IsNil)
1879 c.Assert(idx.Entries, HasLen, 11)
1880
1881 e, err := idx.Entry("qux/foo")
1882 c.Assert(err, IsNil)
1883 c.Assert(e.Mode, Equals, filemode.Executable)
1884
1885 e, err = idx.Entry("qux/baz/bar")
1886 c.Assert(err, IsNil)
1887 c.Assert(e.Mode, Equals, filemode.Executable)
1888
1889 status, err := w.Status()
1890 c.Assert(err, IsNil)
1891 c.Assert(status, HasLen, 2)
1892
1893 file := status.File("qux/foo")
1894 c.Assert(file.Staging, Equals, Added)
1895 c.Assert(file.Worktree, Equals, Unmodified)
1896
1897 file = status.File("qux/baz/bar")
1898 c.Assert(file.Staging, Equals, Added)
1899 c.Assert(file.Worktree, Equals, Unmodified)
1900}
1901
1902func (s *WorktreeSuite) TestAddDirectoryErrorNotFound(c *C) {
1903 r, _ := Init(memory.NewStorage(), memfs.New())
1904 w, _ := r.Worktree()
1905
1906 h, err := w.Add("foo")
1907 c.Assert(err, NotNil)
1908 c.Assert(h.IsZero(), Equals, true)
1909}
1910
1911func (s *WorktreeSuite) TestAddAll(c *C) {
1912 fs := memfs.New()
1913 w := &Worktree{
1914 r: s.Repository,
1915 Filesystem: fs,
1916 }
1917
1918 err := w.Checkout(&CheckoutOptions{Force: true})
1919 c.Assert(err, IsNil)
1920
1921 idx, err := w.r.Storer.Index()
1922 c.Assert(err, IsNil)
1923 c.Assert(idx.Entries, HasLen, 9)
1924
1925 err = util.WriteFile(w.Filesystem, "file1", []byte("file1"), 0644)
1926 c.Assert(err, IsNil)
1927
1928 err = util.WriteFile(w.Filesystem, "file2", []byte("file2"), 0644)
1929 c.Assert(err, IsNil)
1930
1931 err = util.WriteFile(w.Filesystem, "file3", []byte("ignore me"), 0644)
1932 c.Assert(err, IsNil)
1933
1934 w.Excludes = make([]gitignore.Pattern, 0)
1935 w.Excludes = append(w.Excludes, gitignore.ParsePattern("file3", nil))
1936
1937 err = w.AddWithOptions(&AddOptions{All: true})
1938 c.Assert(err, IsNil)
1939
1940 idx, err = w.r.Storer.Index()
1941 c.Assert(err, IsNil)
1942 c.Assert(idx.Entries, HasLen, 11)
1943
1944 status, err := w.Status()
1945 c.Assert(err, IsNil)
1946 c.Assert(status, HasLen, 2)
1947
1948 file1 := status.File("file1")
1949 c.Assert(file1.Staging, Equals, Added)
1950 file2 := status.File("file2")
1951 c.Assert(file2.Staging, Equals, Added)
1952 file3 := status.File("file3")
1953 c.Assert(file3.Staging, Equals, Untracked)
1954 c.Assert(file3.Worktree, Equals, Untracked)
1955}
1956
1957func (s *WorktreeSuite) TestAddGlob(c *C) {
1958 fs := memfs.New()
1959 w := &Worktree{
1960 r: s.Repository,
1961 Filesystem: fs,
1962 }
1963
1964 err := w.Checkout(&CheckoutOptions{Force: true})
1965 c.Assert(err, IsNil)
1966
1967 idx, err := w.r.Storer.Index()
1968 c.Assert(err, IsNil)
1969 c.Assert(idx.Entries, HasLen, 9)
1970
1971 err = util.WriteFile(w.Filesystem, "qux/qux", []byte("QUX"), 0755)
1972 c.Assert(err, IsNil)
1973 err = util.WriteFile(w.Filesystem, "qux/baz", []byte("BAZ"), 0755)
1974 c.Assert(err, IsNil)
1975 err = util.WriteFile(w.Filesystem, "qux/bar/baz", []byte("BAZ"), 0755)
1976 c.Assert(err, IsNil)
1977
1978 err = w.AddWithOptions(&AddOptions{Glob: w.Filesystem.Join("qux", "b*")})
1979 c.Assert(err, IsNil)
1980
1981 idx, err = w.r.Storer.Index()
1982 c.Assert(err, IsNil)
1983 c.Assert(idx.Entries, HasLen, 11)
1984
1985 e, err := idx.Entry("qux/baz")
1986 c.Assert(err, IsNil)
1987 c.Assert(e.Mode, Equals, filemode.Executable)
1988
1989 e, err = idx.Entry("qux/bar/baz")
1990 c.Assert(err, IsNil)
1991 c.Assert(e.Mode, Equals, filemode.Executable)
1992
1993 status, err := w.Status()
1994 c.Assert(err, IsNil)
1995 c.Assert(status, HasLen, 3)
1996
1997 file := status.File("qux/qux")
1998 c.Assert(file.Staging, Equals, Untracked)
1999 c.Assert(file.Worktree, Equals, Untracked)
2000
2001 file = status.File("qux/baz")
2002 c.Assert(file.Staging, Equals, Added)
2003 c.Assert(file.Worktree, Equals, Unmodified)
2004
2005 file = status.File("qux/bar/baz")
2006 c.Assert(file.Staging, Equals, Added)
2007 c.Assert(file.Worktree, Equals, Unmodified)
2008}
2009
2010func (s *WorktreeSuite) TestAddFilenameStartingWithDot(c *C) {
2011 fs := memfs.New()
2012 w := &Worktree{
2013 r: s.Repository,
2014 Filesystem: fs,
2015 }
2016
2017 err := w.Checkout(&CheckoutOptions{Force: true})
2018 c.Assert(err, IsNil)
2019
2020 idx, err := w.r.Storer.Index()
2021 c.Assert(err, IsNil)
2022 c.Assert(idx.Entries, HasLen, 9)
2023
2024 err = util.WriteFile(w.Filesystem, "qux", []byte("QUX"), 0o755)
2025 c.Assert(err, IsNil)
2026 err = util.WriteFile(w.Filesystem, "baz", []byte("BAZ"), 0o755)
2027 c.Assert(err, IsNil)
2028 err = util.WriteFile(w.Filesystem, "foo/bar/baz", []byte("BAZ"), 0o755)
2029 c.Assert(err, IsNil)
2030
2031 _, err = w.Add("./qux")
2032 c.Assert(err, IsNil)
2033
2034 _, err = w.Add("./baz")
2035 c.Assert(err, IsNil)
2036
2037 _, err = w.Add("foo/bar/../bar/./baz")
2038 c.Assert(err, IsNil)
2039
2040 idx, err = w.r.Storer.Index()
2041 c.Assert(err, IsNil)
2042 c.Assert(idx.Entries, HasLen, 12)
2043
2044 e, err := idx.Entry("qux")
2045 c.Assert(err, IsNil)
2046 c.Assert(e.Mode, Equals, filemode.Executable)
2047
2048 e, err = idx.Entry("baz")
2049 c.Assert(err, IsNil)
2050 c.Assert(e.Mode, Equals, filemode.Executable)
2051
2052 status, err := w.Status()
2053 c.Assert(err, IsNil)
2054 c.Assert(status, HasLen, 3)
2055
2056 file := status.File("qux")
2057 c.Assert(file.Staging, Equals, Added)
2058 c.Assert(file.Worktree, Equals, Unmodified)
2059
2060 file = status.File("baz")
2061 c.Assert(file.Staging, Equals, Added)
2062 c.Assert(file.Worktree, Equals, Unmodified)
2063
2064 file = status.File("foo/bar/baz")
2065 c.Assert(file.Staging, Equals, Added)
2066 c.Assert(file.Worktree, Equals, Unmodified)
2067
2068}
2069
2070func (s *WorktreeSuite) TestAddGlobErrorNoMatches(c *C) {
2071 r, _ := Init(memory.NewStorage(), memfs.New())
2072 w, _ := r.Worktree()
2073
2074 err := w.AddGlob("foo")
2075 c.Assert(err, Equals, ErrGlobNoMatches)
2076}
2077
2078func (s *WorktreeSuite) TestAddSkipStatusAddedPath(c *C) {
2079 fs := memfs.New()
2080 w := &Worktree{
2081 r: s.Repository,
2082 Filesystem: fs,
2083 }
2084
2085 err := w.Checkout(&CheckoutOptions{Force: true})
2086 c.Assert(err, IsNil)
2087
2088 idx, err := w.r.Storer.Index()
2089 c.Assert(err, IsNil)
2090 c.Assert(idx.Entries, HasLen, 9)
2091
2092 err = util.WriteFile(w.Filesystem, "file1", []byte("file1"), 0644)
2093 c.Assert(err, IsNil)
2094
2095 err = w.AddWithOptions(&AddOptions{Path: "file1", SkipStatus: true})
2096 c.Assert(err, IsNil)
2097
2098 idx, err = w.r.Storer.Index()
2099 c.Assert(err, IsNil)
2100 c.Assert(idx.Entries, HasLen, 10)
2101
2102 e, err := idx.Entry("file1")
2103 c.Assert(err, IsNil)
2104 c.Assert(e.Mode, Equals, filemode.Regular)
2105
2106 status, err := w.Status()
2107 c.Assert(err, IsNil)
2108 c.Assert(status, HasLen, 1)
2109
2110 file := status.File("file1")
2111 c.Assert(file.Staging, Equals, Added)
2112 c.Assert(file.Worktree, Equals, Unmodified)
2113}
2114
2115func (s *WorktreeSuite) TestAddSkipStatusModifiedPath(c *C) {
2116 fs := memfs.New()
2117 w := &Worktree{
2118 r: s.Repository,
2119 Filesystem: fs,
2120 }
2121
2122 err := w.Checkout(&CheckoutOptions{Force: true})
2123 c.Assert(err, IsNil)
2124
2125 idx, err := w.r.Storer.Index()
2126 c.Assert(err, IsNil)
2127 c.Assert(idx.Entries, HasLen, 9)
2128
2129 err = util.WriteFile(w.Filesystem, "LICENSE", []byte("file1"), 0644)
2130 c.Assert(err, IsNil)
2131
2132 err = w.AddWithOptions(&AddOptions{Path: "LICENSE", SkipStatus: true})
2133 c.Assert(err, IsNil)
2134
2135 idx, err = w.r.Storer.Index()
2136 c.Assert(err, IsNil)
2137 c.Assert(idx.Entries, HasLen, 9)
2138
2139 e, err := idx.Entry("LICENSE")
2140 c.Assert(err, IsNil)
2141 c.Assert(e.Mode, Equals, filemode.Regular)
2142
2143 status, err := w.Status()
2144 c.Assert(err, IsNil)
2145 c.Assert(status, HasLen, 1)
2146
2147 file := status.File("LICENSE")
2148 c.Assert(file.Staging, Equals, Modified)
2149 c.Assert(file.Worktree, Equals, Unmodified)
2150}
2151
2152func (s *WorktreeSuite) TestAddSkipStatusNonModifiedPath(c *C) {
2153 fs := memfs.New()
2154 w := &Worktree{
2155 r: s.Repository,
2156 Filesystem: fs,
2157 }
2158
2159 err := w.Checkout(&CheckoutOptions{Force: true})
2160 c.Assert(err, IsNil)
2161
2162 idx, err := w.r.Storer.Index()
2163 c.Assert(err, IsNil)
2164 c.Assert(idx.Entries, HasLen, 9)
2165
2166 err = w.AddWithOptions(&AddOptions{Path: "LICENSE", SkipStatus: true})
2167 c.Assert(err, IsNil)
2168
2169 idx, err = w.r.Storer.Index()
2170 c.Assert(err, IsNil)
2171 c.Assert(idx.Entries, HasLen, 9)
2172
2173 e, err := idx.Entry("LICENSE")
2174 c.Assert(err, IsNil)
2175 c.Assert(e.Mode, Equals, filemode.Regular)
2176
2177 status, err := w.Status()
2178 c.Assert(err, IsNil)
2179 c.Assert(status, HasLen, 0)
2180
2181 file := status.File("LICENSE")
2182 c.Assert(file.Staging, Equals, Untracked)
2183 c.Assert(file.Worktree, Equals, Untracked)
2184}
2185
2186func (s *WorktreeSuite) TestAddSkipStatusWithIgnoredPath(c *C) {
2187 fs := memfs.New()
2188 w := &Worktree{
2189 r: s.Repository,
2190 Filesystem: fs,
2191 }
2192
2193 err := w.Checkout(&CheckoutOptions{Force: true})
2194 c.Assert(err, IsNil)
2195
2196 idx, err := w.r.Storer.Index()
2197 c.Assert(err, IsNil)
2198 c.Assert(idx.Entries, HasLen, 9)
2199
2200 err = util.WriteFile(fs, ".gitignore", []byte("fileToIgnore\n"), 0755)
2201 c.Assert(err, IsNil)
2202 _, err = w.Add(".gitignore")
2203 c.Assert(err, IsNil)
2204 _, err = w.Commit("Added .gitignore", defaultTestCommitOptions())
2205 c.Assert(err, IsNil)
2206
2207 err = util.WriteFile(fs, "fileToIgnore", []byte("file to ignore"), 0644)
2208 c.Assert(err, IsNil)
2209
2210 status, err := w.Status()
2211 c.Assert(err, IsNil)
2212 c.Assert(status, HasLen, 0)
2213
2214 file := status.File("fileToIgnore")
2215 c.Assert(file.Staging, Equals, Untracked)
2216 c.Assert(file.Worktree, Equals, Untracked)
2217
2218 err = w.AddWithOptions(&AddOptions{Path: "fileToIgnore", SkipStatus: true})
2219 c.Assert(err, IsNil)
2220
2221 idx, err = w.r.Storer.Index()
2222 c.Assert(err, IsNil)
2223 c.Assert(idx.Entries, HasLen, 10)
2224
2225 e, err := idx.Entry("fileToIgnore")
2226 c.Assert(err, IsNil)
2227 c.Assert(e.Mode, Equals, filemode.Regular)
2228
2229 status, err = w.Status()
2230 c.Assert(err, IsNil)
2231 c.Assert(status, HasLen, 1)
2232
2233 file = status.File("fileToIgnore")
2234 c.Assert(file.Staging, Equals, Added)
2235 c.Assert(file.Worktree, Equals, Unmodified)
2236}
2237
2238func (s *WorktreeSuite) TestRemove(c *C) {
2239 fs := memfs.New()
2240 w := &Worktree{
2241 r: s.Repository,
2242 Filesystem: fs,
2243 }
2244
2245 err := w.Checkout(&CheckoutOptions{Force: true})
2246 c.Assert(err, IsNil)
2247
2248 hash, err := w.Remove("LICENSE")
2249 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
2250 c.Assert(err, IsNil)
2251
2252 status, err := w.Status()
2253 c.Assert(err, IsNil)
2254 c.Assert(status, HasLen, 1)
2255 c.Assert(status.File("LICENSE").Staging, Equals, Deleted)
2256}
2257
2258func (s *WorktreeSuite) TestRemoveNotExistentEntry(c *C) {
2259 fs := memfs.New()
2260 w := &Worktree{
2261 r: s.Repository,
2262 Filesystem: fs,
2263 }
2264
2265 err := w.Checkout(&CheckoutOptions{Force: true})
2266 c.Assert(err, IsNil)
2267
2268 hash, err := w.Remove("not-exists")
2269 c.Assert(hash.IsZero(), Equals, true)
2270 c.Assert(err, NotNil)
2271}
2272
2273func (s *WorktreeSuite) TestRemoveDirectory(c *C) {
2274 fs := memfs.New()
2275 w := &Worktree{
2276 r: s.Repository,
2277 Filesystem: fs,
2278 }
2279
2280 err := w.Checkout(&CheckoutOptions{Force: true})
2281 c.Assert(err, IsNil)
2282
2283 hash, err := w.Remove("json")
2284 c.Assert(hash.IsZero(), Equals, true)
2285 c.Assert(err, IsNil)
2286
2287 status, err := w.Status()
2288 c.Assert(err, IsNil)
2289 c.Assert(status, HasLen, 2)
2290 c.Assert(status.File("json/long.json").Staging, Equals, Deleted)
2291 c.Assert(status.File("json/short.json").Staging, Equals, Deleted)
2292
2293 _, err = w.Filesystem.Stat("json")
2294 c.Assert(os.IsNotExist(err), Equals, true)
2295}
2296
2297func (s *WorktreeSuite) TestRemoveDirectoryUntracked(c *C) {
2298 fs := memfs.New()
2299 w := &Worktree{
2300 r: s.Repository,
2301 Filesystem: fs,
2302 }
2303
2304 err := w.Checkout(&CheckoutOptions{Force: true})
2305 c.Assert(err, IsNil)
2306
2307 err = util.WriteFile(w.Filesystem, "json/foo", []byte("FOO"), 0755)
2308 c.Assert(err, IsNil)
2309
2310 hash, err := w.Remove("json")
2311 c.Assert(hash.IsZero(), Equals, true)
2312 c.Assert(err, IsNil)
2313
2314 status, err := w.Status()
2315 c.Assert(err, IsNil)
2316 c.Assert(status, HasLen, 3)
2317 c.Assert(status.File("json/long.json").Staging, Equals, Deleted)
2318 c.Assert(status.File("json/short.json").Staging, Equals, Deleted)
2319 c.Assert(status.File("json/foo").Staging, Equals, Untracked)
2320
2321 _, err = w.Filesystem.Stat("json")
2322 c.Assert(err, IsNil)
2323}
2324
2325func (s *WorktreeSuite) TestRemoveDeletedFromWorktree(c *C) {
2326 fs := memfs.New()
2327 w := &Worktree{
2328 r: s.Repository,
2329 Filesystem: fs,
2330 }
2331
2332 err := w.Checkout(&CheckoutOptions{Force: true})
2333 c.Assert(err, IsNil)
2334
2335 err = fs.Remove("LICENSE")
2336 c.Assert(err, IsNil)
2337
2338 hash, err := w.Remove("LICENSE")
2339 c.Assert(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
2340 c.Assert(err, IsNil)
2341
2342 status, err := w.Status()
2343 c.Assert(err, IsNil)
2344 c.Assert(status, HasLen, 1)
2345 c.Assert(status.File("LICENSE").Staging, Equals, Deleted)
2346}
2347
2348func (s *WorktreeSuite) TestRemoveGlob(c *C) {
2349 fs := memfs.New()
2350 w := &Worktree{
2351 r: s.Repository,
2352 Filesystem: fs,
2353 }
2354
2355 err := w.Checkout(&CheckoutOptions{Force: true})
2356 c.Assert(err, IsNil)
2357
2358 err = w.RemoveGlob(w.Filesystem.Join("json", "l*"))
2359 c.Assert(err, IsNil)
2360
2361 status, err := w.Status()
2362 c.Assert(err, IsNil)
2363 c.Assert(status, HasLen, 1)
2364 c.Assert(status.File("json/long.json").Staging, Equals, Deleted)
2365}
2366
2367func (s *WorktreeSuite) TestRemoveGlobDirectory(c *C) {
2368 fs := memfs.New()
2369 w := &Worktree{
2370 r: s.Repository,
2371 Filesystem: fs,
2372 }
2373
2374 err := w.Checkout(&CheckoutOptions{Force: true})
2375 c.Assert(err, IsNil)
2376
2377 err = w.RemoveGlob("js*")
2378 c.Assert(err, IsNil)
2379
2380 status, err := w.Status()
2381 c.Assert(err, IsNil)
2382 c.Assert(status, HasLen, 2)
2383 c.Assert(status.File("json/short.json").Staging, Equals, Deleted)
2384 c.Assert(status.File("json/long.json").Staging, Equals, Deleted)
2385
2386 _, err = w.Filesystem.Stat("json")
2387 c.Assert(os.IsNotExist(err), Equals, true)
2388}
2389
2390func (s *WorktreeSuite) TestRemoveGlobDirectoryDeleted(c *C) {
2391 fs := memfs.New()
2392 w := &Worktree{
2393 r: s.Repository,
2394 Filesystem: fs,
2395 }
2396
2397 err := w.Checkout(&CheckoutOptions{Force: true})
2398 c.Assert(err, IsNil)
2399
2400 err = fs.Remove("json/short.json")
2401 c.Assert(err, IsNil)
2402
2403 err = util.WriteFile(w.Filesystem, "json/foo", []byte("FOO"), 0755)
2404 c.Assert(err, IsNil)
2405
2406 err = w.RemoveGlob("js*")
2407 c.Assert(err, IsNil)
2408
2409 status, err := w.Status()
2410 c.Assert(err, IsNil)
2411 c.Assert(status, HasLen, 3)
2412 c.Assert(status.File("json/short.json").Staging, Equals, Deleted)
2413 c.Assert(status.File("json/long.json").Staging, Equals, Deleted)
2414}
2415
2416func (s *WorktreeSuite) TestMove(c *C) {
2417 fs := memfs.New()
2418 w := &Worktree{
2419 r: s.Repository,
2420 Filesystem: fs,
2421 }
2422
2423 err := w.Checkout(&CheckoutOptions{Force: true})
2424 c.Assert(err, IsNil)
2425
2426 hash, err := w.Move("LICENSE", "foo")
2427 c.Check(hash.String(), Equals, "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f")
2428 c.Assert(err, IsNil)
2429
2430 status, err := w.Status()
2431 c.Assert(err, IsNil)
2432 c.Assert(status, HasLen, 2)
2433 c.Assert(status.File("LICENSE").Staging, Equals, Deleted)
2434 c.Assert(status.File("foo").Staging, Equals, Added)
2435
2436}
2437
2438func (s *WorktreeSuite) TestMoveNotExistentEntry(c *C) {
2439 fs := memfs.New()
2440 w := &Worktree{
2441 r: s.Repository,
2442 Filesystem: fs,
2443 }
2444
2445 err := w.Checkout(&CheckoutOptions{Force: true})
2446 c.Assert(err, IsNil)
2447
2448 hash, err := w.Move("not-exists", "foo")
2449 c.Assert(hash.IsZero(), Equals, true)
2450 c.Assert(err, NotNil)
2451}
2452
2453func (s *WorktreeSuite) TestMoveToExistent(c *C) {
2454 fs := memfs.New()
2455 w := &Worktree{
2456 r: s.Repository,
2457 Filesystem: fs,
2458 }
2459
2460 err := w.Checkout(&CheckoutOptions{Force: true})
2461 c.Assert(err, IsNil)
2462
2463 hash, err := w.Move(".gitignore", "LICENSE")
2464 c.Assert(hash.IsZero(), Equals, true)
2465 c.Assert(err, Equals, ErrDestinationExists)
2466}
2467
2468func (s *WorktreeSuite) TestClean(c *C) {
2469 fs := fixtures.ByTag("dirty").One().Worktree()
2470
2471 // Open the repo.
2472 fs, err := fs.Chroot("repo")
2473 c.Assert(err, IsNil)
2474 r, err := PlainOpen(fs.Root())
2475 c.Assert(err, IsNil)
2476
2477 wt, err := r.Worktree()
2478 c.Assert(err, IsNil)
2479
2480 // Status before cleaning.
2481 status, err := wt.Status()
2482 c.Assert(err, IsNil)
2483 c.Assert(len(status), Equals, 2)
2484
2485 err = wt.Clean(&CleanOptions{})
2486 c.Assert(err, IsNil)
2487
2488 // Status after cleaning.
2489 status, err = wt.Status()
2490 c.Assert(err, IsNil)
2491
2492 c.Assert(len(status), Equals, 1)
2493
2494 fi, err := fs.Lstat("pkgA")
2495 c.Assert(err, IsNil)
2496 c.Assert(fi.IsDir(), Equals, true)
2497
2498 // Clean with Dir: true.
2499 err = wt.Clean(&CleanOptions{Dir: true})
2500 c.Assert(err, IsNil)
2501
2502 status, err = wt.Status()
2503 c.Assert(err, IsNil)
2504
2505 c.Assert(len(status), Equals, 0)
2506
2507 // An empty dir should be deleted, as well.
2508 _, err = fs.Lstat("pkgA")
2509 c.Assert(err, ErrorMatches, ".*(no such file or directory.*|.*file does not exist)*.")
2510}
2511
2512func (s *WorktreeSuite) TestCleanBare(c *C) {
2513 storer := memory.NewStorage()
2514
2515 r, err := Init(storer, nil)
2516 c.Assert(err, IsNil)
2517 c.Assert(r, NotNil)
2518
2519 wtfs := memfs.New()
2520
2521 err = wtfs.MkdirAll("worktree", os.ModePerm)
2522 c.Assert(err, IsNil)
2523
2524 wtfs, err = wtfs.Chroot("worktree")
2525 c.Assert(err, IsNil)
2526
2527 r, err = Open(storer, wtfs)
2528 c.Assert(err, IsNil)
2529
2530 wt, err := r.Worktree()
2531 c.Assert(err, IsNil)
2532
2533 _, err = wt.Filesystem.Lstat(".")
2534 c.Assert(err, IsNil)
2535
2536 // Clean with Dir: true.
2537 err = wt.Clean(&CleanOptions{Dir: true})
2538 c.Assert(err, IsNil)
2539
2540 // Root worktree directory must remain after cleaning
2541 _, err = wt.Filesystem.Lstat(".")
2542 c.Assert(err, IsNil)
2543}
2544
2545func TestAlternatesRepo(t *testing.T) {
2546 fs := fixtures.ByTag("alternates").One().Worktree()
2547
2548 // Open 1st repo.
2549 rep1fs, err := fs.Chroot("rep1")
2550 assert.NoError(t, err)
2551 rep1, err := PlainOpen(rep1fs.Root())
2552 assert.NoError(t, err)
2553
2554 // Open 2nd repo.
2555 rep2fs, err := fs.Chroot("rep2")
2556 assert.NoError(t, err)
2557 d, _ := rep2fs.Chroot(GitDirName)
2558 storer := filesystem.NewStorageWithOptions(d,
2559 cache.NewObjectLRUDefault(), filesystem.Options{
2560 AlternatesFS: fs,
2561 })
2562 rep2, err := Open(storer, rep2fs)
2563
2564 assert.NoError(t, err)
2565
2566 // Get the HEAD commit from the main repo.
2567 h, err := rep1.Head()
2568 assert.NoError(t, err)
2569 commit1, err := rep1.CommitObject(h.Hash())
2570 assert.NoError(t, err)
2571
2572 // Get the HEAD commit from the shared repo.
2573 h, err = rep2.Head()
2574 assert.NoError(t, err)
2575 commit2, err := rep2.CommitObject(h.Hash())
2576 assert.NoError(t, err)
2577
2578 assert.Equal(t, commit1.String(), commit2.String())
2579}
2580
2581func (s *WorktreeSuite) TestGrep(c *C) {
2582 cases := []struct {
2583 name string
2584 options GrepOptions
2585 wantResult []GrepResult
2586 dontWantResult []GrepResult
2587 wantError error
2588 }{
2589 {
2590 name: "basic word match",
2591 options: GrepOptions{
2592 Patterns: []*regexp.Regexp{regexp.MustCompile("import")},
2593 },
2594 wantResult: []GrepResult{
2595 {
2596 FileName: "go/example.go",
2597 LineNumber: 3,
2598 Content: "import (",
2599 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2600 },
2601 {
2602 FileName: "vendor/foo.go",
2603 LineNumber: 3,
2604 Content: "import \"fmt\"",
2605 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2606 },
2607 },
2608 }, {
2609 name: "case insensitive match",
2610 options: GrepOptions{
2611 Patterns: []*regexp.Regexp{regexp.MustCompile(`(?i)IMport`)},
2612 },
2613 wantResult: []GrepResult{
2614 {
2615 FileName: "go/example.go",
2616 LineNumber: 3,
2617 Content: "import (",
2618 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2619 },
2620 {
2621 FileName: "vendor/foo.go",
2622 LineNumber: 3,
2623 Content: "import \"fmt\"",
2624 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2625 },
2626 },
2627 }, {
2628 name: "invert match",
2629 options: GrepOptions{
2630 Patterns: []*regexp.Regexp{regexp.MustCompile("import")},
2631 InvertMatch: true,
2632 },
2633 dontWantResult: []GrepResult{
2634 {
2635 FileName: "go/example.go",
2636 LineNumber: 3,
2637 Content: "import (",
2638 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2639 },
2640 {
2641 FileName: "vendor/foo.go",
2642 LineNumber: 3,
2643 Content: "import \"fmt\"",
2644 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2645 },
2646 },
2647 }, {
2648 name: "match at a given commit hash",
2649 options: GrepOptions{
2650 Patterns: []*regexp.Regexp{regexp.MustCompile("The MIT License")},
2651 CommitHash: plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
2652 },
2653 wantResult: []GrepResult{
2654 {
2655 FileName: "LICENSE",
2656 LineNumber: 1,
2657 Content: "The MIT License (MIT)",
2658 TreeName: "b029517f6300c2da0f4b651b8642506cd6aaf45d",
2659 },
2660 },
2661 dontWantResult: []GrepResult{
2662 {
2663 FileName: "go/example.go",
2664 LineNumber: 3,
2665 Content: "import (",
2666 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2667 },
2668 },
2669 }, {
2670 name: "match for a given pathspec",
2671 options: GrepOptions{
2672 Patterns: []*regexp.Regexp{regexp.MustCompile("import")},
2673 PathSpecs: []*regexp.Regexp{regexp.MustCompile("go/")},
2674 },
2675 wantResult: []GrepResult{
2676 {
2677 FileName: "go/example.go",
2678 LineNumber: 3,
2679 Content: "import (",
2680 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2681 },
2682 },
2683 dontWantResult: []GrepResult{
2684 {
2685 FileName: "vendor/foo.go",
2686 LineNumber: 3,
2687 Content: "import \"fmt\"",
2688 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2689 },
2690 },
2691 }, {
2692 name: "match at a given reference name",
2693 options: GrepOptions{
2694 Patterns: []*regexp.Regexp{regexp.MustCompile("import")},
2695 ReferenceName: "refs/heads/master",
2696 },
2697 wantResult: []GrepResult{
2698 {
2699 FileName: "go/example.go",
2700 LineNumber: 3,
2701 Content: "import (",
2702 TreeName: "refs/heads/master",
2703 },
2704 },
2705 }, {
2706 name: "ambiguous options",
2707 options: GrepOptions{
2708 Patterns: []*regexp.Regexp{regexp.MustCompile("import")},
2709 CommitHash: plumbing.NewHash("2d55a722f3c3ecc36da919dfd8b6de38352f3507"),
2710 ReferenceName: "somereferencename",
2711 },
2712 wantError: ErrHashOrReference,
2713 }, {
2714 name: "multiple patterns",
2715 options: GrepOptions{
2716 Patterns: []*regexp.Regexp{
2717 regexp.MustCompile("import"),
2718 regexp.MustCompile("License"),
2719 },
2720 },
2721 wantResult: []GrepResult{
2722 {
2723 FileName: "go/example.go",
2724 LineNumber: 3,
2725 Content: "import (",
2726 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2727 },
2728 {
2729 FileName: "vendor/foo.go",
2730 LineNumber: 3,
2731 Content: "import \"fmt\"",
2732 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2733 },
2734 {
2735 FileName: "LICENSE",
2736 LineNumber: 1,
2737 Content: "The MIT License (MIT)",
2738 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2739 },
2740 },
2741 }, {
2742 name: "multiple pathspecs",
2743 options: GrepOptions{
2744 Patterns: []*regexp.Regexp{regexp.MustCompile("import")},
2745 PathSpecs: []*regexp.Regexp{
2746 regexp.MustCompile("go/"),
2747 regexp.MustCompile("vendor/"),
2748 },
2749 },
2750 wantResult: []GrepResult{
2751 {
2752 FileName: "go/example.go",
2753 LineNumber: 3,
2754 Content: "import (",
2755 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2756 },
2757 {
2758 FileName: "vendor/foo.go",
2759 LineNumber: 3,
2760 Content: "import \"fmt\"",
2761 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2762 },
2763 },
2764 },
2765 }
2766
2767 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
2768
2769 dir := c.MkDir()
2770
2771 server, err := PlainClone(dir, false, &CloneOptions{
2772 URL: path,
2773 })
2774 c.Assert(err, IsNil)
2775
2776 w, err := server.Worktree()
2777 c.Assert(err, IsNil)
2778
2779 for _, tc := range cases {
2780 gr, err := w.Grep(&tc.options)
2781 if tc.wantError != nil {
2782 c.Assert(err, Equals, tc.wantError)
2783 } else {
2784 c.Assert(err, IsNil)
2785 }
2786
2787 // Iterate through the results and check if the wanted result is present
2788 // in the got result.
2789 for _, wantResult := range tc.wantResult {
2790 found := false
2791 for _, gotResult := range gr {
2792 if wantResult == gotResult {
2793 found = true
2794 break
2795 }
2796 }
2797 if !found {
2798 c.Errorf("unexpected grep results for %q, expected result to contain: %v", tc.name, wantResult)
2799 }
2800 }
2801
2802 // Iterate through the results and check if the not wanted result is
2803 // present in the got result.
2804 for _, dontWantResult := range tc.dontWantResult {
2805 found := false
2806 for _, gotResult := range gr {
2807 if dontWantResult == gotResult {
2808 found = true
2809 break
2810 }
2811 }
2812 if found {
2813 c.Errorf("unexpected grep results for %q, expected result to NOT contain: %v", tc.name, dontWantResult)
2814 }
2815 }
2816 }
2817}
2818
2819func (s *WorktreeSuite) TestGrepBare(c *C) {
2820 cases := []struct {
2821 name string
2822 options GrepOptions
2823 wantResult []GrepResult
2824 dontWantResult []GrepResult
2825 wantError error
2826 }{
2827 {
2828 name: "basic word match",
2829 options: GrepOptions{
2830 Patterns: []*regexp.Regexp{regexp.MustCompile("import")},
2831 CommitHash: plumbing.ZeroHash,
2832 },
2833 wantResult: []GrepResult{
2834 {
2835 FileName: "go/example.go",
2836 LineNumber: 3,
2837 Content: "import (",
2838 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2839 },
2840 {
2841 FileName: "vendor/foo.go",
2842 LineNumber: 3,
2843 Content: "import \"fmt\"",
2844 TreeName: "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2845 },
2846 },
2847 },
2848 }
2849
2850 path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()
2851
2852 dir := c.MkDir()
2853
2854 r, err := PlainClone(dir, true, &CloneOptions{
2855 URL: path,
2856 })
2857 c.Assert(err, IsNil)
2858
2859 for _, tc := range cases {
2860 gr, err := r.Grep(&tc.options)
2861 if tc.wantError != nil {
2862 c.Assert(err, Equals, tc.wantError)
2863 } else {
2864 c.Assert(err, IsNil)
2865 }
2866
2867 // Iterate through the results and check if the wanted result is present
2868 // in the got result.
2869 for _, wantResult := range tc.wantResult {
2870 found := false
2871 for _, gotResult := range gr {
2872 if wantResult == gotResult {
2873 found = true
2874 break
2875 }
2876 }
2877 if !found {
2878 c.Errorf("unexpected grep results for %q, expected result to contain: %v", tc.name, wantResult)
2879 }
2880 }
2881
2882 // Iterate through the results and check if the not wanted result is
2883 // present in the got result.
2884 for _, dontWantResult := range tc.dontWantResult {
2885 found := false
2886 for _, gotResult := range gr {
2887 if dontWantResult == gotResult {
2888 found = true
2889 break
2890 }
2891 }
2892 if found {
2893 c.Errorf("unexpected grep results for %q, expected result to NOT contain: %v", tc.name, dontWantResult)
2894 }
2895 }
2896 }
2897}
2898
2899func (s *WorktreeSuite) TestResetLingeringDirectories(c *C) {
2900 dir := c.MkDir()
2901
2902 commitOpts := &CommitOptions{Author: &object.Signature{
2903 Name: "foo",
2904 Email: "foo@foo.foo",
2905 When: time.Now(),
2906 }}
2907
2908 repo, err := PlainInit(dir, false)
2909 c.Assert(err, IsNil)
2910
2911 w, err := repo.Worktree()
2912 c.Assert(err, IsNil)
2913
2914 os.WriteFile(filepath.Join(dir, "README"), []byte("placeholder"), 0o644)
2915
2916 _, err = w.Add(".")
2917 c.Assert(err, IsNil)
2918
2919 initialHash, err := w.Commit("Initial commit", commitOpts)
2920 c.Assert(err, IsNil)
2921
2922 os.MkdirAll(filepath.Join(dir, "a", "b"), 0o755)
2923 os.WriteFile(filepath.Join(dir, "a", "b", "1"), []byte("1"), 0o644)
2924
2925 _, err = w.Add(".")
2926 c.Assert(err, IsNil)
2927
2928 _, err = w.Commit("Add file in nested sub-directories", commitOpts)
2929 c.Assert(err, IsNil)
2930
2931 // reset to initial commit, which should remove a/b/1, a/b, and a
2932 err = w.Reset(&ResetOptions{
2933 Commit: initialHash,
2934 Mode: HardReset,
2935 })
2936 c.Assert(err, IsNil)
2937
2938 _, err = os.Stat(filepath.Join(dir, "a", "b", "1"))
2939 c.Assert(errors.Is(err, os.ErrNotExist), Equals, true)
2940
2941 _, err = os.Stat(filepath.Join(dir, "a", "b"))
2942 c.Assert(errors.Is(err, os.ErrNotExist), Equals, true)
2943
2944 _, err = os.Stat(filepath.Join(dir, "a"))
2945 c.Assert(errors.Is(err, os.ErrNotExist), Equals, true)
2946}
2947
2948func (s *WorktreeSuite) TestAddAndCommit(c *C) {
2949 expectedFiles := 2
2950
2951 dir := c.MkDir()
2952
2953 repo, err := PlainInit(dir, false)
2954 c.Assert(err, IsNil)
2955
2956 w, err := repo.Worktree()
2957 c.Assert(err, IsNil)
2958
2959 os.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0o644)
2960 os.WriteFile(filepath.Join(dir, "bar"), []byte("foo"), 0o644)
2961
2962 _, err = w.Add(".")
2963 c.Assert(err, IsNil)
2964
2965 _, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
2966 Name: "foo",
2967 Email: "foo@foo.foo",
2968 When: time.Now(),
2969 }})
2970 c.Assert(err, IsNil)
2971
2972 iter, err := w.r.Log(&LogOptions{})
2973 c.Assert(err, IsNil)
2974
2975 filesFound := 0
2976 err = iter.ForEach(func(c *object.Commit) error {
2977 files, err := c.Files()
2978 if err != nil {
2979 return err
2980 }
2981
2982 err = files.ForEach(func(f *object.File) error {
2983 filesFound++
2984 return nil
2985 })
2986 return err
2987 })
2988 c.Assert(err, IsNil)
2989 c.Assert(filesFound, Equals, expectedFiles)
2990}
2991
2992func (s *WorktreeSuite) TestAddAndCommitEmpty(c *C) {
2993 dir := c.MkDir()
2994
2995 repo, err := PlainInit(dir, false)
2996 c.Assert(err, IsNil)
2997
2998 w, err := repo.Worktree()
2999 c.Assert(err, IsNil)
3000
3001 _, err = w.Add(".")
3002 c.Assert(err, IsNil)
3003
3004 _, err = w.Commit("Test Add And Commit", &CommitOptions{Author: &object.Signature{
3005 Name: "foo",
3006 Email: "foo@foo.foo",
3007 When: time.Now(),
3008 }})
3009 c.Assert(err, Equals, ErrEmptyCommit)
3010}
3011
3012func (s *WorktreeSuite) TestLinkedWorktree(c *C) {
3013 fs := fixtures.ByTag("linked-worktree").One().Worktree()
3014
3015 // Open main repo.
3016 {
3017 fs, err := fs.Chroot("main")
3018 c.Assert(err, IsNil)
3019 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true})
3020 c.Assert(err, IsNil)
3021
3022 wt, err := repo.Worktree()
3023 c.Assert(err, IsNil)
3024
3025 status, err := wt.Status()
3026 c.Assert(err, IsNil)
3027 c.Assert(len(status), Equals, 2) // 2 files
3028
3029 head, err := repo.Head()
3030 c.Assert(err, IsNil)
3031 c.Assert(string(head.Name()), Equals, "refs/heads/master")
3032 }
3033
3034 // Open linked-worktree #1.
3035 {
3036 fs, err := fs.Chroot("linked-worktree-1")
3037 c.Assert(err, IsNil)
3038 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true})
3039 c.Assert(err, IsNil)
3040
3041 wt, err := repo.Worktree()
3042 c.Assert(err, IsNil)
3043
3044 status, err := wt.Status()
3045 c.Assert(err, IsNil)
3046 c.Assert(len(status), Equals, 3) // 3 files
3047
3048 _, ok := status["linked-worktree-1-unique-file.txt"]
3049 c.Assert(ok, Equals, true)
3050
3051 head, err := repo.Head()
3052 c.Assert(err, IsNil)
3053 c.Assert(string(head.Name()), Equals, "refs/heads/linked-worktree-1")
3054 }
3055
3056 // Open linked-worktree #2.
3057 {
3058 fs, err := fs.Chroot("linked-worktree-2")
3059 c.Assert(err, IsNil)
3060 repo, err := PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true})
3061 c.Assert(err, IsNil)
3062
3063 wt, err := repo.Worktree()
3064 c.Assert(err, IsNil)
3065
3066 status, err := wt.Status()
3067 c.Assert(err, IsNil)
3068 c.Assert(len(status), Equals, 3) // 3 files
3069
3070 _, ok := status["linked-worktree-2-unique-file.txt"]
3071 c.Assert(ok, Equals, true)
3072
3073 head, err := repo.Head()
3074 c.Assert(err, IsNil)
3075 c.Assert(string(head.Name()), Equals, "refs/heads/branch-with-different-name")
3076 }
3077
3078 // Open linked-worktree #2.
3079 {
3080 fs, err := fs.Chroot("linked-worktree-invalid-commondir")
3081 c.Assert(err, IsNil)
3082 _, err = PlainOpenWithOptions(fs.Root(), &PlainOpenOptions{EnableDotGitCommonDir: true})
3083 c.Assert(err, Equals, ErrRepositoryIncomplete)
3084 }
3085}
3086
3087func TestValidPath(t *testing.T) {
3088 type testcase struct {
3089 path string
3090 wantErr bool
3091 }
3092
3093 tests := []testcase{
3094 {".git", true},
3095 {".git/b", true},
3096 {".git\\b", true},
3097 {"git~1", true},
3098 {"a/../b", true},
3099 {"a\\..\\b", true},
3100 {"/", true},
3101 {"", true},
3102 {".gitmodules", false},
3103 {".gitignore", false},
3104 {"a..b", false},
3105 {".", false},
3106 {"a/.git", false},
3107 {"a\\.git", false},
3108 {"a/.git/b", false},
3109 {"a\\.git\\b", false},
3110 }
3111
3112 if runtime.GOOS == "windows" {
3113 tests = append(tests, []testcase{
3114 {"\\\\a\\b", true},
3115 {"C:\\a\\b", true},
3116 {".git . . .", true},
3117 {".git . . ", true},
3118 {".git ", true},
3119 {".git.", true},
3120 {".git::$INDEX_ALLOCATION", true},
3121 }...)
3122 }
3123
3124 for _, tc := range tests {
3125 t.Run(tc.path, func(t *testing.T) {
3126 err := validPath(tc.path)
3127 if tc.wantErr {
3128 assert.Error(t, err)
3129 } else {
3130 assert.NoError(t, err)
3131 }
3132 })
3133 }
3134}
3135
3136func TestWindowsValidPath(t *testing.T) {
3137 tests := []struct {
3138 path string
3139 want bool
3140 }{
3141 {".git", false},
3142 {".git . . .", false},
3143 {".git ", false},
3144 {".git ", false},
3145 {".git . .", false},
3146 {".git . .", false},
3147 {".git::$INDEX_ALLOCATION", false},
3148 {".git:", false},
3149 {"a", true},
3150 {"a\\b", true},
3151 {"a/b", true},
3152 {".gitm", true},
3153 }
3154
3155 for _, tc := range tests {
3156 t.Run(tc.path, func(t *testing.T) {
3157 got := windowsValidPath(tc.path)
3158 assert.Equal(t, tc.want, got)
3159 })
3160 }
3161}
3162
3163var statusCodeNames = map[StatusCode]string{
3164 Unmodified: "Unmodified",
3165 Untracked: "Untracked",
3166 Modified: "Modified",
3167 Added: "Added",
3168 Deleted: "Deleted",
3169 Renamed: "Renamed",
3170 Copied: "Copied",
3171 UpdatedButUnmerged: "UpdatedButUnmerged",
3172}
3173
3174func setupForRestore(c *C, s *WorktreeSuite) (fs billy.Filesystem, w *Worktree, names []string) {
3175 fs = memfs.New()
3176 w = &Worktree{
3177 r: s.Repository,
3178 Filesystem: fs,
3179 }
3180
3181 err := w.Checkout(&CheckoutOptions{})
3182 c.Assert(err, IsNil)
3183
3184 names = []string{"foo", "CHANGELOG", "LICENSE", "binary.jpg"}
3185 verifyStatus(c, "Checkout", w, names, []FileStatus{
3186 {Worktree: Untracked, Staging: Untracked},
3187 {Worktree: Untracked, Staging: Untracked},
3188 {Worktree: Untracked, Staging: Untracked},
3189 {Worktree: Untracked, Staging: Untracked},
3190 })
3191
3192 // Touch of bunch of files including create a new file and delete an exsiting file
3193 for _, name := range names {
3194 err = util.WriteFile(fs, name, []byte("Foo Bar"), 0755)
3195 c.Assert(err, IsNil)
3196 }
3197 err = util.RemoveAll(fs, names[3])
3198 c.Assert(err, IsNil)
3199
3200 // Confirm the status after doing the edits without staging anything
3201 verifyStatus(c, "Edits", w, names, []FileStatus{
3202 {Worktree: Untracked, Staging: Untracked},
3203 {Worktree: Modified, Staging: Unmodified},
3204 {Worktree: Modified, Staging: Unmodified},
3205 {Worktree: Deleted, Staging: Unmodified},
3206 })
3207
3208 // Stage all files and verify the updated status
3209 for _, name := range names {
3210 _, err = w.Add(name)
3211 c.Assert(err, IsNil)
3212 }
3213 verifyStatus(c, "Staged", w, names, []FileStatus{
3214 {Worktree: Unmodified, Staging: Added},
3215 {Worktree: Unmodified, Staging: Modified},
3216 {Worktree: Unmodified, Staging: Modified},
3217 {Worktree: Unmodified, Staging: Deleted},
3218 })
3219
3220 // Add secondary changes to a file to make sure we only restore the staged file
3221 err = util.WriteFile(fs, names[1], []byte("Foo Bar:11"), 0755)
3222 c.Assert(err, IsNil)
3223 err = util.WriteFile(fs, names[2], []byte("Foo Bar:22"), 0755)
3224 c.Assert(err, IsNil)
3225
3226 verifyStatus(c, "Secondary Edits", w, names, []FileStatus{
3227 {Worktree: Unmodified, Staging: Added},
3228 {Worktree: Modified, Staging: Modified},
3229 {Worktree: Modified, Staging: Modified},
3230 {Worktree: Unmodified, Staging: Deleted},
3231 })
3232
3233 return
3234}
3235
3236func verifyStatus(c *C, marker string, w *Worktree, files []string, statuses []FileStatus) {
3237 c.Assert(len(files), Equals, len(statuses))
3238
3239 status, err := w.Status()
3240 c.Assert(err, IsNil)
3241
3242 for i, file := range files {
3243 current := status.File(file)
3244 expected := statuses[i]
3245 c.Assert(current.Worktree, Equals, expected.Worktree, Commentf("%s - [%d] : %s Worktree %s != %s", marker, i, file, statusCodeNames[current.Worktree], statusCodeNames[expected.Worktree]))
3246 c.Assert(current.Staging, Equals, expected.Staging, Commentf("%s - [%d] : %s Staging %s != %s", marker, i, file, statusCodeNames[current.Staging], statusCodeNames[expected.Staging]))
3247 }
3248}
3249
3250func (s *WorktreeSuite) TestRestoreStaged(c *C) {
3251 fs, w, names := setupForRestore(c, s)
3252
3253 // Attempt without files should throw an error like the git restore --staged
3254 opts := RestoreOptions{Staged: true}
3255 err := w.Restore(&opts)
3256 c.Assert(err, Equals, ErrNoRestorePaths)
3257
3258 // Restore Staged files in 2 groups and confirm status
3259 opts.Files = []string{names[0], "./" + names[1]}
3260 err = w.Restore(&opts)
3261 c.Assert(err, IsNil)
3262 verifyStatus(c, "Restored First", w, names, []FileStatus{
3263 {Worktree: Untracked, Staging: Untracked},
3264 {Worktree: Modified, Staging: Unmodified},
3265 {Worktree: Modified, Staging: Modified},
3266 {Worktree: Unmodified, Staging: Deleted},
3267 })
3268
3269 // Make sure the restore didn't overwrite our secondary changes
3270 contents, err := util.ReadFile(fs, names[1])
3271 c.Assert(err, IsNil)
3272 c.Assert(string(contents), Equals, "Foo Bar:11")
3273
3274 opts.Files = []string{"./" + names[2], names[3]}
3275 err = w.Restore(&opts)
3276 c.Assert(err, IsNil)
3277 verifyStatus(c, "Restored Second", w, names, []FileStatus{
3278 {Worktree: Untracked, Staging: Untracked},
3279 {Worktree: Modified, Staging: Unmodified},
3280 {Worktree: Modified, Staging: Unmodified},
3281 {Worktree: Deleted, Staging: Unmodified},
3282 })
3283
3284 // Make sure the restore didn't overwrite our secondary changes
3285 contents, err = util.ReadFile(fs, names[2])
3286 c.Assert(err, IsNil)
3287 c.Assert(string(contents), Equals, "Foo Bar:22")
3288}
3289
3290func (s *WorktreeSuite) TestRestoreWorktree(c *C) {
3291 _, w, names := setupForRestore(c, s)
3292
3293 // Attempt without files should throw an error like the git restore
3294 opts := RestoreOptions{}
3295 err := w.Restore(&opts)
3296 c.Assert(err, Equals, ErrNoRestorePaths)
3297
3298 opts.Files = []string{names[0], names[1]}
3299 err = w.Restore(&opts)
3300 c.Assert(err, Equals, ErrRestoreWorktreeOnlyNotSupported)
3301}
3302
3303func (s *WorktreeSuite) TestRestoreBoth(c *C) {
3304 _, w, names := setupForRestore(c, s)
3305
3306 // Attempt without files should throw an error like the git restore --staged --worktree
3307 opts := RestoreOptions{Staged: true, Worktree: true}
3308 err := w.Restore(&opts)
3309 c.Assert(err, Equals, ErrNoRestorePaths)
3310
3311 // Restore Staged files in 2 groups and confirm status
3312 opts.Files = []string{names[0], names[1]}
3313 err = w.Restore(&opts)
3314 c.Assert(err, IsNil)
3315 verifyStatus(c, "Restored First", w, names, []FileStatus{
3316 {Worktree: Untracked, Staging: Untracked},
3317 {Worktree: Untracked, Staging: Untracked},
3318 {Worktree: Modified, Staging: Modified},
3319 {Worktree: Unmodified, Staging: Deleted},
3320 })
3321
3322 opts.Files = []string{names[2], names[3]}
3323 err = w.Restore(&opts)
3324 c.Assert(err, IsNil)
3325 verifyStatus(c, "Restored Second", w, names, []FileStatus{
3326 {Worktree: Untracked, Staging: Untracked},
3327 {Worktree: Untracked, Staging: Untracked},
3328 {Worktree: Untracked, Staging: Untracked},
3329 {Worktree: Untracked, Staging: Untracked},
3330 })
3331}
3332
3333func TestFilePermissions(t *testing.T) {
3334
3335 // Initialize an in memory repository
3336 remoteUrl := t.TempDir()
3337
3338 inMemoryFs := memfs.New()
3339 remoteFs := osfs.New(remoteUrl)
3340 remoteStorage := filesystem.NewStorage(remoteFs, cache.NewObjectLRUDefault())
3341
3342 remoteRepository, err := Init(remoteStorage, inMemoryFs)
3343 assert.NoError(t, err)
3344
3345 err = util.WriteFile(inMemoryFs, "fileWithExecuteBit", []byte("Initial data"), 0755)
3346 assert.NoError(t, err)
3347
3348 err = util.WriteFile(inMemoryFs, "regularFile", []byte("Initial data"), 0644)
3349 assert.NoError(t, err)
3350
3351 remoteWorktree, err := remoteRepository.Worktree()
3352 assert.NoError(t, err)
3353
3354 _, err = remoteWorktree.Add("fileWithExecuteBit")
3355 assert.NoError(t, err)
3356
3357 _, err = remoteWorktree.Add("regularFile")
3358 assert.NoError(t, err)
3359
3360 _, err = remoteWorktree.Commit("my commit", &CommitOptions{})
3361 assert.NoError(t, err)
3362
3363 worktreePath := t.TempDir()
3364
3365 localRepo, err := PlainClone(worktreePath, false, &CloneOptions{URL: remoteUrl})
3366 assert.NoError(t, err)
3367
3368 localWorktree, err := localRepo.Worktree()
3369 assert.NoError(t, err)
3370
3371 idx, err := localWorktree.r.Storer.Index()
3372 assert.NoError(t, err)
3373
3374 expectedEntries := []index.Entry{
3375 {
3376 Name: "fileWithExecuteBit",
3377 Mode: filemode.Executable,
3378 },
3379 {
3380 Name: "regularFile",
3381 Mode: filemode.Regular,
3382 },
3383 }
3384
3385 assert.Len(t, idx.Entries, len(expectedEntries))
3386
3387 for i, expectedEntry := range expectedEntries {
3388 assert.Equal(t, expectedEntry.Name, idx.Entries[i].Name)
3389 assert.Equal(t, expectedEntry.Mode, idx.Entries[i].Mode)
3390 }
3391
3392}