Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 9775f82

Browse files
authored
Merge pull request #487 from mcuadros/checkout-create
worktree: checkout, create branch
2 parents bebcb4f + 9afc472 commit 9775f82

File tree

3 files changed

+116
-23
lines changed

3 files changed

+116
-23
lines changed

options.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -194,20 +194,35 @@ type SubmoduleUpdateOptions struct {
194194
RecurseSubmodules SubmoduleRescursivity
195195
}
196196

197+
var (
198+
ErrBranchHashExclusive = errors.New("Branch and Hash are mutually exclusive")
199+
ErrCreateRequiresBranch = errors.New("Branch is mandatory when Create is used")
200+
)
201+
197202
// CheckoutOptions describes how a checkout 31operation should be performed.
198203
type CheckoutOptions struct {
199204
// Hash to be checked out, if used HEAD will in detached mode. Branch and
200-
// Hash are mutual exclusive.
205+
// Hash are mutually exclusive, if Create is not used.
201206
Hash plumbing.Hash
202207
// Branch to be checked out, if Branch and Hash are empty is set to `master`.
203208
Branch plumbing.ReferenceName
209+
// Create a new branch named Branch and start it at Hash.
210+
Create bool
204211
// Force, if true when switching branches, proceed even if the index or the
205212
// working tree differs from HEAD. This is used to throw away local changes
206213
Force bool
207214
}
208215

