1use clap::{Arg, ArgAction, Command, ValueHint};
2use clap_complete::{Generator, Shell, generate};
3use std::io;
4
5pub fn dispatch(shell_raw: &str, extra: &[String]) -> i32 {
6 if !extra.is_empty() {
7 eprintln!("git-cli: error: expected `git-cli completion <bash|zsh>`");
8 return 1;
9 }
10
11 match shell_raw {
12 "bash" => generate_script(Shell::Bash),
13 "zsh" => generate_script(Shell::Zsh),
14 other => {
15 eprintln!("git-cli: error: unsupported completion shell '{other}'");
16 eprintln!("usage: git-cli completion <bash|zsh>");
17 1
18 }
19 }
20}
21
22fn generate_script<G: Generator>(generator: G) -> i32 {
23 let mut command = build_command_model();
24 let bin_name = command.get_name().to_string();
25 generate(generator, &mut command, bin_name, &mut io::stdout());
26 0
27}
28
29fn build_command_model() -> Command {
30 Command::new("git-cli")
31 .version(env!("CARGO_PKG_VERSION"))
32 .about("Git helper CLI")
33 .disable_help_subcommand(true)
34 .subcommand(build_utils_group())
35 .subcommand(build_reset_group())
36 .subcommand(build_commit_group())
37 .subcommand(build_branch_group())
38 .subcommand(build_ci_group())
39 .subcommand(build_open_group())
40 .subcommand(Command::new("help").about("Display help message for git-cli"))
41 .subcommand(
42 Command::new("completion")
43 .about("Export shell completion script")
44 .arg(
45 Arg::new("shell")
46 .value_name("shell")
47 .value_parser(["bash", "zsh"])
48 .required(true),
49 ),
50 )
51}
52
53fn build_utils_group() -> Command {
54 Command::new("utils")
55 .about("Utility helpers")
56 .subcommand(Command::new("zip").about("Create zip archive from HEAD"))
57 .subcommand(
58 Command::new("copy-staged")
59 .visible_alias("copy")
60 .about("Copy staged diff to clipboard")
61 .arg(Arg::new("stdout").long("stdout").action(ArgAction::SetTrue))
62 .arg(
63 Arg::new("print")
64 .short('p')
65 .long("print")
66 .action(ArgAction::SetTrue),
67 )
68 .arg(Arg::new("both").long("both").action(ArgAction::SetTrue)),
69 )
70 .subcommand(
71 Command::new("root")
72 .about("Jump to git root")
73 .arg(Arg::new("shell").long("shell").action(ArgAction::SetTrue)),
74 )
75 .subcommand(
76 Command::new("commit-hash")
77 .visible_alias("hash")
78 .about("Resolve commit hash")
79 .arg(Arg::new("ref").value_name("ref")),
80 )
81 .subcommand(Command::new("help").about("Display help message for utils"))
82}
83
84fn build_reset_group() -> Command {
85 let count_arg = || Arg::new("count").value_name("count");
86
87 Command::new("reset")
88 .about("Reset helpers")
89 .subcommand(
90 Command::new("soft")
91 .about("Reset to HEAD~N (soft)")
92 .arg(count_arg()),
93 )
94 .subcommand(
95 Command::new("mixed")
96 .about("Reset to HEAD~N (mixed)")
97 .arg(count_arg()),
98 )
99 .subcommand(
100 Command::new("hard")
101 .about("Reset to HEAD~N (hard)")
102 .arg(count_arg()),
103 )
104 .subcommand(Command::new("undo").about("Undo last reset"))
105 .subcommand(Command::new("back-head").about("Checkout HEAD@{1}"))
106 .subcommand(Command::new("back-checkout").about("Return to previous branch"))
107 .subcommand(
108 Command::new("remote")
109 .about("Reset to remote branch")
110 .arg(Arg::new("ref").long("ref").value_name("ref"))
111 .arg(
112 Arg::new("remote")
113 .short('r')
114 .long("remote")
115 .value_name("remote"),
116 )
117 .arg(
118 Arg::new("branch")
119 .short('b')
120 .long("branch")
121 .value_name("branch"),
122 )
123 .arg(
124 Arg::new("no-fetch")
125 .long("no-fetch")
126 .action(ArgAction::SetTrue),
127 )
128 .arg(Arg::new("prune").long("prune").action(ArgAction::SetTrue))
129 .arg(Arg::new("clean").long("clean").action(ArgAction::SetTrue))
130 .arg(
131 Arg::new("set-upstream")
132 .long("set-upstream")
133 .action(ArgAction::SetTrue),
134 )
135 .arg(
136 Arg::new("yes")
137 .short('y')
138 .long("yes")
139 .action(ArgAction::SetTrue),
140 ),
141 )
142 .subcommand(Command::new("help").about("Display help message for reset"))
143}
144
145fn build_commit_group() -> Command {
146 Command::new("commit")
147 .about("Commit helpers")
148 .subcommand(
149 Command::new("context")
150 .about("Print commit context")
151 .arg(Arg::new("stdout").long("stdout").action(ArgAction::SetTrue))
152 .arg(Arg::new("both").long("both").action(ArgAction::SetTrue))
153 .arg(
154 Arg::new("no-color")
155 .long("no-color")
156 .action(ArgAction::SetTrue),
157 )
158 .arg(
159 Arg::new("include")
160 .long("include")
161 .value_name("glob")
162 .num_args(1..),
163 ),
164 )
165 .subcommand(
166 Command::new("context-json")
167 .visible_aliases(["context_json", "contextjson", "json"])
168 .about("Print commit context as JSON")
169 .arg(Arg::new("stdout").long("stdout").action(ArgAction::SetTrue))
170 .arg(Arg::new("both").long("both").action(ArgAction::SetTrue))
171 .arg(Arg::new("pretty").long("pretty").action(ArgAction::SetTrue))
172 .arg(Arg::new("bundle").long("bundle").action(ArgAction::SetTrue))
173 .arg(Arg::new("out-dir").long("out-dir").value_name("path")),
174 )
175 .subcommand(
176 Command::new("to-stash")
177 .visible_alias("stash")
178 .about("Create stash from commit")
179 .arg(Arg::new("ref").value_name("ref")),
180 )
181 .subcommand(Command::new("help").about("Display help message for commit"))
182}
183
184fn build_branch_group() -> Command {
185 Command::new("branch")
186 .about("Branch helpers")
187 .subcommand(
188 Command::new("cleanup")
189 .visible_alias("delete-merged")
190 .about("Delete merged branches")
191 .arg(Arg::new("base").short('b').long("base").value_name("base"))
192 .arg(
193 Arg::new("squash")
194 .short('s')
195 .long("squash")
196 .action(ArgAction::SetTrue),
197 ),
198 )
199 .subcommand(Command::new("help").about("Display help message for branch"))
200}
201
202fn build_ci_group() -> Command {
203 Command::new("ci")
204 .about("CI helpers")
205 .subcommand(
206 Command::new("pick")
207 .about("Cherry-pick into CI branch")
208 .arg(
209 Arg::new("remote")
210 .short('r')
211 .long("remote")
212 .value_name("name"),
213 )
214 .arg(
215 Arg::new("no-fetch")
216 .long("no-fetch")
217 .action(ArgAction::SetTrue),
218 )
219 .arg(
220 Arg::new("force")
221 .short('f')
222 .long("force")
223 .action(ArgAction::SetTrue),
224 )
225 .arg(Arg::new("stay").long("stay").action(ArgAction::SetTrue)),
226 )
227 .subcommand(Command::new("help").about("Display help message for ci"))
228}
229
230fn build_open_group() -> Command {
231 Command::new("open")
232 .about("Open remote pages")
233 .subcommand(
234 Command::new("repo")
235 .about("Open repository page")
236 .arg(remotes_arg()),
237 )
238 .subcommand(
239 Command::new("branch")
240 .about("Open branch tree page")
241 .arg(Arg::new("ref").value_name("ref")),
242 )
243 .subcommand(
244 Command::new("default-branch")
245 .visible_alias("default")
246 .about("Open default branch tree page")
247 .arg(remotes_arg()),
248 )
249 .subcommand(
250 Command::new("commit")
251 .about("Open commit page")
252 .arg(Arg::new("ref").value_name("ref")),
253 )
254 .subcommand(
255 Command::new("compare")
256 .about("Open compare page")
257 .arg(Arg::new("from").value_name("from"))
258 .arg(Arg::new("to").value_name("to")),
259 )
260 .subcommand(
261 Command::new("pr")
262 .visible_aliases(["pull-request", "mr", "merge-request"])
263 .about("Open pull or merge request page")
264 .arg(Arg::new("id").value_name("id")),
265 )
266 .subcommand(
267 Command::new("pulls")
268 .visible_aliases(["prs", "merge-requests", "mrs"])
269 .about("Open pull or merge request list"),
270 )
271 .subcommand(
272 Command::new("issues")
273 .visible_alias("issue")
274 .about("Open issues list/page")
275 .arg(Arg::new("id").value_name("id")),
276 )
277 .subcommand(
278 Command::new("actions")
279 .visible_alias("action")
280 .about("Open actions page")
281 .arg(Arg::new("workflow").value_name("workflow")),
282 )
283 .subcommand(
284 Command::new("releases")
285 .visible_alias("release")
286 .about("Open releases list/page")
287 .arg(Arg::new("tag").value_name("tag")),
288 )
289 .subcommand(
290 Command::new("tags")
291 .visible_alias("tag")
292 .about("Open tags list/page")
293 .arg(Arg::new("tag").value_name("tag")),
294 )
295 .subcommand(
296 Command::new("commits")
297 .visible_alias("history")
298 .about("Open commit history page")
299 .arg(Arg::new("ref").value_name("ref")),
300 )
301 .subcommand(
302 Command::new("file")
303 .visible_alias("blob")
304 .about("Open file page")
305 .arg(
306 Arg::new("path")
307 .value_name("path")
308 .value_hint(ValueHint::FilePath),
309 )
310 .arg(Arg::new("ref").value_name("ref")),
311 )
312 .subcommand(
313 Command::new("blame")
314 .about("Open blame page")
315 .arg(
316 Arg::new("path")
317 .value_name("path")
318 .value_hint(ValueHint::FilePath),
319 )
320 .arg(Arg::new("ref").value_name("ref")),
321 )
322 .subcommand(Command::new("help").about("Display help message for open"))
323}
324
325fn remotes_arg() -> Arg {
326 Arg::new("remote").value_name("remote")
327}