feat: remove file from index

This commit is contained in:
Antonin Ruan
2026-03-12 19:18:33 +01:00
parent 3027a99b5f
commit c118829c11
5 changed files with 97 additions and 23 deletions
-1
View File
@@ -184,7 +184,6 @@ fn parse_rule(raw: String, rel_to: &Path) -> Option<GitIgnoreRule> {
} }
fn load_rules(rel_to: &Path) -> Result<Vec<GitIgnoreRule>> { fn load_rules(rel_to: &Path) -> Result<Vec<GitIgnoreRule>> {
println!("Loading rule for {}", rel_to.display());
let path = rel_to.join(".gitignore"); let path = rel_to.join(".gitignore");
let n_rel_to = normalize_path_in_worktree(rel_to)?; let n_rel_to = normalize_path_in_worktree(rel_to)?;
+54 -14
View File
@@ -58,7 +58,7 @@ impl IndexEntry {
let cname = CStr::from_bytes_until_nul(&bytes[62..])?; let cname = CStr::from_bytes_until_nul(&bytes[62..])?;
let name = String::from(cname.to_str()?); let name = String::from(cname.to_str()?);
let entry_size = usize::div_ceil(62 + cname.count_bytes(), 8) * 8; let entry_size = usize::div_ceil(62 + cname.count_bytes() + 1, 8) * 8;
Ok(( Ok((
IndexEntry { IndexEntry {
@@ -159,16 +159,6 @@ impl Index {
Ok(()) Ok(())
} }
fn insert_entry(&mut self, entry: IndexEntry) {
match self
.entries
.binary_search_by_key(&entry.name, |e| e.name.clone())
{
Ok(pos) => self.entries[pos] = entry,
Err(pos) => self.entries.insert(pos, entry),
}
}
pub fn add_file(&mut self, name: String, hash: [u8; 20]) -> Result<()> { pub fn add_file(&mut self, name: String, hash: [u8; 20]) -> Result<()> {
let metadata = fs::metadata(&name)?; let metadata = fs::metadata(&name)?;
if metadata.is_dir() { if metadata.is_dir() {
@@ -212,6 +202,16 @@ impl Index {
Ok(()) Ok(())
} }
fn insert_entry(&mut self, entry: IndexEntry) {
match self
.entries
.binary_search_by_key(&entry.name, |e| e.name.clone())
{
Ok(pos) => self.entries[pos] = entry,
Err(pos) => self.entries.insert(pos, entry),
}
}
fn from_bytes(bytes: Vec<u8>) -> Result<Self> { fn from_bytes(bytes: Vec<u8>) -> Result<Self> {
match &bytes[0..4] { match &bytes[0..4] {
b"DIRC" => (), b"DIRC" => (),
@@ -328,7 +328,7 @@ mod tests {
hash: [0x19u8; 20], hash: [0x19u8; 20],
name: String::from("src/git_fs/head.rs"), name: String::from("src/git_fs/head.rs"),
}; };
let (entry1, _) = IndexEntry::from_bytes(&vec![ let (entry1, len1) = IndexEntry::from_bytes(&vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xa4,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19,
@@ -345,7 +345,7 @@ mod tests {
hash: [0x19u8; 20], hash: [0x19u8; 20],
name: String::from("src/git_fs/head.r"), name: String::from("src/git_fs/head.r"),
}; };
let (entry2, _) = IndexEntry::from_bytes(&vec![ let (entry2, len2) = IndexEntry::from_bytes(&vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19,
@@ -361,7 +361,7 @@ mod tests {
hash: [0x19u8; 20], hash: [0x19u8; 20],
name: String::from("src/git_fs/head.rst"), name: String::from("src/git_fs/head.rst"),
}; };
let (entry3, _) = IndexEntry::from_bytes(&vec![ let (entry3, len3) = IndexEntry::from_bytes(&vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19,
@@ -375,6 +375,10 @@ mod tests {
assert_eq!(entry1, expected1); assert_eq!(entry1, expected1);
assert_eq!(entry2, expected2); assert_eq!(entry2, expected2);
assert_eq!(entry3, expected3); assert_eq!(entry3, expected3);
assert_eq!(len1, 88);
assert_eq!(len2, 80);
assert_eq!(len3, 88);
} }
#[test] #[test]
@@ -418,4 +422,40 @@ mod tests {
assert_eq!(index, expected); assert_eq!(index, expected);
} }
#[test]
fn index_remove_entry() {
let entry1 = IndexEntry {
object_type: ObjectType::Regular,
permissions: 0o644u16,
hash: [0x19u8; 20],
name: String::from("a"),
};
let entry2 = IndexEntry {
object_type: ObjectType::Regular,
permissions: 0o644u16,
hash: [0x19u8; 20],
name: String::from("b"),
};
let entry3 = IndexEntry {
object_type: ObjectType::SymLink,
permissions: 0,
hash: [0x19u8; 20],
name: String::from(".hello"),
};
let mut index = Index {
entries: vec![entry3.clone(), entry1, entry2.clone()],
..Default::default()
};
index.remove_file(String::from("a")).unwrap();
let expected = Index {
entries: vec![entry3, entry2],
..Default::default()
};
assert_eq!(index, expected);
}
} }
-2
View File
@@ -25,10 +25,8 @@ impl Subcommand for AddSubcommand {
for path in self.paths.iter().map(|p| Path::new(p).to_path_buf()) { for path in self.paths.iter().map(|p| Path::new(p).to_path_buf()) {
let expanded = expands_and_filter_path(path)?; let expanded = expands_and_filter_path(path)?;
for p in expanded { for p in expanded {
println!("Adding {}", p.display());
let path_str = String::from(p.to_str().unwrap()); let path_str = String::from(p.to_str().unwrap());
let name = String::from(normalize_path_in_worktree(&p)?.to_str().unwrap()); let name = String::from(normalize_path_in_worktree(&p)?.to_str().unwrap());
println!("adding {}", name);
let hash = HashObjectSubcommand { let hash = HashObjectSubcommand {
write: true, write: true,
path: path_str, path: path_str,
+6 -6
View File
@@ -1,21 +1,21 @@
use crate::subcommands::{ use crate::subcommands::{
add::AddSubcommand, hash_object::HashObjectSubcommand, init::InitSubcommand, add::AddSubcommand, hash_object::HashObjectSubcommand, init::InitSubcommand, rm::RmSubcommand,
remove::RemoveSubcommand, test::TestSubcommand, test::TestSubcommand,
}; };
use anyhow::Result; use anyhow::Result;
mod add; mod add;
mod hash_object; mod hash_object;
mod init; mod init;
mod remove; mod rm;
mod test; mod test;
#[derive(clap::Parser, Debug)] #[derive(clap::Parser, Debug)]
pub enum SubcommandType { pub enum SubcommandType {
/// Add file(s) to index /// Add file(s) to index
Add(AddSubcommand), Add(AddSubcommand),
/// Remove file from the working and the index /// Remove file(s) from the index
Remove(RemoveSubcommand), Rm(RmSubcommand),
/// Init a Git repository /// Init a Git repository
Init(InitSubcommand), Init(InitSubcommand),
HashObject(HashObjectSubcommand), HashObject(HashObjectSubcommand),
@@ -30,7 +30,7 @@ impl Subcommand for SubcommandType {
fn run(&self) -> Result<String> { fn run(&self) -> Result<String> {
match self { match self {
Self::Add(cmd) => cmd.run(), Self::Add(cmd) => cmd.run(),
Self::Remove(cmd) => cmd.run(), Self::Rm(cmd) => cmd.run(),
Self::Init(cmd) => cmd.run(), Self::Init(cmd) => cmd.run(),
Self::HashObject(cmd) => cmd.run(), Self::HashObject(cmd) => cmd.run(),
Self::Test(cmd) => cmd.run(), Self::Test(cmd) => cmd.run(),
+37
View File
@@ -0,0 +1,37 @@
use anyhow::Result;
use clap::Parser;
use std::path::Path;
use crate::git_fs::{gitignore::expands_and_filter_path, index::Index};
use super::Subcommand;
#[derive(Parser, Debug)]
pub struct RmSubcommand {
#[arg(short, long, default_value_t = false)]
/// Only remove object in index
pub cached: bool,
pub paths: Vec<String>,
}
impl Subcommand for RmSubcommand {
fn run(&self) -> Result<String> {
if self.paths.is_empty() {
return Ok(String::from("Nothing specified, nothing removed"));
}
let mut index = Index::load()?;
for path in self.paths.iter().map(|p| Path::new(p).to_path_buf()) {
let expanded = expands_and_filter_path(path)?;
for p in expanded {
index.remove_file(String::from(p.to_str().unwrap()))?;
}
}
index.save()?;
Ok(String::from(""))
}
}