Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/commands/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,20 @@ fn collect_files_recursively(dir: &PathBuf) -> Result<Vec<PathBuf>> {
/// Main function for the `guts add` command
/// Adds files to the staging area (index)
pub fn run(args: &AddArgs) -> Result<String> {
// Determine current directory to use
let current_dir = args
.dir
.clone()
.unwrap_or_else(|| std::env::current_dir().expect("failed to get current directory"));

// Check if we're in a git repository
if !simple_index::is_git_repository()? {
if !simple_index::is_git_repository_from(Some(&current_dir))? {
return Err(anyhow!("fatal: not a git repository"));
}

let mut added_files = Vec::new();
let mut output = String::new();

// Determine current directory to use
let current_dir = args
.dir
.clone()
.unwrap_or_else(|| std::env::current_dir().expect("failed to get current directory"));

// Load .gutsignore matcher
let matcher = IgnoreMatcher::from_gutsignore(&current_dir)
.unwrap_or_else(|_| IgnoreMatcher::empty());
Expand All @@ -76,7 +76,7 @@ pub fn run(args: &AddArgs) -> Result<String> {
if matcher.is_ignored(&file, &current_dir) {
continue;
}
simple_index::add_file_to_index(&file)?;
simple_index::add_file_to_index_from(&file, Some(&current_dir))?;
added_files.push(file.display().to_string());
}
continue;
Expand All @@ -97,7 +97,7 @@ pub fn run(args: &AddArgs) -> Result<String> {
if matcher.is_ignored(&file, &current_dir) {
continue;
}
simple_index::add_file_to_index(&file)?;
simple_index::add_file_to_index_from(&file, Some(&current_dir))?;
added_files.push(file.display().to_string());
}
} else {
Expand All @@ -106,7 +106,7 @@ pub fn run(args: &AddArgs) -> Result<String> {
continue;
}
// Add the file to the JSON index
simple_index::add_file_to_index(file_path)?;
simple_index::add_file_to_index_from(file_path, Some(&current_dir))?;
added_files.push(file_path.display().to_string());
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn run(args: &CommitArgs) -> Result<String> {

fn run_commit(args: &CommitArgs) -> Result<String> {
// Check if we're in a git repository
if !simple_index::is_git_repository()? {
if !simple_index::is_git_repository_from(args.dir.as_ref())? {
return Err(anyhow::anyhow!("fatal: not a git repository"));
}

Expand Down
2 changes: 1 addition & 1 deletion src/commands/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn run(args: &LogArgs) -> Result<String> {
.unwrap_or_else(|| std::env::current_dir().expect("failed to get current directory"));

// Check if we're in a git repository
if !simple_index::is_git_repository()? {
if !simple_index::is_git_repository_from(args.dir.as_ref())? {
return Err(anyhow!("fatal: not a git repository"));
}

Expand Down
2 changes: 1 addition & 1 deletion src/commands/rm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fn remove_file_from_index(file_path: &PathBuf) -> Result<bool> {
/// Removes files from working directory and index
pub fn run(args: &RmArgs) -> Result<String> {
// Check if we're in a git repository
if !simple_index::is_git_repository()? {
if !simple_index::is_git_repository_from(args.dir.as_ref())? {
return Err(anyhow!("fatal: not a git repository"));
}

Expand Down
12 changes: 7 additions & 5 deletions src/commands/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ pub fn run(args: &StatusObject) -> Result<String> {
.clone()
.unwrap_or_else(|| std::env::current_dir().expect("failed to get current directory"));

if !simple_index::is_git_repository()? {
if !simple_index::is_git_repository_from(Some(&current_dir))? {
return Ok("fatal: not a git repository".to_string());
}

let matcher = IgnoreMatcher::from_gutsignore(&current_dir)
.unwrap_or_else(|_| IgnoreMatcher::empty());

let committed_files = simple_index::get_committed_files()?;
let index = simple_index::SimpleIndex::load()?;
let committed_files = simple_index::get_committed_files_from(Some(&current_dir))?;
let index = simple_index::SimpleIndex::load_from(Some(&current_dir))?;
let work_files = list_working_dir_files(&current_dir, &matcher)?;

let current_branch = read_head::get_current_branch()
Expand Down Expand Up @@ -154,9 +154,11 @@ fn list_working_dir_files(current_dir: &PathBuf, matcher: &IgnoreMatcher) -> Res
}

fn get_relative_path(file_path: &PathBuf, current_dir: &PathBuf) -> Result<String> {
// Find repo root from current directory context
let repo_root = simple_index::find_repo_root_from(Some(current_dir))?;
let relative = file_path
.strip_prefix(current_dir)
.map_err(|_| anyhow::anyhow!("file is not in the current directory"))?;
.strip_prefix(&repo_root)
.map_err(|_| anyhow::anyhow!("file is not in the repository"))?;
Ok(relative.to_string_lossy().to_string())
}

Expand Down
4 changes: 2 additions & 2 deletions src/commands/write_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ pub struct WriteTreeArgs {

/// New version of write-tree that uses the simple JSON index
/// Instead of reading the filesystem, reads the index to create the tree
pub fn run(_args: &WriteTreeArgs) -> Result<String> {
pub fn run(args: &WriteTreeArgs) -> Result<String> {
// Check if we're in a git repository
if !simple_index::is_git_repository()? {
if !simple_index::is_git_repository_from(args.dir.as_ref())? {
return Err(anyhow::anyhow!("fatal: not a git repository"));
}

Expand Down
121 changes: 121 additions & 0 deletions src/core/simple_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ impl SimpleIndex {
Ok(index)
}

/// Load index from .git/simple_index.json from a specific directory
pub fn load_from(start_dir: Option<&PathBuf>) -> Result<Self> {
let index_path = get_simple_index_path_from(start_dir)?;

if !index_path.exists() {
return Ok(SimpleIndex::default());
}

let content = fs::read_to_string(&index_path)
.with_context(|| format!("unable to read {:?}", index_path))?;

let index: SimpleIndex =
serde_json::from_str(&content).with_context(|| "invalid JSON in index")?;

Ok(index)
}

/// Save index to .git/simple_index.json
pub fn save(&self) -> Result<()> {
let index_path = get_simple_index_path()?;
Expand Down Expand Up @@ -108,6 +125,12 @@ fn get_simple_index_path() -> Result<PathBuf> {
Ok(repo_root.join(".git").join("simple_index.json"))
}

/// Return path to .git/simple_index.json from a specific directory
fn get_simple_index_path_from(start_dir: Option<&PathBuf>) -> Result<PathBuf> {
let repo_root = find_repo_root_from(start_dir)?;
Ok(repo_root.join(".git").join("simple_index.json"))
}

/// Convert absolute path to relative path from repo root
fn get_relative_path(file_path: &Path) -> Result<String> {
let repo_root = find_repo_root()?;
Expand Down Expand Up @@ -245,3 +268,101 @@ fn decompress_object(data: &[u8]) -> Result<Vec<u8>> {
}
}
}

/// Find Git repository root from a specific directory
pub fn find_repo_root_from(start_dir: Option<&PathBuf>) -> Result<PathBuf> {
let mut current = match start_dir {
Some(dir) => dir.clone(),
None => std::env::current_dir().with_context(|| "unable to get current directory")?,
};

loop {
let git_dir = current.join(".git");
if git_dir.exists() && git_dir.is_dir() {
return Ok(current);
}

match current.parent() {
Some(parent) => current = parent.to_path_buf(),
None => return Err(anyhow!("not a git repository")),
}
}
}

/// Check if we're in a Git repository from a specific directory
pub fn is_git_repository_from(start_dir: Option<&PathBuf>) -> Result<bool> {
match find_repo_root_from(start_dir) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}

/// Add a file to the index from a specific directory context
pub fn add_file_to_index_from(file_path: &Path, start_dir: Option<&PathBuf>) -> Result<()> {
// Set current directory context if provided
let original_dir = std::env::current_dir()?;

if let Some(dir) = start_dir {
std::env::set_current_dir(dir)?;
}

// Use existing add_file_to_index function
let result = add_file_to_index(file_path);

// Restore original directory
std::env::set_current_dir(&original_dir)?;

result
}

/// Get the files committed in the current HEAD from a specific directory
/// Returns a HashMap: relative file path -> SHA-1 hash
pub fn get_committed_files_from(start_dir: Option<&PathBuf>) -> Result<HashMap<String, String>> {
let repo_root = find_repo_root_from(start_dir)?;
let git_dir = repo_root.join(".git");

// Read HEAD to get current commit
let head_path = git_dir.join("HEAD");
if !head_path.exists() {
// No commits yet
return Ok(HashMap::new());
}

let head_content = fs::read_to_string(&head_path)?;
let head_content = head_content.trim();

// Get the commit hash
let commit_hash = if head_content.starts_with("ref: ") {
// HEAD points to a branch
let ref_path = head_content.strip_prefix("ref: ").unwrap();
let ref_file = git_dir.join(ref_path);

if !ref_file.exists() {
// Branch exists but no commits yet
return Ok(HashMap::new());
}

fs::read_to_string(ref_file)?.trim().to_string()
} else {
// Detached HEAD, direct commit hash
head_content.to_string()
};

// Read the commit object to get the tree hash
let commit_obj_path = cat::get_object_path(&git_dir, &commit_hash);
if !commit_obj_path.exists() {
return Ok(HashMap::new());
}

let commit_data = fs::read(&commit_obj_path)?;
let decompressed = decompress_object(&commit_data)?;
let parsed = cat::parse_object(&decompressed)?;

let tree_hash = match parsed {
cat::ParsedObject::Commit(commit) => commit.tree,
_ => return Err(anyhow!("HEAD does not point to a commit object")),
};

// Read the tree object to get the files
get_files_from_tree(&git_dir, &tree_hash, "")
}
4 changes: 2 additions & 2 deletions src/terminal/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ impl App {
}

// Sinon, commande système via shell
let _cleaned_dir = if self.current_dir.starts_with(r"\\?\") {
let cleaned_dir = if self.current_dir.starts_with(r"\\?\") {
self.current_dir.trim_start_matches(r"\\?\\").to_string()
} else {
self.current_dir.clone()
Expand All @@ -346,7 +346,7 @@ impl App {
let shell_result = std::process::Command::new("sh")
.arg("-c")
.arg(&command)
.current_dir(&self.current_dir)
.current_dir(&cleaned_dir)
.output();

let result = match shell_result {
Expand Down