git-ds 0.9.2
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.rdoc +795 -0
- data/doc/Examples.rdoc +36 -0
- data/doc/examples/key_value/kv_get.rb +29 -0
- data/doc/examples/key_value/kv_init.rb +20 -0
- data/doc/examples/key_value/kv_list.rb +28 -0
- data/doc/examples/key_value/kv_remove.rb +29 -0
- data/doc/examples/key_value/kv_set.rb +39 -0
- data/doc/examples/key_value/model.rb +156 -0
- data/doc/examples/key_value/test.rb +50 -0
- data/doc/examples/test_suite/model.rb +503 -0
- data/doc/examples/test_suite/test.rb +173 -0
- data/doc/examples/test_suite/ts_add_bug.rb +65 -0
- data/doc/examples/test_suite/ts_add_module.rb +74 -0
- data/doc/examples/test_suite/ts_add_module_to_test.rb +78 -0
- data/doc/examples/test_suite/ts_add_test.rb +77 -0
- data/doc/examples/test_suite/ts_add_test_suite.rb +65 -0
- data/doc/examples/test_suite/ts_add_test_to_bug.rb +76 -0
- data/doc/examples/test_suite/ts_init.rb +20 -0
- data/doc/examples/test_suite/ts_list.rb +118 -0
- data/doc/examples/test_suite/ts_perform_test.rb +104 -0
- data/doc/examples/test_suite/ts_update_bugs.rb +58 -0
- data/doc/examples/user_group/model.rb +265 -0
- data/doc/examples/user_group/test.rb +64 -0
- data/doc/examples/user_group/ug_add_group.rb +39 -0
- data/doc/examples/user_group/ug_add_group_user.rb +36 -0
- data/doc/examples/user_group/ug_add_user.rb +39 -0
- data/doc/examples/user_group/ug_init.rb +20 -0
- data/doc/examples/user_group/ug_list.rb +32 -0
- data/lib/git-ds.rb +14 -0
- data/lib/git-ds/config.rb +53 -0
- data/lib/git-ds/database.rb +289 -0
- data/lib/git-ds/exec_cmd.rb +107 -0
- data/lib/git-ds/index.rb +205 -0
- data/lib/git-ds/model.rb +136 -0
- data/lib/git-ds/model/db_item.rb +42 -0
- data/lib/git-ds/model/fs_item.rb +51 -0
- data/lib/git-ds/model/item.rb +428 -0
- data/lib/git-ds/model/item_list.rb +97 -0
- data/lib/git-ds/model/item_proxy.rb +128 -0
- data/lib/git-ds/model/property.rb +144 -0
- data/lib/git-ds/model/root.rb +46 -0
- data/lib/git-ds/repo.rb +455 -0
- data/lib/git-ds/shared.rb +17 -0
- data/lib/git-ds/transaction.rb +77 -0
- data/tests/ut_database.rb +304 -0
- data/tests/ut_git_grit_equiv.rb +195 -0
- data/tests/ut_index.rb +203 -0
- data/tests/ut_model.rb +360 -0
- data/tests/ut_repo.rb +260 -0
- data/tests/ut_user_group_model.rb +316 -0
- metadata +142 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Initialize a user/group database repo
|
3
|
+
# Copyright 2011 Thoughtgang <http://www.thoughtgang.org>
|
4
|
+
|
5
|
+
BASE=File.dirname(File.expand_path(__FILE__)\
|
6
|
+
).split(File::SEPARATOR)[0..-4].join(File::SEPARATOR)
|
7
|
+
$: << BASE + File::SEPARATOR + 'lib'
|
8
|
+
$: << BASE + File::SEPARATOR + 'doc' + File::SEPARATOR + 'examples'
|
9
|
+
|
10
|
+
require 'user_group/model'
|
11
|
+
|
12
|
+
if __FILE__ == $0
|
13
|
+
if ARGV.count != 1
|
14
|
+
puts "Usage : #{$0} FILENAME"
|
15
|
+
exit -1
|
16
|
+
end
|
17
|
+
|
18
|
+
UserGroupModel.new(GitDS::Database.connect(ARGV.shift, true))
|
19
|
+
exit 0
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# List User/Group database contents
|
3
|
+
# Copyright 2011 Thoughtgang <http://www.thoughtgang.org>
|
4
|
+
|
5
|
+
BASE=File.dirname(File.expand_path(__FILE__)\
|
6
|
+
).split(File::SEPARATOR)[0..-4].join(File::SEPARATOR)
|
7
|
+
$: << BASE + File::SEPARATOR + 'lib'
|
8
|
+
$: << BASE + File::SEPARATOR + 'doc' + File::SEPARATOR + 'examples'
|
9
|
+
|
10
|
+
require 'user_group/model'
|
11
|
+
|
12
|
+
if __FILE__ == $0
|
13
|
+
|
14
|
+
path = ARGV.count == 1 ? ARGV.shift : GitDS::Database.top_level
|
15
|
+
model = UserGroupModel.new(GitDS::Database.connect(path, false))
|
16
|
+
raise "Could not connect to model" if not model
|
17
|
+
|
18
|
+
puts "Users:"
|
19
|
+
model.users.each do |user|
|
20
|
+
u = model.user(user)
|
21
|
+
puts "\t#{u.id}\t#{u.username}\t#{u.created}\t#{u.full_name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "\nGroups:"
|
25
|
+
model.groups.each do |grp|
|
26
|
+
puts "\t#{grp}"
|
27
|
+
g = model.group(grp)
|
28
|
+
puts "\t#{g.id}\t#{g.name}\t#{g.owner.username}\t#{g.users.inspect}"
|
29
|
+
end
|
30
|
+
|
31
|
+
exit 0
|
32
|
+
end
|
data/lib/git-ds.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# :title: GitDS
|
3
|
+
=begin rdoc
|
4
|
+
<i>Copyright 2011 Thoughtgang <http://www.thoughtgang.org></i>
|
5
|
+
=end
|
6
|
+
|
7
|
+
require 'git-ds/shared'
|
8
|
+
require 'git-ds/index'
|
9
|
+
require 'git-ds/repo'
|
10
|
+
require 'git-ds/config'
|
11
|
+
require 'git-ds/database'
|
12
|
+
require 'git-ds/exec_cmd'
|
13
|
+
require 'git-ds/transaction'
|
14
|
+
require 'git-ds/model'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# :title: Git-DS::RepoConfig
|
3
|
+
=begin rdoc
|
4
|
+
|
5
|
+
Copyright 2011 Thoughtgang <http://www.thoughtgang.org>
|
6
|
+
=end
|
7
|
+
|
8
|
+
module GitDS
|
9
|
+
|
10
|
+
=begin rdoc
|
11
|
+
Provides access to the repo .git/config file as a hash.
|
12
|
+
|
13
|
+
Note that this limits access to a specific section of the config file,
|
14
|
+
named by the parameter 'section'.
|
15
|
+
=end
|
16
|
+
class RepoConfig
|
17
|
+
|
18
|
+
def initialize(db, section='misc')
|
19
|
+
@db = db
|
20
|
+
@section = clean(section)
|
21
|
+
end
|
22
|
+
|
23
|
+
=begin rdoc
|
24
|
+
Clean key so it is a valid Config token
|
25
|
+
=end
|
26
|
+
def clean(str)
|
27
|
+
str.gsub(/[^-[:alnum:]]/, '-')
|
28
|
+
end
|
29
|
+
|
30
|
+
=begin rdoc
|
31
|
+
Return the full path to the variable for 'key'.
|
32
|
+
=end
|
33
|
+
def path(key)
|
34
|
+
@section + '.' + clean(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
=begin rdoc
|
38
|
+
Return the String value of the variable 'key'.
|
39
|
+
=end
|
40
|
+
def [](key)
|
41
|
+
rv = @db.repo_config[path(key)]
|
42
|
+
rv ? rv : ''
|
43
|
+
end
|
44
|
+
|
45
|
+
=begin rdoc
|
46
|
+
Writes the String representation of 'value' to the variable 'key'.
|
47
|
+
=end
|
48
|
+
def []=(key, value)
|
49
|
+
@db.repo_config[path(key)] = value.to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,289 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# :title: Git-DS::Database
|
3
|
+
=begin rdoc
|
4
|
+
|
5
|
+
Copyright 2011 Thoughtgang <http://www.thoughtgang.org>
|
6
|
+
=end
|
7
|
+
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
require 'git-ds/repo'
|
11
|
+
require 'git-ds/config'
|
12
|
+
require 'git-ds/model'
|
13
|
+
require 'git-ds/shared'
|
14
|
+
require 'git-ds/exec_cmd'
|
15
|
+
require 'git-ds/transaction'
|
16
|
+
|
17
|
+
module GitDS
|
18
|
+
|
19
|
+
=begin rdoc
|
20
|
+
Exception raised when a closed database is accessed
|
21
|
+
=end
|
22
|
+
class InvalidDbError < RuntimeError
|
23
|
+
end
|
24
|
+
|
25
|
+
=begin rdoc
|
26
|
+
Actually DbConnection to the repository.
|
27
|
+
|
28
|
+
Note: all operations should be in exec or transaction blocks. These use a
|
29
|
+
persistent staging index, and are more efficient.
|
30
|
+
=end
|
31
|
+
class Database < Repo
|
32
|
+
|
33
|
+
=begin rdoc
|
34
|
+
Flag to mark if database has been closed (i.e. connection is invalid).
|
35
|
+
=end
|
36
|
+
attr_reader :stale
|
37
|
+
|
38
|
+
=begin rdoc
|
39
|
+
Actor to use when performing Database operations. All Transaction and
|
40
|
+
ExecCmd objects will use this actor by default.
|
41
|
+
|
42
|
+
Default is nil (i.e. let Git read actor from .git/config or ENV).
|
43
|
+
=end
|
44
|
+
attr_accessor :actor
|
45
|
+
|
46
|
+
=begin rdoc
|
47
|
+
Return a connection to the Git DB.
|
48
|
+
Creates the DB if it does not already exist.
|
49
|
+
=end
|
50
|
+
def initialize(path, username=nil, email=nil)
|
51
|
+
init = false
|
52
|
+
if not File.exist? path
|
53
|
+
Repo.create(path)
|
54
|
+
init = true
|
55
|
+
end
|
56
|
+
|
57
|
+
@stale = false
|
58
|
+
super(path)
|
59
|
+
|
60
|
+
if init
|
61
|
+
# initial commit is needed for branches to work smoothly
|
62
|
+
stage { |idx| idx.add('.git-ds/version', "1.0\n") }
|
63
|
+
staging.commit('Database initialized.')
|
64
|
+
unstage
|
65
|
+
end
|
66
|
+
|
67
|
+
@actor = Grit::Actor.new(username, email) if username
|
68
|
+
end
|
69
|
+
|
70
|
+
=begin rdoc
|
71
|
+
Open a connection to database.
|
72
|
+
|
73
|
+
If 'create' is true (the default), a database will be created if one does
|
74
|
+
not exist at 'path'.
|
75
|
+
=end
|
76
|
+
def self.connect(path, create=true)
|
77
|
+
return nil if (not create) && (not File.exist? path)
|
78
|
+
connect_as(path, nil, nil, create)
|
79
|
+
end
|
80
|
+
|
81
|
+
=begin rdoc
|
82
|
+
Connect to a git database as the specified user.
|
83
|
+
=end
|
84
|
+
def self.connect_as(path, username, email, create=true)
|
85
|
+
return nil if (not create) && (not File.exist? path)
|
86
|
+
new(path, username, email)
|
87
|
+
end
|
88
|
+
|
89
|
+
=begin rdoc
|
90
|
+
Close DB connection, writing all changes to disk.
|
91
|
+
|
92
|
+
NOTE: This does not create a commit! Ony the staging index changes.
|
93
|
+
=end
|
94
|
+
def close(save=true)
|
95
|
+
raise InvalidDbError if @stale
|
96
|
+
|
97
|
+
if save && self.staging?
|
98
|
+
self.staging.write
|
99
|
+
end
|
100
|
+
self.staging = nil
|
101
|
+
@stale = true
|
102
|
+
|
103
|
+
# TODO: remove all locks etc
|
104
|
+
end
|
105
|
+
|
106
|
+
=begin rdoc
|
107
|
+
Delete Database (including entire repository) from disk.
|
108
|
+
=end
|
109
|
+
def purge
|
110
|
+
raise InvalidDbError if @stale
|
111
|
+
|
112
|
+
close(false)
|
113
|
+
FileUtils.remove_dir(@path) if ::File.exist?(@path)
|
114
|
+
end
|
115
|
+
|
116
|
+
=begin rdoc
|
117
|
+
Grit::Repo#config is wrapped by Database#config.
|
118
|
+
=end
|
119
|
+
alias :repo_config :config
|
120
|
+
|
121
|
+
=begin rdoc
|
122
|
+
Provides access to the Hash of Git-DS config variables.
|
123
|
+
=end
|
124
|
+
def config
|
125
|
+
@git_config ||= RepoConfig.new(self, 'git-ds')
|
126
|
+
end
|
127
|
+
|
128
|
+
=begin rdoc
|
129
|
+
Set the Git author information for the database connection. Wrapper for
|
130
|
+
actor=.
|
131
|
+
=end
|
132
|
+
def set_author(name, email=nil)
|
133
|
+
self.actor = name ? Grit::Actor.new(name, (email ? email : '')) : nil
|
134
|
+
end
|
135
|
+
|
136
|
+
=begin rdoc
|
137
|
+
Execute a block in the context of the staging index.
|
138
|
+
|
139
|
+
See ExecCmd.
|
140
|
+
=end
|
141
|
+
def exec(&block)
|
142
|
+
raise InvalidDbError if @stale
|
143
|
+
|
144
|
+
return exec_in_staging(true, &block) if self.staging?
|
145
|
+
|
146
|
+
begin
|
147
|
+
self.staging
|
148
|
+
exec_in_staging(false, &block)
|
149
|
+
self.staging.write
|
150
|
+
ensure
|
151
|
+
self.staging = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
=begin
|
157
|
+
Execute a transaction in the context of the staging index.
|
158
|
+
|
159
|
+
See Transaction.
|
160
|
+
=end
|
161
|
+
def transaction(&block)
|
162
|
+
raise InvalidDbError if @stale
|
163
|
+
|
164
|
+
return transaction_in_staging(true, &block) if self.staging?
|
165
|
+
|
166
|
+
begin
|
167
|
+
self.staging
|
168
|
+
transaction_in_staging(false, &block)
|
169
|
+
ensure
|
170
|
+
self.staging = nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
=begin rdoc
|
175
|
+
Add files to the database. Calls exec to ensure that a write is not performed
|
176
|
+
if a staging index already exists.
|
177
|
+
=end
|
178
|
+
def add(path, data='', on_fs=false)
|
179
|
+
exec { index.add(path, data, on_fs) }
|
180
|
+
end
|
181
|
+
|
182
|
+
=begin rdoc
|
183
|
+
Delete an object from the database.
|
184
|
+
=end
|
185
|
+
def delete(path)
|
186
|
+
exec { index.delete(path) }
|
187
|
+
end
|
188
|
+
|
189
|
+
=begin rdoc
|
190
|
+
Wrapper for Grit::Repo#index that checks if Database has been closed.
|
191
|
+
=end
|
192
|
+
def index_new
|
193
|
+
raise InvalidDbError if @stale
|
194
|
+
super
|
195
|
+
end
|
196
|
+
|
197
|
+
=begin rdoc
|
198
|
+
Wrapper for Grit::Repo#staging that checks if Database has been closed.
|
199
|
+
=end
|
200
|
+
def staging
|
201
|
+
raise InvalidDbError if @stale
|
202
|
+
super
|
203
|
+
end
|
204
|
+
|
205
|
+
=begin rdoc
|
206
|
+
Wrapper for Grit::Repo#head that checks if Database has been closed.
|
207
|
+
=end
|
208
|
+
def head
|
209
|
+
raise InvalidDbError if @stale
|
210
|
+
super
|
211
|
+
end
|
212
|
+
|
213
|
+
=begin rdoc
|
214
|
+
Wrapper for Grit::Repo#tree that checks if Database has been closed.
|
215
|
+
=end
|
216
|
+
def tree(treeish = 'master', paths = [])
|
217
|
+
raise InvalidDbError if @stale
|
218
|
+
super
|
219
|
+
end
|
220
|
+
|
221
|
+
=begin rdoc
|
222
|
+
Generate a tag object for the most recent commit.
|
223
|
+
=end
|
224
|
+
def mark(msg)
|
225
|
+
tag_object(msg, commits.first.id)
|
226
|
+
end
|
227
|
+
|
228
|
+
=begin rdoc
|
229
|
+
Branch-and-merge:
|
230
|
+
Run block in a transaction under a new branch. If the transaction succeeds,
|
231
|
+
the branch is merged back into master.
|
232
|
+
|
233
|
+
See Database#transaction .
|
234
|
+
=end
|
235
|
+
def branch_and_merge(name=next_branch_tag(), actor=nil, &block)
|
236
|
+
raise InvalidDbError if @stale
|
237
|
+
|
238
|
+
|
239
|
+
# save old actor
|
240
|
+
old_actor = self.actor
|
241
|
+
self.actor = actor if actor
|
242
|
+
|
243
|
+
# save old staging
|
244
|
+
saved_stage = (self.staging?) ? self.staging : nil
|
245
|
+
|
246
|
+
sha = commits.first ? commits.first.id : nil
|
247
|
+
tag = create_branch(name, sha)
|
248
|
+
set_branch(tag, self.actor)
|
249
|
+
|
250
|
+
# ensure staging index is nil [in case branch name was re-used]
|
251
|
+
unstage
|
252
|
+
|
253
|
+
# execute block in a transaction
|
254
|
+
rv = true
|
255
|
+
begin
|
256
|
+
transaction(&block)
|
257
|
+
merge_branch(tag, self.actor)
|
258
|
+
rescue Exception =>e
|
259
|
+
rv = false
|
260
|
+
end
|
261
|
+
|
262
|
+
# restore actor
|
263
|
+
self.actor = old_actor if actor
|
264
|
+
|
265
|
+
rv
|
266
|
+
end
|
267
|
+
|
268
|
+
private
|
269
|
+
|
270
|
+
=begin rdoc
|
271
|
+
Execute code block in context of current DB index
|
272
|
+
=end
|
273
|
+
def exec_in_staging(nested, &block)
|
274
|
+
cmd = ExecCmd.new(self.staging, nested, &block)
|
275
|
+
cmd.actor = self.actor
|
276
|
+
cmd.perform
|
277
|
+
end
|
278
|
+
|
279
|
+
=begin rdoc
|
280
|
+
Perform transaction in context of current DB index
|
281
|
+
=end
|
282
|
+
def transaction_in_staging(nested, &block)
|
283
|
+
t = Transaction.new(self.staging, nested, &block)
|
284
|
+
t.actor = self.actor
|
285
|
+
t.perform
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# :title: Git-DS::ExecCmd
|
3
|
+
=begin rdoc
|
4
|
+
|
5
|
+
Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
|
6
|
+
=end
|
7
|
+
|
8
|
+
require 'git-ds/shared'
|
9
|
+
|
10
|
+
module GitDS
|
11
|
+
|
12
|
+
=begin rdoc
|
13
|
+
A command to be executed in a database context (i.e. an Index).
|
14
|
+
|
15
|
+
Usage:
|
16
|
+
|
17
|
+
db.exec { |idx| ... }
|
18
|
+
|
19
|
+
See also Transaction.
|
20
|
+
=end
|
21
|
+
class ExecCmd
|
22
|
+
|
23
|
+
=begin rdoc
|
24
|
+
The GitDS::Index on which the command operates.
|
25
|
+
=end
|
26
|
+
attr_reader :index
|
27
|
+
=begin rdoc
|
28
|
+
The GitDS::Database on which the command operates. Useful for nesting.
|
29
|
+
=end
|
30
|
+
attr_reader :database
|
31
|
+
=begin rdoc
|
32
|
+
The message to use for the commit at the end of the command.
|
33
|
+
=end
|
34
|
+
attr_reader :commit_msg
|
35
|
+
DEFAULT_MESSAGE = 'auto-commit on transaction'
|
36
|
+
=begin rdoc
|
37
|
+
The Git author for the commit performed at the end of the command.
|
38
|
+
See commit_msg.
|
39
|
+
=end
|
40
|
+
attr_reader :commit_author
|
41
|
+
=begin rdoc
|
42
|
+
The body of the command.
|
43
|
+
=end
|
44
|
+
attr_reader :block
|
45
|
+
=begin rdoc
|
46
|
+
Is command nested (inside a parent)?
|
47
|
+
If true, a write and commit will not be performed.
|
48
|
+
=end
|
49
|
+
attr_reader :nested
|
50
|
+
|
51
|
+
def initialize(index, nested, msg=DEFAULT_MESSAGE, &block)
|
52
|
+
@index = index
|
53
|
+
@database = index.repo
|
54
|
+
@nested = nested
|
55
|
+
@block = block
|
56
|
+
# Default to no commit
|
57
|
+
@commit_msg = msg
|
58
|
+
# Default to config[user.name] and config[user.email]
|
59
|
+
@commit_author = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
=begin rdoc
|
63
|
+
Set a commit message for this command.
|
64
|
+
=end
|
65
|
+
def message(str)
|
66
|
+
@commit_msg = str
|
67
|
+
end
|
68
|
+
|
69
|
+
=begin rdoc
|
70
|
+
Set the Git Author info for the commit. By default, this information
|
71
|
+
is pulled from the Git config file.
|
72
|
+
=end
|
73
|
+
def author(name, email)
|
74
|
+
@commit_author = Grit::Actor.new(name, email)
|
75
|
+
end
|
76
|
+
|
77
|
+
=begin rdoc
|
78
|
+
Set actor for commit.
|
79
|
+
=end
|
80
|
+
def actor=(actor)
|
81
|
+
@commit_author = actor
|
82
|
+
end
|
83
|
+
|
84
|
+
=begin rdoc
|
85
|
+
Commit index.
|
86
|
+
=end
|
87
|
+
def commit
|
88
|
+
self.index.commit(@commit_msg, @commit_author)
|
89
|
+
end
|
90
|
+
|
91
|
+
=begin rdoc
|
92
|
+
Perform command.
|
93
|
+
=end
|
94
|
+
def perform
|
95
|
+
rv = instance_eval(&self.block)
|
96
|
+
|
97
|
+
#self.index.build
|
98
|
+
if not self.nested
|
99
|
+
self.index.build
|
100
|
+
commit
|
101
|
+
end
|
102
|
+
|
103
|
+
rv
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|