Skip to content
Browse files

implemented Tags and Users

  • Loading branch information...
1 parent cea4e48 commit 97ed50093f8574055a6caaffc27035b245fce48d @georgi committed
Showing with 214 additions and 92 deletions.
  1. +16 −7 git_store.gemspec
  2. +35 −16 lib/git_store.rb
  3. +9 −1 lib/git_store/blob.rb
  4. +21 −22 lib/git_store/commit.rb
  5. +40 −0 lib/git_store/tag.rb
  6. +10 −7 lib/git_store/tree.rb
  7. +29 −0 lib/git_store/user.rb
  8. +33 −0 test/bar_store_spec.rb
  9. +6 −7 test/commit_spec.rb
  10. +15 −32 test/git_store_spec.rb
View
23 git_store.gemspec
@@ -1,31 +1,40 @@
Gem::Specification.new do |s|
s.name = 'git_store'
- s.version = '0.3'
+ s.version = '0.3.1'
s.summary = 'a simple data store based on git'
s.author = 'Matthias Georgi'
s.email = 'matti.georgi@gmail.com'
- s.homepage = 'http://github.com/georgi/git_store'
- s.description = 'A simple git based data store'
+ s.homepage = 'http://ww.matthias-georgi.de/git_store'
+ s.description = <<END
+GitStore implements a versioned data store based on the revision
+management system Git. You can store object hierarchies as nested
+hashes, which will be mapped on the directory structure of a git
+repository. GitStore checks out the repository into a in-memory
+representation, which can be modified and finally committed.
+END
s.require_path = 'lib'
s.has_rdoc = true
s.extra_rdoc_files = ['README.md']
s.files = %w{
.gitignore
LICENSE
-Rakefile
README.md
+Rakefile
git_store.gemspec
lib/git_store.rb
lib/git_store/blob.rb
lib/git_store/commit.rb
lib/git_store/diff.rb
-lib/git_store/tree.rb
lib/git_store/handlers.rb
lib/git_store/pack.rb
-test/tree_spec.rb
+lib/git_store/tag.rb
+lib/git_store/tree.rb
+lib/git_store/user.rb
+test/bare_store_spec.rb
+test/benchmark.rb
test/commit_spec.rb
test/git_store_spec.rb
-test/benchmark.rb
+test/tree_spec.rb
}
end
View
51 lib/git_store.rb
@@ -7,6 +7,8 @@
require 'git_store/blob'
require 'git_store/diff'
require 'git_store/tree'
+require 'git_store/tag'
+require 'git_store/user'
require 'git_store/pack'
require 'git_store/commit'
require 'git_store/handlers'
@@ -42,10 +44,18 @@ class GitStore
TYPE_CLASS = {
'tree' => Tree,
'blob' => Blob,
- 'commit' => Commit
+ 'commit' => Commit,
+ 'tag' => Tag
}
- attr_reader :path, :index, :root, :branch, :user, :lock_file, :head, :packs, :handler, :bare
+ CLASS_TYPE = {
+ Tree => 'tree',
+ Blob => 'blob',
+ Commit => 'commit',
+ Tag => 'tag'
+ }
+
+ attr_reader :path, :index, :root, :branch, :lock_file, :head, :packs, :handler, :bare, :objects
# Initialize a store.
def initialize(path, branch = 'master', bare = false)
@@ -59,14 +69,10 @@ def initialize(path, branch = 'master', bare = false)
@branch = branch
@root = Tree.new(self)
@packs = {}
+ @objects = {}
init_handler
- name = IO.popen("git config user.name") { |io| io.gets.chomp }
- email = IO.popen("git config user.email") { |io| io.gets.chomp }
-
- @user = "#{name} <#{email}>"
-
load_packs("#{git_path}/objects/pack")
load
end
@@ -157,7 +163,7 @@ def changed?
def load(from_disk = false)
if id = read_head_id
@head = get(id)
- @root = get(@head.tree)
+ @root = @head.tree
end
load_from_disk if from_disk
@@ -218,6 +224,7 @@ def start_transaction
#
# Any changes made to the store are discarded.
def rollback
+ objects.clear
load
finish_transaction
end
@@ -232,16 +239,14 @@ def finish_transaction
File.unlink("#{head_path}.lock") rescue nil
end
- def user_info(user, time)
- "#{ user } #{ time.to_i } #{ time.to_s.split[4] }"
- end
-
# Write the commit object to disk and set the head of the current branch.
#
# Returns the id of the commit object
- def commit(message = '', author = "#{user_info user, Time.now}", committer = "#{user_info user, Time.now}")
+ def commit(message = '', author = User.from_config, committer = author)
+ root.write
+
commit = Commit.new(self)
- commit.tree = root.write
+ commit.tree = root
commit.parent << head.id if head
commit.author = author
commit.committer = committer
@@ -269,10 +274,24 @@ def commits(limit = 10, start = head)
def get(id)
return nil if id.nil?
+
+ return objects[id] if objects.has_key?(id)
+
type, content = get_object(id)
klass = TYPE_CLASS[type] or raise NotImplementedError, "type not supported: #{type}"
- klass.new(self, id, content)
+
+ objects[id] = klass.new(self, id, content)
+ end
+
+ def put(object)
+ type = CLASS_TYPE[object.class] or raise NotImplementedError, "class not supported: #{object.class}"
+
+ id = put_object(type, object.dump)
+
+ objects[id] = object
+
+ id
end
# Returns the hash value of an object string.
@@ -320,7 +339,7 @@ def put_object(type, content)
f.write Zlib::Deflate.deflate(data)
end
end
-
+
id
end
View
10 lib/git_store/blob.rb
@@ -14,9 +14,17 @@ def initialize(store, id = nil, data = nil)
@mode = "100644"
end
+ def ==(other)
+ Blob === other and id == other.id
+ end
+
+ def dump
+ @data
+ end
+
# Write the data to the git object store
def write
- @id = store.put_object('blob', data)
+ @id = store.put(self)
end
end
View
43 lib/git_store/commit.rb
@@ -1,9 +1,7 @@
class GitStore
class Commit
- attr_accessor :store, :id, :data, :author, :committer, :tree, :parent, :message, :headers
- attr_reader :author_name, :author_email, :author_time
- attr_reader :committer_name, :committer_email, :committer_time
+ attr_accessor :store, :id, :tree, :parent, :author, :committer, :message
def initialize(store, id = nil, data = nil)
@store = store
@@ -11,28 +9,29 @@ def initialize(store, id = nil, data = nil)
@parent = []
parse(data) if data
-
- @author_name, @author_email, @author_time = parse_user(author) if author
- @committer_name, @commiter_email, @committer_time = parse_user(committer) if committer
end
- def parse_user(user)
- if match = user.match(/(.*)<(.*)> (\d+) ([+-]\d+)/)
- [ match[1].chomp,
- match[2].chomp,
- Time.at(match[3].to_i)]
- end
- end
+ def ==(other)
+ Commit === other and id == other.id
+ end
def parse(data)
headers, @message = data.split(/\n\n/, 2)
headers.split(/\n/).each do |header|
key, value = header.split(/ /, 2)
- if key == 'parent'
+ case key
+ when 'parent'
@parent << value
- else
- instance_variable_set "@#{key}", value
+
+ when 'author'
+ @author = User.parse(value)
+
+ when 'committer'
+ @committer = User.parse(value)
+
+ when 'tree'
+ @tree = store.get(value)
end
end
@@ -49,16 +48,16 @@ def diffs(path = nil)
end
def write
- @id = store.put_object('commit', dump)
+ @id = store.put(self)
end
def dump
- [ "tree #@tree",
- @parent.map { |parent| "parent #{parent}" },
- "author #@author",
- "committer #@committer",
+ [ "tree #{ tree.id }",
+ parent.map { |parent| "parent #{parent}" },
+ "author #{ author.dump }",
+ "committer #{ committer.dump }",
'',
- @message ].flatten.join("\n")
+ message ].flatten.join("\n")
end
end
View
40 lib/git_store/tag.rb
@@ -0,0 +1,40 @@
+class GitStore
+
+ class Tag
+ attr_accessor :store, :id, :object, :type, :tagger, :message
+
+ def initialize(store, id = nil, data = nil)
+ @store = store
+ @id = id
+
+ parse(data) if data
+ end
+
+ def ==(other)
+ Tag === other and id == other.id
+ end
+
+ def parse(data)
+ headers, @message = data.split(/\n\n/, 2)
+
+ headers.split(/\n/).each do |header|
+ key, value = header.split(/ /, 2)
+ case key
+ when 'type'
+ @type = value
+
+ when 'object'
+ @object = store.get(value)
+
+ when 'tagger'
+ @tagger = User.parse(value)
+
+ end
+ end
+
+ self
+ end
+
+ end
+
+end
View
17 lib/git_store/tree.rb
@@ -15,6 +15,10 @@ def initialize(store, id = nil, data = nil)
parse(data) if data
end
+ def ==(other)
+ Tree === other and id == other.id
+ end
+
# Has this tree been modified?
def modified?
@modified or @table.values.any? { |entry| Tree === entry and entry.modified? }
@@ -37,19 +41,18 @@ def parse(data)
@table[name] = store.get(id)
end
end
+
+ def dump
+ @table.map { |k, v| "#{ v.mode } #{ k }\0#{ [v.write].pack("H*") }" }.join
+ end
# Write this tree back to the git repository.
#
# Returns the object id of the tree.
def write
- return id if not modified?
-
- contents = @table.map do |name, entry|
- "#{ entry.mode } #{ name }\0#{ [entry.write].pack("H*") }"
- end
-
+ return id if not modified?
@modified = false
- @id = store.put_object('tree', contents.join)
+ @id = store.put(self)
end
# Read entry with specified name.
View
29 lib/git_store/user.rb
@@ -0,0 +1,29 @@
+class GitStore
+
+ class User
+ attr_accessor :name, :email, :time
+
+ def initialize(name, email, time)
+ @name, @email, @time = name, email, time
+ end
+
+ def dump
+ "#{ name } <#{email}> #{ time.to_i } #{ time.strftime('%z') }"
+ end
+
+ def self.from_config
+ name = IO.popen("git config user.name") { |io| io.gets.chomp }
+ email = IO.popen("git config user.email") { |io| io.gets.chomp }
+
+ new name, email, Time.now
+ end
+
+ def self.parse(user)
+ if match = user.match(/(.*)<(.*)> (\d+) ([+-]\d+)/)
+ new match[1].strip, match[2].strip, Time.at(match[3].to_i + match[4].to_i * 3600)
+ end
+ end
+
+ end
+
+end
View
33 test/bar_store_spec.rb
@@ -0,0 +1,33 @@
+require "#{File.dirname(__FILE__)}/../lib/git_store"
+require "#{File.dirname(__FILE__)}/helper"
+require 'pp'
+
+describe GitStore do
+
+ REPO = '/tmp/git_store_test.git'
+
+ attr_reader :store
+
+ before(:each) do
+ FileUtils.rm_rf REPO
+ Dir.mkdir REPO
+ Dir.chdir REPO
+
+ `git init --bare`
+ @store = GitStore.new(REPO, 'master', true)
+ end
+
+ it 'should fail to initialize without a valid git repository' do
+ lambda {
+ GitStore.new('/foo', 'master', true)
+ }.should raise_error(ArgumentError)
+ end
+
+ it 'should save and load entries' do
+ store['a'] = 'Hello'
+ store.commit
+ store.load
+
+ store['a'].should == 'Hello'
+ end
+end
View
13 test/commit_spec.rb
@@ -16,25 +16,24 @@
end
it "should dump in right format" do
- time = Time.now
- timestamp = "#{ time.to_i } #{ time.to_s.split[4] }"
+ user = GitStore::User.new("hanni", "hanni@email.de", Time.now)
commit = GitStore::Commit.new(nil)
commit.tree = @store.root.id
- commit.author = "hanni #{timestamp}"
- commit.committer = "hanni #{timestamp}"
+ commit.author = user
+ commit.committer = user
commit.message = "This is a message"
commit.dump.should == "tree #{@store.root.id}
-author hanni #{timestamp}
-committer hanni #{timestamp}
+author #{user.dump}
+committer #{user.dump}
This is a message"
end
it "should be readable by git binary" do
time = Time.local(2009, 4, 20)
- author = store.user_info("hans <hans@email.de>", time)
+ author = GitStore::User.new("hans", "hans@email.de", time)
store['a'] = "Yay"
commit = store.commit("Commit Message", author, author)
View
47 test/git_store_spec.rb
@@ -88,7 +88,7 @@ def file(file, data)
end
store.load
-
+
store['x/a'].should == 'a'
store.transaction do
@@ -236,39 +236,22 @@ def file(file, data)
store.commits[1].message.should == 'added a'
end
- describe 'use bare repository' do
-
- BARE_REPO = '/tmp/git_store_test.git'
-
- attr_reader :bare_store
-
- before(:each) do
- FileUtils.rm_rf BARE_REPO
- Dir.mkdir BARE_REPO
- Dir.chdir BARE_REPO
-
- `git init --bare`
- @bare_store = GitStore.new(BARE_REPO, 'master', true)
- end
-
- it 'should fail to initialize without a valid git repository' do
- lambda {
- GitStore.new('/foo', 'master', true)
- }.should raise_error(ArgumentError)
- end
-
- it 'should find modified entries' do
- bare_store['a'] = 'Hello'
-
- bare_store.root.should be_modified
-
- bare_store.commit
+ it "should load tags" do
+ file 'a', 'init'
+
+ `git tag -m 'message' 0.1`
- bare_store.root.should_not be_modified
+ store.load
- bare_store['a'] = 'Bello'
+ user = GitStore::User.from_config
+ id = File.read('.git/refs/tags/0.1')
+ tag = store.get(id)
- bare_store.root.should be_modified
- end
+ tag.type.should == 'commit'
+ tag.object.should == store.head
+ tag.tagger.name.should == user.name
+ tag.tagger.email.should == user.email
+ tag.message.should =~ /message/
end
+
end

0 comments on commit 97ed500

Please sign in to comment.
Something went wrong with that request. Please try again.