[NEW] Adding Bazaar support: Add commits.rb and head.rb adapter modules with unit...
authorMichael Adam <obnox@samba.org>
Thu, 5 Feb 2009 00:22:12 +0000 (01:22 +0100)
committerMichael Adam <obnox@samba.org>
Thu, 5 Feb 2009 00:34:03 +0000 (01:34 +0100)
Michael

lib/scm/adapters/bzr/commits.rb [new file with mode: 0644]
lib/scm/adapters/bzr/head.rb [new file with mode: 0644]
lib/scm/adapters/bzr_adapter.rb
test/unit/bzr_commits_test.rb [new file with mode: 0644]
test/unit/bzr_head_test.rb [new file with mode: 0644]

diff --git a/lib/scm/adapters/bzr/commits.rb b/lib/scm/adapters/bzr/commits.rb
new file mode 100644 (file)
index 0000000..71723e7
--- /dev/null
@@ -0,0 +1,76 @@
+module Scm::Adapters
+       class BzrAdapter < AbstractAdapter
+
+               # Return the number of commits in the repository following +since+.
+               def commit_count(since=0)
+                       commit_tokens(since || 0).size
+               end
+
+               # Return the list of commit tokens following +since+.
+               def commit_tokens(since=0)
+                       tokens = run("#{rev_list_command(since)} --log-format=line | cut -d':' -f1").split("\n")
+               end
+
+               # Returns a list of shallow commits (i.e., the diffs are not populated).
+               # Not including the diffs is meant to be a memory savings when
+               # we encounter massive repositories.  If you need all commits
+               # including diffs, you should use the each_commit() iterator,
+               # which only holds one commit in memory at a time.
+               def commits(since=0)
+                       log = run("#{rev_list_command(since)} | cat")
+                       a = Scm::Parsers::BzrParser.parse(log)
+               end
+
+               # Returns a single commit, including its diffs
+               def verbose_commit(token)
+                       log = run("cd '#{self.url}' && bzr log -v -c #{token}")
+                       Scm::Parsers::BzrParser.parse(log).first
+               end
+
+               # Yields each commit after +since+, including its diffs.
+               # The log is stored in a temporary file.
+               # This is designed to prevent excessive RAM usage when we
+               # encounter a massive repository.  Only a single commit is ever
+               # held in memory at once.
+               def each_commit(since=0)
+                       open_log_file(since) do |io|
+                               Scm::Parsers::BzrParser.parse(io) do |commit|
+                                       yield commit if block_given? && commit.token != since
+                               end
+                       end
+               end
+
+               # Not used by Ohloh proper, but handy for debugging and testing
+               def log(since=0)
+                       run "#{rev_list_command(since)} -v"
+               end
+
+               # Returns a file handle to the log.
+               # In our standard, the log should include everything AFTER
+               # +since+. However, bzr doesn't work that way; it returns
+               # everything after and INCLUDING +since+. Therefore, consumers
+               # of this file should check for and reject the duplicate commit.
+               def open_log_file(since=0)
+                       begin
+                               if since == head_token # There are no new commits
+                                       # As a time optimization, just create an empty
+                                       # file rather than fetch a log we know will be empty.
+                                       File.open(log_filename, 'w') { }
+                               else
+                                       run "#{rev_list_command} -v > #{log_filename}"
+                               end
+                               File.open(log_filename, 'r') { |io| yield io }
+                       ensure
+                               File.delete(log_filename) if FileTest.exist?(log_filename)
+                       end
+               end
+
+               def log_filename
+                 File.join('/tmp', (self.url).gsub(/\W/,'') + '.log')
+               end
+
+               def rev_list_command(since=nil)
+                       "cd '#{self.url}' && bzr log --forward -r #{since ? since+1 : 1}.."
+               end
+       end
+end
diff --git a/lib/scm/adapters/bzr/head.rb b/lib/scm/adapters/bzr/head.rb
new file mode 100644 (file)
index 0000000..d9025c7
--- /dev/null
@@ -0,0 +1,19 @@
+module Scm::Adapters
+       class BzrAdapter < AbstractAdapter
+               def head_token
+                       run("bzr revno -q #{url}").strip
+               end
+
+               def head
+                       verbose_commit(head_token)
+               end
+
+               def parent_tokens(commit)
+                       run("cd '#{url}' && bzr log -c $((#{commit.token} - 1)) --log-format=line | cut -f1 -d':'").split("\n")
+               end
+
+               def parents(commit)
+                       parent_tokens(commit).collect { |token| verbose_commit(token) }
+               end
+       end
+end
index ec631d9e252cb648a1dc5432ac673c91080e2329..0e9099b802070e1453e686a1de790cd7bc1aa407 100644 (file)
@@ -7,3 +7,5 @@ module Scm::Adapters
 end
 
 require 'lib/scm/adapters/bzr/validation'
