Skip to content

Commit 6f22e73

Browse files
committed
feat(init): set man.viewer to be able to access git-branchless man-pages
1 parent 12ad157 commit 6f22e73

File tree

4 files changed

+116
-15
lines changed

4 files changed

+116
-15
lines changed

src/commands/init.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,13 +308,42 @@ the branchless workflow will work properly.
308308
}
309309

310310
#[instrument]
311-
fn install_man_pages(effects: &Effects, repo: &Repo) -> eyre::Result<()> {
311+
fn install_man_pages(effects: &Effects, repo: &Repo, config: &mut Config) -> eyre::Result<()> {
312312
let should_install = cfg!(feature = "man-pages");
313313
if !should_install {
314314
return Ok(());
315315
}
316316

317-
let man_dir = repo.get_man1_dir();
317+
let man_dir = repo.get_man_dir();
318+
let man_dir_relative = {
319+
let man_dir_relative = man_dir.strip_prefix(repo.get_path()).wrap_err_with(|| {
320+
format!(
321+
"Getting relative path for {:?} with respect to {:?}",
322+
&man_dir,
323+
repo.get_path()
324+
)
325+
})?;
326+
&man_dir_relative.to_str().ok_or_else(|| {
327+
eyre::eyre!(
328+
"Could not convert man dir to UTF-8 string: {:?}",
329+
&man_dir_relative
330+
)
331+
})?
332+
};
333+
config.set(
334+
"man.branchless.cmd",
335+
format!(
336+
// FIXME: the path to the man directory is not shell-escaped.
337+
//
338+
// NB: the trailing `:` at the end of `MANPATH` indicates to `man`
339+
// that it should try its normal lookup paths if the requested
340+
// `man`-page cannot be found in the provided `MANPATH`.
341+
"env MANPATH=.git/{}: man",
342+
man_dir_relative
343+
),
344+
)?;
345+
config.set("man.viewer", "branchless")?;
346+
318347
write_man_pages(&man_dir).wrap_err_with(|| format!("Writing man-pages to: {:?}", &man_dir))?;
319348
Ok(())
320349
}
@@ -459,7 +488,7 @@ pub fn init(effects: &Effects, git_run_info: &GitRunInfo) -> eyre::Result<()> {
459488
set_configs(&mut in_, effects, &repo, &mut config)?;
460489
install_hooks(effects, &repo)?;
461490
install_aliases(effects, &mut repo, &mut config, git_run_info)?;
462-
install_man_pages(effects, &repo)?;
491+
install_man_pages(effects, &repo, &mut config)?;
463492
writeln!(
464493
effects.get_output_stream(),
465494
"{}",

src/git/repo.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,12 @@ impl Repo {
223223
self.get_path().join("branchless").join("dag")
224224
}
225225

226-
/// Get the directory to store man-pages. Note that this is the `man/man1`
227-
/// directory, specifically referring to the section 1 manual pages.
226+
/// Get the directory to store man-pages. Note that this is the `man`
227+
/// directory, and not a subsection thereof. `git-branchless` man-pages must
228+
/// go into the `man/man1` directory to be found by `man`.
228229
#[instrument]
229-
pub fn get_man1_dir(&self) -> PathBuf {
230-
self.get_path().join("branchless").join("man").join("man1")
230+
pub fn get_man_dir(&self) -> PathBuf {
231+
self.get_path().join("branchless").join("man")
231232
}
232233

233234
/// Get the connection to the SQLite database for this repository.

src/opts.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -238,22 +238,23 @@ pub struct Opts {
238238

239239
/// Generate and write man-pages into the specified directory.
240240
///
241-
/// The generated files are named things like `git-branchless-smartlog.1`, so
242-
/// this directory should be of the form `path/to/man/man1`, to ensure that
243-
/// these files get generated into the correct man-page section.
241+
/// The generated files are named things like `man1/git-branchless-smartlog.1`,
242+
/// so this directory should be of the form `path/to/man`, to ensure that these
243+
/// files get generated into the correct man-page section.
244244
pub fn write_man_pages(man_dir: &Path) -> std::io::Result<()> {
245-
std::fs::create_dir_all(&man_dir)?;
245+
let man1_dir = man_dir.join("man1");
246+
std::fs::create_dir_all(&man1_dir)?;
246247

247248
let app = Opts::into_app();
248-
generate_man_page(man_dir, "git-branchless", &app)?;
249+
generate_man_page(&man1_dir, "git-branchless", &app)?;
249250
for subcommand in app.get_subcommands() {
250251
let subcommand_exe_name = format!("git-branchless-{}", subcommand.get_name());
251-
generate_man_page(man_dir, &subcommand_exe_name, subcommand)?;
252+
generate_man_page(&man1_dir, &subcommand_exe_name, subcommand)?;
252253
}
253254
Ok(())
254255
}
255256

256-
fn generate_man_page(man_dir: &Path, name: &str, command: &App) -> std::io::Result<()> {
257+
fn generate_man_page(man1_dir: &Path, name: &str, command: &App) -> std::io::Result<()> {
257258
let mut manual = Manual::new(name);
258259
if let Some(about) = command.get_about() {
259260
manual = manual.about(about);
@@ -312,7 +313,7 @@ fn generate_man_page(man_dir: &Path, name: &str, command: &App) -> std::io::Resu
312313

313314
// @nocommit do rest of man page info
314315

315-
let output_path = man_dir.join(format!("{}.1", name));
316+
let output_path = man1_dir.join(format!("{}.1", name));
316317
std::fs::write(output_path, manual.render())?;
317318
Ok(())
318319
}

tests/command/test_init.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,73 @@ fn test_init_uninstall() -> eyre::Result<()> {
254254

255255
Ok(())
256256
}
257+
258+
#[cfg(feature = "man-pages")]
259+
#[test]
260+
fn test_man_viewer_installed() -> eyre::Result<()> {
261+
use std::collections::HashMap;
262+
263+
use itertools::Itertools;
264+
265+
// The `man` executable isn't installed for most Windows Git installations.
266+
// In particular, it's not installed on Github Actions. It might be
267+
// possible to install it manually, but I didn't bother.
268+
//
269+
// See https://stackoverflow.com/q/5517564,
270+
// https://github.com/swcarpentry/shell-novice/issues/249
271+
let should_skip = cfg!(windows);
272+
if should_skip {
273+
return Ok(());
274+
}
275+
276+
let git = make_git()?;
277+
git.init_repo()?;
278+
279+
// `env` and `man` are not on the sanitized testing `PATH`, so use the
280+
// caller's `PATH` instead.
281+
let testing_path = git.get_path_for_env();
282+
let testing_path = std::env::split_paths(&testing_path).collect_vec();
283+
let inherited_path = std::env::var_os("PATH").unwrap();
284+
let inherited_path = std::env::split_paths(&inherited_path).collect_vec();
285+
let env = {
286+
let mut env = HashMap::new();
287+
let full_path = std::env::join_paths(testing_path.iter().chain(inherited_path.iter()))?;
288+
let full_path = full_path.to_str().unwrap().to_owned();
289+
env.insert("PATH".to_string(), full_path);
290+
env
291+
};
292+
293+
{
294+
let (stdout, _stderr) = git.run_with_options(
295+
&["smartlog", "--help"],
296+
&GitRunOptions {
297+
env: env.clone(),
298+
..Default::default()
299+
},
300+
)?;
301+
let first_word = stdout.split_whitespace().next();
302+
insta::assert_debug_snapshot!(first_word, @r###"
303+
Some(
304+
"GIT-BRANCHLESS-SMARTLOG(1)",
305+
)
306+
"###);
307+
}
308+
309+
{
310+
let (stdout, _stderr) = git.run_with_options(
311+
&["init", "--help"],
312+
&GitRunOptions {
313+
env,
314+
..Default::default()
315+
},
316+
)?;
317+
let first_word = stdout.split_whitespace().next();
318+
insta::assert_debug_snapshot!(first_word, @r###"
319+
Some(
320+
"GIT-INIT(1)",
321+
)
322+
"###);
323+
}
324+
325+
Ok(())
326+
}

0 commit comments

Comments
 (0)