Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 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 <jo.shmo@gmail.com> 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 <nicklink483@gmail.com> 1430522440 -0700\n")
- commit_content += (committer = "committer Dominic Muller <nicklink483@gmail.com> 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 <nicklink483@gmail.com> 1430523736 -0700\n")
- new_commit_content += (committer = "committer Dominic Muller <nicklink483@gmail.com> 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
Add Comment
Please, Sign In to add comment