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