feat: adding ls-index
All checks were successful
CI checks / Clippy (push) Successful in 29s
CI checks / Format (push) Successful in 24s

This commit is contained in:
Patrick MARIE 2025-02-06 08:18:57 +01:00
parent f047bb5181
commit bed1464bb9
Signed by: mycroft
GPG Key ID: BB519E5CD8E7BFA7
4 changed files with 174 additions and 0 deletions

16
Cargo.lock generated
View File

@ -207,6 +207,12 @@ version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]] [[package]]
name = "mg" name = "mg"
version = "0.1.0" version = "0.1.0"
@ -215,6 +221,7 @@ dependencies = [
"clap", "clap",
"flate2", "flate2",
"hex", "hex",
"nom",
"sha1", "sha1",
"thiserror", "thiserror",
] ]
@ -228,6 +235,15 @@ dependencies = [
"adler2", "adler2",
] ]
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.20.2" version = "1.20.2"

View File

@ -8,5 +8,6 @@ anyhow = "1.0.95"
clap = { version = "4.5.27", features = ["derive", "string"] } clap = { version = "4.5.27", features = ["derive", "string"] }
flate2 = "1.0.35" flate2 = "1.0.35"
hex = "0.4.3" hex = "0.4.3"
nom = "8.0.0"
sha1 = "0.10.6" sha1 = "0.10.6"
thiserror = "2.0.11" thiserror = "2.0.11"

150
src/index.rs Normal file
View File

@ -0,0 +1,150 @@
use std::path::Path;
use nom::{
bytes::complete::take,
number::complete::{be_u16, be_u32},
IResult, Parser,
};
use anyhow::{anyhow, Error, Result};
use crate::repository::Repository;
#[derive(Debug)]
#[allow(dead_code)]
struct IndexHeader {
signature: [u8; 4], // "DIRC"
version: u32, // 2, 3, or 4
entries_count: u32,
}
#[derive(Debug)]
#[allow(dead_code)]
struct IndexEntry {
ctime_s: u32,
ctime_n: u32,
mtime_s: u32,
mtime_n: u32,
dev: u32,
ino: u32,
mode: u32,
uid: u32,
gid: u32,
size: u32,
sha1: [u8; 20],
flags: u16,
file_path: String,
}
#[derive(Debug)]
#[allow(dead_code)]
struct Index {
header: IndexHeader,
entries: Vec<IndexEntry>,
}
fn parse_index(input: &[u8]) -> IResult<&[u8], Index> {
let (mut input, header) = parse_header(input)?;
let mut entries = Vec::with_capacity(header.entries_count as usize);
for _ in 0..header.entries_count {
let (remaining, entry) = parse_entry(input)?;
entries.push(entry);
input = remaining;
}
Ok((input, Index { header, entries }))
}
fn parse_header(input: &[u8]) -> IResult<&[u8], IndexHeader> {
let (input, (signature, version, entries_count)) =
(take(4usize), be_u32, be_u32).parse(input)?;
let mut sig = [0u8; 4];
sig.copy_from_slice(signature);
Ok((
input,
IndexHeader {
signature: sig,
version,
entries_count,
},
))
}
fn parse_entry(input: &[u8]) -> IResult<&[u8], IndexEntry> {
let start_input_len = input.len();
let (
input,
(ctime_s, ctime_n, mtime_s, mtime_n, dev, ino, mode, uid, gid, size, sha1_bytes, flags),
) = (
be_u32,
be_u32,
be_u32,
be_u32,
be_u32,
be_u32,
be_u32,
be_u32,
be_u32,
be_u32,
take(20usize),
be_u16,
)
.parse(input)?;
let current_input_len = input.len();
let path_len = flags & 0xFFF;
let (input, path_bytes) = take(path_len as usize)(input)?;
let file_path = String::from_utf8_lossy(path_bytes).into_owned();
// between 1 and 8 NUL bytes to pad the entry.
let padding_len = 8 - ((start_input_len - current_input_len) + path_len as usize) % 8;
let (input, _) = take(padding_len)(input)?;
let mut sha1 = [0u8; 20];
sha1.copy_from_slice(sha1_bytes);
Ok((
input,
IndexEntry {
ctime_s,
ctime_n,
mtime_s,
mtime_n,
dev,
ino,
mode,
uid,
gid,
size,
sha1,
flags,
file_path,
},
))
}
impl Index {
pub fn read_from_file(path: &Path) -> Result<Self, Error> {
let content = std::fs::read(path)?;
let (_remaining, index) =
parse_index(&content).map_err(|e| anyhow!("Failed to parse index: {}", e))?;
Ok(index)
}
}
impl Repository {
pub fn read_index(&self) -> Result<()> {
let index_path = self.path.join(".git").join("index");
let index = Index::read_from_file(&index_path)?;
for entry in index.entries {
println!("{} {}", hex::encode(entry.sha1), entry.file_path);
}
Ok(())
}
}

View File

@ -7,6 +7,7 @@ use clap::Subcommand;
mod commit; mod commit;
mod error; mod error;
mod index;
mod kind; mod kind;
mod log; mod log;
mod object; mod object;
@ -59,6 +60,8 @@ enum Command {
}, },
/// Show the commit log /// Show the commit log
Log, Log,
/// List the index entries
LsIndex,
} }
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
@ -99,6 +102,10 @@ fn main() -> Result<(), Error> {
Ok(_) => (), Ok(_) => (),
Err(e) => eprintln!("Failed to show log: {}", e), Err(e) => eprintln!("Failed to show log: {}", e),
}, },
Command::LsIndex => match repo.read_index() {
Ok(_) => (),
Err(e) => eprintln!("Failed to list index: {}", e),
},
} }
Ok(()) Ok(())