feat: adding commit, show commands
This commit is contained in:
parent
49ed740014
commit
62af81b28f
106
src/commit.rs
Normal file
106
src/commit.rs
Normal 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(¤t_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(¤t_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(())
|
||||||
|
}
|
||||||
|
}
|
25
src/main.rs
25
src/main.rs
@ -5,6 +5,7 @@ use std::path::PathBuf;
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
|
||||||
|
mod commit;
|
||||||
mod error;
|
mod error;
|
||||||
mod kind;
|
mod kind;
|
||||||
mod object;
|
mod object;
|
||||||
@ -43,6 +44,18 @@ enum Command {
|
|||||||
/// The path to write
|
/// The path to write
|
||||||
path: PathBuf,
|
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> {
|
fn main() -> Result<(), Error> {
|
||||||
@ -67,6 +80,18 @@ fn main() -> Result<(), Error> {
|
|||||||
Ok(hash) => println!("{}", hex::encode(hash)),
|
Ok(hash) => println!("{}", hex::encode(hash)),
|
||||||
Err(e) => eprintln!("Failed to write tree: {}", e),
|
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(())
|
Ok(())
|
||||||
|
@ -77,7 +77,7 @@ impl Repository {
|
|||||||
|
|
||||||
let content = std::fs::read(file)?;
|
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]> {
|
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());
|
let mut zlib_out = ZlibEncoder::new(file_out_fd, Compression::default());
|
||||||
write!(zlib_out, "{} {}\0", kind.string(), content.len())
|
write!(zlib_out, "{} {}\0", kind.string(), content.len())
|
||||||
.context("could not write header")?;
|
.context("could not write header")?;
|
||||||
zlib_out.write(content)?;
|
zlib_out.write_all(content)?;
|
||||||
zlib_out
|
zlib_out
|
||||||
.finish()
|
.finish()
|
||||||
.context("could not compress or write file")?;
|
.context("could not compress or write file")?;
|
||||||
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
fs::{create_dir, read_to_string},
|
fs::{create_dir, read_to_string},
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Repository {
|
pub struct Repository {
|
||||||
@ -42,8 +42,8 @@ impl Repository {
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_repository(&mut self, path: &PathBuf) -> Result<PathBuf> {
|
pub fn init_repository(&mut self, path: &Path) -> Result<PathBuf> {
|
||||||
self.path = path.clone();
|
self.path = path.to_path_buf();
|
||||||
let git_dir = self.path.join(".git");
|
let git_dir = self.path.join(".git");
|
||||||
|
|
||||||
create_dir(&git_dir)?;
|
create_dir(&git_dir)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user