Skip to content

Instantly share code, notes, and snippets.

@jcoglan
Last active October 10, 2017 22:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcoglan/a82001bb5e01173678dd7326ccbf07ab to your computer and use it in GitHub Desktop.
Save jcoglan/a82001bb5e01173678dd7326ccbf07ab to your computer and use it in GitHub Desktop.
Blob = Struct.new(:oid, :content) do
def type
Database::BLOB
end
def to_s
content
end
end
Commit = Struct.new(:oid, :tree, :message) do
def type
Database::COMMIT
end
def to_s
["tree #{ tree.oid }", "", message].join("\n")
end
end
require "digest/sha1"
require "fileutils"
require "zlib"
require_relative "./blob"
require_relative "./tree"
require_relative "./commit"
class Database
BLOB = "blob"
TREE = "tree"
COMMIT = "commit"
def initialize(pathname)
@pathname = pathname
end
def store(object)
string = object.to_s.force_encoding(Encoding::ASCII_8BIT)
content = "#{ object.type } #{ string.bytesize }\0#{ string }"
object.oid = Digest::SHA1.hexdigest(content)
write_file(object.oid, content)
end
def write_file(oid, content)
path = @pathname.join(oid[0..1], oid[2..-1])
FileUtils.mkdir_p(path.dirname)
File.write(path, Zlib::Deflate.deflate(content))
end
end
require "fileutils"
require "pathname"
require_relative "./database"
require_relative "./workspace"
work_path = Pathname.new(Dir.getwd)
workspace = Workspace.new(work_path)
git_path = work_path.join(".git")
database = Database.new(git_path.join("objects"))
%w[objects refs].each do |dir|
FileUtils.mkdir_p(git_path.join(dir))
end
source_files = workspace.list_files
entries = source_files.map do |path|
data = workspace.read_file(path)
stat = workspace.stat_file(path)
blob = Blob.new(nil, data)
database.store(blob)
[stat, blob, path]
end
tree = Tree.new(nil, entries)
database.store(tree)
message = "Just enough code to write a commit."
commit = Commit.new(nil, tree, message)
database.store(commit)
File.open(git_path.join("HEAD"), "w") { |file| file.puts(commit.oid) }
Tree = Struct.new(:oid, :entries) do
def type
Database::TREE
end
def to_s
entries.sort_by!(&:last)
lines = entries.map do |stat, blob, path|
["100644", path, blob.oid].pack("A7Z*H40")
end
lines.join("")
end
end
class Workspace
IGNORE = %w[. .. .git]
def initialize(pathname)
@pathname = pathname
end
def list_files
Dir.entries(@pathname) - IGNORE
end
def read_file(path)
File.read(@pathname.join(path))
end
def stat_file(path)
File.stat(@pathname.join(path))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment