Skip to content
Open
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
191 changes: 191 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ ignore = "0.4"

# Syntax highlighting
syntect = "5.2"

# Symbol parsing (Go to Definition)
tree-sitter = "0.23"
tree-sitter-typescript = "0.23"
tree-sitter-javascript = "0.23"
tree-sitter-rust = "0.23"
tree-sitter-python = "0.23"
tree-sitter-go = "0.23"
tree-sitter-java = "0.23"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
tree-sitter-c-sharp = "0.23"
tree-sitter-ruby = "0.23"
tree-sitter-json = "0.23"
tree-sitter-php = "0.23"
tree-sitter-bash = "0.23"
tree-sitter-html = "0.23"
tree-sitter-css = "0.23"
streaming-iterator = "0.1"
tauri-plugin-dialog = "2.4.2"

# Review storage
Expand Down
64 changes: 64 additions & 0 deletions src-tauri/src/git/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,70 @@ pub fn get_file_at_ref(repo: &Path, ref_name: &str, path: &str) -> Result<File,
}
}

/// List files in a directory at a specific ref.
///
/// Returns file paths relative to the directory.
/// For WORKDIR, reads from the filesystem.
/// For other refs, uses git ls-tree.
pub fn list_files_in_dir(
repo: &Path,
ref_name: &str,
dir_path: &str,
) -> Result<Vec<String>, GitError> {
if ref_name == WORKDIR {
// Read from working directory
let full_path = repo.join(dir_path);

if !full_path.exists() || !full_path.is_dir() {
return Ok(vec![]);
}

let mut files = Vec::new();
if let Ok(entries) = std::fs::read_dir(&full_path) {
for entry in entries.flatten() {
if let Ok(file_type) = entry.file_type() {
if file_type.is_file() {
if let Some(name) = entry.file_name().to_str() {
// Return path relative to repo root
let rel_path = if dir_path.is_empty() {
name.to_string()
} else {
format!("{}/{}", dir_path, name)
};
files.push(rel_path);
}
}
}
}
}
Ok(files)
} else {
// Use git ls-tree to list files at ref
let tree_spec = if dir_path.is_empty() {
ref_name.to_string()
} else {
format!("{}:{}", ref_name, dir_path)
};

// git ls-tree --name-only <ref>:<dir>
let output = cli::run(repo, &["ls-tree", "--name-only", &tree_spec]).unwrap_or_default();

let files: Vec<String> = output
.lines()
.filter(|line| !line.is_empty())
.map(|name| {
if dir_path.is_empty() {
name.to_string()
} else {
format!("{}/{}", dir_path, name)
}
})
.collect();

Ok(files)
}
}

/// Check if data appears to be binary (contains null bytes in first 8KB)
fn is_binary(data: &[u8]) -> bool {
let check_len = data.len().min(8192);
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/git/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ mod types;
pub use cli::GitError;
pub use commit::commit;
pub use diff::{get_file_diff, list_diff_files};
pub use files::{get_file_at_ref, search_files};
pub use files::{get_file_at_ref, list_files_in_dir, search_files};
pub use github::{
check_github_auth, fetch_pr, invalidate_cache as invalidate_pr_cache, list_pull_requests,
sync_review_to_github, GitHubAuthStatus, GitHubSyncResult, PullRequest,
};
pub use refs::{get_repo_root, list_refs, merge_base, resolve_ref};
pub use refs::{get_current_branch, get_repo_root, list_refs, merge_base, resolve_ref};
pub use types::*;
12 changes: 12 additions & 0 deletions src-tauri/src/git/refs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,15 @@ pub fn resolve_ref(repo: &Path, reference: &str) -> Result<String, GitError> {
let output = cli::run(repo, &["rev-parse", reference])?;
Ok(output.trim().to_string())
}

/// Get the current branch name (or None if in detached HEAD state)
pub fn get_current_branch(repo: &Path) -> Result<Option<String>, GitError> {
match cli::run(repo, &["symbolic-ref", "--short", "HEAD"]) {
Ok(output) => Ok(Some(output.trim().to_string())),
Err(GitError::CommandFailed(msg)) if msg.contains("not a symbolic ref") => {
// Detached HEAD state
Ok(None)
}
Err(e) => Err(e),
}
}
Loading