diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-07-18 21:41:34 +0200 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2017-07-18 21:41:34 +0200 |
commit | 9afc47237c301ecee66619d1ef8ec286185cb070 (patch) | |
tree | 5b7d67f820b14db690edcc6f506df64aaf28b717 | |
parent | d3c7400c39f86a4c59340c7a9cda8497186e00fc (diff) | |
download | go-git-9afc47237c301ecee66619d1ef8ec286185cb070.tar.gz |
worktree: checkout, create branch
-rw-r--r-- | options.go | 17 | ||||
-rw-r--r-- | worktree.go | 37 | ||||
-rw-r--r-- | worktree_test.go | 85 |
3 files changed, 116 insertions, 23 deletions
@@ -194,13 +194,20 @@ type SubmoduleUpdateOptions struct { RecurseSubmodules SubmoduleRescursivity } +var ( + ErrBranchHashExclusive = errors.New("Branch and Hash are mutually exclusive") + ErrCreateRequiresBranch = errors.New("Branch is mandatory when Create is used") +) + // CheckoutOptions describes how a checkout 31operation should be performed. type CheckoutOptions struct { // Hash to be checked out, if used HEAD will in detached mode. Branch and - // Hash are mutual exclusive. + // Hash are mutually exclusive, if Create is not used. Hash plumbing.Hash // Branch to be checked out, if Branch and Hash are empty is set to `master`. Branch plumbing.ReferenceName + // Create a new branch named Branch and start it at Hash. + Create bool // Force, if true when switching branches, proceed even if the index or the // working tree differs from HEAD. This is used to throw away local changes Force bool @@ -208,6 +215,14 @@ type CheckoutOptions struct { // Validate validates the fields and sets the default values. func (o *CheckoutOptions) Validate() error { + if !o.Create && !o.Hash.IsZero() && o.Branch != "" { + return ErrBranchHashExclusive + } + + if o.Create && o.Branch == "" { + return ErrCreateRequiresBranch + } + if o.Branch == "" { o.Branch = plumbing.Master } diff --git a/worktree.go b/worktree.go index ae1ab28..e0f5fdf 100644 --- a/worktree.go +++ b/worktree.go @@ -38,8 +38,14 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error { return err } + if opts.Create { + if err := w.createBranch(opts); err != nil { + return err + } + } + if !opts.Force { - unstaged, err := w.cointainsUnstagedChanges() + unstaged, err := w.containsUnstagedChanges() if err != nil { return err } @@ -59,7 +65,7 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error { ro.Mode = HardReset } - if !opts.Hash.IsZero() { + if !opts.Hash.IsZero() && !opts.Create { err = w.setHEADToCommit(opts.Hash) } else { err = w.setHEADToBranch(opts.Branch, c) @@ -71,6 +77,29 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error { return w.Reset(ro) } +func (w *Worktree) createBranch(opts *CheckoutOptions) error { + _, err := w.r.Storer.Reference(opts.Branch) + if err == nil { + return fmt.Errorf("a branch named %q already exists", opts.Branch) + } + + if err != plumbing.ErrReferenceNotFound { + return err + } + + if opts.Hash.IsZero() { + ref, err := w.r.Head() + if err != nil { + return err + } + + opts.Hash = ref.Hash() + } + + return w.r.Storer.SetReference( + plumbing.NewHashReference(opts.Branch, opts.Hash), + ) +} func (w *Worktree) getCommitFromCheckoutOptions(opts *CheckoutOptions) (plumbing.Hash, error) { if !opts.Hash.IsZero() { @@ -133,7 +162,7 @@ func (w *Worktree) Reset(opts *ResetOptions) error { } if opts.Mode == MergeReset { - unstaged, err := w.cointainsUnstagedChanges() + unstaged, err := w.containsUnstagedChanges() if err != nil { return err } @@ -171,7 +200,7 @@ func (w *Worktree) Reset(opts *ResetOptions) error { return w.setHEADCommit(opts.Commit) } -func (w *Worktree) cointainsUnstagedChanges() (bool, error) { +func (w *Worktree) containsUnstagedChanges() (bool, error) { ch, err := w.diffStagingWithWorktree() if err != nil { return false, err diff --git a/worktree_test.go b/worktree_test.go index 4c9907b..a6c7b06 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -173,47 +173,96 @@ func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) { c.Assert(idx.Entries[0].GID, Not(Equals), uint32(0)) } -func (s *WorktreeSuite) TestCheckoutChange(c *C) { - fs := memfs.New() +func (s *WorktreeSuite) TestCheckoutBranch(c *C) { w := &Worktree{ r: s.Repository, - fs: fs, + fs: memfs.New(), } - err := w.Checkout(&CheckoutOptions{}) + err := w.Checkout(&CheckoutOptions{ + Branch: "refs/heads/branch", + }) c.Assert(err, IsNil) head, err := w.r.Head() c.Assert(err, IsNil) - c.Assert(head.Name().String(), Equals, "refs/heads/master") + c.Assert(head.Name().String(), Equals, "refs/heads/branch") status, err := w.Status() c.Assert(err, IsNil) c.Assert(status.IsClean(), Equals, true) +} - _, err = fs.Stat("README") - c.Assert(err, Equals, os.ErrNotExist) - _, err = fs.Stat("vendor") - c.Assert(err, Equals, nil) +func (s *WorktreeSuite) TestCheckoutCreateWithHash(c *C) { + w := &Worktree{ + r: s.Repository, + fs: memfs.New(), + } - err = w.Checkout(&CheckoutOptions{ - Branch: "refs/heads/branch", + err := w.Checkout(&CheckoutOptions{ + Create: true, + Branch: "refs/heads/foo", + Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), }) c.Assert(err, IsNil) - status, err = w.Status() + head, err := w.r.Head() + c.Assert(err, IsNil) + c.Assert(head.Name().String(), Equals, "refs/heads/foo") + c.Assert(head.Hash(), Equals, plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9")) + + status, err := w.Status() c.Assert(err, IsNil) c.Assert(status.IsClean(), Equals, true) +} - _, err = fs.Stat("README") - c.Assert(err, Equals, nil) +func (s *WorktreeSuite) TestCheckoutCreate(c *C) { + w := &Worktree{ + r: s.Repository, + fs: memfs.New(), + } - _, err = fs.Stat("vendor") - c.Assert(err, Equals, os.ErrNotExist) + err := w.Checkout(&CheckoutOptions{ + Create: true, + Branch: "refs/heads/foo", + }) + c.Assert(err, IsNil) - head, err = w.r.Head() + head, err := w.r.Head() c.Assert(err, IsNil) - c.Assert(head.Name().String(), Equals, "refs/heads/branch") + c.Assert(head.Name().String(), Equals, "refs/heads/foo") + c.Assert(head.Hash(), Equals, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")) + + status, err := w.Status() + c.Assert(err, IsNil) + c.Assert(status.IsClean(), Equals, true) +} + +func (s *WorktreeSuite) TestCheckoutBranchAndHash(c *C) { + w := &Worktree{ + r: s.Repository, + fs: memfs.New(), + } + + err := w.Checkout(&CheckoutOptions{ + Branch: "refs/heads/foo", + Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"), + }) + + c.Assert(err, Equals, ErrBranchHashExclusive) +} + +func (s *WorktreeSuite) TestCheckoutCreateMissingBranch(c *C) { + w := &Worktree{ + r: s.Repository, + fs: memfs.New(), + } + + err := w.Checkout(&CheckoutOptions{ + Create: true, + }) + + c.Assert(err, Equals, ErrCreateRequiresBranch) } func (s *WorktreeSuite) TestCheckoutTag(c *C) { |