ddollar-git-db 0.0.2 → 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/README.rdoc CHANGED
@@ -4,33 +4,54 @@ CouchDB-based git server, avoids the filesystem. (VERY ALPHA)
4
4
 
5
5
  == Installation
6
6
 
7
+ * Install CouchDB on <tt>localhost</tt>, and start it up.
8
+
7
9
  * Install the gem
8
10
 
9
- $ gem install ddollar-git-db
11
+ $ gem install ddollar-git-db
10
12
 
11
13
  * Create a <tt>git</tt> user. (Name can be whatever you like)
14
+
12
15
  * Set a home directory for the user.
16
+
13
17
  * Set up the <tt>git</tt> user's authorized_keys2 file: (modify the command to match your gem particulars)
14
18
 
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>
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>
17
21
 
18
22
  * Add your localhost as a remote to an existing project and push
19
23
 
20
- $ git remote add test-git-db git@localhost:my-repo.git
21
- $ git push test-git-db master
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
22
34
 
23
35
  * Please report any problems on the issue tracker.
24
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
+
25
49
  == Note on Patches/Pull Requests
26
50
 
27
51
  * Fork the project.
28
52
  * 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.
53
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
31
54
  * 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
55
  * Send me a pull request. Bonus points for topic branches.
35
56
 
36
57
  == Copyright
data/Rakefile CHANGED
@@ -10,10 +10,12 @@ begin
10
10
  gem.email = "<ddollar@gmail.com>"
11
11
  gem.homepage = "http://github.com/ddollar/git-db"
12
12
  gem.authors = ["David Dollar"]
13
+
14
+ # development dependencies
13
15
  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
16
+
17
+ # runtime dependencies
18
+ gem.add_dependency "couchrest"
17
19
  end
18
20
  Jeweler::GemcutterTasks.new
19
21
  rescue LoadError
@@ -24,34 +26,18 @@ require 'spec/rake/spectask'
24
26
  Spec::Rake::SpecTask.new(:spec) do |spec|
25
27
  spec.libs << 'lib' << 'spec'
26
28
  spec.spec_files = FileList['spec/**/*_spec.rb']
29
+ spec.spec_opts << '--colour --format specdoc'
27
30
  end
28
31
 
29
32
  Spec::Rake::SpecTask.new(:rcov) do |spec|
30
33
  spec.libs << 'lib' << 'spec'
31
34
  spec.pattern = 'spec/**/*_spec.rb'
32
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
33
39
  end
34
40
 
35
41
  task :spec => :check_dependencies
36
42
 
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
43
  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 CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.2
data/bin/git-db CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $:.unshift('/Users/david/Code/git-db/lib')
4
3
  require 'git-db'
5
4
  include GitDB
6
5
 
@@ -12,5 +11,7 @@ if git_command
12
11
  command = git_command[1]
13
12
  repository = git_command[2]
14
13
 
15
- Git::Commands.execute(command, [repository])
14
+ repository = repository.gsub(/\.git$/, '')
15
+
16
+ GitDB::Commands.execute(command, [repository])
16
17
  end
data/git-db.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{git-db}
8
- s.version = "0.0.2"
8
+ s.version = "0.1.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["David Dollar"]
12
- s.date = %q{2009-09-13}
12
+ s.date = %q{2009-09-14}
13
13
  s.default_executable = %q{git-db}
14
14
  s.description = %q{Database-based git server}
15
15
  s.email = %q{<ddollar@gmail.com>}
@@ -31,22 +31,30 @@ Gem::Specification.new do |s|
31
31
  "features/support/env.rb",
32
32
  "git-db.gemspec",
33
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",
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",
49
55
  "spec/git-db_spec.rb",
56
+ "spec/rcov.opts",
57
+ "spec/spec.opts",
50
58
  "spec/spec_helper.rb"
51
59
  ]
52
60
  s.homepage = %q{http://github.com/ddollar/git-db}
@@ -55,7 +63,13 @@ Gem::Specification.new do |s|
55
63
  s.rubygems_version = %q{1.3.5}
56
64
  s.summary = %q{Database-based git server}
57
65
  s.test_files = [
58
- "spec/git-db_spec.rb",
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",
59
73
  "spec/spec_helper.rb"
60
74
  ]
61
75
 
@@ -65,16 +79,13 @@ Gem::Specification.new do |s|
65
79
 
66
80
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
67
81
  s.add_development_dependency(%q<rspec>, [">= 0"])
68
- s.add_development_dependency(%q<yard>, [">= 0"])
69
- s.add_development_dependency(%q<cucumber>, [">= 0"])
82
+ s.add_runtime_dependency(%q<couchrest>, [">= 0"])
70
83
  else
71
84
  s.add_dependency(%q<rspec>, [">= 0"])
72
- s.add_dependency(%q<yard>, [">= 0"])
73
- s.add_dependency(%q<cucumber>, [">= 0"])
85
+ s.add_dependency(%q<couchrest>, [">= 0"])
74
86
  end
75
87
  else
76
88
  s.add_dependency(%q<rspec>, [">= 0"])
77
- s.add_dependency(%q<yard>, [">= 0"])
78
- s.add_dependency(%q<cucumber>, [">= 0"])
89
+ s.add_dependency(%q<couchrest>, [">= 0"])
79
90
  end
80
91
  end
data/lib/git-db.rb CHANGED
@@ -1,16 +1,68 @@
1
+ require 'base64'
2
+ require 'couchrest'
1
3
  require 'logger'
2
4
 
3
5
  module GitDB;
4
6
 
7
+ ## git constants #############################################################
8
+
9
+ OBJ_NONE = 0
10
+ OBJ_COMMIT = 1
11
+ OBJ_TREE = 2
12
+ OBJ_BLOB = 3
13
+ OBJ_TAG = 4
14
+ OBJ_OFS_DELTA = 6
15
+ OBJ_REF_DELTA = 7
16
+
17
+ ## git utility ###############################################################
18
+
19
+ def self.sha1_to_hex(sha)
20
+ hex = ""
21
+ sha.split('').each do |char|
22
+ val = char[0]
23
+ hex << (val >> 4).to_s(16)
24
+ hex << (val & 0xf).to_s(16)
25
+ end
26
+ hex
27
+ end
28
+
29
+ def self.hex_to_sha1(hex)
30
+ sha = ""
31
+ len = 0
32
+ until (len == hex.length)
33
+ val = (hex[len, 1].to_i(16) << 4)
34
+ val += hex[len+1, 1].to_i(16)
35
+ sha << val.chr
36
+ len += 2
37
+ end
38
+ sha
39
+ end
40
+
41
+ def self.null_sha1
42
+ "0000000000000000000000000000000000000000"
43
+ end
44
+
45
+ ## logging ###################################################################
46
+
5
47
  def self.logger
6
- @logger ||= File.open("/tmp/git-db.log", "w")
7
- #@logger ||= STDERR
48
+ @logger ||= STDERR
8
49
  end
9
50
 
10
51
  def self.log(message)
11
52
  logger.puts message
12
53
  end
54
+
55
+ ## database ##################################################################
56
+
57
+ def self.database(repository)
58
+ GitDB::Database.database(repository)
59
+ end
60
+
13
61
  end
14
62
 
15
- require 'git'
16
- require 'utility'
63
+ require 'git-db/commands'
64
+ require 'git-db/database'
65
+ require 'git-db/objects'
66
+ require 'git-db/pack'
67
+ require 'git-db/protocol'
68
+ require 'git-db/utility'
@@ -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,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