cli/diff: Use clap

The implementation of the *deprecated* command is removed, it now
directly invokes `git diff`.
This commit is contained in:
Lorenz Leutgeb 2025-10-01 22:17:24 +02:00 committed by Erik Kundt
parent 532e5a0de0
commit 72cf3d1919
4 changed files with 122 additions and 249 deletions

View File

@ -32,76 +32,74 @@ $ git commit -m "Make changes"
``` ```
$ rad diff HEAD^ HEAD $ rad diff HEAD^ HEAD
╭────────────────────────────────────────────╮ diff --git a/README b/README.md
│ README -> README.md moved similarity index 100%
╰────────────────────────────────────────────╯ rename from README
rename to README.md
╭────────────────────────────────────────────╮ diff --git a/main.c b/main.c
│ main.c +6 created new file mode 100644
├────────────────────────────────────────────┤ index 0000000..aae4e0e
│ @@ -0,0 +1,6 @@ │ --- /dev/null
│ 1 + #include <stdio.h> +++ b/main.c
│ 2 + │ @@ -0,0 +1,6 @@
│ 3 + int main(void) { │ +#include <stdio.h>
│ 4 + printf("Hello World!/n"); │ +
│ 5 + return 0; │ +int main(void) {
│ 6 + } │ + printf("Hello World!/n");
╰────────────────────────────────────────────╯ + return 0;
+}
``` ```
``` ```
$ sed -i 's/Hello World/Hello Radicle/' main.c $ sed -i 's/Hello World/Hello Radicle/' main.c
$ rad diff $ rad diff
╭──────────────────────────────────────────────╮ diff --git a/main.c b/main.c
│ main.c -1 +1 │ index aae4e0e..a3ed869 100644
├──────────────────────────────────────────────┤ --- a/main.c
│ @@ -1,6 +1,6 @@ │ +++ b/main.c
│ 1 1 #include <stdio.h> @@ -1,6 +1,6 @@
│ 2 2 │ #include <stdio.h>
│ 3 3 int main(void) { │
│ 4 - printf("Hello World!/n"); │ int main(void) {
│ 4 + printf("Hello Radicle!/n"); │ - printf("Hello World!/n");
│ 5 5 return 0; │ + printf("Hello Radicle!/n");
│ 6 6 } │ return 0;
╰──────────────────────────────────────────────╯ }
``` ```
``` ```
$ git add main.c $ git add main.c
$ rad diff $ rad diff
$ rad diff --staged $ rad diff --staged
╭──────────────────────────────────────────────╮ diff --git a/main.c b/main.c
│ main.c -1 +1 │ index aae4e0e..a3ed869 100644
├──────────────────────────────────────────────┤ --- a/main.c
│ @@ -1,6 +1,6 @@ │ +++ b/main.c
│ 1 1 #include <stdio.h> @@ -1,6 +1,6 @@
│ 2 2 │ #include <stdio.h>
│ 3 3 int main(void) { │
│ 4 - printf("Hello World!/n"); │ int main(void) {
│ 4 + printf("Hello Radicle!/n"); │ - printf("Hello World!/n");
│ 5 5 return 0; │ + printf("Hello Radicle!/n");
│ 6 6 } │ return 0;
╰──────────────────────────────────────────────╯ }
``` ```
``` ```
$ git rm -f -q main.c $ git rm -f -q main.c
$ rad diff --staged $ rad diff --staged
╭────────────────────────────────────────────╮ diff --git a/main.c b/main.c
│ main.c -6 deleted deleted file mode 100644
├────────────────────────────────────────────┤ index aae4e0e..0000000
│ @@ -1,6 +0,0 @@ │ --- a/main.c
│ 1 - #include <stdio.h> +++ /dev/null
│ 2 - │ @@ -1,6 +0,0 @@
│ 3 - int main(void) { │ -#include <stdio.h>
│ 4 - printf("Hello World!/n"); │ -
│ 5 - return 0; │ -int main(void) {
│ 6 - } │ - printf("Hello World!/n");
╰────────────────────────────────────────────╯ - return 0;
-}
``` ```
For now, copies are not detected. For now, copies are not detected.
@ -112,13 +110,13 @@ $ mkdir docs
$ cp README.md docs/README.md $ cp README.md docs/README.md
$ git add docs $ git add docs
$ rad diff --staged $ rad diff --staged
╭─────────────────────────────╮ diff --git a/docs/README.md b/docs/README.md
│ docs/README.md +1 created new file mode 100644
├─────────────────────────────┤ index 0000000..980a0d5
│ @@ -0,0 +1,1 @@ │ --- /dev/null
│ 1 + Hello World! │ +++ b/docs/README.md
╰─────────────────────────────╯ @@ -0,0 +1 @@
+Hello World!
$ git reset $ git reset
$ git checkout . $ git checkout .
``` ```
@ -129,10 +127,9 @@ Empty file.
$ touch EMPTY $ touch EMPTY
$ git add EMPTY $ git add EMPTY
$ rad diff --staged $ rad diff --staged
╭─────────────────╮ diff --git a/EMPTY b/EMPTY
│ EMPTY created new file mode 100644
╰─────────────────╯ index 0000000..e69de29
$ git reset $ git reset
$ git checkout . $ git checkout .
``` ```
@ -142,10 +139,9 @@ File mode change.
``` ```
$ chmod +x README.md $ chmod +x README.md
$ rad diff $ rad diff
╭───────────────────────────────────────────╮ diff --git a/README.md b/README.md
│ README.md 100644 -> 100755 mode changed old mode 100644
╰───────────────────────────────────────────╯ new mode 100755
$ git reset -q $ git reset -q
$ git checkout . $ git checkout .
``` ```
@ -157,8 +153,8 @@ $ touch file.bin
$ truncate -s 8 file.bin $ truncate -s 8 file.bin
$ git add file.bin $ git add file.bin
$ rad diff --staged $ rad diff --staged
╭─────────────────────────────╮ diff --git a/file.bin b/file.bin
│ file.bin binary created new file mode 100644
╰─────────────────────────────╯ index 0000000..1b1cb4d
Binary files /dev/null and b/file.bin differ
``` ```

