ddollar-git-db 0.0.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 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