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
31 changes: 31 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
name: Bug Report
about: Create a report to help us improve git-rs
title: "[BUG] "
labels: bug
assignees: ''

---

## Describe the Bug
A clear and concise description of what the bug is.

## To Reproduce
Steps to reproduce the behavior:
1. Run command '...'
2. Input '...'
3. See error

## Expected Behavior
A clear and concise description of what you expected to happen.

## Actual Behavior
A clear and concise description of what actually happened.

## Environment (please complete the following information):
- OS: [e.g. Ubuntu, macOS, Windows]
- Rust Version: [e.g. 1.70.0]
- `git-rs` Version/Commit: [e.g. 0.3.0 or commit hash]

## Additional Context
Add any other context about the problem here (e.g. stack traces, logs).
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Feature Request
about: Suggest an idea for this project
title: "[FEATURE] "
labels: enhancement
assignees: ''

---

## Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

## Describe the solution you'd like
A clear and concise description of what you want to happen. Consider referencing official Git behavior.

## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

## Additional context
Add any other context or screenshots about the feature request here.
20 changes: 15 additions & 5 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
# Description

Please include a summary of the change and which issue is fixed.
Please include a summary of the change and which issue is fixed. If this PR introduces a new architecture or modifies a critical storage mechanism, explain the *why* behind your approach.

Fixes # (issue)

## Type of Change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Refactor (memory/safety optimization)
- [ ] Documentation update
- [ ] Performance improvement

## Testing and Verification

Please describe the tests that you ran to verify your changes.
- [ ] I have verified this change operates correctly alongside the official `git` binary (e.g., `git cat-file -p <hash>`).
- [ ] I have added unit/integration tests that prove my fix is effective or my feature works.
- [ ] `cargo test` passes locally.

## Checklist

- [ ] My code follows the style guidelines of this project
- [ ] I have run `cargo fmt`
- [ ] I have run `cargo clippy -- -D warnings` and fixed all warnings
- [ ] I have added/updated documentation if necessary
- [ ] My code follows the style guidelines of this project (`cargo fmt`).
- [ ] I have run `cargo clippy -- -D warnings` and fixed all warnings.
- [ ] I have added/updated strictly formatted Rustdoc comments (`///` or `//!`) detailing the *why* for any new/modified logic.
- [ ] I have updated the `CHANGELOG.md` with my changes (if applicable).
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2026-06-25

### Added
- **Deep Strict Documentation:** Added comprehensive, intent-revealing Rustdoc comments (`///` and `//!`) across all modules (`main.rs`, `object.rs`, `tree.rs`, `commit.rs`, `refs.rs`, `config.rs`). The documentation directly reflects the current codebase state, clearly defining architectural domain logic and providing context drawn from the project's history.
- **GitHub Issue & PR Templates:** Created strict template structures (`.github/ISSUE_TEMPLATE/bug_report.md`, `.github/ISSUE_TEMPLATE/feature_request.md`) and refined `.github/PULL_REQUEST_TEMPLATE.md` to ensure better integrity and structured code review processes.

## [0.3.0] - 2026-06-25

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ If official Git can read the database, the binary format is mathematically corre
| [**Commit Objects & the DAG**](docs/04_commit_object_and_commit_tree.md) | **4** | **Commit object format, DAG structure, parent references, and serialization** |
| [**DAG & Commit Serialization**](docs/05_dag_and_commit_serialization.md) | **4** | **Deep dive: DAG mathematics, commit serialization pipeline, content deduplication** |
| [**Porcelain Commit & Refs**](docs/06_porcelain_commit_and_refs.md) | **5** | **`commit` workflow, `HEAD` resolution, branch pointer mutation, plumbing vs porcelain** |
| [**Architectural Overview**](docs/architecture/overview.md) | **All** | **High-level summary of domain logic and core modules within the Rust codebase.** |

---

Expand Down
34 changes: 34 additions & 0 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Git-rs Architectural Overview

## Introduction
`git-rs` is a from-scratch implementation of Git's core object storage engine in Rust. This document outlines the high-level architecture and the specific domain logic that makes up the repository.

## Core Modules

1. **CLI Dispatcher (`src/main.rs`)**
The entry point of the application. It utilizes `clap` to parse command-line arguments and route them to the appropriate core logic functions. It handles the bridge between the user and the system's underlying capabilities, functioning as both a plumbing and porcelain interface.

2. **Object Database Engine (`src/object.rs`)**
This module handles the core content-addressable storage mechanics. Git relies on a strict, continuous stream of bytes. This engine ensures the bytes are formatted correctly: `<type> <size>\0<content>`, and handles hashing (SHA-1) and compression (Zlib) before writing objects to `.git/objects/XX/YYY...`.