View File

@ -11,13 +11,13 @@ $ git push rad HEAD:refs/patches
``` ```
``` ```
$ rad patch diff 147309e $ rad patch diff 147309e
╭───────────────────────────╮ diff --git a/README.md b/README.md
│ README.md +1 created new file mode 100644
├───────────────────────────┤ index 0000000..980a0d5
│ @@ -0,0 +1,1 @@ │ --- /dev/null
│ 1 + Hello World! │ +++ b/README.md
╰───────────────────────────╯ @@ -0,0 +1 @@
+Hello World!
``` ```
If we add another file and update the patch, we can see it in the diff. If we add another file and update the patch, we can see it in the diff.
@ -32,20 +32,20 @@ $ git push -f
``` ```
``` ```
$ rad patch diff 147309e $ rad patch diff 147309e
╭─────────────────────────────╮ diff --git a/RADICLE.md b/RADICLE.md
│ RADICLE.md +1 created new file mode 100644
├─────────────────────────────┤ index 0000000..e517184
│ @@ -0,0 +1,1 @@ │ --- /dev/null
│ 1 + Hello Radicle! │ +++ b/RADICLE.md
╰─────────────────────────────╯ @@ -0,0 +1 @@
+Hello Radicle!
╭─────────────────────────────╮ diff --git a/README.md b/README.md
│ README.md +1 created new file mode 100644
├─────────────────────────────┤ index 0000000..980a0d5
│ @@ -0,0 +1,1 @@ │ --- /dev/null
│ 1 + Hello World! │ +++ b/README.md
╰─────────────────────────────╯ @@ -0,0 +1 @@
+Hello World!
``` ```
Buf if we only want to see the changes from the first revision, we can do that Buf if we only want to see the changes from the first revision, we can do that
@ -53,11 +53,11 @@ too.
``` ```
$ rad patch diff 147309e --revision 147309e $ rad patch diff 147309e --revision 147309e
╭───────────────────────────╮ diff --git a/README.md b/README.md
│ README.md +1 created new file mode 100644
├───────────────────────────┤ index 0000000..980a0d5
│ @@ -0,0 +1,1 @@ │ --- /dev/null
│ 1 + Hello World! │ +++ b/README.md
╰───────────────────────────╯ @@ -0,0 +1 @@
+Hello World!
``` ```

View File

