ddollar-git-db 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
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,38 @@
1
+ = git-db
2
+
3
+ CouchDB-based git server, avoids the filesystem. (VERY ALPHA)
4
+
5
+ == Installation
6
+
7
+ * Install the gem
8
+
9
+ $ gem install ddollar-git-db
10
+
11
+ * Create a <tt>git</tt> user. (Name can be whatever you like)
12
+ * Set a home directory for the user.
13
+ * Set up the <tt>git</tt> user's authorized_keys2 file: (modify the command to match your gem particulars)
14
+
15
+ # ~/.git/.ssh/authorized_keys2
16
+ command="/usr/bin/git-db david",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty <your ssh public key>
17
+
18
+ * Add your localhost as a remote to an existing project and push
19
+
20
+ $ git remote add test-git-db git@localhost:my-repo.git
21
+ $ git push test-git-db master
22
+
23
+ * Please report any problems on the issue tracker.
24
+
25
+ == Note on Patches/Pull Requests
26
+
27
+ * Fork the project.
28
+ * Make your feature addition or bug fix.
29
+ * Add tests for it. This is important so I don't break it in a
30
+ future version unintentionally.
31
+ * Commit, do not mess with rakefile, version, or history.
32
+ (if you want to have your own version, that is fine but
33
+ bump version in a commit by itself I can ignore when I pull)
34
+ * Send me a pull request. Bonus points for topic branches.
35
+
36
+ == Copyright
37
+
38
+ Copyright (c) 2009 David Dollar. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
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
+ gem.add_development_dependency "rspec"
14
+ gem.add_development_dependency "yard"
15
+ gem.add_development_dependency "cucumber"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :spec => :check_dependencies
36
+
37
+ begin
38
+ require 'cucumber/rake/task'
39
+ Cucumber::Rake::Task.new(:features)
40
+
41
+ task :features => :check_dependencies
42
+ rescue LoadError
43
+ task :features do
44
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
45
+ end
46
+ end
47
+
48
+ task :default => :spec
49
+
50
+ begin
51
+ require 'yard'
52
+ YARD::Rake::YardocTask.new
53
+ rescue LoadError
54
+ task :yardoc do
55
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
56
+ end
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
data/bin/git-db ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift('/Users/david/Code/git-db/lib')
4
+ require 'git-db'
5
+ include GitDB
6
+
7
+ GitDB.log(ENV.inspect)
8
+
9
+ git_command = ENV['SSH_ORIGINAL_COMMAND'].match(/git-(.+) '(.+)'/)
10
+
11
+ if git_command
12
+ command = git_command[1]
13
+ repository = git_command[2]
14
+
15
+ Git::Commands.execute(command, [repository])
16
+ end
@@ -0,0 +1,9 @@
1
+ Feature: something something
2
+ In order to something something
3
+ A user something something
4
+ something something something
5
+
6
+ Scenario: something something
7
+ Given inspiration
8
+ When I create a sweet new gem
9
+ Then everyone should see how awesome I am
File without changes
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'git-db'
3
+
4
+ require 'spec/expectations'
data/git-db.gemspec ADDED
@@ -0,0 +1,80 @@
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.0.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-13}
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.rb",
35
+ "lib/git/commands.rb",
36
+ "lib/git/commands/receive-pack.rb",
37
+ "lib/git/commands/upload-pack.rb",
38
+ "lib/git/objects.rb",
39
+ "lib/git/objects/base.rb",
40
+ "lib/git/objects/blob.rb",
41
+ "lib/git/objects/commit.rb",
42
+ "lib/git/objects/entry.rb",
43
+ "lib/git/objects/tag.rb",
44
+ "lib/git/objects/tree.rb",
45
+ "lib/git/pack.rb",
46
+ "lib/git/protocol.rb",
47
+ "lib/utility.rb",
48
+ "lib/utility/counting_io.rb",
49
+ "spec/git-db_spec.rb",
50
+ "spec/spec_helper.rb"
51
+ ]
52
+ s.homepage = %q{http://github.com/ddollar/git-db}
53
+ s.rdoc_options = ["--charset=UTF-8"]
54
+ s.require_paths = ["lib"]
55
+ s.rubygems_version = %q{1.3.5}
56
+ s.summary = %q{Database-based git server}
57
+ s.test_files = [
58
+ "spec/git-db_spec.rb",
59
+ "spec/spec_helper.rb"
60
+ ]
61
+
62
+ if s.respond_to? :specification_version then
63
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
+ s.specification_version = 3
65
+
66
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
67
+ s.add_development_dependency(%q<rspec>, [">= 0"])
68
+ s.add_development_dependency(%q<yard>, [">= 0"])
69
+ s.add_development_dependency(%q<cucumber>, [">= 0"])
70
+ else
71
+ s.add_dependency(%q<rspec>, [">= 0"])
72
+ s.add_dependency(%q<yard>, [">= 0"])
73
+ s.add_dependency(%q<cucumber>, [">= 0"])
74
+ end
75
+ else
76
+ s.add_dependency(%q<rspec>, [">= 0"])
77
+ s.add_dependency(%q<yard>, [">= 0"])
78
+ s.add_dependency(%q<cucumber>, [">= 0"])
79
+ end
80
+ end
@@ -0,0 +1,101 @@
1
+ require 'fileutils'
2
+ require 'zlib'
3
+
4
+ class GitDB::Git::Commands::ReceivePack
5
+
6
+ def execute(args)
7
+ repository = args.first
8
+ raise ArgumentError, "repository required" unless repository
9
+
10
+ needs_capabilities = true
11
+ each_git_ref do |ref, sha|
12
+ write_ref(ref, sha, needs_capabilities)
13
+ needs_capabilities = false
14
+ end
15
+ write_ref("capabilities^{}", null_sha1) if needs_capabilities
16
+ io.write_eof
17
+
18
+ refs = []
19
+ new_shas = []
20
+
21
+ while (data = io.read_command)
22
+ old_sha, new_sha, ref = data.split(' ')
23
+ ref, report = ref.split(0.chr)
24
+ refs << ref
25
+ new_shas << new_sha
26
+ if new_sha == null_sha1
27
+ delete_git_file(ref)
28
+ else
29
+ write_git_file(ref, new_sha)
30
+ end
31
+ # GitDB.log("OLDSHA: #{old_sha}")
32
+ # GitDB.log("NEWSHA: #{new_sha}")
33
+ # GitDB.log("REF: #{ref}")
34
+ # GitDB.log("REPORT: #{report}")
35
+ end
36
+
37
+ unless new_shas.reject { |sha| sha == null_sha1 }.length.zero?
38
+ while (entries = io.read_pack)
39
+ GitDB.log("ENTRIES: #{entries.inspect}")
40
+ entries.each do |entry|
41
+ filename = "objects/#{entry.sha[0..1]}/#{entry.sha[2..-1]}"
42
+ write_git_file(filename, Zlib::Deflate.deflate(entry.raw))
43
+ end
44
+ end
45
+ end
46
+
47
+ io.write_command("unpack ok\n")
48
+ refs.each do |ref|
49
+ io.write_command("ok #{ref}\n")
50
+ end
51
+ io.write_eof
52
+ end
53
+
54
+ private
55
+
56
+ def capabilities
57
+ " report-status delete-refs ofs-delta "
58
+ end
59
+
60
+ def io
61
+ @io ||= GitDB::Git::Protocol.new
62
+ end
63
+
64
+ def null_sha1
65
+ "0000000000000000000000000000000000000000"
66
+ end
67
+
68
+ def each_git_ref(&block)
69
+ Dir["/tmp/foo/.git/refs/*/*"].each do |ref|
70
+ sha = File.read(ref).strip
71
+ ref = ref.gsub('/tmp/foo/.git/', '')
72
+ yield ref, sha
73
+ end
74
+ end
75
+
76
+ def delete_git_file(filename)
77
+ filename = "/tmp/foo/.git/#{filename}"
78
+ GitDB.log("REMOVING: #{filename}")
79
+ FileUtils.rm_rf(filename)
80
+ end
81
+
82
+ def write_git_file(filename, data)
83
+ filename = "/tmp/foo/.git/#{filename}"
84
+ FileUtils.mkdir_p(File.dirname(filename))
85
+ File.open(filename, 'w') do |file|
86
+ file.print(data)
87
+ end
88
+ end
89
+
90
+ def write_ref(ref, sha, needs_capabilities=true)
91
+ if needs_capabilities
92
+ header = "%s %s\000%s\n" % [ sha, ref, capabilities ]
93
+ io.write_command(header)
94
+ else
95
+ header = "%s %s\n" % [ sha, ref ]
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ GitDB::Git::Commands.register 'receive-pack', GitDB::Git::Commands::ReceivePack
@@ -0,0 +1,204 @@
1
+ require 'fileutils'
2
+ require 'zlib'
3
+
4
+ class GitDB::Git::Commands::UploadPack
5
+
6
+ def execute(args)
7
+ repository = args.first
8
+ raise ArgumentError, "repository required" unless repository
9
+
10
+ #execute_transcript
11
+ execute_real
12
+ end
13
+
14
+ def execute_transcript
15
+ cmd = GitDB::Git::Protocol.new(IO.popen("/opt/local/bin/git-upload-pack '/tmp/foo'", 'r+'))
16
+
17
+ while (data = cmd.read_command)
18
+ GitDB.log("CMD COMMAND: #{data}")
19
+ io.write_command(data)
20
+ end
21
+ io.write_eof
22
+
23
+ while (data = io.read_command)
24
+ GitDB.log("IO COMMAND: #{data}")
25
+ cmd.write_command(data)
26
+ end
27
+ cmd.write_eof
28
+
29
+ while (data = io.read_command)
30
+ cmd.write_command(data)
31
+ data = data.strip
32
+ break if data == 'done'
33
+ GitDB.log("READ FROM IO #{data}")
34
+ end
35
+
36
+ while (data = cmd.read_command)
37
+ GitDB.log("GOT COMMAND DATA: #{data.inspect}")
38
+ end
39
+
40
+ # data = io.reader.read(9)
41
+ # GitDB.log("READ FROM IO: #{data.inspect}")
42
+ # cmd.writer.write(data)
43
+
44
+ # while (data = cmd.read_command)
45
+ # GitDB.log("CMD COMMAND: #{data.inspect}")
46
+ # io.write_command(data)
47
+ # GitDB.log('weee')
48
+ # if data[0] == 1
49
+ # GitDB.log("ITS A PACK!")
50
+ # pack = data[1..-1]
51
+ # unpacker = GitDB::Git::Pack.new(StringIO.new(pack))
52
+ # unpacker.read
53
+ # end
54
+ # end
55
+ # io.write_eof
56
+
57
+ # while (data = cmd.read_command)
58
+ # GitDB.log("CMD COMMAND: #{data}")
59
+ # io.write_command(data)
60
+ # end
61
+ # io.write_eof
62
+ end
63
+
64
+ def execute_real
65
+ head_ref do |ref, sha|
66
+ write_ref(ref, sha)
67
+ end
68
+ each_git_ref do |ref, sha|
69
+ write_ref(ref, sha)
70
+ #write_ref(ref, sha.gsub('9', '1'))
71
+ end
72
+ io.write_eof
73
+
74
+ shas_to_read = []
75
+ shas_to_ignore = []
76
+
77
+ while (data = io.read_command)
78
+ GitDB.log("GOT COMMAND: #{data.inspect}")
79
+ command, sha, options = data.split(' ', 3)
80
+ shas_to_read << sha
81
+ end
82
+
83
+ while (data = io.read_command)
84
+ data = data.strip
85
+ break if data == 'done'
86
+ command, sha = data.split(" ", 2)
87
+ case command
88
+ when 'have' then
89
+ shas_to_ignore << sha
90
+ else
91
+ raise "Unknown SHA command: #{command}"
92
+ end
93
+ end
94
+
95
+ if shas_to_ignore.length.zero?
96
+ io.write_command("NAK\n")
97
+ else
98
+ io.write_command("ACK #{shas_to_ignore.last}\n")
99
+ end
100
+
101
+ shas_to_ignore, _ = load_entries(shas_to_ignore, false)
102
+ # GitDB.log("SHAS_TO_READ: #{shas_to_read.inspect}")
103
+ # GitDB.log("SHAS_READ: #{shas_read.inspect}")
104
+
105
+ shas, entries = load_entries(shas_to_read, true, shas_to_ignore)
106
+
107
+ GitDB.log(entries.map { |e| e.inspect })
108
+
109
+ io.write_pack(entries)
110
+ end
111
+
112
+ private
113
+
114
+ def load_entries(shas_to_read, keep_entries, shas_to_ignore=[])
115
+ entries = []
116
+ shas = []
117
+
118
+ while sha = shas_to_read.shift
119
+ next if shas_to_ignore.include?(sha)
120
+ shas_to_ignore << sha
121
+
122
+ shas << sha
123
+
124
+ raw_data = read_git_object(sha)
125
+ type = raw_data.split(" ").first
126
+ data = raw_data.split("\000", 2).last
127
+
128
+ #GitDB.log("SHADATA: #{data.inspect}")
129
+ case type
130
+ when 'commit' then
131
+ commit = GitDB::Git::Objects::Commit.new(data)
132
+ shas_to_read << commit.tree
133
+ shas_to_read += commit.parents if commit.parents
134
+ entries << commit if keep_entries
135
+ when 'tree' then
136
+ tree = GitDB::Git::Objects::Tree.new(data)
137
+ shas_to_read += tree.entries.map { |e| e.sha }
138
+ entries << tree if keep_entries
139
+ when 'blob' then
140
+ blob = GitDB::Git::Objects::Blob.new(data)
141
+ entries << blob if keep_entries
142
+ else
143
+ raise "UNKNOWN TYPE!! #{type}"
144
+ end
145
+ end
146
+
147
+ [shas, entries]
148
+ end
149
+
150
+ def capabilities
151
+ " shallow include-tag"
152
+ end
153
+
154
+ def io
155
+ @io ||= GitDB::Git::Protocol.new
156
+ end
157
+
158
+ def null_sha1
159
+ "0000000000000000000000000000000000000000"
160
+ end
161
+
162
+ def each_git_ref(&block)
163
+ Dir["/tmp/foo/.git/refs/*/*"].each do |ref|
164
+ sha = File.read(ref).strip
165
+ ref = ref.gsub('/tmp/foo/.git/', '')
166
+ yield ref, sha
167
+ end
168
+ end
169
+
170
+ def head_ref(&block)
171
+ sha = File.read("/tmp/foo/.git/HEAD").strip
172
+ if sha =~ /ref: (.+)/
173
+ sha = File.read("/tmp/foo/.git/#{$1}")
174
+ end
175
+ yield "HEAD", sha
176
+ end
177
+
178
+ def delete_git_file(filename)
179
+ filename = "/tmp/foo/.git/#{filename}"
180
+ GitDB.log("REMOVING: #{filename}")
181
+ FileUtils.rm_rf(filename)
182
+ end
183
+
184
+ def read_git_object(sha)
185
+ filename = "/tmp/foo/.git/objects/#{sha[0..1]}/#{sha[2..-1]}"
186
+ data = Zlib::Inflate.inflate(File.read(filename))
187
+ end
188
+
189
+ def write_git_file(filename, data)
190
+ filename = "/tmp/foo/.git/#{filename}"
191
+ FileUtils.mkdir_p(File.dirname(filename))
192
+ File.open(filename, 'w') do |file|
193
+ file.print(data)
194
+ end
195
+ end
196
+
197
+ def write_ref(ref, sha, needs_capabilities=true)
198
+ header = "%s %s\000%s\n" % [ sha, ref, capabilities ]
199
+ io.write_command(header)
200
+ end
201
+
202
+ end
203
+
204
+ GitDB::Git::Commands.register 'upload-pack', GitDB::Git::Commands::UploadPack
@@ -0,0 +1,17 @@
1
+ module GitDB::Git::Commands
2
+
3
+ def self.execute(command, args=[])
4
+ return unless @commands
5
+ raise ArgumentError, "Unknown command: #{command}" unless @commands[command]
6
+ @commands[command].execute(args)
7
+ end
8
+
9
+ def self.register(command, klass)
10
+ @commands ||= {}
11
+ @commands[command] = klass.new
12
+ end
13
+
14
+ end
15
+
16
+ require 'git/commands/receive-pack'
17
+ require 'git/commands/upload-pack'
@@ -0,0 +1,29 @@
1
+ class GitDB::Git::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_arguments_as_string}>}
11
+ end
12
+
13
+ def sha
14
+ Digest::SHA1.hexdigest(raw)
15
+ end
16
+
17
+ private ######################################################################
18
+
19
+ def inspect_arguments
20
+ [:data]
21
+ end
22
+
23
+ def inspect_arguments_as_string
24
+ inspect_arguments.unshift(:sha).map do |argument|
25
+ "#{argument}=#{self.send(argument).inspect}"
26
+ end.join(' ')
27
+ end
28
+
29
+ end
@@ -0,0 +1,11 @@
1
+ class GitDB::Git::Objects::Blob < GitDB::Git::Objects::Base
2
+
3
+ def raw
4
+ "blob #{data.length}\000#{data}"
5
+ end
6
+
7
+ def type
8
+ Git::OBJ_BLOB
9
+ end
10
+
11
+ end
@@ -0,0 +1,49 @@
1
+ class GitDB::Git::Objects::Commit < GitDB::Git::Objects::Base
2
+
3
+ def raw
4
+ "commit #{data.length}\000#{data}"
5
+ end
6
+
7
+ def type
8
+ Git::OBJ_COMMIT
9
+ end
10
+
11
+ def message
12
+ data.split("\n\n", 2).last
13
+ end
14
+
15
+ def author
16
+ attributes['author'].first
17
+ end
18
+
19
+ def committer
20
+ attributes['committer'].first
21
+ end
22
+
23
+ def tree
24
+ attributes['tree'].first
25
+ end
26
+
27
+ def parents
28
+ attributes['parent']
29
+ end
30
+
31
+ private ######################################################################
32
+
33
+ def inspect_arguments
34
+ [:tree, :parents, :author, :committer, :message]
35
+ end
36
+
37
+ def attributes
38
+ @attributes ||= begin
39
+ attributes = data.split("\n\n", 2).first
40
+ attributes.split("\n").inject({}) do |hash, line|
41
+ key, value = line.split(' ', 2)
42
+ hash[key] ||= []
43
+ hash[key] << value
44
+ hash
45
+ end
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,19 @@
1
+ require 'stringio'
2
+
3
+ class GitDB::Git::Objects::Entry < GitDB::Git::Objects::Base
4
+
5
+ attr_reader :sha, :permissions, :name
6
+
7
+ def initialize(sha, permissions, name)
8
+ @sha = sha
9
+ @permissions = permissions
10
+ @name = name
11
+ end
12
+
13
+ private ######################################################################
14
+
15
+ def inspect_arguments
16
+ [:permissions, :name]
17
+ end
18
+
19
+ end
@@ -0,0 +1,7 @@
1
+ class GitDB::Git::Objects::Tag < GitDB::Git::Objects::Base
2
+
3
+ def type
4
+ Git::OBJ_TAG
5
+ end
6
+
7
+ end
@@ -0,0 +1,45 @@
1
+ require 'stringio'
2
+
3
+ class GitDB::Git::Objects::Tree < GitDB::Git::Objects::Base
4
+
5
+ def entries
6
+ @entries ||= begin
7
+ entries = []
8
+ stream = StringIO.new(data)
9
+ until stream.eof?
10
+ perms = read_until(stream, ' ').to_i
11
+ name = read_until(stream, 0.chr)
12
+ sha = Git.sha1_to_hex(stream.read(20))
13
+ entries << GitDB::Git::Objects::Entry.new(sha, perms, name)
14
+ end
15
+ entries
16
+ end
17
+ end
18
+
19
+ def raw
20
+ "tree #{data.length}\000#{data}"
21
+ end
22
+
23
+ def type
24
+ Git::OBJ_TREE
25
+ end
26
+
27
+ private ######################################################################
28
+
29
+ def inspect_arguments
30
+ [:entries]
31
+ end
32
+
33
+ def read_until(stream, separator)
34
+ data = ""
35
+ char = ""
36
+ loop do
37
+ char = stream.read(1)
38
+ break if char.nil?
39
+ break if char == separator
40
+ data << char
41
+ end
42
+ data
43
+ end
44
+
45
+ end
@@ -0,0 +1,20 @@
1
+ module GitDB::Git::Objects;
2
+
3
+ # def self.new_from_type(type, data)
4
+ # case type
5
+ # when Git::OBJ_COMMIT then GitDB::Git::Objects::Commit.new(data)
6
+ # when Git::OBJ_TREE then GitDB::Git::Objects::Tree.new(data)
7
+ # when Git::OBJ_BLOB then GitDB::Git::Objects::Blob.new(data)
8
+ # when Git::OBJ_TAG then GitDB::Git::Objects::Tag.new(data)
9
+ # else raise "Unknown object type: #{type}"
10
+ # end
11
+ # end
12
+
13
+ end
14
+
15
+ require 'git/objects/base'
16
+ require 'git/objects/blob'
17
+ require 'git/objects/commit'
18
+ require 'git/objects/entry'
19
+ require 'git/objects/tag'
20
+ require 'git/objects/tree'
data/lib/git/pack.rb ADDED
@@ -0,0 +1,182 @@
1
+ require 'digest/sha1'
2
+ require 'stringio'
3
+ require 'zlib'
4
+
5
+ class GitDB::Git::Pack
6
+
7
+ PackObject = Struct.new(:type, :offset, :data)
8
+
9
+ attr_reader :io
10
+
11
+ def initialize(io)
12
+ @io = GitDB::Utility::CountingIO.new(io)
13
+ end
14
+
15
+ def read
16
+ header = io.read(12)
17
+ return nil unless header
18
+
19
+ signature, version, entries = header.unpack("a4NN")
20
+ raise 'invalid pack signature' unless signature == 'PACK'
21
+ raise 'invalid version' unless version == 2
22
+
23
+ objects = {}
24
+
25
+ 1.upto(entries) do
26
+ object_offset = io.offset
27
+
28
+ type, size = unpack_pack_header(io)
29
+
30
+ object = case type
31
+ when 1 then
32
+ GitDB::Git::Objects::Commit.new(read_compressed(io))
33
+ when 2 then
34
+ GitDB::Git::Objects::Tree.new(read_compressed(io))
35
+ when 3 then
36
+ GitDB::Git::Objects::Blob.new(read_compressed(io))
37
+ when 4 then
38
+ GitDB::Git::Objects::Tag.new(read_compressed(io))
39
+ when 5 then
40
+ raise 'Invalid Type: 5'
41
+ when 6 then
42
+ offset = object_offset - unpack_delta_size(io)
43
+ patch = read_compressed(io)
44
+ base = objects[offset]
45
+ base.class.new(apply_patch(base.data, patch))
46
+ when 7 then
47
+ # TODO
48
+ sha = io.read(20)
49
+ # base = lookup_by_sha(sha)
50
+ patch = read_compressed(io)
51
+ # base.class.new(apply_patch(base.data, patch))
52
+ nil
53
+ end
54
+
55
+ objects[object_offset] = object
56
+ end
57
+
58
+ GitDB.log(objects.values.map { |o| o.inspect })
59
+
60
+ io.read(20)
61
+
62
+ objects.values.compact
63
+ end
64
+
65
+ def write(entries)
66
+ buffer = ""
67
+ signature = ["PACK", 2, entries.length].pack("a4NN")
68
+ #GitDB.log("SIGNATURE: #{signature}")
69
+ io.write(signature)
70
+ buffer << signature
71
+
72
+ entries.each do |entry|
73
+ header = pack_pack_header(entry.type, entry.data.length)
74
+ #GitDB.log("HEADER: #{header.inspect}")
75
+ io.write(header)
76
+ buffer << header
77
+ compressed = Zlib::Deflate.deflate(entry.data)
78
+ io.write(compressed)
79
+ buffer << compressed
80
+ end
81
+
82
+ #GitDB.log("BUFFER: #{buffer.inspect}")
83
+ signature = Git::hex_to_sha1(Digest::SHA1.hexdigest(buffer))
84
+ #GitDB.log("SIGNATURE: #{signature.inspect}")
85
+ io.write(signature)
86
+ io.flush
87
+ end
88
+
89
+ private ######################################################################
90
+
91
+ def apply_patch(original, patch)
92
+ patch_stream = StringIO.new(patch)
93
+ source_size = unpack_size(patch_stream)
94
+ destination_size = unpack_size(patch_stream)
95
+
96
+ data = ""
97
+
98
+ until patch_stream.eof?
99
+ offset = size = 0
100
+ cmd = patch_stream.read(1)[0]
101
+ if (cmd & 0x80) != 0
102
+ offset = (patch_stream.read(1)[0]) if (cmd & 0x01) != 0
103
+ offset |= (patch_stream.read(1)[0] << 8) if (cmd & 0x02) != 0
104
+ offset |= (patch_stream.read(1)[0] << 16) if (cmd & 0x04) != 0
105
+ offset |= (patch_stream.read(1)[0] << 24) if (cmd & 0x08) != 0
106
+ size = (patch_stream.read(1)[0]) if (cmd & 0x10) != 0
107
+ size |= (patch_stream.read(1)[0] << 8) if (cmd & 0x20) != 0
108
+ size |= (patch_stream.read(1)[0] << 16) if (cmd & 0x40) != 0
109
+ size = 0x10000 if size == 0
110
+
111
+ if ((offset + size) < size) ||
112
+ ((offset + size) > source_size) ||
113
+ (size > destination_size)
114
+ break
115
+ end
116
+ data += original[offset,size]
117
+ elsif (cmd != 0)
118
+ data += patch_stream.read(cmd)
119
+ end
120
+ end
121
+
122
+ data
123
+ end
124
+
125
+ def read_compressed(stream)
126
+ zstream = Zlib::Inflate.new
127
+ data = ""
128
+ loop do
129
+ data += zstream.inflate(stream.read(1))
130
+ break if zstream.finished?
131
+ end
132
+ data
133
+ end
134
+
135
+ def pack_pack_header(type, size)
136
+ data = ""
137
+ c = (type << 4) | (size & 15);
138
+ size >>= 4;
139
+ while (size > 0)
140
+ data << (c | 0x80).chr
141
+ c = size & 0x7f;
142
+ size >>= 7;
143
+ end
144
+ data << c.chr
145
+ end
146
+
147
+ def unpack_delta_size(stream)
148
+ c = stream.read(1)[0]
149
+ size = (c & 127)
150
+ while (c & 128) != 0
151
+ size += 1
152
+ c = stream.read(1)[0]
153
+ size = (size << 7) + (c & 127)
154
+ end
155
+ size
156
+ end
157
+
158
+ def unpack_pack_header(stream)
159
+ c = stream.read(1)[0]
160
+ type = (c >> 4) & 7
161
+ size = (c & 15)
162
+ shift = 4
163
+ while ((c & 0x80) != 0)
164
+ c = stream.read(1)[0]
165
+ size += ((c & 0x7f) << shift)
166
+ shift += 7
167
+ end
168
+ [type, size]
169
+ end
170
+
171
+ def unpack_size(stream)
172
+ size = shift = 0
173
+ loop do
174
+ c = stream.read(1)[0]
175
+ size += (c & 127) << shift
176
+ shift += 7
177
+ break if (c & 128) == 0
178
+ end
179
+ size
180
+ end
181
+
182
+ end
@@ -0,0 +1,73 @@
1
+ class GitDB::Git::Protocol
2
+
3
+ attr_reader :reader
4
+ attr_reader :writer
5
+
6
+ def initialize(io=nil)
7
+ if io
8
+ @reader = io
9
+ @writer = io
10
+ else
11
+ @reader = STDIN
12
+ @writer = STDOUT
13
+ end
14
+ end
15
+
16
+ ## commands ##################################################################
17
+
18
+ def flush
19
+ writer.flush
20
+ end
21
+
22
+ def read_command
23
+ length = reader.read(4)
24
+ return nil unless length
25
+ length = length.to_i(16) - 4
26
+ if (length == -4)
27
+ GitDB.log('GOT EOF')
28
+ return
29
+ end
30
+ data = reader.read(length)
31
+ GitDB.log("GOT DATA: #{data.inspect}")
32
+ data
33
+ end
34
+
35
+ def write_command(command)
36
+ raw_command = encode_command(command)
37
+ GitDB.log("WWRITING COMMAND: #{raw_command.inspect}")
38
+ writer.print raw_command
39
+ writer.flush
40
+ end
41
+
42
+ def write(data)
43
+ writer.write data
44
+ writer.flush
45
+ end
46
+
47
+ def write_eof
48
+ #GitDB.log("WRITING EOF")
49
+ writer.print '0000'
50
+ writer.flush
51
+ end
52
+
53
+ ## packs #####################################################################
54
+
55
+ def read_pack
56
+ GitDB::Git::Pack.new(reader).read
57
+ end
58
+
59
+ def write_pack(entries)
60
+ GitDB::Git::Pack.new(writer).write(entries)
61
+ end
62
+
63
+ private ######################################################################
64
+
65
+ def encode_command(command)
66
+ length_as_hex(command) << command
67
+ end
68
+
69
+ def length_as_hex(command)
70
+ hex = (command.length + 4).to_s(16).rjust(4, '0')
71
+ end
72
+
73
+ end
data/lib/git-db.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'logger'
2
+
3
+ module GitDB;
4
+
5
+ def self.logger
6
+ @logger ||= File.open("/tmp/git-db.log", "w")
7
+ #@logger ||= STDERR
8
+ end
9
+
10
+ def self.log(message)
11
+ logger.puts message
12
+ end
13
+ end
14
+
15
+ require 'git'
16
+ require 'utility'
data/lib/git.rb ADDED
@@ -0,0 +1,39 @@
1
+ module GitDB::Git;
2
+
3
+ # git constants
4
+ OBJ_NONE = 0
5
+ OBJ_COMMIT = 1
6
+ OBJ_TREE = 2
7
+ OBJ_BLOB = 3
8
+ OBJ_TAG = 4
9
+ OBJ_OFS_DELTA = 6
10
+ OBJ_REF_DELTA = 7
11
+
12
+ def self.sha1_to_hex(sha)
13
+ hex = ""
14
+ sha.split('').each do |char|
15
+ val = char[0]
16
+ hex << (val >> 4).to_s(16)
17
+ hex << (val & 0xf).to_s(16)
18
+ end
19
+ hex
20
+ end
21
+
22
+ def self.hex_to_sha1(hex)
23
+ sha = ""
24
+ len = 0
25
+ until (len == hex.length)
26
+ val = (hex[len, 1].to_i(16) << 4)
27
+ val += hex[len+1, 1].to_i(16)
28
+ sha << val.chr
29
+ len += 2
30
+ end
31
+ sha
32
+ end
33
+
34
+ end
35
+
36
+ require 'git/commands'
37
+ require 'git/objects'
38
+ require 'git/pack'
39
+ require 'git/protocol'
@@ -0,0 +1,24 @@
1
+ class GitDB::Utility::CountingIO
2
+
3
+ attr_reader :io, :offset
4
+
5
+ def initialize(io)
6
+ @io = io
7
+ @offset = 0
8
+ end
9
+
10
+ def flush
11
+ io.flush
12
+ end
13
+
14
+ def read(n)
15
+ data = io.read(n)
16
+ @offset += n
17
+ data
18
+ end
19
+
20
+ def write(data)
21
+ io.write(data)
22
+ end
23
+
24
+ end
data/lib/utility.rb ADDED
@@ -0,0 +1,3 @@
1
+ module GitDB::Utility; end
2
+
3
+ require 'utility/counting_io'
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "GitDb" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'git-db'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ddollar-git-db
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - David Dollar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-13 00:00:00 -07:00
13
+ default_executable: git-db
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: cucumber
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: Database-based git server
46
+ email: <ddollar@gmail.com>
47
+ executables:
48
+ - git-db
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.rdoc
59
+ - Rakefile
60
+ - VERSION
61
+ - bin/git-db
62
+ - features/git-db.feature
63
+ - features/step_definitions/git-db_steps.rb
64
+ - features/support/env.rb
65
+ - git-db.gemspec
66
+ - lib/git-db.rb
67
+ - lib/git.rb
68
+ - lib/git/commands.rb
69
+ - lib/git/commands/receive-pack.rb
70
+ - lib/git/commands/upload-pack.rb
71
+ - lib/git/objects.rb
72
+ - lib/git/objects/base.rb
73
+ - lib/git/objects/blob.rb
74
+ - lib/git/objects/commit.rb
75
+ - lib/git/objects/entry.rb
76
+ - lib/git/objects/tag.rb
77
+ - lib/git/objects/tree.rb
78
+ - lib/git/pack.rb
79
+ - lib/git/protocol.rb
80
+ - lib/utility.rb
81
+ - lib/utility/counting_io.rb
82
+ - spec/git-db_spec.rb
83
+ - spec/spec_helper.rb
84
+ has_rdoc: false
85
+ homepage: http://github.com/ddollar/git-db
86
+ licenses:
87
+ post_install_message:
88
+ rdoc_options:
89
+ - --charset=UTF-8
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ version:
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "0"
103
+ version:
104
+ requirements: []
105
+
106
+ rubyforge_project:
107
+ rubygems_version: 1.3.5
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Database-based git server
111
+ test_files:
112
+ - spec/git-db_spec.rb
113
+ - spec/spec_helper.rb