A lightweight implementation of core Git functionality built from scratch in TypeScript/Bun. This project implements Git's internal object model, packfile protocol, and essential commands for version control operations.
This is a functional Git implementation that handles:
- Repository initialization
- Object storage and retrieval (blobs, trees, commits)
- Hash object creation and manipulation
- Tree operations and directory traversal
- Commit creation with parent tracking
- Repository cloning with packfile parsing
- Working directory checkout
Built as part of the CodeCrafters "Build Your Own Git" challenge.
init- Initialize a new Git repositorycat-file- Display contents of Git objectshash-object- Create blob objects from filesls-tree- List contents of tree objectswrite-tree- Create tree objects from working directorycommit-tree- Create commit objects with metadataclone- Clone remote repositories with full packfile support
- Object Storage: Implements Git's content-addressable storage using SHA-1 hashing
- Compression: Uses zlib deflate/inflate for efficient storage
- Packfile Protocol: Full support for Git's packfile format including:
- Pack header parsing
- Object type identification (blob, tree, commit, tag)
- Delta compression (ref-delta and ofs-delta)
- Checksum validation
Supports all core Git object types:
- Blobs - File content storage
- Trees - Directory structure
- Commits - Snapshots with metadata
- Tags - Named references (parsing support)
- Bun 1.1 or higher (Install Bun)
- Basic understanding of Git concepts
-
Clone the repository
git clone <repository-url> cd git-implementation
-
Install dependencies
bun install
-
Run locally
./your_program.sh <command> [args...]
./your_program.sh initCreates a new .git directory with the standard structure:
.git/
├── objects/
├── refs/
└── HEAD
./your_program.sh cat-file -p <object-hash>Displays the contents of a Git object (blob, tree, commit, or tag).
./your_program.sh hash-object -w <file-path>Creates a blob object from a file and writes it to .git/objects/.
./your_program.sh ls-tree --name-only <tree-hash>Lists all files and directories in a tree object.
./your_program.sh write-treeRecursively creates tree objects for the current directory structure.
./your_program.sh commit-tree <tree-hash> -p <parent-hash> -m "Commit message"Creates a commit object with the specified tree and parent commit.
./your_program.sh clone <repository-url> <directory>Clones a remote repository including:
- Fetching all objects via packfile
- Parsing delta-compressed objects
- Checking out the working directory
- Setting up HEAD and refs
.
├── .codecrafters/
│ ├── compile.sh # Compilation script (no-op for TypeScript)
│ └── run.sh # Execution script for CodeCrafters
├── app/
│ └── main.ts # Main implementation file
├── .gitignore
├── codecrafters.yml # CodeCrafters configuration
├── package.json
├── your_program.sh # Local execution script
└── README.md
const hashBuffer = (
buffer: Buffer,
type: "blob" | "tree" | "commit"
): { hash: string; content: any }Creates SHA-1 hashes for Git objects with proper headers.
- Compression:
zlib.deflateSync()for writing objects - Decompression:
zlib.inflateSync()for reading objects
- readFileSync: Reads and decompresses Git objects
- writeFileSync: Compresses and writes Git objects
- writeFileSyncToPath: Creates nested directories as needed
- recursiveReadDir: Recursively traverses directories to create tree objects
- Handles file permissions (regular, executable, symlinks)
- Sorts entries alphabetically (Git convention)
Complex implementation supporting:
- Pack header validation (
PACKsignature) - Object count parsing (network byte order)
- Variable-length encoding for sizes
- Delta decompression:
- Ref-delta (references base object by hash)
- Ofs-delta (references base object by offset)
- Instruction parsing for delta reconstruction
- Checksum verification
Git objects are stored in .git/objects/<xx>/<yy...> where <xxyy...> is the SHA-1 hash.
Object Format:
<type> <size>\0<content>
Example Blob:
blob 14\0Hello, World!
<mode> <name>\0<20-byte-hash><mode> <name>\0<20-byte-hash>...
Modes:
100644- Regular file100755- Executable file40000- Directory (tree)120000- Symbolic link
tree <tree-hash>
parent <parent-hash>
author <name> <email> <timestamp> <timezone>
committer <name> <email> <timestamp> <timezone>
<commit message>
Header:
PACK<version><object-count>
Object Entry:
- Type + Size in variable-length encoding
- Optional base reference (for deltas)
- Compressed data (zlib)
Delta Instructions:
- Copy instruction:
1xxxxxxx- Copy from base object - Insert instruction:
0xxxxxxx- Insert new data
Run with CodeCrafters test suite:
codecrafters testOr test locally with manual commands:
# Initialize and create objects
./your_program.sh init
echo "test content" > test.txt
./your_program.sh hash-object -w test.txt
# Create and inspect trees
./your_program.sh write-tree
./your_program.sh ls-tree --name-only <tree-hash>
# Create commits
./your_program.sh commit-tree <tree-hash> -m "Initial commit"The implementation uses a switch-case pattern for command routing:
switch (command) {
case Commands.Init:
// Initialize repository
break;
case Commands.CatFile:
// Read object contents
break;
// ... other commands
}SHA-1 Hashing:
const hash = crypto.createHash("sha1").update(content).digest("hex");Variable-Length Encoding (Packfile):
let MSB = (byte >> 7) & 1;
let size = byte & 15;
while (MSB === 1) {
idx++;
size = size + ((byteArray[idx] & 127) << moveOffset);
MSB = (byteArray[idx] >> 7) & 1;
}Delta Reconstruction:
if (command === 1) {
// Copy instruction: extract offset and size
buffer.concat(baseObject.slice(offset, offset + size));
} else {
// Insert instruction: read new data
buffer.concat(deltaData.slice(idx, idx + size));
}- No garbage collection
- No ref packing
- Limited tag support
- No merge conflict resolution
- No branch operations
- No rebase functionality
- Single-threaded clone operation
- Add branch management commands
- Implement
git logfunctionality - Add merge capabilities
- Support for .gitignore
- Implement gc (garbage collection)
- Add diff functionality
- Support for git config
- Implement rebase
- Add stash functionality
- Support for submodules
This is a learning project, but contributions are welcome! Feel free to:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- Author information is hardcoded in commit creation
- Timezone is set to
+0900(JST) - The implementation prioritizes correctness over performance
- All paths are relative to the repository root
- Large repositories may take time to clone
- Binary file handling needs optimization
- Some edge cases in delta parsing may not be covered
- Limited error messages for debugging
This project is created for educational purposes as part of the CodeCrafters challenge.
- CodeCrafters - For the excellent "Build Your Own Git" challenge
- Git Project - For comprehensive documentation on Git internals
- Bun Team - For the fast TypeScript runtime
Built with TypeScript + Bun
Understanding Git by rebuilding it from scratch