@ -1,153 +1,14 @@
use std::ffi::OsString; use std::{ffi::OsString, process};
use anyhow::anyhow; pub fn run(args: Vec<OsString>) -> anyhow::Result<()> {
use radicle::git;
use radicle::rad;
use radicle_surf as surf;
use crate::git::pretty_diff::ToPretty as _;
use crate::git::Rev;
use crate::terminal as term;
use crate::terminal::args::{Args, Error, Help};
use crate::terminal::highlight::Highlighter;
pub const HELP: Help = Help {
name: "diff",
description: "Show changes between commits",
version: env!("RADICLE_VERSION"),
usage: r#"
Usage
rad diff [<commit>] [--staged] [<option>...]
rad diff <commit> [<commit>] [<option>...]
This command is meant to operate as closely as possible to `git diff`,
except its output is optimized for human-readability.
Options
--unified, -U Context lines to show (default: 5)
--staged View staged changes
--color Force color output
--help Print help
"#,
};
pub struct Options {
pub commits: Vec<Rev>,
pub staged: bool,
pub unified: usize,
pub color: bool,
}
impl Args for Options {
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
use lexopt::prelude::*;
let mut parser = lexopt::Parser::from_args(args);
let mut commits = Vec::new();
let mut staged = false;
let mut unified = 5;
let mut color = false;
while let Some(arg) = parser.next()? {
match arg {
Long("unified") | Short('U') => {
let val = parser.value()?;
unified = term::args::number(&val)?;
}
Long("staged") | Long("cached") => staged = true,
Long("color") => color = true,
Long("help") | Short('h') => return Err(Error::Help.into()),
Value(val) => {
let rev = term::args::rev(&val)?;
commits.push(rev);
}
_ => anyhow::bail!(arg.unexpected()),
}
}
Ok((
Options {
commits,
staged,
unified,
color,
},
vec![],
))
}
}
pub fn run(options: Options, _ctx: impl term::Context) -> anyhow::Result<()> {
crate::warning::deprecated("rad diff", "git diff"); crate::warning::deprecated("rad diff", "git diff");
let repo = rad::repo()?; let mut child = process::Command::new("git")
let oids = options .arg("diff")
.commits .args(args)
.into_iter() .spawn()?;
.map(|rev| {
repo.revparse_single(rev.as_str())
.map_err(|e| anyhow!("unknown object {rev}: {e}"))
.and_then(|o| {
o.into_commit()
.map_err(|_| anyhow!("object {rev} is not a commit"))
})
})
.collect::<Result<Vec<_>, _>>()?;
let mut opts = git::raw::DiffOptions::new(); let exit_status = child.wait()?;
opts.patience(true)
.minimal(true)
.context_lines(options.unified as u32);
let mut find_opts = git::raw::DiffFindOptions::new(); process::exit(exit_status.code().unwrap_or(1));
find_opts.exact_match_only(true);
find_opts.all(true);
let mut diff = match oids.as_slice() {
[] => {
if options.staged {
let head = repo.head()?.peel_to_tree()?;
// HEAD vs. index.
repo.diff_tree_to_index(Some(&head), None, Some(&mut opts))
} else {
// Working tree vs. index.
repo.diff_index_to_workdir(None, None)
}
}
[commit] => {
let commit = commit.tree()?;
if options.staged {
// Commit vs. index.
repo.diff_tree_to_index(Some(&commit), None, Some(&mut opts))
} else {
// Commit vs. working tree.
repo.diff_tree_to_workdir(Some(&commit), Some(&mut opts))
}
}
[left, right] => {
// Commit vs. commit.
let left = left.tree()?;
let right = right.tree()?;
repo.diff_tree_to_tree(Some(&left), Some(&right), Some(&mut opts))
}
_ => {
anyhow::bail!("Too many commits given. See `rad diff --help` for usage.");
}
}?;
diff.find_similar(Some(&mut find_opts))?;
term::Paint::force(options.color);
let diff = surf::diff::Diff::try_from(diff)?;
let mut hi = Highlighter::default();
let pretty = diff.pretty(&mut hi, &(), &repo);
crate::pager::run(pretty)?;
Ok(())
} }

View File

@ -50,6 +50,18 @@ enum Commands {
Clean(clean::Args), Clean(clean::Args),
Clone(clone::Args), Clone(clone::Args),
Debug(debug::Args), Debug(debug::Args),
/// This command is deprecated and delegates to `git diff`.
/// Even before it was deprecated, it was not printed by
/// `rad -h`, so it is also hidden.
///
/// Since it is hidden, it makes no sense to add `about`
/// for the command listing, and since it is external,
/// `--help` will delegate to `git diff --help` it makes
/// no sense to add `long_about` for `rad diff --help`.
#[command(external_subcommand, hide = true)]
Diff(Vec<OsString>),
Fork(fork::Args), Fork(fork::Args),
Init(init::Args), Init(init::Args),
Issue(issue::Args), Issue(issue::Args),
@ -212,7 +224,11 @@ pub(crate) fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyho
term::run_command_args::<config::Options, _>(config::HELP, config::run, args.to_vec()); term::run_command_args::<config::Options, _>(config::HELP, config::run, args.to_vec());
} }
"diff" => { "diff" => {
term::run_command_args::<diff::Options, _>(diff::HELP, diff::run, args.to_vec()); if let Some(Commands::Diff(mut args)) = CliArgs::parse().command {
debug_assert_eq!(args[0], "diff");
args.remove(0);
return diff::run(args).map_err(Some);
}
} }
"debug" => { "debug" => {
if let Some(Commands::Debug(args)) = CliArgs::parse().command { if let Some(Commands::Debug(args)) = CliArgs::parse().command {