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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use clap::{ArgAction, Parser, Subcommand};
use clap_complete::engine::ArgValueCompleter;
use crate::completions;
#[derive(Debug, Parser)]
#[command(name = "git-stk")]
#[command(version)]
#[command(about = "Git-native stacked branch workflow helper, with GitHub and GitLab integration")]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Subcommand)]
pub enum Command {
/// Create a new child branch from the current branch.
New { branch: String },
/// Print a branch's stack parent.
Parent {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
},
/// Print a branch's stack children.
Children {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
},
/// Move up the stack: check out a child of the current branch.
Up {
#[arg(add = ArgValueCompleter::new(completions::child_branch_candidates))]
branch: Option<String>,
},
/// Move down the stack: check out the current branch's parent.
Down,
/// Print the current stack.
List {
/// Print a shareable markdown summary with PR links and states.
#[arg(long, action = ArgAction::SetTrue)]
markdown: bool,
},
/// Print local and remote stack status for a branch.
Status {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
},
/// Attach an existing branch to a parent.
Adopt {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: String,
#[arg(long, add = ArgValueCompleter::new(completions::branch_candidates))]
parent: String,
},
/// Remove stack parent metadata from a branch.
Detach {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
},
/// Rebase the current branch and descendants onto their stack parents.
Restack {
/// Pass --update-refs to git rebase.
#[arg(long, action = ArgAction::SetTrue, conflicts_with = "no_update_refs")]
update_refs: bool,
/// Do not pass --update-refs to git rebase.
#[arg(long, action = ArgAction::SetTrue)]
no_update_refs: bool,
/// Force-push (with lease) every rebased branch afterwards.
#[arg(long, action = ArgAction::SetTrue, conflicts_with = "no_push")]
push: bool,
/// Do not push rebased branches, overriding stk.pushOnRestack.
#[arg(long, action = ArgAction::SetTrue)]
no_push: bool,
},
/// Continue an interrupted restack after resolving conflicts.
Continue,
/// Abort an interrupted restack.
Abort,
/// Print detected review provider.
Provider,
/// Print the review request for a branch.
Review {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
},
/// Sync local stack metadata from remote review requests.
Sync {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
/// Print what would change without updating local metadata.
#[arg(long, action = ArgAction::SetTrue)]
dry_run: bool,
},
/// Rebuild or verify local stack metadata from reviews and ancestry.
Repair {
/// Print what would change without updating local metadata.
#[arg(long, action = ArgAction::SetTrue)]
dry_run: bool,
},
/// Create or update a remote review request for a branch.
Submit {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
/// Print what would change without creating or updating reviews.
#[arg(long, action = ArgAction::SetTrue)]
dry_run: bool,
/// Submit the branch and its descendants parent-first.
#[arg(long, conflicts_with = "branch")]
stack: bool,
/// Push branches (-u --force-with-lease) before submitting.
#[arg(long, action = ArgAction::SetTrue, conflicts_with = "no_push")]
push: bool,
/// Do not push branches, overriding stk.pushOnSubmit.
#[arg(long, action = ArgAction::SetTrue)]
no_push: bool,
},
/// Print all stk git config settings and branch metadata.
Config,
/// Print shell completions.
Completions {
/// Shell to print completions for.
#[arg(value_enum)]
shell: clap_complete::Shell,
},
/// Install the man page and wire up shell completions.
Setup {
/// Skip confirmation prompts.
#[arg(long, short = 'y', action = ArgAction::SetTrue)]
yes: bool,
/// Only re-render generated assets (man page); never touch shell rc files.
#[arg(long, action = ArgAction::SetTrue, conflicts_with = "yes")]
refresh: bool,
},
/// Upgrade git-stk to the latest release.
Upgrade {
/// Build and install the latest unreleased commit instead.
#[arg(long, action = ArgAction::SetTrue, conflicts_with = "force")]
head: bool,
/// Reinstall the latest release even when already up to date.
#[arg(long, action = ArgAction::SetTrue)]
force: bool,
/// Skip the --head confirmation prompt.
#[arg(long, short = 'y', action = ArgAction::SetTrue, requires = "head")]
yes: bool,
},
/// Clean up local metadata for merged review requests.
Cleanup {
#[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
branch: Option<String>,
/// Print what would change without updating local metadata.
#[arg(long, action = ArgAction::SetTrue)]
dry_run: bool,
/// Delete cleaned merged branches after updating stack metadata.
#[arg(long, action = ArgAction::SetTrue)]
delete_branch: bool,
},
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum UpdateRefsMode {
Config,
Enabled,
Disabled,
}
impl UpdateRefsMode {
pub fn from_flags(update_refs: bool, no_update_refs: bool) -> Self {
match (update_refs, no_update_refs) {
(true, false) => Self::Enabled,
(false, true) => Self::Disabled,
_ => Self::Config,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum PushMode {
Config,
Enabled,
Disabled,
}
impl PushMode {
pub fn from_flags(push: bool, no_push: bool) -> Self {
match (push, no_push) {
(true, false) => Self::Enabled,
(false, true) => Self::Disabled,
_ => Self::Config,
}
}
}