3. **Tree Serialization (`src/tree.rs`)**
This module implements the recursive post-order depth-first search required to capture a directory's state. It reads file paths, determines if an entry is a file (blob) or a directory (tree), and recursively bubbles up the computed hashes to build the full tree structure, writing each part to the object database.

4. **Commit Creation (`src/commit.rs`)**
Commits bind a tree hash to metadata (author, committer, timestamp, message, and parent commit). This module handles formatting this ASCII data correctly and storing it as a commit object, acting as the nodes in the Git Directed Acyclic Graph (DAG).

5. **References Management (`src/refs.rs`)**
Branches and tags in Git are simply pointers to commit hashes. This module provides the tools to read the `HEAD` pointer, resolve branches, and safely mutate references when new commits are created.

6. **Configuration Parsing (`src/config.rs`)**
To support local development identity, this module reads the repository-local `.git/config` file. It relies on the `serde` and `toml` crates to deserialize author names and emails used in the commit generation process.

## Data Flow Pipeline
1. **User Action:** A user initiates a command (e.g., `git-rs commit -m "Msg"`).
2. **Snapshot:** `tree.rs` walks the directory, compressing and hashing each file into `object.rs`, and finally returns a root tree hash.
3. **Reference Lookup:** `refs.rs` resolves `HEAD` to find the parent commit hash.
4. **Metadata Assembly:** `commit.rs` pulls author info via `config.rs`, creates the commit ASCII payload, and writes it using `object.rs`.
5. **Reference Update:** `refs.rs` overwrites the active branch file with the new commit hash.

## Integrity Guarantee
If the official Git binary can parse the `.git/objects` and `.git/refs` generated by this implementation, the architecture is structurally sound and mathematically valid.
21 changes: 21 additions & 0 deletions src/commit.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! # Git Commit Creation
//!
//! This module handles the construction and serialization of Git commit objects.
//! A commit object links a tree (the snapshot) with metadata such as the author,
//! committer, timestamp, and an optional parent commit to form the commit history (DAG).

