diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index f8a00ad1..26e76e76 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -25,6 +25,7 @@ pub(crate) mod id; pub(crate) mod import; pub(crate) mod init; pub(crate) mod log; +pub(crate) mod name; pub(crate) mod new; pub(crate) mod next; pub(crate) mod patches; @@ -101,6 +102,7 @@ pub(crate) const STGIT_COMMANDS: &[StGitCommand] = &[ import::STGIT_COMMAND, init::STGIT_COMMAND, log::STGIT_COMMAND, + name::STGIT_COMMAND, new::STGIT_COMMAND, next::STGIT_COMMAND, patches::STGIT_COMMAND, diff --git a/src/cmd/name.rs b/src/cmd/name.rs new file mode 100644 index 00000000..4e4be9f4 --- /dev/null +++ b/src/cmd/name.rs @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only + +//! `stg name` implementation. + +use std::io::Write; + +use anyhow::{anyhow, Result}; +use clap::{Arg, ArgMatches}; +use termcolor::WriteColor; + +use crate::{ + argset, + branchloc::BranchLocator, + ext::RepositoryExtended, + patch::SingleRevisionSpec, + stack::{InitializationPolicy, Stack, StackAccess, StackStateAccess}, +}; + +pub(super) const STGIT_COMMAND: super::StGitCommand = super::StGitCommand { + name: "name", + category: super::CommandCategory::PatchInspection, + make, + run, +}; + +fn make() -> clap::Command { + clap::Command::new(STGIT_COMMAND.name) + .about("Print patch name of a StGit revision") + .long_about( + "Print the patch name of a StGit revision.\n\ + \n\ + Try to get the name of the patch in the current \ + branch as specified by a StGit revision. Revisions \ + can be specified in the all the forms accepted by \ + \"stg id\" command.", + ) + .arg(argset::branch_arg()) + .arg( + Arg::new("show-branch") + .long("showbranch") + .help("Display the branch name with the patch") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("no-show-branch") + .long("no-showbranch") + .help("Do not display branch name") + .hide(true) + .action(clap::ArgAction::SetTrue) + .overrides_with("show-branch"), + ) + .arg( + Arg::new("stgit-revision") + .value_name("revision") + .allow_hyphen_values(true) + .value_parser(clap::value_parser!(SingleRevisionSpec)) + .help("StGit revision"), + ) +} + +fn run(matches: &ArgMatches) -> Result<()> { + let repo = gix::Repository::open()?; + + let stack = Stack::from_branch_locator( + &repo, + matches.get_one::("branch"), + InitializationPolicy::RequireInitialized, + )?; + + let oid = matches + .get_one::("stgit-revision") + .map(|spec| spec.resolve_object(&repo, &stack).map(|object| object.id)) + .transpose()? + .unwrap_or_else(|| stack.get_branch_head().id); + + let Some(patch_name) = (oid == stack.base().id).then_some("{base}").or_else(|| { + stack + .all_patches() + .find(|name| oid == stack.get_patch_commit_id(name)) + .map(|name| name.as_ref()) + }) else { + return Err(anyhow!("patch name not found for revision `{oid}`")); + }; + + let mut stdout = crate::color::get_color_stdout(matches); + let mut color_spec = termcolor::ColorSpec::new(); + color_spec.set_bold(true); + stdout.set_color(&color_spec)?; + + if matches.get_flag("show-branch") { + write!(stdout, "{}:", stack.get_branch_name())?; + } + + write!(stdout, "{patch_name}")?; + color_spec.clear(); + stdout.set_color(&color_spec)?; + writeln!(stdout)?; + + Ok(()) +} diff --git a/t/t3700-name.sh b/t/t3700-name.sh new file mode 100755 index 00000000..ba243ec1 --- /dev/null +++ b/t/t3700-name.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +test_description="Test 'stg name'" + +. ./test-lib.sh + +test_expect_success 'Test on uninitialized repo' ' + command_error stg name 2>err && + grep "stack not initialized" err +' + +test_expect_success 'Init repo' ' + echo "foo" >foo.txt && + git add foo.txt && + git commit -m "initial" && + git commit --allow-empty -m "base" && + git branch nostack && + for i in 1 2; do + echo "line $i" >>foo.txt && + stg new -m "patch-$i" && + stg refresh || return 1 + done && + stg branch -C cloned && + stg branch -c forked && + stg branch master +' + +test_expect_success 'Get patch names' ' + test "$(stg name)" = "patch-2" && + test "$(stg name -1)" = "patch-1" && + test "$(stg name 0~1)" = "{base}" && + test "$(stg name --showbranch)" = "master:patch-2" && + test "$(stg name -b cloned --showbranch)" = "cloned:patch-2" && + test "$(stg name -b forked --showbranch)" = "forked:{base}" +' + +test_expect_success 'Fail to get patch names' ' + command_error stg name -b nostack --showbranch 2>err && + grep "stack not initialized" err && + command_error stg name 0~2 2>err && + grep "patch name not found" err +' + +test_done