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