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
6 changes: 4 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use clap::{Parser, Subcommand};

use crate::commands::{
add, cat_file, commit, commit_tree, hash_object, init, log, rev_parse, rm, show_ref, status, checkout,
write_tree,ls_tree,ls_files
add, cat_file, checkout, commit, commit_tree, hash_object, init, log, ls_files, ls_tree, merge, rev_parse, rm, show_ref, status, write_tree
};

#[derive(Parser)]
Expand Down Expand Up @@ -64,6 +63,9 @@ pub enum Commands {
/// List all files in the index
LsFiles(ls_files::LsFilesArgs),

// Merge 2 branch together
Merge(merge::MergeArgs),

/// Launch graphical terminal UI
Tui,
}
6 changes: 4 additions & 2 deletions src/commands/cat_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ pub fn run(args: &CatFileArgs) -> Result<String> {
ParsedObject::Commit(data) => {
let mut out = String::new();
out += &format!("tree {}\n", data.tree);
if let Some(parent) = &data.parent {
out += &format!("parent {}\n", parent);
if let Some(parents) = &data.parent {
for p in parents {
out += &format!("parent {}\n", p);
}
}
out += &format!("author {} {} +0000\n", data.author, data.author_date);
out += &format!("committer {} {} +0000\n", data.committer, data.committer_date);
Expand Down
31 changes: 17 additions & 14 deletions src/commands/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,20 @@ pub struct CheckoutObject {
pub name: Option<String>,

#[arg(short = 'b', long)]
pub branch_name: Option<String>
pub branch_name: Option<String>,

#[arg(last = true)]
pub dir: Option<PathBuf>,
}

pub fn run(args: &CheckoutObject) -> Result<String> {

let original_dir = std::env::current_dir()?;

if let Some(dir) = &args.dir {
std::env::set_current_dir(dir)?;
}

let current_dir = std::env::current_dir().context("Cannot get the current directory")?;
let git_dir = current_dir.join(".git");

Expand All @@ -28,7 +38,6 @@ pub fn run(args: &CheckoutObject) -> Result<String> {
};

let sha = resolve_ref(&git_dir, &target_ref)?;
println!("Resolved SHA: {}", sha);

let commit_content = read_and_parse_git_object(&git_dir, &sha)?;

Expand Down Expand Up @@ -62,16 +71,17 @@ pub fn run(args: &CheckoutObject) -> Result<String> {

clean_working_directory(&current_dir, &git_dir, &tree_sha)?;

println!("Tree SHA: {}", tree_sha);

let tree_content = read_and_parse_git_object(&git_dir, &tree_sha)?;
parse_tree_object(&git_dir, &tree_content, current_dir)?;

std::env::set_current_dir(&original_dir)?;

Ok(tree_sha)
}
}

fn extract_tree_sha(commit_text: &str) -> Result<String> {
pub fn extract_tree_sha(commit_text: &str) -> Result<String> {
for line in commit_text.lines() {
if let Some(rest) = line.strip_prefix("tree ") {
return Ok(rest.trim().to_string());
Expand All @@ -98,7 +108,7 @@ fn read_git_object(path: &Path) -> Result<Vec<u8>> {
Ok(decompressed)
}

fn parse_tree_object(git_dir: &PathBuf, tree_bytes: &[u8], target_dir: PathBuf) -> Result<()> {
pub fn parse_tree_object(git_dir: &PathBuf, tree_bytes: &[u8], target_dir: PathBuf) -> Result<()> {
for entry in parse_tree(&tree_bytes)? {
let full_path = target_dir.join(&entry.filename);

Expand Down Expand Up @@ -133,7 +143,7 @@ fn read_head_ref(git_dir: &Path) -> Result<Option<String>> {
}
}

fn clean_working_directory(current_dir: &Path, git_dir: &Path, tree_sha: &str) -> Result<()> {
pub fn clean_working_directory(current_dir: &Path, git_dir: &Path, tree_sha: &str) -> Result<()> {
let mut tracked_paths = HashSet::new();
collect_tracked_paths(git_dir, tree_sha, PathBuf::new(), &mut tracked_paths)?;

Expand Down Expand Up @@ -186,13 +196,10 @@ fn collect_tracked_paths(
}

fn has_uncommitted_changes(git_dir: &Path, current_dir: &Path, tree_sha: &str) -> Result<bool> {
println!("DEBUG: Checking for uncommitted changes against tree: {}", tree_sha);

let current_head_tree = read_head_tree_sha(git_dir)?;
println!("DEBUG: Current HEAD tree: {}", current_head_tree);

let tracked_files = list_files_in_tree(git_dir, &current_head_tree)?;
println!("DEBUG: Found {} tracked files in current HEAD", tracked_files.len());

let mut changed = false;
check_tree_for_changes(git_dir, current_dir, current_dir, &tracked_files, &mut changed)?;
Expand Down Expand Up @@ -221,7 +228,6 @@ fn check_tree_for_changes(
check_tree_for_changes(git_dir, current_dir, &path, tracked_files, changed)?;
} else {
let is_tracked = tracked_files.contains(&relative_path);
println!("DEBUG: Checking file {:?}, tracked: {}", relative_path, is_tracked);

if is_tracked {
if let Some(blob_sha) = find_blob_sha_for_path(git_dir, &relative_path)? {
Expand All @@ -231,14 +237,12 @@ fn check_tree_for_changes(
let current_content = fs::read(&path)?;

if current_content != content {
println!("DEBUG: File modified: {:?}", path);
*changed = true;
}
} else {
println!("DEBUG: Could not find blob SHA for tracked file: {:?}", relative_path);
}
} else {
println!("DEBUG: Untracked file: {:?}", relative_path);
*changed = true;
}
}
Expand All @@ -247,7 +251,6 @@ fn check_tree_for_changes(
for tracked_file in tracked_files {
let full_path = current_dir.join(tracked_file);
if !full_path.exists() {
println!("DEBUG: File deleted: {:?}", full_path);
*changed = true;
}
}
Expand Down Expand Up @@ -328,7 +331,7 @@ fn read_head_tree_sha(git_dir: &Path) -> Result<String> {
}


fn read_and_parse_git_object(git_dir: &Path, sha: &str) -> Result<Vec<u8>> {
pub fn read_and_parse_git_object(git_dir: &Path, sha: &str) -> Result<Vec<u8>> {
let obj_path = git_dir.join("objects").join(&sha[..2]).join(&sha[2..]);
let bytes = read_git_object(&obj_path)?;
let (_header, content) = split_header_and_content(&bytes)?;
Expand Down
8 changes: 6 additions & 2 deletions src/commands/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,16 @@ fn run_commit(args: &CommitArgs) -> Result<String> {
let tree_hash = write_tree::run(&write_tree_args)?;

// 2. Get the current HEAD commit (parent) if it exists
let parent = get_current_head()?;
let parent = match get_current_head()? {
Some(p) => Some(vec![p]),
None => None,
};


// 3. Create commit object using commit-tree
let commit_tree_args = commit_tree::CommitObject {
tree: tree_hash.clone(),
parent,
parent: parent,
message: args.message.clone(),
author: "guts <guts@example.com>".to_string(),
committer: "guts <guts@example.com>".to_string(),
Expand Down
11 changes: 9 additions & 2 deletions src/commands/commit_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::path::PathBuf;
pub struct CommitObject {
pub tree: String,
#[arg(short = 'p', long)]
pub parent: Option<String>,
pub parent: Option<Vec<String>>,
#[arg(short = 'm', long)]
pub message: String,
/// Author name and email in format "Name <email>"
Expand Down Expand Up @@ -44,9 +44,16 @@ pub fn run(args: &CommitObject) -> Result<String> {
let author_date = args.author_date.unwrap_or(now);
let committer_date = args.committer_date.unwrap_or(author_date);

let parent = match &args.parent {
Some(vec) if !vec.is_empty() => Some(vec.clone()),
_ => None,
};



let commit = Commit {
tree: args.tree.clone(),
parent: args.parent.clone(),
parent: parent.clone(),
message: args.message.clone(),
author: args.author.clone(),
committer: args.committer.clone(),
Expand Down
2 changes: 1 addition & 1 deletion src/commands/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub fn run(args: &LogArgs) -> Result<String> {
output.push_str(&format!("{} {}\n", current_hash, first_line));

if let Some(parent_hash) = parent {
current_hash = parent_hash;
current_hash = parent_hash[0].clone();
} else {
break;
}
Expand Down
Loading