git-db 0.1.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/.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
|