+require 'lib/scm/adapters/bzr/commits'
+require 'lib/scm/adapters/bzr/head'
diff --git a/test/unit/bzr_commits_test.rb b/test/unit/bzr_commits_test.rb
new file mode 100644 (file)
index 0000000..8d00841
--- /dev/null
@@ -0,0 +1,61 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+       class BzrCommitsTest < Scm::Test
+
+               def test_commit_count
+                       with_bzr_repository('bzr') do |bzr|
+                               assert_equal 6, bzr.commit_count
+                               assert_equal 6, bzr.commit_count(0)
+                               assert_equal 5, bzr.commit_count(1)
+                               assert_equal 1, bzr.commit_count(5)
+                               assert_equal 0, bzr.commit_count(6)
+                       end
+               end
+
+               def test_commit_tokens
+                       with_bzr_repository('bzr') do |bzr|
+                               assert_equal ['1', '2', '3', '4', '5', '6'], bzr.commit_tokens
+                               assert_equal ['2', '3', '4', '5', '6'], bzr.commit_tokens(1)
+                               assert_equal ['6'], bzr.commit_tokens(5)
+                               assert_equal [], bzr.commit_tokens(6)
+                       end
+               end
+
+               def test_commits
+                       with_bzr_repository('bzr') do |bzr|
+                               assert_equal ['1', '2', '3', '4', '5', '6'], bzr.commits.collect { |c| c.token }
+                               assert_equal ['6'], bzr.commits(5).collect { |c| c.token }
+                               assert_equal [], bzr.commits(6).collect { |c| c.token }
+
+                               # Check that the diffs are not populated
+                               assert_equal [], bzr.commits.first.diffs
+                       end
+               end
+
+               def test_each_commit
+                       with_bzr_repository('bzr') do |bzr|
+                               commits = []
+                               bzr.each_commit do |c|
+                                       assert c.committer_name
+                                       assert c.committer_date.is_a?(Time)
+                                       assert c.message.length > 0
+                                       assert c.diffs.any?
+                                       # Check that the diffs are populated
+                                       c.diffs.each do |d|
+                                               assert d.action =~ /^[MAD]$/
+                                               assert d.path.length > 0
+                                       end
+                                       commits << c
+                               end
+
+                               # Make sure we cleaned up after ourselves
+                               assert !FileTest.exist?(bzr.log_filename)
+
+                               # Verify that we got the commits in forward chronological order
+                               assert_equal ['1', '2', '3', '4', '5', '6'], commits.collect{ |c| c.token }
+                       end
+               end
+       end
+end
+
diff --git a/test/unit/bzr_head_test.rb b/test/unit/bzr_head_test.rb
new file mode 100644 (file)
index 0000000..c4e3c06
--- /dev/null
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+       class BzrHeadTest < Scm::Test
+
+               def test_head_and_parents
+                       with_bzr_repository('bzr') do |bzr|
+                               assert_equal '6', bzr.head_token
+                               assert_equal '6', bzr.head.token
+                               assert bzr.head.diffs.any? # diffs should be populated
+
+                               assert_equal '5', bzr.parents(bzr.head).first.token
+                               assert bzr.parents(bzr.head).first.diffs.any?
+                       end
+               end
+
+       end
+end