-
-
Save ybur-yug/041b042296903c304a99 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'digest/sha1' | |
require 'zlib' | |
require 'pp' | |
module Git | |
OBJECTS = {} | |
class Object | |
def initialize(content) | |
@uncompressed_content = header(content) + content | |
puts hash | |
return hash | |
end | |
def hash | |
@sha1 ||= Digest::SHA1.hexdigest(@uncompressed_content) | |
end | |
def compressed | |
@compressed_content ||= Zlib::Deflate.deflate(@uncompressed_content) | |
end | |
def path | |
@path ||= ".git/objects/" + hash[0..1] + "/" + hash[2..-1] | |
end | |
def header(type, content) | |
"#{type} #{content.length}\0" | |
end | |
def save | |
OBJECTS[path.to_sym] ||= compressed | |
end | |
end | |
class Blob < Object | |
def header(content) | |
super("blob", content) | |
end | |
def hash_bytes | |
eval(hash.scan(/../).map {|p| "\x" + p.upcase}.join) | |
end | |
end | |
class Tree < Object | |
def initialize(content) | |
validate(content) | |
super(content) | |
end | |
def header(content) | |
super("tree", content) | |
end | |
private | |
def validate(content) | |
# must be a list of trees and blobs | |
# | |
end | |
end | |
class Commit < Object | |
def initialize(content) | |
validate(content) | |
super(content) | |
end | |
def header(content) | |
super("commit", content) | |
end | |
private | |
def validate(content) | |
# must refer to a tree hash, zero or more parent commit hashes, | |
# an author (with datetime), a commiter (with datetime), and a message | |
end | |
end | |
class Tag < Object | |
def initialize(content) | |
validate(content) | |
super(content) | |
end | |
private | |
def validate(content) | |
#tag must have an object hash, object type, tag name, tagger (with datetime), and message | |
# example: | |
#object 7e7b6a09dc5e466b7992fea125732c67239ac92b | |
#type commit | |
#tag v2.0 | |
#tagger Joe Schmo <[email protected]> 1430542850 -0700 | |
# | |
#tag test | |
# | |
end | |
def save | |
super | |
#and also add a file in .git/refs/tags whose name is the tag name and | |
# whose content is the sha1 of the tag object created in the object store | |
end | |
end | |
end | |
# Every command in git either makes one or more objects in the database, | |
# changes a reference, or displays an object or reference. | |
# The only exception to this rule is modifying your config file. | |
# git add some_file.txt | |
# results in the following: | |
# a new blob is created, and added to the object store | |
file_name = "some_file.txt" | |
file_content = "some text in a file" | |
blob = Git::Blob.new(file_content) | |
blob.save | |
pp Git::OBJECTS | |
# it also adds the new blob to the index so that when you commit | |
# the following tree is created | |
tree_content = "100644 #{file_name}\x00#{blob.hash_bytes}" | |
tree = Git::Tree.new(tree_content) | |
tree.save | |
pp Git::OBJECTS | |
commit_content = (tree = "tree #{tree.hash}\n") | |
commit_content += (author = "author Dominic Muller <[email protected]> 1430522440 -0700\n") | |
commit_content += (committer = "committer Dominic Muller <[email protected]> 1430522440 -0700\n") | |
commit_content += (message = "\nI'm a commit message!\n") | |
# git commit -m "I'm a commit message!" | |
# this will now result in a commit object being created that points to that tree | |
# that the index made for you. | |
# The author and commiter will be grabbed from your config files | |
commit = Git::Commit.new(commit_content) | |
commit.save | |
pp Git::OBJECTS | |
# congrats! Now the object store has 3 new key-value pairs. One blob, one tree, and one commit | |
# The files are saved after being compressed with the zlib deflate compression. | |
# so the key is the hash of the content of the object (including the header prepended), | |
# and the value is the compressed content (including the header prepended). | |
# A branch is a reference to some commit | |
# When you make your first commit, Git sets the master reference to point to its hash | |
master = commit.hash | |
# Git also sets a reference called HEAD to point to some branch. | |
# Which in turn points at some commit hash, but we'll just use the branch name for now | |
# let's make another commit! | |
file_content = "new file here!" | |
file_name2 = "new_file.txt" | |
blob2 = Git::Blob.new(file_content) | |
blob2.save | |
# and now to make a tree which has BOTH files so far | |
tree_content = (tree_file_1 = "100644 #{file_name}\x00#{blob.hash_bytes}") | |
tree_content += (tree_file_2 = "100644 #{file_name2}\x00#{blob2.hash_bytes}") | |
second_tree = Git::Tree.new(tree_content) | |
second_tree.save | |
# And of course, if we're going to make history, | |
# we have to tell the new commit where he came from | |
# which requires adding a parent line | |
new_commit_content = (tree = "tree #{second_tree.hash}\n") | |
new_commit_content += (parent = "parent #{commit.hash}\n") | |
new_commit_content += (author = "author Dominic Muller <[email protected]> 1430523736 -0700\n") | |
new_commit_content += (committer = "committer Dominic Muller <[email protected]> 1430523736 -0700\n") | |
new_commit_content += (message = "\nA second commit. I have a parent!\n") | |
new_commit = Git::Commit.new(new_commit_content) | |
new_commit.save | |
master = new_commit.hash | |
pp Git::OBJECTS | |
# If we want, we can go re-inflate any compressed objects | |
compressed_commit = Git::OBJECTS[:".git/objects/#{new_commit.hash[0..1]}/#{new_commit.hash[2..-1]}"] | |
puts Zlib::Inflate.inflate(compressed_commit).split("\0")[1..-1].join | |
# btw, that last line is exactly what `git cat-file -p HEAD` would have done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment