georgi-git_store 0.2.4 → 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +30 -112
- data/git_store.gemspec +5 -1
- data/lib/git_store.rb +137 -118
- data/lib/git_store/blob.rb +9 -49
- data/lib/git_store/commit.rb +66 -0
- data/lib/git_store/diff.rb +76 -0
- data/lib/git_store/handlers.rb +4 -25
- data/lib/git_store/tree.rb +89 -117
- data/test/benchmark.rb +2 -2
- data/test/commit_spec.rb +82 -0
- data/test/git_store_spec.rb +170 -141
- data/test/tree_spec.rb +92 -0
- metadata +5 -1
data/test/benchmark.rb
CHANGED
@@ -3,7 +3,7 @@ require 'grit'
|
|
3
3
|
require 'benchmark'
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
|
-
REPO =
|
6
|
+
REPO = '/tmp/git-store'
|
7
7
|
|
8
8
|
FileUtils.rm_rf REPO
|
9
9
|
FileUtils.mkpath REPO
|
@@ -21,7 +21,7 @@ Benchmark.bm 20 do |x|
|
|
21
21
|
store.transaction { store['aa'] = rand.to_s }
|
22
22
|
end
|
23
23
|
x.report 'load 1000 objects' do
|
24
|
-
GitStore.new('.')
|
24
|
+
GitStore.new('.').values { |v| v }
|
25
25
|
end
|
26
26
|
x.report 'load 1000 with grit' do
|
27
27
|
Grit::Repo.new('.').tree.contents.each { |e| e.data }
|
data/test/commit_spec.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../lib/git_store"
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
describe GitStore::Commit do
|
5
|
+
|
6
|
+
REPO = '/tmp/git_store_test'
|
7
|
+
|
8
|
+
attr_reader :store
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
FileUtils.rm_rf REPO
|
12
|
+
Dir.mkdir REPO
|
13
|
+
Dir.chdir REPO
|
14
|
+
`git init`
|
15
|
+
@store = GitStore.new(REPO)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should dump in right format" do
|
19
|
+
time = Time.now
|
20
|
+
timestamp = "#{ time.to_i } #{ time.to_s.split[4] }"
|
21
|
+
|
22
|
+
commit = GitStore::Commit.new(nil)
|
23
|
+
commit.tree = @store.root.id
|
24
|
+
commit.author = "hanni #{timestamp}"
|
25
|
+
commit.committer = "hanni #{timestamp}"
|
26
|
+
commit.message = "This is a message"
|
27
|
+
|
28
|
+
commit.dump.should == "tree #{@store.root.id}
|
29
|
+
author hanni #{timestamp}
|
30
|
+
committer hanni #{timestamp}
|
31
|
+
|
32
|
+
This is a message"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be readable by git binary" do
|
36
|
+
time = Time.local(2009, 4, 20)
|
37
|
+
author = store.user_info("hans <hans@email.de>", time)
|
38
|
+
|
39
|
+
store['a'] = "Yay"
|
40
|
+
commit = store.commit("Commit Message", author, author)
|
41
|
+
|
42
|
+
IO.popen("git log") do |io|
|
43
|
+
io.gets.should == "commit #{commit.id}\n"
|
44
|
+
io.gets.should == "Author: hans <hans@email.de>\n"
|
45
|
+
io.gets.should == "Date: Mon Apr 20 00:00:00 2009 +0200\n"
|
46
|
+
io.gets.should == "\n"
|
47
|
+
io.gets.should == " Commit Message\n"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should diff 2 commits" do
|
52
|
+
store['x'] = 'a'
|
53
|
+
store['y'] = "
|
54
|
+
First Line.
|
55
|
+
Second Line.
|
56
|
+
Last Line.
|
57
|
+
"
|
58
|
+
a = store.commit
|
59
|
+
|
60
|
+
store.delete('x')
|
61
|
+
store['y'] = "
|
62
|
+
First Line.
|
63
|
+
Last Line.
|
64
|
+
Another Line.
|
65
|
+
"
|
66
|
+
store['z'] = 'c'
|
67
|
+
|
68
|
+
b = store.commit
|
69
|
+
|
70
|
+
diff = b.diff(a)
|
71
|
+
|
72
|
+
diff[0].a_path.should == 'x'
|
73
|
+
diff[0].deleted_file.should be_true
|
74
|
+
|
75
|
+
diff[1].a_path.should == 'y'
|
76
|
+
diff[1].diff.should == "--- a/y\n+++ b/y\n@@ -1,4 +1,4 @@\n \n First Line.\n-Second Line.\n Last Line.\n+Another Line."
|
77
|
+
|
78
|
+
diff[2].a_path.should == 'z'
|
79
|
+
diff[2].new_file.should be_true
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/test/git_store_spec.rb
CHANGED
@@ -1,210 +1,239 @@
|
|
1
1
|
require "#{File.dirname(__FILE__)}/../lib/git_store"
|
2
|
+
require "#{File.dirname(__FILE__)}/helper"
|
3
|
+
require 'pp'
|
2
4
|
|
3
5
|
describe GitStore do
|
4
6
|
|
5
|
-
#REPO = File.expand_path(File.dirname(__FILE__) + '/repo')
|
6
7
|
REPO = '/tmp/git_store_test'
|
7
8
|
|
9
|
+
attr_reader :store
|
10
|
+
|
8
11
|
before(:each) do
|
9
12
|
FileUtils.rm_rf REPO
|
10
13
|
Dir.mkdir REPO
|
11
14
|
Dir.chdir REPO
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@store
|
15
|
+
|
16
|
+
`git init`
|
17
|
+
@store = GitStore.new(REPO)
|
16
18
|
end
|
17
19
|
|
18
20
|
def file(file, data)
|
19
21
|
FileUtils.mkpath(File.dirname(file))
|
20
22
|
open(file, 'w') { |io| io << data }
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
|
24
|
+
`git add #{file}`
|
25
|
+
`git commit -m 'added #{file}'`
|
26
|
+
File.unlink(file)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should fail to initialize without a valid git repository' do
|
30
|
+
lambda {
|
31
|
+
GitStore.new('/')
|
32
|
+
}.should raise_error(ArgumentError)
|
26
33
|
end
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
store['a'] = 'Hello'
|
35
|
+
it 'should find modified entries' do
|
36
|
+
store['a'] = 'Hello'
|
31
37
|
|
32
|
-
|
33
|
-
store.root.table['a'].should be_modified
|
38
|
+
store.root.should be_modified
|
34
39
|
|
35
|
-
|
40
|
+
store.commit
|
41
|
+
|
42
|
+
store.root.should_not be_modified
|
36
43
|
|
37
|
-
|
44
|
+
store['a'] = 'Bello'
|
38
45
|
|
39
|
-
|
40
|
-
|
46
|
+
store.root.should be_modified
|
47
|
+
end
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
it 'should load a repo' do
|
50
|
+
file 'a', 'Hello'
|
51
|
+
file 'b', 'World'
|
52
|
+
|
53
|
+
store.load
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
55
|
+
store['a'].should == 'Hello'
|
56
|
+
store['b'].should == 'World'
|
57
|
+
end
|
51
58
|
|
52
|
-
|
53
|
-
|
54
|
-
|
59
|
+
it 'should load folders' do
|
60
|
+
file 'x/a', 'Hello'
|
61
|
+
file 'y/b', 'World'
|
55
62
|
|
56
|
-
|
57
|
-
|
58
|
-
|
63
|
+
store.load
|
64
|
+
store['x'].should be_kind_of(GitStore::Tree)
|
65
|
+
store['y'].should be_kind_of(GitStore::Tree)
|
59
66
|
|
60
|
-
|
61
|
-
|
62
|
-
|
67
|
+
store['x']['a'].should == 'Hello'
|
68
|
+
store['y']['b'].should == 'World'
|
69
|
+
end
|
63
70
|
|
64
|
-
|
65
|
-
|
71
|
+
it 'should load yaml' do
|
72
|
+
file 'x/a.yml', '[1, 2, 3, 4]'
|
66
73
|
|
67
|
-
|
74
|
+
store.load
|
68
75
|
|
69
|
-
|
70
|
-
|
71
|
-
|
76
|
+
store['x']['a.yml'].should == [1,2,3,4]
|
77
|
+
store['x']['a.yml'] = [1,2,3,4,5]
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should save yaml' do
|
81
|
+
store['x/a.yml'] = [1,2,3,4,5]
|
82
|
+
store['x/a.yml'].should == [1,2,3,4,5]
|
83
|
+
end
|
72
84
|
|
73
|
-
|
74
|
-
|
75
|
-
|
85
|
+
it 'should detect modification' do
|
86
|
+
store.transaction do
|
87
|
+
store['x/a'] = 'a'
|
88
|
+
end
|
76
89
|
|
77
|
-
|
90
|
+
store.load
|
78
91
|
|
79
|
-
|
80
|
-
store['y/b'].should == 'World'
|
81
|
-
|
82
|
-
store['y/b'] = 'Now this'
|
92
|
+
store['x/a'].should == 'a'
|
83
93
|
|
84
|
-
|
94
|
+
store.transaction do
|
95
|
+
store['x/a'] = 'b'
|
96
|
+
store['x'].should be_modified
|
97
|
+
store.root.should be_modified
|
85
98
|
end
|
86
99
|
|
87
|
-
|
88
|
-
store['new/tree'] = 'This tree'
|
89
|
-
store['this', 'tree'] = 'Another'
|
90
|
-
store['new/tree'].should == 'This tree'
|
91
|
-
store['this/tree'].should == 'Another'
|
92
|
-
end
|
100
|
+
store.load
|
93
101
|
|
94
|
-
|
95
|
-
store['a'] = 'Hello'
|
96
|
-
store.delete('a')
|
97
|
-
|
98
|
-
store['a'].should be_nil
|
99
|
-
end
|
102
|
+
store['x/a'].should == 'b'
|
100
103
|
end
|
101
104
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
105
|
+
it 'should resolve paths' do
|
106
|
+
file 'x/a', 'Hello'
|
107
|
+
file 'y/b', 'World'
|
108
|
+
|
109
|
+
store.load
|
110
|
+
|
111
|
+
store['x/a'].should == 'Hello'
|
112
|
+
store['y/b'].should == 'World'
|
108
113
|
|
109
|
-
|
114
|
+
store['y/b'] = 'Now this'
|
110
115
|
|
111
|
-
|
112
|
-
|
116
|
+
store['y']['b'].should == 'Now this'
|
117
|
+
end
|
113
118
|
|
114
|
-
|
115
|
-
|
116
|
-
|
119
|
+
it 'should create new trees' do
|
120
|
+
store['new/tree'] = 'This tree'
|
121
|
+
store['new/tree'].should == 'This tree'
|
122
|
+
end
|
117
123
|
|
118
|
-
|
119
|
-
|
124
|
+
it 'should delete entries' do
|
125
|
+
store['a'] = 'Hello'
|
126
|
+
store.delete('a')
|
127
|
+
|
128
|
+
store['a'].should be_nil
|
129
|
+
end
|
120
130
|
|
121
|
-
|
122
|
-
|
131
|
+
it 'should have a head commit' do
|
132
|
+
file 'a', 'Hello'
|
123
133
|
|
124
|
-
|
125
|
-
|
126
|
-
|
134
|
+
store.load
|
135
|
+
store.head.should_not be_nil
|
136
|
+
end
|
127
137
|
|
128
|
-
|
129
|
-
|
130
|
-
store['a/b'] = 'Changed'
|
131
|
-
store['x/a'] = 'Added'
|
132
|
-
raise
|
133
|
-
end
|
134
|
-
rescue
|
135
|
-
end
|
138
|
+
it 'should detect changes' do
|
139
|
+
file 'a', 'Hello'
|
136
140
|
|
137
|
-
|
138
|
-
|
139
|
-
store['x/a'].should be_nil
|
140
|
-
end
|
141
|
+
store.should be_changed
|
142
|
+
end
|
141
143
|
|
142
|
-
|
143
|
-
|
144
|
-
|
144
|
+
it 'should rollback a transaction' do
|
145
|
+
file 'a/b', 'Hello'
|
146
|
+
file 'c/d', 'World'
|
145
147
|
|
148
|
+
begin
|
146
149
|
store.transaction do
|
147
150
|
store['a/b'] = 'Changed'
|
148
151
|
store['x/a'] = 'Added'
|
152
|
+
raise
|
149
153
|
end
|
154
|
+
rescue
|
155
|
+
end
|
150
156
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
157
|
+
store['a/b'].should == 'Hello'
|
158
|
+
store['c/d'].should == 'World'
|
159
|
+
store['x/a'].should be_nil
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should commit a transaction' do
|
163
|
+
file 'a/b', 'Hello'
|
164
|
+
file 'c/d', 'World'
|
165
|
+
|
166
|
+
store.transaction do
|
167
|
+
store['a/b'] = 'Changed'
|
168
|
+
store['x/a'] = 'Added'
|
156
169
|
end
|
157
170
|
|
158
|
-
|
159
|
-
|
171
|
+
a = git_ls_tree(store['a'].id)
|
172
|
+
x = git_ls_tree(store['x'].id)
|
160
173
|
|
161
|
-
|
174
|
+
a.should == [["100644", "blob", "b653cf27cef08de46da49a11fa5016421e9e3b32", "b"]]
|
175
|
+
x.should == [["100644", "blob", "87d2b203800386b1cc8735a7d540a33e246357fa", "a"]]
|
162
176
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
sleep 0.01 until ready
|
177
|
+
git_show(a[0][2]).should == 'Changed'
|
178
|
+
git_show(x[0][2]).should == 'Added'
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should save blobs" do
|
182
|
+
store['a'] = 'a'
|
183
|
+
store['b'] = 'b'
|
184
|
+
store['c'] = 'c'
|
174
185
|
|
175
|
-
|
176
|
-
|
177
|
-
store['a/b'].should == 'Changed by second thread'
|
178
|
-
end
|
186
|
+
store.commit
|
179
187
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
store['d'] = 'World'
|
184
|
-
store.commit
|
188
|
+
a = store.id_for('blob', 'a')
|
189
|
+
b = store.id_for('blob', 'b')
|
190
|
+
c = store.id_for('blob', 'c')
|
185
191
|
|
186
|
-
|
192
|
+
git_show(a).should == 'a'
|
193
|
+
git_show(b).should == 'b'
|
194
|
+
git_show(c).should == 'c'
|
195
|
+
end
|
187
196
|
|
188
|
-
|
189
|
-
|
197
|
+
it 'should allow only one transaction' do
|
198
|
+
file 'a/b', 'Hello'
|
190
199
|
|
191
|
-
|
192
|
-
File.read('d').should == 'World'
|
193
|
-
end
|
194
|
-
end
|
200
|
+
ready = false
|
195
201
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
202
|
+
store.transaction do
|
203
|
+
Thread.start do
|
204
|
+
store.transaction do
|
205
|
+
store['a/b'] = 'Changed by second thread'
|
206
|
+
end
|
207
|
+
ready = true
|
208
|
+
end
|
209
|
+
store['a/b'] = 'Changed'
|
200
210
|
end
|
201
211
|
|
202
|
-
|
212
|
+
sleep 0.01 until ready
|
213
|
+
|
214
|
+
store.load
|
215
|
+
|
216
|
+
store['a/b'].should == 'Changed by second thread'
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should find all objects' do
|
220
|
+
store.load
|
221
|
+
store['c'] = 'Hello'
|
222
|
+
store['d'] = 'World'
|
223
|
+
store.commit
|
224
|
+
|
225
|
+
store.to_a.should == [['c', 'Hello'], ['d', 'World']]
|
203
226
|
end
|
204
227
|
|
205
|
-
it
|
206
|
-
|
207
|
-
|
208
|
-
|
228
|
+
it "should load commits" do
|
229
|
+
store['a'] = 'a'
|
230
|
+
store.commit 'added a'
|
231
|
+
|
232
|
+
store['b'] = 'b'
|
233
|
+
store.commit 'added b'
|
234
|
+
|
235
|
+
store.commits[0].message.should == 'added b'
|
236
|
+
store.commits[1].message.should == 'added a'
|
209
237
|
end
|
238
|
+
|
210
239
|
end
|