git-db 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +59 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/bin/git-db +17 -0
- data/features/git-db.feature +9 -0
- data/features/step_definitions/git-db_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/git-db.gemspec +91 -0
- data/lib/git-db/commands/receive-pack.rb +73 -0
- data/lib/git-db/commands/upload-pack.rb +140 -0
- data/lib/git-db/commands.rb +21 -0
- data/lib/git-db/database.rb +134 -0
- data/lib/git-db/objects/base.rb +34 -0
- data/lib/git-db/objects/blob.rb +11 -0
- data/lib/git-db/objects/commit.rb +49 -0
- data/lib/git-db/objects/entry.rb +27 -0
- data/lib/git-db/objects/tag.rb +7 -0
- data/lib/git-db/objects/tree.rb +45 -0
- data/lib/git-db/objects.rb +20 -0
- data/lib/git-db/pack.rb +182 -0
- data/lib/git-db/protocol.rb +73 -0
- data/lib/git-db/utility/counting_io.rb +24 -0
- data/lib/git-db/utility.rb +3 -0
- data/lib/git-db.rb +68 -0
- data/spec/git-db/commands_spec.rb +23 -0
- data/spec/git-db/objects/base_spec.rb +27 -0
- data/spec/git-db/objects/entry_spec.rb +25 -0
- data/spec/git-db/objects/tag_spec.rb +14 -0
- data/spec/git-db/objects/tree_spec.rb +42 -0
- data/spec/git-db/utility/counting_io_spec.rb +30 -0
- data/spec/git-db_spec.rb +45 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +11 -0
- metadata +118 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 David Dollar
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
= git-db
|
2
|
+
|
3
|
+
CouchDB-based git server, avoids the filesystem. (VERY ALPHA)
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
* Install CouchDB on <tt>localhost</tt>, and start it up.
|
8
|
+
|
9
|
+
* Install the gem
|
10
|
+
|
11
|
+
$ gem install ddollar-git-db
|
12
|
+
|
13
|
+
* Create a <tt>git</tt> user. (Name can be whatever you like)
|
14
|
+
|
15
|
+
* Set a home directory for the user.
|
16
|
+
|
17
|
+
* Set up the <tt>git</tt> user's authorized_keys2 file: (modify the command to match your gem particulars)
|
18
|
+
|
19
|
+
# $HOME/git/.ssh/authorized_keys2
|
20
|
+
command="/usr/bin/git-db",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty <your ssh public key>
|
21
|
+
|
22
|
+
* Add your localhost as a remote to an existing project and push
|
23
|
+
|
24
|
+
$ git remote add test-git-db git@localhost:my-repo.git
|
25
|
+
$ git push test-git-db master
|
26
|
+
|
27
|
+
* Go look at the data in CouchDB
|
28
|
+
|
29
|
+
http://127.0.0.1:5984/_utils/database.html?gitdb-my-repo
|
30
|
+
|
31
|
+
* Clone your repository somewhere else and examine it
|
32
|
+
|
33
|
+
$ git clone git@localhost:my-repo.git /tmp/my-repo
|
34
|
+
|
35
|
+
* Please report any problems on the issue tracker.
|
36
|
+
|
37
|
+
== Links
|
38
|
+
|
39
|
+
* Continuous Integration - http://runcoderun.com/ddollar/git-db
|
40
|
+
* Documentation - http://rdoc.info/projects/ddollar/git-db
|
41
|
+
|
42
|
+
== TODO
|
43
|
+
|
44
|
+
* Tests
|
45
|
+
* Refactor and clean up (experimenting with binary protocols can make things a bit messy)
|
46
|
+
* Authentication tie-in
|
47
|
+
* Never look at a raw git pack file again
|
48
|
+
|
49
|
+
== Note on Patches/Pull Requests
|
50
|
+
|
51
|
+
* Fork the project.
|
52
|
+
* Make your feature addition or bug fix.
|
53
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
54
|
+
* Commit, do not mess with rakefile, version, or history.
|
55
|
+
* Send me a pull request. Bonus points for topic branches.
|
56
|
+
|
57
|
+
== Copyright
|
58
|
+
|
59
|
+
Copyright (c) 2009 David Dollar. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "git-db"
|
8
|
+
gem.summary = %Q{Database-based git server}
|
9
|
+
gem.description = gem.summary
|
10
|
+
gem.email = "<ddollar@gmail.com>"
|
11
|
+
gem.homepage = "http://github.com/ddollar/git-db"
|
12
|
+
gem.authors = ["David Dollar"]
|
13
|
+
|
14
|
+
# development dependencies
|
15
|
+
gem.add_development_dependency "rspec"
|
16
|
+
|
17
|
+
# runtime dependencies
|
18
|
+
gem.add_dependency "couchrest"
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
spec.spec_opts << '--colour --format specdoc'
|
30
|
+
end
|
31
|
+
|
32
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
33
|
+
spec.libs << 'lib' << 'spec'
|
34
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
35
|
+
spec.rcov = true
|
36
|
+
spec.rcov_opts = lambda do
|
37
|
+
IO.readlines("#{File.dirname(__FILE__)}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :spec => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.2
|
data/bin/git-db
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'git-db'
|
4
|
+
include GitDB
|
5
|
+
|
6
|
+
GitDB.log(ENV.inspect)
|
7
|
+
|
8
|
+
git_command = ENV['SSH_ORIGINAL_COMMAND'].match(/git-(.+) '(.+)'/)
|
9
|
+
|
10
|
+
if git_command
|
11
|
+
command = git_command[1]
|
12
|
+
repository = git_command[2]
|
13
|
+
|
14
|
+
repository = repository.gsub(/\.git$/, '')
|
15
|
+
|
16
|
+
GitDB::Commands.execute(command, [repository])
|
17
|
+
end
|
File without changes
|
data/git-db.gemspec
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{git-db}
|
8
|
+
s.version = "0.1.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["David Dollar"]
|
12
|
+
s.date = %q{2009-09-14}
|
13
|
+
s.default_executable = %q{git-db}
|
14
|
+
s.description = %q{Database-based git server}
|
15
|
+
s.email = %q{<ddollar@gmail.com>}
|
16
|
+
s.executables = ["git-db"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".gitignore",
|
24
|
+
"LICENSE",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"bin/git-db",
|
29
|
+
"features/git-db.feature",
|
30
|
+
"features/step_definitions/git-db_steps.rb",
|
31
|
+
"features/support/env.rb",
|
32
|
+
"git-db.gemspec",
|
33
|
+
"lib/git-db.rb",
|
34
|
+
"lib/git-db/commands.rb",
|
35
|
+
"lib/git-db/commands/receive-pack.rb",
|
36
|
+
"lib/git-db/commands/upload-pack.rb",
|
37
|
+
"lib/git-db/database.rb",
|
38
|
+
"lib/git-db/objects.rb",
|
39
|
+
"lib/git-db/objects/base.rb",
|
40
|
+
"lib/git-db/objects/blob.rb",
|
41
|
+
"lib/git-db/objects/commit.rb",
|
42
|
+
"lib/git-db/objects/entry.rb",
|
43
|
+
"lib/git-db/objects/tag.rb",
|
44
|
+
"lib/git-db/objects/tree.rb",
|
45
|
+
"lib/git-db/pack.rb",
|
46
|
+
"lib/git-db/protocol.rb",
|
47
|
+
"lib/git-db/utility.rb",
|
48
|
+
"lib/git-db/utility/counting_io.rb",
|
49
|
+
"spec/git-db/commands_spec.rb",
|
50
|
+
"spec/git-db/objects/base_spec.rb",
|
51
|
+
"spec/git-db/objects/entry_spec.rb",
|
52
|
+
"spec/git-db/objects/tag_spec.rb",
|
53
|
+
"spec/git-db/objects/tree_spec.rb",
|
54
|
+
"spec/git-db/utility/counting_io_spec.rb",
|
55
|
+
"spec/git-db_spec.rb",
|
56
|
+
"spec/rcov.opts",
|
57
|
+
"spec/spec.opts",
|
58
|
+
"spec/spec_helper.rb"
|
59
|
+
]
|
60
|
+
s.homepage = %q{http://github.com/ddollar/git-db}
|
61
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
62
|
+
s.require_paths = ["lib"]
|
63
|
+
s.rubygems_version = %q{1.3.5}
|
64
|
+
s.summary = %q{Database-based git server}
|
65
|
+
s.test_files = [
|
66
|
+
"spec/git-db/commands_spec.rb",
|
67
|
+
"spec/git-db/objects/base_spec.rb",
|
68
|
+
"spec/git-db/objects/entry_spec.rb",
|
69
|
+
"spec/git-db/objects/tag_spec.rb",
|
70
|
+
"spec/git-db/objects/tree_spec.rb",
|
71
|
+
"spec/git-db/utility/counting_io_spec.rb",
|
72
|
+
"spec/git-db_spec.rb",
|
73
|
+
"spec/spec_helper.rb"
|
74
|
+
]
|
75
|
+
|
76
|
+
if s.respond_to? :specification_version then
|
77
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
78
|
+
s.specification_version = 3
|
79
|
+
|
80
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
81
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
82
|
+
s.add_runtime_dependency(%q<couchrest>, [">= 0"])
|
83
|
+
else
|
84
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
85
|
+
s.add_dependency(%q<couchrest>, [">= 0"])
|
86
|
+
end
|
87
|
+
else
|
88
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
89
|
+
s.add_dependency(%q<couchrest>, [">= 0"])
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
class GitDB::Commands::ReceivePack
|
5
|
+
|
6
|
+
def execute(args)
|
7
|
+
repository = args.first
|
8
|
+
raise ArgumentError, "repository required" unless repository
|
9
|
+
|
10
|
+
database = GitDB.database(repository)
|
11
|
+
|
12
|
+
needs_capabilities = true
|
13
|
+
database.get_refs.each do |ref, sha|
|
14
|
+
GitDB.log("ISAERF #{ref} #{sha}")
|
15
|
+
write_ref(ref, sha, needs_capabilities)
|
16
|
+
needs_capabilities = false
|
17
|
+
end
|
18
|
+
write_ref("capabilities^{}", GitDB.null_sha1) if needs_capabilities
|
19
|
+
io.write_eof
|
20
|
+
|
21
|
+
refs = []
|
22
|
+
new_shas = []
|
23
|
+
|
24
|
+
while (data = io.read_command)
|
25
|
+
old_sha, new_sha, ref = data.split(' ')
|
26
|
+
ref, report = ref.split(0.chr)
|
27
|
+
|
28
|
+
refs << ref
|
29
|
+
new_shas << new_sha
|
30
|
+
|
31
|
+
if new_sha == GitDB.null_sha1
|
32
|
+
database.delete_ref(ref)
|
33
|
+
else
|
34
|
+
database.write_ref(ref, new_sha)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
unless new_shas.reject { |sha| sha == GitDB.null_sha1 }.length.zero?
|
39
|
+
while (entries = io.read_pack)
|
40
|
+
database.write_objects(entries)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
io.write_command("unpack ok\n")
|
45
|
+
refs.each do |ref|
|
46
|
+
io.write_command("ok #{ref}\n")
|
47
|
+
end
|
48
|
+
io.write_eof
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def capabilities
|
54
|
+
" report-status delete-refs ofs-delta "
|
55
|
+
end
|
56
|
+
|
57
|
+
def io
|
58
|
+
@io ||= GitDB::Protocol.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_ref(ref, sha, needs_capabilities=true)
|
62
|
+
if needs_capabilities
|
63
|
+
header = "%s %s\000%s\n" % [ sha, ref, capabilities ]
|
64
|
+
io.write_command(header)
|
65
|
+
else
|
66
|
+
header = "%s %s\n" % [ sha, ref ]
|
67
|
+
io.write_command(header)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
GitDB::Commands.register 'receive-pack', GitDB::Commands::ReceivePack
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
class GitDB::Commands::UploadPack
|
5
|
+
|
6
|
+
def execute(args)
|
7
|
+
repository = args.first
|
8
|
+
raise ArgumentError, "repository required" unless repository
|
9
|
+
|
10
|
+
database = GitDB.database(repository)
|
11
|
+
|
12
|
+
#execute_transcript(database)
|
13
|
+
execute_real(database)
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute_transcript(database)
|
17
|
+
cmd = GitDB::Protocol.new(IO.popen("/opt/local/bin/git-upload-pack '/tmp/foo'", 'r+'))
|
18
|
+
|
19
|
+
while (data = cmd.read_command)
|
20
|
+
GitDB.log("CMD COMMAND: #{data}")
|
21
|
+
io.write_command(data)
|
22
|
+
end
|
23
|
+
io.write_eof
|
24
|
+
|
25
|
+
while (data = io.read_command)
|
26
|
+
GitDB.log("IO COMMAND: #{data}")
|
27
|
+
cmd.write_command(data)
|
28
|
+
end
|
29
|
+
cmd.write_eof
|
30
|
+
|
31
|
+
while (data = io.read_command)
|
32
|
+
cmd.write_command(data)
|
33
|
+
data = data.strip
|
34
|
+
break if data == 'done'
|
35
|
+
GitDB.log("READ FROM IO #{data}")
|
36
|
+
end
|
37
|
+
|
38
|
+
while (data = cmd.read_command)
|
39
|
+
GitDB.log("GOT COMMAND DATA: #{data.inspect}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute_real(database)
|
44
|
+
write_ref 'HEAD', database.get_ref('refs/heads/master')['sha']
|
45
|
+
|
46
|
+
database.get_refs.each do |ref, sha|
|
47
|
+
write_ref(ref, sha)
|
48
|
+
end
|
49
|
+
io.write_eof
|
50
|
+
|
51
|
+
shas_to_read = []
|
52
|
+
shas_to_ignore = []
|
53
|
+
|
54
|
+
while (data = io.read_command)
|
55
|
+
GitDB.log("GOT COMMAND: #{data.inspect}")
|
56
|
+
command, sha, options = data.split(' ', 3)
|
57
|
+
shas_to_read << sha
|
58
|
+
end
|
59
|
+
|
60
|
+
while (data = io.read_command)
|
61
|
+
data = data.strip
|
62
|
+
break if data == 'done'
|
63
|
+
command, sha = data.split(" ", 2)
|
64
|
+
case command
|
65
|
+
when 'have' then
|
66
|
+
shas_to_ignore << sha
|
67
|
+
else
|
68
|
+
raise "Unknown SHA command: #{command}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if shas_to_ignore.length.zero?
|
73
|
+
io.write_command("NAK\n")
|
74
|
+
else
|
75
|
+
io.write_command("ACK #{shas_to_ignore.last}\n")
|
76
|
+
end
|
77
|
+
|
78
|
+
shas_to_ignore, _ = load_entries(database, shas_to_ignore, false)
|
79
|
+
shas, entries = load_entries(database, shas_to_read, true, shas_to_ignore)
|
80
|
+
|
81
|
+
GitDB.log(entries.map { |e| e.inspect })
|
82
|
+
|
83
|
+
io.write_pack(entries)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def load_entries(database, shas_to_read, keep_entries, shas_to_ignore=[])
|
89
|
+
entries = []
|
90
|
+
shas = []
|
91
|
+
|
92
|
+
while sha = shas_to_read.shift
|
93
|
+
next if shas_to_ignore.include?(sha)
|
94
|
+
GitDB.log("SHAS_TO_IGNORE: #{shas_to_ignore.sort.inspect}")
|
95
|
+
GitDB.log("READING SHA: #{sha}")
|
96
|
+
shas_to_ignore << sha
|
97
|
+
|
98
|
+
shas << sha
|
99
|
+
|
100
|
+
object = database.get_object(sha)
|
101
|
+
GitDB.log("OBJECT: #{object.inspect}")
|
102
|
+
data = object.data
|
103
|
+
|
104
|
+
case object
|
105
|
+
when GitDB::Objects::Commit then
|
106
|
+
commit = GitDB::Objects::Commit.new(data)
|
107
|
+
shas_to_read << commit.tree
|
108
|
+
shas_to_read += commit.parents if commit.parents
|
109
|
+
entries << commit if keep_entries
|
110
|
+
when GitDB::Objects::Tree then
|
111
|
+
tree = GitDB::Objects::Tree.new(data)
|
112
|
+
shas_to_read += tree.entries.map { |e| e.sha }
|
113
|
+
entries << tree if keep_entries
|
114
|
+
when GitDB::Objects::Blob then
|
115
|
+
blob = GitDB::Objects::Blob.new(data)
|
116
|
+
entries << blob if keep_entries
|
117
|
+
else
|
118
|
+
raise "UNKNOWN TYPE!! #{object.class}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
[shas, entries]
|
123
|
+
end
|
124
|
+
|
125
|
+
def capabilities
|
126
|
+
" shallow include-tag"
|
127
|
+
end
|
128
|
+
|
129
|
+
def io
|
130
|
+
@io ||= GitDB::Protocol.new
|
131
|
+
end
|
132
|
+
|
133
|
+
def write_ref(ref, sha, needs_capabilities=true)
|
134
|
+
header = "%s %s\000%s\n" % [ sha, ref, capabilities ]
|
135
|
+
io.write_command(header)
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
GitDB::Commands.register 'upload-pack', GitDB::Commands::UploadPack
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GitDB::Commands
|
2
|
+
|
3
|
+
def self.commands
|
4
|
+
@commands
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.execute(command, args=[])
|
8
|
+
return unless commands
|
9
|
+
raise ArgumentError, "Unknown command: #{command}" unless commands[command]
|
10
|
+
commands[command].execute(args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.register(command, klass)
|
14
|
+
@commands ||= {}
|
15
|
+
@commands[command] = klass.new
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'git-db/commands/receive-pack'
|
21
|
+
require 'git-db/commands/upload-pack'
|
@@ -0,0 +1,134 @@
|
|
1
|
+
class GitDB::Database
|
2
|
+
|
3
|
+
def self.couch
|
4
|
+
@couch ||= CouchRest.new('http://localhost:5984')
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.database(repository)
|
8
|
+
@databases ||= {}
|
9
|
+
@databases[repository] ||= new(repository)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :repository, :name, :database
|
13
|
+
|
14
|
+
def initialize(repository)
|
15
|
+
@repository = repository
|
16
|
+
@name = "gitdb-#{repository.gsub('/', '-')}"
|
17
|
+
@database = self.class.couch.database!(name)
|
18
|
+
update_views
|
19
|
+
end
|
20
|
+
|
21
|
+
## refs ######################################################################
|
22
|
+
|
23
|
+
def get_ref(ref)
|
24
|
+
doc = database.view('refs/all', :key => ref)['rows'].first
|
25
|
+
doc ? doc['value'] : nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_refs
|
29
|
+
database.view('refs/all')['rows'].inject({}) do |hash, row|
|
30
|
+
hash.update(row['key'] => row['value']['sha'])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_ref(ref, sha)
|
35
|
+
if doc = get_ref(ref)
|
36
|
+
doc['sha'] = sha
|
37
|
+
database.save_doc(doc)
|
38
|
+
else
|
39
|
+
database.save_doc(:doctype => 'ref', :ref => ref, :sha => sha)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_ref(ref)
|
44
|
+
if doc = get_ref(ref)
|
45
|
+
database.delete_doc(doc)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
## objects ###################################################################
|
50
|
+
|
51
|
+
def get_raw_object(sha)
|
52
|
+
doc = database.view('objects/all', :key => sha)['rows'].first
|
53
|
+
doc = doc ? decode_object(doc['value']) : nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_object(sha)
|
57
|
+
raw = get_raw_object(sha)
|
58
|
+
raw ? GitDB::Objects.new_from_type(raw['type'], raw['data']) : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_object(object)
|
62
|
+
doc = object_to_doc(object)
|
63
|
+
doc = (get_raw_object(object.sha) || {}).merge(doc)
|
64
|
+
database.save_doc(doc)
|
65
|
+
end
|
66
|
+
|
67
|
+
def write_objects(objects)
|
68
|
+
docs = objects.map do |object|
|
69
|
+
doc = object_to_doc(object)
|
70
|
+
doc = (get_raw_object(object.sha) || {}).merge(doc)
|
71
|
+
end
|
72
|
+
database.bulk_save(docs)
|
73
|
+
end
|
74
|
+
|
75
|
+
## utility ###################################################################
|
76
|
+
|
77
|
+
def document_ids
|
78
|
+
database.documents['rows'].map { |row| row['id']}
|
79
|
+
end
|
80
|
+
|
81
|
+
def encode_object(doc)
|
82
|
+
doc['data'] = Base64.encode64(doc['data'])
|
83
|
+
doc
|
84
|
+
end
|
85
|
+
|
86
|
+
def decode_object(doc)
|
87
|
+
doc['data'] = Base64.decode64(doc['data'])
|
88
|
+
doc
|
89
|
+
end
|
90
|
+
|
91
|
+
def object_to_doc(object)
|
92
|
+
properties = object.properties
|
93
|
+
properties += [:type, :data, :sha]
|
94
|
+
doc = properties.inject({ :doctype => 'object' }) do |hash, property|
|
95
|
+
hash.update(property.to_s => object.send(property))
|
96
|
+
end
|
97
|
+
encode_object(doc)
|
98
|
+
end
|
99
|
+
|
100
|
+
def update_views
|
101
|
+
if document_ids.include?('_design/refs')
|
102
|
+
database.delete_doc(database.get('_design/refs'))
|
103
|
+
end
|
104
|
+
database.save_doc({
|
105
|
+
'_id' => '_design/refs',
|
106
|
+
:views => {
|
107
|
+
:all => {
|
108
|
+
:map => %{
|
109
|
+
function(doc) {
|
110
|
+
if (doc.doctype == 'ref') { emit(doc.ref, doc); }
|
111
|
+
}
|
112
|
+
}
|
113
|
+
},
|
114
|
+
}
|
115
|
+
})
|
116
|
+
if document_ids.include?('_design/objects')
|
117
|
+
database.delete_doc(database.get('_design/objects'))
|
118
|
+
end
|
119
|
+
database.save_doc({
|
120
|
+
'_id' => '_design/objects',
|
121
|
+
:views => {
|
122
|
+
:all => {
|
123
|
+
:map => %{
|
124
|
+
function(doc) {
|
125
|
+
if (doc.doctype == 'object') { emit(doc.sha, doc); }
|
126
|
+
}
|
127
|
+
}
|
128
|
+
},
|
129
|
+
}
|
130
|
+
})
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class GitDB::Objects::Base
|
2
|
+
|
3
|
+
attr_reader :data
|
4
|
+
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
%{#<#{self.class} #{inspect_properties}>}
|
11
|
+
end
|
12
|
+
|
13
|
+
def properties
|
14
|
+
[:data]
|
15
|
+
end
|
16
|
+
|
17
|
+
def raw
|
18
|
+
data
|
19
|
+
end
|
20
|
+
|
21
|
+
def sha
|
22
|
+
Digest::SHA1.hexdigest(raw)
|
23
|
+
end
|
24
|
+
|
25
|
+
private ######################################################################
|
26
|
+
|
27
|
+
def inspect_properties
|
28
|
+
inspectors = properties.unshift(:sha).map do |argument|
|
29
|
+
"#{argument}=#{self.send(argument).inspect}"
|
30
|
+
end
|
31
|
+
inspectors.join(' ')
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|