git_prole/cli.rs
1use camino::Utf8PathBuf;
2use clap::Args;
3use clap::Parser;
4use clap::Subcommand;
5
6/// A `git-worktree(1)` manager.
7#[derive(Debug, Clone, Parser)]
8#[command(version, author, about)]
9#[command(max_term_width = 100, disable_help_subcommand = true)]
10pub struct Cli {
11 /// Log filter directives, of the form `target[span{field=value}]=level`, where all components
12 /// except the level are optional.
13 ///
14 /// Try `debug` or `trace`.
15 #[arg(long, default_value = "info", env = "GIT_PROLE_LOG", global = true)]
16 pub log: String,
17
18 /// If set, do not perform any actions, and instead only construct and print a plan.
19 #[arg(long, visible_alias = "dry", default_value = "false", global = true)]
20 pub dry_run: bool,
21
22 /// The location to read the configuration file from. Defaults to
23 /// `~/.config/git-prole/config.toml`.
24 #[arg(long, global = true)]
25 pub config: Option<Utf8PathBuf>,
26
27 #[command(subcommand)]
28 pub command: Command,
29}
30
31impl Cli {
32 /// A fake stub CLI for testing.
33 #[cfg(test)]
34 pub fn test_stub() -> Self {
35 Self {
36 log: "info".to_owned(),
37 dry_run: false,
38 config: None,
39 command: Command::Convert(ConvertArgs {
40 default_branch: None,
41 destination: None,
42 }),
43 }
44 }
45}
46
47#[allow(rustdoc::bare_urls)]
48#[derive(Debug, Clone, Subcommand)]
49pub enum Command {
50 /// Convert a repository into a worktree checkout.
51 ///
52 /// This will convert the repository in the current directory into a worktree repository. This includes:
53 ///
54 /// - Making the repository a bare repository.
55 ///
56 /// - Converting the current checkout (branch, commit, whatever) into a worktree.
57 /// Uncommited changes will be kept, but will not remain unstaged.
58 ///
59 /// - Creating a new worktree for the default branch.
60 Convert(ConvertArgs),
61
62 /// Clone a repository into a worktree checkout.
63 ///
64 /// If you have `gh` installed and the URL looks `gh`-like and isn't an existing local path,
65 /// I'll pass the repository URL to that.
66 ///
67 /// This is just a regular `git clone` followed by `git prole convert`.
68 Clone(CloneArgs),
69
70 /// Add a new worktree.
71 ///
72 /// This command tries to guess what you want, and as a result the behavior can be a little bit
73 /// subtle! If given, the `--branch` argument will always create a new branch, and the
74 /// `COMMITISH` argument will always be checked out in the new worktree; use those to
75 /// disambiguate when necessary.
76 ///
77 /// Unlike `git worktree add`, this will set new worktrees to start at and track the default
78 /// branch by default, rather than the checked out commit or branch of the worktree the command
79 /// is run from.
80 ///
81 /// By default, untracked files are copied to the new worktree.
82 Add(AddArgs),
83
84 /// Initialize the configuration file.
85 #[command(subcommand)]
86 Config(ConfigCommand),
87
88 /// Generate shell completions.
89 Completions {
90 /// Shell to generate completions for.
91 shell: clap_complete::shells::Shell,
92 },
93
94 /// Generate man pages.
95 #[cfg(feature = "clap_mangen")]
96 Manpages {
97 /// Directory to write man pages to.
98 out_dir: camino::Utf8PathBuf,
99 },
100}
101
102#[derive(Args, Clone, Debug)]
103pub struct ConvertArgs {
104 /// A default branch to create a worktree for.
105 #[arg(long)]
106 pub default_branch: Option<String>,
107
108 /// The directory to place the worktrees into.
109 #[arg()]
110 pub destination: Option<Utf8PathBuf>,
111}
112
113#[derive(Args, Clone, Debug)]
114pub struct CloneArgs {
115 /// The repository URL to clone.
116 #[arg()]
117 pub repository: String,
118
119 /// The directory to setup the worktrees in.
120 ///
121 /// Defaults to the last component of the repository URL, with a trailing `.git` removed.
122 #[arg()]
123 pub directory: Option<Utf8PathBuf>,
124
125 /// Extra arguments to forward to `git clone`.
126 #[arg(last = true)]
127 pub clone_args: Vec<String>,
128}
129
130#[derive(Args, Clone, Debug)]
131pub struct AddArgs {
132 #[command(flatten)]
133 pub inner: AddArgsInner,
134
135 /// The commit to check out in the new worktree.
136 ///
137 /// If this is the name of a unique remote branch, then a local branch with the same name will
138 /// be created to track the remote branch.
139 #[arg()]
140 pub commitish: Option<String>,
141
142 /// Extra arguments to forward to `git worktree add`.
143 #[arg(last = true)]
144 pub worktree_add_args: Vec<String>,
145}
146
147#[derive(Args, Clone, Debug)]
148#[group(required = true, multiple = true)]
149pub struct AddArgsInner {
150 /// Create a new branch with the given name instead of checking out an existing branch.
151 ///
152 /// This will refuse to reset a branch if it already exists; use `--force-branch`/`-B` to
153 /// reset existing branches.
154 #[arg(
155 long,
156 short = 'b',
157 visible_alias = "create",
158 visible_short_alias = 'c',
159 conflicts_with_all = ["force_branch", "detach"],
160 )]
161 pub branch: Option<String>,
162
163 /// Create a new branch with the given name, overwriting any existing branch with the same
164 /// name.
165 #[arg(
166 long,
167 short = 'B',
168 visible_alias = "force-create",
169 visible_short_alias = 'C',
170 conflicts_with_all = ["branch", "detach"],
171 )]
172 pub force_branch: Option<String>,
173
174 /// Create the new worktree in detached mode, not checked out on any branch.
175 #[arg(
176 long,
177 short = 'd',
178 alias = "detached",
179 conflicts_with_all = ["branch", "force_branch"],
180 )]
181 pub detach: bool,
182
183 /// The new worktree's name or path.
184 ///
185 /// If the name contains a `/`, it's assumed to be a path. Otherwise, it's assumed to be a
186 /// worktree name: it's used as a name in the same directory as the other worktrees, and (by
187 /// default) a branch with that name is checked out or created. (When this is a path, only the
188 /// last component of the path is used as the branch name.)
189 #[arg()]
190 pub name_or_path: Option<String>,
191}
192
193#[derive(Debug, Clone, Subcommand)]
194pub enum ConfigCommand {
195 /// Initialize a default configuration file.
196 Init(ConfigInitArgs),
197}
198
199#[derive(Args, Clone, Debug)]
200pub struct ConfigInitArgs {
201 /// The location to write the configuration file. Can be `-` for stdout. Defaults to
202 /// `~/.config/git-prole/config.toml`.
203 pub output: Option<Utf8PathBuf>,
204}