Skip to main content

git_branchless/commands/
mod.rs

1//! Sub-commands of `git-branchless`.
2
3mod amend;
4mod bug_report;
5mod hide;
6mod repair;
7mod restack;
8mod snapshot;
9mod split;
10mod sync;
11mod wrap;
12
13use git_branchless_invoke::CommandContext;
14use lib::core::rewrite::MergeConflictRemediation;
15
16use lib::util::ExitCode;
17use lib::{core::gc, util::EyreExitOr};
18
19use git_branchless_opts::{
20    Command, MessageArgs, Opts, ResolveRevsetOptions, SnapshotSubcommand, WrappedCommand,
21    rewrite_args,
22};
23use lib::git::GitRunInfo;
24
25fn command_main(ctx: CommandContext, opts: Opts) -> EyreExitOr<()> {
26    let CommandContext {
27        effects,
28        git_run_info,
29    } = ctx.clone();
30    let Opts {
31        global_args: _,
32        command,
33    } = opts;
34
35    let exit_code = match command {
36        Command::Amend {
37            move_options,
38            untracked_file_strategy,
39        } => amend::amend(
40            &effects,
41            &git_run_info,
42            &ResolveRevsetOptions::default(),
43            &move_options,
44            untracked_file_strategy,
45        )?,
46
47        Command::BugReport => bug_report::bug_report(&effects, &git_run_info)?,
48
49        Command::Difftool(opts) => {
50            let result = scm_diff_editor::run(opts);
51            match result {
52                Ok(()) | Err(scm_diff_editor::Error::Cancelled) => Ok(()),
53                Err(err) => {
54                    eprintln!("Error: {err}");
55                    Err(ExitCode(1))
56                }
57            }
58        }
59
60        Command::Switch { switch_options } => {
61            git_branchless_navigation::switch(&effects, &git_run_info, &switch_options)?
62        }
63
64        Command::Gc => {
65            gc::gc(&effects)?;
66            Ok(())
67        }
68
69        Command::Hook(args) => git_branchless_hook::command_main(ctx, args)?,
70
71        Command::Hide {
72            revsets,
73            resolve_revset_options,
74            no_delete_branches,
75            recursive,
76        } => hide::hide(
77            &effects,
78            &git_run_info,
79            revsets,
80            &resolve_revset_options,
81            no_delete_branches,
82            recursive,
83        )?,
84
85        Command::Init(args) => git_branchless_init::command_main(ctx, args)?,
86
87        Command::InstallManPages(args) => {
88            git_branchless_init::command_install_man_pages(ctx, args)?
89        }
90
91        Command::Move {
92            source,
93            dest,
94            base,
95            exact,
96            resolve_revset_options,
97            move_options,
98            fixup,
99            insert,
100            dry_run,
101        } => git_branchless_move::r#move(
102            &effects,
103            &git_run_info,
104            source,
105            dest,
106            base,
107            exact,
108            &resolve_revset_options,
109            &move_options,
110            fixup,
111            insert,
112            dry_run,
113        )?,
114
115        Command::Next {
116            traverse_commits_options,
117        } => git_branchless_navigation::traverse_commits(
118            &effects,
119            &git_run_info,
120            git_branchless_navigation::Command::Next,
121            &traverse_commits_options,
122        )?,
123
124        Command::Prev {
125            traverse_commits_options,
126        } => git_branchless_navigation::traverse_commits(
127            &effects,
128            &git_run_info,
129            git_branchless_navigation::Command::Prev,
130            &traverse_commits_options,
131        )?,
132
133        Command::Query(args) => git_branchless_query::command_main(ctx, args)?,
134
135        Command::Repair { dry_run } => repair::repair(&effects, dry_run)?,
136
137        Command::Restack {
138            revsets,
139            resolve_revset_options,
140            move_options,
141        } => restack::restack(
142            &effects,
143            &git_run_info,
144            revsets,
145            &resolve_revset_options,
146            &move_options,
147            MergeConflictRemediation::Retry,
148        )?,
149
150        Command::Record(args) => git_branchless_record::command_main(ctx, args)?,
151
152        Command::Reword {
153            revsets,
154            message_args:
155                MessageArgs {
156                    messages,
157                    commit_to_fixup,
158                },
159            resolve_revset_options,
160            force_rewrite_public_commits,
161            discard,
162        } => {
163            let messages = if discard {
164                git_branchless_reword::InitialCommitMessages::Discard
165            } else if let Some(commit_to_fixup) = commit_to_fixup {
166                git_branchless_reword::InitialCommitMessages::FixUp(commit_to_fixup)
167            } else {
168                git_branchless_reword::InitialCommitMessages::Messages(messages)
169            };
170            git_branchless_reword::reword(
171                &effects,
172                revsets,
173                &resolve_revset_options,
174                messages,
175                &git_run_info,
176                force_rewrite_public_commits,
177            )?
178        }
179
180        Command::Smartlog(args) => git_branchless_smartlog::command_main(ctx, args)?,
181
182        Command::Snapshot { subcommand } => match subcommand {
183            SnapshotSubcommand::Create => snapshot::create(&effects, &git_run_info)?,
184            SnapshotSubcommand::Restore { snapshot_oid } => {
185                snapshot::restore(&effects, &git_run_info, snapshot_oid)?
186            }
187        },
188
189        Command::Split {
190            before,
191            detach,
192            discard,
193            files,
194            resolve_revset_options,
195            revset,
196            move_options,
197        } => {
198            let split_mode = match (before, detach, discard) {
199                (false, true, false) => split::SplitMode::DetachAfter,
200                (false, false, true) => split::SplitMode::Discard,
201                (false, false, false) => split::SplitMode::InsertAfter,
202                (true, false, false) => split::SplitMode::InsertBefore,
203                (true, true, false)
204                | (true, false, true)
205                | (false, true, true)
206                | (true, true, true) => {
207                    unreachable!("clap should prevent this")
208                }
209            };
210
211            split::split(
212                &effects,
213                revset,
214                &resolve_revset_options,
215                files,
216                split_mode,
217                &move_options,
218                &git_run_info,
219            )?
220        }
221
222        Command::Submit(args) => git_branchless_submit::command_main(ctx, args)?,
223
224        Command::Sync {
225            pull,
226            move_options,
227            revsets,
228            resolve_revset_options,
229        } => sync::sync(
230            &effects,
231            &git_run_info,
232            pull,
233            &move_options,
234            revsets,
235            &resolve_revset_options,
236        )?,
237
238        Command::Test(args) => git_branchless_test::command_main(ctx, args)?,
239
240        Command::Undo { interactive, yes } => {
241            git_branchless_undo::undo(&effects, &git_run_info, interactive, yes)?
242        }
243
244        Command::Unhide {
245            revsets,
246            resolve_revset_options,
247            recursive,
248        } => hide::unhide(&effects, revsets, &resolve_revset_options, recursive)?,
249
250        Command::Wrap {
251            git_executable: explicit_git_executable,
252            command: WrappedCommand::WrappedCommand(args),
253        } => {
254            let git_run_info = match explicit_git_executable {
255                Some(path_to_git) => GitRunInfo {
256                    path_to_git,
257                    ..git_run_info
258                },
259                None => git_run_info,
260            };
261            wrap::wrap(&git_run_info, args.as_slice())?
262        }
263    };
264
265    Ok(exit_code)
266}
267
268/// Execute the main process and exit with the appropriate exit code.
269pub fn main() {
270    // Install panic handler.
271    color_eyre::install().expect("Could not install panic handler");
272    let args: Vec<_> = std::env::args_os().collect();
273    let args = rewrite_args(args);
274    let exit_code = git_branchless_invoke::do_main_and_drop_locals(command_main, args)
275        .expect("A fatal error occurred");
276    std::process::exit(exit_code);
277}