--- /dev/null
+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
--- /dev/null
+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
end
require 'lib/scm/adapters/bzr/validation'
+require 'lib/scm/adapters/bzr/commits'
+require 'lib/scm/adapters/bzr/head'
--- /dev/null
+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
+
--- /dev/null
+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