-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathswitch.go
More file actions
140 lines (122 loc) · 3.59 KB
/
Copy pathswitch.go
File metadata and controls
140 lines (122 loc) · 3.59 KB
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
package cmd
import (
"fmt"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/cli/go-gh/v2/pkg/text"
"github.com/github/gh-stack/internal/config"
"github.com/github/gh-stack/internal/git"
"github.com/spf13/cobra"
)
func SwitchCmd(cfg *config.Config) *cobra.Command {
return &cobra.Command{
Use: "switch",
Short: "Interactively switch to another branch in the stack",
Long: `Show an interactive picker listing all branches in the current stack
and switch to the selected one.
Branches are displayed from top (furthest from trunk) to bottom (closest to
trunk) with their position number. Use the down/up arrow keys or j/k to
navigate and Enter to select.
To move one branch down or up without an interactive picker, use
'gh stack down' or 'gh stack up' instead.`,
Example: ` # Open the branch picker for the current stack
$ gh stack switch`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runSwitch(cfg)
},
}
}
func runSwitch(cfg *config.Config) error {
result, err := loadStack(cfg, "")
if err != nil {
return ErrNotInStack
}
s := result.Stack
if len(s.Branches) == 0 {
cfg.Errorf("stack has no branches")
return ErrNotInStack
}
if !cfg.IsInteractive() {
cfg.Errorf("switch requires an interactive terminal")
return ErrSilent
}
// Build options in reverse order (top of stack first) with 1-based numbering.
n := len(s.Branches)
options := make([]string, n)
currentBranch := result.CurrentBranch
var defaultOpt string
for i := 0; i < n; i++ {
branchIdx := n - 1 - i
options[i] = fmt.Sprintf("%d. %s", branchIdx+1, s.Branches[branchIdx].Branch)
if s.Branches[branchIdx].Branch == currentBranch {
defaultOpt = options[i]
}
}
selected, err := selectSwitchBranch(cfg, "Select a branch in the stack to switch to:", defaultOpt, options)
if err != nil {
if isInterruptError(err) {
clearSelectPrompt(cfg, len(options))
printInterrupt(cfg)
return errInterrupt
}
cfg.Errorf("failed to select branch: %v", err)
return ErrSilent
}
if selected < 0 || selected >= n {
cfg.Errorf("invalid selection")
return ErrSilent
}
// Map selection back: index 0 in options = branch at n-1, etc.
branchIdx := n - 1 - selected
targetBranch := s.Branches[branchIdx].Branch
if targetBranch == currentBranch {
cfg.Infof("Already on %s", targetBranch)
return nil
}
if err := git.CheckoutBranch(targetBranch); err != nil {
cfg.Errorf("failed to checkout %s: %v", targetBranch, err)
return ErrSilent
}
cfg.Successf("Switched to %s", targetBranch)
return nil
}
func selectSwitchBranch(cfg *config.Config, prompt, defaultValue string, options []string) (int, error) {
if cfg.SelectFn != nil {
return cfg.SelectFn(prompt, defaultValue, options)
}
var selected int
err := survey.AskOne(
newSwitchSelect(prompt, defaultValue, options),
&selected,
survey.WithStdio(cfg.In, cfg.Out, cfg.Err),
)
if err != nil {
return 0, fmt.Errorf("could not prompt: %w", err)
}
return selected, nil
}
func newSwitchSelect(prompt, defaultValue string, options []string) *survey.Select {
selectPrompt := &survey.Select{
Message: prompt,
Options: options,
PageSize: selectPromptPageSize,
VimMode: true,
Filter: switchSelectFilter,
}
if defaultValue != "" {
for _, option := range options {
if option == defaultValue {
selectPrompt.Default = defaultValue
break
}
}
}
return selectPrompt
}
func switchSelectFilter(filter, value string, _ int) bool {
filter = strings.ToLower(filter)
value = strings.ToLower(value)
return strings.Contains(value, filter) ||
strings.Contains(text.RemoveDiacritics(value), filter)
}