add cli flag to open files tab with selected file (#2746)

This commit is contained in:
Christian Zangl 2025-11-28 21:53:07 +01:00 committed by GitHub
parent a6d6f31885
commit ffa6d9e57b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 77 additions and 19 deletions

View file

@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* dx: `make check` checks Cargo.toml dependency ordering using `cargo sort` [[@naseschwarz](https://github.com/naseschwarz)]
* add `use_selection_fg` to theme file to allow customizing selection foreground color [[@Upsylonbare](https://github.com/Upsylonbare)] ([#2515](https://github.com/gitui-org/gitui/pull/2515))
* Add "go to line" command for the blame view [[@andrea-berling](https://github.com/andrea-berling)] ([#2262](https://github.com/extrawurst/gitui/pull/2262))
* add `--file` cli flag to open the files tab with the given file already selected [[@laktak](https://github.com/laktak)] ([#2510](https://github.com/gitui-org/gitui/issues/2510))
### Changed
* improve error messages [[@acuteenvy](https://github.com/acuteenvy)] ([#2617](https://github.com/gitui-org/gitui/pull/2617))

View file

@ -1,5 +1,6 @@
use crate::{
accessors,
args::CliArgs,
cmdbar::CommandBar,
components::{
command_pump, event_pump, CommandInfo, Component,
@ -151,13 +152,14 @@ impl App {
///
#[allow(clippy::too_many_lines)]
pub fn new(
repo: RepoPathRef,
cliargs: CliArgs,
sender_git: Sender<AsyncGitNotification>,
sender_app: Sender<AsyncAppNotification>,
input: Input,
theme: Theme,
key_config: KeyConfig,
) -> Result<Self> {
let repo = RefCell::new(cliargs.repo_path.clone());
log::trace!("open repo at: {:?}", &repo);
let repo_path_text =
@ -173,7 +175,20 @@ impl App {
sender_app,
};
let tab = env.options.borrow().current_tab();
let mut select_file: Option<PathBuf> = None;
let tab = if let Some(file) = cliargs.select_file {
// convert to relative git path
if let Ok(abs) = file.canonicalize() {
if let Ok(path) = abs.strip_prefix(
env.repo.borrow().gitpath().canonicalize()?,
) {
select_file = Some(Path::new(".").join(path));
}
}
2
} else {
env.options.borrow().current_tab()
};
let mut app = Self {
input,
@ -218,7 +233,7 @@ impl App {
status_tab: Status::new(&env),
stashing_tab: Stashing::new(&env),
stashlist_tab: StashList::new(&env),
files_tab: FilesTab::new(&env),
files_tab: FilesTab::new(&env, select_file),
goto_line_popup: GotoLinePopup::new(&env),
tab: 0,
queue: env.queue,

View file

@ -17,13 +17,16 @@ const LOG_FILE_FLAG_ID: &str = "logfile";
const LOGGING_FLAG_ID: &str = "logging";
const THEME_FLAG_ID: &str = "theme";
const WORKDIR_FLAG_ID: &str = "workdir";
const FILE_FLAG_ID: &str = "file";
const GIT_DIR_FLAG_ID: &str = "directory";
const WATCHER_FLAG_ID: &str = "watcher";
const DEFAULT_THEME: &str = "theme.ron";
const DEFAULT_GIT_DIR: &str = ".";
#[derive(Clone)]
pub struct CliArgs {
pub theme: PathBuf,
pub select_file: Option<PathBuf>,
pub repo_path: RepoPath,
pub notify_watcher: bool,
}
@ -51,6 +54,10 @@ pub fn process_cmdline() -> Result<CliArgs> {
PathBuf::from,
);
let select_file = arg_matches
.get_one::<String>(FILE_FLAG_ID)
.map(PathBuf::from);
let repo_path = if let Some(w) = workdir {
RepoPath::Workdir { gitdir, workdir: w }
} else {
@ -75,6 +82,7 @@ pub fn process_cmdline() -> Result<CliArgs> {
Ok(CliArgs {
theme,
select_file,
repo_path,
notify_watcher,
})
@ -129,6 +137,13 @@ fn app() -> ClapApp {
.long("bugreport")
.action(clap::ArgAction::SetTrue),
)
.arg(
Arg::new(FILE_FLAG_ID)
.help("Select the file in the file tab")
.short('f')
.long("file")
.num_args(1),
)
.arg(
Arg::new(GIT_DIR_FLAG_ID)
.help("Set the git directory")

View file

@ -30,7 +30,10 @@ use ratatui::{
Frame,
};
use std::{borrow::Cow, fmt::Write};
use std::{collections::BTreeSet, path::Path};
use std::{
collections::BTreeSet,
path::{Path, PathBuf},
};
use unicode_truncate::UnicodeTruncateStr;
use unicode_width::UnicodeWidthStr;
@ -53,11 +56,15 @@ pub struct RevisionFilesComponent {
revision: Option<CommitInfo>,
focus: Focus,
key_config: SharedKeyConfig,
select_file: Option<PathBuf>,
}
impl RevisionFilesComponent {
///
pub fn new(env: &Environment) -> Self {
pub fn new(
env: &Environment,
select_file: Option<PathBuf>,
) -> Self {
Self {
queue: env.queue.clone(),
tree: FileTree::default(),
@ -72,6 +79,7 @@ impl RevisionFilesComponent {
focus: Focus::Tree,
key_config: env.key_config.clone(),
repo: env.repo.clone(),
select_file,
visible: false,
}
}
@ -134,6 +142,12 @@ impl RevisionFilesComponent {
self.tree.collapse_but_root();
self.files = Some(last);
let select_file = self.select_file.clone();
self.select_file = None;
if let Some(file) = select_file {
self.find_file(file.as_path());
}
}
} else if let Some(rev) = &self.revision {
self.request_files(rev.id);

View file

@ -79,7 +79,10 @@ mod tabs;
mod ui;
mod watcher;
use crate::{app::App, args::process_cmdline};
use crate::{
app::App,
args::{process_cmdline, CliArgs},
};
use anyhow::{anyhow, bail, Result};
use app::QuitState;
use asyncgit::{
@ -102,7 +105,6 @@ use scopeguard::defer;
use scopetime::scope_time;
use spinner::Spinner;
use std::{
cell::RefCell,
io::{self, Stdout},
panic,
path::Path,
@ -180,8 +182,8 @@ fn main() -> Result<()> {
set_panic_handler()?;
let mut repo_path = cliargs.repo_path;
let mut terminal = start_terminal(io::stdout(), &repo_path)?;
let mut terminal =
start_terminal(io::stdout(), &cliargs.repo_path)?;
let input = Input::new();
let updater = if cliargs.notify_watcher {
@ -190,10 +192,12 @@ fn main() -> Result<()> {
Updater::Ticker
};
let mut args = cliargs;
loop {
let quit_state = run_app(
app_start,
repo_path.clone(),
args.clone(),
theme.clone(),
key_config.clone(),
&input,
@ -203,7 +207,12 @@ fn main() -> Result<()> {
match quit_state {
QuitState::OpenSubmodule(p) => {
repo_path = p;
args = CliArgs {
repo_path: p,
select_file: None,
theme: args.theme,
notify_watcher: args.notify_watcher,
}
}
_ => break,
}
@ -214,7 +223,7 @@ fn main() -> Result<()> {
fn run_app(
app_start: Instant,
repo: RepoPath,
cliargs: CliArgs,
theme: Theme,
key_config: KeyConfig,
input: &Input,
@ -228,8 +237,9 @@ fn run_app(
let (rx_ticker, rx_watcher) = match updater {
Updater::NotifyWatcher => {
let repo_watcher =
RepoWatcher::new(repo_work_dir(&repo)?.as_str());
let repo_watcher = RepoWatcher::new(
repo_work_dir(&cliargs.repo_path)?.as_str(),
);
(never(), repo_watcher.receiver())
}
@ -239,7 +249,7 @@ fn run_app(
let spinner_ticker = tick(SPINNER_INTERVAL);
let mut app = App::new(
RefCell::new(repo),
cliargs,
tx_git,
tx_app,
input.clone(),

View file

@ -38,7 +38,7 @@ impl RevisionFilesPopup {
///
pub fn new(env: &Environment) -> Self {
Self {
files: RevisionFilesComponent::new(env),
files: RevisionFilesComponent::new(env, None),
visible: false,
key_config: env.key_config.clone(),
open_request: None,

View file

@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use crate::{
app::Environment,
@ -19,10 +19,13 @@ pub struct FilesTab {
impl FilesTab {
///
pub fn new(env: &Environment) -> Self {
pub fn new(
env: &Environment,
select_file: Option<PathBuf>,
) -> Self {
Self {
visible: false,
files: RevisionFilesComponent::new(env),
files: RevisionFilesComponent::new(env, select_file),
repo: env.repo.clone(),
}
}