Skip to content

Commit 2fe3fcd

Browse files
authored
Git integration (lsd-rs#822)
1 parent 6840c01 commit 2fe3fcd

26 files changed

+1056
-38
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88
### Added
9+
- Add [Git integration](https://github.com/Peltoche/lsd/issues/7) from [hpwxf](https://github.com/hpwxf)
910
- In keeping with the coreutils change, add quotes and escapes for necessary filenames from [merelymyself](https://github.com/merelymyself)
1011
- Add support for icon theme from [zwpaper](https://github.com/zwpaper)
1112
- Add icon for kt and kts from [LeeWeeder](https://github.com/LeeWeeder)

Cargo.lock

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ clap_complete = "4.1"
2121
version_check = "0.9.*"
2222

2323
[dependencies]
24-
crossterm = { version = "0.24.0", features = ["serde"]}
24+
crossterm = { version = "0.24.0", features = ["serde"] }
2525
dirs = "3.0.*"
2626
libc = "0.2.*"
2727
human-sort = "0.2.2"
@@ -42,6 +42,10 @@ serde = { version = "1.0", features = ["derive"] }
4242
serde_yaml = "0.8"
4343
url = "2.1.*"
4444

45+
[target."cfg(not(all(windows, target_arch = \"x86\", target_env = \"gnu\")))".dependencies]
46+
# if ssl feature is enabled compilation will fail on arm-unknown-linux-gnueabihf and i686-pc-windows-gnu
47+
git2 = { version = "0.16", optional = true, default-features = false }
48+
4549
[target.'cfg(unix)'.dependencies]
4650
users = "0.11.*"
4751
xattr = "0.2.*"
@@ -61,7 +65,9 @@ tempfile = "3"
6165
serial_test = "0.5"
6266

6367
[features]
68+
default = ["git2"]
6469
sudo = []
70+
no-git = [] # force disabling git even if available by default
6571

6672
[profile.release]
6773
lto = true

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ classic: false
103103
# == Blocks ==
104104
# This specifies the columns and their order when using the long and the tree
105105
# layout.
106-
# Possible values: permission, user, group, context, size, date, name, inode, links
106+
# Possible values: permission, user, group, context, size, date, name, inode, links, git
107107
blocks:
108108
- permission
109109
- user

build.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,15 @@ fn main() {
3434
generate_to(Zsh, &mut app, bin_name, &outdir).expect("Failed to generate Zsh completions");
3535
generate_to(PowerShell, &mut app, bin_name, &outdir)
3636
.expect("Failed to generate PowerShell completions");
37+
38+
// Disable git feature for these target where git2 is not well supported
39+
if !std::env::var("CARGO_FEATURE_GIT2")
40+
.map(|flag| flag == "1")
41+
.unwrap_or(false)
42+
|| std::env::var("TARGET")
43+
.map(|target| target == "i686-pc-windows-gnu")
44+
.unwrap_or(false)
45+
{
46+
println!(r#"cargo:rustc-cfg=feature="no-git""#);
47+
}
3748
}

ci/before_deploy.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
set -ex
55

66
build() {
7-
cargo build --target "$TARGET" --release --verbose
7+
cargo build --target "$TARGET" --features="$FEATURES" --release --verbose
88
}
99

1010
pack() {

doc/lsd.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich
3838
`-X`, `--extensionsort`
3939
: Sort by file extension
4040

41+
`--git`
42+
: Display git status. Directory git status is a reduction of included file statuses (recursively).
43+
4144
`--help`
4245
: Prints help information
4346

@@ -90,7 +93,7 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich
9093
: Natural sort of (version) numbers within text
9194

9295
`--blocks <blocks>...`
93-
: Specify the blocks that will be displayed and in what order [possible values: permission, user, group, size, date, name, inode]
96+
: Specify the blocks that will be displayed and in what order [possible values: permission, user, group, size, date, name, inode, git]
9497

9598
`--color <color>...`
9699
: When to use terminal colours [default: auto] [possible values: always, auto, never]
@@ -126,7 +129,7 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich
126129
: How to display size [default: default] [possible values: default, short, bytes]
127130

128131
`--sort <WORD>...`
129-
: Sort by WORD instead of name [possible values: size, time, version, extension]
132+
: Sort by WORD instead of name [possible values: size, time, version, extension, git]
130133

131134
`-U`, `--no-sort`
132135
: Do not sort. List entries in directory order

src/app.rs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ pub struct Cli {
9696
#[arg(short = 'X', long)]
9797
pub extensionsort: bool,
9898

99+
/// Sort by git status
100+
#[arg(short = 'G', long)]
101+
pub gitsort: bool,
102+
99103
/// Natural sort of (version) numbers within text
100104
#[arg(short = 'v', long)]
101105
pub versionsort: bool,
@@ -104,13 +108,13 @@ pub struct Cli {
104108
#[arg(
105109
long,
106110
value_name = "TYPE",
107-
value_parser = ["size", "time", "version", "extension", "none"],
108-
overrides_with_all = ["timesort", "sizesort", "extensionsort", "versionsort", "no_sort"]
111+
value_parser = ["size", "time", "version", "extension", "git", "none"],
112+
overrides_with_all = ["timesort", "sizesort", "extensionsort", "versionsort", "gitsort", "no_sort"]
109113
)]
110114
pub sort: Option<String>,
111115

112116
/// Do not sort. List entries in directory order
113-
#[arg(short = 'U', long, overrides_with_all = ["timesort", "sizesort", "extensionsort", "versionsort", "sort"])]
117+
#[arg(short = 'U', long, overrides_with_all = ["timesort", "sizesort", "extensionsort", "versionsort", "gitsort", "sort"])]
114118
pub no_sort: bool,
115119

116120
/// Reverse the order of the sort
@@ -127,9 +131,9 @@ pub struct Cli {
127131

128132
/// Specify the blocks that will be displayed and in what order
129133
#[arg(
130-
long,
131-
value_delimiter = ',',
132-
value_parser = ["permission", "user", "group", "context", "size", "date", "name", "inode", "links"],
134+
long,
135+
value_delimiter = ',',
136+
value_parser = ["permission", "user", "group", "context", "size", "date", "name", "inode", "links", "git"],
133137
)]
134138
pub blocks: Vec<String>,
135139

@@ -150,6 +154,11 @@ pub struct Cli {
150154
#[arg(short, long)]
151155
pub inode: bool,
152156

157+
/// Show git status on file and directory"
158+
/// Only when used with --long option
159+
#[arg(short, long)]
160+
pub git: bool,
161+
153162
/// When showing file information for a symbolic link,
154163
/// show information for the file the link references rather than for the link itself
155164
#[arg(short = 'L', long)]
@@ -196,23 +205,23 @@ pub fn validate_time_format(formatter: &str) -> Result<String, String> {
196205
Some('f') => (),
197206
Some(n @ ('3' | '6' | '9')) => match chars.next() {
198207
Some('f') => (),
199-
Some(c) => return Err(format!("invalid format specifier: %.{}{}", n, c)),
208+
Some(c) => return Err(format!("invalid format specifier: %.{n}{c}")),
200209
None => return Err("missing format specifier".to_owned()),
201210
},
202-
Some(c) => return Err(format!("invalid format specifier: %.{}", c)),
211+
Some(c) => return Err(format!("invalid format specifier: %.{c}")),
203212
None => return Err("missing format specifier".to_owned()),
204213
},
205214
Some(n @ (':' | '#')) => match chars.next() {
206215
Some('z') => (),
207-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
216+
Some(c) => return Err(format!("invalid format specifier: %{n}{c}")),
208217
None => return Err("missing format specifier".to_owned()),
209218
},
210219
Some(n @ ('-' | '_' | '0')) => match chars.next() {
211220
Some(
212221
'C' | 'd' | 'e' | 'f' | 'G' | 'g' | 'H' | 'I' | 'j' | 'k' | 'l' | 'M' | 'm'
213222
| 'S' | 's' | 'U' | 'u' | 'V' | 'W' | 'w' | 'Y' | 'y',
214223
) => (),
215-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
224+
Some(c) => return Err(format!("invalid format specifier: %{n}{c}")),
216225
None => return Err("missing format specifier".to_owned()),
217226
},
218227
Some(
@@ -223,10 +232,10 @@ pub fn validate_time_format(formatter: &str) -> Result<String, String> {
223232
) => (),
224233
Some(n @ ('3' | '6' | '9')) => match chars.next() {
225234
Some('f') => (),
226-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
235+
Some(c) => return Err(format!("invalid format specifier: %{n}{c}")),
227236
None => return Err("missing format specifier".to_owned()),
228237
},
229-
Some(c) => return Err(format!("invalid format specifier: %{}", c)),
238+
Some(c) => return Err(format!("invalid format specifier: %{c}")),
230239
None => return Err("missing format specifier".to_owned()),
231240
},
232241
None => break,
@@ -235,3 +244,19 @@ pub fn validate_time_format(formatter: &str) -> Result<String, String> {
235244
}
236245
Ok(formatter.to_owned())
237246
}
247+
248+
// Wrapper for value_parser to simply remove non supported option (mainly git flag)
249+
// required since value_parser requires impl Into<ValueParser> that Vec do not support
250+
// should be located here, since this file is included by build.rs
251+
struct LabelFilter<Filter: Fn(&'static str) -> bool, const C: usize>([&'static str; C], Filter);
252+
253+
impl<Filter: Fn(&'static str) -> bool, const C: usize> From<LabelFilter<Filter, C>>
254+
for clap::builder::ValueParser
255+
{
256+
fn from(label_filter: LabelFilter<Filter, C>) -> Self {
257+
let filter = label_filter.1;
258+
let values = label_filter.0.into_iter().filter(|x| filter(x));
259+
let inner = clap::builder::PossibleValuesParser::from(values);
260+
Self::from(inner)
261+
}
262+
}

src/color.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use lscolors::{Indicator, LsColors};
44
use std::path::Path;
55

66
pub use crate::flags::color::ThemeOption;
7+
use crate::git::GitStatus;
78
use crate::theme::{color::ColorTheme, Theme};
89

910
#[allow(dead_code)]
@@ -61,6 +62,10 @@ pub enum Elem {
6162
},
6263

6364
TreeEdge,
65+
66+
GitStatus {
67+
status: GitStatus,
68+
},
6469
}
6570

6671
impl Elem {
@@ -121,6 +126,7 @@ impl Elem {
121126
Elem::TreeEdge => theme.tree_edge,
122127
Elem::Links { valid: false } => theme.links.invalid,
123128
Elem::Links { valid: true } => theme.links.valid,
129+
Elem::GitStatus { .. } => theme.git_status.default,
124130
}
125131
}
126132
}
@@ -389,6 +395,7 @@ mod elem {
389395
invalid: Color::AnsiValue(245), // Grey
390396
},
391397
tree_edge: Color::AnsiValue(245), // Grey
398+
git_status: Default::default(),
392399
}
393400
}
394401

src/config_file.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ classic: false
203203
# == Blocks ==
204204
# This specifies the columns and their order when using the long and the tree
205205
# layout.
206-
# Possible values: permission, user, group, context, size, date, name, inode
206+
# Possible values: permission, user, group, context, size, date, name, inode, git
207207
blocks:
208208
- permission
209209
- user
@@ -388,7 +388,7 @@ mod tests {
388388
total_size: Some(false),
389389
symlink_arrow: Some("⇒".into()),
390390
hyperlink: Some(HyperlinkOption::Never),
391-
header: None
391+
header: None,
392392
},
393393
c
394394
);

0 commit comments

Comments
 (0)