use crate::{config::get_author, object::write_object};
use std::{
io::Write,
Expand All @@ -6,6 +12,7 @@ use std::{
};
use thiserror::Error;

/// Errors that can occur during commit creation and serialization.
#[derive(Debug, Error)]
pub enum CommitError {
#[error("I/O error: {0}")]
Expand All @@ -17,13 +24,16 @@ pub enum CommitError {
#[error("Object storage error: {0}")]
Object(#[from] crate::object::ObjectError),
}

/// Represents a Git author or committer signature.
pub struct Signature {
name: String,
email: String,
timestamp: u64,
timezone: String,
}

/// Represents a Git commit object and its metadata.
pub struct Commit {
tree: String,
author: Signature,
Expand All @@ -32,11 +42,16 @@ pub struct Commit {
parent: Option<String>,
}

/// Retrieves the current system time as a UNIX timestamp (seconds since epoch).
fn get_timestamp() -> Result<u64, CommitError> {
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
Ok(timestamp)
}

/// Constructs a `Commit` struct with the provided tree, message, and parent.
///
/// Author and committer information are read from the repository's `.git/config`
/// file, and the current system time is used for the timestamps.
fn create_commit(
tree_hash: &str,
commit_message: &str,
Expand Down Expand Up @@ -68,6 +83,8 @@ fn create_commit(
Ok(commit)
}

/// Serializes a `Commit` struct into the official Git ASCII format and writes
/// it to the object database. Returns the SHA-1 hash of the commit object.
fn write_commit(commit: &Commit, dir: &Path) -> Result<String, CommitError> {
let mut serialized = Vec::new();
writeln!(&mut serialized, "tree {}", commit.tree)?;
Expand All @@ -92,6 +109,10 @@ fn write_commit(commit: &Commit, dir: &Path) -> Result<String, CommitError> {
Ok(oid)
}

/// High-level function to create and write a commit object in one step.
///
/// This serves as the primary entry point for commit creation from the CLI dispatcher.
/// Returns the 40-character hex SHA-1 hash of the new commit object.
pub fn write_commit_object(
tree_hash: &str,
commit_message: &str,
Expand Down
15 changes: 15 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
//! # Git Configuration Parsing
//!
//! This module handles the parsing of `.git/config` files to extract repository-local
//! settings, primarily focusing on user identity (name and email) for commits.

use std::path::Path;

/// Represents the top-level structure of a Git configuration file.
#[derive(serde::Deserialize)]
pub struct GitConfig {
/// The `[user]` section of the config.
pub user: UserConfig,
}

/// Represents the `[user]` section containing identity information.
#[derive(serde::Deserialize)]
pub struct UserConfig {
/// The user's name (e.g., "John Doe").
pub name: String,
/// The user's email address (e.g., "john@example.com").
pub email: String,
}

/// Reads the repository's `.git/config` file and extracts the author's name and email.
///
/// If the config file is missing or malformed, it falls back to a default
/// "unknown_user" and "unknown@localhost" to ensure commit creation does not fail.
pub fn get_author(dir: &Path) -> (String, String) {
let unknown_user = UserConfig {
name: "unknown_user".to_string(),
Expand Down
39 changes: 36 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
//! # Git-rs CLI Dispatcher
//!
//! This module contains the main entry point for the `git-rs` command-line interface.
//! It uses the `clap` crate to define, parse, and route CLI arguments to the appropriate
//! plumbing or porcelain commands (e.g., `init`, `hash-object`, `commit`).
//!
//! The `git-rs` project is a from-scratch implementation of Git's core object
//! storage engine, intended for educational purposes and systems programming practice.

mod commit;
mod config;
mod object;
Expand All @@ -19,16 +28,19 @@ use crate::tree::write_tree;

use clap::{Parser, Subcommand};

/// The root command-line interface structure parsed by `clap`.
#[derive(Parser)]
#[command(
name = "git-rs",
about = "A from-scratch implementation of Git's core object storage engine in Rust."
)]
struct Cli {
/// The specific subcommand to execute.
#[command(subcommand)]
command: Commands,
}

/// The available Git commands supported by `git-rs`.
#[derive(Subcommand)]
enum Commands {
/// Initialize a new git-rs repository
Expand Down Expand Up @@ -72,6 +84,10 @@ enum Commands {
},
}

/// The main entry point of the `git-rs` application.
///
/// It initializes the CLI parser, determines the current working directory,
/// and delegates execution to the corresponding command handler function.
fn main() -> anyhow::Result<()> {
// The magic happens here! clap reads env::args(), validates everything,
// and populates the Cli struct.
Expand Down Expand Up @@ -114,8 +130,10 @@ fn main() -> anyhow::Result<()> {
}
}

// DELETED: expect_args and run functions are no longer needed!

/// Initializes a new, empty Git repository in the current directory.
///
/// Creates the `.git` directory structure, including `objects/`, `refs/`,
/// a default `HEAD` pointer, and a default `.git/config` if one does not exist.
fn cmd_init(repo_dir: &Path) -> anyhow::Result<()> {
let git_dir = repo_dir.join(".git");
fs::create_dir_all(git_dir.join("objects/info"))?;
Expand All @@ -134,6 +152,9 @@ fn cmd_init(repo_dir: &Path) -> anyhow::Result<()> {
Ok(())
}

/// Reads an object from the Git database and outputs its content, type, or size.
///
/// Exactly one of `pretty`, `show_type`, or `show_size` must be true.
fn cmd_cat_file(
pretty: bool,
show_type: bool,
Expand All @@ -155,6 +176,7 @@ fn cmd_cat_file(
Ok(())
}

/// Computes the SHA-1 hash of a file's content and optionally writes it to the database as a blob.
fn cmd_hash_object(file: &str, write: bool, dir: &Path) -> anyhow::Result<()> {
let content = fs::read(file)?;
if write {
Expand All @@ -166,12 +188,18 @@ fn cmd_hash_object(file: &str, write: bool, dir: &Path) -> anyhow::Result<()> {
Ok(())
}

/// Recursively snapshots the working directory into a tree object.
///
/// Returns the SHA-1 hash of the resulting root tree object.
fn cmd_write_tree(path: &Path, dir: &Path) -> anyhow::Result<String> {
let tree_hash = write_tree(path, dir).context("Failed to write tree")?;
Ok(tree_hash)
}

// FIXED: Removed the `flag` parameter and the `match flag` block
/// Low-level plumbing command to create a commit object.
///
/// Requires an existing tree hash, a commit message, and an optional parent commit hash.
/// Returns the SHA-1 hash of the newly created commit object.
fn cmd_write_commit(
tree_hash: &str,
commit_message: &str,
Expand All @@ -183,6 +211,11 @@ fn cmd_write_commit(
Ok(commit_hash)
}

/// High-level porcelain command to record changes to the repository.
///
/// Snapshots the current working directory, creates a commit object referencing
/// the snapshot, sets the parent to the current HEAD, and updates HEAD to point
/// to the new commit. Returns the SHA-1 hash of the newly created commit.
fn cmd_commit(commit_message: &str, dir: &Path) -> anyhow::Result<String> {
let current_path = Path::new(".");
let tree_hash =
Expand Down
Loading
Loading