feat: adding commit, show commands
All checks were successful
CI checks / Clippy (push) Successful in 26s
CI checks / Format (push) Successful in 25s

This commit is contained in:
Patrick MARIE 2025-02-05 21:55:05 +01:00
parent 49ed740014
commit 62af81b28f
Signed by: mycroft
GPG Key ID: BB519E5CD8E7BFA7
4 changed files with 136 additions and 5 deletions

106
src/commit.rs Normal file
View File

@ -0,0 +1,106 @@
use std::fs::{read_to_string, File};
use anyhow::{Context, Result};
use hex::FromHex;
use crate::{kind::Kind, repository::Repository};
impl Repository {
pub fn read_head(&self) -> Result<String> {
let head_path = self.path.join(".git").join("HEAD");
read_to_string(head_path).context("reading head")
}
pub fn current_branch(&self) -> Result<String> {
let head = self.read_head()?;
Ok(head
.trim_start_matches("ref: refs/heads/")
.trim_end()
.to_string())
}
pub fn current_commit(&self) -> Result<[u8; 20]> {
let current_branch = self.current_branch()?;
let branch_path = self
.path
.join(".git")
.join("refs")
.join("heads")
.join(&current_branch);
let r = read_to_string(branch_path).context("could not read current branch")?;
let r = r.trim();
Ok(<[u8; 20]>::from_hex(r)?)
}
pub fn has_current_commit(&self) -> bool {
self.current_commit().is_ok()
}
pub fn set_current_commit(&self, hash: &[u8; 20]) -> Result<()> {
let current_branch = self
.current_branch()
.context("could not find current branch")?;
let branch_path = self.path.join(".git").join("refs").join("heads");
if !branch_path.exists() {
std::fs::create_dir_all(&branch_path)?;
}
let branch_path = branch_path.join(&current_branch);
// file does not exist
if !branch_path.exists() {
File::create(&branch_path)?;
}
std::fs::write(branch_path, hex::encode(hash))?;
Ok(())
}
pub fn commit(&self, message: &str) -> Result<[u8; 20]> {
let has_current_commit = self.has_current_commit();
let mut out: Vec<u8> = Vec::new();
let tree_hash = self
.write_tree(&self.path)
.context("could not write_tree")?;
out.extend_from_slice(b"tree ");
out.extend_from_slice(hex::encode(tree_hash).as_bytes());
out.push(b'\n');
if has_current_commit {
let current_commit_id = self.current_commit()?;
out.extend_from_slice(b"parent ");
out.extend_from_slice(hex::encode(current_commit_id).as_bytes());
out.push(b'\n');
}
out.push(b'\n');
out.extend_from_slice(message.as_bytes());
out.push(b'\n');
let hash = self.write_object(Kind::Commit, &out).context("Write")?;
// update current branch's commit id
self.set_current_commit(&hash)?;
Ok(hash)
}
pub fn show(&self, hash: Option<String>) -> Result<()> {
let mut commit = if let Some(hash) = hash {
self.read_object(&hash)?
} else {
let current_commit = self.current_commit()?;
self.read_object(&hex::encode(current_commit))?
};
println!("{}", commit.string()?);
Ok(())
}
}

View File

@ -5,6 +5,7 @@ use std::path::PathBuf;
use clap::Parser;
use clap::Subcommand;
mod commit;
mod error;
mod kind;
mod object;
@ -43,6 +44,18 @@ enum Command {
/// The path to write
path: PathBuf,
},
/// Commit current changes
Commit {
/// The commit message
message: String,
},
/// Get the current branch
Branch,
/// Get the latest commit
Show {
/// The commit to show
hash: Option<String>,
},
}
fn main() -> Result<(), Error> {
@ -67,6 +80,18 @@ fn main() -> Result<(), Error> {
Ok(hash) => println!("{}", hex::encode(hash)),
Err(e) => eprintln!("Failed to write tree: {}", e),
},
Command::Commit { message } => match repo.commit(&message) {
Ok(hash) => println!("{}", hex::encode(hash)),
Err(e) => eprintln!("Failed to commit: {}", e),
},
Command::Branch => match repo.current_branch() {
Ok(branch) => println!("{}", branch),
Err(e) => eprintln!("Failed to get branch: {}", e),
},
Command::Show { hash } => match repo.show(hash) {
Ok(_) => (),
Err(e) => eprintln!("Failed to show: {}", e),
},
}
Ok(())

View File

@ -77,7 +77,7 @@ impl Repository {
let content = std::fs::read(file)?;
Ok(self.write_object(Kind::Blob(false), &content)?)
self.write_object(Kind::Blob(false), &content)
}
pub fn write_object(&self, kind: Kind, content: &[u8]) -> Result<[u8; 20]> {
@ -102,7 +102,7 @@ impl Repository {
let mut zlib_out = ZlibEncoder::new(file_out_fd, Compression::default());
write!(zlib_out, "{} {}\0", kind.string(), content.len())
.context("could not write header")?;
zlib_out.write(content)?;
zlib_out.write_all(content)?;
zlib_out
.finish()
.context("could not compress or write file")?;

View File

@ -2,7 +2,7 @@ use anyhow::Result;
use std::{
env,
fs::{create_dir, read_to_string},
path::PathBuf,
path::{Path, PathBuf},
};
pub struct Repository {
@ -42,8 +42,8 @@ impl Repository {
Ok(true)
}
pub fn init_repository(&mut self, path: &PathBuf) -> Result<PathBuf> {
self.path = path.clone();
pub fn init_repository(&mut self, path: &Path) -> Result<PathBuf> {
self.path = path.to_path_buf();
let git_dir = self.path.join(".git");
create_dir(&git_dir)?;