209216
// Validate validates the fields and sets the default values.
210217
func (o *CheckoutOptions) Validate() error {
218+
if !o.Create && !o.Hash.IsZero() && o.Branch != "" {
219+
return ErrBranchHashExclusive
220+
}
221+
222+
if o.Create && o.Branch == "" {
223+
return ErrCreateRequiresBranch
224+
}
225+
211226
if o.Branch == "" {
212227
o.Branch = plumbing.Master
213228
}

worktree.go

+33-4
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error {
3838
return err
3939
}
4040

41+
if opts.Create {
42+
if err := w.createBranch(opts); err != nil {
43+
return err
44+
}
45+
}
46+
4147
if !opts.Force {
42-
unstaged, err := w.cointainsUnstagedChanges()
48+
unstaged, err := w.containsUnstagedChanges()
4349
if err != nil {
4450
return err
4551
}
@@ -59,7 +65,7 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error {
5965
ro.Mode = HardReset
6066
}
6167

62-
if !opts.Hash.IsZero() {
68+
if !opts.Hash.IsZero() && !opts.Create {
6369
err = w.setHEADToCommit(opts.Hash)
6470
} else {
6571
err = w.setHEADToBranch(opts.Branch, c)
@@ -71,6 +77,29 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error {
7177

7278
return w.Reset(ro)
7379
}
80+
func (w *Worktree) createBranch(opts *CheckoutOptions) error {
81+
_, err := w.r.Storer.Reference(opts.Branch)
82+
if err == nil {
83+
return fmt.Errorf("a branch named %q already exists", opts.Branch)
84+
}
85+
86+
if err != plumbing.ErrReferenceNotFound {
87+
return err
88+
}
89+
90+
if opts.Hash.IsZero() {
91+
ref, err := w.r.Head()
92+
if err != nil {
93+
return err
94+
}
95+
96+
opts.Hash = ref.Hash()
97+
}
98+
99+
return w.r.Storer.SetReference(
100+
plumbing.NewHashReference(opts.Branch, opts.Hash),
101+
)
102+
}
74103

75104
func (w *Worktree) getCommitFromCheckoutOptions(opts *CheckoutOptions) (plumbing.Hash, error) {
76105
if !opts.Hash.IsZero() {
@@ -133,7 +162,7 @@ func (w *Worktree) Reset(opts *ResetOptions) error {
133162
}
134163

135164
if opts.Mode == MergeReset {
136-
unstaged, err := w.cointainsUnstagedChanges()
165+
unstaged, err := w.containsUnstagedChanges()
137166
if err != nil {
138167
return err
139168
}
@@ -171,7 +200,7 @@ func (w *Worktree) Reset(opts *ResetOptions) error {
171200
return w.setHEADCommit(opts.Commit)
172201
}
173202

174-
func (w *Worktree) cointainsUnstagedChanges() (bool, error) {
203+
func (w *Worktree) containsUnstagedChanges() (bool, error) {
175204
ch, err := w.diffStagingWithWorktree()
176205
if err != nil {
177206
return false, err

worktree_test.go

+67-18
Original file line numberDiff line numberDiff line change
@@ -173,47 +173,96 @@ func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) {
173173
c.Assert(idx.Entries[0].GID, Not(Equals), uint32(0))
174174
}
175175

176-
func (s *WorktreeSuite) TestCheckoutChange(c *C) {
177-
fs := memfs.New()
176+
func (s *WorktreeSuite) TestCheckoutBranch(c *C) {
178177
w := &Worktree{
179178
r: s.Repository,
180-
fs: fs,
179+
fs: memfs.New(),
181180
}
182181

183-
err := w.Checkout(&CheckoutOptions{})
182+
err := w.Checkout(&CheckoutOptions{
183+
Branch: "refs/heads/branch",
184+
})
184185
c.Assert(err, IsNil)
185186

186187
head, err := w.r.Head()
187188
c.Assert(err, IsNil)
188-
c.Assert(head.Name().String(), Equals, "refs/heads/master")
189+
c.Assert(head.Name().String(), Equals, "refs/heads/branch")
189190

190191
status, err := w.Status()
191192
c.Assert(err, IsNil)
192193
c.Assert(status.IsClean(), Equals, true)
194+
}
193195

194-
_, err = fs.Stat("README")
195-
c.Assert(err, Equals, os.ErrNotExist)
196-
_, err = fs.Stat("vendor")
197-
c.Assert(err, Equals, nil)
196+
func (s *WorktreeSuite) TestCheckoutCreateWithHash(c *C) {
197+
w := &Worktree{
198+
r: s.Repository,
199+
fs: memfs.New(),
200+
}
198201

199-
err = w.Checkout(&CheckoutOptions{
200-
Branch: "refs/heads/branch",
202+
err := w.Checkout(&CheckoutOptions{
203+
Create: true,
204+
Branch: "refs/heads/foo",
205+
Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
201206
})
202207
c.Assert(err, IsNil)
203208

204-
status, err = w.Status()
209+
head, err := w.r.Head()
210+
c.Assert(err, IsNil)
211+
c.Assert(head.Name().String(), Equals, "refs/heads/foo")
212+
c.Assert(head.Hash(), Equals, plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"))
213+
214+
status, err := w.Status()
205215
c.Assert(err, IsNil)
206216
c.Assert(status.IsClean(), Equals, true)
217+
}
207218

208-
_, err = fs.Stat("README")
209-
c.Assert(err, Equals, nil)
219+
func (s *WorktreeSuite) TestCheckoutCreate(c *C) {
220+
w := &Worktree{
221+
r: s.Repository,
222+
fs: memfs.New(),
223+
}
210224

211-
_, err = fs.Stat("vendor")
212-
c.Assert(err, Equals, os.ErrNotExist)
225+
err := w.Checkout(&CheckoutOptions{
226+
Create: true,
227+
Branch: "refs/heads/foo",
228+
})
229+
c.Assert(err, IsNil)
213230

214-
head, err = w.r.Head()
231+
head, err := w.r.Head()
215232
c.Assert(err, IsNil)
216-
c.Assert(head.Name().String(), Equals, "refs/heads/branch")
233+
c.Assert(head.Name().String(), Equals, "refs/heads/foo")
234+
c.Assert(head.Hash(), Equals, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
235+
236+
status, err := w.Status()
237+
c.Assert(err, IsNil)
238+
c.Assert(status.IsClean(), Equals, true)
239+
}
240+
241+
func (s *WorktreeSuite) TestCheckoutBranchAndHash(c *C) {
242+
w := &Worktree{
243+
r: s.Repository,
244+
fs: memfs.New(),
245+
}
246+
247+
err := w.Checkout(&CheckoutOptions{
248+
Branch: "refs/heads/foo",
249+
Hash: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
250+
})
251+
252+
c.Assert(err, Equals, ErrBranchHashExclusive)
253+
}
254+
255+
func (s *WorktreeSuite) TestCheckoutCreateMissingBranch(c *C) {
256+
w := &Worktree{
257+
r: s.Repository,
258+
fs: memfs.New(),
259+
}
260+
261+
err := w.Checkout(&CheckoutOptions{
262+
Create: true,
263+
})
264+
265+
c.Assert(err, Equals, ErrCreateRequiresBranch)
217266
}
218267

219268
func (s *WorktreeSuite) TestCheckoutTag(c *C) {

0 commit comments

Comments
 (0)