-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathbranch_checkout.go
144 lines (124 loc) · 3.76 KB
/
branch_checkout.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package main
import (
"context"
"errors"
"fmt"
"github.com/charmbracelet/log"
"go.abhg.dev/gs/internal/git"
"go.abhg.dev/gs/internal/spice"
"go.abhg.dev/gs/internal/spice/state"
"go.abhg.dev/gs/internal/text"
"go.abhg.dev/gs/internal/ui"
)
type checkoutOptions struct {
DryRun bool `short:"n" xor:"detach-or-dry-run" help:"Print the target branch without checking it out"`
Detach bool `xor:"detach-or-dry-run" help:"Detach HEAD after checking out"`
}
type branchCheckoutCmd struct {
checkoutOptions
BranchPromptConfig
Untracked bool `short:"u" config:"branchCheckout.showUntracked" help:"Show untracked branches if one isn't supplied"`
Branch string `arg:"" optional:"" help:"Name of the branch to checkout" predictor:"branches"`
}
func (*branchCheckoutCmd) Help() string {
return text.Dedent(`
A prompt will allow selecting between tracked branches.
Provide a branch name as an argument to skip the prompt.
Use -u/--untracked to show untracked branches in the prompt.
Use the spice.branchPrompt.sort configuration option
to specify the sort order of branches in the prompt.
Commonly used field names include "refname", "commiterdate", etc.
By default, branches are sorted by name.
`)
}
// AfterApply runs after command line options have been parsed
// but before the command is executed.
//
// We'll use this to fill in the branch name if it's missing.
func (cmd *branchCheckoutCmd) AfterApply(
ctx context.Context,
view ui.View,
repo *git.Repository,
branchPrompt *branchPrompter,
) error {
if cmd.Branch == "" {
if !ui.Interactive(view) {
return fmt.Errorf("cannot proceed without a branch name: %w", errNoPrompt)
}
// If a branch name is not provided,
// list branches besides the current branch and pick one.
currentBranch, err := repo.CurrentBranch(ctx)
if err != nil {
currentBranch = ""
}
cmd.Branch, err = branchPrompt.Prompt(ctx, &branchPromptRequest{
Disabled: func(b git.LocalBranch) bool {
return b.Name != currentBranch && b.Worktree != ""
},
Default: currentBranch,
TrackedOnly: !cmd.Untracked,
Title: "Select a branch to checkout",
})
if err != nil {
return fmt.Errorf("select branch: %w", err)
}
}
return nil
}
func (cmd *branchCheckoutCmd) Run(
ctx context.Context,
log *log.Logger,
view ui.View,
repo *git.Repository,
store *state.Store,
svc *spice.Service,
) error {
if cmd.Branch != store.Trunk() {
if err := svc.VerifyRestacked(ctx, cmd.Branch); err != nil {
var restackErr *spice.BranchNeedsRestackError
switch {
case errors.As(err, &restackErr):
log.Warnf("%v: needs to be restacked: run 'gs branch restack --branch=%v'", cmd.Branch, cmd.Branch)
case errors.Is(err, state.ErrNotExist):
if !ui.Interactive(view) {
log.Warnf("%v: branch not tracked: run 'gs branch track'", cmd.Branch)
break
}
log.Warnf("%v: branch not tracked", cmd.Branch)
track := true
prompt := ui.NewConfirm().
WithValue(&track).
WithTitle("Do you want to track this branch now?")
if err := ui.Run(view, prompt); err != nil {
return fmt.Errorf("prompt: %w", err)
}
if track {
err := (&branchTrackCmd{
Branch: cmd.Branch,
}).Run(ctx, log, repo, store, svc)
if err != nil {
return fmt.Errorf("track branch: %w", err)
}
}
case errors.Is(err, git.ErrNotExist):
return fmt.Errorf("branch %q does not exist", cmd.Branch)
default:
log.Warnf("error checking branch: %v", err)
}
}
}
if cmd.DryRun {
fmt.Println(cmd.Branch)
return nil
}
if cmd.Detach {
if err := repo.DetachHead(ctx, cmd.Branch); err != nil {
return fmt.Errorf("detach HEAD: %w", err)
}
return nil
}
if err := repo.Checkout(ctx, cmd.Branch); err != nil {
return fmt.Errorf("checkout branch: %w", err)
}
return nil
}