diff --git a/.gitignore b/.gitignore
index 444fe1a3..b28b67a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
*.swp
+*.pyc
pkg/
+*.cache
+.byebug*
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 00000000..482f3b3e
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1,2 @@
+2.2.5
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..b844736e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: ruby
+env:
+ - TRAVIS_CONFIG_DIR=.travis
+before_install:
+ - cd $TRAVIS_CONFIG_DIR
+ - sh .travis_ssh_setup.sh
+install:
+ - sh .install_multiple_scms.sh
+ - sh .install_dependencies.sh
+ - gem install posix-spawn
+ - gem install nokogiri -v 1.6.8
+before_script:
+ - cd $TRAVIS_BUILD_DIR
diff --git a/.travis/.install_dependencies.sh b/.travis/.install_dependencies.sh
new file mode 100755
index 00000000..8cff1281
--- /dev/null
+++ b/.travis/.install_dependencies.sh
@@ -0,0 +1,15 @@
+#/usr/bin/env sh
+
+bazaar_plugins_path=`bzr --version | awk '/bzrlib:/ {print $2}'`
+
+cd "$bazaar_plugins_path/plugins"
+
+sudo bzr branch lp:bzr-xmloutput
+
+sudo mv bzr-xmloutput xmloutput
+
+gem install 'test-unit' -v '3.2.4'
+
+cd xmloutput
+
+python setup.py build_ext -i
diff --git a/.travis/.install_multiple_scms.sh b/.travis/.install_multiple_scms.sh
new file mode 100644
index 00000000..3fd09e34
--- /dev/null
+++ b/.travis/.install_multiple_scms.sh
@@ -0,0 +1,6 @@
+sudo sh -c 'echo "deb http://opensource.wandisco.com/ubuntu precise svn18" >> /etc/apt/sources.list.d/subversion18.list'
+sudo wget -q http://opensource.wandisco.com/wandisco-debian.gpg -O- | sudo apt-key add -
+sudo apt-add-repository -y ppa:git-core/ppa
+sudo apt-get update
+sudo apt-get install -y git subversion cvs bzr mercurial
+sudo ln -s /usr/bin/cvs /usr/bin/cvsnt
diff --git a/.travis/.travis_ssh_setup.sh b/.travis/.travis_ssh_setup.sh
new file mode 100644
index 00000000..8b560541
--- /dev/null
+++ b/.travis/.travis_ssh_setup.sh
@@ -0,0 +1,3 @@
+ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
+cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
+ssh-keyscan -t rsa `hostname` >> ~/.ssh/known_hosts
diff --git a/README b/README.md
similarity index 65%
rename from README
rename to README.md
index 533d2eb4..02771432 100644
--- a/README
+++ b/README.md
@@ -1,4 +1,6 @@
-= Ohloh SCM
+[![Ohloh SCM on Ohloh](https://www.ohloh.net/p/ohloh_scm/widgets/project_partner_badge.gif)](https://www.ohloh.net/p/ohloh_scm) [![Build Status](https://travis-ci.org/blackducksoftware/ohloh_scm.svg?branch=master)](https://travis-ci.org/blackducksoftware/ohloh_scm)
+
+# Ohloh SCM
The Ohloh source control management library
@@ -16,7 +18,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-== Overview
+## Overview
Ohloh SCM is an abstraction layer for source control management systems,
allowing an application to interoperate with various SCMs using a
@@ -25,7 +27,7 @@ single interface.
It was originally developed at Ohloh, and is used to generate
the reports at www.ohloh.net.
-== System Requirements
+## System Requirements
Ohloh SCM is developed on Mac OS X 10.5 and Ubuntu 6.06 LTS. Other Linux
environments should also work, but your mileage may vary.
@@ -39,24 +41,71 @@ shell. In order to pass the unit tests, all three systems must be installed
and on your path. Ohloh uses the following versions, and other versions are
totally unsupported at this time:
-cvsnt 2.5.03
-svn 1.4.2
-git 1.6.0.4
-hg 1.1.2
+cvsnt 2.5.03
+svn 1.4.2
+git 1.8.2.1
+hg 1.1.2
If you are using CVS instead of CVSNT, you can potentially try creating
a shell alias or symlink mapping 'cvsnt' to 'cvs'.
-== Running
+Ohloh SCM uses [posix-spawn](https://github.com/rtomayko/posix-spawn) to
+execute commands so ensure *posix-spawn* gem is installed
+
+``gem install posix-spawn``
+
+
+## Expedited Installation
+
+The .travis file contains the necessary sh files to install the needed libraries. Simply execute the following commands:
+
+sh .travis/.install_multiple_scms.sh
+sh .travis/.install_dependencies.sh
+
+
+## Usage with Bundler
+
+```
+gem 'ohloh_scm', git: 'https://github.com/blackducksw/ohloh_scm/', require: 'scm'
+gem 'posix-spawn'
+```
+## Running
+
+Ensure that cvsnt, svn, svnadmin, svnsync, git, and hg are all on your path. You'll also need to ensure that you have the xmloutput plugin installed for bazaar.
+
+### Installing The XmlOutput Plugin
+ $ cd ~
+ $ mkdir .bazaar
+ $ cd .bazaar
+ $ mkdir plugins
+ $ cd plugins
+
+Now checkout the latest version of the xmloutput plugin (0.8.8 as of 11/21/2011).
+
+ $ bzr branch lp:~amujumdar/bzr-xmloutput/emit_authors
+
+The default checkout directory is poorly named and bazaar will complain about this unless it is renamed.
+
+ $ mv emit_authors xmloutput
+
+Now you just need to install the xmloutput plugin
+
+ $ cd xmloutput
+ $ python setup.py build_ext -i
+
+Verify that the plugin was installed correctly
+
+ $ bzr plugins
+
+You should see some text like "xmloutput 0.8.8"
-Ensure that cvsnt, svn, svnadmin, svnsync, git, and hg are all on your path.
Then you can run the unit tests:
-$ rake
+ $ rake
-You can load the library into your own Ruby application by requiring lib/scm.rb.
+You can load the library into your own Ruby application by requiring lib/ohloh_scm.rb.
-= Functionality
+# Functionality
For each tracked repository, Ohloh uses the SCM library to maintain a private
local mirror. The SCM library hides the differences between source control
@@ -74,27 +123,23 @@ tasks on the local mirror:
1. Pull changes -- From a remote repository URL, pull any changes to the local
mirror. This step may involve conversion from one system to another.
-
2. Push changes -- From the local mirror, push any changes to another Ohloh
server. This is required to create backup copies and perform load balancing on
the Ohloh cluster, and typically occurs over ssh.
-
3. Commit log -- Given the last known commit, report the list of new commits,
if any, including their diffs.
-
4. Cat file or parent -- Given a commit, return either the contents of a
single file, or that file's previous contents.
-
5. Export tree -- Given a commit, export the entire contents of the source tree
to a specified temp directory.
The adapter must also implement validation routines used to filter user inputs
and confirm the presence of the remote server.
-= Contact Ohloh
+# Contact Ohloh
For more information visit the Ohloh website:
- http://labs.ohloh.net
+[Ohloh Labs](http://labs.ohloh.net)
You can reach Ohloh via email at:
- info@ohloh.net
+[info@ohloh.net](mailto:info@ohloh.net)
diff --git a/Rakefile b/Rakefile
index c6c75d15..cf03307d 100644
--- a/Rakefile
+++ b/Rakefile
@@ -3,7 +3,7 @@ require 'rake/clean'
require 'rake/testtask'
require 'rubygems'
-require 'rake/gempackagetask'
+require 'rubygems/package_task'
spec = Gem::Specification.new do |s|
s.name = 'ohloh_scm'
@@ -15,14 +15,13 @@ spec = Gem::Specification.new do |s|
s.summary = 'Ohloh Source Control Management Library'
s.files = FileList['README', 'COPYING', '{bin,lib,test}/**/*']
s.require_path = 'lib'
- s.autorequire = 'scm'
s.executables = 'ohlog'
s.has_rdoc = true
s.extra_rdoc_files = ['README']
s.test_files = FileList["test/**/*"]
end
-Rake::GemPackageTask.new(spec) do |pkg|
+Gem::PackageTask.new(spec) do |pkg|
pkg.need_tar = true
pkg.need_zip = true
end
diff --git a/bin/ohlog b/bin/ohlog
index d46b7a52..d814afc1 100755
--- a/bin/ohlog
+++ b/bin/ohlog
@@ -1,12 +1,12 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../lib/scm'
+require File.dirname(__FILE__) + '/../lib/ohloh_scm'
# This is a simple command line tool which parses CVS and Subversion logs.
# It is not used by the main Ohloh system.
#
# I use it primarily to help debug Ohloh behavior. It's a convenient way
# to turn an enormously long CVS log into something readable.
-module Scm::Parsers
+module OhlohScm::Parsers
class CommandLine
attr_accessor :paths, :writer
@@ -26,10 +26,12 @@ Ohloh source control log parser
[option] can be one of the following:
--cvs Parse a CVS rlog
+ --git Parse a Git log (generated with or without --name-status option)
--svn Parse a Subversion log
--svn-xml Parse a Subversion XML log
--hg Parse a Mercurial log
--bzr Parse a Bazaar log
+ --bzr-xml Parse a Bazaar XML log
-h, --human Output result as a human-readable log (default)
-x, --xml Output result as an XML log
@@ -49,6 +51,8 @@ Examples:
bzr log -v | ohlog --bzr
+ bzr xmllog -v | ohlog --bzr
+
HELP
end
@@ -56,6 +60,10 @@ HELP
parse CvsParser
end
+ def git
+ parse GitParser
+ end
+
def svn
parse SvnParser
end
@@ -72,6 +80,10 @@ HELP
parse BzrParser
end
+ def bzr_xml
+ parse BzrXmlParser
+ end
+
def parse(parser)
self.writer ||= HumanWriter.new(STDOUT)
@@ -101,6 +113,8 @@ HELP
case option
when '--cvs'
self.subcommand = :cvs
+ when '--git'
+ self.subcommand = :git
when '--svn'
self.subcommand = :svn
when '--svn-xml'
@@ -109,6 +123,8 @@ HELP
self.subcommand = :hg
when '--bzr'
self.subcommand = :bzr
+ when '--bzr-xml'
+ self.subcommand = :bzr_xml
when '-h', '--human'
self.writer = HumanWriter.new(STDOUT)
when '-x', '--xml'
diff --git a/bin/string_encoder b/bin/string_encoder
new file mode 100755
index 00000000..a0e6e69a
--- /dev/null
+++ b/bin/string_encoder
@@ -0,0 +1,13 @@
+#! /usr/bin/env ruby
+# Replaces invalid utf-8 characters with �.
+#
+# Usage:
+# $ cat some_file | string_encoder
+
+while input = gets
+ if input.to_s.valid_encoding?
+ puts input
+ else
+ puts input.encode('UTF-8', 'binary', invalid: :replace, undef: :replace)
+ end
+end
diff --git a/lib/ohloh_scm.rb b/lib/ohloh_scm.rb
new file mode 100644
index 00000000..cf0c9299
--- /dev/null
+++ b/lib/ohloh_scm.rb
@@ -0,0 +1,38 @@
+module OhlohScm
+end
+
+#require 'rbconfig'
+
+#$: << File.join(File.dirname(__FILE__),"..")
+
+require_relative 'ohloh_scm/shellout'
+require_relative 'ohloh_scm/scratch_dir'
+require_relative 'ohloh_scm/commit'
+require_relative 'ohloh_scm/diff'
+
+require_relative 'ohloh_scm/adapters/abstract_adapter'
+require_relative 'ohloh_scm/adapters/cvs_adapter'
+require_relative 'ohloh_scm/adapters/svn_adapter'
+require_relative 'ohloh_scm/adapters/svn_chain_adapter'
+require_relative 'ohloh_scm/adapters/git_adapter'
+require_relative 'ohloh_scm/adapters/hg_adapter'
+require_relative 'ohloh_scm/adapters/hglib_adapter'
+require_relative 'ohloh_scm/adapters/bzr_adapter'
+require_relative 'ohloh_scm/adapters/bzrlib_adapter'
+require_relative 'ohloh_scm/adapters/factory'
+
+require_relative 'ohloh_scm/parsers/parser'
+require_relative 'ohloh_scm/parsers/branch_number'
+require_relative 'ohloh_scm/parsers/cvs_parser'
+require_relative 'ohloh_scm/parsers/svn_parser'
+require_relative 'ohloh_scm/parsers/svn_xml_parser'
+require_relative 'ohloh_scm/parsers/git_parser'
+require_relative 'ohloh_scm/parsers/git_styled_parser'
+require_relative 'ohloh_scm/parsers/hg_parser'
+require_relative 'ohloh_scm/parsers/hg_styled_parser'
+require_relative 'ohloh_scm/parsers/bzr_xml_parser'
+require_relative 'ohloh_scm/parsers/bzr_parser'
+
+require_relative 'ohloh_scm/parsers/array_writer'
+require_relative 'ohloh_scm/parsers/xml_writer'
+require_relative 'ohloh_scm/parsers/human_writer'
diff --git a/lib/ohloh_scm/adapters/abstract/misc.rb b/lib/ohloh_scm/adapters/abstract/misc.rb
new file mode 100644
index 00000000..a312ab15
--- /dev/null
+++ b/lib/ohloh_scm/adapters/abstract/misc.rb
@@ -0,0 +1,13 @@
+module OhlohScm::Adapters
+ class AbstractAdapter
+
+ def is_merge_commit?(commit)
+ false
+ end
+
+ def tags
+ []
+ end
+
+ end
+end
diff --git a/lib/scm/adapters/abstract/sha1.rb b/lib/ohloh_scm/adapters/abstract/sha1.rb
similarity index 88%
rename from lib/scm/adapters/abstract/sha1.rb
rename to lib/ohloh_scm/adapters/abstract/sha1.rb
index 8a00bbad..cd1c5447 100644
--- a/lib/scm/adapters/abstract/sha1.rb
+++ b/lib/ohloh_scm/adapters/abstract/sha1.rb
@@ -1,8 +1,8 @@
-require 'sha1'
+require 'digest/sha1'
NULL_SHA1 = '0000000000000000000000000000000000000000' unless defined?(NULL_SHA1)
-module Scm::Adapters
+module OhlohScm::Adapters
class AbstractAdapter
# This file provides SHA1 computation helpers for source control systems that
@@ -11,7 +11,7 @@ class AbstractAdapter
# that match those generated natively by Git.
def compute_sha1(blob)
- blob.to_s == '' ? NULL_SHA1 : SHA1.sha1("blob #{blob.length}\0#{blob}").to_s
+ blob.to_s == '' ? NULL_SHA1 : Digest::SHA1.hexdigest("blob #{blob.length}\0#{blob}")
end
# Populates the SHA1 values for each diff in a commit.
diff --git a/lib/scm/adapters/abstract/system.rb b/lib/ohloh_scm/adapters/abstract/system.rb
similarity index 78%
rename from lib/scm/adapters/abstract/system.rb
rename to lib/ohloh_scm/adapters/abstract/system.rb
index d3b452ac..922c14ed 100644
--- a/lib/scm/adapters/abstract/system.rb
+++ b/lib/ohloh_scm/adapters/abstract/system.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
require 'logger'
class AbstractAdapter
def self.logger
@@ -17,7 +17,7 @@ def logger
# Raises an exception if the shell returns non-zero exit code.
def self.run(cmd)
logger.debug { cmd }
- status, out, err = systemu(cmd)
+ status, out, err = Shellout.execute(cmd)
raise RuntimeError.new("#{cmd} failed: #{out}\n#{err}") if status.exitstatus != 0
out
end
@@ -27,11 +27,11 @@ def run(cmd)
end
# As above, but does not raise an exception when an error occurs.
- # Returns two values, stdout and stderr.
+ # Returns three values: stdout, stderr, and process exit code
def self.run_with_err(cmd)
logger.debug { cmd }
- status, out, err = systemu(cmd)
- [out, err]
+ status, out, err = Shellout.new.run(cmd)
+ [out, err, status]
end
def run_with_err(cmd)
diff --git a/lib/scm/adapters/abstract/validation.rb b/lib/ohloh_scm/adapters/abstract/validation.rb
similarity index 96%
rename from lib/scm/adapters/abstract/validation.rb
rename to lib/ohloh_scm/adapters/abstract/validation.rb
index aa3b3c36..78c47e7e 100644
--- a/lib/scm/adapters/abstract/validation.rb
+++ b/lib/ohloh_scm/adapters/abstract/validation.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class AbstractAdapter
# The full regex that permits all possible URLs supported by the source control system.
def self.url_regex
@@ -30,7 +30,7 @@ def validate_url
def validate_branch_name
return nil if @branch_name.to_s == ''
- return [:branch_name, "The branch name must not be longer than 50 characters."] unless @branch_name.length <= 50
+ return [:branch_name, "The branch name must not be longer than 80 characters."] unless @branch_name.length <= 80
return [:branch_name, "The branch name may contain only letters, numbers, spaces, and the special characters '_', '-', '+', '/', '^', and '.'"] unless @branch_name =~ /^[A-Za-z0-9_^\-\+\.\/\ ]+$/
end
diff --git a/lib/ohloh_scm/adapters/abstract_adapter.rb b/lib/ohloh_scm/adapters/abstract_adapter.rb
new file mode 100644
index 00000000..f14e2d68
--- /dev/null
+++ b/lib/ohloh_scm/adapters/abstract_adapter.rb
@@ -0,0 +1,32 @@
+module OhlohScm::Adapters
+ class AbstractAdapter
+ attr_accessor :url, :branch_name, :username, :password, :errors, :public_urls_only
+ attr_writer :temp_folder
+
+ def initialize(params={})
+ params.each { |k,v| send(k.to_s + '=', v) if respond_to?(k.to_s + '=') }
+ end
+
+ # Handy for test overrides
+ def metaclass
+ class << self
+ self
+ end
+ end
+
+ # Returns path to the string_encoder binary.
+ # For use with inline system commands like `run`.
+ def string_encoder
+ File.expand_path('../../../../bin/string_encoder', __FILE__)
+ end
+
+ def temp_folder
+ @temp_folder || '/tmp'
+ end
+ end
+end
+
+require_relative 'abstract/system'
+require_relative 'abstract/validation'
+require_relative 'abstract/sha1'
+require_relative 'abstract/misc'
diff --git a/lib/ohloh_scm/adapters/bzr/cat_file.rb b/lib/ohloh_scm/adapters/bzr/cat_file.rb
new file mode 100644
index 00000000..0c21b26c
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzr/cat_file.rb
@@ -0,0 +1,25 @@
+module OhlohScm::Adapters
+ class BzrAdapter < AbstractAdapter
+ def cat_file(commit, diff)
+ cat(commit.token, diff.path)
+ end
+
+ def cat_file_parent(commit, diff)
+ first_parent_token = parent_tokens(commit).first
+ cat(first_parent_token, diff.path) if first_parent_token
+ end
+
+ def cat(revision, path)
+ out, err, status = run_with_err("cd '#{url}' && bzr cat --name-from-revision -r #{to_rev_param(revision)} '#{escape(path)}'")
+ return nil if err =~ / is not present in revision /
+ raise RuntimeError.new(err) unless status == 0
+ out
+ end
+
+ # Bzr doesn't like it when the filename includes a colon
+ # Also, fix the case where the filename includes a single quote
+ def escape(path)
+ path.gsub(/[:]/) { |c| '\\' + c }.gsub("'", "''")
+ end
+ end
+end
diff --git a/lib/ohloh_scm/adapters/bzr/commits.rb b/lib/ohloh_scm/adapters/bzr/commits.rb
new file mode 100644
index 00000000..25fda009
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzr/commits.rb
@@ -0,0 +1,100 @@
+module OhlohScm::Adapters
+ class BzrAdapter < AbstractAdapter
+
+ # Return the number of commits in the repository following +after+.
+ def commit_count(opts={})
+ commit_tokens(opts).size
+ end
+
+ # Return the list of commit tokens following +after+.
+ def commit_tokens(opts={})
+ commits(opts).map(&:token)
+ 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(opts={})
+ after = opts[:after]
+ log = run("#{rev_list_command(opts)} | cat")
+ a = OhlohScm::Parsers::BzrXmlParser.parse(log)
+
+ if after && i = a.index { |commit| commit.token == after }
+ a[(i+1)..-1]
+ else
+ a
+ end
+ end
+
+ # Returns a single commit, including its diffs
+ def verbose_commit(token)
+ log = run("cd '#{self.url}' && bzr xmllog --show-id -v --limit 1 -c #{to_rev_param(token)}")
+ OhlohScm::Parsers::BzrXmlParser.parse(log).first
+ end
+
+ # Yields each commit after +after+, 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(opts={})
+ after = opts[:after]
+ skip_commits = !!after # Don't emit any commits until the 'after' resume point passes
+
+ open_log_file(opts) do |io|
+ OhlohScm::Parsers::BzrXmlParser.parse(io) do |commit|
+ yield remove_directories(commit) if block_given? && !skip_commits
+ skip_commits = false if commit.token == after
+ end
+ end
+ end
+
+ # Ohloh tracks only files, not directories. This function removes directories
+ # from the commit diffs.
+ def remove_directories(commit)
+ commit.diffs.delete_if { |d| d.path[-1..-1] == '/' }
+ commit
+ end
+
+
+ # Not used by Ohloh proper, but handy for debugging and testing
+ def log(opts={})
+ run "#{rev_list_command(opts)} -v"
+ end
+
+ # Returns a file handle to the log.
+ # In our standard, the log should include everything AFTER
+ # +after+. However, bzr doesn't work that way; it returns
+ # everything after and INCLUDING +after+. Therefore, consumers
+ # of this file should check for and reject the duplicate commit.
+ def open_log_file(opts={})
+ after = opts[:after]
+ begin
+ if after == 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') { |f| f.puts '' }
+ else
+ run "#{rev_list_command(opts)} -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(temp_folder, (self.url).gsub(/\W/,'') + '.log')
+ end
+
+ # Uses xmllog command for output to be used by BzrXmlParser.
+ def rev_list_command(opts={})
+ after = opts[:after]
+ trunk_only = opts[:trunk_only] ? '--levels=1' : '--include-merges'
+ "cd '#{self.url}' && bzr xmllog --show-id --forward #{trunk_only} -r #{to_rev_param(after)}.."
+ end
+
+ end
+end
diff --git a/lib/scm/adapters/bzr/head.rb b/lib/ohloh_scm/adapters/bzr/head.rb
similarity index 94%
rename from lib/scm/adapters/bzr/head.rb
rename to lib/ohloh_scm/adapters/bzr/head.rb
index d35eba95..5abaedcf 100644
--- a/lib/scm/adapters/bzr/head.rb
+++ b/lib/ohloh_scm/adapters/bzr/head.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class BzrAdapter < AbstractAdapter
def head_token
run("bzr log --limit 1 --show-id #{url} 2> /dev/null | grep ^revision-id | cut -f2 -d' '").strip
diff --git a/lib/ohloh_scm/adapters/bzr/misc.rb b/lib/ohloh_scm/adapters/bzr/misc.rb
new file mode 100644
index 00000000..bf9cac1d
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzr/misc.rb
@@ -0,0 +1,56 @@
+module OhlohScm::Adapters
+ class BzrAdapter < AbstractAdapter
+ def exist?
+ begin
+ head_token.to_s.length > 0
+ rescue
+ logger.debug { $! }
+ false
+ end
+ end
+
+ def ls_tree(token)
+ run("cd #{path} && bzr ls -V -r #{to_rev_param(token)}").split("\n")
+ end
+
+ def to_rev_param(r=nil)
+ case r
+ when nil
+ 1
+ when Fixnum
+ r.to_s
+ when /^\d+$/
+ r
+ else
+ "'revid:#{r.to_s}'"
+ end
+ end
+
+ def is_merge_commit?(commit)
+ parent_tokens(commit).size > 1
+ end
+
+ def export_tag(dest_dir, tag_name)
+ run "cd '#{path}' && bzr export -r tag:#{tag_name} #{dest_dir}"
+ end
+
+ def export(dest_dir, token=head_token)
+ # Unlike other SCMs, Bzr doesn't simply place the contents into dest_dir.
+ # It actually *creates* dest_dir. Since it should already exist at this point,
+ # first we have to delete it.
+ Dir.delete(dest_dir) if File.exist?(dest_dir)
+
+ run "cd '#{url}' && bzr export --format=dir -r #{to_rev_param(token)} '#{dest_dir}'"
+ end
+
+ def tags
+ tag_strings = run("cd '#{url}' && bzr tags").split(/\n/)
+ tag_strings.map do |tag_string|
+ tag_name, rev = tag_string.split(/\s+/)
+ next if rev == '?' || tag_name == '....'
+ time_string = run("cd '#{ url }' && bzr log -r #{ rev } | grep 'timestamp:' | sed 's/timestamp://'")
+ [tag_name, rev, Time.parse(time_string)]
+ end.compact
+ end
+ end
+end
diff --git a/lib/scm/adapters/bzr/pull.rb b/lib/ohloh_scm/adapters/bzr/pull.rb
similarity index 81%
rename from lib/scm/adapters/bzr/pull.rb
rename to lib/ohloh_scm/adapters/bzr/pull.rb
index 196b5c06..a02f9646 100644
--- a/lib/scm/adapters/bzr/pull.rb
+++ b/lib/ohloh_scm/adapters/bzr/pull.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class BzrAdapter < AbstractAdapter
def pull(from, &block)
@@ -12,7 +12,7 @@ def pull(from, &block)
run "rm -rf '#{self.url}'"
run "bzr branch '#{from.url}' '#{self.url}'"
else
- run "cd '#{self.url}' && bzr pull --overwrite '#{from.url}'"
+ run "cd '#{self.url}' && bzr revert && bzr pull --overwrite '#{from.url}'"
end
yield(1,1) if block_given? # Progress bar callback
diff --git a/lib/scm/adapters/bzr/push.rb b/lib/ohloh_scm/adapters/bzr/push.rb
similarity index 91%
rename from lib/scm/adapters/bzr/push.rb
rename to lib/ohloh_scm/adapters/bzr/push.rb
index e6875b9e..a84c1709 100644
--- a/lib/scm/adapters/bzr/push.rb
+++ b/lib/ohloh_scm/adapters/bzr/push.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class BzrAdapter < AbstractAdapter
def push(to, &block)
@@ -16,7 +16,7 @@ def push(to, &block)
run "scp -rpqB #{bzr_path} #{to.hostname}:#{to.path}"
end
else
- run "cd '#{self.url}' && bzr push '#{to.url}'"
+ run "cd '#{self.url}' && bzr revert && bzr push '#{to.url}'"
end
yield(1,1) if block_given? # Progress bar callback
diff --git a/lib/scm/adapters/bzr/validation.rb b/lib/ohloh_scm/adapters/bzr/validation.rb
similarity index 51%
rename from lib/scm/adapters/bzr/validation.rb
rename to lib/ohloh_scm/adapters/bzr/validation.rb
index 468818b2..33adcfaa 100644
--- a/lib/scm/adapters/bzr/validation.rb
+++ b/lib/ohloh_scm/adapters/bzr/validation.rb
@@ -1,11 +1,11 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class BzrAdapter < AbstractAdapter
def self.url_regex
- /^((http|https|bzr\+ssh|file):\/\/((\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/)?)?[A-Za-z0-9_\-\.\/\~\+]*$/
+ /^((((http|https|bzr|bzr\+ssh|file):\/\/((\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/)?)|(lp:[A-Za-z0-9_\-\.\~])))?[A-Za-z0-9_@\-\.\/\~\+]*$/
end
def self.public_url_regex
- /^(http|https):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/[A-Za-z0-9_\-\.\/\~\+]*$/
+ /^(((http|https|bzr):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/)|(lp:[A-Za-z0-9_\-\.\~]))[A-Za-z0-9_\-\.\/\~\+]*$/
end
def validate_server_connection
diff --git a/lib/ohloh_scm/adapters/bzr_adapter.rb b/lib/ohloh_scm/adapters/bzr_adapter.rb
new file mode 100644
index 00000000..f0fd82d6
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzr_adapter.rb
@@ -0,0 +1,15 @@
+module OhlohScm::Adapters
+ class BzrAdapter < AbstractAdapter
+ def english_name
+ "Bazaar"
+ end
+ end
+end
+
+require_relative 'bzr/validation'
+require_relative 'bzr/commits'
+require_relative 'bzr/head'
+require_relative 'bzr/cat_file'
+require_relative 'bzr/misc'
+require_relative 'bzr/pull'
+require_relative 'bzr/push'
diff --git a/lib/ohloh_scm/adapters/bzrlib/bzrlib_pipe_client.rb b/lib/ohloh_scm/adapters/bzrlib/bzrlib_pipe_client.rb
new file mode 100644
index 00000000..d200c056
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzrlib/bzrlib_pipe_client.rb
@@ -0,0 +1,71 @@
+require 'rubygems'
+require 'posix/spawn'
+
+class BzrPipeClient
+ def initialize(repository_url)
+ @repository_url = repository_url
+ @py_script = File.dirname(__FILE__) + '/bzrlib_pipe_server.py'
+ end
+
+ def start
+ @pid, @stdin, @stdout, @stderr = POSIX::Spawn::popen4 "python #{@py_script}"
+ open_repository
+ end
+
+ def open_repository
+ send_command("REPO_OPEN|#{@repository_url}")
+ end
+ def cat_file(revision, file)
+ send_command("CAT_FILE|#{revision}|#{file}")
+ end
+
+ def parent_tokens(revision)
+ send_command("PARENT_TOKENS|#{revision}").split('|')
+ end
+
+ def send_command(cmd)
+ # send the command
+ @stdin.puts cmd
+ @stdin.flush
+ return if cmd == "QUIT"
+
+ # get status on stderr, first letter indicates state,
+ # remaing value indicates length of the file content
+ status = @stderr.read(10)
+ flag = status[0,1]
+ size = status[1,9].to_i
+ if flag == 'F'
+ return nil
+ elsif flag == 'E'
+ error = @stdout.read(size)
+ raise RuntimeError.new("Exception in server process\n#{error}")
+ end
+
+ # read content from stdout
+ return @stdout.read(size)
+ end
+
+ def shutdown
+ send_command("QUIT")
+ [@stdout, @stdin, @stderr].each { |io| io.close unless io.closed? }
+ Process.waitpid(@pid, Process::WNOHANG)
+ end
+end
+
+def cat_all_files(client, datafile)
+ count = 0
+ bytes = 0
+ File.open(datafile).each do |line|
+ parts = line.split('|')
+ count = count + 1
+ bytes = bytes + client.cat_file(parts[0], parts[1]).size
+ puts "file=#{count}, bytes=#{bytes}"
+ end
+end
+
+def all_parent_tokens(client, datafile)
+ File.open(datafile).each do |line|
+ parts = line.split('|')
+ puts client.parent_tokens(parts[0])
+ end
+end
diff --git a/lib/ohloh_scm/adapters/bzrlib/bzrlib_pipe_server.py b/lib/ohloh_scm/adapters/bzrlib/bzrlib_pipe_server.py
new file mode 100644
index 00000000..f751cf81
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzrlib/bzrlib_pipe_server.py
@@ -0,0 +1,100 @@
+from bzrlib.branch import Branch
+from bzrlib.revisionspec import RevisionSpec
+import os
+import sys
+import time
+import traceback
+import logging
+
+class BzrPipeServer:
+ def __init__(self, repository_url):
+ self.branch = Branch.open(repository_url)
+ self.branch.lock_read()
+
+ def get_file_content(self, filename, revision):
+ rev_spec = RevisionSpec.from_string(revision)
+ tree = rev_spec.as_tree(self.branch)
+ file_id = tree.path2id(unicode(filename, 'utf8'))
+ if file_id == None:
+ return None
+ content = tree.get_file_text(file_id)
+ return content
+
+ def get_parent_tokens(self, revision):
+ rev_spec = RevisionSpec.from_string(revision)
+ tree = rev_spec.as_tree(self.branch)
+ parents = tree.get_parent_ids()
+ return parents
+
+ def cleanup(self):
+ self.branch.unlock()
+
+class Command:
+ def __init__(self, line):
+ self.args = line.rstrip().split('|')
+
+ def get_action(self):
+ return self.args[0]
+
+ def get_arg(self, num):
+ return self.args[num]
+
+def send_status(code, data_len):
+ sys.stderr.write('%s%09d' % (code, data_len))
+ sys.stderr.flush()
+
+def send_success(data_len=0):
+ send_status('T', data_len)
+
+def send_failure(data_len=0):
+ send_status('F', data_len)
+
+def send_error(data_len=0):
+ send_status('E', data_len)
+
+def send_data(result):
+ sys.stdout.write(result)
+ sys.stdout.flush()
+
+def exit_delayed(status, delay=1):
+ time.sleep(delay)
+ sys.exit(status)
+
+def command_loop():
+ while True:
+ cmd = Command(sys.stdin.readline())
+ if cmd.get_action() == 'REPO_OPEN':
+ commander = BzrPipeServer(cmd.get_arg(1))
+ send_success()
+ elif cmd.get_action() == 'CAT_FILE':
+ content = commander.get_file_content(cmd.get_arg(2), cmd.get_arg(1))
+ if content == None:
+ send_failure()
+ else:
+ send_success(len(content))
+ send_data(content)
+ elif cmd.get_action() == 'PARENT_TOKENS':
+ tokens = commander.get_parent_tokens(cmd.get_arg(1))
+ tokens = '|'.join(tokens)
+ send_success(len(tokens))
+ send_data(tokens)
+ elif cmd.get_action() == 'QUIT':
+ commander.cleanup()
+ send_success()
+ exit_delayed(status=0)
+ else:
+ error = "Invalid Command - %s" % cmd.get_action()
+ send_error(len(error))
+ send_data(error)
+ exit_delayed(status=1)
+
+if __name__ == "__main__":
+ try:
+ handler = logging.FileHandler(os.devnull)
+ logging.getLogger('bzr').addHandler(handler)
+ command_loop()
+ except:
+ exc_trace = traceback.format_exc()
+ send_error(len(exc_trace))
+ send_data(exc_trace)
+ exit_delayed(status=1)
diff --git a/lib/ohloh_scm/adapters/bzrlib/cat_file.rb b/lib/ohloh_scm/adapters/bzrlib/cat_file.rb
new file mode 100644
index 00000000..111c67a4
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzrlib/cat_file.rb
@@ -0,0 +1,9 @@
+module OhlohScm::Adapters
+ class BzrlibAdapter < BzrAdapter
+
+ def cat(revision, path)
+ content = bzr_client.cat_file(revision, path)
+ end
+
+ end
+end
diff --git a/lib/ohloh_scm/adapters/bzrlib/head.rb b/lib/ohloh_scm/adapters/bzrlib/head.rb
new file mode 100644
index 00000000..147e076a
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzrlib/head.rb
@@ -0,0 +1,9 @@
+module OhlohScm::Adapters
+ class BzrlibAdapter < BzrAdapter
+
+ def parent_tokens(commit)
+ bzr_client.parent_tokens(commit.token)
+ end
+
+ end
+end
diff --git a/lib/ohloh_scm/adapters/bzrlib_adapter.rb b/lib/ohloh_scm/adapters/bzrlib_adapter.rb
new file mode 100644
index 00000000..bde29fa5
--- /dev/null
+++ b/lib/ohloh_scm/adapters/bzrlib_adapter.rb
@@ -0,0 +1,25 @@
+require 'rubygems'
+
+require_relative 'bzrlib/bzrlib_pipe_client'
+module OhlohScm::Adapters
+ class BzrlibAdapter < BzrAdapter
+
+ def setup
+ bzr_client = BzrPipeClient.new(url)
+ bzr_client.start
+ bzr_client
+ end
+
+ def bzr_client
+ @bzr_client ||= setup
+ end
+
+ def cleanup
+ @bzr_client.shutdown
+ end
+
+ end
+end
+
+require_relative 'bzrlib/head'
+require_relative 'bzrlib/cat_file'
diff --git a/lib/scm/adapters/cvs/commits.rb b/lib/ohloh_scm/adapters/cvs/commits.rb
similarity index 68%
rename from lib/scm/adapters/cvs/commits.rb
rename to lib/ohloh_scm/adapters/cvs/commits.rb
index f17721aa..a3174183 100644
--- a/lib/scm/adapters/cvs/commits.rb
+++ b/lib/ohloh_scm/adapters/cvs/commits.rb
@@ -1,29 +1,34 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class CvsAdapter
- def commits(since=nil)
+ def commits(opts={})
+ after = opts[:after]
result = []
- open_log_file(since) do |io|
- result = Scm::Parsers::CvsParser.parse(io, :branch_name => branch_name)
+
+ open_log_file(opts) do |io|
+ result = OhlohScm::Parsers::CvsParser.parse(io, :branch_name => branch_name)
end
+ # Git converter needs a backpointer to the scm for each commit
+ result.each { |c| c.scm = self }
+
return result if result.size == 0 # Nothing found; we're done here.
- return result if since.to_s == '' # We requested everything, so just return everything.
+ return result if after.to_s == '' # We requested everything, so just return everything.
# We must now remove any duplicates caused by timestamp fudge factors,
- # and only return commits with timestamp > since.
+ # and only return commits with timestamp > after.
- # If the first commit is newer than since, then the whole list is new and we can simply return.
- return result if parse_time(result.first.token) > parse_time(since)
+ # If the first commit is newer than after, then the whole list is new and we can simply return.
+ return result if parse_time(result.first.token) > parse_time(after)
# Walk the list of commits to find the first new one, throwing away all of the old ones.
# I want to string-compare timestamps without converting to dates objects (I think it's faster).
# Some CVS servers print dates as 2006/01/02 03:04:05, others as 2006-01-02 03:04:05.
# To work around this, we'll build a regex that matches either date format.
- re = Regexp.new(since.gsub(/[\/-]/, '.'))
+ re = Regexp.new(after.gsub(/[\/-]/, '.'))
result.each_index do |i|
- if result[i].token =~ re # We found the match for since
+ if result[i].token =~ re # We found the match for after
if i == result.size-1
return [] # There aren't any new commits.
else
@@ -32,7 +37,7 @@ def commits(since=nil)
end
end
- # Something bad is going on: 'since' does not match any timestamp in the rlog.
+ # Something bad is going on: 'after' does not match any timestamp in the rlog.
# This is very rare, but it can happen.
#
# Often this means that the *last* time we ran commits(), there was some kind of
@@ -41,8 +46,8 @@ def commits(since=nil)
#
# There's no work around for this condition here in the code, but there are some things
# you can try manually to fix the problem. Typically, you can try throwing way the
- # commit associated with 'since' and fetching it again (git reset --hard HEAD^).
- raise RuntimeError.new("token '#{since}' not found in rlog.")
+ # commit associated with 'after' and fetching it again (git reset --hard HEAD^).
+ raise RuntimeError.new("token '#{after}' not found in rlog.")
end
# Gets the rlog of the repository and saves it in a temporary file.
@@ -54,9 +59,11 @@ def commits(since=nil)
# In any case, to be sure not to miss any commits, this method subtracts 10 seconds from the provided timestamp.
# This means that the returned log might actually contain a few revisions that predate the requested time.
# That's better than missing revisions completely! Just be sure to check for duplicates.
- def open_log_file(since=nil)
+ def open_log_file(opts={})
+ after = opts[:after]
begin
- run "cvsnt -d #{self.url} rlog #{opt_branch} #{opt_time(since)} '#{self.module_name}' > #{rlog_filename}"
+ ensure_host_key
+ run "cvsnt -d #{self.url} rlog #{opt_branch} #{opt_time(after)} '#{self.module_name}' | #{ string_encoder } > #{rlog_filename}"
File.open(rlog_filename, 'r') do |file|
yield file
end
@@ -65,9 +72,9 @@ def open_log_file(since=nil)
end
end
- def opt_time(since=nil)
- if since
- most_recent_time = parse_time(since) - 10
+ def opt_time(after=nil)
+ if after
+ most_recent_time = parse_time(after) - 10
" -d '#{most_recent_time.strftime('%Y-%m-%d %H:%M:%S')}Z<#{Time.now.utc.strftime('%Y-%m-%d %H:%M:%S')}Z' "
else
""
@@ -75,7 +82,7 @@ def opt_time(since=nil)
end
def rlog_filename
- File.join('/tmp', (self.url + self.module_name.to_s + self.branch_name.to_s).gsub(/\W/,'') + '.rlog')
+ File.join(temp_folder, (self.url + self.module_name.to_s + self.branch_name.to_s).gsub(/\W/,'') + '.rlog')
end
# Converts a CVS time string to a Ruby Time object
diff --git a/lib/scm/adapters/cvs/misc.rb b/lib/ohloh_scm/adapters/cvs/misc.rb
similarity index 73%
rename from lib/scm/adapters/cvs/misc.rb
rename to lib/ohloh_scm/adapters/cvs/misc.rb
index c95d8858..3c256e8a 100644
--- a/lib/scm/adapters/cvs/misc.rb
+++ b/lib/ohloh_scm/adapters/cvs/misc.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class CvsAdapter
# Returns an array of file and directory names from the remote server.
# Directory names will end with a trailing '/' character.
@@ -12,6 +12,8 @@ def ls(path=nil)
cmd = "cvsnt -q -d #{url} ls -e '#{path}'"
+ ensure_host_key
+
stdout, stderr = run_with_err(cmd)
files = []
@@ -36,14 +38,14 @@ def ls(path=nil)
/-m wrapper option is not supported remotely; ignored/,
/cannot open directory .* No such file or directory/,
/ignoring module/,
- /skipping directory/,
- /existing repository .* does not match/,
- /nothing known about/
- ]
+ /skipping directory/,
+ /existing repository .* does not match/,
+ /nothing known about/,
- # The signal 11 error should not really be ignored, but dev.eclipse.org
- # returns it at the end of every ls. Yes, this sucks.
- ignored_error_messages << /Terminated with fatal signal 11/ if guess_forge == 'eclipse.org'
+ # The signal 11 error should not really be ignored, but many CVS servers
+ # including dev.eclipse.org return it at the end of every ls.
+ /Terminated with fatal signal 11/
+ ]
if s.length == 0
error_handled = true
@@ -64,12 +66,18 @@ def ls(path=nil)
end
def log(most_recent_token=nil)
- run "cvsnt -d #{self.url} rlog #{opt_branch} #{opt_time(most_recent_token)} '#{self.module_name}'"
+ ensure_host_key
+ run "cvsnt -d #{self.url} rlog #{opt_branch} #{opt_time(most_recent_token)} '#{self.module_name}' | #{ string_encoder }"
end
+ def export_tag(dest_dir, tag_name = 'HEAD')
+ run "cvsnt -d #{self.url} export -d'#{dest_dir}' -r #{tag_name} '#{self.module_name}'"
+ end
+
def checkout(r, local_directory)
opt_D = r.token ? "-D'#{r.token}Z'" : ""
+ ensure_host_key
if FileTest.exists?(local_directory + '/CVS/Root')
# We already have a local enlistment, so do a quick update.
if r.directories.size > 0
@@ -146,7 +154,7 @@ def trim_directory(d)
end
def root
- "#{$2}/#{self.module_name}/" if self.url =~ /^:pserver:.*@[^:]+:(\d+)?(\/.*)$/
+ "#{$3}/#{self.module_name}/" if self.url =~ /^:(pserver|ext):.*@[^:]+:(\d+)?(\/.*)$/
end
def opt_branch
@@ -156,5 +164,40 @@ def opt_branch
"-b -r1:"
end
end
+
+ # returns the host this adapter is connecting to
+ def host
+ @host ||= begin
+ self.url =~ /@([^:]*):/
+ $1
+ end
+ end
+
+ # returns the protocol this adapter connects with
+ def protocol
+ @protocol ||= case self.url
+ when /^:pserver/ then :pserver
+ when /^:ext/ then :ext
+ end
+ end
+
+ # using :ext (ssh) protocol might trigger ssh to confirm accepting the host's
+ # ssh key. This causes the UI to hang asking for manual confirmation. To avoid
+ # this we pre-populate the ~/.ssh/known_hosts file with the host's key.
+ def ensure_host_key
+ if self.protocol == :ext
+ ensure_key_file = File.dirname(__FILE__) + "/../../../../bin/ensure_key"
+ cmd = "#{ensure_key_file} '#{ self.host }'"
+ stdout, stderr = run_with_err(cmd)
+ end
+ end
+
+ def tags
+ tag_strings = run("cvs -Q -d #{ url } rlog -h #{ module_name } | awk -F\"[.:]\" '/^\\t/&&$(NF-1)!=0'").split(/\n/)
+ tag_strings.map do |tag_string|
+ tag_name, version = tag_string.split(':')
+ [tag_name.gsub(/\t/, ''), version.strip]
+ end
+ end
end
end
diff --git a/lib/scm/adapters/cvs/validation.rb b/lib/ohloh_scm/adapters/cvs/validation.rb
similarity index 88%
rename from lib/scm/adapters/cvs/validation.rb
rename to lib/ohloh_scm/adapters/cvs/validation.rb
index 3cd8ff8d..53a4b7f8 100644
--- a/lib/scm/adapters/cvs/validation.rb
+++ b/lib/ohloh_scm/adapters/cvs/validation.rb
@@ -1,11 +1,11 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class CvsAdapter
def self.url_regex
- /^(:pserver:[\w\-\+\_]*(:[\w\-\+\_]*)?@[A-Za-z0-9_\-\+\.]+:[0-9]*)?\/[A-Za-z0-9_\-\+\.\/]*$/
+ /^(:(pserver|ext):[\w\-\+\_]*(:[\w\-\+\_]*)?@[A-Za-z0-9_\-\+\.]+:[0-9]*)?\/[A-Za-z0-9_\-\+\.\/]*$/
end
def self.public_url_regex
- /^:pserver:[\w\-\+\_]*(:[\w\-\+\_]*)?@[A-Za-z0-9_\-\+\.]+:[0-9]*\/[A-Za-z0-9_\-\+\.\/]*$/
+ /^:(pserver|ext):[\w\-\+\_]*(:[\w\-\+\_]*)?@[A-Za-z0-9_\-\+\.]+:[0-9]*\/[A-Za-z0-9_\-\+\.\/]*$/
end
def validate
@@ -71,7 +71,7 @@ def validate_server_connection
# Based on the URL, take a guess about which forge this code is hosted on.
def guess_forge
- @url =~ /.*pserver.*@(([^\.]+\.)?cvs\.)?(dev\.)?([^:]+):\//i ? $4.downcase : nil
+ @url =~ /.*(pserver|ext).*@(([^\.]+\.)?(cvs|dev)\.)?([^:]+):\//i ? $5.downcase : nil
end
end
end
diff --git a/lib/scm/adapters/cvs_adapter.rb b/lib/ohloh_scm/adapters/cvs_adapter.rb
similarity index 58%
rename from lib/scm/adapters/cvs_adapter.rb
rename to lib/ohloh_scm/adapters/cvs_adapter.rb
index ce78e70d..b5c2a328 100644
--- a/lib/scm/adapters/cvs_adapter.rb
+++ b/lib/ohloh_scm/adapters/cvs_adapter.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class CvsAdapter < AbstractAdapter
attr_accessor :module_name
@@ -13,6 +13,6 @@ def initialize(params={})
end
end
-require 'lib/scm/adapters/cvs/validation'
-require 'lib/scm/adapters/cvs/commits'
-require 'lib/scm/adapters/cvs/misc'
+require_relative 'cvs/validation'
+require_relative 'cvs/commits'
+require_relative 'cvs/misc'
diff --git a/lib/scm/adapters/factory.rb b/lib/ohloh_scm/adapters/factory.rb
similarity index 97%
rename from lib/scm/adapters/factory.rb
rename to lib/ohloh_scm/adapters/factory.rb
index b9e1e661..920aad81 100644
--- a/lib/scm/adapters/factory.rb
+++ b/lib/ohloh_scm/adapters/factory.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class Factory
# Given a local path on disk, try to guess what kind of repository it is,
diff --git a/lib/scm/adapters/git/cat_file.rb b/lib/ohloh_scm/adapters/git/cat_file.rb
similarity index 91%
rename from lib/scm/adapters/git/cat_file.rb
rename to lib/ohloh_scm/adapters/git/cat_file.rb
index a2e15258..0b6165a9 100644
--- a/lib/scm/adapters/git/cat_file.rb
+++ b/lib/ohloh_scm/adapters/git/cat_file.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
def cat_file(commit, diff)
cat(diff.sha1)
diff --git a/lib/scm/adapters/git/commit_all.rb b/lib/ohloh_scm/adapters/git/commit_all.rb
similarity index 97%
rename from lib/scm/adapters/git/commit_all.rb
rename to lib/ohloh_scm/adapters/git/commit_all.rb
index acf06cf6..2493248f 100644
--- a/lib/scm/adapters/git/commit_all.rb
+++ b/lib/ohloh_scm/adapters/git/commit_all.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
#---------------------------------------------------------------------------
@@ -16,7 +16,7 @@ def commit_all(commit=Commit.new)
run "cd '#{self.url}' && git add ."
if anything_to_commit?
- run " cd '#{self.url}' && git commit -a -F #{message_filename}"
+ run "cd '#{self.url}' && git commit -a -F #{message_filename}"
else
logger.info { "nothing to commit" }
end
@@ -64,7 +64,7 @@ def message_filename
# True if there are pending changes to commit.
def anything_to_commit?
- run("cd '#{self.url}' && git status | tail -1") =~ /nothing to commit / ? false : true
+ run("cd '#{self.url}' && git status | tail -1") =~ /nothing to commit/ ? false : true
end
# Ensures that the repository directory exists, and that the git database has been initialized.
diff --git a/lib/ohloh_scm/adapters/git/commits.rb b/lib/ohloh_scm/adapters/git/commits.rb
new file mode 100644
index 00000000..a132009a
--- /dev/null
+++ b/lib/ohloh_scm/adapters/git/commits.rb
@@ -0,0 +1,123 @@
+module OhlohScm::Adapters
+ class GitAdapter < AbstractAdapter
+
+ # Returns the number of commits in the repository following the commit with SHA1 'after'.
+ def commit_count(opts={})
+ run("#{rev_list_command(opts)} | wc -l").to_i
+ end
+
+ # Returns the SHA1 hash for every commit in the repository following the commit with SHA1 'after'.
+ def commit_tokens(opts={})
+ run(rev_list_command(opts)).split("\n")
+ end
+
+ # Yields each commit following the commit with SHA1 'after'.
+ # Officially, this method isn't required to provide diffs with these commits,
+ # and the Subversion equivalent of this method does not,
+ # so if you really require the diffs you should be using each_commit() instead.
+ def commits(opts={})
+ result = []
+ each_commit(opts) { |c| result << c }
+ result
+ end
+
+ # Yields each commit in the repository following the commit with SHA1 'after'.
+ # These commits are populated with diffs.
+ def each_commit(opts={})
+
+ # Bug fix (hack) follows.
+ #
+ # git-whatchanged emits a merge commit multiple times, once for each parent, giving the
+ # delta to each parent in turn.
+ #
+ # This causes us to emit too many commits, with repeated merge commits.
+ #
+ # To fix this, we track the previous commit, and emit a new commit only if it is distinct
+ # from the previous.
+ #
+ # This means that the diffs for a merge commit yielded by this method will be the diffs
+ # vs. the first parent only, and diffs vs. other parents are lost. For Ohloh, this is fine
+ # because Ohloh ignores merge diffs anyway.
+
+ previous = nil
+ open_log_file(opts) do |io|
+ OhlohScm::Parsers::GitStyledParser.parse(io) do |e|
+ yield fixup_null_merge(e) unless previous && previous.token == e.token
+ previous = e
+ end
+ end
+ end
+
+ # Returns a single commit, including its diffs
+ def verbose_commit(token)
+ c = OhlohScm::Parsers::GitStyledParser.parse(run("cd '#{url}' && #{OhlohScm::Parsers::GitStyledParser.whatchanged} #{token} | #{ string_encoder }")).first
+ fixup_null_merge(c)
+ end
+
+ # For a merge commit, we ask `git whatchanged` to output the changes relative to each parent.
+ # It is possible, through developer hackery, to create a merge commit which does not change the tree.
+ # When this happens, `git whatchanged` will suppress its output relative to the first parent,
+ # and jump immediately to the second (branch) parent. Our code mistakenly interprets this output
+ # as the missing changes relative to the first parent.
+ #
+ # To avoid this calamity, we must compare the tree hash of this commit with its first parent's.
+ # If they are the same, then the diff should be empty, regardless of what `git whatchanged` says.
+ #
+ # Yes, this is a convoluted, time-wasting hack to address a very rare circumstance. Ultimatley
+ # we should stop parsing `git whatchanged` to extract commit data.
+ def fixup_null_merge(c)
+ first_parent_token = parent_tokens(c).first
+ if first_parent_token && get_commit_tree(first_parent_token) == get_commit_tree(c.token)
+ c.diffs = []
+ end
+ c
+ end
+
+ # Retrieves the git log in the format expected by GitStyledParser.
+ # We get the log forward chronological order (oldest first)
+ def log(opts={})
+ if has_branch?
+ if opts[:after] && opts[:after]==self.head_token
+ '' # Nothing new.
+ else
+ run "#{rev_list_command(opts)} | xargs -n 1 #{OhlohScm::Parsers::GitStyledParser.whatchanged} | #{ string_encoder }"
+ end
+ else
+ ''
+ end
+ end
+
+
+ # Same as log() method above, except that it writes the log to
+ # a file.
+ def open_log_file(opts={})
+ if has_branch?
+ if opts[:after] && opts[:after]==self.head_token
+ '' # Nothing new.
+ else
+ begin
+ run "#{rev_list_command(opts)} | xargs -n 1 #{OhlohScm::Parsers::GitStyledParser.whatchanged} | #{ string_encoder } > #{log_filename}"
+ File.open(log_filename, 'r') { |io| yield io }
+ ensure
+ File.delete(log_filename) if FileTest.exist?(log_filename)
+ end
+ end
+ else
+ ''
+ end
+ end
+
+ def log_filename
+ File.join(temp_folder, (self.url).gsub(/\W/,'') + '.log')
+ end
+
+ def rev_list_command(opts={})
+ up_to = opts[:up_to] || branch_name
+ range = opts[:after] ? "#{opts[:after]}..#{up_to}" : up_to
+
+ trunk_only = opts[:trunk_only] ? "--first-parent" : ""
+
+ "cd '#{url}' && git rev-list --topo-order --reverse #{trunk_only} #{range}"
+ end
+ end
+end
diff --git a/lib/scm/adapters/git/head.rb b/lib/ohloh_scm/adapters/git/head.rb
similarity index 94%
rename from lib/scm/adapters/git/head.rb
rename to lib/ohloh_scm/adapters/git/head.rb
index 851de72d..1cc80acd 100644
--- a/lib/scm/adapters/git/head.rb
+++ b/lib/ohloh_scm/adapters/git/head.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
def head_token
diff --git a/lib/scm/adapters/git/misc.rb b/lib/ohloh_scm/adapters/git/misc.rb
similarity index 65%
rename from lib/scm/adapters/git/misc.rb
rename to lib/ohloh_scm/adapters/git/misc.rb
index a6b30617..dd61fb21 100644
--- a/lib/scm/adapters/git/misc.rb
+++ b/lib/ohloh_scm/adapters/git/misc.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
def git_path
File.join(self.url, '/.git')
@@ -21,6 +21,11 @@ def ls_tree(token='HEAD')
run("cd #{url} && git ls-tree -r #{token} | cut -f 2 -d '\t'").split("\n")
end
+ # For a given commit ID, returns the SHA1 hash of its tree
+ def get_commit_tree(token='HEAD')
+ run("cd #{url} && git cat-file commit #{token} | grep '^tree' | cut -d ' ' -f 2").strip
+ end
+
# Moves us the correct branch and checks out the most recent files.
#
# Anything not tracked by Git is deleted.
@@ -43,7 +48,7 @@ def checkout
# Returns an array of all branch names
def branches
- run("cd '#{self.url}' && git branch").split.collect { |b| b =~ /\b(.+)$/ ; $1 }.compact
+ run("cd '#{self.url}' && git branch | #{ string_encoder }").split.collect { |b| b =~ /\b(.+)$/ ; $1 }.compact
end
def has_branch?(name=self.branch_name)
@@ -64,5 +69,21 @@ def create_tracking_branch(name)
def is_merge_commit?(commit)
parent_tokens(commit).size > 1
end
+
+ def no_tags?
+ run("cd #{ url } && git tag | head -1").empty?
+ end
+
+ def tags
+ return [] if no_tags?
+ tag_strings = run("cd #{url} && git tag --format='%(creatordate:iso-strict) %(objectname) %(refname)'").split(/\n/)
+ tag_strings.map do |tag_string|
+ timestamp_string, commit_hash, tag_path = tag_string.split(/\s/)
+ timestamp_string = '1970-01-01' if timestamp_string.strip.empty?
+ timestamp = Time.parse(timestamp_string)
+ tag_name = tag_path.gsub('refs/tags/', '')
+ [tag_name, commit_hash, timestamp]
+ end
+ end
end
end
diff --git a/lib/scm/adapters/git/patch.rb b/lib/ohloh_scm/adapters/git/patch.rb
similarity index 88%
rename from lib/scm/adapters/git/patch.rb
rename to lib/ohloh_scm/adapters/git/patch.rb
index 33411f92..cc234969 100644
--- a/lib/scm/adapters/git/patch.rb
+++ b/lib/ohloh_scm/adapters/git/patch.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
def patch_for_commit(commit)
parent_tokens(commit).map {|token|
diff --git a/lib/scm/adapters/git/pull.rb b/lib/ohloh_scm/adapters/git/pull.rb
similarity index 87%
rename from lib/scm/adapters/git/pull.rb
rename to lib/ohloh_scm/adapters/git/pull.rb
index 1b6d7f58..e259b640 100644
--- a/lib/scm/adapters/git/pull.rb
+++ b/lib/ohloh_scm/adapters/git/pull.rb
@@ -1,8 +1,9 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
def pull(from, &block)
logger.info { "Pulling #{from.url}" }
+
case from
when GitAdapter
clone_or_fetch(from, &block)
@@ -71,8 +72,8 @@ def clone_or_fetch(source_scm)
def convert(source_scm)
yield(0,1) if block_given? # Progress bar callback
- # Any new work to be done since last time we were here?
- commits = source_scm.commits(read_token)
+ # Any new work to be done since the last time we were here?
+ commits = source_scm.commits(:after => read_token)
if commits and commits.size > 0
# Start by making sure we are in a known good state. Set up our working directory.
clean_up_disk
@@ -83,20 +84,29 @@ def convert(source_scm)
logger.info { "Downloading revision #{r.token} (#{i+1} of #{commits.size})... " }
begin
- source_scm.checkout(r, url)
+ r.scm.checkout(r, url)
rescue
logger.error { $!.inspect }
# If we fail to checkout, it's often because there is junk of some kind
# in our working directory.
logger.info { "Checkout failed. Cleaning and trying again..." }
clean_up_disk
- source_scm.checkout(r, url)
+ r.scm.checkout(r, url)
end
+ # Sometimes svn conflicts occur leading to a silent `svn checkout` failure.
+ if source_scm.is_a?(SvnAdapter) && SvnAdapter.has_conflicts?(url)
+ logger.info { "Working copy has svn conflicts. Cleaning and trying again..." }
+ clean_up_disk
+ r.scm.checkout(r, url)
+ end
+
logger.debug { "Committing revision #{r.token} (#{i+1} of #{commits.size})... " }
commit_all(r)
end
yield(commits.size, commits.size) if block_given?
+ elsif !read_token && commits.empty?
+ raise RuntimeError, "Empty repository"
else
logger.info { "Already up-to-date." }
end
diff --git a/lib/scm/adapters/git/push.rb b/lib/ohloh_scm/adapters/git/push.rb
similarity index 97%
rename from lib/scm/adapters/git/push.rb
rename to lib/ohloh_scm/adapters/git/push.rb
index 11aa8918..bae4e69a 100644
--- a/lib/scm/adapters/git/push.rb
+++ b/lib/ohloh_scm/adapters/git/push.rb
@@ -1,6 +1,6 @@
require 'socket'
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
COMMITTER_NAME = 'ohloh_slave' unless defined?(COMMITTER_NAME)
diff --git a/lib/scm/adapters/git/token.rb b/lib/ohloh_scm/adapters/git/token.rb
similarity index 98%
rename from lib/scm/adapters/git/token.rb
rename to lib/ohloh_scm/adapters/git/token.rb
index b102af9b..115a5f41 100644
--- a/lib/scm/adapters/git/token.rb
+++ b/lib/ohloh_scm/adapters/git/token.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class GitAdapter < AbstractAdapter
#---------------------------------------------------------------------------
diff --git a/lib/ohloh_scm/adapters/git/validation.rb b/lib/ohloh_scm/adapters/git/validation.rb
new file mode 100644
index 00000000..e7d5d51d
--- /dev/null
+++ b/lib/ohloh_scm/adapters/git/validation.rb
@@ -0,0 +1,50 @@
+module OhlohScm::Adapters
+ class GitAdapter < AbstractAdapter
+ def self.url_regex
+ /^(http|https|rsync|git|ssh):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/[A-Za-z0-9_@\-\.\/\~\+]*$/
+ end
+
+ def self.public_url_regex
+ /^(http|https|git):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/[A-Za-z0-9_\-\.\/\~\+]*$/
+ end
+
+ def normalize
+ super
+ @url = normalize_url
+ @branch_name = 'master' if @branch_name.to_s == ''
+ self
+ end
+
+ # Given a Github read-write URL, return a git protocol read-only URL
+ # Given a Github web URL, return a git protocol read-only URL
+ # Given a Git read-write protocol URL, return a git protocol read-only URL
+ # Else, return the URL
+ def normalize_url
+ case @url
+ when /^https?:\/\/\w+@github.com\/(.+)\.git$/
+ "git://github.com/#{$1}.git"
+ when /^https?:\/\/github.com\/(.+)/
+ "git://github.com/#{$1}"
+ when /^git@github.com:(.+)\.git$/
+ "git://github.com/#{$1}.git"
+ else
+ @url
+ end
+ end
+
+ def validate_server_connection
+ return unless valid?
+ @errors << [:failed, "The server did not respond to the 'git-ls-remote' command. Is the URL correct?"] unless self.exists?
+ end
+
+ def guess_forge
+ u = @url =~ /:\/\/(.*\.?git\.)?([^\/^:]+)(:\d+)?\// ? $2 : nil
+ case u
+ when /(sourceforge\.net$)/
+ $1
+ else
+ u
+ end
+ end
+ end
+end
diff --git a/lib/ohloh_scm/adapters/git_adapter.rb b/lib/ohloh_scm/adapters/git_adapter.rb
new file mode 100644
index 00000000..1441f370
--- /dev/null
+++ b/lib/ohloh_scm/adapters/git_adapter.rb
@@ -0,0 +1,18 @@
+module OhlohScm::Adapters
+ class GitAdapter < AbstractAdapter
+ def english_name
+ "Git"
+ end
+ end
+end
+
+require_relative 'git/validation'
+require_relative 'git/cat_file'
+require_relative 'git/commits'
+require_relative 'git/commit_all'
+require_relative 'git/token'
+require_relative 'git/push'
+require_relative 'git/pull'
+require_relative 'git/head'
+require_relative 'git/misc'
+require_relative 'git/patch'
diff --git a/lib/scm/adapters/hg/cat_file.rb b/lib/ohloh_scm/adapters/hg/cat_file.rb
similarity index 72%
rename from lib/scm/adapters/hg/cat_file.rb
rename to lib/ohloh_scm/adapters/hg/cat_file.rb
index 6985066f..1620d8eb 100644
--- a/lib/scm/adapters/hg/cat_file.rb
+++ b/lib/ohloh_scm/adapters/hg/cat_file.rb
@@ -1,17 +1,19 @@
-module Scm::Adapters
+require 'shellwords'
+
+module OhlohScm::Adapters
class HgAdapter < AbstractAdapter
def cat_file(commit, diff)
cat(commit.token, diff.path)
end
def cat_file_parent(commit, diff)
- p = parents(commit)
- cat(p.first.token, diff.path) if p.first
+ p = parent_tokens(commit)
+ cat(p.first, diff.path) if p.first
end
def cat(revision, path)
out, err = run_with_err("cd '#{url}' && hg cat -r #{revision} #{escape(path)}")
- return nil if err =~ /No such file in rev/
+ return nil if err =~ /No such file in rev/i
raise RuntimeError.new(err) unless err.to_s == ''
out
end
@@ -20,7 +22,7 @@ def cat(revision, path)
# Example:
# "Foo Bar & Baz" => "Foo\ Bar\ \&\ Baz"
def escape(path)
- path.gsub(/[ '"&]/) { |c| '\\' + c }
+ path.shellescape
end
end
end
diff --git a/lib/ohloh_scm/adapters/hg/commits.rb b/lib/ohloh_scm/adapters/hg/commits.rb
new file mode 100644
index 00000000..fcadbbc2
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hg/commits.rb
@@ -0,0 +1,108 @@
+module OhlohScm::Adapters
+ class HgAdapter < AbstractAdapter
+
+ # Return the number of commits in the repository following +after+.
+ def commit_count(opts={})
+ commit_tokens(opts).size
+ end
+
+ # Return the list of commit tokens following +after+.
+ def commit_tokens(opts={})
+ hg_log_with_opts, after = hg_command_builder(opts)
+ # We reverse the final result in Ruby, rather than passing the --reverse flag to hg.
+ # That's because the -f (follow) flag doesn't behave the same in both directions.
+ # Basically, we're trying very hard to make this act just like Git. The hg_rev_list_test checks this.
+ tokens = run("cd '#{self.url}' && #{ hg_log_with_opts } --template='{node}\\n'").split("\n").reverse
+
+ # Hg returns everything after *and including* after.
+ # We want to exclude it.
+ if tokens.any? && tokens.first == after
+ tokens[1..-1]
+ else
+ tokens
+ end
+ 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(opts={})
+ hg_log_with_opts, after = hg_command_builder(opts)
+
+ log = run("cd '#{self.url}' && #{ hg_log_with_opts } --style #{OhlohScm::Parsers::HgStyledParser.style_path}")
+ a = OhlohScm::Parsers::HgStyledParser.parse(log).reverse
+
+ if a.any? && a.first.token == after
+ a[1..-1]
+ else
+ a
+ end
+ end
+
+ # Returns a single commit, including its diffs
+ def verbose_commit(token)
+ log = run("cd '#{self.url}' && hg log -v -r #{token} --style #{OhlohScm::Parsers::HgStyledParser.verbose_style_path} | #{ string_encoder }")
+ OhlohScm::Parsers::HgStyledParser.parse(log).first
+ end
+
+ # Yields each commit after +after+, 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(opts={})
+ after = opts[:after] || 0
+ open_log_file(opts) do |io|
+ commits = OhlohScm::Parsers::HgStyledParser.parse(io)
+ commits.reverse.each do |commit|
+ yield commit if block_given? && commit.token != after
+ end
+ end
+ end
+
+ # Not used by Ohloh proper, but handy for debugging and testing
+ def log(opts={})
+ hg_log_with_opts = hg_command_builder(opts)
+ run "cd '#{url}' && #{ hg_log_with_opts } | #{ string_encoder }"
+ end
+
+ # Returns a file handle to the log.
+ # In our standard, the log should include everything AFTER +after+. However, hg doesn't work that way;
+ # it returns everything after and INCLUDING +after+. Therefore, consumers of this file should check for
+ # and reject the duplicate commit.
+ def open_log_file(opts={})
+ hg_log_with_opts, after = hg_command_builder(opts)
+ begin
+ if after == 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 "cd '#{url}' && #{ hg_log_with_opts } --style #{OhlohScm::Parsers::HgStyledParser.verbose_style_path} | #{ string_encoder } > #{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(temp_folder, (self.url).gsub(/\W/,'') + '.log')
+ end
+
+ private
+
+ def hg_command_builder(opts)
+ after = opts[:after] || 0
+ up_to = opts[:up_to] || :tip
+
+ options = if opts[:trunk_only]
+ "--follow-first -r #{ up_to }:#{ after }"
+ else
+ query = "and (branch(#{ branch_name }) or ancestors(#{ branch_name }))" if branch_name && branch_name != 'default'
+ "-r '#{ up_to }:#{ after } #{ query }'"
+ end
+
+ ["hg log -f -v #{ options }", after]
+ end
+ end
+end
diff --git a/lib/scm/adapters/hg/head.rb b/lib/ohloh_scm/adapters/hg/head.rb
similarity index 52%
rename from lib/scm/adapters/hg/head.rb
rename to lib/ohloh_scm/adapters/hg/head.rb
index e3c39216..cf89a16a 100644
--- a/lib/scm/adapters/hg/head.rb
+++ b/lib/ohloh_scm/adapters/hg/head.rb
@@ -1,9 +1,17 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class HgAdapter < AbstractAdapter
def head_token
# This only returns first 12 characters.
# How can we make it return the entire hash?
- run("hg id -q #{url}").strip
+ branch_opts = "--rev #{branch_name || :default}"
+ token = run("hg id --debug -i -q #{url} #{branch_opts}").strip
+
+ # Recent versions of Hg now somtimes append a '+' char to the token.
+ # I believe this signifies pending changes... but we don't care.
+ # Strip the trailing '+', if any.
+ token = token[0..-2] if token[-1..-1] == '+'
+
+ token
end
def head
diff --git a/lib/ohloh_scm/adapters/hg/misc.rb b/lib/ohloh_scm/adapters/hg/misc.rb
new file mode 100644
index 00000000..1f25463e
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hg/misc.rb
@@ -0,0 +1,32 @@
+module OhlohScm::Adapters
+ class HgAdapter < AbstractAdapter
+ def exist?
+ begin
+ !!(head_token)
+ rescue
+ logger.debug { $! }
+ false
+ end
+ end
+
+ def ls_tree(token)
+ run("cd '#{path}' && hg manifest -r #{token} | #{ string_encoder }").split("\n")
+ end
+
+ def export(dest_dir, token='tip')
+ run("cd '#{path}' && hg archive -r #{token} '#{dest_dir}'")
+ # Hg leaves a little cookie crumb in the export directory. Remove it.
+ File.delete(File.join(dest_dir, '.hg_archival.txt')) if File.exist?(File.join(dest_dir, '.hg_archival.txt'))
+ end
+
+ def tags
+ tag_strings = run("cd '#{path}' && hg tags").split(/\n/)
+ tag_strings.map do |tag_string|
+ tag_name, rev_number_and_hash = tag_string.split(/\s+/)
+ rev = rev_number_and_hash.slice(/\A\d+/)
+ time_string = run("cd '#{ path }' && hg log -r #{ rev } | grep 'date:' | sed 's/date://'")
+ [tag_name, rev, Time.parse(time_string)]
+ end
+ end
+ end
+end
diff --git a/lib/scm/adapters/hg/patch.rb b/lib/ohloh_scm/adapters/hg/patch.rb
similarity index 89%
rename from lib/scm/adapters/hg/patch.rb
rename to lib/ohloh_scm/adapters/hg/patch.rb
index 1b9cff53..0ea390a7 100644
--- a/lib/scm/adapters/hg/patch.rb
+++ b/lib/ohloh_scm/adapters/hg/patch.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class HgAdapter < AbstractAdapter
def patch_for_commit(commit)
parent_tokens(commit).map {|token|
diff --git a/lib/scm/adapters/hg/pull.rb b/lib/ohloh_scm/adapters/hg/pull.rb
similarity index 71%
rename from lib/scm/adapters/hg/pull.rb
rename to lib/ohloh_scm/adapters/hg/pull.rb
index f57b4639..ab3a4421 100644
--- a/lib/scm/adapters/hg/pull.rb
+++ b/lib/ohloh_scm/adapters/hg/pull.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class HgAdapter < AbstractAdapter
def pull(from, &block)
@@ -12,7 +12,8 @@ def pull(from, &block)
run "rm -rf '#{self.url}'"
run "hg clone -U '#{from.url}' '#{self.url}'"
else
- run "cd '#{self.url}' && hg pull -u -y '#{from.url}'"
+ branch_opts = "-r #{ from.branch_name }" if branch_name
+ run "cd '#{self.url}' && hg revert --all && hg pull #{ branch_opts } -u -y '#{from.url}'"
end
yield(1,1) if block_given? # Progress bar callback
diff --git a/lib/scm/adapters/hg/push.rb b/lib/ohloh_scm/adapters/hg/push.rb
similarity index 97%
rename from lib/scm/adapters/hg/push.rb
rename to lib/ohloh_scm/adapters/hg/push.rb
index 4c86fa4c..9805bc17 100644
--- a/lib/scm/adapters/hg/push.rb
+++ b/lib/ohloh_scm/adapters/hg/push.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class HgAdapter < AbstractAdapter
def push(to, &block)
diff --git a/lib/scm/adapters/hg/validation.rb b/lib/ohloh_scm/adapters/hg/validation.rb
similarity index 66%
rename from lib/scm/adapters/hg/validation.rb
rename to lib/ohloh_scm/adapters/hg/validation.rb
index bfeb90fa..d0747f98 100644
--- a/lib/scm/adapters/hg/validation.rb
+++ b/lib/ohloh_scm/adapters/hg/validation.rb
@@ -1,7 +1,7 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class HgAdapter < AbstractAdapter
def self.url_regex
- /^((http|https|ssh|file):\/\/((\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/)?)?[A-Za-z0-9_\-\.\/\~\+]*$/
+ /^((http|https|ssh|file):\/\/((\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/)?)?[A-Za-z0-9_@\-\.\/\~\+]*$/
end
def self.public_url_regex
@@ -12,5 +12,15 @@ def validate_server_connection
return unless valid?
@errors << [:failed, "The server did not respond to the 'hg id' command. Is the URL correct?"] unless self.exist?
end
+
+ def guess_forge
+ u = @url =~ /:\/\/(.*\.?hg\.)?([^\/^:]+)(:\d+)?\// ? $2 : nil
+ case u
+ when /(sourceforge\.net$)/
+ $1
+ else
+ u
+ end
+ end
end
end
diff --git a/lib/ohloh_scm/adapters/hg_adapter.rb b/lib/ohloh_scm/adapters/hg_adapter.rb
new file mode 100644
index 00000000..eab19467
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hg_adapter.rb
@@ -0,0 +1,21 @@
+module OhlohScm::Adapters
+ class HgAdapter < AbstractAdapter
+ def english_name
+ "Mercurial"
+ end
+
+ def branch_name=(branch_name)
+ branch_name = nil if branch_name.to_s.empty?
+ super
+ end
+ end
+end
+
+require_relative 'hg/validation'
+require_relative 'hg/cat_file'
+require_relative 'hg/commits'
+require_relative 'hg/misc'
+require_relative 'hg/pull'
+require_relative 'hg/push'
+require_relative 'hg/head'
+require_relative 'hg/patch'
diff --git a/lib/ohloh_scm/adapters/hglib/cat_file.rb b/lib/ohloh_scm/adapters/hglib/cat_file.rb
new file mode 100644
index 00000000..621e3946
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hglib/cat_file.rb
@@ -0,0 +1,14 @@
+module OhlohScm::Adapters
+ class HglibAdapter < HgAdapter
+
+ def cat_file(commit, diff)
+ hg_client.cat_file(commit.token, diff.path)
+ end
+
+ def cat_file_parent(commit, diff)
+ tokens = parent_tokens(commit)
+ hg_client.cat_file(tokens.first, diff.path) if tokens.first
+ end
+
+ end
+end
diff --git a/lib/ohloh_scm/adapters/hglib/client.rb b/lib/ohloh_scm/adapters/hglib/client.rb
new file mode 100644
index 00000000..e3d8733b
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hglib/client.rb
@@ -0,0 +1,62 @@
+require 'rubygems'
+require 'posix/spawn'
+
+class HglibClient
+ def initialize(repository_url)
+ @repository_url = repository_url
+ @py_script = File.dirname(__FILE__) + '/server.py'
+ end
+
+ def start
+ @pid, @stdin, @stdout, @stderr = POSIX::Spawn::popen4 "python #{@py_script}"
+ open_repository
+ end
+
+ def open_repository
+ send_command("REPO_OPEN\t#{@repository_url}")
+ end
+
+ def cat_file(revision, file)
+ begin
+ send_command("CAT_FILE\t#{revision}\t#{file}")
+ rescue RuntimeError => e
+ if e.message =~ /not found in manifest/
+ return nil # File does not exist.
+ else
+ raise
+ end
+ end
+ end
+
+ def parent_tokens(revision)
+ send_command("PARENT_TOKENS\t#{revision}").split("\t")
+ end
+
+ def send_command(cmd)
+ # send the command
+ @stdin.puts cmd
+ @stdin.flush
+ return if cmd == "QUIT"
+
+ # get status on stderr, first letter indicates state,
+ # remaing value indicates length of the file content
+ status = @stderr.read(10)
+ flag = status[0,1]
+ size = status[1,9].to_i
+ if flag == 'F'
+ return nil
+ elsif flag == 'E'
+ error = @stdout.read(size)
+ raise RuntimeError.new("Exception in server process\n#{error}")
+ end
+
+ # read content from stdout
+ return @stdout.read(size)
+ end
+
+ def shutdown
+ send_command("QUIT")
+ [@stdout, @stdin, @stderr].each { |io| io.close unless io.closed? }
+ Process.waitpid(@pid, Process::WNOHANG)
+ end
+end
diff --git a/lib/ohloh_scm/adapters/hglib/head.rb b/lib/ohloh_scm/adapters/hglib/head.rb
new file mode 100644
index 00000000..31b5366c
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hglib/head.rb
@@ -0,0 +1,9 @@
+module OhlohScm::Adapters
+ class HglibAdapter < HgAdapter
+
+ def parent_tokens(commit)
+ hg_client.parent_tokens(commit.token)
+ end
+
+ end
+end
diff --git a/lib/ohloh_scm/adapters/hglib/server.py b/lib/ohloh_scm/adapters/hglib/server.py
new file mode 100644
index 00000000..aaa46f6f
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hglib/server.py
@@ -0,0 +1,84 @@
+import sys
+import time
+import traceback
+
+from mercurial import ui, hg
+
+class HglibPipeServer:
+ def __init__(self, repository_url):
+ self.ui = ui.ui()
+ self.repository = hg.repository(self.ui, repository_url)
+
+ def get_file_content(self, filename, revision):
+ c = self.repository.changectx(revision)
+ fc = c[filename]
+ contents = fc.data()
+ return contents
+
+ def get_parent_tokens(self, revision):
+ c = self.repository.changectx(revision)
+ parents = [p.hex() for p in c.parents() if p.hex() != '0000000000000000000000000000000000000000']
+ return parents
+
+class Command:
+ def __init__(self, line):
+ self.args = line.rstrip().split('\t')
+
+ def get_action(self):
+ return self.args[0]
+
+ def get_arg(self, num):
+ return self.args[num]
+
+def send_status(code, data_len):
+ sys.stderr.write('%s%09d' % (code, data_len))
+ sys.stderr.flush()
+
+def send_success(data_len=0):
+ send_status('T', data_len)
+
+def send_failure(data_len=0):
+ send_status('F', data_len)
+
+def send_error(data_len=0):
+ send_status('E', data_len)
+
+def send_data(result):
+ sys.stdout.write(result)
+ sys.stdout.flush()
+
+def command_loop():
+ while True:
+ s = sys.stdin.readline()
+ cmd = Command(s)
+ if s == '' or cmd.get_action() == 'QUIT':
+ sys.exit(0)
+ elif cmd.get_action() == 'REPO_OPEN':
+ commander = HglibPipeServer(cmd.get_arg(1))
+ send_success()
+ elif cmd.get_action() == 'CAT_FILE':
+ try:
+ content = commander.get_file_content(cmd.get_arg(2), cmd.get_arg(1))
+ send_success(len(content))
+ send_data(content)
+ except Exception:
+ send_failure() # Assume file not found
+ elif cmd.get_action() == 'PARENT_TOKENS':
+ tokens = commander.get_parent_tokens(cmd.get_arg(1))
+ tokens = '\t'.join(tokens)
+ send_success(len(tokens))
+ send_data(tokens)
+ else:
+ error = "Invalid Command - %s" % cmd.get_action()
+ send_error(len(error))
+ send_data(error)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ try:
+ command_loop()
+ except Exception:
+ exc_trace = traceback.format_exc()
+ send_error(len(exc_trace))
+ send_data(exc_trace)
+ sys.exit(1)
diff --git a/lib/ohloh_scm/adapters/hglib_adapter.rb b/lib/ohloh_scm/adapters/hglib_adapter.rb
new file mode 100644
index 00000000..f9dca0e3
--- /dev/null
+++ b/lib/ohloh_scm/adapters/hglib_adapter.rb
@@ -0,0 +1,25 @@
+require 'rubygems'
+require_relative 'hglib/client'
+
+module OhlohScm::Adapters
+ class HglibAdapter < HgAdapter
+
+ def setup
+ hg_client = HglibClient.new(url)
+ hg_client.start
+ hg_client
+ end
+
+ def hg_client
+ @hg_client ||= setup
+ end
+
+ def cleanup
+ @hg_client && @hg_client.shutdown
+ end
+
+ end
+end
+
+require_relative 'hglib/head'
+require_relative 'hglib/cat_file'
diff --git a/lib/ohloh_scm/adapters/svn/cat_file.rb b/lib/ohloh_scm/adapters/svn/cat_file.rb
new file mode 100644
index 00000000..3a15ecf6
--- /dev/null
+++ b/lib/ohloh_scm/adapters/svn/cat_file.rb
@@ -0,0 +1,19 @@
+module OhlohScm::Adapters
+ class SvnAdapter < AbstractAdapter
+ def cat_file(commit, diff)
+ cat(diff.path, commit.token)
+ end
+
+ def cat_file_parent(commit, diff)
+ cat(diff.path, commit.token.to_i-1)
+ end
+
+ def cat(path, revision)
+ begin
+ run "svn cat --trust-server-cert --non-interactive -r #{revision} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name.to_s, path.to_s))}@#{revision}'"
+ rescue
+ raise unless $!.message =~ /svn:.*Could not cat all targets because some targets (don't exist|are directories)/
+ end
+ end
+ end
+end
diff --git a/lib/scm/adapters/svn/commits.rb b/lib/ohloh_scm/adapters/svn/commits.rb
similarity index 54%
rename from lib/scm/adapters/svn/commits.rb
rename to lib/ohloh_scm/adapters/svn/commits.rb
index 2ea8dd95..1ce64079 100644
--- a/lib/scm/adapters/svn/commits.rb
+++ b/lib/ohloh_scm/adapters/svn/commits.rb
@@ -1,51 +1,50 @@
require 'rexml/document'
-module Scm::Adapters
+module OhlohScm::Adapters
class SvnAdapter < AbstractAdapter
- # In all commit- and log-related methods, 'since' refers to the revision number of the last known commit,
- # and the methods return the commits *following* this commit.
+ # In all commit- and log-related methods, 'after' refers to the revision
+ # number of the last known commit, and the methods return the commits
+ # *following* this commit.
#
# Examples:
# commits(1) => [rev 2, rev 3, ..., HEAD]
# commits(3) => [rev 4, rev 5, ..., HEAD]
#
- # This is convenient for Ohloh -- Ohloh passes the last commit it is aware of, and these methods return any new commits.
-
- # Returns the count of commits following revision number 'since'.
- def commit_count(since=0)
- run("svn log -q -r #{since.to_i + 1}:HEAD --stop-on-copy '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}' | grep -E -e '^r[0-9]+ ' | wc -l").strip.to_i
- end
-
- # Returns an array of revision numbers for all commits following revision number 'since'.
- def commit_tokens(since=0)
- cmd = "svn log -q -r #{since.to_i + 1}:HEAD --stop-on-copy '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}' | grep -E -e '^r[0-9]+ ' | cut -f 1 -d '|' | cut -c 2-"
+ # This is convenient for Ohloh -- Ohloh passes the last commit it is aware
+ # of, and these methods return any new commits.
+
+ # The last revision to be analyzed in this repository. Everything after this revision is ignored.
+ # The repository is considered to be retired after this point, and under no circumstances should
+ # this adapter ever return information regarding commits after this point.
+ attr_accessor :final_token
+
+ # Returns the count of commits following revision number 'after'.
+ def commit_count(opts={})
+ after = (opts[:after] || 0).to_i
+ return 0 if final_token && after >= final_token
+ run("svn log --trust-server-cert --non-interactive -q -r #{after.to_i + 1}:#{final_token || 'HEAD'} --stop-on-copy '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}@#{final_token || 'HEAD'}' | grep -E -e '^r[0-9]+ ' | wc -l").strip.to_i
+ end
+
+ # Returns an array of revision numbers for all commits following revision number 'after'.
+ def commit_tokens(opts={})
+ after = (opts[:after] || 0).to_i
+ return [] if final_token && after >= final_token
+ cmd = "svn log --trust-server-cert --non-interactive -q -r #{after + 1}:#{final_token || 'HEAD'} --stop-on-copy '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}@#{final_token || 'HEAD'}' | grep -E -e '^r[0-9]+ ' | cut -f 1 -d '|' | cut -c 2-"
run(cmd).split.collect { |r| r.to_i }
end
- # Returns an array of commits following revision number 'since'. These commit objects do not include diffs.
- def commits(since=0)
- c = []
- open_log_file(since) do |io|
- c = Scm::Parsers::SvnXmlParser.parse(io)
+ # Returns an array of commits following revision number 'after'.
+ # These commit objects do not include diffs.
+ def commits(opts={})
+ list = []
+ open_log_file(opts) do |io|
+ list = OhlohScm::Parsers::SvnXmlParser.parse(io)
end
-
- # We may be using a log saved on disk from a previous fetch.
- # If so, exclude the portion of the log up to 'since'.
- c.each_index do |i|
- if c[i].token.to_i == since.to_i
- if i == commits.size-1
- # We're up to date
- return []
- else
- return c[i+1..-1]
- end
- end
- end
- c
+ list.each { |c| c.scm = self }
end
- # Yields each commit following revision number 'since'. These commit object are populated with diffs.
+ # Yields each commit following revision number 'after'. These commit object are populated with diffs.
#
# With Subversion, populating the diffs can be tricky because when an entire directory is affected,
# Subversion abbreviates the log by simply listing the directory name, rather than all of the directory
@@ -53,9 +52,9 @@ def commits(since=0)
# directories, the complexity (and time) of this method comes in expanding directories with a recursion
# through every file in the directory.
#
- def each_commit(since=nil)
- commit_tokens(since).each do |rev|
- yield deepen_commit(strip_commit_branch(verbose_commit(rev)))
+ def each_commit(opts={})
+ commit_tokens(opts).each do |rev|
+ yield verbose_commit(rev)
end
end
@@ -66,7 +65,7 @@ def deepen_commit(commit)
if commit.diffs
deep_commit.diffs = commit.diffs.collect do |diff|
deepen_diff(diff, commit.token)
- end.flatten.uniq.sort { |a,b| a.action <=> b.action }.sort { |a,b| a.path <=> b.path }
+ end.compact.flatten.uniq.sort { |a,b| a.action <=> b.action }.sort { |a,b| a.path <=> b.path }
end
remove_dupes(deep_commit)
@@ -79,10 +78,10 @@ def remove_dupes(commit)
# Because we expand directories, the result is that the file may be listed twice -- once as part of our expansion,
# and once from the regular log entry.
#
- # So look for diffs of the form ["M", "path"] which are matched by ["A", "path"] and remove them.
+ # So look for diffs of the form ["M", "path"] which are matched by ["A", "path"], and keep only the "A" diff.
if commit.diffs
commit.diffs.delete_if do |d|
- d.action =~ /[MR]/ && commit.diffs.select { |x| x.action == 'A' and x.path == d.path }.any?
+ d && d.action =~ /[MR]/ && commit.diffs.select { |x| x.action == 'A' and x.path == d.path }.any?
end
end
commit
@@ -93,11 +92,14 @@ def remove_dupes(commit)
def deepen_diff(diff, rev)
# Note that if the directory was deleted, we have to look at the previous revision to see what it held.
recurse_rev = (diff.action == 'D') ? rev-1 : rev
+
if (diff.action == 'D' or diff.action == 'A') && is_directory?(diff.path, recurse_rev)
+ # Deleting or adding a directory. Expand it out to show every file.
recurse_files(diff.path, recurse_rev).collect do |f|
- Scm::Diff.new(:action => diff.action, :path => File.join(diff.path, f))
+ OhlohScm::Diff.new(:action => diff.action, :path => File.join(diff.path, f))
end
else
+ # An ordinary file action. Just return the diff.
diff
end
end
@@ -130,27 +132,29 @@ def strip_path_branch(path)
end
end
- # A single commit, including any changed paths.
- # Basically equivalent to the data you get back from the Subversion log when you pass the --verbose flag.
def verbose_commit(rev)
- Scm::Parsers::SvnXmlParser.parse(single_revision_xml(rev)).first
+ c = OhlohScm::Parsers::SvnXmlParser.parse(single_revision_xml(rev)).first
+ c.scm = self
+ deepen_commit(strip_commit_branch(c))
end
#---------------------------------------------------------------------
# Log-related code ; get log for entire file or single revision
#---------------------------------------------------------------------
- def log(since=0)
- run "svn log --xml --stop-on-copy -r #{since.to_i + 1}:HEAD '#{SvnAdapter.uri_encode(self.url)}' #{opt_auth}"
+ def log(opts={})
+ after = (opts[:after] || 0).to_i
+ run "svn log --trust-server-cert --non-interactive --xml --stop-on-copy -r #{after.to_i + 1}:#{final_token || 'HEAD'} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name.to_s))}@#{final_token || 'HEAD'}' #{opt_auth} | #{ string_encoder }"
end
- def open_log_file(since=0)
+ def open_log_file(opts={})
+ after = (opts[:after] || 0).to_i
begin
- if (since.to_i + 1) <= head_token
- run "svn log --xml --stop-on-copy -r #{since.to_i + 1}:HEAD '#{SvnAdapter.uri_encode(self.url)}' #{opt_auth} > #{log_filename}"
- else
+ if (final_token && after >= final_token) || after >= head_token
# As a time optimization, just create an empty file rather than fetch a log we know will be empty.
File.open(log_filename, 'w') { |f| f.puts '' }
+ else
+ run "svn log --trust-server-cert --non-interactive --xml --stop-on-copy -r #{after + 1}:#{final_token || 'HEAD'} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name))}@#{final_token || 'HEAD'}' #{opt_auth} | #{ string_encoder } > #{log_filename}"
end
File.open(log_filename, 'r') { |io| yield io }
ensure
@@ -159,11 +163,12 @@ def open_log_file(since=0)
end
def log_filename
- File.join('/tmp', (self.url).gsub(/\W/,'') + '.log')
+ File.join(temp_folder, (self.url).gsub(/\W/,'') + '.log')
end
+ # Returns one commit with the exact revision number provided
def single_revision_xml(revision)
- run "svn log --verbose --xml --stop-on-copy -r #{revision} --limit 1 #{opt_auth} '#{SvnAdapter.uri_encode(self.url)}@#{revision}'"
+ run "svn log --trust-server-cert --non-interactive --verbose --xml --stop-on-copy -r #{revision} --limit 1 #{opt_auth} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name))}@#{revision}' | #{ string_encoder }"
end
# Recurses the entire repository and returns an array of file names.
@@ -171,9 +176,9 @@ def single_revision_xml(revision)
# Directories named 'CVSROOT' are always ignored and the files they contain are never returned.
# An empty array means that the call succeeded, but the remote directory is empty.
# A nil result means that the call failed and the remote server could not be queried.
- def recurse_files(path=nil, revision='HEAD')
+ def recurse_files(path=nil, revision=final_token || 'HEAD')
begin
- stdout = run "svn ls -r #{revision} --recursive #{opt_auth} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s, path.to_s))}@#{revision}'"
+ stdout = run "svn ls --trust-server-cert --non-interactive -r #{revision} --recursive #{opt_auth} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s, path.to_s))}@#{revision}'"
rescue
puts $!.inspect
return nil
@@ -181,7 +186,7 @@ def recurse_files(path=nil, revision='HEAD')
files = []
stdout.each_line do |s|
- s.chomp!
+ s.chomp!.force_encoding('UTF-8')
files << s if s.length > 0 and s !~ /CVSROOT\// and s[-1..-1] != '/'
end
files.sort
diff --git a/lib/scm/adapters/svn/head.rb b/lib/ohloh_scm/adapters/svn/head.rb
similarity index 52%
rename from lib/scm/adapters/svn/head.rb
rename to lib/ohloh_scm/adapters/svn/head.rb
index 90bc1d87..131d1d6f 100644
--- a/lib/scm/adapters/svn/head.rb
+++ b/lib/ohloh_scm/adapters/svn/head.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class SvnAdapter < AbstractAdapter
def head_token
self.info =~ /^Revision: (\d+)$/ ? $1.to_i : nil
@@ -11,8 +11,8 @@ def head
def parents(commit)
# Subversion doesn't have an actual "parent" command, so get
# a log for this commit and the one preceding it, and keep only the preceding.
- log = run "svn log --verbose --xml --stop-on-copy -r #{commit.token}:1 --limit 2 '#{SvnAdapter.uri_encode(self.url)}' #{opt_auth}"
- [deepen_commit(strip_commit_branch(Scm::Parsers::SvnXmlParser.parse(log).last))]
+ log = run "svn log --trust-server-cert --non-interactive --verbose --xml --stop-on-copy -r #{commit.token}:1 --limit 2 '#{SvnAdapter.uri_encode(self.url)}' #{opt_auth} | #{ string_encoder }"
+ [deepen_commit(strip_commit_branch(OhlohScm::Parsers::SvnXmlParser.parse(log).last))]
end
end
end
diff --git a/lib/ohloh_scm/adapters/svn/misc.rb b/lib/ohloh_scm/adapters/svn/misc.rb
new file mode 100644
index 00000000..40069bd2
--- /dev/null
+++ b/lib/ohloh_scm/adapters/svn/misc.rb
@@ -0,0 +1,170 @@
+require 'open-uri'
+require 'nokogiri'
+
+module OhlohScm::Adapters
+ class SvnAdapter < AbstractAdapter
+ # Converts an URL of form file://local/path to simply /local/path.
+ def path
+ case url
+ when /^file:\/\/(.*)/
+ $1
+ when /^svn\+ssh:\/\/([^\/]+)(\/.+)/
+ $2
+ end
+ end
+
+ def hostname
+ $1 if url =~ /^svn\+ssh:\/\/([^\/]+)(\/.+)/
+ end
+
+ # Does some simple searching through the server's directory tree for a
+ # good canditate for the trunk. Basically, we are looking for a trunk
+ # in order to avoid the heavy lifting of processing all the branches and tags.
+ #
+ # There are two simple rules to the search:
+ # (1) If the current directory contains a subdirectory named 'trunk', go there.
+ # (2) If the current directory is empty except for a single subdirectory, go there.
+ # Repeat until neither rule is satisfied.
+ #
+ # The url and branch_name of this object will be updated with the selected location.
+ # The url will be unmodified if there is a problem connecting to the server.
+ def restrict_url_to_trunk
+ return self.url if self.url =~ /\/trunk\/?$/
+
+ list = ls
+ return self.url unless list
+
+ if list.include? 'trunk/'
+ self.url = File.join(self.url, 'trunk')
+ self.branch_name = File.join(self.branch_name, 'trunk')
+ elsif list.size == 1 and list.first[-1..-1] == '/'
+ self.url = File.join(self.url, list.first[0..-2])
+ self.branch_name = File.join(self.branch_name, list.first[0..-2])
+ return restrict_url_to_trunk
+ end
+ self.url
+ end
+
+ # It appears that the default URI encoder does not encode some characters.
+ # This fixes it for us.
+ def self.uri_encode(uri)
+ URI.encode(uri,/#{URI::UNSAFE}|[\[\]';\? ]/) # Add [ ] ' ; ? and space
+ end
+
+ def exist?
+ begin
+ !!(head_token)
+ rescue
+ logger.debug { $! }
+ false
+ end
+ end
+
+ def info(path=nil, revision=final_token || 'HEAD')
+ @info ||= {}
+ uri = if path
+ File.join(root, branch_name.to_s, path)
+ else
+ url
+ end
+ @info[[path, revision]] ||= run "svn info --trust-server-cert --non-interactive -r #{revision} #{opt_auth} '#{SvnAdapter.uri_encode(uri)}@#{revision}'"
+ end
+
+ def root
+ $1 if self.info =~ /^Repository Root: (.+)$/
+ end
+
+ def uuid
+ $1 if self.info =~ /^Repository UUID: (.+)$/
+ end
+
+ # Returns an array of file and directory names.
+ # Directory names will end with a trailing '/' character.
+ # Directories named 'CVSROOT' are always ignored and never returned.
+ # An empty array means that the call succeeded, but the remote directory is empty.
+ # A nil result means that the call failed and the remote server could not be queried.
+ def ls(path=nil, revision=final_token || 'HEAD')
+ begin
+ stdout = run "svn ls --trust-server-cert --non-interactive -r #{revision} #{opt_auth} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s, path.to_s))}@#{revision}'"
+ rescue
+ return nil
+ end
+
+ files = []
+ stdout.each_line do |s|
+ s.chomp!
+ files << s if s.length > 0 and s != 'CVSROOT/'
+ end
+ files.sort
+ end
+
+ def node_kind(path=nil, revision=final_token || 'HEAD')
+ $1 if self.info(path, revision) =~ /Node Kind: (\w+)\W/
+ end
+
+ def is_directory?(path=nil, revision=final_token || 'HEAD')
+ begin
+ return node_kind(path, revision) == 'directory'
+ rescue Exception
+ if $!.message =~ /svn: E200009: Could not display info for all targets because some targets don't exist/
+ return false
+ else
+ raise
+ end
+ end
+ end
+
+ def checkout(rev, dest_dir)
+ FileUtils.mkdir_p(File.dirname(dest_dir)) unless FileTest.exist?(File.dirname(dest_dir))
+ run "svn checkout --trust-server-cert --non-interactive -r #{rev.token} '#{SvnAdapter.uri_encode(self.url)}@#{rev.token}' '#{dest_dir}' --ignore-externals #{opt_auth}"
+ end
+
+ def export(dest_dir, commit_id = final_token || 'HEAD')
+ FileUtils.mkdir_p(File.dirname(dest_dir)) unless FileTest.exist?(File.dirname(dest_dir))
+ run "svn export --trust-server-cert --non-interactive --ignore-externals --force -r #{commit_id} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}' '#{dest_dir}'"
+ end
+
+ def export_tag(dest_dir, tag_name)
+ tag_url = "#{base_path}/tags/#{tag_name}"
+ run "svn export --trust-server-cert --non-interactive --ignore-externals --force '#{tag_url}' '#{dest_dir}'"
+ end
+
+ def ls_tree(token)
+ run("svn ls --trust-server-cert --non-interactive -R -r #{token} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}@#{token}'").split("\n")
+ end
+
+ def opt_auth
+ opt_password = ""
+ opt_password = "--username='#{self.username}' --password='#{self.password}'" if self.username && self.username != ''
+ " #{opt_password} --no-auth-cache "
+ end
+
+ # Svn root is not usable here since several projects are nested in subfolders.
+ # e.g. https://svn.apache.org/repos/asf/openoffice/ooo-site/trunk/
+ # http://svn.apache.org/repos/asf/httpd/httpd/trunk
+ # http://svn.apache.org/repos/asf/maven/plugin-testing/trunk
+ # all have the same root value(https://svn.apache.org/repos/asf)
+ def tags
+ doc = Nokogiri::XML(`svn ls --xml #{ base_path}/tags`)
+ doc.xpath('//lists/list/entry').map do |entry|
+ tag_name = entry.xpath('name').text
+ revision = entry.xpath('commit').attr('revision').text
+ date_string = Time.parse(entry.xpath("commit/date").text)
+ [tag_name, revision, date_string]
+ end
+ end
+
+ class << self
+ def has_conflicts?(working_copy_url)
+ system("cd '#{ working_copy_url }' && svn status | grep 'Summary of conflicts'")
+ end
+ end
+
+ private
+
+ def base_path
+ url.sub(/(.*)(branches|trunk|tags)(.*)/, '\1').chomp('/')
+ end
+
+ end
+end
diff --git a/lib/scm/adapters/svn/patch.rb b/lib/ohloh_scm/adapters/svn/patch.rb
similarity index 50%
rename from lib/scm/adapters/svn/patch.rb
rename to lib/ohloh_scm/adapters/svn/patch.rb
index 394f4264..5bb982fc 100644
--- a/lib/scm/adapters/svn/patch.rb
+++ b/lib/ohloh_scm/adapters/svn/patch.rb
@@ -1,8 +1,8 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class SvnAdapter < AbstractAdapter
def patch_for_commit(commit)
parent = commit.token.to_i - 1
- run("svn diff -r#{parent}:#{commit.token} #{url}")
+ run("svn diff --trust-server-cert --non-interactive -r#{parent}:#{commit.token} #{url}")
end
end
end
diff --git a/lib/scm/adapters/svn/pre-revprop-change b/lib/ohloh_scm/adapters/svn/pre-revprop-change
similarity index 100%
rename from lib/scm/adapters/svn/pre-revprop-change
rename to lib/ohloh_scm/adapters/svn/pre-revprop-change
diff --git a/lib/scm/adapters/svn/pull.rb b/lib/ohloh_scm/adapters/svn/pull.rb
similarity index 75%
rename from lib/scm/adapters/svn/pull.rb
rename to lib/ohloh_scm/adapters/svn/pull.rb
index 16f42111..67b0ddf4 100644
--- a/lib/scm/adapters/svn/pull.rb
+++ b/lib/ohloh_scm/adapters/svn/pull.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class SvnAdapter < AbstractAdapter
def pull(from)
@@ -42,6 +42,7 @@ def svnadmin_create_local
FileUtils.rmdir path
run "svnadmin create #{path}"
FileUtils.cp pre_revprop_change_template, pre_revprop_change_path
+ FileUtils.chmod 0755, pre_revprop_change_path
end
def svnadmin_create_remote
@@ -50,7 +51,7 @@ def svnadmin_create_remote
end
def svnsync_init(from)
- run "svnsync init #{from.opt_auth} '#{url}' #{from.root}"
+ run "svnsync init --trust-server-cert --non-interactive #{from.opt_auth} '#{url}' #{from.root}"
end
def self.svnsync_sync(src, dest)
@@ -61,15 +62,15 @@ def self.svnsync_sync(src, dest)
dest.propset('sync-from-url', src.root)
dest.propset('sync-from-uuid', src.uuid)
- run "svnsync sync #{src.opt_auth} --non-interactive '#{SvnAdapter.uri_encode(dest.root)}'"
+ run "svnsync sync #{src.opt_auth} --trust-server-cert --non-interactive '#{SvnAdapter.uri_encode(dest.root)}'"
end
def propget(propname)
- run("svn propget #{opt_auth} --revprop -r 0 svn:#{propname} '#{SvnAdapter.uri_encode(root)}'").strip!
+ run("svn propget --trust-server-cert --non-interactive #{opt_auth} --revprop -r 0 svn:#{propname} '#{SvnAdapter.uri_encode(root)}'").strip!
end
def propset(propname, value)
- run("svn propset #{opt_auth} --revprop -r 0 svn:#{propname} #{value} '#{SvnAdapter.uri_encode(root)}'")
+ run("svn propset --trust-server-cert --non-interactive #{opt_auth} --revprop -r 0 svn:#{propname} #{value} '#{SvnAdapter.uri_encode(root)}'")
end
end
diff --git a/lib/scm/adapters/svn/push.rb b/lib/ohloh_scm/adapters/svn/push.rb
similarity index 89%
rename from lib/scm/adapters/svn/push.rb
rename to lib/ohloh_scm/adapters/svn/push.rb
index 1a8f99e2..a338004e 100644
--- a/lib/scm/adapters/svn/push.rb
+++ b/lib/ohloh_scm/adapters/svn/push.rb
@@ -1,4 +1,4 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class SvnAdapter < AbstractAdapter
def push(to)
diff --git a/lib/scm/adapters/svn/validation.rb b/lib/ohloh_scm/adapters/svn/validation.rb
similarity index 55%
rename from lib/scm/adapters/svn/validation.rb
rename to lib/ohloh_scm/adapters/svn/validation.rb
index 347c44d9..43d4a5ab 100644
--- a/lib/scm/adapters/svn/validation.rb
+++ b/lib/ohloh_scm/adapters/svn/validation.rb
@@ -1,29 +1,42 @@
-module Scm::Adapters
+module OhlohScm::Adapters
class SvnAdapter < AbstractAdapter
def self.url_regex
- /^(file|http|https|svn):\/\/(\/)?[A-Za-z0-9_\-\.]+(:\d+)?(\/[A-Za-z0-9_\-\.\/\+%^~]*)?$/
+ /^(file|http|https|svn):\/\/(\/)?[A-Za-z0-9_\-\.]+(:\d+)?(\/[A-Za-z0-9_@\-\.\/\+%^~ ]*)?$/
end
def self.public_url_regex
- /^(http|https|svn):\/\/[A-Za-z0-9_\-\.]+(:\d+)?(\/[A-Za-z0-9_\-\.\/\+%^~]*)?$/
+ /^(http|https|svn):\/\/[A-Za-z0-9_\-\.]+(:\d+)?(\/[A-Za-z0-9_\-\.\/\+%^~ ]*)?$/
end
def normalize
super
@url = path_to_file_url(@url)
@url = force_https_if_sourceforge(@url)
- @branch_name = @branch_name[0..-2] if @branch_name && @branch_name[-1..-1] == '/'
+ if @branch_name
+ clean_branch_name
+ else
+ @branch_name = recalc_branch_name
+ end
self
end
+ # Subversion usernames have been relaxed from the abstract rules. We allow email names as usernames.
+ def validate_username
+ return nil unless @username
+ return nil if @username.length == 0
+ return [:username, "The username must not be longer than 32 characters."] unless @username.length <= 32
+ return [:username, "The username contains illegal characters."] unless @username =~ /^\w[\w@\.\+\-]*$/
+ end
+
# If the URL is a simple directory path, make sure it is prefixed by file://
def path_to_file_url(path)
+ return nil if path.empty?
url =~ /:\/\// ? url : 'file://' + File.expand_path(path)
end
def force_https_if_sourceforge(url)
# SourceForge requires https for svnsync
- url =~ /http(:\/\/.*svn\.sourceforge\.net.*)/ ? "https#{$1}" : url
+ url =~ /http(:\/\/.*svn\.(sourceforge|code\.sf)\.net.*)/ ? "https#{$1}" : url
end
def validate_server_connection
@@ -33,7 +46,7 @@ def validate_server_connection
@errors << [:failed, "The server did not respond to a 'svn info' command. Is the URL correct?"]
elsif self.url[0..root.length-1] != root
@errors << [:failed, "The URL did not match the Subversion root #{root}. Is the URL correct?"]
- elsif ls.nil?
+ elsif recalc_branch_name && ls.nil?
@errors << [:failed, "The server did not respond to a 'svn ls' command. Is the URL correct?"]
end
rescue
@@ -45,20 +58,30 @@ def validate_server_connection
# From the given URL, determine which part of it is the root and which part of it is the branch_name.
# The current branch_name is overwritten.
def recalc_branch_name
- @branch_name = @url ? @url[root.length..-1] : @branch_name
- @branch_name = @branch_name[0..-2] if @branch_name[-1..-1] == '/'
- @branch_name
+ begin
+ @branch_name = @url ? @url[root.length..-1] : @branch_name
+ rescue RuntimeError => exception
+ @branch_name = '' if exception.message =~ /(svn:*is not a working copy|Unable to open an ra_local session to URL)/ # we have a file system
+ end
+ clean_branch_name
+ @branch_name
end
def guess_forge
- u = @url =~ /:\/\/(.*\.?svn\.)?([^\/^:]+)(:\d+)?\// ? $2 : nil
+ u = @url =~ /:\/\/(.*\.?svn\.)?([^\/^:]+)(:\d+)?(\/|$)/ ? $2 : nil
case u
when /(googlecode\.com$)/, /(tigris\.org$)/, /(sunsource\.net$)/, /(java\.net$)/,
- /(openoffice\.org$)/, /(netbeans\.org$)/, /(dev2dev\.bea\.com$)/
+ /(openoffice\.org$)/, /(netbeans\.org$)/, /(dev2dev\.bea\.com$)/, /(rubyforge\.org$)/
$1
else
u
end
end
+
+ private
+ def clean_branch_name
+ return unless @branch_name
+ @branch_name.chop! if @branch_name.end_with?('/')
+ end
end
end
diff --git a/lib/ohloh_scm/adapters/svn_adapter.rb b/lib/ohloh_scm/adapters/svn_adapter.rb
new file mode 100644
index 00000000..f6a50729
--- /dev/null
+++ b/lib/ohloh_scm/adapters/svn_adapter.rb
@@ -0,0 +1,17 @@
+module OhlohScm::Adapters
+ class SvnAdapter < AbstractAdapter
+ def english_name
+ "Subversion"
+ end
+ end
+end
+
+require_relative 'svn/validation'
+require_relative 'svn/cat_file'
+require_relative 'svn/commits'
+require_relative 'svn/push'
+require_relative 'svn/pull'
+require_relative 'svn/head'
+require_relative 'svn/misc'
+require_relative 'svn/patch'
+
diff --git a/lib/ohloh_scm/adapters/svn_chain/cat_file.rb b/lib/ohloh_scm/adapters/svn_chain/cat_file.rb
new file mode 100644
index 00000000..d4632424
--- /dev/null
+++ b/lib/ohloh_scm/adapters/svn_chain/cat_file.rb
@@ -0,0 +1,8 @@
+module OhlohScm::Adapters
+ class SvnChainAdapter < SvnAdapter
+ def cat(path, revision)
+ parent_svn(revision) ? parent_svn.cat(path, revision) : super(path, revision)
+ end
+ end
+end
+
diff --git a/lib/ohloh_scm/adapters/svn_chain/chain.rb b/lib/ohloh_scm/adapters/svn_chain/chain.rb
new file mode 100644
index 00000000..d7c21063
--- /dev/null
+++ b/lib/ohloh_scm/adapters/svn_chain/chain.rb
@@ -0,0 +1,100 @@
+module OhlohScm::Adapters
+ class SvnChainAdapter < SvnAdapter
+
+ # Returns the entire SvnAdapter ancestry chain as a simple array.
+ def chain
+ (parent_svn ? parent_svn.chain : []) << self
+ end
+
+ # If this adapter's branch was created by copying or renaming another branch,
+ # then return a new adapter that points to that prior branch.
+ #
+ # Only commits following +after+ are considered, so if the copy or rename
+ # occured on or before +after+, then no parent will be found or returned.
+ def parent_svn(after=0)
+ @parent_svn ||={} # Poor man's memoize
+
+ @parent_svn[after] ||= begin
+ parent = nil
+ c = first_commit(after)
+ if c
+ # === Long explanation of real head-scratching bug fix. ===
+ #
+ # It is possible for some Subversion commits to include *multiple*
+ # renames/copies of a source directory. For example:
+ #
+ # A /foo (from /trunk:1)
+ # D /foo/my_branch
+ # A /foo/bar (from /trunk:1)
+ # D /trunk
+ #
+ # If we simply processed these entries in the order given, then
+ # we would conclude that /foo/bar/my_branch has parent
+ # /trunk/bar/my_branch (because the first A matches) and exit.
+ #
+ # This is incorrect! We must look for the *longest* A that matches
+ # our path, and follow that one. In the example above, the correct
+ # parent for /foo/bar/my_branch is /trunk/my_branch.
+ #
+ # Therefore, we must sort diffs by descending filename length, so
+ # that we choose the longest match.
+ c.diffs.sort { |a,b| b.path.length <=> a.path.length }.each do |d|
+
+ # If this diff actually creates this branch, then a parent is impossible.
+ # Stop looking for parents.
+ #
+ # This check exists because of the following complicated commit:
+ # http://dendro.cornell.edu/svn/corina/branches/databasing/src/edu/cornell/dendro@813
+ # It's long to explain, but basically a directory is renamed and
+ # then our branch is created within it, all in a single commit.
+ # Without this check, our code mistakenly thinks there is a parent.
+ if diff_creates_branch(d)
+ return nil
+ end
+
+ if (b = parent_branch_name(d))
+ parent = SvnChainAdapter.new(
+ :url => File.join(root, b), :branch_name => b,
+ :username => username, :password => password,
+ :final_token => d.from_revision).normalize
+ break
+ end
+
+ end
+ end
+ parent
+ end
+ end
+
+ def first_token(after=0)
+ c = first_commit(after)
+ c && c.token
+ end
+
+ def first_commit(after=0)
+ @first_commit ||={} # Poor man's memoize
+ @first_commit[after] ||= OhlohScm::Parsers::SvnXmlParser.parse(next_revision_xml(after)).first
+ end
+
+ # Returns the first commit with a revision number greater than the provided revision number
+ def next_revision_xml(after=0)
+ return "" if after.to_i >= head_token
+ run "svn log --trust-server-cert --non-interactive --verbose --xml --stop-on-copy -r #{after.to_i+1}:#{final_token || 'HEAD'} --limit 1 #{opt_auth} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name.to_s))}@#{final_token || 'HEAD'}' | #{ string_encoder }"
+ end
+
+ # If the passed diff represents the wholesale movement of the entire
+ # code tree from one directory to another, this method returns the name
+ # of the previous directory.
+ def parent_branch_name(d)
+ if %w(A R).include?(d.action) && branch_name[0, d.path.size] == d.path && d.from_path && d.from_revision
+ d.from_path + branch_name[d.path.size..-1]
+ end
+ end
+
+ # True if the passed diff represents the initial creation of the
+ # branch -- not a move or copy from somewhere else.
+ def diff_creates_branch(d)
+ d.action == 'A' && branch_name[0, d.path.size] == d.path && !d.from_path
+ end
+ end
+end
diff --git a/lib/ohloh_scm/adapters/svn_chain/commits.rb b/lib/ohloh_scm/adapters/svn_chain/commits.rb
new file mode 100644
index 00000000..1e174488
--- /dev/null
+++ b/lib/ohloh_scm/adapters/svn_chain/commits.rb
@@ -0,0 +1,37 @@
+module OhlohScm::Adapters
+ class SvnChainAdapter < SvnAdapter
+
+ # Returns the count of commits following revision number 'after'.
+ def commit_count(opts={})
+ (parent_svn(opts[:after]) ? parent_svn(opts[:after]).commit_count(opts) : 0) + super(opts)
+ end
+
+ # Returns an array of revision numbers for all commits following revision number 'after'.
+ def commit_tokens(opts={})
+ (parent_svn(opts[:after]) ? parent_svn(opts[:after]).commit_tokens(opts) : []) + super(opts)
+ end
+
+ # Returns an array of commits following revision number 'after'.
+ def commits(opts={})
+ (parent_svn(opts[:after]) ? parent_svn(opts[:after]).commits(opts) : []) + super(opts)
+ end
+
+ def verbose_commit(rev=0)
+ parent_svn(rev) ? parent_svn.verbose_commit(rev) : super(rev)
+ end
+
+ # If the diff points to a file, simply returns the diff.
+ # If the diff points to a directory, returns an array of diffs for every file in the directory.
+ def deepen_diff(diff, rev)
+ if %w(A R).include?(diff.action) && diff.path == '' && parent_svn && rev == first_token
+ # A very special case that is important for chaining.
+ # This is the first commit, and the entire tree is being created by copying from parent_svn.
+ # In this case, there isn't actually any change, just
+ # a change of branch_name. Return no diffs at all.
+ nil
+ else
+ super(diff, rev)
+ end
+ end
+ end
+end
diff --git a/lib/ohloh_scm/adapters/svn_chain_adapter.rb b/lib/ohloh_scm/adapters/svn_chain_adapter.rb
new file mode 100644
index 00000000..c9c06bfb
--- /dev/null
+++ b/lib/ohloh_scm/adapters/svn_chain_adapter.rb
@@ -0,0 +1,44 @@
+module OhlohScm::Adapters
+ # Some explanation is in order about "chaining."
+ #
+ # First, realize that a base SvnAdapter only tracks the history of a single
+ # subdirectory. If you point an adapter at /trunk, then that adapter is
+ # going to ignore eveything in /branches and /tags.
+ #
+ # The problem with this is that directories often get moved about. What is
+ # called "/trunk" today might have been in a branch directory at some point
+ # in the past. But after we completely ignore other directories, we never see
+ # that old history.
+ #
+ # Suppose for example that from revisions 1 to 100, development occured in
+ # /branches/beta. Then at revision 101, /trunk was created by copying
+ # /branches/beta, and this /trunk lives on to this day.
+ #
+ # The log for revision 101 is going to look something like this:
+ #
+ # Changed paths:
+ # D /branches/beta
+ # A /trunk (from /branches/beta:100)
+ #
+ # A single SvnAdapter pointed at today's /trunk will only see revisions 101
+ # through HEAD, because /trunk didn't even exist before revision 101.
+ #
+ # To capture the prior history, we need to create *another* SvnAdapter
+ # which points at /branches/beta, and which considers revisions from 1 to 100.
+ #
+ # That's what chaining is: when we find that the first commit of an adapter
+ # indicates the wholesale renaming or copying of the entire tree from
+ # another location, then we generate a new SvnAdapter that points to that
+ # prior location, and process that SvnAdapter as well.
+ #
+ # This behavior recurses ("chains") all the way back to revision 1.
+ #
+ # It only works if the *entire branch* moves. We don't chain when
+ # subdirectories or individual files are copied.
+ class SvnChainAdapter < SvnAdapter
+ end
+end
+
+require_relative 'svn_chain/chain'
+require_relative 'svn_chain/commits'
+require_relative 'svn_chain/cat_file'
diff --git a/lib/scm/commit.rb b/lib/ohloh_scm/commit.rb
similarity index 95%
rename from lib/scm/commit.rb
rename to lib/ohloh_scm/commit.rb
index af119113..2a8e12f0 100644
--- a/lib/scm/commit.rb
+++ b/lib/ohloh_scm/commit.rb
@@ -1,4 +1,4 @@
-module Scm
+module OhlohScm
# A commit is a collection of diffs united by a single timestamp, author, and
# message.
#
@@ -34,6 +34,9 @@ class Commit
# the approximate timestamp of the change.
attr_accessor :token
+ # A pointer back to the adapter that contains this commit.
+ attr_accessor :scm
+
# Hack. To optimize CVS updates, we will store the names of all the
# directories that require updating during this commit. Ohloh itself never
# actually sees this.
diff --git a/lib/scm/diff.rb b/lib/ohloh_scm/diff.rb
similarity index 87%
rename from lib/scm/diff.rb
rename to lib/ohloh_scm/diff.rb
index 82db21a8..83169185 100644
--- a/lib/scm/diff.rb
+++ b/lib/ohloh_scm/diff.rb
@@ -1,10 +1,10 @@
-module Scm
+module OhlohScm
# A +Diff+ represents a change to a single file. It can represent the addition or
# deletion of a file, or it can represent a modification of the file contents.
- #
+ #
# Ohloh does not track filename changes. If a file is renamed, Ohloh treats this
# as the deletion of one file and the creation of another.
- #
+ #
# Ohloh does not track directories, only the files within directories.
#
# Don't confuse our use of the word "Diff" with a patch file or the output of the
@@ -22,11 +22,15 @@ class Diff
# 'M' modified
# 'D' deleted
attr_accessor :action
-
+
# The SHA1 hash of the file contents both before and after the change.
# These must be computed using the same method as Git.
attr_accessor :parent_sha1, :sha1
+ # For Subversion only, a path may be reported as copied from another location.
+ # These attributes store the path and revision number of the source of the copy.
+ attr_accessor :from_path, :from_revision
+
def initialize(params={})
params.each { |k,v| send(k.to_s + '=', v) if respond_to?(k.to_s + '=') }
end
diff --git a/lib/scm/parsers/array_writer.rb b/lib/ohloh_scm/parsers/array_writer.rb
similarity index 90%
rename from lib/scm/parsers/array_writer.rb
rename to lib/ohloh_scm/parsers/array_writer.rb
index 53716024..4d248999 100644
--- a/lib/scm/parsers/array_writer.rb
+++ b/lib/ohloh_scm/parsers/array_writer.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
class ArrayWriter
attr_accessor :buffer
diff --git a/lib/scm/parsers/branch_number.rb b/lib/ohloh_scm/parsers/branch_number.rb
similarity index 98%
rename from lib/scm/parsers/branch_number.rb
rename to lib/ohloh_scm/parsers/branch_number.rb
index 6b235c7e..234cbc47 100644
--- a/lib/scm/parsers/branch_number.rb
+++ b/lib/ohloh_scm/parsers/branch_number.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
class BranchNumber
def initialize(s)
@a = s.split('.').collect { |i| i.to_i }
diff --git a/lib/ohloh_scm/parsers/bzr_parser.rb b/lib/ohloh_scm/parsers/bzr_parser.rb
new file mode 100644
index 00000000..acc79bba
--- /dev/null
+++ b/lib/ohloh_scm/parsers/bzr_parser.rb
@@ -0,0 +1,128 @@
+module OhlohScm::Parsers
+ # This parser can process the default bzr logs, with or without the --verbose flag.
+ class BzrParser < Parser
+ def self.scm
+ 'bzr'
+ end
+
+ def self.internal_parse(buffer, opts)
+ e = nil
+ state = :data
+ action = ''
+ indent = '' # Track the level of indentation as we descend into branches
+ show_id = false # true if this log includes revision and file ids
+
+ buffer.each_line do |l|
+ next_state = state
+
+ case l
+ # A commit message can contain lines of only dashes, which makes parsing difficult.
+ #
+ # This delimiter detector filters most casual cases of using dash lines in commits.
+ # We check that the dashed line is exactly 60 chars long, and is prepended by 4*n spaces.
+ #
+ # Unless the commit message itself includes leading spaces, the commit message will
+ # begin in column 4*n+2, and thus will not match our pattern.
+ when /^(( )*)-{60,60}$/
+ # a new commit begins
+ indent = $1
+ if e && block_given?
+ e.diffs = remove_dupes(e.diffs)
+ yield e
+ end
+ e = OhlohScm::Commit.new
+ e.diffs = []
+ next_state = :data
+ when /^#{indent}revno:\s+(\d+)$/
+ e.token = $1
+ next_state = :data
+ when /^#{indent}revision-id:\s+(\S+)$/
+ e.token = $1
+ show_id = true
+ next_state = :data
+ when /^#{indent}author:\s+(.+?)(\s+<(.+)>)?$/
+ e.author_name = $1
+ e.author_email = $3
+ next_state = :data
+ when /^#{indent}committer:\s+(.+?)(\s+<(.+)>)?$/
+ e.committer_name = $1
+ e.committer_email = $3
+ next_state = :data
+ when /^#{indent}timestamp:\s+(.+)/
+ e.committer_date = Time.parse($1)
+ next_state = :data
+ when /^#{indent}added:$/
+ next_state = :collect_files
+ action = 'A'
+ when /^#{indent}modified:$/
+ next_state = :collect_files
+ action = 'M'
+ when /^#{indent}removed:$/
+ next_state = :collect_files
+ action = 'D'
+ when /^#{indent}renamed:$/
+ next_state = :collect_files
+ action = :rename
+ when /^#{indent}message:$/
+ next_state = :collect_message
+ e.message ||= ''
+ when /^#{indent} (.*)$/
+ case state
+ when :collect_files
+ line = $1
+ # strip the id from the end of the line if it is present
+ line = $1 if show_id && line =~ /^(.+?)\s+(\S+)$/
+ parse_diffs(action, line).each { |d| e.diffs << d }
+ when :collect_message
+ e.message << $1
+ e.message << "\n"
+ end
+ end
+
+ state = next_state
+ end
+ if e && block_given?
+ e.diffs = remove_dupes(e.diffs)
+ yield e
+ end
+ end
+
+ # Given a line from the log represent a file operation,
+ # return a collection of diffs for that action
+ def self.parse_diffs(action, line)
+ case action
+ when :rename
+ # A rename action requires two diffs: one to remove the old filename,
+ # another to add the new filename.
+ #
+ # Note that is possible to be renamed to the empty string!
+ # This happens when a subdirectory is moved to become the root.
+ before, after = line.scan(/(.+) => ?(.*)/).first
+ [ OhlohScm::Diff.new(:action => 'D', :path => before),
+ OhlohScm::Diff.new(:action => 'A', :path => after || '' )]
+ else
+ [OhlohScm::Diff.new(:action => action, :path => line)]
+ end.each do |d|
+ d.path = strip_trailing_asterisk(d.path)
+ end
+ end
+
+ def self.strip_trailing_asterisk(path)
+ path[-1..-1] == '*' ? path[0..-2] : path
+ end
+
+ def self.remove_dupes(diffs)
+ # Bazaar may report that a file was added and modified in a single commit.
+ # Reduce these cases to a single 'A' action.
+ diffs.delete_if do |d|
+ d.action == 'M' && diffs.select { |x| x.path == d.path && x.action == 'A' }.any?
+ end
+
+ # Bazaar may report that a file was both deleted and added in a single commit.
+ # Reduce these cases to a single 'M' action.
+ diffs.each do |d|
+ d.action = 'M' if diffs.select { |x| x.path == d.path }.size > 1
+ end.uniq
+ end
+ end
+end
diff --git a/lib/ohloh_scm/parsers/bzr_xml_parser.rb b/lib/ohloh_scm/parsers/bzr_xml_parser.rb
new file mode 100644
index 00000000..325e25e4
--- /dev/null
+++ b/lib/ohloh_scm/parsers/bzr_xml_parser.rb
@@ -0,0 +1,158 @@
+require 'rexml/document'
+require 'rexml/streamlistener'
+
+module OhlohScm::Parsers
+ class BazaarListener
+ include REXML::StreamListener
+ attr_accessor :callback
+
+ def initialize(callback)
+ @callback = callback
+ @merge_commit = []
+ @state = :none
+ @authors = []
+ end
+
+ attr_accessor :text, :commit, :diff
+
+ def tag_start(name, attrs)
+ case name
+ when 'log'
+ @commit = OhlohScm::Commit.new
+ @commit.diffs = []
+ when 'affected-files'
+ @diffs = []
+ when 'added', 'modified', 'removed', 'renamed'
+ @action = name
+ @state = :collect_files
+ when 'file'
+ @before_path = attrs['oldpath']
+ when 'merge'
+ # This is a merge commit, save it and pop it after all branch commits
+ @merge_commit.push(@commit)
+ when 'authors'
+ @state = :collect_authors
+ @authors = []
+ end
+ end
+
+ def tag_end(name)
+ case name
+ when 'log'
+ @callback.call(@commit)
+ when 'revisionid'
+ @commit.token = @text
+ when 'message'
+ @commit.message = @cdata
+ when 'committer'
+ committer = BzrXmlParser.capture_name(@text)
+ @commit.committer_name = committer[0]
+ @commit.committer_email = committer[1]
+ when 'author'
+ author = BzrXmlParser.capture_name(@text)
+ @authors << {:author_name => author[0], :author_email => author[1]}
+ when 'timestamp'
+ @commit.committer_date = Time.parse(@text)
+ when 'file'
+ if @state == :collect_files
+ @diffs.concat(parse_diff(@action, @text, @before_path))
+ end
+ @before_path = nil
+ @text = nil
+ when 'added', 'modified', 'removed', 'renamed'
+ @state = :none
+ when 'affected-files'
+ @commit.diffs = remove_dupes(@diffs)
+ when 'merge'
+ @commit = @merge_commit.pop
+ when 'authors'
+ @commit.author_name = @authors[0][:author_name]
+ @commit.author_email = @authors[0][:author_email]
+ @authors.clear
+ end
+ end
+
+ def cdata(data)
+ @cdata = data
+ end
+
+ def text(text)
+ @text = text
+ end
+
+ private
+ # Parse one single diff
+ def parse_diff(action, path, before_path)
+ diffs = []
+ case action
+ # A rename action requires two diffs: one to remove the old filename,
+ # another to add the new filename.
+ #
+ # Note that is possible to be renamed to the empty string!
+ # This happens when a subdirectory is moved to become the root.
+ when 'renamed'
+ diffs = [ OhlohScm::Diff.new(:action => 'D', :path => before_path),
+ OhlohScm::Diff.new(:action => 'A', :path => path || '')]
+ when 'added'
+ diffs = [OhlohScm::Diff.new(:action => 'A', :path => path)]
+ when 'modified'
+ diffs = [OhlohScm::Diff.new(:action => 'M', :path => path)]
+ when 'removed'
+ diffs = [OhlohScm::Diff.new(:action => 'D', :path => path)]
+ end
+ diffs.each do |d|
+ d.path = strip_trailing_asterisk(d.path)
+ end
+ diffs
+ end
+
+ def strip_trailing_asterisk(path)
+ path[-1..-1] == '*' ? path[0..-2] : path
+ end
+
+ def remove_dupes(diffs)
+ BzrXmlParser.remove_dupes(diffs)
+ end
+
+ end
+
+ class BzrXmlParser < Parser
+ NAME_REGEX = /^(.+?)(\s+<(.+)>\s*)?$/
+ def self.internal_parse(buffer, opts)
+ buffer = '' if buffer.is_a?(StringIO) and buffer.length < 2
+ begin
+ REXML::Document.parse_stream(buffer, BazaarListener.new(Proc.new { |c| yield c if block_given? }))
+ rescue EOFError
+ end
+ end
+
+ def self.scm
+ 'bzr'
+ end
+
+ def self.remove_dupes(diffs)
+ # Bazaar may report that a file was added and modified in a single commit.
+ # Reduce these cases to a single 'A' action.
+ diffs.delete_if do |d|
+ d.action == 'M' && diffs.select { |x| x.path == d.path && x.action == 'A' }.any?
+ end
+
+ # Bazaar may report that a file was both deleted and added in a single commit.
+ # Reduce these cases to a single 'M' action.
+ diffs.each do |d|
+ d.action = 'M' if diffs.select { |x| x.path == d.path }.size > 1
+ end.uniq
+ end
+
+ # Bazaar expects committer/author to be specified in this format
+ # Name , or John Doe
+ # However, we find many variations in the real world including
+ # ones where only email is specified as name.
+ def self.capture_name(text)
+ parts = text.match(NAME_REGEX).to_a
+ name = parts[1] || parts[0]
+ email = parts[3]
+ [name, email]
+ end
+ end
+end
diff --git a/lib/scm/parsers/cvs_parser.rb b/lib/ohloh_scm/parsers/cvs_parser.rb
similarity index 98%
rename from lib/scm/parsers/cvs_parser.rb
rename to lib/ohloh_scm/parsers/cvs_parser.rb
index 17fd4274..99a49816 100644
--- a/lib/scm/parsers/cvs_parser.rb
+++ b/lib/ohloh_scm/parsers/cvs_parser.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
class CvsParser < Parser
def self.scm
@@ -139,7 +139,7 @@ def self.read_commit(io, filename, commit_number, should_yield)
should_yield = false if commit_number == '1.1' and state == 'dead'
message = read_message(io)
if should_yield
- commit = Scm::Commit.new
+ commit = OhlohScm::Commit.new
commit.token = committer_date[0..18]
commit.committer_date = Time.parse(committer_date[0..18] + ' +0000').utc
commit.committer_name = committer_name
diff --git a/lib/ohloh_scm/parsers/git_parser.rb b/lib/ohloh_scm/parsers/git_parser.rb
new file mode 100644
index 00000000..8a48a46d
--- /dev/null
+++ b/lib/ohloh_scm/parsers/git_parser.rb
@@ -0,0 +1,67 @@
+module OhlohScm::Parsers
+ class GitParser < Parser
+ def self.scm
+ 'git'
+ end
+
+ ANONYMOUS = "(no author)" unless defined?(ANONYMOUS)
+
+ def self.internal_parse(io, opts)
+ e = nil
+ state = :key_values
+
+ io.each do |line|
+ line.chomp!
+
+ # Kind of a hack: the diffs section is not always present.
+ # Also, we don't know when the next commit is going to begin,
+ # so we may need to make an unexpected state change.
+ if line =~ /^commit ([a-z0-9]{40,40})$/
+ state = :key_values
+ elsif state == :message and line =~ /^[ADM]\s+(.+)$/
+ state = :diffs
+ end
+
+ if state == :key_values
+ case line
+ when /^commit ([a-z0-9]{40,40})$/
+ sha1 = $1
+ yield e if e
+ e = OhlohScm::Commit.new
+ e.diffs = []
+ e.token = sha1
+ e.author_name = ANONYMOUS
+ when /^Author: (.+) <(.*)>$/
+ # In the rare case that the Git repository does not contain any names (see OpenEmbedded for example)
+ # we use the email instead.
+ e.author_name = $1 || $2
+ e.author_email = $2
+ when /^Date: (.*)$/
+ e.author_date = Time.parse($1).utc # Note strongly: MUST be RFC2822 format to parse properly
+ state = :message
+ end
+
+ elsif state == :message
+ case line
+ when / (.*)/
+ if e.message
+ e.message << "\n" << $1
+ else
+ e.message = $1
+ end
+ end
+
+ elsif state == :diffs
+ if line =~ /^([ADM])\t(.+)$/
+ e.diffs << OhlohScm::Diff.new( :action => $1, :path => $2)
+ end
+
+ else
+ raise RuntimeError("Unknown parser state #{state.to_s}")
+ end
+ end
+
+ yield e if e
+ end
+ end
+end
diff --git a/lib/scm/parsers/git_styled_parser.rb b/lib/ohloh_scm/parsers/git_styled_parser.rb
similarity index 78%
rename from lib/scm/parsers/git_styled_parser.rb
rename to lib/ohloh_scm/parsers/git_styled_parser.rb
index 7f020c7b..beec97ba 100644
--- a/lib/scm/parsers/git_styled_parser.rb
+++ b/lib/ohloh_scm/parsers/git_styled_parser.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
# This parser processes Git whatchanged generated using a custom style.
# This custom style provides additional information required by Ohloh.
class GitStyledParser < Parser
@@ -7,11 +7,11 @@ def self.scm
end
def self.whatchanged
- "git whatchanged --root -m --abbrev=40 --max-count=1 --pretty=#{format}"
+ "git whatchanged --root -m --abbrev=40 --max-count=1 --always --pretty=#{format}"
end
def self.format
- "format:'__BEGIN_COMMIT__%nCommit: %H%nAuthor: %an%nAuthorEmail: %ae%nDate: %aD%n__BEGIN_COMMENT__%n%s%n%b%n__END_COMMENT__'"
+ "format:'__BEGIN_COMMIT__%nCommit: %H%nAuthor: %an%nAuthorEmail: %ae%nDate: %aD%n__BEGIN_COMMENT__%n%s%n%b%n__END_COMMENT__%n'"
end
ANONYMOUS = "(no author)" unless defined?(ANONYMOUS)
@@ -35,14 +35,15 @@ def self.internal_parse(io, opts)
if line =~ /^Commit: ([a-z0-9]+)$/
sha1 = $1
yield e if e
- e = Scm::Commit.new
+ e = OhlohScm::Commit.new
e.diffs = []
e.token = sha1
e.author_name = ANONYMOUS
elsif line =~ /^Author: (.+)$/
e.author_name = $1
elsif line =~ /^Date: (.*)$/
- e.author_date = Time.parse($1).utc # Note strongly: MUST be RFC2822 format to parse properly
+ # MUST be RFC2822 format to parse properly, else defaults to epoch time
+ e.author_date = parse_date($1)
elsif line == "__BEGIN_COMMENT__"
state = :message
elsif line =~ /^AuthorEmail: (.+)$/
@@ -69,7 +70,7 @@ def self.internal_parse(io, opts)
elsif line =~ /:([0-9]+) ([0-9]+) ([a-z0-9]+) ([a-z0-9]+) ([A-Z])\t"?(.+[^"])"?$/
# Submodules have a file mode of '160000', which indicates a "gitlink"
# We ignore submodules completely.
- e.diffs << Scm::Diff.new( :action => $5, :path => $6, :sha1 => $4, :parent_sha1 => $3 ) unless $1=='160000' || $2=='160000'
+ e.diffs << OhlohScm::Diff.new( :action => $5, :path => $6, :sha1 => $4, :parent_sha1 => $3 ) unless $1=='160000' || $2=='160000'
end
else
@@ -79,5 +80,10 @@ def self.internal_parse(io, opts)
yield e if e
end
+
+ def self.parse_date(date)
+ t = Time.rfc2822(date) rescue Time.at(0)
+ t.utc
+ end
end
end
diff --git a/lib/scm/parsers/hg_parser.rb b/lib/ohloh_scm/parsers/hg_parser.rb
similarity index 92%
rename from lib/scm/parsers/hg_parser.rb
rename to lib/ohloh_scm/parsers/hg_parser.rb
index 6c006fac..48cf9cee 100644
--- a/lib/scm/parsers/hg_parser.rb
+++ b/lib/ohloh_scm/parsers/hg_parser.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
# This parser can process the default hg logs, with or without the --verbose flag.
# It is handy for debugging but is not detailed enough for Ohloh analysis.
# See the HgStyledParser.
@@ -17,7 +17,7 @@ def self.internal_parse(buffer, opts)
case l
when /^changeset:\s+\d+:([0-9a-f]+)/
yield e if e && block_given?
- e = Scm::Commit.new
+ e = OhlohScm::Commit.new
e.diffs = []
e.token = $1
when /^user:\s+(.+?)(\s+<(.+)>)?$/
@@ -27,7 +27,7 @@ def self.internal_parse(buffer, opts)
e.committer_date = Time.parse($1).utc
when /^files:\s+(.+)/
($1 || '').split(' ').each do |file|
- e.diffs << Scm::Diff.new(:action => '?', :path => file)
+ e.diffs << OhlohScm::Diff.new(:action => '?', :path => file)
end
when /^summary:\s+(.+)/
e.message = $1
diff --git a/lib/scm/parsers/hg_style b/lib/ohloh_scm/parsers/hg_style
similarity index 100%
rename from lib/scm/parsers/hg_style
rename to lib/ohloh_scm/parsers/hg_style
diff --git a/lib/scm/parsers/hg_styled_parser.rb b/lib/ohloh_scm/parsers/hg_styled_parser.rb
similarity index 92%
rename from lib/scm/parsers/hg_styled_parser.rb
rename to lib/ohloh_scm/parsers/hg_styled_parser.rb
index 40181218..2dc6e753 100644
--- a/lib/scm/parsers/hg_styled_parser.rb
+++ b/lib/ohloh_scm/parsers/hg_styled_parser.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
# This parser processes Mercurial logs which have been generated using a custom style.
# This custom style provides additional information required by Ohloh.
class HgStyledParser < Parser
@@ -25,7 +25,7 @@ def self.internal_parse(buffer, opts)
if state == :data
case l
when /^changeset:\s+([0-9a-f]+)/
- e = Scm::Commit.new
+ e = OhlohScm::Commit.new
e.diffs = []
e.token = $1
when /^user:\s+(.+?)(\s+<(.+)>)?$/
@@ -46,7 +46,7 @@ def self.internal_parse(buffer, opts)
if l == "__END_FILES__\n"
next_state = :data
elsif l =~ /^([MAD]) (.+)$/
- e.diffs << Scm::Diff.new(:action => $1, :path => $2)
+ e.diffs << OhlohScm::Diff.new(:action => $1, :path => $2)
end
elsif state == :long_comment
diff --git a/lib/scm/parsers/hg_verbose_style b/lib/ohloh_scm/parsers/hg_verbose_style
similarity index 100%
rename from lib/scm/parsers/hg_verbose_style
rename to lib/ohloh_scm/parsers/hg_verbose_style
diff --git a/lib/scm/parsers/human_writer.rb b/lib/ohloh_scm/parsers/human_writer.rb
similarity index 98%
rename from lib/scm/parsers/human_writer.rb
rename to lib/ohloh_scm/parsers/human_writer.rb
index 972c2515..67d4d702 100644
--- a/lib/scm/parsers/human_writer.rb
+++ b/lib/ohloh_scm/parsers/human_writer.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
class HumanWriter
# Note that we use << instead of write() or puts() in this writer because
# the << operator works on both File and String objects.
diff --git a/lib/scm/parsers/parser.rb b/lib/ohloh_scm/parsers/parser.rb
similarity index 95%
rename from lib/scm/parsers/parser.rb
rename to lib/ohloh_scm/parsers/parser.rb
index a321db8c..233c51e6 100644
--- a/lib/scm/parsers/parser.rb
+++ b/lib/ohloh_scm/parsers/parser.rb
@@ -1,6 +1,6 @@
require 'stringio'
-module Scm::Parsers
+module OhlohScm::Parsers
class Parser
def self.parse(buffer='', opts={})
buffer = StringIO.new(buffer) if buffer.is_a? String
diff --git a/lib/scm/parsers/svn_parser.rb b/lib/ohloh_scm/parsers/svn_parser.rb
similarity index 88%
rename from lib/scm/parsers/svn_parser.rb
rename to lib/ohloh_scm/parsers/svn_parser.rb
index 97f0d4ac..7977649f 100644
--- a/lib/scm/parsers/svn_parser.rb
+++ b/lib/ohloh_scm/parsers/svn_parser.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
class SvnParser < Parser
def self.scm
'svn'
@@ -13,7 +13,7 @@ def self.internal_parse(buffer, opts)
next_state = state
if state == :data
if l =~ /^r(\d+) \| (.*) \| (\d+-\d+-\d+ .*) \(.*\) \| .*/
- e = Scm::Commit.new
+ e = OhlohScm::Commit.new
e.token = $1.to_i
e.committer_name = $2
e.committer_date = Time.parse($3).utc
@@ -24,9 +24,9 @@ def self.internal_parse(buffer, opts)
end
elsif state == :diffs
- if l =~ /^ (\w) ([^\(\)]+)( \(from .+:\d+\))?$/
+ if l =~ /^ (\w) ([^\(\)]+)( \(from (.+):(\d+)\))?$/
e.diffs ||= []
- e.diffs << Scm::Diff.new(:action => $1, :path => $2)
+ e.diffs << OhlohScm::Diff.new(:action => $1, :path => $2, :from_path => $4, :from_revision => $5.to_i)
else
next_state = :comment
end
diff --git a/lib/scm/parsers/svn_xml_parser.rb b/lib/ohloh_scm/parsers/svn_xml_parser.rb
similarity index 78%
rename from lib/scm/parsers/svn_xml_parser.rb
rename to lib/ohloh_scm/parsers/svn_xml_parser.rb
index 5d95ce39..bfac5854 100644
--- a/lib/scm/parsers/svn_xml_parser.rb
+++ b/lib/ohloh_scm/parsers/svn_xml_parser.rb
@@ -1,8 +1,7 @@
-require 'parsedate'
require 'rexml/document'
require 'rexml/streamlistener'
-module Scm::Parsers
+module OhlohScm::Parsers
class SubversionListener
include REXML::StreamListener
@@ -16,11 +15,13 @@ def initialize(callback)
def tag_start(name, attrs)
case name
when 'logentry'
- @commit = Scm::Commit.new
+ @commit = OhlohScm::Commit.new
@commit.diffs = []
@commit.token = attrs['revision'].to_i
when 'path'
- @diff = Scm::Diff.new(:action => attrs['action'])
+ @diff = OhlohScm::Diff.new(:action => attrs['action'],
+ :from_path => attrs['copyfrom-path'],
+ :from_revision => attrs['copyfrom-rev'].to_i)
end
end
@@ -31,7 +32,7 @@ def tag_end(name)
when 'author'
@commit.committer_name = @text
when 'date'
- @commit.committer_date = Time.utc(*ParseDate.parsedate(@text))
+ @commit.committer_date = Time.parse(@text).round.utc
when 'path'
@diff.path = @text
@commit.diffs << @diff
diff --git a/lib/scm/parsers/xml_writer.rb b/lib/ohloh_scm/parsers/xml_writer.rb
similarity index 98%
rename from lib/scm/parsers/xml_writer.rb
rename to lib/ohloh_scm/parsers/xml_writer.rb
index 4442e0e6..ae20ba46 100644
--- a/lib/scm/parsers/xml_writer.rb
+++ b/lib/ohloh_scm/parsers/xml_writer.rb
@@ -1,4 +1,4 @@
-module Scm::Parsers
+module OhlohScm::Parsers
class XmlWriter
# Note that we use << instead of write() or puts() in this writer because
# the << operator works on both File and String objects.
diff --git a/lib/scm/scratch_dir.rb b/lib/ohloh_scm/scratch_dir.rb
similarity index 98%
rename from lib/scm/scratch_dir.rb
rename to lib/ohloh_scm/scratch_dir.rb
index 4eaa06a5..b68c4fb4 100644
--- a/lib/scm/scratch_dir.rb
+++ b/lib/ohloh_scm/scratch_dir.rb
@@ -1,7 +1,7 @@
require 'fileutils'
# A utility class to manage the creation and automatic cleanup of temporary directories.
-module Scm
+module OhlohScm
class ScratchDir
attr_reader :path
diff --git a/lib/ohloh_scm/shellout.rb b/lib/ohloh_scm/shellout.rb
new file mode 100644
index 00000000..68fe3775
--- /dev/null
+++ b/lib/ohloh_scm/shellout.rb
@@ -0,0 +1,42 @@
+require 'rubygems'
+require 'stringio'
+require 'posix/spawn'
+
+class Shellout
+
+ def self.relay src, dst
+ while((buf = src.read(8192))); dst << buf; end
+ end
+
+ def self.execute(cmd)
+ posix_spawn = POSIX::Spawn::Child.new(cmd)
+
+ return posix_spawn.status, posix_spawn.out, posix_spawn.err
+ end
+
+ def run(cmd)
+ Shellout::execute(cmd)
+ end
+
+end
+
+if $0 == __FILE__
+ shell = Shellout.new
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
+ status, stdout, stderr = shell.run(date)
+ p [status.exitstatus, stdout, stderr]
+
+ sleep = %q( ruby -e" p(sleep(1)) " )
+ status, stdout, stderr = shell.run(sleep)
+ p [status.exitstatus, stdout, stderr]
+
+ cat = 'ruby -e" puts Array.new(65536){ 42 } "'
+ status, stdout, stderr = shell.run(cat)
+ p [status.exitstatus, stdout, stderr]
+
+ status, stdout, stderr = shell.run('osiudfoisynajtet32')
+ p [status.exitstatus, stdout, stderr]
+
+end
+
+
diff --git a/lib/ohloh_scm/version.rb b/lib/ohloh_scm/version.rb
new file mode 100644
index 00000000..c68bbaba
--- /dev/null
+++ b/lib/ohloh_scm/version.rb
@@ -0,0 +1,5 @@
+module OhlohScm
+ module Version
+ STRING = '2.3.1'
+ end
+end
diff --git a/lib/scm.rb b/lib/scm.rb
deleted file mode 100644
index 3089c090..00000000
--- a/lib/scm.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-module Scm
-end
-
-require 'rbconfig'
-
-$: << File.join(File.dirname(__FILE__),"..")
-
-require 'lib/scm/systemu'
-require 'lib/scm/scratch_dir'
-require 'lib/scm/commit'
-require 'lib/scm/diff'
-
-require 'lib/scm/adapters/abstract_adapter'
-require 'lib/scm/adapters/cvs_adapter'
-require 'lib/scm/adapters/svn_adapter'
-require 'lib/scm/adapters/git_adapter'
-require 'lib/scm/adapters/hg_adapter'
-require 'lib/scm/adapters/bzr_adapter'
-require 'lib/scm/adapters/factory'
-
-require 'lib/scm/parsers/parser'
-require 'lib/scm/parsers/branch_number'
-require 'lib/scm/parsers/cvs_parser'
-require 'lib/scm/parsers/svn_parser'
-require 'lib/scm/parsers/svn_xml_parser'
-require 'lib/scm/parsers/git_styled_parser'
-require 'lib/scm/parsers/hg_parser'
-require 'lib/scm/parsers/hg_styled_parser'
-require 'lib/scm/parsers/bzr_parser'
-
-require 'lib/scm/parsers/array_writer'
-require 'lib/scm/parsers/xml_writer'
-require 'lib/scm/parsers/human_writer'
diff --git a/lib/scm/adapters/abstract/misc.rb b/lib/scm/adapters/abstract/misc.rb
deleted file mode 100644
index f0552328..00000000
--- a/lib/scm/adapters/abstract/misc.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Scm::Adapters
- class AbstractAdapter
-
- def is_merge_commit?(commit)
- false
- end
-
- end
-end
diff --git a/lib/scm/adapters/abstract_adapter.rb b/lib/scm/adapters/abstract_adapter.rb
deleted file mode 100644
index aa906716..00000000
--- a/lib/scm/adapters/abstract_adapter.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Scm::Adapters
- class AbstractAdapter
- attr_accessor :url, :branch_name, :username, :password, :errors, :public_urls_only
-
- def initialize(params={})
- @url = params[:url]
- @branch_name = params[:branch_name]
- @username = params[:username]
- @password = params[:password]
- @public_urls_only = params[:public_urls_only]
- end
-
- # Handy for test overrides
- def metaclass
- class << self
- self
- end
- end
-
- end
-end
-
-require 'lib/scm/adapters/abstract/system'
-require 'lib/scm/adapters/abstract/validation'
-require 'lib/scm/adapters/abstract/sha1'
-require 'lib/scm/adapters/abstract/misc'
diff --git a/lib/scm/adapters/bzr/cat_file.rb b/lib/scm/adapters/bzr/cat_file.rb
deleted file mode 100644
index 8017919d..00000000
--- a/lib/scm/adapters/bzr/cat_file.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Scm::Adapters
- class BzrAdapter < AbstractAdapter
- def cat_file(commit, diff)
- cat(commit.token, diff.path)
- end
-
- def cat_file_parent(commit, diff)
- p = parents(commit)
- cat(p.first.token, diff.path) if p.first
- end
-
- def cat(revision, path)
- out, err = run_with_err("cd '#{url}' && bzr cat --name-from-revision -r #{to_rev_param(revision)} #{escape(path)}")
- return nil if err =~ / is not present in revision /
- raise RuntimeError.new(err) unless err.to_s == ''
- out
- end
-
- # Escape bash-significant characters in the filename
- # Example:
- # "Foo Bar & Baz" => "Foo\ Bar\ \&\ Baz"
- def escape(path)
- path.gsub(/[ '"&]/) { |c| '\\' + c }
- end
- end
-end
diff --git a/lib/scm/adapters/bzr/commits.rb b/lib/scm/adapters/bzr/commits.rb
deleted file mode 100644
index 4101e3bc..00000000
--- a/lib/scm/adapters/bzr/commits.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-module Scm::Adapters
- class BzrAdapter < AbstractAdapter
-
- # Return the number of commits in the repository following +since+.
- def commit_count(since=nil)
- commit_tokens(since).size
- end
-
- # Return the list of commit tokens following +since+.
- def commit_tokens(since=nil)
- tokens = run("#{rev_list_command(since)} | grep -E -e '^( *)revision-id: ' | cut -f2 -d':' | cut -c 2-").split("\n")
-
- # Bzr returns everything after *and including* since.
- # We want to exclude it.
- if tokens.any? && tokens.first == since
- tokens[1..-1]
- else
- tokens
- end
- 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=nil)
- log = run("#{rev_list_command(since)} | cat")
- a = Scm::Parsers::BzrParser.parse(log)
-
- if a.any? && a.first.token == since
- a[1..-1]
- else
- a
- end
- end
-
- # Returns a single commit, including its diffs
- def verbose_commit(token)
- log = run("cd '#{self.url}' && bzr log --long --show-id -v --limit 1 -c #{to_rev_param(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=nil)
- open_log_file(since) do |io|
- Scm::Parsers::BzrParser.parse(io) do |commit|
- yield remove_dupes(remove_directories(commit)) if block_given? && commit.token != since
- end
- end
- end
-
- # Ohloh tracks only files, not directories. This function removes directories
- # from the commit diffs.
- def remove_directories(commit)
- commit.diffs.delete_if { |d| d.path[-1..-1] == '/' }
- commit
- end
-
- # Bazaar may report that a file was both removed and added in the same commit.
- # We're going to consider this to be simply a modification of the file.
- # So we'll remove the 'D' action, and replace the 'A' with an 'M' action.
- def remove_dupes(commit)
- if commit.diffs
- dupes = commit.diffs.collect do |d|
- d if d.action == 'D' && commit.diffs.select { |x| x.action == 'A' and x.path == d.path }.any?
- end.compact
- dupes.each do |dupe|
- commit.diffs.delete_if { |del| del.action == 'D' and del.path == dupe.path }
- commit.diffs.each { |add| add.action = 'M' if add.path == dupe.path }
- end
- end
- commit
- end
-
-
- # Not used by Ohloh proper, but handy for debugging and testing
- def log(since=nil)
- 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=nil)
- 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(since)} -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 --long --show-id --forward -r #{to_rev_param(since)}.."
- end
- end
-end
diff --git a/lib/scm/adapters/bzr/misc.rb b/lib/scm/adapters/bzr/misc.rb
deleted file mode 100644
index 856fad62..00000000
--- a/lib/scm/adapters/bzr/misc.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-module Scm::Adapters
- class BzrAdapter < AbstractAdapter
- def exist?
- begin
- head_token.to_s.length > 0
- rescue
- logger.debug { $! }
- false
- end
- end
-
- def ls_tree(token)
- run("cd #{path} && bzr ls -V -r #{to_rev_param(token)}").split("\n")
- end
-
- # If you want to pass a revision-id as a bzr parameter, you
- # must prefix it with "revid:". This takes care of that.
- def to_rev_param(r=nil)
- case r
- when nil
- 1
- when Fixnum
- r.to_s
- when /^\d+$/
- r
- else
- "revid:#{r.to_s}"
- end
- end
-
- def is_merge_commit?(commit)
- parent_tokens(commit).size > 1
- end
- end
-end
diff --git a/lib/scm/adapters/bzr_adapter.rb b/lib/scm/adapters/bzr_adapter.rb
deleted file mode 100644
index def60a3f..00000000
--- a/lib/scm/adapters/bzr_adapter.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Scm::Adapters
- class BzrAdapter < AbstractAdapter
- def english_name
- "Bazaar"
- end
- end
-end
-
-require 'lib/scm/adapters/bzr/validation'
-require 'lib/scm/adapters/bzr/commits'
-require 'lib/scm/adapters/bzr/head'
-require 'lib/scm/adapters/bzr/cat_file'
-require 'lib/scm/adapters/bzr/misc'
-require 'lib/scm/adapters/bzr/pull'
-require 'lib/scm/adapters/bzr/push'
diff --git a/lib/scm/adapters/git/commits.rb b/lib/scm/adapters/git/commits.rb
deleted file mode 100644
index f7628696..00000000
--- a/lib/scm/adapters/git/commits.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-module Scm::Adapters
- class GitAdapter < AbstractAdapter
-
- # Returns the number of commits in the repository following the commit with SHA1 'since'.
- def commit_count(since=nil)
- run("#{rev_list_command(since)} | wc -l").to_i
- end
-
- # Returns the SHA1 hash for every commit in the repository following the commit with SHA1 'since'.
- def commit_tokens(since=nil, up_to=branch_name)
- run(rev_list_command(since, up_to)).split("\n")
- end
-
- # Yields each commit following the commit with SHA1 'since'.
- # Officially, this method isn't required to provide diffs with these commits, and the Subversion equivalent of this method does not,
- # so if you really require the diffs you should be using each_commit() instead.
- def commits(since=nil)
- result = []
- each_commit(since) { |c| result << c }
- result
- end
-
- # Yields each commit following the commit with SHA1 'since'.
- # These commits are populated with diffs.
- def each_commit(since=nil)
- Scm::Parsers::GitStyledParser.parse(log(since)) do |e|
- yield e
- end
- end
-
- # Returns a single commit, including its diffs
- def verbose_commit(token)
- Scm::Parsers::GitStyledParser.parse(run("cd '#{url}' && #{Scm::Parsers::GitStyledParser.whatchanged} #{token}")).first
- end
-
- # Retrieves the git log in the format expected by GitStyledParser.
- # We get the log forward chronological order (oldest first)
- def log(since=nil)
- if has_branch?
- if since && since==self.head_token
- '' # Nothing new.
- else
- run "#{rev_list_command(since)} | xargs -n 1 #{Scm::Parsers::GitStyledParser.whatchanged}"
- end
- else
- ''
- end
- end
-
- def rev_list_command(since=nil, up_to=branch_name)
- range = since ? "#{since}..#{up_to}" : up_to
- "cd '#{url}' && git rev-list --topo-order --reverse #{range}"
- end
- end
-end
diff --git a/lib/scm/adapters/git/validation.rb b/lib/scm/adapters/git/validation.rb
deleted file mode 100644
index ef5cc60b..00000000
--- a/lib/scm/adapters/git/validation.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module Scm::Adapters
- class GitAdapter < AbstractAdapter
- def self.url_regex
- /^(http|https|rsync|git|ssh):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/[A-Za-z0-9_\-\.\/\~\+]*$/
- end
-
- def self.public_url_regex
- /^(http|https|git):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/[A-Za-z0-9_\-\.\/\~\+]*$/
- end
-
- def normalize
- super
- @branch_name = 'master' if @branch_name.to_s == ''
- self
- end
-
- def validate_server_connection
- return unless valid?
- @errors << [:failed, "The server did not respond to the 'git-ls-remote' command. Is the URL correct?"] unless self.exists?
- end
- end
-end
diff --git a/lib/scm/adapters/git_adapter.rb b/lib/scm/adapters/git_adapter.rb
deleted file mode 100644
index f942a576..00000000
--- a/lib/scm/adapters/git_adapter.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Scm::Adapters
- class GitAdapter < AbstractAdapter
- def english_name
- "Git"
- end
- end
-end
-
-require 'lib/scm/adapters/git/validation'
-require 'lib/scm/adapters/git/cat_file'
-require 'lib/scm/adapters/git/commits'
-require 'lib/scm/adapters/git/commit_all'
-require 'lib/scm/adapters/git/token'
-require 'lib/scm/adapters/git/push'
-require 'lib/scm/adapters/git/pull'
-require 'lib/scm/adapters/git/head'
-require 'lib/scm/adapters/git/misc'
-require 'lib/scm/adapters/git/patch'
diff --git a/lib/scm/adapters/hg/commits.rb b/lib/scm/adapters/hg/commits.rb
deleted file mode 100644
index e3284b2a..00000000
--- a/lib/scm/adapters/hg/commits.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-module Scm::Adapters
- class HgAdapter < 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, up_to='tip')
- # We reverse the final result in Ruby, rather than passing the --reverse flag to hg.
- # That's because the -f (follow) flag doesn't behave the same in both directions.
- # Basically, we're trying very hard to make this act just like Git. The hg_rev_list_test checks this.
- tokens = run("cd '#{self.url}' && hg log -f -r #{up_to || 'tip'}:#{since || 0} --template='{node}\\n'").split("\n").reverse
-
- # Hg returns everything after *and including* since.
- # We want to exclude it.
- if tokens.any? && tokens.first == since
- tokens[1..-1]
- else
- tokens
- end
- 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("cd '#{self.url}' && hg log -f -v -r tip:#{since || 0} --style #{Scm::Parsers::HgStyledParser.style_path}")
- a = Scm::Parsers::HgStyledParser.parse(log).reverse
-
- if a.any? && a.first.token == since
- a[1..-1]
- else
- a
- end
- end
-
- # Returns a single commit, including its diffs
- def verbose_commit(token)
- log = run("cd '#{self.url}' && hg log -v -r #{token} --style #{Scm::Parsers::HgStyledParser.verbose_style_path}")
- Scm::Parsers::HgStyledParser.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::HgStyledParser.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 "cd '#{url}' && hg log -f -v -r tip:#{since}"
- end
-
- # Returns a file handle to the log.
- # In our standard, the log should include everything AFTER +since+. However, hg 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 "cd '#{url}' && hg log --verbose -r #{since || 0}:tip --style #{Scm::Parsers::HgStyledParser.verbose_style_path} > #{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
-
- end
-end
diff --git a/lib/scm/adapters/hg/misc.rb b/lib/scm/adapters/hg/misc.rb
deleted file mode 100644
index 0542df7a..00000000
--- a/lib/scm/adapters/hg/misc.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Scm::Adapters
- class HgAdapter < AbstractAdapter
- def exist?
- begin
- !!(head_token)
- rescue
- logger.debug { $! }
- false
- end
- end
-
- def ls_tree(token)
- run("cd #{path} && hg manifest -r #{token}").split("\n")
- end
- end
-end
diff --git a/lib/scm/adapters/hg_adapter.rb b/lib/scm/adapters/hg_adapter.rb
deleted file mode 100644
index 2853305b..00000000
--- a/lib/scm/adapters/hg_adapter.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Scm::Adapters
- class HgAdapter < AbstractAdapter
- def english_name
- "Mercurial"
- end
- end
-end
-
-require 'lib/scm/adapters/hg/validation'
-require 'lib/scm/adapters/hg/cat_file'
-require 'lib/scm/adapters/hg/commits'
-require 'lib/scm/adapters/hg/misc'
-require 'lib/scm/adapters/hg/pull'
-require 'lib/scm/adapters/hg/push'
-require 'lib/scm/adapters/hg/head'
-require 'lib/scm/adapters/hg/patch'
diff --git a/lib/scm/adapters/svn/cat_file.rb b/lib/scm/adapters/svn/cat_file.rb
deleted file mode 100644
index 68c3b2d0..00000000
--- a/lib/scm/adapters/svn/cat_file.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Scm::Adapters
- class SvnAdapter < AbstractAdapter
- def cat_file(commit, diff)
- cat(diff.path, commit.token)
- end
-
- def cat_file_parent(commit, diff)
- cat(diff.path, commit.token.to_i-1)
- end
-
- def cat(path=nil, revision='HEAD')
- begin
- run "svn cat -r #{revision} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name.to_s, path.to_s))}@#{revision}'"
- rescue
- raise unless $!.message =~ /svn: (File not found|.* is not a directory in filesystem)/
- end
- end
- end
-end
diff --git a/lib/scm/adapters/svn/misc.rb b/lib/scm/adapters/svn/misc.rb
deleted file mode 100644
index 307cfaea..00000000
--- a/lib/scm/adapters/svn/misc.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-require 'open-uri'
-
-module Scm::Adapters
- class SvnAdapter < AbstractAdapter
-
- # Converts an URL of form file://local/path to simply /local/path.
- def path
- case url
- when /^file:\/\/(.*)/
- $1
- when /^svn\+ssh:\/\/([^\/]+)(\/.+)/
- $2
- end
- end
-
- def hostname
- $1 if url =~ /^svn\+ssh:\/\/([^\/]+)(\/.+)/
- end
-
- # Does some simple searching through the server's directory tree for a
- # good canditate for the trunk. Basically, we are looking for a trunk
- # in order to avoid the heavy lifting of processing all the branches and tags.
- #
- # There are two simple rules to the search:
- # (1) If the current directory contains a subdirectory named 'trunk', go there.
- # (2) If the current directory is empty except for a single subdirectory, go there.
- # Repeat until neither rule is satisfied.
- #
- # The url of this object will be updated with the selected location.
- # The url will be unmodified if there is a problem connecting to the server.
- def restrict_url_to_trunk
- list = ls
- return self.url unless list
-
- if list.include? 'trunk/'
- self.url = self.url + '/trunk'
- return restrict_url_to_trunk
- elsif list.size == 1 and list.first[-1..-1] == '/'
- self.url = self.url + '/' + list.first[0..-2]
- return restrict_url_to_trunk
- end
- self.url
- end
-
- # It appears that the default URI encoder does not encode some characters.
- # This fixes it for us.
- def self.uri_encode(uri)
- URI.encode(uri,/#{URI::UNSAFE}|[\[\]';\? ]/) # Add [ ] ' ; ? and space
- end
-
- def exist?
- begin
- !!(head_token)
- rescue
- logger.debug { $! }
- false
- end
- end
-
- def info(path=nil, revision='HEAD')
- if path || (revision != 'HEAD')
- run "svn info -r #{revision} #{opt_auth} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name.to_s, path.to_s))}@#{revision}'"
- else
- # Cache the default info query for performance
- @info ||= run "svn info -r #{revision} #{opt_auth} '#{SvnAdapter.uri_encode(self.url)}@#{revision}'"
- end
- end
-
- def root
- $1 if self.info =~ /^Repository Root: (.+)$/
- end
-
- def uuid
- $1 if self.info =~ /^Repository UUID: (.+)$/
- end
-
- # Returns an array of file and directory names.
- # Directory names will end with a trailing '/' character.
- # Directories named 'CVSROOT' are always ignored and never returned.
- # An empty array means that the call succeeded, but the remote directory is empty.
- # A nil result means that the call failed and the remote server could not be queried.
- def ls(path=nil, revision='HEAD')
- begin
- stdout = run "svn ls -r #{revision} #{opt_auth} '#{SvnAdapter.uri_encode(File.join(url, path.to_s))}@#{revision}'"
- rescue
- return nil
- end
-
- files = []
- stdout.each_line do |s|
- s.chomp!
- files << s if s.length > 0 and s != 'CVSROOT/'
- end
- files.sort
- end
-
- def node_kind(path=nil, revision='HEAD')
- $1 if self.info(path, revision) =~ /^Node Kind: (.+)$/
- end
-
- def is_directory?(path=nil, revision='HEAD')
- begin
- return node_kind(path, revision) == 'directory'
- rescue
- if $!.message =~ /svn: .* is not a directory in filesystem/
- return false
- else
- raise
- end
- end
- end
-
- def checkout(rev, dest_dir)
- FileUtils.mkdir_p(File.dirname(dest_dir)) unless FileTest.exist?(File.dirname(dest_dir))
- run "svn checkout -r #{rev.token} '#{SvnAdapter.uri_encode(self.url)}@#{rev.token}' '#{dest_dir}' --ignore-externals #{opt_auth}"
- end
-
- def export(dest_dir, commit_id = 'HEAD')
- FileUtils.mkdir_p(File.dirname(dest_dir)) unless FileTest.exist?(File.dirname(dest_dir))
- run "svn export --force -r #{commit_id} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}' '#{dest_dir}'"
- end
-
- def ls_tree(token)
- run("svn ls -R -r #{token} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s))}@#{token}'").split("\n")
- end
-
- def opt_auth
- opt_password = ""
- opt_password = "--username='#{self.username}' --password='#{self.password}'" if self.username && self.username != ''
- " #{opt_password} --no-auth-cache "
- end
- end
-end
diff --git a/lib/scm/adapters/svn_adapter.rb b/lib/scm/adapters/svn_adapter.rb
deleted file mode 100644
index e8f28d5d..00000000
--- a/lib/scm/adapters/svn_adapter.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Scm::Adapters
- class SvnAdapter < AbstractAdapter
- def english_name
- "Subversion"
- end
- end
-end
-
-require 'lib/scm/adapters/svn/validation'
-require 'lib/scm/adapters/svn/cat_file'
-require 'lib/scm/adapters/svn/commits'
-require 'lib/scm/adapters/svn/push'
-require 'lib/scm/adapters/svn/pull'
-require 'lib/scm/adapters/svn/head'
-require 'lib/scm/adapters/svn/misc'
-require 'lib/scm/adapters/svn/patch'
-
diff --git a/lib/scm/parsers/bzr_parser.rb b/lib/scm/parsers/bzr_parser.rb
deleted file mode 100644
index 056642aa..00000000
--- a/lib/scm/parsers/bzr_parser.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-module Scm::Parsers
- # This parser can process the default bzr logs, with or without the --verbose flag.
- class BzrParser < Parser
- def self.scm
- 'bzr'
- end
-
- def self.internal_parse(buffer, opts)
- e = nil
- state = :data
- action = ''
- indent = '' # Track the level of indentation as we descend into branches
- show_id = false # true if this log includes revision and file ids
-
- buffer.each_line do |l|
- next_state = state
-
- case l
- when /^( *)-+$/
- # a new commit begins
- indent = $1
- yield e if e && block_given?
- e = Scm::Commit.new
- e.diffs = []
- next_state = :data
- when /^#{indent}revno:\s+(\d+)$/
- e.token = $1
- next_state = :data
- when /^#{indent}revision-id:\s+(\S+)$/
- e.token = $1
- show_id = true
- next_state = :data
- when /^#{indent}committer:\s+(.+?)(\s+<(.+)>)?$/
- e.committer_name = $1
- e.committer_email = $3
- next_state = :data
- when /^#{indent}timestamp:\s+(.+)/
- e.committer_date = Time.parse($1)
- next_state = :data
- when /^#{indent}added:$/
- next_state = :collect_files
- action = 'A'
- when /^#{indent}modified:$/
- next_state = :collect_files
- action = 'M'
- when /^#{indent}removed:$/
- next_state = :collect_files
- action = 'D'
- when /^#{indent}message:$/
- next_state = :collect_message
- e.message ||= ''
- when /^#{indent} (.*)$/
- case state
- when :collect_files
- path = $1
- # strip the id from the filename if it is present
- path = $1 if show_id && path =~ /^(.+?)\s+(\S+)$/
- e.diffs << Scm::Diff.new(:action => action, :path => path)
- when :collect_message
- e.message << $1
- e.message << "\n"
- end
- end
-
- state = next_state
- end
- yield e if e && block_given?
- end
-
- end
-end
diff --git a/lib/scm/systemu.rb b/lib/scm/systemu.rb
deleted file mode 100644
index 27c0323f..00000000
--- a/lib/scm/systemu.rb
+++ /dev/null
@@ -1,299 +0,0 @@
-# vim: ts=2:sw=2:sts=2:et:fdm=marker
-require 'tmpdir'
-require 'socket'
-require 'fileutils'
-require 'rbconfig'
-require 'thread'
-require 'yaml'
-
-class Object
- def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end
-end
-
-class SystemUniversal
-#
-# constants
-#
- SystemUniversal::VERSION = '1.2.0' unless defined? SystemUniversal::VERSION
- def version() SystemUniversal::VERSION end
-#
-# class methods
-#
-
- @host = Socket.gethostname
- @ppid = Process.ppid
- @pid = Process.pid
- @turd = ENV['SYSTEMU_TURD']
-
- c = ::Config::CONFIG
- ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
- @ruby = if system('%s -e 42' % ruby)
- ruby
- else
- system('%s -e 42' % 'ruby') ? 'ruby' : warn('no ruby in PATH/CONFIG')
- end
-
- class << self
- %w( host ppid pid ruby turd ).each{|a| attr_accessor a}
- end
-
-#
-# instance methods
-#
-
- def initialize argv, opts = {}, &block
- getopt = getopts opts
-
- @argv = argv
- @block = block
-
- @stdin = getopt[ ['stdin', 'in', '0', 0] ]
- @stdout = getopt[ ['stdout', 'out', '1', 1] ]
- @stderr = getopt[ ['stderr', 'err', '2', 2] ]
- @env = getopt[ 'env' ]
- @cwd = getopt[ 'cwd' ]
-
- @host = getopt[ 'host', self.class.host ]
- @ppid = getopt[ 'ppid', self.class.ppid ]
- @pid = getopt[ 'pid', self.class.pid ]
- @ruby = getopt[ 'ruby', self.class.ruby ]
- end
-
- def systemu
- tmpdir do |tmp|
- c = child_setup tmp
- status = nil
-
- begin
- thread = nil
-
- quietly{
- IO.popen "#{ @ruby } #{ c['program'] }", 'r+' do |pipe|
- line = pipe.gets
- case line
- when %r/^pid: \d+$/
- cid = Integer line[%r/\d+/]
- else
- begin
- buf = pipe.read
- buf = "#{ line }#{ buf }"
- e = Marshal.load buf
- raise unless Exception === e
- raise e
- rescue
- raise "wtf?\n#{ buf }\n"
- end
- end
- thread = new_thread cid, @block if @block
- pipe.read rescue nil
- end
- }
- status = $?
- ensure
- if thread
- begin
- class << status
- attr 'thread'
- end
- status.instance_eval{ @thread = thread }
- rescue
- 42
- end
- end
- end
-
- if @stdout or @stderr
- open(c['stdout']){|f| relay f => @stdout} if @stdout
- open(c['stderr']){|f| relay f => @stderr} if @stderr
- status
- else
- [status, IO.read(c['stdout']), IO.read(c['stderr'])]
- end
- end
- end
-
- def new_thread cid, block
- q = Queue.new
- Thread.new(cid) do |cid|
- current = Thread.current
- current.abort_on_exception = true
- q.push current
- block.call cid
- end
- q.pop
- end
-
- def child_setup tmp
- stdin = File.expand_path(File.join(tmp, 'stdin'))
- stdout = File.expand_path(File.join(tmp, 'stdout'))
- stderr = File.expand_path(File.join(tmp, 'stderr'))
- program = File.expand_path(File.join(tmp, 'program'))
- config = File.expand_path(File.join(tmp, 'config'))
-
- if @stdin
- open(stdin, 'w'){|f| relay @stdin => f}
- else
- FileUtils.touch stdin
- end
- FileUtils.touch stdout
- FileUtils.touch stderr
-
- c = {}
- c['argv'] = @argv
- c['env'] = @env
- c['cwd'] = @cwd
- c['stdin'] = stdin
- c['stdout'] = stdout
- c['stderr'] = stderr
- c['program'] = program
- open(config, 'w'){|f| YAML.dump c, f}
-
- open(program, 'w'){|f| f.write child_program(config)}
-
- c
- end
-
- def quietly
- v = $VERBOSE
- $VERBOSE = nil
- yield
- ensure
- $VERBOSE = v
- end
-
- def child_program config
- <<-program
- PIPE = STDOUT.dup
- begin
- require 'yaml'
-
- config = YAML.load(IO.read('#{ config }'))
-
- argv = config['argv']
- env = config['env']
- cwd = config['cwd']
- stdin = config['stdin']
- stdout = config['stdout']
- stderr = config['stderr']
-
- Dir.chdir cwd if cwd
- env.each{|k,v| ENV[k.to_s] = v.to_s} if env
-
- STDIN.reopen stdin
- STDOUT.reopen stdout
- STDERR.reopen stderr
-
- PIPE.puts "pid: \#{ Process.pid }"
- PIPE.flush ### the process is ready yo!
- PIPE.close
-
- exec *argv
- rescue Exception => e
- PIPE.write Marshal.dump(e) rescue nil
- exit 42
- end
- program
- end
-
- def relay srcdst
- src, dst, ignored = srcdst.to_a.first
- if src.respond_to? 'read'
- while((buf = src.read(8192))); dst << buf; end
- else
- src.each{|buf| dst << buf}
- end
- end
-
- def tmpdir d = Dir.tmpdir, max = 42, &b
- i = -1 and loop{
- i += 1
-
- tmp = File.join d, "systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }"
-
- begin
- Dir.mkdir tmp
- rescue Errno::EEXIST
- raise if i >= max
- next
- end
-
- break(
- if b
- begin
- b.call tmp
- ensure
- FileUtils.rm_rf tmp unless SystemU.turd
- end
- else
- tmp
- end
- )
- }
- end
-
- def getopts opts = {}
- lambda do |*args|
- keys, default, ignored = args
- catch('opt') do
- [keys].flatten.each do |key|
- [key, key.to_s, key.to_s.intern].each do |key|
- throw 'opt', opts[key] if opts.has_key?(key)
- end
- end
- default
- end
- end
- end
-end
-
-SystemU = SystemUniversal unless defined? SystemU
-
-
-
-
-
-
-
-
-
-
-
-
-
-if $0 == __FILE__
-#
-# date
-#
- date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
-
- status, stdout, stderr = systemu date
- p [status, stdout, stderr]
-
- status = systemu date, 1=>(stdout = '')
- p [status, stdout]
-
- status = systemu date, 2=>(stderr = '')
- p [status, stderr]
-#
-# sleep
-#
- sleep = %q( ruby -e" p(sleep(1)) " )
- status, stdout, stderr = systemu sleep
- p [status, stdout, stderr]
-
- sleep = %q( ruby -e" p(sleep(42)) " )
- status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid}
- p [status, stdout, stderr]
-#
-# env
-#
- env = %q( ruby -e" p ENV['A'] " )
- status, stdout, stderr = systemu env, :env => {'A' => 42}
- p [status, stdout, stderr]
-#
-# cwd
-#
- env = %q( ruby -e" p Dir.pwd " )
- status, stdout, stderr = systemu env, :cwd => Dir.tmpdir
- p [status, stdout, stderr]
-end
diff --git a/ohloh_scm.gemspec b/ohloh_scm.gemspec
new file mode 100644
index 00000000..f8caa14e
--- /dev/null
+++ b/ohloh_scm.gemspec
@@ -0,0 +1,22 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require 'ohloh_scm/version'
+
+Gem::Specification.new do |gem|
+ gem.name = 'ohloh_scm'
+ gem.version = OhlohScm::Version::STRING
+ gem.authors = ["BlackDuck Software"]
+ gem.email = ["info@openhub.net"]
+ gem.summary = %[Source Control Management]
+ gem.description = %[The Ohloh source control management library for interacting with Git, SVN, CVS, Hg and Bzr repositories.]
+ gem.homepage = %[https://github.com/blackducksw/ohloh_scm/]
+ gem.license = %[GPL v2.0]
+
+ gem.files = `git ls-files -z`.split("\x0")
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
+ gem.require_paths = %w(lib)
+
+ gem.add_runtime_dependency 'posix-spawn', '~> 0.3'
+ gem.add_runtime_dependency 'nokogiri', '~> 1.6', '>= 1.6.8'
+end
diff --git a/test/bin/svn b/test/bin/svn
new file mode 100755
index 00000000..e5f2e9f6
--- /dev/null
+++ b/test/bin/svn
@@ -0,0 +1,7 @@
+#! /usr/bin/env bash
+# This is for use with tests dealing with invalid encodings in svn log.
+# This script returns a sample svn xml log with some non utf-8 characters.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+cat $DIR/../data/svn_with_invalid_encoding.log
diff --git a/test/data/branch_merge.bzr_xml_log b/test/data/branch_merge.bzr_xml_log
new file mode 100644
index 00000000..c42c223d
--- /dev/null
+++ b/test/data/branch_merge.bzr_xml_log
@@ -0,0 +1,87 @@
+
+
+
+ 7
+ amujumdar@blackducksoftware.com-20110722184732-seu94oakpvy66nuu
+
+ amujumdar@blackducksoftware.com-20110722184717-u0ykbdm4pquaj0ai
+
+ Abhay <amujumdar@blackducksoftware.com>
+ myproject
+ Fri 2011-07-22 14:47:32 -0400
+
+
+
+ subdir/test4.txt
+
+
+
+
+ 8
+ amujumdar@blackducksoftware.com-20110722184839-og2qcc9g9re09iuu
+
+ amujumdar@blackducksoftware.com-20110722184732-seu94oakpvy66nuu
+ amujumdar@blackducksoftware.com-20110722184630-kn5n3oo0c9hns3i8
+
+ Abhay <amujumdar@blackducksoftware.com>
+ myproject
+ Fri 2011-07-22 14:48:39 -0400
+
+
+
+ test1.txt
+ test2.txt
+
+
+
+
+ 5.1.1
+ amujumdar@blackducksoftware.com-20110722184557-splhn3urfvq9mqfl
+
+ amujumdar@blackducksoftware.com-20110722180659-be4tmnzss40h05o6
+
+ Abhay <amujumdar@blackducksoftware.com>
+ feature_a
+ Fri 2011-07-22 14:45:57 -0400
+
+
+
+ test1.txt
+
+
+
+
+ 5.1.2
+ amujumdar@blackducksoftware.com-20110722184630-kn5n3oo0c9hns3i8
+
+ amujumdar@blackducksoftware.com-20110722184557-splhn3urfvq9mqfl
+
+ Abhay <amujumdar@blackducksoftware.com>
+ feature_a
+ Fri 2011-07-22 14:46:30 -0400
+
+
+
+ test2.txt
+
+
+
+
+
+
+ 9
+ amujumdar@blackducksoftware.com-20110722185038-e0i4d1mdxwpipxc4
+
+ amujumdar@blackducksoftware.com-20110722184839-og2qcc9g9re09iuu
+
+ Abhay <amujumdar@blackducksoftware.com>
+ myproject
+ Fri 2011-07-22 14:50:38 -0400
+
+
+
+ test1.txt
+
+
+
+
diff --git a/test/data/invalid-utf-word b/test/data/invalid-utf-word
new file mode 100644
index 00000000..22d22108
--- /dev/null
+++ b/test/data/invalid-utf-word
@@ -0,0 +1 @@
+
diff --git a/test/data/sample-content b/test/data/sample-content
new file mode 100644
index 00000000..b28fd339
--- /dev/null
+++ b/test/data/sample-content
@@ -0,0 +1,32 @@
+RCS file: /cvsroot/cncms/phparticle212/admin/template.php,v
+head: 1.4
+branch:
+locks: strict
+access list:
+symbolic names:
+ pa212: 1.1.1.1
+ niuboy: 1.1.1
+keyword substitution: kv
+total revisions: 5; selected revisions: 4
+
+description:
+----------------------------
+revision 1.4
+date: 2006/12/08 12:08:10; author: semirock; state: Exp; lines: +514 -478
+rss.phpļĬʾ20ƪ
+
+----------------------------
+revision 1.3
+date: 2006/11/22 08:35:59; author: niuboy; state: Exp; lines: +6 -2
+no message
+----------------------------
+revision 1.2
+date: 2006/11/19 19:51:28; author: niuboy; state: Exp; lines: +4 -4
+no message
+
+----------------------------
+revision 1.1
+date: 2006/11/11 07:21:46; author: niuboy; state: Exp;
+branches: 1.1.1;
+Initial revision
+=============================================================================
diff --git a/test/data/simple.bzr_xml_log b/test/data/simple.bzr_xml_log
new file mode 100644
index 00000000..6e8dd78b
--- /dev/null
+++ b/test/data/simple.bzr_xml_log
@@ -0,0 +1,41 @@
+
+
+
+ 9
+ amujumdar@blackducksoftware.com-20110722185038-e0i4d1mdxwpipxc4
+
+ amujumdar@blackducksoftware.com-20110722184839-og2qcc9g9re09iuu
+
+ Abhay <amujumdar@blackducksoftware.com>
+ myproject
+ Fri 2011-07-22 14:50:38 -0400
+
+
+
+ test1.txt
+
+
+
+
+ 10
+ test@example.com-20110725174345-brbpkwumeh07aoh8
+
+ amujumdar@blackducksoftware.com-20110722185038-e0i4d1mdxwpipxc4
+
+ test <test@example.com>
+ myproject
+ Mon 2011-07-25 13:43:45 -0400
+
+
+
+ test2.txt
+
+
+ test_a.txt
+
+
+ subdir/test_b.txt
+
+
+
+
diff --git a/test/data/svn_with_invalid_encoding.log b/test/data/svn_with_invalid_encoding.log
new file mode 100644
index 00000000..7a2cf7d5
--- /dev/null
+++ b/test/data/svn_with_invalid_encoding.log
@@ -0,0 +1,33 @@
+
+
+
+cartman
+2000-08-29T18:45:43.000000Z
+Ample parking day or night, people spouting: "Howdy, neighbor!"
+
+
+
+primus
+2002-07-11T14:27:53.000000Z
+I'm goin' down to South Park, gonna leave my woes behind.
+
+Thanks to Kenny Mller
+
+
+
+stan
+2002-07-11T18:19:49.000000Z
+Friendly faces everywhere, humble folks without temptation
+
+
+
+primus
+2002-07-12T08:14:23.000000Z
+I'm goin' down to South Park, gonna have myself a time.
+
+
+
diff --git a/test/repositories/bzr/.bzr/branch/last-revision b/test/repositories/bzr/.bzr/branch/last-revision
index 9a8027d9..a13831a2 100644
--- a/test/repositories/bzr/.bzr/branch/last-revision
+++ b/test/repositories/bzr/.bzr/branch/last-revision
@@ -1 +1 @@
-6 obnox@samba.org-20090204004942-73rnw0izen42f154
+7 test@example.com-20111222183733-y91if5npo3pe8ifs
diff --git a/test/repositories/bzr/.bzr/branch/tags b/test/repositories/bzr/.bzr/branch/tags
index e69de29b..e845266f 100644
--- a/test/repositories/bzr/.bzr/branch/tags
+++ b/test/repositories/bzr/.bzr/branch/tags
@@ -0,0 +1 @@
+d6:v1.0.047:obnox@samba.org-20090204002540-gmana8tk5f9gboq96:v2.0.048:test@example.com-20111222183733-y91if5npo3pe8ifse
\ No newline at end of file
diff --git a/test/repositories/bzr/.bzr/checkout/dirstate b/test/repositories/bzr/.bzr/checkout/dirstate
index 7643466d..915a4297 100644
Binary files a/test/repositories/bzr/.bzr/checkout/dirstate and b/test/repositories/bzr/.bzr/checkout/dirstate differ
diff --git a/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.iix b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.iix
new file mode 100644
index 00000000..b7998a5a
Binary files /dev/null and b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.iix differ
diff --git a/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.rix b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.rix
new file mode 100644
index 00000000..61bda8a4
Binary files /dev/null and b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.rix differ
diff --git a/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.six b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.tix b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.tix
new file mode 100644
index 00000000..3719aa07
Binary files /dev/null and b/test/repositories/bzr/.bzr/repository/indices/62f9cada7c58bce361b9b852d180ff56.tix differ
diff --git a/test/repositories/bzr/.bzr/repository/pack-names b/test/repositories/bzr/.bzr/repository/pack-names
index 59915d04..b0f555a0 100644
Binary files a/test/repositories/bzr/.bzr/repository/pack-names and b/test/repositories/bzr/.bzr/repository/pack-names differ
diff --git a/test/repositories/bzr/.bzr/repository/packs/62f9cada7c58bce361b9b852d180ff56.pack b/test/repositories/bzr/.bzr/repository/packs/62f9cada7c58bce361b9b852d180ff56.pack
new file mode 100644
index 00000000..eaeb3534
Binary files /dev/null and b/test/repositories/bzr/.bzr/repository/packs/62f9cada7c58bce361b9b852d180ff56.pack differ
diff --git "a/test/repositories/bzr/Ce\314\201dric.txt" "b/test/repositories/bzr/Ce\314\201dric.txt"
new file mode 100644
index 00000000..9d6265cc
--- /dev/null
+++ "b/test/repositories/bzr/Ce\314\201dric.txt"
@@ -0,0 +1,2 @@
+first file
+second line
diff --git "a/test/repositories/bzr/C\303\251dric.txt" "b/test/repositories/bzr/C\303\251dric.txt"
new file mode 100644
index 00000000..9d6265cc
--- /dev/null
+++ "b/test/repositories/bzr/C\303\251dric.txt"
@@ -0,0 +1,2 @@
+first file
+second line
diff --git a/test/repositories/bzr_colon.tgz b/test/repositories/bzr_colon.tgz
new file mode 100644
index 00000000..c49ff509
Binary files /dev/null and b/test/repositories/bzr_colon.tgz differ
diff --git a/test/repositories/bzr_with_authors/.bzr/README b/test/repositories/bzr_with_authors/.bzr/README
new file mode 100644
index 00000000..4f8e767e
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/README
@@ -0,0 +1,3 @@
+This is a Bazaar control directory.
+Do not change any files in this directory.
+See http://bazaar-vcs.org/ for more information about Bazaar.
diff --git a/test/repositories/bzr_with_authors/.bzr/branch-format b/test/repositories/bzr_with_authors/.bzr/branch-format
new file mode 100644
index 00000000..9eb09b73
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/branch-format
@@ -0,0 +1 @@
+Bazaar-NG meta directory, format 1
diff --git a/test/repositories/cvs/CVSROOT/.#cvsrc b/test/repositories/bzr_with_authors/.bzr/branch/branch.conf
similarity index 100%
rename from test/repositories/cvs/CVSROOT/.#cvsrc
rename to test/repositories/bzr_with_authors/.bzr/branch/branch.conf
diff --git a/test/repositories/bzr_with_authors/.bzr/branch/format b/test/repositories/bzr_with_authors/.bzr/branch/format
new file mode 100644
index 00000000..dc392f44
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/branch/format
@@ -0,0 +1 @@
+Bazaar Branch Format 7 (needs bzr 1.6)
diff --git a/test/repositories/bzr_with_authors/.bzr/branch/last-revision b/test/repositories/bzr_with_authors/.bzr/branch/last-revision
new file mode 100644
index 00000000..de93b6d7
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/branch/last-revision
@@ -0,0 +1 @@
+3 test@example.com-20111011162601-ud1nidteswfdbhbu
diff --git a/test/repositories/cvs/CVSROOT/cvsrc b/test/repositories/bzr_with_authors/.bzr/branch/tags
similarity index 100%
rename from test/repositories/cvs/CVSROOT/cvsrc
rename to test/repositories/bzr_with_authors/.bzr/branch/tags
diff --git a/test/repositories/bzr_with_authors/.bzr/checkout/conflicts b/test/repositories/bzr_with_authors/.bzr/checkout/conflicts
new file mode 100644
index 00000000..0dc2d3a0
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/checkout/conflicts
@@ -0,0 +1 @@
+BZR conflict list format 1
diff --git a/test/repositories/bzr_with_authors/.bzr/checkout/dirstate b/test/repositories/bzr_with_authors/.bzr/checkout/dirstate
new file mode 100644
index 00000000..7c6ed0de
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/checkout/dirstate differ
diff --git a/test/repositories/bzr_with_authors/.bzr/checkout/format b/test/repositories/bzr_with_authors/.bzr/checkout/format
new file mode 100644
index 00000000..e0261c79
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/checkout/format
@@ -0,0 +1 @@
+Bazaar Working Tree Format 6 (bzr 1.14)
diff --git a/test/repositories/bzr_with_authors/.bzr/checkout/views b/test/repositories/bzr_with_authors/.bzr/checkout/views
new file mode 100644
index 00000000..e69de29b
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/format b/test/repositories/bzr_with_authors/.bzr/repository/format
new file mode 100644
index 00000000..b200528b
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/repository/format
@@ -0,0 +1 @@
+Bazaar repository format 2a (needs bzr 1.16 or later)
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.cix b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.cix
new file mode 100644
index 00000000..fc2252c8
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.cix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.iix b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.iix
new file mode 100644
index 00000000..c396b07e
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.iix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.rix b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.rix
new file mode 100644
index 00000000..4a9e6cea
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.rix
@@ -0,0 +1,6 @@
+B+Tree Graph Index 2
+node_ref_lists=1
+key_elements=1
+len=1
+row_lengths=1
+x]K @QǮ
&n@5|vmٹ:XOHGěsK8mVU;6@j[Kl"
eC'˕ۊ^۠$ruW8+
\ No newline at end of file
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.six b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.six
new file mode 100644
index 00000000..a2afde6b
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.six
@@ -0,0 +1,5 @@
+B+Tree Graph Index 2
+node_ref_lists=0
+key_elements=1
+len=0
+row_lengths=
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.tix b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.tix
new file mode 100644
index 00000000..066c97f8
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/1326ecee2f4f69991771137c5307689a.tix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.cix b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.cix
new file mode 100644
index 00000000..6b4cd91c
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.cix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.iix b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.iix
new file mode 100644
index 00000000..ddf0ca77
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.iix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.rix b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.rix
new file mode 100644
index 00000000..51c8a1e3
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.rix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.six b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.six
new file mode 100644
index 00000000..a2afde6b
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.six
@@ -0,0 +1,5 @@
+B+Tree Graph Index 2
+node_ref_lists=0
+key_elements=1
+len=0
+row_lengths=
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.tix b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.tix
new file mode 100644
index 00000000..7135df2c
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/repository/indices/adf730b6cf7c7959afcedb87e155654d.tix
@@ -0,0 +1,6 @@
+B+Tree Graph Index 2
+node_ref_lists=1
+key_elements=2
+len=2
+row_lengths=1
+xK @QǮ
`H &A>&*BQշ:N3oJ/8LRqOFӁiCK:bY:lkMUMfJq+xw9j־$rtoW*%K
\ No newline at end of file
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.cix b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.cix
new file mode 100644
index 00000000..a426a203
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.cix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.iix b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.iix
new file mode 100644
index 00000000..68a9ee76
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.iix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.rix b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.rix
new file mode 100644
index 00000000..215205dc
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.rix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.six b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.six
new file mode 100644
index 00000000..a2afde6b
--- /dev/null
+++ b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.six
@@ -0,0 +1,5 @@
+B+Tree Graph Index 2
+node_ref_lists=0
+key_elements=1
+len=0
+row_lengths=
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.tix b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.tix
new file mode 100644
index 00000000..3f075545
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/indices/d2613fcfb5e48e79073b96270782f95c.tix differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/pack-names b/test/repositories/bzr_with_authors/.bzr/repository/pack-names
new file mode 100644
index 00000000..16e3d82c
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/pack-names differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/packs/1326ecee2f4f69991771137c5307689a.pack b/test/repositories/bzr_with_authors/.bzr/repository/packs/1326ecee2f4f69991771137c5307689a.pack
new file mode 100644
index 00000000..28993a08
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/packs/1326ecee2f4f69991771137c5307689a.pack differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/packs/adf730b6cf7c7959afcedb87e155654d.pack b/test/repositories/bzr_with_authors/.bzr/repository/packs/adf730b6cf7c7959afcedb87e155654d.pack
new file mode 100644
index 00000000..8abb9178
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/packs/adf730b6cf7c7959afcedb87e155654d.pack differ
diff --git a/test/repositories/bzr_with_authors/.bzr/repository/packs/d2613fcfb5e48e79073b96270782f95c.pack b/test/repositories/bzr_with_authors/.bzr/repository/packs/d2613fcfb5e48e79073b96270782f95c.pack
new file mode 100644
index 00000000..f3348299
Binary files /dev/null and b/test/repositories/bzr_with_authors/.bzr/repository/packs/d2613fcfb5e48e79073b96270782f95c.pack differ
diff --git a/test/repositories/bzr_with_authors/test.txt b/test/repositories/bzr_with_authors/test.txt
new file mode 100644
index 00000000..ea1cc16a
--- /dev/null
+++ b/test/repositories/bzr_with_authors/test.txt
@@ -0,0 +1 @@
+test file updated by many authors
diff --git a/test/repositories/bzr_with_invalid_encoding.tgz b/test/repositories/bzr_with_invalid_encoding.tgz
new file mode 100644
index 00000000..cec80b9f
Binary files /dev/null and b/test/repositories/bzr_with_invalid_encoding.tgz differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/README b/test/repositories/bzr_with_nested_branches/.bzr/README
new file mode 100644
index 00000000..4f8e767e
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/README
@@ -0,0 +1,3 @@
+This is a Bazaar control directory.
+Do not change any files in this directory.
+See http://bazaar-vcs.org/ for more information about Bazaar.
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/branch-format b/test/repositories/bzr_with_nested_branches/.bzr/branch-format
new file mode 100644
index 00000000..9eb09b73
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/branch-format
@@ -0,0 +1 @@
+Bazaar-NG meta directory, format 1
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/branch/branch.conf b/test/repositories/bzr_with_nested_branches/.bzr/branch/branch.conf
new file mode 100644
index 00000000..7d11ab06
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/branch/branch.conf
@@ -0,0 +1 @@
+submit_branch = file:///home/amujumdar/app/ohloh_scm/test/repositories/bzr_branch_a/
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/branch/format b/test/repositories/bzr_with_nested_branches/.bzr/branch/format
new file mode 100644
index 00000000..1fc6b88e
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/branch/format
@@ -0,0 +1 @@
+Bazaar Branch Format 6 (bzr 0.15)
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/branch/last-revision b/test/repositories/bzr_with_nested_branches/.bzr/branch/last-revision
new file mode 100644
index 00000000..3e4bc166
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/branch/last-revision
@@ -0,0 +1 @@
+7 test@example.com-20110803170818-v44umypquqg8migo
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/branch/tags b/test/repositories/bzr_with_nested_branches/.bzr/branch/tags
new file mode 100644
index 00000000..e69de29b
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/checkout/conflicts b/test/repositories/bzr_with_nested_branches/.bzr/checkout/conflicts
new file mode 100644
index 00000000..0dc2d3a0
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/checkout/conflicts
@@ -0,0 +1 @@
+BZR conflict list format 1
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/checkout/dirstate b/test/repositories/bzr_with_nested_branches/.bzr/checkout/dirstate
new file mode 100644
index 00000000..6afcd64b
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/checkout/dirstate differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/checkout/format b/test/repositories/bzr_with_nested_branches/.bzr/checkout/format
new file mode 100644
index 00000000..9ffb5e44
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/checkout/format
@@ -0,0 +1 @@
+Bazaar Working Tree Format 4 (bzr 0.15)
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/checkout/merge-hashes b/test/repositories/bzr_with_nested_branches/.bzr/checkout/merge-hashes
new file mode 100644
index 00000000..16bc5227
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/checkout/merge-hashes
@@ -0,0 +1,6 @@
+BZR merge-modified list format 1
+file_id: file7.txt-20110803170452-nymk4xd500w4qn43-1
+hash: 35115b73ad026bdb3b95e4abc64cf1764ef65e78
+
+file_id: file6.txt-20110803170200-9ytx1mxfxh3nh6yp-1
+hash: f654c347dda8c7b60f790393bb0f3d02241c4197
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/format b/test/repositories/bzr_with_nested_branches/.bzr/repository/format
new file mode 100644
index 00000000..520349da
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/format
@@ -0,0 +1 @@
+Bazaar pack repository format 1 (needs bzr 0.92)
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.iix
new file mode 100644
index 00000000..94a5cc60
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.rix
new file mode 100644
index 00000000..dc5836ed
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.tix
new file mode 100644
index 00000000..bca0f049
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/0428489d1a03d05c496c2c429fe96e90.tix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.iix
new file mode 100644
index 00000000..b856232c
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.rix
new file mode 100644
index 00000000..f5335640
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.tix
new file mode 100644
index 00000000..db2744b3
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/30fcaac048e328a7727156986055f6e8.tix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.iix
new file mode 100644
index 00000000..594f8053
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.rix
new file mode 100644
index 00000000..3b56cde9
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.tix
new file mode 100644
index 00000000..80600395
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/673cc297ed321f667e1d8d4fff600829.tix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.iix
new file mode 100644
index 00000000..273571ca
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.rix
new file mode 100644
index 00000000..b0c9c3b0
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.tix
new file mode 100644
index 00000000..12a7b8fe
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/71af8bae249bcb824a4ff17c62029142.tix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.iix
new file mode 100644
index 00000000..cf7f8ffc
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.rix
new file mode 100644
index 00000000..92431c05
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.tix
new file mode 100644
index 00000000..78227aa0
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/7202eb77b81a80eca5296f317ed42149.tix
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=2
+key_elements=2
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.iix
new file mode 100644
index 00000000..7a0c57bf
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.rix
new file mode 100644
index 00000000..1b5b38eb
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.tix
new file mode 100644
index 00000000..beccd5bf
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/8a571ba35ee54cd133b71e967442fe17.tix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.iix
new file mode 100644
index 00000000..d6e2fe19
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.rix
new file mode 100644
index 00000000..1bd5029f
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.tix
new file mode 100644
index 00000000..3833ed28
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/a06b9c10004fc3e05affee8cf0a9febd.tix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.iix
new file mode 100644
index 00000000..4b1f2c2b
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.rix
new file mode 100644
index 00000000..f7591be4
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.tix
new file mode 100644
index 00000000..78227aa0
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/da6c79a024c70fd6831e323430a96cc8.tix
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=2
+key_elements=2
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.iix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.iix
new file mode 100644
index 00000000..393d9e14
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.iix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.rix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.rix
new file mode 100644
index 00000000..a4777273
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.rix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.six b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.six
new file mode 100644
index 00000000..1206f930
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.six
@@ -0,0 +1,5 @@
+Bazaar Graph Index 1
+node_ref_lists=0
+key_elements=1
+len=0
+
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.tix b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.tix
new file mode 100644
index 00000000..c4f928f4
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/indices/f35e1020b6a55c81b6d2fe4c7bcd4645.tix differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/pack-names b/test/repositories/bzr_with_nested_branches/.bzr/repository/pack-names
new file mode 100644
index 00000000..b2b94864
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/pack-names differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/0428489d1a03d05c496c2c429fe96e90.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/0428489d1a03d05c496c2c429fe96e90.pack
new file mode 100644
index 00000000..0aea1a43
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/0428489d1a03d05c496c2c429fe96e90.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/30fcaac048e328a7727156986055f6e8.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/30fcaac048e328a7727156986055f6e8.pack
new file mode 100644
index 00000000..9ba7cc32
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/30fcaac048e328a7727156986055f6e8.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/673cc297ed321f667e1d8d4fff600829.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/673cc297ed321f667e1d8d4fff600829.pack
new file mode 100644
index 00000000..143f207d
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/673cc297ed321f667e1d8d4fff600829.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/71af8bae249bcb824a4ff17c62029142.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/71af8bae249bcb824a4ff17c62029142.pack
new file mode 100644
index 00000000..f6942b13
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/71af8bae249bcb824a4ff17c62029142.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/7202eb77b81a80eca5296f317ed42149.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/7202eb77b81a80eca5296f317ed42149.pack
new file mode 100644
index 00000000..bbf60ffe
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/7202eb77b81a80eca5296f317ed42149.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/8a571ba35ee54cd133b71e967442fe17.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/8a571ba35ee54cd133b71e967442fe17.pack
new file mode 100644
index 00000000..fbeeff5f
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/8a571ba35ee54cd133b71e967442fe17.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/a06b9c10004fc3e05affee8cf0a9febd.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/a06b9c10004fc3e05affee8cf0a9febd.pack
new file mode 100644
index 00000000..ab504c81
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/a06b9c10004fc3e05affee8cf0a9febd.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/da6c79a024c70fd6831e323430a96cc8.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/da6c79a024c70fd6831e323430a96cc8.pack
new file mode 100644
index 00000000..6aab76e0
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/da6c79a024c70fd6831e323430a96cc8.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/f35e1020b6a55c81b6d2fe4c7bcd4645.pack b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/f35e1020b6a55c81b6d2fe4c7bcd4645.pack
new file mode 100644
index 00000000..fd1bb7af
Binary files /dev/null and b/test/repositories/bzr_with_nested_branches/.bzr/repository/packs/f35e1020b6a55c81b6d2fe4c7bcd4645.pack differ
diff --git a/test/repositories/bzr_with_nested_branches/.bzr/repository/upload/.gitignore b/test/repositories/bzr_with_nested_branches/.bzr/repository/upload/.gitignore
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/.bzr/repository/upload/.gitignore
@@ -0,0 +1 @@
+
diff --git a/test/repositories/bzr_with_nested_branches/file1.txt b/test/repositories/bzr_with_nested_branches/file1.txt
new file mode 100644
index 00000000..9d6265cc
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/file1.txt
@@ -0,0 +1,2 @@
+first file
+second line
diff --git a/test/repositories/bzr_with_nested_branches/file3.txt b/test/repositories/bzr_with_nested_branches/file3.txt
new file mode 100644
index 00000000..dc4b480e
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/file3.txt
@@ -0,0 +1 @@
+a third file
diff --git a/test/repositories/bzr_with_nested_branches/file4.txt b/test/repositories/bzr_with_nested_branches/file4.txt
new file mode 100644
index 00000000..7c3f1a85
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/file4.txt
@@ -0,0 +1 @@
+yet another file
diff --git a/test/repositories/bzr_with_nested_branches/file5.txt b/test/repositories/bzr_with_nested_branches/file5.txt
new file mode 100644
index 00000000..f71a6e04
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/file5.txt
@@ -0,0 +1,4 @@
+and yet another file
+
+
+...
diff --git a/test/repositories/bzr_with_nested_branches/file6.txt b/test/repositories/bzr_with_nested_branches/file6.txt
new file mode 100644
index 00000000..33a752d5
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/file6.txt
@@ -0,0 +1,2 @@
+File added on branch a
+and modified.
diff --git a/test/repositories/bzr_with_nested_branches/file7.txt b/test/repositories/bzr_with_nested_branches/file7.txt
new file mode 100644
index 00000000..40db9f70
--- /dev/null
+++ b/test/repositories/bzr_with_nested_branches/file7.txt
@@ -0,0 +1,2 @@
+Added on branch b.
+and modified.
diff --git a/test/repositories/cvs.tgz b/test/repositories/cvs.tgz
new file mode 100644
index 00000000..6603f7a8
Binary files /dev/null and b/test/repositories/cvs.tgz differ
diff --git a/test/repositories/cvs/CVSROOT/.#checkoutlist b/test/repositories/cvs/CVSROOT/.#checkoutlist
deleted file mode 100644
index b04b3501..00000000
--- a/test/repositories/cvs/CVSROOT/.#checkoutlist
+++ /dev/null
@@ -1,13 +0,0 @@
-# The "checkoutlist" file is used to support additional version controlled
-# administrative files in $CVSROOT/CVSROOT, such as template files.
-#
-# The first entry on a line is a filename which will be checked out from
-# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
-# The remainder of the line is an error message to use if the file cannot
-# be checked out.
-#
-# File format:
-#
-# []
-#
-# comment lines begin with '#'
diff --git a/test/repositories/cvs/CVSROOT/.#commit_email b/test/repositories/cvs/CVSROOT/.#commit_email
deleted file mode 100644
index 63863618..00000000
--- a/test/repositories/cvs/CVSROOT/.#commit_email
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "commit_email" file is used to control templates for emails sent
-# during commit and import.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/.#commitinfo b/test/repositories/cvs/CVSROOT/.#commitinfo
deleted file mode 100644
index 63ea65ad..00000000
--- a/test/repositories/cvs/CVSROOT/.#commitinfo
+++ /dev/null
@@ -1,15 +0,0 @@
-# The "commitinfo" file is used to control pre-commit checks.
-# The filter on the right is invoked with the repository name. A list
-# of files to check is passed to the standard input of the script. A non-zero
-# exit of the filter program will cause the commit to be aborted.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/.#config b/test/repositories/cvs/CVSROOT/.#config
deleted file mode 100644
index af2a28c4..00000000
--- a/test/repositories/cvs/CVSROOT/.#config
+++ /dev/null
@@ -1,26 +0,0 @@
-# Set this to 'no' if pserver shouldn't check system users/passwords
-#SystemAuth=yes
-
-# Set the Acl parsing type (none,compat,normal).
-#AclMode=compat
-
-# Alternate location of CVS LockServer. Set to 'none' to disable..
-#LockServer=localhost:2402
-
-# Set 'TopLevelAdmin' to 'yes' to create a CVS directory at the top
-# level of the new working directory when using the 'cvs checkout'
-# command.
-#TopLevelAdmin=no
-
-# Set 'LogHistory' to 'all' or 'TOFEWGCMAR' to log all transactions to the
-# history file, or a subset as needed (ie 'TMAR' logs all write operations)
-#LogHistory=TOFEWGCMAR
-
-# Set 'RereadLogAfterVerify' to control rereading of the log file after a verifymsg
-# 'always' or 'yes' to always reread the log regardless
-# 'never' or 'no' (default) to never reread the log
-#RereadLogAfterVerify=no
-
-# Set 'Watcher' to set a user who gets all notify events within the repository whether
-# or not the ifle is watched.
-#Watcher=watch_user
\ No newline at end of file
diff --git a/test/repositories/cvs/CVSROOT/.#cvswrappers b/test/repositories/cvs/CVSROOT/.#cvswrappers
deleted file mode 100644
index 11fa3b39..00000000
--- a/test/repositories/cvs/CVSROOT/.#cvswrappers
+++ /dev/null
@@ -1,18 +0,0 @@
-# This file affects handling of files based on their names.
-#
-# The -m option specifies whether CVS attempts to merge files.
-#
-# The -k option specifies keyword expansion (e.g. -kb for binary).
-#
-# The -t option overrides the default mime type.
-#
-# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
-#
-# wildcard [option value][option value]...
-#
-# where option is one of
-# -k expansion mode value: b, o, kkv, etc.
-#
-# and value is a single-quote delimited value.
-# For example:
-#*.gif -kb
diff --git a/test/repositories/cvs/CVSROOT/.#historyinfo b/test/repositories/cvs/CVSROOT/.#historyinfo
deleted file mode 100644
index 889ff1be..00000000
--- a/test/repositories/cvs/CVSROOT/.#historyinfo
+++ /dev/null
@@ -1,15 +0,0 @@
-# The "historyinfo" file is used to log the history file output.
-# The filter on the right is invoked with the repository name. Its
-# standard input contains the history line that has just been written
-# to the history file (if it exists)
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/.#keywords b/test/repositories/cvs/CVSROOT/.#keywords
deleted file mode 100644
index a94adf4d..00000000
--- a/test/repositories/cvs/CVSROOT/.#keywords
+++ /dev/null
@@ -1,30 +0,0 @@
-# The "keywords" file is used to modify the standard set of RCS keywords
-# or define entirely new ones.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. Subsequent lines contain keyword definitions, indented by a space
-# to separate them from module definitions
-.#
-# If the repository name does not match any of the definitions in this
-# file, the "ALL" section is used, if it is specified.
-#
-# Any keyword with an empty definition is ignored. This can be used to selectively
-# disable individual RCS keywords.
-#
-# The default defintions are:
-#
-#ALL
-# Author %a
-# Date %d
-# Header %r/%p/%f %v %d %a %s %l
-# CVSHeader %p/%f %v %d %a %s %l
-# Id %f %v %d %a %s %l
-# Locker %l
-# Log %f
-# Name %N
-# RCSfile %f
-# Revision %v
-# Source %r/%p/%f
-# State %s
-# CommitId %C
diff --git a/test/repositories/cvs/CVSROOT/.#loginfo b/test/repositories/cvs/CVSROOT/.#loginfo
deleted file mode 100644
index f3c43297..00000000
--- a/test/repositories/cvs/CVSROOT/.#loginfo
+++ /dev/null
@@ -1,22 +0,0 @@
-# The "loginfo" file controls where "cvs commit" log information
-# is sent. The first entry on a line is a regular expression which must match
-# the directory that the change is being made to, relative to the
-# $CVSROOT. If a match is found, then the remainder of the line is a filter
-# program that should expect log information on its standard input.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name ALL appears as a regular expression it is always used
-# in addition to the first matching regex or DEFAULT.
-#
-# You may specify a format string as part of the
-# filter. The string is composed of a '%' followed
-# by a single format character, or followed by a set of format
-# characters surrounded by '{' and '}' as separators. The format
-# characters are:
-#
-# s = file name
-# V = old version number (pre-checkin)
-# v = new version number (post-checkin)
-#
diff --git a/test/repositories/cvs/CVSROOT/.#modules b/test/repositories/cvs/CVSROOT/.#modules
deleted file mode 100644
index cb9e9efc..00000000
--- a/test/repositories/cvs/CVSROOT/.#modules
+++ /dev/null
@@ -1,26 +0,0 @@
-# Three different line formats are valid:
-# key -a aliases...
-# key [options] directory
-# key [options] directory files...
-#
-# Where "options" are composed of:
-# -i prog Run "prog" on "cvs commit" from top-level of module.
-# -o prog Run "prog" on "cvs checkout" of module.
-# -e prog Run "prog" on "cvs export" of module.
-# -t prog Run "prog" on "cvs rtag" of module.
-# -u prog Run "prog" on "cvs update" of module.
-# -d dir Place module in directory "dir" instead of module name.
-# -l Top-level directory only -- do not recurse.
-#
-# NOTE: If you change any of the "Run" options above, you'll have to
-# release and re-checkout any working directories of these modules.
-#
-# And "directory" is a path to a directory relative to $CVSROOT.
-#
-# The "-a" option specifies an alias. An alias is interpreted as if
-# everything on the right of the "-a" had been typed on the command line.
-#
-# You can encode a module within a module by using the special '&'
-# character to interpose another module into the current module. This
-# can be useful for creating a module that consists of many directories
-# spread out over the entire source repository.
diff --git a/test/repositories/cvs/CVSROOT/.#modules2 b/test/repositories/cvs/CVSROOT/.#modules2
deleted file mode 100644
index 6cdc5bae..00000000
--- a/test/repositories/cvs/CVSROOT/.#modules2
+++ /dev/null
@@ -1,22 +0,0 @@
-# *** modules2 currently has 'experimental' status. Testing is encouraged but.
-# for greatest stability use the modules file. ***
-#
-# This file describes the layout of virtual directory structures
-# within the repository.
-#
-# The layout is similar to a Windows .ini file. For example:
-#
-# [foo]
-# dir1/dir2/dir3 = realdir1/realdir2
-# dir1/dir3 = !realdir1/realdir3 (^*js$|^*cpp$)
-#
-# [bar]
-# / = realdir4
-# dir_to_delete =
-# foo = foo
-#
-# The special character '!' stops recursion to directories below the one specified (-l option).
-# The special character '+' stops parsing of that line, so that you can avoid infinte loops.
-#
-# Items in (...) are an extended regular expression applied to the filenames. All files which do not.
-# match are ignored.
diff --git a/test/repositories/cvs/CVSROOT/.#notify b/test/repositories/cvs/CVSROOT/.#notify
deleted file mode 100644
index cdb3320c..00000000
--- a/test/repositories/cvs/CVSROOT/.#notify
+++ /dev/null
@@ -1,22 +0,0 @@
-# The "notify" file controls where notifications from watches set by
-# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
-# a regular expression which is tested against the directory that the
-# change is being made to, relative to the $CVSROOT. If it matches,
-# then the remainder of the line is a filter program that should contain
-# one occurrence of %s for the user to notify, and information on its
-# standard input.
-#
-# "ALL" or "DEFAULT" can be used in place of the regular expression.
-#
-# You may specify a format string as part of the
-# filter. The format characters are:
-#
-# s = user being notified
-# b = Bug identifier
-# m = Message supplied on command line
-# d = Date of action
-# u = User performing the unedit
-# t = tag or branch being edited
-#
-# For example:
-#ALL mail %s -s "CVS notification for bug %b"
diff --git a/test/repositories/cvs/CVSROOT/.#notify_email b/test/repositories/cvs/CVSROOT/.#notify_email
deleted file mode 100644
index c673ab4c..00000000
--- a/test/repositories/cvs/CVSROOT/.#notify_email
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "notify_email" file is used to control templates for emails sent
-# when notifying users.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/.#postcommand b/test/repositories/cvs/CVSROOT/.#postcommand
deleted file mode 100644
index ab3ef145..00000000
--- a/test/repositories/cvs/CVSROOT/.#postcommand
+++ /dev/null
@@ -1,14 +0,0 @@
-# The "postcommand" file is run after a cvs command has finished.
-# The filter on the right is invoked with the repository name and
-# the name of the command that has been executed.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/.#postmodule b/test/repositories/cvs/CVSROOT/.#postmodule
deleted file mode 100644
index b6d258be..00000000
--- a/test/repositories/cvs/CVSROOT/.#postmodule
+++ /dev/null
@@ -1,14 +0,0 @@
-# The "postmodule" file is run after a cvs module is processed.
-# The filter on the right is invoked with the repository name,
-# the name of the command that has been executed, and the module name.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/.#precommand b/test/repositories/cvs/CVSROOT/.#precommand
deleted file mode 100644
index 05c8b890..00000000
--- a/test/repositories/cvs/CVSROOT/.#precommand
+++ /dev/null
@@ -1,18 +0,0 @@
-# The "precommand" file is run before a cvs command is executed.
-# The filter on the right is invoked with the repository name and
-# the name of the command that has been executed. A non-zero return
-# value with abort the command with an error.
-#
-# The standard input of the filter receives each command argument,
-# separated by linefeeds.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/.#premodule b/test/repositories/cvs/CVSROOT/.#premodule
deleted file mode 100644
index 4837892d..00000000
--- a/test/repositories/cvs/CVSROOT/.#premodule
+++ /dev/null
@@ -1,15 +0,0 @@
-# The "premodule" file is run before cvs module is processed.
-# The filter on the right is invoked with the repository name,
-# the name of the command that has been executed, and the module name.
-# A non-zero return value with abort the command with an error.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/.#rcsinfo b/test/repositories/cvs/CVSROOT/.#rcsinfo
deleted file mode 100644
index 27c002f7..00000000
--- a/test/repositories/cvs/CVSROOT/.#rcsinfo
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "rcsinfo" file is used to control templates with which the editor
-# is invoked on commit and import.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/.#shadow b/test/repositories/cvs/CVSROOT/.#shadow
deleted file mode 100644
index 7ccd24e2..00000000
--- a/test/repositories/cvs/CVSROOT/.#shadow
+++ /dev/null
@@ -1,7 +0,0 @@
-# The "shadow" file is used to control automatic checkouts.
-#
-# Each line has 3 parts:
-#
-#
-# In common with other commit support files, use forward slashes
-# and escape any spaces in filenames.
diff --git a/test/repositories/cvs/CVSROOT/.#tag_email b/test/repositories/cvs/CVSROOT/.#tag_email
deleted file mode 100644
index 48d545fe..00000000
--- a/test/repositories/cvs/CVSROOT/.#tag_email
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "tag_email" file is used to control templates for emails sent
-# during tagging operations.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/.#taginfo b/test/repositories/cvs/CVSROOT/.#taginfo
deleted file mode 100644
index 3beb822e..00000000
--- a/test/repositories/cvs/CVSROOT/.#taginfo
+++ /dev/null
@@ -1,21 +0,0 @@
-# The "taginfo" file is used to control pre-tag checks.
-# The filter on the right is invoked with the following arguments:
-#
-# $1 -- tagname
-# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
-# $3 -- repository
-#
-# The filter is passed a series of filename/version pairs on its standard input
-#
-# A non-zero exit of the filter program will cause the tag to be aborted.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/.#triggers b/test/repositories/cvs/CVSROOT/.#triggers
deleted file mode 100644
index 48a9fc33..00000000
--- a/test/repositories/cvs/CVSROOT/.#triggers
+++ /dev/null
@@ -1,5 +0,0 @@
-# The "triggers" file lists libraries which handle the events for each
-# module.
-#
-# In addition to the lines here, the default_trigger library is loaded, if available.
-#
diff --git a/test/repositories/cvs/CVSROOT/.#verifymsg b/test/repositories/cvs/CVSROOT/.#verifymsg
deleted file mode 100644
index 28b1d6ea..00000000
--- a/test/repositories/cvs/CVSROOT/.#verifymsg
+++ /dev/null
@@ -1,17 +0,0 @@
-# The "verifymsg" file is used to allow verification of logging
-# information. It works best when a template (as specified in the
-# rcsinfo file) is provided for the logging procedure. Given a
-# template with locations for, a bug-id number, a list of people who
-# reviewed the code before it can be checked in, and an external
-# process to catalog the differences that were code reviewed, the
-# following test can be applied to the code:
-#
-# Making sure that the entered bug-id number is correct.
-# Validating that the code that was reviewed is indeed the code being
-# checked in (using the bug-id number or a seperate review
-# number to identify this particular code set.).
-#
-# If any of the above test failed, then the commit would be aborted.
-#
-# Actions such as mailing a copy of the report to each reviewer are
-# better handled by an entry in the loginfo file.
diff --git a/test/repositories/cvs/CVSROOT/CVS/fileattr.xml b/test/repositories/cvs/CVSROOT/CVS/fileattr.xml
deleted file mode 100644
index 83ce5ca0..00000000
--- a/test/repositories/cvs/CVSROOT/CVS/fileattr.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- robin
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/repositories/cvs/CVSROOT/checkoutlist b/test/repositories/cvs/CVSROOT/checkoutlist
deleted file mode 100644
index b04b3501..00000000
--- a/test/repositories/cvs/CVSROOT/checkoutlist
+++ /dev/null
@@ -1,13 +0,0 @@
-# The "checkoutlist" file is used to support additional version controlled
-# administrative files in $CVSROOT/CVSROOT, such as template files.
-#
-# The first entry on a line is a filename which will be checked out from
-# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
-# The remainder of the line is an error message to use if the file cannot
-# be checked out.
-#
-# File format:
-#
-# []
-#
-# comment lines begin with '#'
diff --git a/test/repositories/cvs/CVSROOT/checkoutlist,v b/test/repositories/cvs/CVSROOT/checkoutlist,v
deleted file mode 100644
index 13ddb4ff..00000000
--- a/test/repositories/cvs/CVSROOT/checkoutlist,v
+++ /dev/null
@@ -1,39 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "checkoutlist" file is used to support additional version controlled
-# administrative files in $CVSROOT/CVSROOT, such as template files.
-#
-# The first entry on a line is a filename which will be checked out from
-# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
-# The remainder of the line is an error message to use if the file cannot
-# be checked out.
-#
-# File format:
-#
-# []
-#
-# comment lines begin with '#'
-@
-
diff --git a/test/repositories/cvs/CVSROOT/commit_email b/test/repositories/cvs/CVSROOT/commit_email
deleted file mode 100644
index 63863618..00000000
--- a/test/repositories/cvs/CVSROOT/commit_email
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "commit_email" file is used to control templates for emails sent
-# during commit and import.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/commit_email,v b/test/repositories/cvs/CVSROOT/commit_email,v
deleted file mode 100644
index 79135d9e..00000000
--- a/test/repositories/cvs/CVSROOT/commit_email,v
+++ /dev/null
@@ -1,36 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "commit_email" file is used to control templates for emails sent
-# during commit and import.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-@
-
diff --git a/test/repositories/cvs/CVSROOT/commitinfo b/test/repositories/cvs/CVSROOT/commitinfo
deleted file mode 100644
index 63ea65ad..00000000
--- a/test/repositories/cvs/CVSROOT/commitinfo
+++ /dev/null
@@ -1,15 +0,0 @@
-# The "commitinfo" file is used to control pre-commit checks.
-# The filter on the right is invoked with the repository name. A list
-# of files to check is passed to the standard input of the script. A non-zero
-# exit of the filter program will cause the commit to be aborted.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/commitinfo,v b/test/repositories/cvs/CVSROOT/commitinfo,v
deleted file mode 100644
index 62bf695b..00000000
--- a/test/repositories/cvs/CVSROOT/commitinfo,v
+++ /dev/null
@@ -1,41 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "commitinfo" file is used to control pre-commit checks.
-# The filter on the right is invoked with the repository name. A list
-# of files to check is passed to the standard input of the script. A non-zero
-# exit of the filter program will cause the commit to be aborted.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
-@
-
diff --git a/test/repositories/cvs/CVSROOT/config b/test/repositories/cvs/CVSROOT/config
deleted file mode 100644
index af2a28c4..00000000
--- a/test/repositories/cvs/CVSROOT/config
+++ /dev/null
@@ -1,26 +0,0 @@
-# Set this to 'no' if pserver shouldn't check system users/passwords
-#SystemAuth=yes
-
-# Set the Acl parsing type (none,compat,normal).
-#AclMode=compat
-
-# Alternate location of CVS LockServer. Set to 'none' to disable..
-#LockServer=localhost:2402
-
-# Set 'TopLevelAdmin' to 'yes' to create a CVS directory at the top
-# level of the new working directory when using the 'cvs checkout'
-# command.
-#TopLevelAdmin=no
-
-# Set 'LogHistory' to 'all' or 'TOFEWGCMAR' to log all transactions to the
-# history file, or a subset as needed (ie 'TMAR' logs all write operations)
-#LogHistory=TOFEWGCMAR
-
-# Set 'RereadLogAfterVerify' to control rereading of the log file after a verifymsg
-# 'always' or 'yes' to always reread the log regardless
-# 'never' or 'no' (default) to never reread the log
-#RereadLogAfterVerify=no
-
-# Set 'Watcher' to set a user who gets all notify events within the repository whether
-# or not the ifle is watched.
-#Watcher=watch_user
\ No newline at end of file
diff --git a/test/repositories/cvs/CVSROOT/config,v b/test/repositories/cvs/CVSROOT/config,v
deleted file mode 100644
index 80bcd01f..00000000
--- a/test/repositories/cvs/CVSROOT/config,v
+++ /dev/null
@@ -1,51 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# Set this to 'no' if pserver shouldn't check system users/passwords
-#SystemAuth=yes
-
-# Set the Acl parsing type (none,compat,normal).
-#AclMode=compat
-
-# Alternate location of CVS LockServer. Set to 'none' to disable..
-#LockServer=localhost:2402
-
-# Set 'TopLevelAdmin' to 'yes' to create a CVS directory at the top
-# level of the new working directory when using the 'cvs checkout'
-# command.
-#TopLevelAdmin=no
-
-# Set 'LogHistory' to 'all' or 'TOFEWGCMAR' to log all transactions to the
-# history file, or a subset as needed (ie 'TMAR' logs all write operations)
-#LogHistory=TOFEWGCMAR
-
-# Set 'RereadLogAfterVerify' to control rereading of the log file after a verifymsg
-# 'always' or 'yes' to always reread the log regardless
-# 'never' or 'no' (default) to never reread the log
-#RereadLogAfterVerify=no
-
-# Set 'Watcher' to set a user who gets all notify events within the repository whether
-# or not the ifle is watched.
-#Watcher=watch_user@
-
diff --git a/test/repositories/cvs/CVSROOT/cvsrc,v b/test/repositories/cvs/CVSROOT/cvsrc,v
deleted file mode 100644
index 6e50ecf6..00000000
--- a/test/repositories/cvs/CVSROOT/cvsrc,v
+++ /dev/null
@@ -1,26 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@@
-
diff --git a/test/repositories/cvs/CVSROOT/cvswrappers b/test/repositories/cvs/CVSROOT/cvswrappers
deleted file mode 100644
index 11fa3b39..00000000
--- a/test/repositories/cvs/CVSROOT/cvswrappers
+++ /dev/null
@@ -1,18 +0,0 @@
-# This file affects handling of files based on their names.
-#
-# The -m option specifies whether CVS attempts to merge files.
-#
-# The -k option specifies keyword expansion (e.g. -kb for binary).
-#
-# The -t option overrides the default mime type.
-#
-# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
-#
-# wildcard [option value][option value]...
-#
-# where option is one of
-# -k expansion mode value: b, o, kkv, etc.
-#
-# and value is a single-quote delimited value.
-# For example:
-#*.gif -kb
diff --git a/test/repositories/cvs/CVSROOT/cvswrappers,v b/test/repositories/cvs/CVSROOT/cvswrappers,v
deleted file mode 100644
index badb2259..00000000
--- a/test/repositories/cvs/CVSROOT/cvswrappers,v
+++ /dev/null
@@ -1,44 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# This file affects handling of files based on their names.
-#
-# The -m option specifies whether CVS attempts to merge files.
-#
-# The -k option specifies keyword expansion (e.g. -kb for binary).
-#
-# The -t option overrides the default mime type.
-#
-# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
-#
-# wildcard [option value][option value]...
-#
-# where option is one of
-# -k expansion mode value: b, o, kkv, etc.
-#
-# and value is a single-quote delimited value.
-# For example:
-#*.gif -kb
-@
-
diff --git a/test/repositories/cvs/CVSROOT/historyinfo b/test/repositories/cvs/CVSROOT/historyinfo
deleted file mode 100644
index 889ff1be..00000000
--- a/test/repositories/cvs/CVSROOT/historyinfo
+++ /dev/null
@@ -1,15 +0,0 @@
-# The "historyinfo" file is used to log the history file output.
-# The filter on the right is invoked with the repository name. Its
-# standard input contains the history line that has just been written
-# to the history file (if it exists)
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/historyinfo,v b/test/repositories/cvs/CVSROOT/historyinfo,v
deleted file mode 100644
index 645bd87f..00000000
--- a/test/repositories/cvs/CVSROOT/historyinfo,v
+++ /dev/null
@@ -1,41 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "historyinfo" file is used to log the history file output.
-# The filter on the right is invoked with the repository name. Its
-# standard input contains the history line that has just been written
-# to the history file (if it exists)
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
-@
-
diff --git a/test/repositories/cvs/CVSROOT/keywords b/test/repositories/cvs/CVSROOT/keywords
deleted file mode 100644
index a94adf4d..00000000
--- a/test/repositories/cvs/CVSROOT/keywords
+++ /dev/null
@@ -1,30 +0,0 @@
-# The "keywords" file is used to modify the standard set of RCS keywords
-# or define entirely new ones.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. Subsequent lines contain keyword definitions, indented by a space
-# to separate them from module definitions
-.#
-# If the repository name does not match any of the definitions in this
-# file, the "ALL" section is used, if it is specified.
-#
-# Any keyword with an empty definition is ignored. This can be used to selectively
-# disable individual RCS keywords.
-#
-# The default defintions are:
-#
-#ALL
-# Author %a
-# Date %d
-# Header %r/%p/%f %v %d %a %s %l
-# CVSHeader %p/%f %v %d %a %s %l
-# Id %f %v %d %a %s %l
-# Locker %l
-# Log %f
-# Name %N
-# RCSfile %f
-# Revision %v
-# Source %r/%p/%f
-# State %s
-# CommitId %C
diff --git a/test/repositories/cvs/CVSROOT/keywords,v b/test/repositories/cvs/CVSROOT/keywords,v
deleted file mode 100644
index 83f97ed2..00000000
--- a/test/repositories/cvs/CVSROOT/keywords,v
+++ /dev/null
@@ -1,56 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "keywords" file is used to modify the standard set of RCS keywords
-# or define entirely new ones.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. Subsequent lines contain keyword definitions, indented by a space
-# to separate them from module definitions
-.#
-# If the repository name does not match any of the definitions in this
-# file, the "ALL" section is used, if it is specified.
-#
-# Any keyword with an empty definition is ignored. This can be used to selectively
-# disable individual RCS keywords.
-#
-# The default defintions are:
-#
-#ALL
-# Author %a
-# Date %d
-# Header %r/%p/%f %v %d %a %s %l
-# CVSHeader %p/%f %v %d %a %s %l
-# Id %f %v %d %a %s %l
-# Locker %l
-# Log %f
-# Name %N
-# RCSfile %f
-# Revision %v
-# Source %r/%p/%f
-# State %s
-# CommitId %C
-@
-
diff --git a/test/repositories/cvs/CVSROOT/loginfo b/test/repositories/cvs/CVSROOT/loginfo
deleted file mode 100644
index f3c43297..00000000
--- a/test/repositories/cvs/CVSROOT/loginfo
+++ /dev/null
@@ -1,22 +0,0 @@
-# The "loginfo" file controls where "cvs commit" log information
-# is sent. The first entry on a line is a regular expression which must match
-# the directory that the change is being made to, relative to the
-# $CVSROOT. If a match is found, then the remainder of the line is a filter
-# program that should expect log information on its standard input.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name ALL appears as a regular expression it is always used
-# in addition to the first matching regex or DEFAULT.
-#
-# You may specify a format string as part of the
-# filter. The string is composed of a '%' followed
-# by a single format character, or followed by a set of format
-# characters surrounded by '{' and '}' as separators. The format
-# characters are:
-#
-# s = file name
-# V = old version number (pre-checkin)
-# v = new version number (post-checkin)
-#
diff --git a/test/repositories/cvs/CVSROOT/loginfo,v b/test/repositories/cvs/CVSROOT/loginfo,v
deleted file mode 100644
index aac350e9..00000000
--- a/test/repositories/cvs/CVSROOT/loginfo,v
+++ /dev/null
@@ -1,48 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "loginfo" file controls where "cvs commit" log information
-# is sent. The first entry on a line is a regular expression which must match
-# the directory that the change is being made to, relative to the
-# $CVSROOT. If a match is found, then the remainder of the line is a filter
-# program that should expect log information on its standard input.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name ALL appears as a regular expression it is always used
-# in addition to the first matching regex or DEFAULT.
-#
-# You may specify a format string as part of the
-# filter. The string is composed of a '%' followed
-# by a single format character, or followed by a set of format
-# characters surrounded by '{' and '}' as separators. The format
-# characters are:
-#
-# s = file name
-# V = old version number (pre-checkin)
-# v = new version number (post-checkin)
-#
-@
-
diff --git a/test/repositories/cvs/CVSROOT/modules b/test/repositories/cvs/CVSROOT/modules
deleted file mode 100644
index cb9e9efc..00000000
--- a/test/repositories/cvs/CVSROOT/modules
+++ /dev/null
@@ -1,26 +0,0 @@
-# Three different line formats are valid:
-# key -a aliases...
-# key [options] directory
-# key [options] directory files...
-#
-# Where "options" are composed of:
-# -i prog Run "prog" on "cvs commit" from top-level of module.
-# -o prog Run "prog" on "cvs checkout" of module.
-# -e prog Run "prog" on "cvs export" of module.
-# -t prog Run "prog" on "cvs rtag" of module.
-# -u prog Run "prog" on "cvs update" of module.
-# -d dir Place module in directory "dir" instead of module name.
-# -l Top-level directory only -- do not recurse.
-#
-# NOTE: If you change any of the "Run" options above, you'll have to
-# release and re-checkout any working directories of these modules.
-#
-# And "directory" is a path to a directory relative to $CVSROOT.
-#
-# The "-a" option specifies an alias. An alias is interpreted as if
-# everything on the right of the "-a" had been typed on the command line.
-#
-# You can encode a module within a module by using the special '&'
-# character to interpose another module into the current module. This
-# can be useful for creating a module that consists of many directories
-# spread out over the entire source repository.
diff --git a/test/repositories/cvs/CVSROOT/modules,v b/test/repositories/cvs/CVSROOT/modules,v
deleted file mode 100644
index bced0056..00000000
--- a/test/repositories/cvs/CVSROOT/modules,v
+++ /dev/null
@@ -1,52 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# Three different line formats are valid:
-# key -a aliases...
-# key [options] directory
-# key [options] directory files...
-#
-# Where "options" are composed of:
-# -i prog Run "prog" on "cvs commit" from top-level of module.
-# -o prog Run "prog" on "cvs checkout" of module.
-# -e prog Run "prog" on "cvs export" of module.
-# -t prog Run "prog" on "cvs rtag" of module.
-# -u prog Run "prog" on "cvs update" of module.
-# -d dir Place module in directory "dir" instead of module name.
-# -l Top-level directory only -- do not recurse.
-#
-# NOTE: If you change any of the "Run" options above, you'll have to
-# release and re-checkout any working directories of these modules.
-#
-# And "directory" is a path to a directory relative to $CVSROOT.
-#
-# The "-a" option specifies an alias. An alias is interpreted as if
-# everything on the right of the "-a" had been typed on the command line.
-#
-# You can encode a module within a module by using the special '&'
-# character to interpose another module into the current module. This
-# can be useful for creating a module that consists of many directories
-# spread out over the entire source repository.
-@
-
diff --git a/test/repositories/cvs/CVSROOT/modules2 b/test/repositories/cvs/CVSROOT/modules2
deleted file mode 100644
index 6cdc5bae..00000000
--- a/test/repositories/cvs/CVSROOT/modules2
+++ /dev/null
@@ -1,22 +0,0 @@
-# *** modules2 currently has 'experimental' status. Testing is encouraged but.
-# for greatest stability use the modules file. ***
-#
-# This file describes the layout of virtual directory structures
-# within the repository.
-#
-# The layout is similar to a Windows .ini file. For example:
-#
-# [foo]
-# dir1/dir2/dir3 = realdir1/realdir2
-# dir1/dir3 = !realdir1/realdir3 (^*js$|^*cpp$)
-#
-# [bar]
-# / = realdir4
-# dir_to_delete =
-# foo = foo
-#
-# The special character '!' stops recursion to directories below the one specified (-l option).
-# The special character '+' stops parsing of that line, so that you can avoid infinte loops.
-#
-# Items in (...) are an extended regular expression applied to the filenames. All files which do not.
-# match are ignored.
diff --git a/test/repositories/cvs/CVSROOT/modules2,v b/test/repositories/cvs/CVSROOT/modules2,v
deleted file mode 100644
index 0986d954..00000000
--- a/test/repositories/cvs/CVSROOT/modules2,v
+++ /dev/null
@@ -1,48 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# *** modules2 currently has 'experimental' status. Testing is encouraged but.
-# for greatest stability use the modules file. ***
-#
-# This file describes the layout of virtual directory structures
-# within the repository.
-#
-# The layout is similar to a Windows .ini file. For example:
-#
-# [foo]
-# dir1/dir2/dir3 = realdir1/realdir2
-# dir1/dir3 = !realdir1/realdir3 (^*js$|^*cpp$)
-#
-# [bar]
-# / = realdir4
-# dir_to_delete =
-# foo = foo
-#
-# The special character '!' stops recursion to directories below the one specified (-l option).
-# The special character '+' stops parsing of that line, so that you can avoid infinte loops.
-#
-# Items in (...) are an extended regular expression applied to the filenames. All files which do not.
-# match are ignored.
-@
-
diff --git a/test/repositories/cvs/CVSROOT/notify b/test/repositories/cvs/CVSROOT/notify
deleted file mode 100644
index cdb3320c..00000000
--- a/test/repositories/cvs/CVSROOT/notify
+++ /dev/null
@@ -1,22 +0,0 @@
-# The "notify" file controls where notifications from watches set by
-# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
-# a regular expression which is tested against the directory that the
-# change is being made to, relative to the $CVSROOT. If it matches,
-# then the remainder of the line is a filter program that should contain
-# one occurrence of %s for the user to notify, and information on its
-# standard input.
-#
-# "ALL" or "DEFAULT" can be used in place of the regular expression.
-#
-# You may specify a format string as part of the
-# filter. The format characters are:
-#
-# s = user being notified
-# b = Bug identifier
-# m = Message supplied on command line
-# d = Date of action
-# u = User performing the unedit
-# t = tag or branch being edited
-#
-# For example:
-#ALL mail %s -s "CVS notification for bug %b"
diff --git a/test/repositories/cvs/CVSROOT/notify,v b/test/repositories/cvs/CVSROOT/notify,v
deleted file mode 100644
index 5edf2f51..00000000
--- a/test/repositories/cvs/CVSROOT/notify,v
+++ /dev/null
@@ -1,48 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "notify" file controls where notifications from watches set by
-# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
-# a regular expression which is tested against the directory that the
-# change is being made to, relative to the $CVSROOT. If it matches,
-# then the remainder of the line is a filter program that should contain
-# one occurrence of %s for the user to notify, and information on its
-# standard input.
-#
-# "ALL" or "DEFAULT" can be used in place of the regular expression.
-#
-# You may specify a format string as part of the
-# filter. The format characters are:
-#
-# s = user being notified
-# b = Bug identifier
-# m = Message supplied on command line
-# d = Date of action
-# u = User performing the unedit
-# t = tag or branch being edited
-#
-# For example:
-#ALL mail %s -s "CVS notification for bug %b"
-@
-
diff --git a/test/repositories/cvs/CVSROOT/notify_email b/test/repositories/cvs/CVSROOT/notify_email
deleted file mode 100644
index c673ab4c..00000000
--- a/test/repositories/cvs/CVSROOT/notify_email
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "notify_email" file is used to control templates for emails sent
-# when notifying users.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/notify_email,v b/test/repositories/cvs/CVSROOT/notify_email,v
deleted file mode 100644
index e9fcc95e..00000000
--- a/test/repositories/cvs/CVSROOT/notify_email,v
+++ /dev/null
@@ -1,36 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "notify_email" file is used to control templates for emails sent
-# when notifying users.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-@
-
diff --git a/test/repositories/cvs/CVSROOT/postcommand b/test/repositories/cvs/CVSROOT/postcommand
deleted file mode 100644
index ab3ef145..00000000
--- a/test/repositories/cvs/CVSROOT/postcommand
+++ /dev/null
@@ -1,14 +0,0 @@
-# The "postcommand" file is run after a cvs command has finished.
-# The filter on the right is invoked with the repository name and
-# the name of the command that has been executed.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/postcommand,v b/test/repositories/cvs/CVSROOT/postcommand,v
deleted file mode 100644
index b9a194bd..00000000
--- a/test/repositories/cvs/CVSROOT/postcommand,v
+++ /dev/null
@@ -1,40 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "postcommand" file is run after a cvs command has finished.
-# The filter on the right is invoked with the repository name and
-# the name of the command that has been executed.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
-@
-
diff --git a/test/repositories/cvs/CVSROOT/postmodule b/test/repositories/cvs/CVSROOT/postmodule
deleted file mode 100644
index b6d258be..00000000
--- a/test/repositories/cvs/CVSROOT/postmodule
+++ /dev/null
@@ -1,14 +0,0 @@
-# The "postmodule" file is run after a cvs module is processed.
-# The filter on the right is invoked with the repository name,
-# the name of the command that has been executed, and the module name.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/postmodule,v b/test/repositories/cvs/CVSROOT/postmodule,v
deleted file mode 100644
index f70fae0b..00000000
--- a/test/repositories/cvs/CVSROOT/postmodule,v
+++ /dev/null
@@ -1,40 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "postmodule" file is run after a cvs module is processed.
-# The filter on the right is invoked with the repository name,
-# the name of the command that has been executed, and the module name.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
-@
-
diff --git a/test/repositories/cvs/CVSROOT/precommand b/test/repositories/cvs/CVSROOT/precommand
deleted file mode 100644
index 05c8b890..00000000
--- a/test/repositories/cvs/CVSROOT/precommand
+++ /dev/null
@@ -1,18 +0,0 @@
-# The "precommand" file is run before a cvs command is executed.
-# The filter on the right is invoked with the repository name and
-# the name of the command that has been executed. A non-zero return
-# value with abort the command with an error.
-#
-# The standard input of the filter receives each command argument,
-# separated by linefeeds.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/precommand,v b/test/repositories/cvs/CVSROOT/precommand,v
deleted file mode 100644
index f2a2db5e..00000000
--- a/test/repositories/cvs/CVSROOT/precommand,v
+++ /dev/null
@@ -1,44 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "precommand" file is run before a cvs command is executed.
-# The filter on the right is invoked with the repository name and
-# the name of the command that has been executed. A non-zero return
-# value with abort the command with an error.
-#
-# The standard input of the filter receives each command argument,
-# separated by linefeeds.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
-@
-
diff --git a/test/repositories/cvs/CVSROOT/premodule b/test/repositories/cvs/CVSROOT/premodule
deleted file mode 100644
index 4837892d..00000000
--- a/test/repositories/cvs/CVSROOT/premodule
+++ /dev/null
@@ -1,15 +0,0 @@
-# The "premodule" file is run before cvs module is processed.
-# The filter on the right is invoked with the repository name,
-# the name of the command that has been executed, and the module name.
-# A non-zero return value with abort the command with an error.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/premodule,v b/test/repositories/cvs/CVSROOT/premodule,v
deleted file mode 100644
index 0d1b7c4d..00000000
--- a/test/repositories/cvs/CVSROOT/premodule,v
+++ /dev/null
@@ -1,41 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "premodule" file is run before cvs module is processed.
-# The filter on the right is invoked with the repository name,
-# the name of the command that has been executed, and the module name.
-# A non-zero return value with abort the command with an error.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
-@
-
diff --git a/test/repositories/cvs/CVSROOT/rcsinfo b/test/repositories/cvs/CVSROOT/rcsinfo
deleted file mode 100644
index 27c002f7..00000000
--- a/test/repositories/cvs/CVSROOT/rcsinfo
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "rcsinfo" file is used to control templates with which the editor
-# is invoked on commit and import.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/rcsinfo,v b/test/repositories/cvs/CVSROOT/rcsinfo,v
deleted file mode 100644
index a3c803d6..00000000
--- a/test/repositories/cvs/CVSROOT/rcsinfo,v
+++ /dev/null
@@ -1,36 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "rcsinfo" file is used to control templates with which the editor
-# is invoked on commit and import.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-@
-
diff --git a/test/repositories/cvs/CVSROOT/shadow b/test/repositories/cvs/CVSROOT/shadow
deleted file mode 100644
index 7ccd24e2..00000000
--- a/test/repositories/cvs/CVSROOT/shadow
+++ /dev/null
@@ -1,7 +0,0 @@
-# The "shadow" file is used to control automatic checkouts.
-#
-# Each line has 3 parts:
-#
-#
-# In common with other commit support files, use forward slashes
-# and escape any spaces in filenames.
diff --git a/test/repositories/cvs/CVSROOT/shadow,v b/test/repositories/cvs/CVSROOT/shadow,v
deleted file mode 100644
index a3c14d97..00000000
--- a/test/repositories/cvs/CVSROOT/shadow,v
+++ /dev/null
@@ -1,33 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "shadow" file is used to control automatic checkouts.
-#
-# Each line has 3 parts:
-#
-#
-# In common with other commit support files, use forward slashes
-# and escape any spaces in filenames.
-@
-
diff --git a/test/repositories/cvs/CVSROOT/tag_email b/test/repositories/cvs/CVSROOT/tag_email
deleted file mode 100644
index 48d545fe..00000000
--- a/test/repositories/cvs/CVSROOT/tag_email
+++ /dev/null
@@ -1,10 +0,0 @@
-# The "tag_email" file is used to control templates for emails sent
-# during tagging operations.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
diff --git a/test/repositories/cvs/CVSROOT/tag_email,v b/test/repositories/cvs/CVSROOT/tag_email,v
deleted file mode 100644
index 3fc9444b..00000000
--- a/test/repositories/cvs/CVSROOT/tag_email,v
+++ /dev/null
@@ -1,36 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "tag_email" file is used to control templates for emails sent
-# during tagging operations.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being made to, relative to the
-# $CVSROOT. For the first match that is found, then the remainder of the
-# line is the name of the file that contains the template.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-@
-
diff --git a/test/repositories/cvs/CVSROOT/taginfo b/test/repositories/cvs/CVSROOT/taginfo
deleted file mode 100644
index 3beb822e..00000000
--- a/test/repositories/cvs/CVSROOT/taginfo
+++ /dev/null
@@ -1,21 +0,0 @@
-# The "taginfo" file is used to control pre-tag checks.
-# The filter on the right is invoked with the following arguments:
-#
-# $1 -- tagname
-# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
-# $3 -- repository
-#
-# The filter is passed a series of filename/version pairs on its standard input
-#
-# A non-zero exit of the filter program will cause the tag to be aborted.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
diff --git a/test/repositories/cvs/CVSROOT/taginfo,v b/test/repositories/cvs/CVSROOT/taginfo,v
deleted file mode 100644
index f511baba..00000000
--- a/test/repositories/cvs/CVSROOT/taginfo,v
+++ /dev/null
@@ -1,47 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "taginfo" file is used to control pre-tag checks.
-# The filter on the right is invoked with the following arguments:
-#
-# $1 -- tagname
-# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d
-# $3 -- repository
-#
-# The filter is passed a series of filename/version pairs on its standard input
-#
-# A non-zero exit of the filter program will cause the tag to be aborted.
-#
-# The first entry on a line is a regular expression which is tested
-# against the directory that the change is being committed to, relative
-# to the $CVSROOT. For the first match that is found, then the remainder
-# of the line is the name of the filter to run.
-#
-# If the repository name does not match any of the regular expressions in this
-# file, the "DEFAULT" line is used, if it is specified.
-#
-# If the name "ALL" appears as a regular expression it is always used
-# in addition to the first matching regex or "DEFAULT".
-@
-
diff --git a/test/repositories/cvs/CVSROOT/triggers b/test/repositories/cvs/CVSROOT/triggers
deleted file mode 100644
index 48a9fc33..00000000
--- a/test/repositories/cvs/CVSROOT/triggers
+++ /dev/null
@@ -1,5 +0,0 @@
-# The "triggers" file lists libraries which handle the events for each
-# module.
-#
-# In addition to the lines here, the default_trigger library is loaded, if available.
-#
diff --git a/test/repositories/cvs/CVSROOT/triggers,v b/test/repositories/cvs/CVSROOT/triggers,v
deleted file mode 100644
index 78a9e625..00000000
--- a/test/repositories/cvs/CVSROOT/triggers,v
+++ /dev/null
@@ -1,31 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "triggers" file lists libraries which handle the events for each
-# module.
-#
-# In addition to the lines here, the default_trigger library is loaded, if available.
-#
-@
-
diff --git a/test/repositories/cvs/CVSROOT/val-tags b/test/repositories/cvs/CVSROOT/val-tags
deleted file mode 100644
index 6f25c268..00000000
--- a/test/repositories/cvs/CVSROOT/val-tags
+++ /dev/null
@@ -1,2 +0,0 @@
-my_branch y
-another_branch y
diff --git a/test/repositories/cvs/CVSROOT/verifymsg b/test/repositories/cvs/CVSROOT/verifymsg
deleted file mode 100644
index 28b1d6ea..00000000
--- a/test/repositories/cvs/CVSROOT/verifymsg
+++ /dev/null
@@ -1,17 +0,0 @@
-# The "verifymsg" file is used to allow verification of logging
-# information. It works best when a template (as specified in the
-# rcsinfo file) is provided for the logging procedure. Given a
-# template with locations for, a bug-id number, a list of people who
-# reviewed the code before it can be checked in, and an external
-# process to catalog the differences that were code reviewed, the
-# following test can be applied to the code:
-#
-# Making sure that the entered bug-id number is correct.
-# Validating that the code that was reviewed is indeed the code being
-# checked in (using the bug-id number or a seperate review
-# number to identify this particular code set.).
-#
-# If any of the above test failed, then the commit would be aborted.
-#
-# Actions such as mailing a copy of the report to each reviewer are
-# better handled by an entry in the loginfo file.
diff --git a/test/repositories/cvs/CVSROOT/verifymsg,v b/test/repositories/cvs/CVSROOT/verifymsg,v
deleted file mode 100644
index 78394d81..00000000
--- a/test/repositories/cvs/CVSROOT/verifymsg,v
+++ /dev/null
@@ -1,43 +0,0 @@
-head 1.1;
-access ;
-symbols ;
-locks ; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.19.58; author robin; state Exp;
-branches;
-next ;
-kopt kv;
-deltatype text;
-permissions 664;
-
-desc
-@@
-
-
-
-1.1
-log
-@initial checkin@
-text
-@# The "verifymsg" file is used to allow verification of logging
-# information. It works best when a template (as specified in the
-# rcsinfo file) is provided for the logging procedure. Given a
-# template with locations for, a bug-id number, a list of people who
-# reviewed the code before it can be checked in, and an external
-# process to catalog the differences that were code reviewed, the
-# following test can be applied to the code:
-#
-# Making sure that the entered bug-id number is correct.
-# Validating that the code that was reviewed is indeed the code being
-# checked in (using the bug-id number or a seperate review
-# number to identify this particular code set.).
-#
-# If any of the above test failed, then the commit would be aborted.
-#
-# Actions such as mailing a copy of the report to each reviewer are
-# better handled by an entry in the loginfo file.
-@
-
diff --git a/test/repositories/cvs/simple/CVS/fileattr.xml b/test/repositories/cvs/simple/CVS/fileattr.xml
deleted file mode 100644
index f8767f21..00000000
--- a/test/repositories/cvs/simple/CVS/fileattr.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- robin
-
-
diff --git a/test/repositories/cvs/simple/another_file.rb,v b/test/repositories/cvs/simple/another_file.rb,v
deleted file mode 100644
index 638a8f75..00000000
--- a/test/repositories/cvs/simple/another_file.rb,v
+++ /dev/null
@@ -1,113 +0,0 @@
-head 1.2;
-access;
-symbols
- another_branch:1.1.0.2;
-locks; strict;
-comment @# @;
-
-
-1.2
-date 2006.06.29.18.45.29; author robin; state Exp;
-branches;
-next 1.1;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid db744a41fc8a6ac;
-mergepoint1 1.1.2.2;
-filename another_file.rb;
-
-1.1
-date 2006.06.29.18.44.27; author robin; state dead;
-branches
- 1.1.2.1;
-next ;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid db044a41f8b01e1;
-filename another_file.rb;
-
-1.1.2.1
-date 2006.06.29.18.44.27; author robin; state Exp;
-branches;
-next 1.1.2.2;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid db044a41f8b01e1;
-filename another_file.rb;
-
-1.1.2.2
-date 2006.06.29.18.44.56; author robin; state Exp;
-branches;
-next 1.1.2.3;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid db244a41fa871cc;
-filename another_file.rb;
-
-1.1.2.3
-date 2006.06.29.18.50.37; author robin; state Exp;
-branches;
-next ;
-deltatype text;
-kopt kv;
-permissions 600;
-commitid dfc44a420fde53f;
-mergepoint1 1.2;
-filename another_file.rb;
-
-
-desc
-@@
-
-
-1.2
-log
-@merged another_file from another_branch onto the HEAD
-@
-text
-@puts "this file was created on another branch"
-puts "I edited this file on another branch only"
-@
-
-
-1.1
-log
-@file another_file.rb was initially added on branch another_branch.
-@
-text
-@d1 2
-@
-
-
-1.1.2.1
-log
-@created another_file on another_branch
-@
-text
-@a0 1
-puts "this file was created on another branch"
-@
-
-
-1.1.2.2
-log
-@edited another_file
-@
-text
-@a1 1
-puts "I edited this file on another branch only"
-@
-
-
-1.1.2.3
-log
-@merged from head onto another_branch. this added file late_addition
-@
-text
-@@
-
-
diff --git a/test/repositories/cvs/simple/foo.rb,v b/test/repositories/cvs/simple/foo.rb,v
deleted file mode 100644
index 6ed04b1c..00000000
--- a/test/repositories/cvs/simple/foo.rb,v
+++ /dev/null
@@ -1,50 +0,0 @@
-head 1.1;
-branch 1.1.1;
-access;
-symbols
- another_branch:1.1.1.1.0.4
- my_branch:1.1.1.1.0.2
- simple_release_tag:1.1.1.1
- simple_vendor_tag:1.1.1;
-locks; strict;
-comment @# @;
-
-
-1.1
-date 2006.06.29.16.21.07; author robin; state Exp;
-branches
- 1.1.1.1;
-next ;
-deltatype text;
-kopt kv;
-permissions 644;
-
-1.1.1.1
-date 2006.06.29.16.21.07; author robin; state Exp;
-branches;
-next ;
-deltatype text;
-kopt kv;
-permissions 644;
-
-
-desc
-@@
-
-
-
-1.1
-log
-@Initial revision
-@
-text
-@puts "Hi, I'm foo.rb!"
-@
-
-
-1.1.1.1
-log
-@Initial import.
-@
-text
-@@
diff --git a/test/repositories/cvs/simple/late_addition.rb,v b/test/repositories/cvs/simple/late_addition.rb,v
deleted file mode 100644
index 91b4c72e..00000000
--- a/test/repositories/cvs/simple/late_addition.rb,v
+++ /dev/null
@@ -1,93 +0,0 @@
-head 1.2;
-access;
-symbols
- another_branch:1.1.0.2;
-locks; strict;
-comment @# @;
-
-
-1.2
-date 2006.06.29.18.52.23; author robin; state Exp;
-branches;
-next 1.1;
-deltatype text;
-kopt kv;
-permissions 600;
-commitid e0544a421671465;
-mergepoint1 1.1.2.2;
-filename late_addition.rb;
-
-1.1
-date 2006.06.29.18.48.54; author robin; state Exp;
-branches
- 1.1.2.1;
-next ;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid df344a420967b0e;
-filename late_addition.rb;
-
-1.1.2.1
-date 2006.06.29.18.50.37; author robin; state Exp;
-branches;
-next 1.1.2.2;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid dfc44a420fde53f;
-mergepoint1 1.1;
-filename late_addition.rb;
-
-1.1.2.2
-date 2006.06.29.18.51.25; author robin; state Exp;
-branches;
-next ;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid dff44a4212d348f;
-filename late_addition.rb;
-
-
-desc
-@@
-
-
-1.2
-log
-@merged the change to late_addition from another_branch to the head
-@
-text
-@puts "This file was created on the HEAD after the branches were already made"
-puts "This line of code was added on another_branch after merging from the head"
-@
-
-
-1.1
-log
-@created late_addition on the HEAD
-@
-text
-@d2 1
-@
-
-
-1.1.2.1
-log
-@merged from head onto another_branch. this added file late_addition
-@
-text
-@@
-
-
-1.1.2.2
-log
-@edited late_addition on another_branch
-@
-text
-@a1 1
-puts "This line of code was added on another_branch after merging from the head"
-@
-
-
diff --git a/test/repositories/cvs/simple/new_file.rb,v b/test/repositories/cvs/simple/new_file.rb,v
deleted file mode 100644
index 72d4e578..00000000
--- a/test/repositories/cvs/simple/new_file.rb,v
+++ /dev/null
@@ -1,112 +0,0 @@
-head 1.2;
-access;
-symbols
- another_branch:1.2.0.2
- my_branch:1.1.0.2;
-locks; strict;
-comment @# @;
-
-
-1.2
-date 2006.06.29.18.14.47; author robin; state Exp;
-branches;
-next 1.1;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid c3944a41896430e;
-mergepoint1 1.1.2.1;
-filename new_file.rb;
-
-1.1
-date 2006.06.29.18.05.27; author robin; state dead;
-branches
- 1.1.2.1;
-next ;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid bd144a41667e765;
-filename new_file.rb;
-
-1.1.2.1
-date 2006.06.29.18.05.27; author robin; state Exp;
-branches;
-next 1.1.2.2;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid bd144a41667e765;
-filename new_file.rb;
-
-1.1.2.2
-date 2006.06.29.18.17.49; author robin; state Exp;
-branches;
-next 1.1.2.3;
-deltatype text;
-kopt kv;
-permissions 644;
-commitid c7744a4194cefc8;
-filename new_file.rb;
-
-1.1.2.3
-date 2006.06.29.18.40.38; author robin; state dead;
-branches;
-next ;
-deltatype text;
-kopt kv;
-permissions 444;
-commitid d4e44a41ea6477e;
-filename new_file.rb;
-
-
-desc
-@@
-
-
-1.2
-log
-@merged new_file.rb from branch onto the HEAD
-@
-text
-@puts "I was created on the branch"
-@
-
-
-1.1
-log
-@file new_file.rb was initially added on branch my_branch.
-@
-text
-@d1 1
-@
-
-
-1.1.2.1
-log
-@added new_file.rb on the branch
-@
-text
-@a0 1
-puts "I was created on the branch"
-@
-
-
-1.1.2.2
-log
-@modifed new_file.rb on the branch only
-@
-text
-@a1 1
-puts "This file was edited on the branch after the orginal was merged onto the HEAD"
-@
-
-
-1.1.2.3
-log
-@removed new_file.rb from the branch only
-@
-text
-@@
-
-
diff --git a/test/repositories/git.tgz b/test/repositories/git.tgz
index 5ff058f5..e5c12a53 100644
Binary files a/test/repositories/git.tgz and b/test/repositories/git.tgz differ
diff --git a/test/repositories/git_dupe_delete.tgz b/test/repositories/git_dupe_delete.tgz
new file mode 100644
index 00000000..e6bbc0c0
Binary files /dev/null and b/test/repositories/git_dupe_delete.tgz differ
diff --git a/test/repositories/git_with_empty_merge.tgz b/test/repositories/git_with_empty_merge.tgz
new file mode 100644
index 00000000..4276f993
Binary files /dev/null and b/test/repositories/git_with_empty_merge.tgz differ
diff --git a/test/repositories/git_with_invalid_encoding.tgz b/test/repositories/git_with_invalid_encoding.tgz
new file mode 100644
index 00000000..5bf454be
Binary files /dev/null and b/test/repositories/git_with_invalid_encoding.tgz differ
diff --git a/test/repositories/git_with_null_merge.tgz b/test/repositories/git_with_null_merge.tgz
new file mode 100644
index 00000000..283db5ce
Binary files /dev/null and b/test/repositories/git_with_null_merge.tgz differ
diff --git a/test/repositories/hg.tgz b/test/repositories/hg.tgz
new file mode 100644
index 00000000..a08852fa
Binary files /dev/null and b/test/repositories/hg.tgz differ
diff --git a/test/repositories/hg/.hg/00changelog.i b/test/repositories/hg/.hg/00changelog.i
deleted file mode 100644
index d3a83110..00000000
Binary files a/test/repositories/hg/.hg/00changelog.i and /dev/null differ
diff --git a/test/repositories/hg/.hg/branch b/test/repositories/hg/.hg/branch
deleted file mode 100644
index 4ad96d51..00000000
--- a/test/repositories/hg/.hg/branch
+++ /dev/null
@@ -1 +0,0 @@
-default
diff --git a/test/repositories/hg/.hg/branch.cache b/test/repositories/hg/.hg/branch.cache
deleted file mode 100644
index 4e2e2d4e..00000000
--- a/test/repositories/hg/.hg/branch.cache
+++ /dev/null
@@ -1,2 +0,0 @@
-75532c1e1f1de55c2271f6fd29d98efbe35397c4 3
-75532c1e1f1de55c2271f6fd29d98efbe35397c4 default
diff --git a/test/repositories/hg/.hg/dirstate b/test/repositories/hg/.hg/dirstate
deleted file mode 100644
index 18386c3b..00000000
Binary files a/test/repositories/hg/.hg/dirstate and /dev/null differ
diff --git a/test/repositories/hg/.hg/requires b/test/repositories/hg/.hg/requires
deleted file mode 100644
index 57601b5e..00000000
--- a/test/repositories/hg/.hg/requires
+++ /dev/null
@@ -1,2 +0,0 @@
-revlogv1
-store
diff --git a/test/repositories/hg/.hg/store/00changelog.i b/test/repositories/hg/.hg/store/00changelog.i
deleted file mode 100644
index 154a1df5..00000000
Binary files a/test/repositories/hg/.hg/store/00changelog.i and /dev/null differ
diff --git a/test/repositories/hg/.hg/store/00manifest.i b/test/repositories/hg/.hg/store/00manifest.i
deleted file mode 100644
index 17166141..00000000
Binary files a/test/repositories/hg/.hg/store/00manifest.i and /dev/null differ
diff --git a/test/repositories/hg/.hg/store/data/_r_e_a_d_m_e.i b/test/repositories/hg/.hg/store/data/_r_e_a_d_m_e.i
deleted file mode 100644
index 888bdb0d..00000000
Binary files a/test/repositories/hg/.hg/store/data/_r_e_a_d_m_e.i and /dev/null differ
diff --git a/test/repositories/hg/.hg/store/data/helloworld.c.i b/test/repositories/hg/.hg/store/data/helloworld.c.i
deleted file mode 100644
index 9214368d..00000000
Binary files a/test/repositories/hg/.hg/store/data/helloworld.c.i and /dev/null differ
diff --git a/test/repositories/hg/.hg/store/data/makefile.i b/test/repositories/hg/.hg/store/data/makefile.i
deleted file mode 100644
index febd4591..00000000
Binary files a/test/repositories/hg/.hg/store/data/makefile.i and /dev/null differ
diff --git a/test/repositories/hg/.hg/store/fncache b/test/repositories/hg/.hg/store/fncache
deleted file mode 100644
index f44e7d8b..00000000
--- a/test/repositories/hg/.hg/store/fncache
+++ /dev/null
@@ -1,3 +0,0 @@
-data/helloworld.c.i
-data/makefile.i
-data/README.i
diff --git a/test/repositories/hg/.hg/store/undo b/test/repositories/hg/.hg/store/undo
deleted file mode 100644
index b5a3a6aa..00000000
Binary files a/test/repositories/hg/.hg/store/undo and /dev/null differ
diff --git a/test/repositories/hg/.hg/undo.branch b/test/repositories/hg/.hg/undo.branch
deleted file mode 100644
index 331d858c..00000000
--- a/test/repositories/hg/.hg/undo.branch
+++ /dev/null
@@ -1 +0,0 @@
-default
\ No newline at end of file
diff --git a/test/repositories/hg/.hg/undo.dirstate b/test/repositories/hg/.hg/undo.dirstate
deleted file mode 100644
index 173ce74d..00000000
Binary files a/test/repositories/hg/.hg/undo.dirstate and /dev/null differ
diff --git a/test/repositories/hg_dupe_delete.tgz b/test/repositories/hg_dupe_delete.tgz
new file mode 100644
index 00000000..61b3ddd7
Binary files /dev/null and b/test/repositories/hg_dupe_delete.tgz differ
diff --git a/test/repositories/hg_walk.tgz b/test/repositories/hg_walk.tgz
new file mode 100644
index 00000000..b7cdf274
Binary files /dev/null and b/test/repositories/hg_walk.tgz differ
diff --git a/test/repositories/hg_walk/.hg/00changelog.i b/test/repositories/hg_walk/.hg/00changelog.i
deleted file mode 100644
index d3a83110..00000000
Binary files a/test/repositories/hg_walk/.hg/00changelog.i and /dev/null differ
diff --git a/test/repositories/hg_walk/.hg/branch b/test/repositories/hg_walk/.hg/branch
deleted file mode 100644
index 4ad96d51..00000000
--- a/test/repositories/hg_walk/.hg/branch
+++ /dev/null
@@ -1 +0,0 @@
-default
diff --git a/test/repositories/hg_walk/.hg/branch.cache b/test/repositories/hg_walk/.hg/branch.cache
deleted file mode 100644
index 2ad19474..00000000
--- a/test/repositories/hg_walk/.hg/branch.cache
+++ /dev/null
@@ -1,2 +0,0 @@
-8daa1aefa228d3ee5f9a0f685d696826e88266fb 6
-8daa1aefa228d3ee5f9a0f685d696826e88266fb default
diff --git a/test/repositories/hg_walk/.hg/dirstate b/test/repositories/hg_walk/.hg/dirstate
deleted file mode 100644
index d1e8f54b..00000000
Binary files a/test/repositories/hg_walk/.hg/dirstate and /dev/null differ
diff --git a/test/repositories/hg_walk/.hg/merge/69e27356ef629022720d868ab0c0e3394775b6c1 b/test/repositories/hg_walk/.hg/merge/69e27356ef629022720d868ab0c0e3394775b6c1
deleted file mode 100644
index 3cc58df8..00000000
--- a/test/repositories/hg_walk/.hg/merge/69e27356ef629022720d868ab0c0e3394775b6c1
+++ /dev/null
@@ -1 +0,0 @@
-C
diff --git a/test/repositories/hg_walk/.hg/merge/state b/test/repositories/hg_walk/.hg/merge/state
deleted file mode 100644
index 07ff7881..00000000
Binary files a/test/repositories/hg_walk/.hg/merge/state and /dev/null differ
diff --git a/test/repositories/hg_walk/.hg/requires b/test/repositories/hg_walk/.hg/requires
deleted file mode 100644
index 57601b5e..00000000
--- a/test/repositories/hg_walk/.hg/requires
+++ /dev/null
@@ -1,2 +0,0 @@
-revlogv1
-store
diff --git a/test/repositories/hg_walk/.hg/store/00changelog.i b/test/repositories/hg_walk/.hg/store/00changelog.i
deleted file mode 100644
index 18c3192b..00000000
Binary files a/test/repositories/hg_walk/.hg/store/00changelog.i and /dev/null differ
diff --git a/test/repositories/hg_walk/.hg/store/00manifest.i b/test/repositories/hg_walk/.hg/store/00manifest.i
deleted file mode 100644
index 38746052..00000000
Binary files a/test/repositories/hg_walk/.hg/store/00manifest.i and /dev/null differ
diff --git a/test/repositories/hg_walk/.hg/store/data/_r_e_a_d_m_e.i b/test/repositories/hg_walk/.hg/store/data/_r_e_a_d_m_e.i
deleted file mode 100644
index 0f0c4c30..00000000
Binary files a/test/repositories/hg_walk/.hg/store/data/_r_e_a_d_m_e.i and /dev/null differ
diff --git a/test/repositories/hg_walk/.hg/store/fncache b/test/repositories/hg_walk/.hg/store/fncache
deleted file mode 100644
index fb1271bb..00000000
--- a/test/repositories/hg_walk/.hg/store/fncache
+++ /dev/null
@@ -1 +0,0 @@
-data/README.i
diff --git a/test/repositories/hg_walk/.hg/store/undo b/test/repositories/hg_walk/.hg/store/undo
deleted file mode 100644
index dee7b931..00000000
Binary files a/test/repositories/hg_walk/.hg/store/undo and /dev/null differ
diff --git a/test/repositories/hg_walk/.hg/undo.branch b/test/repositories/hg_walk/.hg/undo.branch
deleted file mode 100644
index 331d858c..00000000
--- a/test/repositories/hg_walk/.hg/undo.branch
+++ /dev/null
@@ -1 +0,0 @@
-default
\ No newline at end of file
diff --git a/test/repositories/hg_walk/.hg/undo.dirstate b/test/repositories/hg_walk/.hg/undo.dirstate
deleted file mode 100644
index 867d35c1..00000000
Binary files a/test/repositories/hg_walk/.hg/undo.dirstate and /dev/null differ
diff --git a/test/repositories/hg_walk/README b/test/repositories/hg_walk/README
deleted file mode 100644
index 17848105..00000000
--- a/test/repositories/hg_walk/README
+++ /dev/null
@@ -1 +0,0 @@
-D
diff --git a/test/repositories/hg_with_invalid_encoding.tgz b/test/repositories/hg_with_invalid_encoding.tgz
new file mode 100644
index 00000000..9695948e
Binary files /dev/null and b/test/repositories/hg_with_invalid_encoding.tgz differ
diff --git a/test/repositories/svn/hooks/pre-revprop-change b/test/repositories/svn/hooks/pre-revprop-change
new file mode 100755
index 00000000..4768dd4c
--- /dev/null
+++ b/test/repositories/svn/hooks/pre-revprop-change
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# PRE-REVPROP-CHANGE HOOK
+#
+# The pre-revprop-change hook is invoked before a revision property
+# is added, modified or deleted. Subversion runs this hook by invoking
+# a program (script, executable, binary, etc.) named 'pre-revprop-change'
+# (for which this file is a template), with the following ordered
+# arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] REVISION (the revision being tweaked)
+# [3] USER (the username of the person tweaking the property)
+# [4] PROPNAME (the property being set on the revision)
+# [5] ACTION (the property is being 'A'dded, 'M'odified, or 'D'eleted)
+#
+# [STDIN] PROPVAL ** the new property value is passed via STDIN.
+#
+# If the hook program exits with success, the propchange happens; but
+# if it exits with failure (non-zero), the propchange doesn't happen.
+# The hook program can use the 'svnlook' utility to examine the
+# existing value of the revision property.
+#
+# WARNING: unlike other hooks, this hook MUST exist for revision
+# properties to be changed. If the hook does not exist, Subversion
+# will behave as if the hook were present, but failed. The reason
+# for this is that revision properties are UNVERSIONED, meaning that
+# a successful propchange is destructive; the old value is gone
+# forever. We recommend the hook back up the old value somewhere.
+#
+# On a Unix system, the normal procedure is to have 'pre-revprop-change'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'pre-revprop-change' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-revprop-change.bat' or 'pre-revprop-change.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+REV="$2"
+USER="$3"
+PROPNAME="$4"
+ACTION="$5"
+
+if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
+if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:date" ]; then exit 0; fi
+
+echo "Only svn:log and svn:date revision properties can be changed" >&2
+exit 1
diff --git a/test/repositories/svn_empty/README.txt b/test/repositories/svn_empty/README.txt
new file mode 100644
index 00000000..9935818a
--- /dev/null
+++ b/test/repositories/svn_empty/README.txt
@@ -0,0 +1,5 @@
+This is a Subversion repository; use the 'svnadmin' and 'svnlook'
+tools to examine it. Do not add, delete, or modify files here
+unless you know how to avoid corrupting the repository.
+
+Visit http://subversion.apache.org/ for more information.
diff --git a/test/repositories/svn_empty/conf/authz b/test/repositories/svn_empty/conf/authz
new file mode 100644
index 00000000..0b9a4107
--- /dev/null
+++ b/test/repositories/svn_empty/conf/authz
@@ -0,0 +1,32 @@
+### This file is an example authorization file for svnserve.
+### Its format is identical to that of mod_authz_svn authorization
+### files.
+### As shown below each section defines authorizations for the path and
+### (optional) repository specified by the section name.
+### The authorizations follow. An authorization line can refer to:
+### - a single user,
+### - a group of users defined in a special [groups] section,
+### - an alias defined in a special [aliases] section,
+### - all authenticated users, using the '$authenticated' token,
+### - only anonymous users, using the '$anonymous' token,
+### - anyone, using the '*' wildcard.
+###
+### A match can be inverted by prefixing the rule with '~'. Rules can
+### grant read ('r') access, read-write ('rw') access, or no access
+### ('').
+
+[aliases]
+# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average
+
+[groups]
+# harry_and_sally = harry,sally
+# harry_sally_and_joe = harry,sally,&joe
+
+# [/foo/bar]
+# harry = rw
+# &joe = r
+# * =
+
+# [repository:/baz/fuz]
+# @harry_and_sally = rw
+# * = r
diff --git a/test/repositories/svn_empty/conf/hooks-env.tmpl b/test/repositories/svn_empty/conf/hooks-env.tmpl
new file mode 100644
index 00000000..ee965c31
--- /dev/null
+++ b/test/repositories/svn_empty/conf/hooks-env.tmpl
@@ -0,0 +1,19 @@
+### This file is an example hook script environment configuration file.
+### Hook scripts run in an empty environment by default.
+### As shown below each section defines environment variables for a
+### particular hook script. The [default] section defines environment
+### variables for all hook scripts, unless overridden by a hook-specific
+### section.
+
+### This example configures a UTF-8 locale for all hook scripts, so that
+### special characters, such as umlauts, may be printed to stderr.
+### If UTF-8 is used with a mod_dav_svn server, the SVNUseUTF8 option must
+### also be set to 'yes' in httpd.conf.
+### With svnserve, the LANG environment variable of the svnserve process
+### must be set to the same value as given here.
+[default]
+LANG = en_US.UTF-8
+
+### This sets the PATH environment variable for the pre-commit hook.
+[pre-commit]
+PATH = /usr/local/bin:/usr/bin:/usr/sbin
diff --git a/test/repositories/svn_empty/conf/passwd b/test/repositories/svn_empty/conf/passwd
new file mode 100644
index 00000000..ecaa08dc
--- /dev/null
+++ b/test/repositories/svn_empty/conf/passwd
@@ -0,0 +1,8 @@
+### This file is an example password file for svnserve.
+### Its format is similar to that of svnserve.conf. As shown in the
+### example below it contains one section labelled [users].
+### The name and password for each user follow, one account per line.
+
+[users]
+# harry = harryssecret
+# sally = sallyssecret
diff --git a/test/repositories/svn_empty/conf/svnserve.conf b/test/repositories/svn_empty/conf/svnserve.conf
new file mode 100644
index 00000000..a1eb6c8f
--- /dev/null
+++ b/test/repositories/svn_empty/conf/svnserve.conf
@@ -0,0 +1,76 @@
+### This file controls the configuration of the svnserve daemon, if you
+### use it to allow access to this repository. (If you only allow
+### access through http: and/or file: URLs, then this file is
+### irrelevant.)
+
+### Visit http://subversion.apache.org/ for more information.
+
+[general]
+### The anon-access and auth-access options control access to the
+### repository for unauthenticated (a.k.a. anonymous) users and
+### authenticated users, respectively.
+### Valid values are "write", "read", and "none".
+### Setting the value to "none" prohibits both reading and writing;
+### "read" allows read-only access, and "write" allows complete
+### read/write access to the repository.
+### The sample settings below are the defaults and specify that anonymous
+### users have read-only access to the repository, while authenticated
+### users have read and write access to the repository.
+# anon-access = read
+# auth-access = write
+### The password-db option controls the location of the password
+### database file. Unless you specify a path starting with a /,
+### the file's location is relative to the directory containing
+### this configuration file.
+### If SASL is enabled (see below), this file will NOT be used.
+### Uncomment the line below to use the default password file.
+# password-db = passwd
+### The authz-db option controls the location of the authorization
+### rules for path-based access control. Unless you specify a path
+### starting with a /, the file's location is relative to the
+### directory containing this file. The specified path may be a
+### repository relative URL (^/) or an absolute file:// URL to a text
+### file in a Subversion repository. If you don't specify an authz-db,
+### no path-based access control is done.
+### Uncomment the line below to use the default authorization file.
+# authz-db = authz
+### The groups-db option controls the location of the groups file.
+### Unless you specify a path starting with a /, the file's location is
+### relative to the directory containing this file. The specified path
+### may be a repository relative URL (^/) or an absolute file:// URL to a
+### text file in a Subversion repository.
+# groups-db = groups
+### This option specifies the authentication realm of the repository.
+### If two repositories have the same authentication realm, they should
+### have the same password database, and vice versa. The default realm
+### is repository's uuid.
+# realm = My First Repository
+### The force-username-case option causes svnserve to case-normalize
+### usernames before comparing them against the authorization rules in the
+### authz-db file configured above. Valid values are "upper" (to upper-
+### case the usernames), "lower" (to lowercase the usernames), and
+### "none" (to compare usernames as-is without case conversion, which
+### is the default behavior).
+# force-username-case = none
+### The hooks-env options specifies a path to the hook script environment
+### configuration file. This option overrides the per-repository default
+### and can be used to configure the hook script environment for multiple
+### repositories in a single file, if an absolute path is specified.
+### Unless you specify an absolute path, the file's location is relative
+### to the directory containing this file.
+# hooks-env = hooks-env
+
+[sasl]
+### This option specifies whether you want to use the Cyrus SASL
+### library for authentication. Default is false.
+### This section will be ignored if svnserve is not built with Cyrus
+### SASL support; to check, run 'svnserve --version' and look for a line
+### reading 'Cyrus SASL authentication is available.'
+# use-sasl = true
+### These options specify the desired strength of the security layer
+### that you want SASL to provide. 0 means no encryption, 1 means
+### integrity-checking only, values larger than 1 are correlated
+### to the effective key length for encryption (e.g. 128 means 128-bit
+### encryption). The values below are the defaults.
+# min-encryption = 0
+# max-encryption = 256
diff --git a/test/repositories/svn_empty/db/current b/test/repositories/svn_empty/db/current
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/repositories/svn_empty/db/current
@@ -0,0 +1 @@
+0
diff --git a/test/repositories/svn_empty/db/format b/test/repositories/svn_empty/db/format
new file mode 100644
index 00000000..3fc1fa47
--- /dev/null
+++ b/test/repositories/svn_empty/db/format
@@ -0,0 +1,2 @@
+6
+layout sharded 1000
diff --git a/test/repositories/svn_empty/db/fs-type b/test/repositories/svn_empty/db/fs-type
new file mode 100644
index 00000000..4fdd9531
--- /dev/null
+++ b/test/repositories/svn_empty/db/fs-type
@@ -0,0 +1 @@
+fsfs
diff --git a/test/repositories/svn_empty/db/fsfs.conf b/test/repositories/svn_empty/db/fsfs.conf
new file mode 100644
index 00000000..7c0bf9f7
--- /dev/null
+++ b/test/repositories/svn_empty/db/fsfs.conf
@@ -0,0 +1,125 @@
+### This file controls the configuration of the FSFS filesystem.
+
+[memcached-servers]
+### These options name memcached servers used to cache internal FSFS
+### data. See http://www.danga.com/memcached/ for more information on
+### memcached. To use memcached with FSFS, run one or more memcached
+### servers, and specify each of them as an option like so:
+# first-server = 127.0.0.1:11211
+# remote-memcached = mymemcached.corp.example.com:11212
+### The option name is ignored; the value is of the form HOST:PORT.
+### memcached servers can be shared between multiple repositories;
+### however, if you do this, you *must* ensure that repositories have
+### distinct UUIDs and paths, or else cached data from one repository
+### might be used by another accidentally. Note also that memcached has
+### no authentication for reads or writes, so you must ensure that your
+### memcached servers are only accessible by trusted users.
+
+[caches]
+### When a cache-related error occurs, normally Subversion ignores it
+### and continues, logging an error if the server is appropriately
+### configured (and ignoring it with file:// access). To make
+### Subversion never ignore cache errors, uncomment this line.
+# fail-stop = true
+
+[rep-sharing]
+### To conserve space, the filesystem can optionally avoid storing
+### duplicate representations. This comes at a slight cost in
+### performance, as maintaining a database of shared representations can
+### increase commit times. The space savings are dependent upon the size
+### of the repository, the number of objects it contains and the amount of
+### duplication between them, usually a function of the branching and
+### merging process.
+###
+### The following parameter enables rep-sharing in the repository. It can
+### be switched on and off at will, but for best space-saving results
+### should be enabled consistently over the life of the repository.
+### 'svnadmin verify' will check the rep-cache regardless of this setting.
+### rep-sharing is enabled by default.
+# enable-rep-sharing = true
+
+[deltification]
+### To conserve space, the filesystem stores data as differences against
+### existing representations. This comes at a slight cost in performance,
+### as calculating differences can increase commit times. Reading data
+### will also create higher CPU load and the data will be fragmented.
+### Since deltification tends to save significant amounts of disk space,
+### the overall I/O load can actually be lower.
+###
+### The options in this section allow for tuning the deltification
+### strategy. Their effects on data size and server performance may vary
+### from one repository to another. Versions prior to 1.8 will ignore
+### this section.
+###
+### The following parameter enables deltification for directories. It can
+### be switched on and off at will, but for best space-saving results
+### should be enabled consistently over the life of the repository.
+### Repositories containing large directories will benefit greatly.
+### In rarely read repositories, the I/O overhead may be significant as
+### cache hit rates will most likely be low
+### directory deltification is disabled by default.
+# enable-dir-deltification = false
+###
+### The following parameter enables deltification for properties on files
+### and directories. Overall, this is a minor tuning option but can save
+### some disk space if you merge frequently or frequently change node
+### properties. You should not activate this if rep-sharing has been
+### disabled because this may result in a net increase in repository size.
+### property deltification is disabled by default.
+# enable-props-deltification = false
+###
+### During commit, the server may need to walk the whole change history of
+### of a given node to find a suitable deltification base. This linear
+### process can impact commit times, svnadmin load and similar operations.
+### This setting limits the depth of the deltification history. If the
+### threshold has been reached, the node will be stored as fulltext and a
+### new deltification history begins.
+### Note, this is unrelated to svn log.
+### Very large values rarely provide significant additional savings but
+### can impact performance greatly - in particular if directory
+### deltification has been activated. Very small values may be useful in
+### repositories that are dominated by large, changing binaries.
+### Should be a power of two minus 1. A value of 0 will effectively
+### disable deltification.
+### For 1.8, the default value is 1023; earlier versions have no limit.
+# max-deltification-walk = 1023
+###
+### The skip-delta scheme used by FSFS tends to repeatably store redundant
+### delta information where a simple delta against the latest version is
+### often smaller. By default, 1.8+ will therefore use skip deltas only
+### after the linear chain of deltas has grown beyond the threshold
+### specified by this setting.
+### Values up to 64 can result in some reduction in repository size for
+### the cost of quickly increasing I/O and CPU costs. Similarly, smaller
+### numbers can reduce those costs at the cost of more disk space. For
+### rarely read repositories or those containing larger binaries, this may
+### present a better trade-off.
+### Should be a power of two. A value of 1 or smaller will cause the
+### exclusive use of skip-deltas (as in pre-1.8).
+### For 1.8, the default value is 16; earlier versions use 1.
+# max-linear-deltification = 16
+
+[packed-revprops]
+### This parameter controls the size (in kBytes) of packed revprop files.
+### Revprops of consecutive revisions will be concatenated into a single
+### file up to but not exceeding the threshold given here. However, each
+### pack file may be much smaller and revprops of a single revision may be
+### much larger than the limit set here. The threshold will be applied
+### before optional compression takes place.
+### Large values will reduce disk space usage at the expense of increased
+### latency and CPU usage reading and changing individual revprops. They
+### become an advantage when revprop caching has been enabled because a
+### lot of data can be read in one go. Values smaller than 4 kByte will
+### not improve latency any further and quickly render revprop packing
+### ineffective.
+### revprop-pack-size is 64 kBytes by default for non-compressed revprop
+### pack files and 256 kBytes when compression has been enabled.
+# revprop-pack-size = 64
+###
+### To save disk space, packed revprop files may be compressed. Standard
+### revprops tend to allow for very effective compression. Reading and
+### even more so writing, become significantly more CPU intensive. With
+### revprop caching enabled, the overhead can be offset by reduced I/O
+### unless you often modify revprops after packing.
+### Compressing packed revprops is disabled by default.
+# compress-packed-revprops = false
diff --git a/test/repositories/svn_empty/db/min-unpacked-rev b/test/repositories/svn_empty/db/min-unpacked-rev
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/repositories/svn_empty/db/min-unpacked-rev
@@ -0,0 +1 @@
+0
diff --git a/test/repositories/svn_empty/db/revprops/0/0 b/test/repositories/svn_empty/db/revprops/0/0
new file mode 100644
index 00000000..eca610de
--- /dev/null
+++ b/test/repositories/svn_empty/db/revprops/0/0
@@ -0,0 +1,5 @@
+K 8
+svn:date
+V 27
+2017-05-22T10:06:32.301179Z
+END
diff --git a/test/repositories/svn_empty/db/revs/0/0 b/test/repositories/svn_empty/db/revs/0/0
new file mode 100644
index 00000000..10f5c45f
--- /dev/null
+++ b/test/repositories/svn_empty/db/revs/0/0
@@ -0,0 +1,11 @@
+PLAIN
+END
+ENDREP
+id: 0.0.r0/17
+type: dir
+count: 0
+text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e
+cpath: /
+
+
+17 107
diff --git a/test/repositories/svn_empty/db/txn-current b/test/repositories/svn_empty/db/txn-current
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/repositories/svn_empty/db/txn-current
@@ -0,0 +1 @@
+0
diff --git a/test/repositories/svn_empty/db/txn-current-lock b/test/repositories/svn_empty/db/txn-current-lock
new file mode 100644
index 00000000..e69de29b
diff --git a/test/repositories/svn_empty/db/uuid b/test/repositories/svn_empty/db/uuid
new file mode 100644
index 00000000..49845685
--- /dev/null
+++ b/test/repositories/svn_empty/db/uuid
@@ -0,0 +1 @@
+9776d4a3-6c79-43be-aaa4-b7d710814ae4
diff --git a/test/repositories/svn_empty/db/write-lock b/test/repositories/svn_empty/db/write-lock
new file mode 100644
index 00000000..e69de29b
diff --git a/test/repositories/svn_empty/format b/test/repositories/svn_empty/format
new file mode 100644
index 00000000..7ed6ff82
--- /dev/null
+++ b/test/repositories/svn_empty/format
@@ -0,0 +1 @@
+5
diff --git a/test/repositories/svn_empty/hooks/post-commit.tmpl b/test/repositories/svn_empty/hooks/post-commit.tmpl
new file mode 100755
index 00000000..16b7b545
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/post-commit.tmpl
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+# POST-COMMIT HOOK
+#
+# The post-commit hook is invoked after a commit. Subversion runs
+# this hook by invoking a program (script, executable, binary, etc.)
+# named 'post-commit' (for which this file is a template) with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] REV (the number of the revision just committed)
+# [3] TXN-NAME (the name of the transaction that has become REV)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# Because the commit has already completed and cannot be undone,
+# the exit code of the hook program is ignored. The hook program
+# can use the 'svnlook' utility to help it examine the
+# newly-committed tree.
+#
+# On a Unix system, the normal procedure is to have 'post-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-commit.bat' or 'post-commit.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# /usr/share/subversion/hook-scripts, and in the repository at
+# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and
+# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+REV="$2"
+TXN_NAME="$3"
+
+"$REPOS"/hooks/mailer.py commit "$REPOS" $REV "$REPOS"/mailer.conf
diff --git a/test/repositories/svn_empty/hooks/post-lock.tmpl b/test/repositories/svn_empty/hooks/post-lock.tmpl
new file mode 100755
index 00000000..beae9d70
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/post-lock.tmpl
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# POST-LOCK HOOK
+#
+# The post-lock hook is run after a path is locked. Subversion runs
+# this hook by invoking a program (script, executable, binary, etc.)
+# named 'post-lock' (for which this file is a template) with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] USER (the user who created the lock)
+#
+# The paths that were just locked are passed to the hook via STDIN (as
+# of Subversion 1.2, only one path is passed per invocation, but the
+# plan is to pass all locked paths at once, so the hook program
+# should be written accordingly).
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# Because the lock has already been created and cannot be undone,
+# the exit code of the hook program is ignored. The hook program
+# can use the 'svnlook' utility to help it examine the
+# newly-created lock.
+#
+# On a Unix system, the normal procedure is to have 'post-lock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-lock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-lock.bat' or 'post-lock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+USER="$2"
+
+# Send email to interested parties, let them know a lock was created:
+"$REPOS"/hooks/mailer.py lock \
+ "$REPOS" "$USER" "$REPOS"/hooks/mailer.conf
diff --git a/test/repositories/svn_empty/hooks/post-revprop-change.tmpl b/test/repositories/svn_empty/hooks/post-revprop-change.tmpl
new file mode 100755
index 00000000..cf7aedda
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/post-revprop-change.tmpl
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# POST-REVPROP-CHANGE HOOK
+#
+# The post-revprop-change hook is invoked after a revision property
+# has been added, modified or deleted. Subversion runs this hook by
+# invoking a program (script, executable, binary, etc.) named
+# 'post-revprop-change' (for which this file is a template), with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] REV (the revision that was tweaked)
+# [3] USER (the username of the person tweaking the property)
+# [4] PROPNAME (the property that was changed)
+# [5] ACTION (the property was 'A'dded, 'M'odified, or 'D'eleted)
+#
+# [STDIN] PROPVAL ** the old property value is passed via STDIN.
+#
+# Because the propchange has already completed and cannot be undone,
+# the exit code of the hook program is ignored. The hook program
+# can use the 'svnlook' utility to help it examine the
+# new property value.
+#
+# On a Unix system, the normal procedure is to have 'post-revprop-change'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-revprop-change' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-revprop-change.bat' or 'post-revprop-change.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# /usr/share/subversion/hook-scripts, and in the repository at
+# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and
+# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+REV="$2"
+USER="$3"
+PROPNAME="$4"
+ACTION="$5"
+
+"$REPOS"/hooks/mailer.py propchange2 "$REPOS" $REV \
+ "$USER" "$PROPNAME" "$ACTION" "$REPOS"/hooks/mailer.conf
diff --git a/test/repositories/svn_empty/hooks/post-unlock.tmpl b/test/repositories/svn_empty/hooks/post-unlock.tmpl
new file mode 100755
index 00000000..277569f9
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/post-unlock.tmpl
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# POST-UNLOCK HOOK
+#
+# The post-unlock hook runs after a path is unlocked. Subversion runs
+# this hook by invoking a program (script, executable, binary, etc.)
+# named 'post-unlock' (for which this file is a template) with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] USER (the user who destroyed the lock)
+#
+# The paths that were just unlocked are passed to the hook via STDIN
+# (as of Subversion 1.2, only one path is passed per invocation, but
+# the plan is to pass all unlocked paths at once, so the hook program
+# should be written accordingly).
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# Because the lock has already been destroyed and cannot be undone,
+# the exit code of the hook program is ignored.
+#
+# On a Unix system, the normal procedure is to have 'post-unlock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-unlock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-unlock.bat' or 'post-unlock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+USER="$2"
+
+# Send email to interested parties, let them know a lock was removed:
+"$REPOS"/hooks/mailer.py unlock \
+ "$REPOS" "$USER" "$REPOS"/hooks/mailer.conf
diff --git a/test/repositories/svn_empty/hooks/pre-commit.tmpl b/test/repositories/svn_empty/hooks/pre-commit.tmpl
new file mode 100755
index 00000000..44c24b93
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/pre-commit.tmpl
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+# PRE-COMMIT HOOK
+#
+# The pre-commit hook is invoked before a Subversion txn is
+# committed. Subversion runs this hook by invoking a program
+# (script, executable, binary, etc.) named 'pre-commit' (for which
+# this file is a template), with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] TXN-NAME (the name of the txn about to be committed)
+#
+# [STDIN] LOCK-TOKENS ** the lock tokens are passed via STDIN.
+#
+# If STDIN contains the line "LOCK-TOKENS:\n" (the "\n" denotes a
+# single newline), the lines following it are the lock tokens for
+# this commit. The end of the list is marked by a line containing
+# only a newline character.
+#
+# Each lock token line consists of a URI-escaped path, followed
+# by the separator character '|', followed by the lock token string,
+# followed by a newline.
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the txn is committed; but
+# if it exits with failure (non-zero), the txn is aborted, no commit
+# takes place, and STDERR is returned to the client. The hook
+# program can use the 'svnlook' utility to help it examine the txn.
+#
+# On a Unix system, the normal procedure is to have 'pre-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT ***
+# *** FOR REVISION PROPERTIES (like svn:log or svn:author). ***
+#
+# This is why we recommend using the read-only 'svnlook' utility.
+# In the future, Subversion may enforce the rule that pre-commit
+# hooks should not modify the versioned data in txns, or else come
+# up with a mechanism to make it safe to do so (by informing the
+# committing client of the changes). However, right now neither
+# mechanism is implemented, so hook writers just have to be careful.
+#
+# Note that 'pre-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-commit.bat' or 'pre-commit.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# /usr/share/subversion/hook-scripts, and in the repository at
+# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and
+# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+TXN="$2"
+
+# Make sure that the log message contains some text.
+SVNLOOK=/usr/bin/svnlook
+$SVNLOOK log -t "$TXN" "$REPOS" | \
+ grep "[a-zA-Z0-9]" > /dev/null || exit 1
+
+# Exit on all errors.
+set -e
+
+# Check that the author of this commit has the rights to perform
+# the commit on the files and directories being modified.
+"$REPOS"/hooks/commit-access-control.pl "$REPOS" $TXN \
+ "$REPOS"/hooks/commit-access-control.cfg
+
+# All checks passed, so allow the commit.
+exit 0
diff --git a/test/repositories/svn_empty/hooks/pre-lock.tmpl b/test/repositories/svn_empty/hooks/pre-lock.tmpl
new file mode 100755
index 00000000..9fb84425
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/pre-lock.tmpl
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# PRE-LOCK HOOK
+#
+# The pre-lock hook is invoked before an exclusive lock is
+# created. Subversion runs this hook by invoking a program
+# (script, executable, binary, etc.) named 'pre-lock' (for which
+# this file is a template), with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] PATH (the path in the repository about to be locked)
+# [3] USER (the user creating the lock)
+# [4] COMMENT (the comment of the lock)
+# [5] STEAL-LOCK (1 if the user is trying to steal the lock, else 0)
+#
+# If the hook program outputs anything on stdout, the output string will
+# be used as the lock token for this lock operation. If you choose to use
+# this feature, you must guarantee the tokens generated are unique across
+# the repository each time.
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the lock is created; but
+# if it exits with failure (non-zero), the lock action is aborted
+# and STDERR is returned to the client.
+
+# On a Unix system, the normal procedure is to have 'pre-lock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'pre-lock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-lock.bat' or 'pre-lock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+PATH="$2"
+USER="$3"
+COMMENT="$4"
+STEAL="$5"
+
+# If a lock exists and is owned by a different person, don't allow it
+# to be stolen (e.g., with 'svn lock --force ...').
+
+# (Maybe this script could send email to the lock owner?)
+SVNLOOK=/usr/bin/svnlook
+GREP=/bin/grep
+SED=/bin/sed
+
+LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \
+ $GREP '^Owner: ' | $SED 's/Owner: //'`
+
+# If we get no result from svnlook, there's no lock, allow the lock to
+# happen:
+if [ "$LOCK_OWNER" = "" ]; then
+ exit 0
+fi
+
+# If the person locking matches the lock's owner, allow the lock to
+# happen:
+if [ "$LOCK_OWNER" = "$USER" ]; then
+ exit 0
+fi
+
+# Otherwise, we've got an owner mismatch, so return failure:
+echo "Error: $PATH already locked by ${LOCK_OWNER}." 1>&2
+exit 1
diff --git a/test/repositories/svn_empty/hooks/pre-revprop-change.tmpl b/test/repositories/svn_empty/hooks/pre-revprop-change.tmpl
new file mode 100755
index 00000000..5a255285
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/pre-revprop-change.tmpl
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+# PRE-REVPROP-CHANGE HOOK
+#
+# The pre-revprop-change hook is invoked before a revision property
+# is added, modified or deleted. Subversion runs this hook by invoking
+# a program (script, executable, binary, etc.) named 'pre-revprop-change'
+# (for which this file is a template), with the following ordered
+# arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] REV (the revision being tweaked)
+# [3] USER (the username of the person tweaking the property)
+# [4] PROPNAME (the property being set on the revision)
+# [5] ACTION (the property is being 'A'dded, 'M'odified, or 'D'eleted)
+#
+# [STDIN] PROPVAL ** the new property value is passed via STDIN.
+#
+# If the hook program exits with success, the propchange happens; but
+# if it exits with failure (non-zero), the propchange doesn't happen.
+# The hook program can use the 'svnlook' utility to examine the
+# existing value of the revision property.
+#
+# WARNING: unlike other hooks, this hook MUST exist for revision
+# properties to be changed. If the hook does not exist, Subversion
+# will behave as if the hook were present, but failed. The reason
+# for this is that revision properties are UNVERSIONED, meaning that
+# a successful propchange is destructive; the old value is gone
+# forever. We recommend the hook back up the old value somewhere.
+#
+# On a Unix system, the normal procedure is to have 'pre-revprop-change'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'pre-revprop-change' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-revprop-change.bat' or 'pre-revprop-change.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# /usr/share/subversion/hook-scripts, and in the repository at
+# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and
+# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+REV="$2"
+USER="$3"
+PROPNAME="$4"
+ACTION="$5"
+
+if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
+
+echo "Changing revision properties other than svn:log is prohibited" >&2
+exit 1
diff --git a/test/repositories/svn_empty/hooks/pre-unlock.tmpl b/test/repositories/svn_empty/hooks/pre-unlock.tmpl
new file mode 100755
index 00000000..6f326fbd
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/pre-unlock.tmpl
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# PRE-UNLOCK HOOK
+#
+# The pre-unlock hook is invoked before an exclusive lock is
+# destroyed. Subversion runs this hook by invoking a program
+# (script, executable, binary, etc.) named 'pre-unlock' (for which
+# this file is a template), with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] PATH (the path in the repository about to be unlocked)
+# [3] USER (the user destroying the lock)
+# [4] TOKEN (the lock token to be destroyed)
+# [5] BREAK-UNLOCK (1 if the user is breaking the lock, else 0)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the lock is destroyed; but
+# if it exits with failure (non-zero), the unlock action is aborted
+# and STDERR is returned to the client.
+
+# On a Unix system, the normal procedure is to have 'pre-unlock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'pre-unlock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-unlock.bat' or 'pre-unlock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+PATH="$2"
+USER="$3"
+TOKEN="$4"
+BREAK="$5"
+
+# If a lock is owned by a different person, don't allow it be broken.
+# (Maybe this script could send email to the lock owner?)
+
+SVNLOOK=/usr/bin/svnlook
+GREP=/bin/grep
+SED=/bin/sed
+
+LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \
+ $GREP '^Owner: ' | $SED 's/Owner: //'`
+
+# If we get no result from svnlook, there's no lock, return success:
+if [ "$LOCK_OWNER" = "" ]; then
+ exit 0
+fi
+
+# If the person unlocking matches the lock's owner, return success:
+if [ "$LOCK_OWNER" = "$USER" ]; then
+ exit 0
+fi
+
+# Otherwise, we've got an owner mismatch, so return failure:
+echo "Error: $PATH locked by ${LOCK_OWNER}." 1>&2
+exit 1
diff --git a/test/repositories/svn_empty/hooks/start-commit.tmpl b/test/repositories/svn_empty/hooks/start-commit.tmpl
new file mode 100755
index 00000000..5a1e6f85
--- /dev/null
+++ b/test/repositories/svn_empty/hooks/start-commit.tmpl
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+# START-COMMIT HOOK
+#
+# The start-commit hook is invoked immediately after a Subversion txn is
+# created and populated with initial revprops in the process of doing a
+# commit. Subversion runs this hook by invoking a program (script,
+# executable, binary, etc.) named 'start-commit' (for which this file
+# is a template) with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] USER (the authenticated user attempting to commit)
+# [3] CAPABILITIES (a colon-separated list of capabilities reported
+# by the client; see note below)
+# [4] TXN-NAME (the name of the commit txn just created)
+#
+# Note: The CAPABILITIES parameter is new in Subversion 1.5, and 1.5
+# clients will typically report at least the "mergeinfo" capability.
+# If there are other capabilities, then the list is colon-separated,
+# e.g.: "mergeinfo:some-other-capability" (the order is undefined).
+#
+# Note: The TXN-NAME parameter is new in Subversion 1.8. Prior to version
+# 1.8, the start-commit hook was invoked before the commit txn was even
+# created, so the ability to inspect the commit txn and its metadata from
+# within the start-commit hook was not possible.
+#
+# The list is self-reported by the client. Therefore, you should not
+# make security assumptions based on the capabilities list, nor should
+# you assume that clients reliably report every capability they have.
+#
+# The working directory for this hook program's invocation is undefined,
+# so the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the commit continues; but
+# if it exits with failure (non-zero), the commit is stopped before
+# a Subversion txn is created, and STDERR is returned to the client.
+#
+# On a Unix system, the normal procedure is to have 'start-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'start-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'start-commit.bat' or 'start-commit.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# /usr/share/subversion/hook-scripts, and in the repository at
+# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and
+# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+USER="$2"
+
+# Exit on all errors.
+set -e
+
+"$REPOS"/hooks/commit-allower.pl --repository "$REPOS" --user "$USER"
+"$REPOS"/hooks/special-auth-check.py --user "$USER" --auth-level 3
+
+# All checks passed, so allow the commit.
+exit 0
diff --git a/test/repositories/svn_empty/locks/db-logs.lock b/test/repositories/svn_empty/locks/db-logs.lock
new file mode 100644
index 00000000..20dd6369
--- /dev/null
+++ b/test/repositories/svn_empty/locks/db-logs.lock
@@ -0,0 +1,3 @@
+This file is not used by Subversion 1.3.x or later.
+However, its existence is required for compatibility with
+Subversion 1.2.x or earlier.
diff --git a/test/repositories/svn_empty/locks/db.lock b/test/repositories/svn_empty/locks/db.lock
new file mode 100644
index 00000000..20dd6369
--- /dev/null
+++ b/test/repositories/svn_empty/locks/db.lock
@@ -0,0 +1,3 @@
+This file is not used by Subversion 1.3.x or later.
+However, its existence is required for compatibility with
+Subversion 1.2.x or earlier.
diff --git a/test/repositories/svn_with_branching.tgz b/test/repositories/svn_with_branching.tgz
new file mode 100644
index 00000000..5e3ee625
Binary files /dev/null and b/test/repositories/svn_with_branching.tgz differ
diff --git a/test/repositories/svn_with_invalid_encoding.tgz b/test/repositories/svn_with_invalid_encoding.tgz
new file mode 100644
index 00000000..15122bcc
Binary files /dev/null and b/test/repositories/svn_with_invalid_encoding.tgz differ
diff --git a/test/repositories/svn_with_tree_move/README.txt b/test/repositories/svn_with_tree_move/README.txt
new file mode 100644
index 00000000..3bf5a571
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/README.txt
@@ -0,0 +1,5 @@
+This is a Subversion repository; use the 'svnadmin' tool to examine
+it. Do not add, delete, or modify files here unless you know how
+to avoid corrupting the repository.
+
+Visit http://subversion.tigris.org/ for more information.
diff --git a/test/repositories/svn_with_tree_move/conf/authz b/test/repositories/svn_with_tree_move/conf/authz
new file mode 100644
index 00000000..78cb28e9
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/conf/authz
@@ -0,0 +1,21 @@
+### This file is an example authorization file for svnserve.
+### Its format is identical to that of mod_authz_svn authorization
+### files.
+### As shown below each section defines authorizations for the path and
+### (optional) repository specified by the section name.
+### The authorizations follow. An authorization line can refer to a
+### single user, to a group of users defined in a special [groups]
+### section, or to anyone using the '*' wildcard. Each definition can
+### grant read ('r') access, read-write ('rw') access, or no access
+### ('').
+
+[groups]
+# harry_and_sally = harry,sally
+
+# [/foo/bar]
+# harry = rw
+# * =
+
+# [repository:/baz/fuz]
+# @harry_and_sally = rw
+# * = r
diff --git a/test/repositories/svn_with_tree_move/conf/passwd b/test/repositories/svn_with_tree_move/conf/passwd
new file mode 100644
index 00000000..ecaa08dc
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/conf/passwd
@@ -0,0 +1,8 @@
+### This file is an example password file for svnserve.
+### Its format is similar to that of svnserve.conf. As shown in the
+### example below it contains one section labelled [users].
+### The name and password for each user follow, one account per line.
+
+[users]
+# harry = harryssecret
+# sally = sallyssecret
diff --git a/test/repositories/svn_with_tree_move/conf/svnserve.conf b/test/repositories/svn_with_tree_move/conf/svnserve.conf
new file mode 100644
index 00000000..b52bc5ab
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/conf/svnserve.conf
@@ -0,0 +1,30 @@
+### This file controls the configuration of the svnserve daemon, if you
+### use it to allow access to this repository. (If you only allow
+### access through http: and/or file: URLs, then this file is
+### irrelevant.)
+
+### Visit http://subversion.tigris.org/ for more information.
+
+[general]
+### These options control access to the repository for unauthenticated
+### and authenticated users. Valid values are "write", "read",
+### and "none". The sample settings below are the defaults.
+# anon-access = read
+# auth-access = write
+### The password-db option controls the location of the password
+### database file. Unless you specify a path starting with a /,
+### the file's location is relative to the conf directory.
+### Uncomment the line below to use the default password file.
+# password-db = passwd
+### The authz-db option controls the location of the authorization
+### rules for path-based access control. Unless you specify a path
+### starting with a /, the file's location is relative to the conf
+### directory. If you don't specify an authz-db, no path-based access
+### control is done.
+### Uncomment the line below to use the default authorization file.
+# authz-db = authz
+### This option specifies the authentication realm of the repository.
+### If two repositories have the same authentication realm, they should
+### have the same password database, and vice versa. The default realm
+### is repository's uuid.
+# realm = My First Repository
diff --git a/test/repositories/svn_with_tree_move/db/current b/test/repositories/svn_with_tree_move/db/current
new file mode 100644
index 00000000..394811d0
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/current
@@ -0,0 +1 @@
+2 5 2
diff --git a/test/repositories/svn_with_tree_move/db/format b/test/repositories/svn_with_tree_move/db/format
new file mode 100644
index 00000000..0cfbf088
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/format
@@ -0,0 +1 @@
+2
diff --git a/test/repositories/svn_with_tree_move/db/fs-type b/test/repositories/svn_with_tree_move/db/fs-type
new file mode 100644
index 00000000..4fdd9531
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/fs-type
@@ -0,0 +1 @@
+fsfs
diff --git a/test/repositories/svn_with_tree_move/db/revprops/0 b/test/repositories/svn_with_tree_move/db/revprops/0
new file mode 100644
index 00000000..1716bf45
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/revprops/0
@@ -0,0 +1,5 @@
+K 8
+svn:date
+V 27
+2009-02-12T23:29:46.930803Z
+END
diff --git a/test/repositories/svn_with_tree_move/db/revprops/1 b/test/repositories/svn_with_tree_move/db/revprops/1
new file mode 100644
index 00000000..296a9a0a
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/revprops/1
@@ -0,0 +1,13 @@
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2009-02-12T23:31:42.224055Z
+K 7
+svn:log
+V 16
+Initial Revision
+END
diff --git a/test/repositories/svn_with_tree_move/db/revprops/2 b/test/repositories/svn_with_tree_move/db/revprops/2
new file mode 100644
index 00000000..eed0157a
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/revprops/2
@@ -0,0 +1,13 @@
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2009-02-12T23:32:13.278922Z
+K 7
+svn:log
+V 22
+Move tree up one level
+END
diff --git a/test/repositories/svn_with_tree_move/db/revs/0 b/test/repositories/svn_with_tree_move/db/revs/0
new file mode 100644
index 00000000..10f5c45f
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/revs/0
@@ -0,0 +1,11 @@
+PLAIN
+END
+ENDREP
+id: 0.0.r0/17
+type: dir
+count: 0
+text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e
+cpath: /
+
+
+17 107
diff --git a/test/repositories/svn_with_tree_move/db/revs/1 b/test/repositories/svn_with_tree_move/db/revs/1
new file mode 100644
index 00000000..72b68a3e
Binary files /dev/null and b/test/repositories/svn_with_tree_move/db/revs/1 differ
diff --git a/test/repositories/svn_with_tree_move/db/revs/2 b/test/repositories/svn_with_tree_move/db/revs/2
new file mode 100644
index 00000000..61d2ca3e
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/revs/2
@@ -0,0 +1,44 @@
+PLAIN
+END
+ENDREP
+id: 1.0.r2/17
+type: dir
+pred: 1.0.r1/625
+count: 1
+text: 2 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e
+cpath: /all
+copyroot: 0 /
+
+id: 2.1.r2/141
+type: dir
+pred: 2.0.r1/452
+count: 1
+text: 1 405 34 34 421576a59ace312937c26664b844a879
+cpath: /myproject
+copyfrom: 1 /all/myproject
+
+PLAIN
+K 3
+all
+V 13
+dir 1.0.r2/17
+K 9
+myproject
+V 14
+dir 2.1.r2/141
+END
+ENDREP
+id: 0.0.r2/367
+type: dir
+pred: 0.0.r1/782
+count: 2
+text: 2 289 65 65 bc780155f14b1d1109add7e03a436d9f
+cpath: /
+copyroot: 0 /
+
+2.0.r1/452 delete false false /all/myproject
+
+2._0.t1-1 add false false /myproject
+1 /all/myproject
+
+367 493
diff --git a/test/repositories/svn_with_tree_move/db/uuid b/test/repositories/svn_with_tree_move/db/uuid
new file mode 100644
index 00000000..65ff7b93
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/db/uuid
@@ -0,0 +1 @@
+72ad96da-afe6-45cb-97a1-ca313a930b66
diff --git a/test/repositories/svn_with_tree_move/db/write-lock b/test/repositories/svn_with_tree_move/db/write-lock
new file mode 100644
index 00000000..e69de29b
diff --git a/test/repositories/svn_with_tree_move/format b/test/repositories/svn_with_tree_move/format
new file mode 100644
index 00000000..7ed6ff82
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/format
@@ -0,0 +1 @@
+5
diff --git a/test/repositories/svn_with_tree_move/hooks/post-commit.tmpl b/test/repositories/svn_with_tree_move/hooks/post-commit.tmpl
new file mode 100644
index 00000000..b8345c6f
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/post-commit.tmpl
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+# POST-COMMIT HOOK
+#
+# The post-commit hook is invoked after a commit. Subversion runs
+# this hook by invoking a program (script, executable, binary, etc.)
+# named 'post-commit' (for which this file is a template) with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] REV (the number of the revision just committed)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# Because the commit has already completed and cannot be undone,
+# the exit code of the hook program is ignored. The hook program
+# can use the 'svnlook' utility to help it examine the
+# newly-committed tree.
+#
+# On a Unix system, the normal procedure is to have 'post-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-commit.bat' or 'post-commit.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+REV="$2"
+
+commit-email.pl "$REPOS" "$REV" commit-watchers@example.org
+log-commit.py --repository "$REPOS" --revision "$REV"
diff --git a/test/repositories/svn_with_tree_move/hooks/post-lock.tmpl b/test/repositories/svn_with_tree_move/hooks/post-lock.tmpl
new file mode 100644
index 00000000..c779f11d
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/post-lock.tmpl
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# POST-LOCK HOOK
+#
+# The post-lock hook is run after a path is locked. Subversion runs
+# this hook by invoking a program (script, executable, binary, etc.)
+# named 'post-lock' (for which this file is a template) with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] USER (the user who created the lock)
+#
+# The paths that were just locked are passed to the hook via STDIN (as
+# of Subversion 1.2, only one path is passed per invocation, but the
+# plan is to pass all locked paths at once, so the hook program
+# should be written accordingly).
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# Because the lock has already been created and cannot be undone,
+# the exit code of the hook program is ignored. The hook program
+# can use the 'svnlook' utility to help it examine the
+# newly-created lock.
+#
+# On a Unix system, the normal procedure is to have 'post-lock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-lock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-lock.bat' or 'post-lock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+USER="$2"
+
+# Send email to interested parties, let them know a lock was created:
+mailer.py lock "$REPOS" "$USER" /path/to/mailer.conf
diff --git a/test/repositories/svn_with_tree_move/hooks/post-revprop-change.tmpl b/test/repositories/svn_with_tree_move/hooks/post-revprop-change.tmpl
new file mode 100644
index 00000000..2ed8b9ab
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/post-revprop-change.tmpl
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# POST-REVPROP-CHANGE HOOK
+#
+# The post-revprop-change hook is invoked after a revision property
+# has been added, modified or deleted. Subversion runs this hook by
+# invoking a program (script, executable, binary, etc.) named
+# 'post-revprop-change' (for which this file is a template), with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] REV (the revision that was tweaked)
+# [3] USER (the username of the person tweaking the property)
+# [4] PROPNAME (the property that was changed)
+# [5] ACTION (the property was 'A'dded, 'M'odified, or 'D'eleted)
+#
+# [STDIN] PROPVAL ** the old property value is passed via STDIN.
+#
+# Because the propchange has already completed and cannot be undone,
+# the exit code of the hook program is ignored. The hook program
+# can use the 'svnlook' utility to help it examine the
+# new property value.
+#
+# On a Unix system, the normal procedure is to have 'post-revprop-change'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-revprop-change' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-revprop-change.bat' or 'post-revprop-change.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+REV="$2"
+USER="$3"
+PROPNAME="$4"
+ACTION="$5"
+
+propchange-email.pl "$REPOS" "$REV" "$USER" "$PROPNAME" watchers@example.org
diff --git a/test/repositories/svn_with_tree_move/hooks/post-unlock.tmpl b/test/repositories/svn_with_tree_move/hooks/post-unlock.tmpl
new file mode 100644
index 00000000..ae95c4bd
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/post-unlock.tmpl
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# POST-UNLOCK HOOK
+#
+# The post-unlock hook runs after a path is unlocked. Subversion runs
+# this hook by invoking a program (script, executable, binary, etc.)
+# named 'post-unlock' (for which this file is a template) with the
+# following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] USER (the user who destroyed the lock)
+#
+# The paths that were just unlocked are passed to the hook via STDIN
+# (as of Subversion 1.2, only one path is passed per invocation, but
+# the plan is to pass all unlocked paths at once, so the hook program
+# should be written accordingly).
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# Because the lock has already been destroyed and cannot be undone,
+# the exit code of the hook program is ignored.
+#
+# On a Unix system, the normal procedure is to have 'post-unlock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'post-unlock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'post-unlock.bat' or 'post-unlock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+USER="$2"
+
+# Send email to interested parties, let them know a lock was removed:
+mailer.py unlock "$REPOS" "$USER" /path/to/mailer.conf
diff --git a/test/repositories/svn_with_tree_move/hooks/pre-commit.tmpl b/test/repositories/svn_with_tree_move/hooks/pre-commit.tmpl
new file mode 100644
index 00000000..7444c511
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/pre-commit.tmpl
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+# PRE-COMMIT HOOK
+#
+# The pre-commit hook is invoked before a Subversion txn is
+# committed. Subversion runs this hook by invoking a program
+# (script, executable, binary, etc.) named 'pre-commit' (for which
+# this file is a template), with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] TXN-NAME (the name of the txn about to be committed)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the txn is committed; but
+# if it exits with failure (non-zero), the txn is aborted, no commit
+# takes place, and STDERR is returned to the client. The hook
+# program can use the 'svnlook' utility to help it examine the txn.
+#
+# On a Unix system, the normal procedure is to have 'pre-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT ***
+# *** FOR REVISION PROPERTIES (like svn:log or svn:author). ***
+#
+# This is why we recommend using the read-only 'svnlook' utility.
+# In the future, Subversion may enforce the rule that pre-commit
+# hooks should not modify the versioned data in txns, or else come
+# up with a mechanism to make it safe to do so (by informing the
+# committing client of the changes). However, right now neither
+# mechanism is implemented, so hook writers just have to be careful.
+#
+# Note that 'pre-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-commit.bat' or 'pre-commit.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+TXN="$2"
+
+# Make sure that the log message contains some text.
+SVNLOOK=/opt/local/bin/svnlook
+$SVNLOOK log -t "$TXN" "$REPOS" | \
+ grep "[a-zA-Z0-9]" > /dev/null || exit 1
+
+# Check that the author of this commit has the rights to perform
+# the commit on the files and directories being modified.
+commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1
+
+# All checks passed, so allow the commit.
+exit 0
diff --git a/test/repositories/svn_with_tree_move/hooks/pre-lock.tmpl b/test/repositories/svn_with_tree_move/hooks/pre-lock.tmpl
new file mode 100644
index 00000000..020d1727
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/pre-lock.tmpl
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+# PRE-LOCK HOOK
+#
+# The pre-lock hook is invoked before an exclusive lock is
+# created. Subversion runs this hook by invoking a program
+# (script, executable, binary, etc.) named 'pre-lock' (for which
+# this file is a template), with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] PATH (the path in the repository about to be locked)
+# [3] USER (the user creating the lock)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the lock is created; but
+# if it exits with failure (non-zero), the lock action is aborted
+# and STDERR is returned to the client.
+
+# On a Unix system, the normal procedure is to have 'pre-lock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'pre-lock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-lock.bat' or 'pre-lock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+PATH="$2"
+USER="$3"
+
+# If a lock exists and is owned by a different person, don't allow it
+# to be stolen (e.g., with 'svn lock --force ...').
+
+# (Maybe this script could send email to the lock owner?)
+SVNLOOK=/opt/local/bin/svnlook
+GREP=/bin/grep
+SED=/bin/sed
+
+LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \
+ $GREP '^Owner: ' | $SED 's/Owner: //'`
+
+# If we get no result from svnlook, there's no lock, allow the lock to
+# happen:
+if [ "$LOCK_OWNER" = "" ]; then
+ exit 0
+fi
+
+# If the person locking matches the lock's owner, allow the lock to
+# happen:
+if [ "$LOCK_OWNER" = "$USER" ]; then
+ exit 0
+fi
+
+# Otherwise, we've got an owner mismatch, so return failure:
+echo "Error: $PATH already locked by ${LOCK_OWNER}." 1>&2
+exit 1
diff --git a/test/repositories/svn/hooks/pre-revprop-change.tmpl b/test/repositories/svn_with_tree_move/hooks/pre-revprop-change.tmpl
similarity index 100%
rename from test/repositories/svn/hooks/pre-revprop-change.tmpl
rename to test/repositories/svn_with_tree_move/hooks/pre-revprop-change.tmpl
diff --git a/test/repositories/svn_with_tree_move/hooks/pre-unlock.tmpl b/test/repositories/svn_with_tree_move/hooks/pre-unlock.tmpl
new file mode 100644
index 00000000..795f55f8
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/pre-unlock.tmpl
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# PRE-UNLOCK HOOK
+#
+# The pre-unlock hook is invoked before an exclusive lock is
+# destroyed. Subversion runs this hook by invoking a program
+# (script, executable, binary, etc.) named 'pre-unlock' (for which
+# this file is a template), with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] PATH (the path in the repository about to be unlocked)
+# [3] USER (the user destroying the lock)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the lock is destroyed; but
+# if it exits with failure (non-zero), the unlock action is aborted
+# and STDERR is returned to the client.
+
+# On a Unix system, the normal procedure is to have 'pre-unlock'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'pre-unlock' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'pre-unlock.bat' or 'pre-unlock.exe',
+# but the basic idea is the same.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter:
+
+REPOS="$1"
+PATH="$2"
+USER="$3"
+
+# If a lock is owned by a different person, don't allow it be broken.
+# (Maybe this script could send email to the lock owner?)
+
+SVNLOOK=/opt/local/bin/svnlook
+GREP=/bin/grep
+SED=/bin/sed
+
+LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \
+ $GREP '^Owner: ' | $SED 's/Owner: //'`
+
+# If we get no result from svnlook, there's no lock, return success:
+if [ "$LOCK_OWNER" = "" ]; then
+ exit 0
+fi
+# If the person unlocking matches the lock's owner, return success:
+if [ "$LOCK_OWNER" = "$USER" ]; then
+ exit 0
+fi
+
+# Otherwise, we've got an owner mismatch, so return failure:
+echo "Error: $PATH locked by ${LOCK_OWNER}." 1>&2
+exit 1
diff --git a/test/repositories/svn_with_tree_move/hooks/start-commit.tmpl b/test/repositories/svn_with_tree_move/hooks/start-commit.tmpl
new file mode 100644
index 00000000..348d7063
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/hooks/start-commit.tmpl
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# START-COMMIT HOOK
+#
+# The start-commit hook is invoked before a Subversion txn is created
+# in the process of doing a commit. Subversion runs this hook
+# by invoking a program (script, executable, binary, etc.) named
+# 'start-commit' (for which this file is a template)
+# with the following ordered arguments:
+#
+# [1] REPOS-PATH (the path to this repository)
+# [2] USER (the authenticated user attempting to commit)
+#
+# The default working directory for the invocation is undefined, so
+# the program should set one explicitly if it cares.
+#
+# If the hook program exits with success, the commit continues; but
+# if it exits with failure (non-zero), the commit is stopped before
+# a Subversion txn is created, and STDERR is returned to the client.
+#
+# On a Unix system, the normal procedure is to have 'start-commit'
+# invoke other programs to do the real work, though it may do the
+# work itself too.
+#
+# Note that 'start-commit' must be executable by the user(s) who will
+# invoke it (typically the user httpd runs as), and that user must
+# have filesystem-level permission to access the repository.
+#
+# On a Windows system, you should name the hook program
+# 'start-commit.bat' or 'start-commit.exe',
+# but the basic idea is the same.
+#
+# The hook program typically does not inherit the environment of
+# its parent process. For example, a common problem is for the
+# PATH environment variable to not be set to its usual value, so
+# that subprograms fail to launch unless invoked via absolute path.
+# If you're having unexpected problems with a hook program, the
+# culprit may be unusual (or missing) environment variables.
+#
+# Here is an example hook script, for a Unix /bin/sh interpreter.
+# For more examples and pre-written hooks, see those in
+# the Subversion repository at
+# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and
+# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/
+
+
+REPOS="$1"
+USER="$2"
+
+commit-allower.pl --repository "$REPOS" --user "$USER" || exit 1
+special-auth-check.py --user "$USER" --auth-level 3 || exit 1
+
+# All checks passed, so allow the commit.
+exit 0
diff --git a/test/repositories/svn_with_tree_move/locks/db-logs.lock b/test/repositories/svn_with_tree_move/locks/db-logs.lock
new file mode 100644
index 00000000..20dd6369
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/locks/db-logs.lock
@@ -0,0 +1,3 @@
+This file is not used by Subversion 1.3.x or later.
+However, its existence is required for compatibility with
+Subversion 1.2.x or earlier.
diff --git a/test/repositories/svn_with_tree_move/locks/db.lock b/test/repositories/svn_with_tree_move/locks/db.lock
new file mode 100644
index 00000000..20dd6369
--- /dev/null
+++ b/test/repositories/svn_with_tree_move/locks/db.lock
@@ -0,0 +1,3 @@
+This file is not used by Subversion 1.3.x or later.
+However, its existence is required for compatibility with
+Subversion 1.2.x or earlier.
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 9cc3e5aa..3f645acc 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -5,9 +5,9 @@
unless defined?(TEST_DIR)
TEST_DIR = File.dirname(__FILE__)
end
-require TEST_DIR + '/../lib/scm'
+require_relative '../lib/ohloh_scm'
-Scm::Adapters::AbstractAdapter.logger = Logger.new(File.open('log/test.log','a'))
+OhlohScm::Adapters::AbstractAdapter.logger = Logger.new(File.open('log/test.log','a'))
unless defined?(REPO_DIR)
REPO_DIR = File.expand_path(File.join(TEST_DIR, 'repositories'))
@@ -17,7 +17,7 @@
DATA_DIR = File.expand_path(File.join(TEST_DIR, 'data'))
end
-class Scm::Test < Test::Unit::TestCase
+class OhlohScm::Test < Test::Unit::TestCase
# For reasons unknown, the base class defines a default_test method to throw a failure.
# We override it with a no-op to prevent this 'helpful' feature.
def default_test
@@ -25,7 +25,7 @@ def default_test
def assert_convert(parser, log, expected)
result = ''
- parser.parse File.new(log), :writer => Scm::Parsers::XmlWriter.new(result)
+ parser.parse File.new(log), :writer => OhlohScm::Parsers::XmlWriter.new(result)
assert_buffers_equal File.read(expected), result
end
@@ -47,8 +47,8 @@ def assert_buffers_equal(expected, actual)
assert_equal expected_lines, actual_lines
end
- def with_repository(type, name, block)
- Scm::ScratchDir.new do |dir|
+ def with_repository(type, name, branch_name = nil)
+ OhlohScm::ScratchDir.new do |dir|
if Dir.entries(REPO_DIR).include?(name)
`cp -R #{File.join(REPO_DIR, name)} #{dir}`
elsif Dir.entries(REPO_DIR).include?(name + '.tgz')
@@ -56,27 +56,68 @@ def with_repository(type, name, block)
else
raise RuntimeError.new("Repository archive #{File.join(REPO_DIR, name)} not found.")
end
- block.call(type.new(:url => File.join(dir, name)).normalize)
+ yield type.new(:url => File.join(dir, name), branch_name: branch_name).normalize
end
end
- def with_svn_repository(name, &block)
- with_repository(Scm::Adapters::SvnAdapter, name, block)
+ # We are unable to add a commit message with non utf8 characters using svn 1.6 & above.
+ # In order to emulate encoding issues, we use a custom svn executable that returns
+ # an xml log with invalid characters in it.
+ # We prepend our custom svn's location to $PATH to make it available during our tests.
+ def with_invalid_encoded_svn_repository
+ with_repository(OhlohScm::Adapters::SvnChainAdapter, 'svn_with_invalid_encoding') do |svn|
+ original_env_path = ENV['PATH']
+ custom_svn_path = File.expand_path('../bin/', __FILE__)
+ ENV['PATH'] = custom_svn_path + ':' + ENV['PATH']
+
+ yield svn
+
+ ENV['PATH'] = original_env_path
+ end
+ end
+
+ def with_svn_repository(name, branch_name='')
+ with_repository(OhlohScm::Adapters::SvnAdapter, name) do |svn|
+ svn.branch_name = branch_name
+ svn.url = File.join(svn.root, svn.branch_name)
+ svn.url = svn.url[0..-2] if svn.url[-1..-1] == '/' # Strip trailing /
+ yield svn
+ end
end
- def with_cvs_repository(name, &block)
- with_repository(Scm::Adapters::CvsAdapter, name, block)
+ def with_svn_chain_repository(name, branch_name='')
+ with_repository(OhlohScm::Adapters::SvnChainAdapter, name) do |svn|
+ svn.branch_name = branch_name
+ svn.url = File.join(svn.root, svn.branch_name)
+ svn.url = svn.url[0..-2] if svn.url[-1..-1] == '/' # Strip trailing /
+ yield svn
+ end
end
- def with_git_repository(name, &block)
- with_repository(Scm::Adapters::GitAdapter, name, block)
+ def with_cvs_repository(name, module_name='')
+ with_repository(OhlohScm::Adapters::CvsAdapter, name) do |cvs|
+ cvs.module_name = module_name
+ yield cvs
+ end
+ end
+
+ def with_git_repository(name, branch_name = nil)
+ with_repository(OhlohScm::Adapters::GitAdapter, name, branch_name) { |git| yield git }
+ end
+
+ def with_hg_repository(name, branch_name = nil)
+ with_repository(OhlohScm::Adapters::HgAdapter, name, branch_name) { |hg| yield hg }
+ end
+
+ def with_hglib_repository(name)
+ with_repository(OhlohScm::Adapters::HglibAdapter, name) { |hg| yield hg }
end
- def with_hg_repository(name, &block)
- with_repository(Scm::Adapters::HgAdapter, name, block)
+ def with_bzr_repository(name)
+ with_repository(OhlohScm::Adapters::BzrAdapter, name) { |bzr| yield bzr }
end
- def with_bzr_repository(name, &block)
- with_repository(Scm::Adapters::BzrAdapter, name, block)
+ def with_bzrlib_repository(name)
+ with_repository(OhlohScm::Adapters::BzrlibAdapter, name) { |bzr| yield bzr }
end
end
diff --git a/test/unit/abstract_adapter_test.rb b/test/unit/abstract_adapter_test.rb
index b6372558..762ad3a3 100644
--- a/test/unit/abstract_adapter_test.rb
+++ b/test/unit/abstract_adapter_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class AbstractAdapterTest < Scm::Test
+module OhlohScm::Adapters
+ class AbstractAdapterTest < OhlohScm::Test
def test_simple_validation
scm = AbstractAdapter.new()
assert !scm.valid?
@@ -49,13 +49,13 @@ def test_valid_passwords
end
def test_invalid_branch_names
- ['%','a'*51].each do |branch_name|
+ ['%','a'*81].each do |branch_name|
assert AbstractAdapter.new(:branch_name => branch_name).validate_branch_name.any?
end
end
def test_valid_branch_names
- [nil,'','/trunk','_','a'*50].each do |branch_name|
+ [nil,'','/trunk','_','a'*80].each do |branch_name|
assert !AbstractAdapter.new(:branch_name => branch_name).validate_branch_name
end
end
@@ -68,5 +68,39 @@ def test_normalize
assert_equal "joe", scm.username
assert_equal "abc", scm.password
end
+
+ def test_shellout
+ cmd = %q( ruby -e" t = 'Hello World'; STDOUT.puts t; STDERR.puts t " )
+ stdout = AbstractAdapter.run(cmd)
+ assert_equal "Hello World\n", stdout
+ end
+
+ def test_shellout_with_stderr
+ cmd = %q( ruby -e" t = 'Hello World'; STDOUT.puts t; STDERR.puts t " )
+ stdout, stderr, status = AbstractAdapter.run_with_err(cmd)
+ assert_equal 0, status.exitstatus
+ assert_equal "Hello World\n", stdout
+ assert_equal "Hello World\n", stderr
+ end
+
+ def test_shellout_large_output
+ cat = 'ruby -e" puts Array.new(65536){ 42 } "'
+ stdout = AbstractAdapter.run(cat)
+ assert_equal Array.new(65536){ 42 }.join("\n").concat("\n"), stdout
+ end
+
+ def test_shellout_error
+ cmd = "false"
+ assert_raise RuntimeError do
+ stdout = AbstractAdapter.run(cmd)
+ end
+ end
+
+ def test_string_encoder_must_return_path_to_script
+ string_encoder_path = File.expand_path('../../../bin/string_encoder', __FILE__)
+
+ assert_equal string_encoder_path, AbstractAdapter.new.string_encoder
+ end
+
end
end
diff --git a/test/unit/adapter_factory_test.rb b/test/unit/adapter_factory_test.rb
index f86b7613..a298ff5f 100644
--- a/test/unit/adapter_factory_test.rb
+++ b/test/unit/adapter_factory_test.rb
@@ -1,10 +1,10 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class FactoryTest < Scm::Test
+module OhlohScm::Adapters
+ class FactoryTest < OhlohScm::Test
def test_factory_hg
- Scm::ScratchDir.new do |path|
+ OhlohScm::ScratchDir.new do |path|
`cd #{path} && hg init`
hg = Factory.from_path(path)
assert hg.is_a?(HgAdapter)
@@ -13,7 +13,7 @@ def test_factory_hg
end
def test_factory_bzr
- Scm::ScratchDir.new do |path|
+ OhlohScm::ScratchDir.new do |path|
`cd #{path} && bzr init`
bzr = Factory.from_path(path)
assert bzr.is_a?(BzrAdapter)
@@ -22,7 +22,7 @@ def test_factory_bzr
end
def test_factory_git
- Scm::ScratchDir.new do |path|
+ OhlohScm::ScratchDir.new do |path|
`cd #{path} && git init`
git = Factory.from_path(path)
assert git.is_a?(GitAdapter)
@@ -31,7 +31,7 @@ def test_factory_git
end
def test_factory_svn
- Scm::ScratchDir.new do |path|
+ OhlohScm::ScratchDir.new do |path|
`cd #{path} && svnadmin create foo`
svn = Factory.from_path(File.join(path, 'foo'))
assert svn.is_a?(SvnAdapter)
@@ -40,7 +40,7 @@ def test_factory_svn
end
def test_factory_svn_checkout
- Scm::ScratchDir.new do |path|
+ OhlohScm::ScratchDir.new do |path|
`cd #{path} && svnadmin create foo`
`cd #{path} && svn co file://#{File.expand_path(File.join(path, 'foo'))} bar`
svn = Factory.from_path(File.join(path, 'bar'))
@@ -52,8 +52,8 @@ def test_factory_svn_checkout
end
def test_factory_from_cvs_checkout
- with_cvs_repository('cvs') do |cvs|
- Scm::ScratchDir.new do |path|
+ with_cvs_repository('cvs', 'simple') do |cvs|
+ OhlohScm::ScratchDir.new do |path|
`cd #{path} && cvsnt -d #{File.expand_path(cvs.url)} co simple 2> /dev/null`
factory_response = Factory.from_path(File.join(path, 'simple'))
assert factory_response.is_a?(CvsAdapter)
diff --git a/test/unit/array_writer_test.rb b/test/unit/array_writer_test.rb
index f8b1686b..86a3d32b 100644
--- a/test/unit/array_writer_test.rb
+++ b/test/unit/array_writer_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Parsers
- class ArrayWriterTest < Scm::Test
+module OhlohScm::Parsers
+ class ArrayWriterTest < OhlohScm::Test
def test_basic
diff --git a/test/unit/bzr_cat_file_test.rb b/test/unit/bzr_cat_file_test.rb
index 1924f368..f7b08d88 100644
--- a/test/unit/bzr_cat_file_test.rb
+++ b/test/unit/bzr_cat_file_test.rb
@@ -1,7 +1,8 @@
-require File.dirname(__FILE__) + '/../test_helper'
+# encoding: utf-8
+require_relative '../test_helper'
-module Scm::Adapters
- class BzrCatFileTest < Scm::Test
+module OhlohScm::Adapters
+ class BzrCatFileTest < OhlohScm::Test
def test_cat_file
with_bzr_repository('bzr') do |bzr|
@@ -10,15 +11,27 @@ def test_cat_file
second line
EXPECTED
assert_equal expected,
- bzr.cat_file(Scm::Commit::new(:token => 6),
- Scm::Diff.new(:path => "file1.txt"))
+ bzr.cat_file(OhlohScm::Commit::new(:token => 6),
+ OhlohScm::Diff.new(:path => "file1.txt"))
# file2.txt has been removed in commit #5
assert_equal nil, bzr.cat_file(bzr.head,
- Scm::Diff.new(:path => "file2.txt"))
+ OhlohScm::Diff.new(:path => "file2.txt"))
end
end
+ def test_cat_file_non_ascii_name
+ with_bzr_repository('bzr') do |bzr|
+ expected = <<-EXPECTED
+first file
+second line
+EXPECTED
+ assert_equal expected,
+ bzr.cat_file(OhlohScm::Commit::new(:token => 7),
+ OhlohScm::Diff.new(:path => "Cédric.txt"))
+ end
+ end
+
def test_cat_file_parent
with_bzr_repository('bzr') do |bzr|
expected = <<-EXPECTED
@@ -26,16 +39,16 @@ def test_cat_file_parent
second line
EXPECTED
assert_equal expected,
- bzr.cat_file_parent(Scm::Commit::new(:token => 6),
- Scm::Diff.new(:path => "file1.txt"))
+ bzr.cat_file_parent(OhlohScm::Commit::new(:token => 6),
+ OhlohScm::Diff.new(:path => "file1.txt"))
# file2.txt has been removed in commit #5
expected = <<-EXPECTED
another file
EXPECTED
assert_equal expected,
- bzr.cat_file_parent(Scm::Commit.new(:token => 5),
- Scm::Diff.new(:path => "file2.txt"))
+ bzr.cat_file_parent(OhlohScm::Commit.new(:token => 5),
+ OhlohScm::Diff.new(:path => "file2.txt"))
end
end
diff --git a/test/unit/bzr_commits_test.rb b/test/unit/bzr_commits_test.rb
index c4c0549b..755ebcd8 100644
--- a/test/unit/bzr_commits_test.rb
+++ b/test/unit/bzr_commits_test.rb
@@ -1,14 +1,14 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class BzrCommitsTest < Scm::Test
+module OhlohScm::Adapters
+ class BzrCommitsTest < OhlohScm::Test
def test_commit_count
with_bzr_repository('bzr') do |bzr|
- assert_equal 6, bzr.commit_count
- assert_equal 5, bzr.commit_count(revision_ids.first)
- assert_equal 1, bzr.commit_count(revision_ids[4])
- assert_equal 0, bzr.commit_count(revision_ids.last)
+ assert_equal 7, bzr.commit_count
+ assert_equal 6, bzr.commit_count(:after => revision_ids.first)
+ assert_equal 1, bzr.commit_count(:after => revision_ids[5])
+ assert_equal 0, bzr.commit_count(:after => revision_ids.last)
end
end
@@ -19,20 +19,172 @@ def test_commit_count_with_branches
end
end
- def test_commit_tokens
+ def test_commit_count_after_merge
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ last_commit = bzr.commits.last
+ assert_equal 0, bzr.commit_count(:trunk_only => false, :after => last_commit.token)
+ end
+ end
+
+ def test_commit_count_trunk_only
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ # Only 3 commits are on main line
+ assert_equal 3, bzr.commit_count(:trunk_only => true)
+ end
+ end
+
+ def test_commit_tokens_after
with_bzr_repository('bzr') do |bzr|
assert_equal revision_ids, bzr.commit_tokens
- assert_equal revision_ids[1..5], bzr.commit_tokens(revision_ids.first)
- assert_equal revision_ids[5..5], bzr.commit_tokens(revision_ids[4])
- assert_equal [], bzr.commit_tokens(revision_ids.last)
+ assert_equal revision_ids[1..6], bzr.commit_tokens(:after => revision_ids.first)
+ assert_equal revision_ids[6..6], bzr.commit_tokens(:after => revision_ids[5])
+ assert_equal [], bzr.commit_tokens(:after => revision_ids.last)
+ end
+ end
+
+ def test_commit_tokens_after_merge
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ last_commit = bzr.commits.last
+ assert_equal [], bzr.commit_tokens(:trunk_only => false, :after => last_commit.token)
+ end
+ end
+
+ def test_commit_tokens_after_nested_merge
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ last_commit = bzr.commits.last
+ assert_equal [], bzr.commit_tokens(:trunk_only => false, :after => last_commit.token)
+ end
+ end
+
+ def test_commit_tokens_trunk_only_false
+ # Funny business with commit ordering has been fixed by BzrXmlParser.
+ # Now we always see branch commits before merge commit.
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ assert_equal [
+ 'test@example.com-20090206214301-s93cethy9atcqu9h',
+ 'test@example.com-20090206214451-lzjngefdyw3vmgms',
+ 'test@example.com-20090206214350-rqhdpz92l11eoq2t', # branch commit
+ 'test@example.com-20090206214515-21lkfj3dbocao5pr' # merge commit
+ ], bzr.commit_tokens(:trunk_only => false)
+ end
+ end
+
+ def test_commit_tokens_trunk_only_true
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ assert_equal [
+ 'test@example.com-20090206214301-s93cethy9atcqu9h',
+ 'test@example.com-20090206214451-lzjngefdyw3vmgms',
+ 'test@example.com-20090206214515-21lkfj3dbocao5pr' # merge commit
+ ], bzr.commit_tokens(:trunk_only => true)
+ end
+ end
+
+ def test_nested_branches_commit_tokens_trunk_only_false
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ assert_equal [
+ 'obnox@samba.org-20090204002342-5r0q4gejk69rk6uv',
+ 'obnox@samba.org-20090204002422-5ylnq8l4713eqfy0',
+ 'obnox@samba.org-20090204002453-u70a3ehf3ae9kay1',
+ 'obnox@samba.org-20090204002518-yb0x153oa6mhoodu',
+ 'obnox@samba.org-20090204002540-gmana8tk5f9gboq9',
+ 'obnox@samba.org-20090204004942-73rnw0izen42f154',
+ 'test@example.com-20110803170302-fz4mbr89n8f5agha',
+ 'test@example.com-20110803170341-v1icvy05b430t68l',
+ 'test@example.com-20110803170504-z7xz5uxj02e5x3z6',
+ 'test@example.com-20110803170522-asv6i9z6m22jc8zz',
+ 'test@example.com-20110803170648-o0xcbni7lwp97azj',
+ 'test@example.com-20110803170818-v44umypquqg8migo'
+ ], bzr.commit_tokens(:trunk_only => false)
+ end
+ end
+
+ def test_nested_branches_commit_tokens_trunk_only_true
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ assert_equal [
+ 'obnox@samba.org-20090204002342-5r0q4gejk69rk6uv',
+ 'obnox@samba.org-20090204002422-5ylnq8l4713eqfy0',
+ 'obnox@samba.org-20090204002453-u70a3ehf3ae9kay1',
+ 'obnox@samba.org-20090204002518-yb0x153oa6mhoodu',
+ 'obnox@samba.org-20090204002540-gmana8tk5f9gboq9',
+ 'obnox@samba.org-20090204004942-73rnw0izen42f154',
+ 'test@example.com-20110803170818-v44umypquqg8migo'
+ ], bzr.commit_tokens(:trunk_only => true)
+ end
+ end
+
+ def test_commits_trunk_only_false
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ assert_equal [
+ 'test@example.com-20090206214301-s93cethy9atcqu9h',
+ 'test@example.com-20090206214451-lzjngefdyw3vmgms',
+ 'test@example.com-20090206214350-rqhdpz92l11eoq2t', # branch commit
+ 'test@example.com-20090206214515-21lkfj3dbocao5pr' # merge commit
+ ], bzr.commits(:trunk_only => false).map { |c| c.token }
end
end
+ def test_commits_trunk_only_true
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ assert_equal [
+ 'test@example.com-20090206214301-s93cethy9atcqu9h',
+ 'test@example.com-20090206214451-lzjngefdyw3vmgms',
+ 'test@example.com-20090206214515-21lkfj3dbocao5pr' # merge commit
+ ], bzr.commits(:trunk_only => true).map { |c| c.token }
+ end
+ end
+
+ def test_commits_after_merge
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ last_commit = bzr.commits.last
+ assert_equal [], bzr.commits(:trunk_only => false, :after => last_commit.token)
+ end
+ end
+
+ def test_commits_after_nested_merge
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ last_commit = bzr.commits.last
+ assert_equal [], bzr.commits(:trunk_only => false, :after => last_commit.token)
+ end
+ end
+
+ def test_nested_branches_commits_trunk_only_false
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ assert_equal [
+ 'obnox@samba.org-20090204002342-5r0q4gejk69rk6uv',
+ 'obnox@samba.org-20090204002422-5ylnq8l4713eqfy0',
+ 'obnox@samba.org-20090204002453-u70a3ehf3ae9kay1',
+ 'obnox@samba.org-20090204002518-yb0x153oa6mhoodu',
+ 'obnox@samba.org-20090204002540-gmana8tk5f9gboq9',
+ 'obnox@samba.org-20090204004942-73rnw0izen42f154',
+ 'test@example.com-20110803170302-fz4mbr89n8f5agha',
+ 'test@example.com-20110803170341-v1icvy05b430t68l',
+ 'test@example.com-20110803170504-z7xz5uxj02e5x3z6',
+ 'test@example.com-20110803170522-asv6i9z6m22jc8zz',
+ 'test@example.com-20110803170648-o0xcbni7lwp97azj',
+ 'test@example.com-20110803170818-v44umypquqg8migo'
+ ], bzr.commits(:trunk_only => false).map { |c| c.token }
+ end
+ end
+
+ def test_nested_branches_commits_trunk_only_true
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ assert_equal [
+ 'obnox@samba.org-20090204002342-5r0q4gejk69rk6uv',
+ 'obnox@samba.org-20090204002422-5ylnq8l4713eqfy0',
+ 'obnox@samba.org-20090204002453-u70a3ehf3ae9kay1',
+ 'obnox@samba.org-20090204002518-yb0x153oa6mhoodu',
+ 'obnox@samba.org-20090204002540-gmana8tk5f9gboq9',
+ 'obnox@samba.org-20090204004942-73rnw0izen42f154',
+ 'test@example.com-20110803170818-v44umypquqg8migo'
+ ], bzr.commits(:trunk_only => true).map { |c| c.token }
+ end
+ end
+
def test_commits
with_bzr_repository('bzr') do |bzr|
assert_equal revision_ids, bzr.commits.collect { |c| c.token }
- assert_equal revision_ids[5..5], bzr.commits(revision_ids[4]).collect { |c| c.token }
- assert_equal [], bzr.commits(revision_ids.last).collect { |c| c.token }
+ assert_equal revision_ids[6..6], bzr.commits(:after => revision_ids[5]).collect { |c| c.token }
+ assert_equal [], bzr.commits(:after => revision_ids.last).collect { |c| c.token }
# Check that the diffs are not populated
assert_equal [], bzr.commits.first.diffs
@@ -63,13 +215,98 @@ def test_each_commit
end
end
- def test_remove_dupes_add_remove
- bzr = BzrAdapter.new
- c = Scm::Commit.new(:diffs => [ Scm::Diff.new(:action => "A", :path => "foo"),
- Scm::Diff.new(:action => "D", :path => "foo") ])
- bzr.remove_dupes(c)
- assert_equal 1, c.diffs.size
- assert_equal 'M', c.diffs.first.action
+ def test_each_commit_trunk_only_false
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ commits = []
+ bzr.each_commit(:trunk_only => false) { |c| commits << c }
+ assert_equal [
+ 'test@example.com-20090206214301-s93cethy9atcqu9h',
+ 'test@example.com-20090206214451-lzjngefdyw3vmgms',
+ 'test@example.com-20090206214350-rqhdpz92l11eoq2t', # branch commit
+ 'test@example.com-20090206214515-21lkfj3dbocao5pr' # merge commit
+ ], commits.map { |c| c.token }
+ end
+ end
+
+ def test_each_commit_trunk_only_true
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ commits = []
+ bzr.each_commit(:trunk_only => true) { |c| commits << c }
+ assert_equal [
+ 'test@example.com-20090206214301-s93cethy9atcqu9h',
+ 'test@example.com-20090206214451-lzjngefdyw3vmgms',
+ 'test@example.com-20090206214515-21lkfj3dbocao5pr' # merge commit
+ # 'test@example.com-20090206214350-rqhdpz92l11eoq2t' # branch commit -- after merge!
+ ], commits.map { |c| c.token }
+ end
+ end
+
+ def test_each_commit_after_merge
+ with_bzr_repository('bzr_with_branch') do |bzr|
+ last_commit = bzr.commits.last
+
+ commits = []
+ bzr.each_commit(:trunk_only => false, :after => last_commit.token) { |c| commits << c }
+ assert_equal [], commits
+ end
+ end
+
+ def test_each_commit_after_nested_merge_at_tip
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ last_commit = bzr.commits.last
+
+ commits = []
+ bzr.each_commit(:trunk_only => false, :after => last_commit.token) { |c| commits << c }
+ assert_equal [], commits
+ end
+ end
+
+ def test_each_commit_after_nested_merge_not_at_tip
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ last_commit = bzr.commits.last
+ next_to_last_commit = bzr.commits[-2]
+
+ yielded_commits = []
+ bzr.each_commit(:trunk_only => false, :after => next_to_last_commit.token) { |c| yielded_commits << c }
+ assert_equal [last_commit.token], yielded_commits.map(&:token)
+ end
+ end
+
+ def test_nested_branches_each_commit_trunk_only_false
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ commits = []
+ bzr.each_commit(:trunk_only => false) { |c| commits << c}
+ assert_equal [
+ 'obnox@samba.org-20090204002342-5r0q4gejk69rk6uv',
+ 'obnox@samba.org-20090204002422-5ylnq8l4713eqfy0',
+ 'obnox@samba.org-20090204002453-u70a3ehf3ae9kay1',
+ 'obnox@samba.org-20090204002518-yb0x153oa6mhoodu',
+ 'obnox@samba.org-20090204002540-gmana8tk5f9gboq9',
+ 'obnox@samba.org-20090204004942-73rnw0izen42f154',
+ 'test@example.com-20110803170302-fz4mbr89n8f5agha',
+ 'test@example.com-20110803170341-v1icvy05b430t68l',
+ 'test@example.com-20110803170504-z7xz5uxj02e5x3z6',
+ 'test@example.com-20110803170522-asv6i9z6m22jc8zz',
+ 'test@example.com-20110803170648-o0xcbni7lwp97azj',
+ 'test@example.com-20110803170818-v44umypquqg8migo'
+ ], commits.map { |c| c.token }
+ end
+ end
+
+ def test_nested_branches_each_commit_trunk_only_true
+ with_bzr_repository('bzr_with_nested_branches') do |bzr|
+ commits = []
+ bzr.each_commit(:trunk_only => true) { |c| commits << c }
+ assert_equal [
+ 'obnox@samba.org-20090204002342-5r0q4gejk69rk6uv',
+ 'obnox@samba.org-20090204002422-5ylnq8l4713eqfy0',
+ 'obnox@samba.org-20090204002453-u70a3ehf3ae9kay1',
+ 'obnox@samba.org-20090204002518-yb0x153oa6mhoodu',
+ 'obnox@samba.org-20090204002540-gmana8tk5f9gboq9',
+ 'obnox@samba.org-20090204004942-73rnw0izen42f154',
+ 'test@example.com-20110803170818-v44umypquqg8migo'
+ ], commits.map { |c| c.token }
+ end
end
# This bzr repository contains the following tree structure
@@ -89,6 +326,49 @@ def test_each_commit_excludes_directories
end
end
+ # Verfies OTWO-344
+ def test_commit_tokens_with_colon_character
+ with_bzr_repository('bzr_colon') do |bzr|
+ assert_equal ['svn-v4:364a429a-ab12-11de-804f-e3d9c25ff3d2::0'], bzr.commit_tokens
+ end
+ end
+
+ def test_committer_and_author_name
+ with_bzr_repository('bzr_with_authors') do |bzr|
+ commits = []
+ bzr.each_commit do |c|
+ commits << c
+ end
+ assert_equal 3, commits.size
+
+ assert_equal 'Initial.', commits[0].message
+ assert_equal 'Abhay Mujumdar', commits[0].committer_name
+ assert_equal nil, commits[0].author_name
+ assert_equal nil, commits[0].author_email
+
+ assert_equal 'Updated.', commits[1].message
+ assert_equal 'Abhay Mujumdar', commits[1].committer_name
+ assert_equal 'John Doe', commits[1].author_name
+ assert_equal 'johndoe@example.com', commits[1].author_email
+
+ # When there are multiple authors, first one is captured.
+ assert_equal 'Updated by two authors.', commits[2].message
+ assert_equal 'test', commits[2].committer_name
+ assert_equal 'John Doe', commits[2].author_name
+ assert_equal 'johndoe@example.com', commits[2].author_email
+ end
+ end
+
+ # Bzr converts invalid utf-8 characters into valid format before commit.
+ # So no utf-8 encoding issues are seen in ruby when dealing with Bzr.
+ def test_commits_encoding
+ with_bzr_repository('bzr_with_invalid_encoding') do |bzr|
+ assert_nothing_raised do
+ bzr.commits
+ end
+ end
+ end
+
protected
def revision_ids
@@ -98,7 +378,8 @@ def revision_ids
'obnox@samba.org-20090204002453-u70a3ehf3ae9kay1', # 3
'obnox@samba.org-20090204002518-yb0x153oa6mhoodu', # 4
'obnox@samba.org-20090204002540-gmana8tk5f9gboq9', # 5
- 'obnox@samba.org-20090204004942-73rnw0izen42f154' # 6
+ 'obnox@samba.org-20090204004942-73rnw0izen42f154', # 6
+ 'test@example.com-20111222183733-y91if5npo3pe8ifs', # 7
]
end
diff --git a/test/unit/bzr_head_test.rb b/test/unit/bzr_head_test.rb
index 18e8e0df..47e7a809 100644
--- a/test/unit/bzr_head_test.rb
+++ b/test/unit/bzr_head_test.rb
@@ -1,15 +1,15 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class BzrHeadTest < Scm::Test
+module OhlohScm::Adapters
+ class BzrHeadTest < OhlohScm::Test
def test_head_and_parents
with_bzr_repository('bzr') do |bzr|
- assert_equal 'obnox@samba.org-20090204004942-73rnw0izen42f154', bzr.head_token
- assert_equal 'obnox@samba.org-20090204004942-73rnw0izen42f154', bzr.head.token
+ assert_equal 'test@example.com-20111222183733-y91if5npo3pe8ifs', bzr.head_token
+ assert_equal 'test@example.com-20111222183733-y91if5npo3pe8ifs', bzr.head.token
assert bzr.head.diffs.any? # diffs should be populated
- assert_equal 'obnox@samba.org-20090204002540-gmana8tk5f9gboq9', bzr.parents(bzr.head).first.token
+ assert_equal 'obnox@samba.org-20090204004942-73rnw0izen42f154', bzr.parents(bzr.head).first.token
assert bzr.parents(bzr.head).first.diffs.any?
end
end
diff --git a/test/unit/bzr_misc_test.rb b/test/unit/bzr_misc_test.rb
index 14f87d8d..8e7b9786 100644
--- a/test/unit/bzr_misc_test.rb
+++ b/test/unit/bzr_misc_test.rb
@@ -1,7 +1,8 @@
-require File.dirname(__FILE__) + '/../test_helper'
+# encoding: utf-8
+require_relative '../test_helper'
-module Scm::Adapters
- class BzrMiscTest < Scm::Test
+module OhlohScm::Adapters
+ class BzrMiscTest < OhlohScm::Test
def test_exist
save_bzr = nil
@@ -14,13 +15,46 @@ def test_exist
def test_ls_tree
with_bzr_repository('bzr') do |bzr|
- assert_equal ['file1.txt',
+ assert_equal ['Cédric.txt',
+ 'file1.txt',
'file3.txt',
'file4.txt',
'file5.txt'],
- bzr.ls_tree(bzr.head_token).sort
+ bzr.ls_tree(bzr.head_token).sort.map { |filename|
+ filename.force_encoding(Encoding::UTF_8) }
end
end
+ def test_export
+ with_bzr_repository('bzr') do |bzr|
+ OhlohScm::ScratchDir.new do |dir|
+ bzr.export(dir)
+ assert_equal ['.', '..', 'Cédric.txt', 'file1.txt', 'file3.txt', 'file4.txt', 'file5.txt'], Dir.entries(dir).sort
+ end
+ end
+ end
+
+ def test_tags
+ with_bzr_repository('bzr') do |bzr|
+ time_1 = Time.parse('2009-02-04 00:25:40 +0000')
+ time_2 = Time.parse('2011-12-22 18:37:33 +0000')
+ monkey_patch_run_method_to_match_tag_patterns
+ assert_equal [['v1.0.0', '5', time_1], ['v2.0.0','7', time_2]], bzr.tags
+ end
+ end
+
+ private
+
+ def monkey_patch_run_method_to_match_tag_patterns
+ original_method = AbstractAdapter.method(:run)
+ AbstractAdapter.send :define_method, :run do |command|
+ if command =~ /bzr tags/
+ # The output of `bzr tags` sometimes has tags referring to ? while sometimes has dotted separators.
+ "0.11-1.1 ?\n0.14-1 ?\n....\n#{ original_method.call(command) }"
+ else
+ original_method.call(command)
+ end
+ end
+ end
end
end
diff --git a/test/unit/bzr_parser_test.rb b/test/unit/bzr_parser_test.rb
index 57d0e5a0..c52ea2df 100644
--- a/test/unit/bzr_parser_test.rb
+++ b/test/unit/bzr_parser_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Parsers
- class BzrParserTest < Scm::Test
+module OhlohScm::Parsers
+ class BzrParserTest < OhlohScm::Test
def test_empty_array
assert_equal([], BzrParser.parse(''))
@@ -264,5 +264,220 @@ def test_verbose_log_with_nested_merge_commits
assert_equal 'hello_world.c', commits[3].diffs[0].path
assert_equal 'M', commits[3].diffs[0].action
end
+
+ def test_parse_diffs
+ assert_equal 'A', BzrParser.parse_diffs('A', "helloworld.c").first.action
+ assert_equal 'helloworld.c', BzrParser.parse_diffs('A', "helloworld.c").first.path
+ end
+
+ def test_parse_diffs_rename
+ diffs = BzrParser.parse_diffs(:rename, "helloworld.c => goodbyeworld.c")
+ assert_equal 2, diffs.size
+ assert_equal 'D', diffs.first.action
+ assert_equal 'helloworld.c', diffs.first.path
+ assert_equal 'A', diffs.last.action
+ assert_equal 'goodbyeworld.c', diffs.last.path
+ end
+
+ def test_rename
+ log = <<-SAMPLE
+------------------------------------------------------------
+revno: 2
+committer: info
+timestamp: Wed 2005-09-14 21:27:20 +1000
+message:
+ rename a file
+renamed:
+ helloworld.c => goodbyeworld.c
+ SAMPLE
+
+ commits = BzrParser.parse(log)
+
+ assert commits
+ assert_equal 1, commits.size
+ assert_equal 2, commits.first.diffs.size
+
+ assert_equal 'D', commits.first.diffs.first.action
+ assert_equal 'helloworld.c', commits.first.diffs.first.path
+
+ assert_equal 'A', commits.first.diffs.last.action
+ assert_equal 'goodbyeworld.c', commits.first.diffs.last.path
+ end
+
+ def test_remove_dupes_add_remove
+ diffs = BzrParser.remove_dupes([ OhlohScm::Diff.new(:action => "A", :path => "foo"),
+ OhlohScm::Diff.new(:action => "D", :path => "foo") ])
+ assert_equal 1, diffs.size
+ assert_equal 'M', diffs.first.action
+ assert_equal 'foo', diffs.first.path
+ end
+
+ # A somewhat tricky case. A file is deleted, and another
+ # file is renamed to take its place. That file is then modified!
+ #
+ # This is what Ohloh expects to see:
+ #
+ # D goodbyeworld.c
+ # M helloworld.c
+ #
+ def test_complex_rename
+ log = <<-SAMPLE
+------------------------------------------------------------
+revno: 147.1.24
+committer: info
+timestamp: Wed 2005-09-14 21:27:20 +1000
+message:
+ rename a file to replace an existing one, then modify it!
+removed:
+ helloworld.c
+renamed:
+ goodbyeworld.c => helloworld.c
+modified:
+ helloworld.c
+ SAMPLE
+
+ diffs = BzrParser.parse(log).first.diffs
+ diffs.sort! { |a,b| a.path <=> b.path }
+
+ assert_equal 2, diffs.size
+ assert_equal 'D', diffs.first.action
+ assert_equal 'goodbyeworld.c', diffs.first.path
+ assert_equal 'M', diffs.last.action
+ assert_equal 'helloworld.c', diffs.last.path
+ end
+
+ def test_strip_trailing_asterisk_from_executables
+ log = <<-SAMPLE
+------------------------------------------------------------
+revno: 1
+committer: info
+timestamp: Wed 2005-09-14 21:27:20 +1000
+message:
+ added an executable, also renamed an executable
+added:
+ script*
+renamed:
+ helloworld* => goodbyeworld*
+ SAMPLE
+
+ diffs = BzrParser.parse(log).first.diffs
+ diffs.sort! { |a,b| a.path <=> b.path }
+
+ assert_equal 'goodbyeworld', diffs[0].path
+ assert_equal 'helloworld', diffs[1].path
+ assert_equal 'script', diffs[2].path
+ end
+
+ def test_comment_that_contains_dashes
+ log = <<-SAMPLE
+------------------------------------------------------------
+revno: 2
+committer: info
+timestamp: Wed 2005-09-14 21:27:20 +1000
+message:
+ This is a tricky commit message to confirm fix
+ to Ticket 5. We're including a line of dashes in
+ the message that resembles a log delimiter.
+
+ ------------------------------------------------------------
+
+ Happy parsing!
+added:
+ goodbyeworld.c
+------------------------------------------------------------
+revno: 1
+committer: info
+timestamp: Wed 2005-09-14 21:27:20 +1000
+message:
+ Initial Revision
+added:
+ helloworld.c
+ SAMPLE
+
+ commits = BzrParser.parse(log)
+
+ assert_equal 2, commits.size
+ assert_equal '2', commits.first.token
+ assert_equal 1, commits.first.diffs.size
+ assert_equal "goodbyeworld.c", commits.first.diffs.first.path
+
+ assert_equal '1', commits.last.token
+ assert_equal 1, commits.last.diffs.size
+ assert_equal "helloworld.c", commits.last.diffs.first.path
+ end
+
+ # In this example, directory "test/" is renamed to "/".
+ # This shows in the log as being renamed to an empty string.
+ def test_directory_renamed_to_root
+ log = <<-SAMPLE
+ ------------------------------------------------------------
+ revno: 220.90.1
+ revision-id: info@ohloh.net-20081002201109-j4z0r2c8nsgbm2vk
+ parent: info@ohloh.net-20081002200737-pjao1idjcrxpk4n4
+ committer: Ohloh
+ branch nick: subvertpy
+ timestamp: Thu 2008-10-02 22:11:09 +0200
+ message:
+ Promote the test directory to the root.
+ renamed:
+ test => test-20081002184530-hz9mrr3wqq4l8qdx-1
+ SAMPLE
+
+ commits = BzrParser.parse(log)
+
+ assert_equal 1, commits.size
+ assert_equal 'info@ohloh.net-20081002201109-j4z0r2c8nsgbm2vk', commits.first.token
+ assert_equal 2, commits.first.diffs.size
+ assert_equal "D", commits.first.diffs.first.action
+ assert_equal "test", commits.first.diffs.first.path
+ assert_equal "A", commits.first.diffs.last.action
+ assert_equal "", commits.first.diffs.last.path
+ end
+
+ # It is possible for Bzr to report a file as both renamed and modified
+ # in the same commit. We should treat this as only a rename -- that is, we
+ # should see a simple DELETE from the old location and an ADD to the new location.
+ def test_rename_and_modify_in_one_commit
+ log = <<-SAMPLE
+------------------------------------------------------------
+revno: 1
+message:
+ Changed the directory structure
+renamed:
+ oldname => newname
+modified:
+ newname
+ SAMPLE
+
+ commits = BzrParser.parse(log)
+
+ assert_equal 1, commits.size
+ assert_equal 2, commits.first.diffs.size
+ assert_equal "D", commits.first.diffs.first.action
+ assert_equal "oldname", commits.first.diffs.first.path
+ assert_equal "A", commits.first.diffs.last.action
+ assert_equal "newname", commits.first.diffs.last.path
+ end
+
+ def test_different_author_and_committer
+ log = <<-SAMPLE
+------------------------------------------------------------
+revno: 200
+author: Jason Allen
+committer: Robin Luckey
+branch nick: foo
+timestamp: Wed 2009-06-24 19:47:37 +0200
+message:
+ Just a message
+ SAMPLE
+
+ commits = BzrParser.parse(log)
+
+ assert_equal 1, commits.size
+ assert_equal "Jason Allen", commits.first.author_name
+ assert_equal "jason@ohloh.net", commits.first.author_email
+ assert_equal "Robin Luckey", commits.first.committer_name
+ assert_equal "robin@ohloh.net", commits.first.committer_email
+ end
end
end
diff --git a/test/unit/bzr_pull_test.rb b/test/unit/bzr_pull_test.rb
index 9c8a29e3..4f0bbd0c 100644
--- a/test/unit/bzr_pull_test.rb
+++ b/test/unit/bzr_pull_test.rb
@@ -1,11 +1,11 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class BzrPullTest < Scm::Test
+module OhlohScm::Adapters
+ class BzrPullTest < OhlohScm::Test
def test_pull
with_bzr_repository('bzr') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = BzrAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
@@ -17,7 +17,7 @@ def test_pull
# Commit some new code on the original and pull again
src.run "cd '#{src.url}' && touch foo && bzr add foo && bzr whoami 'test ' && bzr commit -m test"
- assert_equal "test\n", src.commits.last.message
+ assert_equal "test", src.commits.last.message
assert_equal "test", src.commits.last.committer_name
assert_equal "test@example.com", src.commits.last.committer_email
diff --git a/test/unit/bzr_push_test.rb b/test/unit/bzr_push_test.rb
index a64d0375..1b1df3d5 100644
--- a/test/unit/bzr_push_test.rb
+++ b/test/unit/bzr_push_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class BzrPushTest < Scm::Test
+module OhlohScm::Adapters
+ class BzrPushTest < OhlohScm::Test
def test_hostname
assert !BzrAdapter.new.hostname
@@ -36,7 +36,7 @@ def test_bzr_path
def test_push
with_bzr_repository('bzr') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = BzrAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
@@ -47,7 +47,7 @@ def test_push
# Commit some new code on the original and pull again
src.run "cd '#{src.url}' && touch foo && bzr add foo && bzr whoami 'test ' && bzr commit -m test"
- assert_equal "test\n", src.commits.last.message
+ assert_equal "test", src.commits.last.message
assert_equal "test", src.commits.last.committer_name
assert_equal "test@example.com", src.commits.last.committer_email
diff --git a/test/unit/bzr_validation_test.rb b/test/unit/bzr_validation_test.rb
index 7ebe0d1b..c4f2d201 100644
--- a/test/unit/bzr_validation_test.rb
+++ b/test/unit/bzr_validation_test.rb
@@ -1,17 +1,18 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class BzrValidationTest < Scm::Test
+module OhlohScm::Adapters
+ class BzrValidationTest < OhlohScm::Test
def test_rejected_urls
[ nil, "", "foo", "http:/", "http:://", "http://", "http://a",
"www.selenic.com/repo/hello", # missing a protool prefix
"http://www.selenic.com/repo/hello%20world", # no encoded strings allowed
"http://www.selenic.com/repo/hello world", # no spaces allowed
"git://www.selenic.com/repo/hello", # git protocol not allowed
- "svn://www.selenic.com/repo/hello" # svn protocol not allowed
+ "svn://www.selenic.com/repo/hello", # svn protocol not allowed
+ "lp://foobar", # lp requires no "//" after colon
].each do |url|
bzr = BzrAdapter.new(:url => url, :public_urls_only => true)
- assert bzr.validate_url.any?
+ assert bzr.validate_url.to_a.any?, "Didn't expect #{ url } to validate"
end
end
@@ -19,6 +20,9 @@ def test_accepted_urls
[ "http://www.selenic.com/repo/hello",
"http://www.selenic.com:80/repo/hello",
"https://www.selenic.com/repo/hello",
+ "bzr://www.selenic.com/repo/hello",
+ "lp:foobar",
+ "lp:~foobar/bar",
].each do |url|
bzr = BzrAdapter.new(:url => url, :public_urls_only => true)
assert !bzr.validate_url
@@ -47,6 +51,9 @@ def test_guess_forge
bzr = BzrAdapter.new(:url => "/home/test/bzr")
assert_equal nil, bzr.guess_forge
+ bzr = BzrAdapter.new( :url => 'bzr://www.selenic.com/repo/hello')
+ assert_equal 'www.selenic.com', bzr.guess_forge
+
bzr = BzrAdapter.new( :url => 'http://www.selenic.com/repo/hello')
assert_equal 'www.selenic.com', bzr.guess_forge
end
diff --git a/test/unit/bzr_xml_parser_test.rb b/test/unit/bzr_xml_parser_test.rb
new file mode 100644
index 00000000..04049cd8
--- /dev/null
+++ b/test/unit/bzr_xml_parser_test.rb
@@ -0,0 +1,409 @@
+require_relative '../test_helper'
+
+module OhlohScm::Parsers
+ class BzrXmlParserTest < OhlohScm::Test
+
+ def test_empty_array
+ assert_equal([], BzrXmlParser.parse(''))
+ end
+
+ def test_empty_xml
+ assert_equal("\n\n\n", BzrXmlParser.parse('', :writer => XmlWriter.new))
+ end
+
+ def test_basic_xml
+ xml = <<-XML
+
+
+
+ 10
+ test@example.com-20110725174345-brbpkwumeh07aoh8
+
+ amujumdar@blackducksoftware.com-20110722185038-e0i4d1mdxwpipxc4
+
+ test <test@example.com>
+ myproject
+ Mon 2011-07-25 13:43:45 -0400
+
+
+
+ XML
+ commits = BzrXmlParser.parse(xml)
+ assert_equal 1, commits.size
+ c = commits.first
+ assert_equal 0, c.diffs.size
+ assert_equal "Renamed test1.txt to subdir/test_b.txt, removed test2.txt and added test_a.txt.", c.message
+ assert_equal "test@example.com-20110725174345-brbpkwumeh07aoh8", c.token
+ end
+
+ def test_verbose_xml
+ xml = <<-XML
+
+
+
+ 10
+ test@example.com-20110725174345-brbpkwumeh07aoh8
+
+ amujumdar@blackducksoftware.com-20110722185038-e0i4d1mdxwpipxc4
+
+ test <test@example.com>
+ myproject
+ Mon 2011-07-25 13:43:45 -0400
+
+
+
+ test2.txt
+
+
+ test_a.txt
+
+
+ subdir/test_b.txt
+
+
+
+
+ XML
+ commits = BzrXmlParser.parse(xml)
+ assert_equal 1, commits.size
+ c = commits.first
+ assert_equal 4, c.diffs.size # Rename is a D followed by A
+
+ assert_equal "test2.txt", c.diffs[0].path
+ assert_equal "D", c.diffs[0].action
+
+ assert_equal "test_a.txt", c.diffs[1].path
+ assert_equal "A", c.diffs[1].action
+
+ assert_equal "test1.txt", c.diffs[2].path
+ assert_equal "D", c.diffs[2].action
+
+ assert_equal "subdir/test_b.txt", c.diffs[3].path
+ assert_equal "A", c.diffs[3].action
+ end
+
+ # When an directory is deleted, bzr outputs one delete entry
+ # per file and one for the directory. For empty dirs, there
+ # is only one directory remove entry.
+ # Ohloh keeps file delete entries but ignores directory
+ # delete entry.
+ def test_ignore_dir_delete_xml
+ xml = <<-XML
+
+
+
+ 720.9.33
+ jano.vesely@gmail.com-20110127220929-d3af6kj4d53lh70t
+
+ jano.vesely@gmail.com-20110125225108-0vxoig7z3d3q0w0w
+ vojtechhorky@users.sourceforge.net-20110126145255-4xdar4rxwcrh6s0a
+
+ Jan Vesely <jano.vesely@gmail.com>
+ helenos
+ Thu 2011-01-27 23:09:29 +0100
+
+
+
+ uspace/lib/net/include/nil_interface.h
+ uspace/srv/hw/bus/usb/
+ uspace/srv/hw/bus/usb/hcd/
+
+
+
+
+ XML
+ commits = BzrXmlParser.parse(xml)
+ assert_equal 1, commits.size
+
+ c = commits.first
+ assert_equal 1, c.diffs.size
+ assert_equal "uspace/lib/net/include/nil_interface.h", c.diffs.first.path
+ end
+
+ # bzr also outputs a kind_changed entry when file kind changes, for example
+ # a symlink is changed to file.
+ # Ohloh ignores such changes.
+ def test_ignore_kind_changed_xml
+ xml = <<-XML
+
+
+
+ 720.9.33
+ jano.vesely@gmail.com-20110127220929-d3af6kj4d53lh70t
+
+ jano.vesely@gmail.com-20110125225108-0vxoig7z3d3q0w0w
+ vojtechhorky@users.sourceforge.net-20110126145255-4xdar4rxwcrh6s0a
+
+ Jan Vesely <jano.vesely@gmail.com>
+ helenos
+ Thu 2011-01-27 23:09:29 +0100
+
+
+
+ uspace/lib/net/include/nil_interface.h
+
+
+ uspace/app/tester/mm/mapping1.c
+
+
+ uspace/lib/usb/include/usb/hcd.h
+ uspace/lib/usb/include/usb/addrkeep.h
+
+
+ .bzrignore
+
+
+
+
+ XML
+ commits = BzrXmlParser.parse(xml)
+ assert_equal 1, commits.size
+
+ c = commits.first
+ assert_equal 3, c.diffs.size
+ assert_equal "D", c.diffs[0].action
+ assert_equal "uspace/lib/net/include/nil_interface.h", c.diffs[0].path
+ assert_equal "A", c.diffs[1].action
+ assert_equal "uspace/app/tester/mm/mapping1.c", c.diffs[1].path
+ assert_equal "M", c.diffs[2].action
+ assert_equal ".bzrignore", c.diffs[2].path
+ end
+
+ def test_different_author_and_committer
+ xml = <<-XML
+
+
+ 1
+ amujumdar@blackducksoftware.com-20111011152356-90nluwydpw9g4ncu
+ Abhay Mujumdar <amujumdar@blackducksoftware.com>
+ bzr_with_authors
+ Tue 2011-10-11 11:23:56 -0400
+
+
+
+ 2
+ amujumdar@blackducksoftware.com-20111011152412-l9ehyruiezws32kj
+
+ amujumdar@blackducksoftware.com-20111011152356-90nluwydpw9g4ncu
+
+ Abhay Mujumdar <amujumdar@blackducksoftware.com>
+
+ John Doe <johndoe@example.com>
+
+ bzr_with_authors
+ Tue 2011-10-11 11:24:12 -0400
+
+
+
+ 3
+ test@example.com-20111011162601-ud1nidteswfdbhbu
+
+ amujumdar@blackducksoftware.com-20111011152412-l9ehyruiezws32kj
+
+ test <test@example.com>
+
+ Jim Beam <jimbeam@example.com>
+ Jane Smith <janesmith@example.com>
+
+ bzr_with_authors
+ Tue 2011-10-11 12:26:01 -0400
+
+
+
+ 4
+ test@example.com-20111011162601-dummyrevision
+
+ test@example.com-20111011162601-ud1nidteswfdbhbu
+
+ test <test@example.com>
+ bzr_with_authors
+ Tue 2011-10-11 12:28:01 -0400
+
+
+
+ XML
+ commits = BzrXmlParser.parse(xml)
+ c = commits[0]
+ assert_equal "Abhay Mujumdar", c.committer_name
+ assert_equal "amujumdar@blackducksoftware.com", c.committer_email
+
+ c = commits[1]
+ assert_equal "Abhay Mujumdar", c.committer_name
+ assert_equal "amujumdar@blackducksoftware.com", c.committer_email
+ assert_equal "John Doe", c.author_name
+ assert_equal "johndoe@example.com", c.author_email
+
+ c = commits[2]
+ assert_equal "test", c.committer_name
+ assert_equal "test@example.com", c.committer_email
+ assert_equal "Jim Beam", c.author_name
+ assert_equal "jimbeam@example.com", c.author_email
+
+ c = commits[3]
+ assert_equal "test", c.committer_name
+ assert_equal "test@example.com", c.committer_email
+ assert_equal nil, c.author_name
+ assert_equal nil, c.author_email
+ end
+
+ def test_rename
+ xml = <<-XML
+
+
+ 10
+ test@example.com-20110725174345-brbpkwumeh07aoh8
+
+ amujumdar@blackducksoftware.com-20110722185038-e0i4d1mdxwpipxc4
+
+ test <test@example.com>
+ myproject
+ Mon 2011-07-25 13:43:45 -0400
+
+
+
+ subdir/test_b.txt
+
+
+
+
+ XML
+ commits = BzrXmlParser.parse(xml)
+ assert_equal 1, commits.size
+ assert_equal 2, commits.first.diffs.size
+ assert_equal 'D', commits.first.diffs.first.action
+ assert_equal 'test1.txt', commits.first.diffs.first.path
+
+ assert_equal 'A', commits.first.diffs.last.action
+ assert_equal 'subdir/test_b.txt', commits.first.diffs.last.path
+ end
+
+ def test_remove_dupes_add_remove
+ diffs = BzrXmlParser.remove_dupes([ OhlohScm::Diff.new(:action => "A", :path => "foo"),
+ OhlohScm::Diff.new(:action => "D", :path => "foo") ])
+ assert_equal 1, diffs.size
+ assert_equal 'M', diffs.first.action
+ assert_equal 'foo', diffs.first.path
+ end
+
+ # A complex delete/rename/modify test.
+ # Removed test_a.txt, Renamed test3.txt to test_a.txt, edited test_a.txt
+ #
+ # This is what Ohloh expects to see:
+ #
+ # D test3.txt
+ # M test_a.txt
+ #
+ def test_complex_rename
+ xml = <<-XML
+
+
+ 11
+ test@example.com-20111012191732-h3bt3lp6l38vbm9v
+
+ test@example.com-20110725174345-brbpkwumeh07aoh8
+
+ test <test@example.com>
+ myproject
+ Wed 2011-10-12 15:17:32 -0400
+
+
+
+ test_a.txt
+
+
+ test_a.txt
+
+
+ test_a.txt
+
+
+
+
+ XML
+ diffs = BzrXmlParser.parse(xml).first.diffs
+ diffs.sort! { |a,b| a.action <=> b.action }
+ assert_equal 2, diffs.size
+ assert_equal 'D', diffs.first.action
+ assert_equal 'test3.txt', diffs.first.path
+ assert_equal 'M', diffs.last.action
+ assert_equal 'test_a.txt', diffs.last.path
+ end
+
+ # This test-case also tests rename and modify in the same commit.
+ # A rename and modify should result in a DELETE and an ADD.
+ def test_strip_trailing_asterisk_from_executables
+ xml = <<-XML
+
+
+ 14
+ test@example.com-20111012195747-seei62z2wmefjhmo
+
+ test@example.com-20111012195606-0shla0kf4e8hq4mq
+
+ test <test@example.com>
+ myproject
+ Wed 2011-10-12 15:57:47 -0400
+
+
+
+ renamed_arch
+
+
+ renamed_arch*
+
+
+
+
+ XML
+ diffs = BzrXmlParser.parse(xml).first.diffs
+ diffs.sort! { |a,b| a.action <=> b.action }
+ assert_equal 'A', diffs[0].action
+ assert_equal 'renamed_arch', diffs[0].path
+ assert_equal 'D', diffs[1].action
+ assert_equal 'arch', diffs[1].path
+ end
+
+ def test_committer_name_capture
+ name, email = BzrXmlParser.capture_name('John')
+ assert_equal name, 'John'
+ assert_equal email, nil
+ assert_equal ['John Doe', nil], BzrXmlParser.capture_name('John Doe')
+ assert_equal ['John Doe jdoe@example.com', nil], BzrXmlParser.capture_name('John Doe jdoe@example.com')
+ assert_equal ['John Doe ', nil], BzrXmlParser.capture_name('John Doe jdoe@example.com>')
+ assert_equal ['John Doe', 'jdoe@example.com'], BzrXmlParser.capture_name('John Doe ')
+ assert_equal ['John Doe', 'jdoe@example.com'], BzrXmlParser.capture_name('John Doe ')
+ assert_equal ['jdoe@example.com', nil], BzrXmlParser.capture_name('jdoe@example.com')
+ xml = <<-XML
+
+
+ 15
+ a@b.com-20111013152207-q8uec9pp1330gfbh
+
+ test@example.com-20111012195747-seei62z2wmefjhmo
+
+ a@b.com
+
+ author@c.com
+
+ myproject
+ Thu 2011-10-13 11:22:07 -0400
+
+
+
+ test_a.txt
+
+
+
+
+ XML
+ c = BzrXmlParser.parse(xml).first
+ assert_equal 'M', c.diffs.first.action
+ assert_equal 'test_a.txt', c.diffs.first.path
+ assert_equal 'a@b.com', c.committer_name
+ assert_equal nil, c.committer_email
+ assert_equal 'author@c.com', c.author_name
+ assert_equal nil, c.author_email
+ end
+ end
+end
diff --git a/test/unit/bzrlib_cat_file_test.rb b/test/unit/bzrlib_cat_file_test.rb
new file mode 100644
index 00000000..3579606b
--- /dev/null
+++ b/test/unit/bzrlib_cat_file_test.rb
@@ -0,0 +1,56 @@
+# encoding: utf-8
+require_relative '../test_helper'
+
+module OhlohScm::Adapters
+ class BzrlibCatFileTest < OhlohScm::Test
+
+ def test_cat_file
+ with_bzrlib_repository('bzr') do |bzr|
+ expected = <<-EXPECTED
+first file
+second line
+EXPECTED
+ assert_equal expected,
+ bzr.cat_file(OhlohScm::Commit::new(:token => 6),
+ OhlohScm::Diff.new(:path => "file1.txt"))
+
+ # file2.txt has been removed in commit #5
+ assert_equal nil, bzr.cat_file(bzr.head,
+ OhlohScm::Diff.new(:path => "file2.txt"))
+ end
+ end
+
+ def test_cat_file_non_ascii_name
+ with_bzrlib_repository('bzr') do |bzr|
+ expected = <<-EXPECTED
+first file
+second line
+EXPECTED
+ assert_equal expected,
+ bzr.cat_file(OhlohScm::Commit::new(:token => 7),
+ OhlohScm::Diff.new(:path => "Cédric.txt"))
+ end
+ end
+
+ def test_cat_file_parent
+ with_bzrlib_repository('bzr') do |bzr|
+ expected = <<-EXPECTED
+first file
+second line
+EXPECTED
+ assert_equal expected,
+ bzr.cat_file_parent(OhlohScm::Commit::new(:token => 6),
+ OhlohScm::Diff.new(:path => "file1.txt"))
+
+ # file2.txt has been removed in commit #5
+ expected = <<-EXPECTED
+another file
+EXPECTED
+ assert_equal expected,
+ bzr.cat_file_parent(OhlohScm::Commit.new(:token => 5),
+ OhlohScm::Diff.new(:path => "file2.txt"))
+ end
+ end
+
+ end
+end
diff --git a/test/unit/bzrlib_head_test.rb b/test/unit/bzrlib_head_test.rb
new file mode 100644
index 00000000..12e4268d
--- /dev/null
+++ b/test/unit/bzrlib_head_test.rb
@@ -0,0 +1,18 @@
+require_relative '../test_helper'
+
+module OhlohScm::Adapters
+ class BzrBzrlibHeadTest < OhlohScm::Test
+
+ def test_head_and_parents
+ with_bzrlib_repository('bzr') do |bzr|
+ assert_equal 'test@example.com-20111222183733-y91if5npo3pe8ifs', bzr.head_token
+ assert_equal 'test@example.com-20111222183733-y91if5npo3pe8ifs', bzr.head.token
+ assert bzr.head.diffs.any? # diffs should be populated
+
+ assert_equal 'obnox@samba.org-20090204004942-73rnw0izen42f154', bzr.parents(bzr.head).first.token
+ assert bzr.parents(bzr.head).first.diffs.any?
+ end
+ end
+
+ end
+end
diff --git a/test/unit/cvs_branch_number_test.rb b/test/unit/cvs_branch_number_test.rb
index 272cc8da..03ede171 100644
--- a/test/unit/cvs_branch_number_test.rb
+++ b/test/unit/cvs_branch_number_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Parsers
- class CvsBranchNumberTest < Scm::Test
+module OhlohScm::Parsers
+ class CvsBranchNumberTest < OhlohScm::Test
def test_basic
assert_equal [1,1], BranchNumber.new('1.1').to_a
assert_equal [1234,1234], BranchNumber.new('1234.1234').to_a
@@ -69,21 +69,6 @@ def test_complex_primary_revision_number_change
assert !b.inherits_from?(BranchNumber.new('3.1'))
end
- # Crazy CVS inserts a zero before the last piece of a branch number
- def test_magic_branch_numbers
- assert BranchNumber.new('1.1.2.1').inherits_from?(BranchNumber.new('1.1.0.2'))
- assert BranchNumber.new('1.1.2.1.2.1').inherits_from?(BranchNumber.new('1.1.0.2'))
-
- assert BranchNumber.new('1.1.0.2').inherits_from?(BranchNumber.new('1.1'))
- assert !BranchNumber.new('1.1.0.2').inherits_from?(BranchNumber.new('1.2'))
- assert !BranchNumber.new('1.1.0.2').inherits_from?(BranchNumber.new('1.1.2.1'))
-
- assert BranchNumber.new('1.1.0.4').inherits_from?(BranchNumber.new('1.1'))
- assert !BranchNumber.new('1.1.0.4').inherits_from?(BranchNumber.new('1.1.2.1'))
- assert !BranchNumber.new('1.1.0.4').inherits_from?(BranchNumber.new('1.1.0.2'))
- assert !BranchNumber.new('1.1.0.4').inherits_from?(BranchNumber.new('1.1.4.1'))
- assert !BranchNumber.new('1.1.0.4').inherits_from?(BranchNumber.new('1.1.0.6'))
- end
def test_simple_on_same_line
b = BranchNumber.new('1.3')
@@ -125,27 +110,6 @@ def test_complex_on_same_line
assert !b.on_same_line?(BranchNumber.new('1.3.6.3.2.99.2.1'))
end
- def test_primary_revision_number_change
- b = BranchNumber.new('2.3')
-
- assert b.on_same_line?(BranchNumber.new('2.2'))
- assert b.on_same_line?(BranchNumber.new('2.1'))
- assert b.on_same_line?(BranchNumber.new('1.1'))
- assert b.on_same_line?(BranchNumber.new('1.9999'))
- assert b.on_same_line?(BranchNumber.new('2.4'))
- assert b.on_same_line?(BranchNumber.new('3.1'))
- end
-
- def test_complex_primary_revision_number_change
- b = BranchNumber.new('2.3.2.1')
-
- assert b.on_same_line?(BranchNumber.new('2.3'))
- assert b.on_same_line?(BranchNumber.new('2.2'))
- assert b.on_same_line?(BranchNumber.new('1.1'))
- assert b.on_same_line?(BranchNumber.new('1.9999'))
- assert !b.on_same_line?(BranchNumber.new('2.4'))
- assert !b.on_same_line?(BranchNumber.new('3.1'))
- end
# Crazy CVS inserts a zero before the last piece of a branch number
def test_magic_branch_numbers
diff --git a/test/unit/cvs_commits_test.rb b/test/unit/cvs_commits_test.rb
index 83cb55f2..575bdca4 100644
--- a/test/unit/cvs_commits_test.rb
+++ b/test/unit/cvs_commits_test.rb
@@ -1,27 +1,52 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class CvsCommitsTest < Scm::Test
+module OhlohScm::Adapters
+ class CvsCommitsTest < OhlohScm::Test
def test_commits
- with_cvs_repository('cvs') do |cvs|
+ with_cvs_repository('cvs', 'simple') do |cvs|
- assert_equal ['2006/06/29 16:19:58',
- '2006/06/29 16:21:07',
- '2006/06/29 18:14:47',
- '2006/06/29 18:45:29',
- '2006/06/29 18:48:54',
- '2006/06/29 18:52:23'], cvs.commits.collect { |c| c.token }
-
- assert_equal ['2006/06/29 18:48:54',
- '2006/06/29 18:52:23'], cvs.commits('2006/06/29 18:45:29').collect { |c| c.token }
+ assert_equal ['2006-06-29 16:21:07',
+ '2006-06-29 18:14:47',
+ '2006-06-29 18:45:29',
+ '2006-06-29 18:48:54',
+ '2006-06-29 18:52:23'], cvs.commits.collect { |c| c.token }
# Make sure we are date format agnostic (2008/01/01 is the same as 2008-01-01)
- assert_equal ['2006/06/29 18:48:54',
- '2006/06/29 18:52:23'], cvs.commits('2006-06-29 18:45:29').collect { |c| c.token }
+ assert_equal ['2006-06-29 18:48:54',
+ '2006-06-29 18:52:23'],
+ cvs.commits(:after => '2006/06/29 18:45:29').collect { |c| c.token }
+
+ assert_equal ['2006-06-29 18:48:54',
+ '2006-06-29 18:52:23'],
+ cvs.commits(:after => '2006-06-29 18:45:29').collect { |c| c.token }
- assert_equal [], cvs.commits('2006/06/29 18:52:23').collect { |c| c.token }
+ assert_equal [], cvs.commits(:after => '2006/06/29 18:52:23').collect { |c| c.token }
end
end
+
+ def test_commits_sets_scm
+ with_cvs_repository('cvs', 'simple') do |cvs|
+ cvs.commits.each do |c|
+ assert_equal cvs, c.scm
+ end
+ end
+ end
+
+ def test_open_log_file_encoding
+ with_cvs_repository('cvs', 'invalid_utf8') do |cvs|
+ cvs.open_log_file do |io|
+ assert_equal true, io.read.valid_encoding?
+ end
+ end
+ end
+
+ def test_commits_valid_encoding
+ with_cvs_repository('cvs', 'invalid_utf8') do |cvs|
+ assert_nothing_raised do
+ cvs.commits
+ end
+ end
+ end
end
end
diff --git a/test/unit/cvs_convert_test.rb b/test/unit/cvs_convert_test.rb
index 9a3da406..4f8550b1 100644
--- a/test/unit/cvs_convert_test.rb
+++ b/test/unit/cvs_convert_test.rb
@@ -1,11 +1,11 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class CvsConvertTest < Scm::Test
+module OhlohScm::Adapters
+ class CvsConvertTest < OhlohScm::Test
def test_basic_convert
- with_cvs_repository('cvs') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ with_cvs_repository('cvs', 'simple') do |src|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = GitAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
diff --git a/test/unit/cvs_misc_test.rb b/test/unit/cvs_misc_test.rb
index 59e7ece7..9603f071 100644
--- a/test/unit/cvs_misc_test.rb
+++ b/test/unit/cvs_misc_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class CvsMiscTest < Scm::Test
+module OhlohScm::Adapters
+ class CvsMiscTest < OhlohScm::Test
def test_local_directory_trim
r = CvsAdapter.new(:url => "/Users/robin/cvs_repo/", :module_name => "simple")
assert_equal "/Users/robin/cvs_repo/simple/foo.rb", r.trim_directory('/Users/robin/cvs_repo/simple/foo.rb')
@@ -45,5 +45,38 @@ def test_ordered_directory_list_ignores_Attic
assert_equal "hello", l[2]
assert_equal "foo/bar", l[3]
end
+
+ def host
+ r = CvsAdapter.new(:url => ':ext:anonymous:@moodle.cvs.sourceforge.net:/cvsroot/moodle', :module_name => 'contrib')
+ assert_equal 'moodle.cvs.sourceforge.net', r.host
+ end
+
+ def protocol
+ assert_equal :pserver, CvsAdapter.new(:url => ':pserver:foo:@foo.com:/cvsroot/a', :module_name => 'b')
+ assert_equal :ext, CvsAdapter.new(:url => ':ext:foo:@foo.com:/cvsroot/a', :module_name => 'b')
+ assert_equal :pserver, CvsAdapter.new(:url => ':pserver:ext:@foo.com:/cvsroot/a', :module_name => 'b')
+ end
+
+ def test_log_encoding
+ with_cvs_repository('cvs', 'invalid_utf8') do |cvs|
+ assert_equal true, cvs.log.valid_encoding?
+ end
+ end
+
+ def test_tags
+ with_cvs_repository('cvs', 'simple') do |cvs|
+ assert_equal([['simple_release_tag', '1.1.1.1'], ['simple_vendor_tag', '1.1.1']], cvs.tags)
+ end
+ end
+
+ def test_export_tag
+ with_cvs_repository('cvs', 'simple') do |cvs|
+ OhlohScm::ScratchDir.new do |dir|
+ cvs.export_tag(dir, 'simple_release_tag')
+
+ assert_equal ['.','..','foo.rb'], Dir.entries(dir).sort
+ end
+ end
+ end
end
end
diff --git a/test/unit/cvs_parser_test.rb b/test/unit/cvs_parser_test.rb
index ac3ae676..2af1c97d 100644
--- a/test/unit/cvs_parser_test.rb
+++ b/test/unit/cvs_parser_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Parsers
- class CvsParserTest < Scm::Test
+module OhlohScm::Parsers
+ class CvsParserTest < OhlohScm::Test
def test_basic
assert_convert(CvsParser, DATA_DIR + '/basic.rlog', DATA_DIR + '/basic.ohlog')
diff --git a/test/unit/cvs_validation_test.rb b/test/unit/cvs_validation_test.rb
index c3fe9bc9..1f04a63b 100644
--- a/test/unit/cvs_validation_test.rb
+++ b/test/unit/cvs_validation_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class CvsValidationTest < Scm::Test
+module OhlohScm::Adapters
+ class CvsValidationTest < OhlohScm::Test
def test_rejected_urls
[ nil, "", "foo", "http:/", "http:://", "http://", "http://a",
":pserver", # that's not enough
@@ -19,7 +19,8 @@ def test_rejected_urls
"sourceforge.net/svn/project/trunk", # missing a protocol prefix
"file:///home/robin/cvs", # file protocol is not allowed
"http://svn.sourceforge.net", # http protocol is not allowed
- "git://kernel.org/whatever/linux.git" # git protocol is not allowed
+ "git://kernel.org/whatever/linux.git", # git protocol is not allowed
+ "ext@kernel.org/whatever/linux.git" # ext protocol allowed, but starts with ':'
].each do |url|
# Rejected for both internal and public use
[true, false].each do |p|
@@ -42,7 +43,8 @@ def test_accepted_urls
":pserver:anonymous:@sc2.cvs.sourceforge.net:/cvsroot/sc2",
":pserver:cool-dev:@sc2.cvs.sourceforge.net:/cvsroot/sc2", # Hyphen should be OK in username
":pserver:cvs_anon:@cvs.scms.waikato.ac.nz:/usr/local/global-cvs/ml_cvs", # Underscores should be ok in path
- ":pserver:anonymous:freefem++@idared.ann.jussieu.fr:/Users/pubcvs/cvs" # Pluses should be OK
+ ":pserver:anonymous:freefem++@idared.ann.jussieu.fr:/Users/pubcvs/cvs", # Pluses should be OK
+ ":ext:anoncvs@opensource.conformal.com:/anoncvs/scrotwm" # scrotwm is a real life example
].each do |url|
# Valid for both internal and public use
[true, false].each do |p|
@@ -130,11 +132,17 @@ def test_guess_forge
cvs = CvsAdapter.new(:url => "garbage_in_garbage_out")
assert_equal nil, cvs.guess_forge
+ cvs = CvsAdapter.new(:url => ':pserver:anonymous:@boost.cvs.sourceforge.net:/cvsroot/boost')
+ assert_equal 'sourceforge.net', cvs.guess_forge
+
cvs = CvsAdapter.new(:url => ':pserver:guest:@cvs.dev.java.net:/cvs')
assert_equal 'java.net', cvs.guess_forge
cvs = CvsAdapter.new(:url => ":PSERVER:ANONCVS:@CVS.DEV.JAVA.NET:/cvs")
assert_equal 'java.net', cvs.guess_forge
+
+ cvs = CvsAdapter.new(:url => ":pserver:guest:@colorchooser.dev.java.net:/cvs")
+ assert_equal 'java.net', cvs.guess_forge
end
end
end
diff --git a/test/unit/git_cat_file_test.rb b/test/unit/git_cat_file_test.rb
index 4e2b0a2d..900c8561 100644
--- a/test/unit/git_cat_file_test.rb
+++ b/test/unit/git_cat_file_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitCatFileTest < Scm::Test
+module OhlohScm::Adapters
+ class GitCatFileTest < OhlohScm::Test
def test_cat_file
with_git_repository('git') do |git|
@@ -13,7 +13,7 @@ def test_cat_file
printf("Hello, World!\\n");
}
EXPECTED
- assert_equal expected, git.cat_file(nil, Scm::Diff.new(:sha1 => '4c734ad53b272c9b3d719f214372ac497ff6c068'))
+ assert_equal expected, git.cat_file(nil, OhlohScm::Diff.new(:sha1 => '4c734ad53b272c9b3d719f214372ac497ff6c068'))
end
end
diff --git a/test/unit/git_commit_all_test.rb b/test/unit/git_commit_all_test.rb
index 0fdb111b..e9944eee 100644
--- a/test/unit/git_commit_all_test.rb
+++ b/test/unit/git_commit_all_test.rb
@@ -1,10 +1,10 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitCommitAllTest < Scm::Test
+module OhlohScm::Adapters
+ class GitCommitAllTest < OhlohScm::Test
def test_commit_all
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
git = GitAdapter.new(:url => dir).normalize
git.init_db
@@ -13,7 +13,7 @@ def test_commit_all
File.open(File.join(dir, 'README'), 'w') {}
assert git.anything_to_commit?
- c = Scm::Commit.new
+ c = OhlohScm::Commit.new
c.author_name = "John Q. Developer"
c.message = "Initial checkin."
git.commit_all(c)
diff --git a/test/unit/git_commits_test.rb b/test/unit/git_commits_test.rb
index dc2c8ae8..3b1f9b71 100644
--- a/test/unit/git_commits_test.rb
+++ b/test/unit/git_commits_test.rb
@@ -1,35 +1,165 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitCommitsTest < Scm::Test
+module OhlohScm::Adapters
+ class GitCommitsTest < OhlohScm::Test
- def test_commit
+ def test_commit_count
with_git_repository('git') do |git|
assert_equal 4, git.commit_count
- assert_equal 2, git.commit_count('b6e9220c3cabe53a4ed7f32952aeaeb8a822603d')
- assert_equal 0, git.commit_count('1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+ assert_equal 2, git.commit_count(:after => 'b6e9220c3cabe53a4ed7f32952aeaeb8a822603d')
+ assert_equal 0, git.commit_count(:after => '1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+ end
+ end
+ def test_commit_tokens
+ with_git_repository('git') do |git|
assert_equal ['089c527c61235bd0793c49109b5bd34d439848c6',
'b6e9220c3cabe53a4ed7f32952aeaeb8a822603d',
'2e9366dd7a786fdb35f211fff1c8ea05c51968b1',
'1df547800dcd168e589bb9b26b4039bff3a7f7e4'], git.commit_tokens
assert_equal ['1df547800dcd168e589bb9b26b4039bff3a7f7e4'],
- git.commit_tokens('2e9366dd7a786fdb35f211fff1c8ea05c51968b1')
+ git.commit_tokens(:after => '2e9366dd7a786fdb35f211fff1c8ea05c51968b1')
- assert_equal [], git.commit_tokens('1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+ assert_equal [], git.commit_tokens(:after => '1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+ end
+ end
+ def test_commits
+ with_git_repository('git') do |git|
assert_equal ['089c527c61235bd0793c49109b5bd34d439848c6',
'b6e9220c3cabe53a4ed7f32952aeaeb8a822603d',
'2e9366dd7a786fdb35f211fff1c8ea05c51968b1',
'1df547800dcd168e589bb9b26b4039bff3a7f7e4'], git.commits.collect { |c| c.token }
assert_equal ['1df547800dcd168e589bb9b26b4039bff3a7f7e4'],
- git.commits('2e9366dd7a786fdb35f211fff1c8ea05c51968b1').collect { |c| c.token }
+ git.commits(:after => '2e9366dd7a786fdb35f211fff1c8ea05c51968b1').collect { |c| c.token }
+
+ assert_equal [], git.commits(:after => '1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+ end
+ end
+
+ def test_commits_with_branch
+ with_git_repository('git', 'develop') do |git|
+ assert_equal ['089c527c61235bd0793c49109b5bd34d439848c6',
+ 'b6e9220c3cabe53a4ed7f32952aeaeb8a822603d',
+ '2e9366dd7a786fdb35f211fff1c8ea05c51968b1',
+ 'b4046b9a80fead62fa949232f2b87b0cb78fffcc'], git.commits.map(&:token)
+
+ assert_equal ['b4046b9a80fead62fa949232f2b87b0cb78fffcc'],
+ git.commits(:after => '2e9366dd7a786fdb35f211fff1c8ea05c51968b1').map(&:token)
+
+ assert_equal [], git.commits(:after => 'b4046b9a80fead62fa949232f2b87b0cb78fffcc')
+ end
+ end
+
+ def test_trunk_only_commit_count
+ with_git_repository('git_dupe_delete') do |git|
+ assert_equal 4, git.commit_count(:trunk_only => false)
+ assert_equal 3, git.commit_count(:trunk_only => true)
+ end
+ end
- assert_equal [], git.commits('1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+ def test_trunk_only_commit_tokens
+ with_git_repository('git_dupe_delete') do |git|
+ assert_equal ['a0a2b8623941562031a7d7f95d984feb4a2d719c',
+ 'ad6bb43112706c462e53a9a8a8cd3b05f8e9260f',
+ '6126337d2497806528fd8657181d5d4afadd72a4', # On branch
+ '41c4b1044ebffc968d363e5f5e883134e624f846'],
+ git.commit_tokens(:trunk_only => false)
+
+ assert_equal ['a0a2b8623941562031a7d7f95d984feb4a2d719c',
+ 'ad6bb43112706c462e53a9a8a8cd3b05f8e9260f',
+ # '6126337d2497806528fd8657181d5d4afadd72a4', # On branch
+ '41c4b1044ebffc968d363e5f5e883134e624f846'],
+ git.commit_tokens(:trunk_only => true)
+ end
+ end
+
+ def test_trunk_only_commit_tokens_using_after
+ with_git_repository('git_dupe_delete') do |git|
+ assert_equal ['ad6bb43112706c462e53a9a8a8cd3b05f8e9260f',
+ '41c4b1044ebffc968d363e5f5e883134e624f846'],
+ git.commit_tokens(
+ :after => 'a0a2b8623941562031a7d7f95d984feb4a2d719c',
+ :trunk_only => true)
+
+ # All trunk commit_tokens, with :after == HEAD
+ assert_equal [], git.commit_tokens(
+ :after => '41c4b1044ebffc968d363e5f5e883134e624f846',
+ :trunk_only => true)
+ end
+ end
+
+ def test_trunk_only_commits
+ with_git_repository('git_dupe_delete') do |git|
+ assert_equal ['a0a2b8623941562031a7d7f95d984feb4a2d719c',
+ 'ad6bb43112706c462e53a9a8a8cd3b05f8e9260f',
+ # The following commit is on a branch and should be excluded
+ # '6126337d2497806528fd8657181d5d4afadd72a4',
+ '41c4b1044ebffc968d363e5f5e883134e624f846'],
+ git.commits(:trunk_only => true).collect { |c| c.token }
end
end
+ def test_trunk_only_commits_using_after
+ with_git_repository('git_dupe_delete') do |git|
+ assert_equal ['ad6bb43112706c462e53a9a8a8cd3b05f8e9260f',
+ '41c4b1044ebffc968d363e5f5e883134e624f846'],
+ git.commits(:after => 'a0a2b8623941562031a7d7f95d984feb4a2d719c',
+ :trunk_only => true).collect { |c| c.token }
+
+ assert_equal [], git.commit_tokens(
+ :after => '41c4b1044ebffc968d363e5f5e883134e624f846',
+ :trunk_only => true)
+ end
+ end
+
+ # In rare cases, a merge commit's resulting tree is identical to its first parent's tree.
+ # I believe this is a result of developer trickery, and not a common situation.
+ #
+ # When this happens, `git whatchanged` will omit the changes relative to the first parent,
+ # and instead output only the changes relative to the second parent.
+ #
+ # Our commit parser became confused by this, assuming that these changes relative to the
+ # second parent were in fact the missing changes relative to the first.
+ #
+ # This is bug OTWO-623. This test confirms the fix.
+ def test_verbose_commit_with_null_merge
+ with_git_repository('git_with_null_merge') do |git|
+ c = git.verbose_commit('d3bd0bedbf4b197b2c4eb827e1ec4c35b834482f')
+ # This commit's tree is identical to its parent's. Thus it should contain no diffs.
+ assert_equal [], c.diffs
+ end
+ end
+
+ def test_each_commit_with_null_merge
+ with_git_repository('git_with_null_merge') do |git|
+ git.each_commit do |c|
+ assert_equal [], c.diffs if c.token == 'd3bd0bedbf4b197b2c4eb827e1ec4c35b834482f'
+ end
+ end
+ end
+
+ def test_log_encoding
+ with_git_repository('git_with_invalid_encoding') do |git|
+ assert_equal true, git.log.valid_encoding?
+ end
+ end
+
+ def test_verbose_commits_valid_encoding
+ with_git_repository('git_with_invalid_encoding') do |git|
+ assert_equal true,
+ git.verbose_commit('8d03f4ea64fcd10966fb3773a212b141ada619e1').message.valid_encoding?
+ end
+ end
+
+ def test_open_log_file_encoding
+ with_git_repository('git_with_invalid_encoding') do |git|
+ git.open_log_file do |io|
+ assert_equal true, io.read.valid_encoding?
+ end
+ end
+ end
end
end
diff --git a/test/unit/git_head_test.rb b/test/unit/git_head_test.rb
index ca5bec94..e193bd99 100644
--- a/test/unit/git_head_test.rb
+++ b/test/unit/git_head_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitHeadTest < Scm::Test
+module OhlohScm::Adapters
+ class GitHeadTest < OhlohScm::Test
def test_head_and_parents
with_git_repository('git') do |git|
@@ -15,5 +15,12 @@ def test_head_and_parents
end
end
+ def test_head_token
+ with_git_repository('git_with_invalid_encoding') do |git|
+ assert_nothing_raised do
+ git.head_token
+ end
+ end
+ end
end
end
diff --git a/test/unit/git_log_parser_test.rb b/test/unit/git_log_parser_test.rb
index 0d1eec24..f1f3554b 100644
--- a/test/unit/git_log_parser_test.rb
+++ b/test/unit/git_log_parser_test.rb
@@ -1,8 +1,8 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
require 'date'
-module Scm::Parsers
- class GitStyledParserTest < Scm::Test
+module OhlohScm::Parsers
+ class GitStyledParserTest < OhlohScm::Test
def test_basic
commits = []
@@ -187,7 +187,7 @@ def test_use_email_when_names_are_missing
__BEGIN_COMMIT__
Commit: fa3ee9d4cefc2db81adadf36da9cacbe92ce96f1
-Author:
+Author:
AuthorEmail: mickeyl@openembedded.org
Date: Wed, 11 Jun 2008 00:37:06 +0000
__BEGIN_COMMENT__
@@ -209,5 +209,16 @@ def test_use_email_when_names_are_missing
assert_equal 'mickeyl', commits.first.author_name # Use name when present
assert_equal 'mickeyl@openembedded.org', commits.last.author_name # Else use email
end
+
+ # Verifies OTWO-443
+ def test_empty_merge
+ with_git_repository('git_with_empty_merge') do |git|
+ assert_equal 5, git.commit_count
+ assert_equal 5, git.commits.size
+ c = git.verbose_commit('ff13970b54e5bc373abf932f0708b89e75c842b4')
+ assert_equal "Merge branch 'feature'\n", c.message
+ assert_equal 0, c.diffs.size
+ end
+ end
end
end
diff --git a/test/unit/git_misc_test.rb b/test/unit/git_misc_test.rb
index 55f77db4..ab2464c6 100644
--- a/test/unit/git_misc_test.rb
+++ b/test/unit/git_misc_test.rb
@@ -1,11 +1,11 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitMiscTest < Scm::Test
+module OhlohScm::Adapters
+ class GitMiscTest < OhlohScm::Test
def test_export
with_git_repository('git') do |git|
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
git.export(dir)
assert_equal ['.','..','.gitignore','COPYING','README','helloworld.c','makefile','ohloh_token'], Dir.entries(dir).sort
end
@@ -14,7 +14,7 @@ def test_export
def test_branches
with_git_repository('git') do |git|
- assert_equal ['master'], git.branches
+ assert_equal ['develop', 'master'], git.branches
assert git.has_branch?('master')
end
end
@@ -27,9 +27,35 @@ def test_ls_tree
def test_is_merge_commit
with_git_repository('git_walk') do |git|
- assert git.is_merge_commit?(Scm::Commit.new(:token => 'f264fb40c340a415b305ac1f0b8f12502aa2788f'))
- assert !git.is_merge_commit?(Scm::Commit.new(:token => 'd067161caae2eeedbd74976aeff5c4d8f1ccc946'))
+ assert git.is_merge_commit?(OhlohScm::Commit.new(:token => 'f264fb40c340a415b305ac1f0b8f12502aa2788f'))
+ assert !git.is_merge_commit?(OhlohScm::Commit.new(:token => 'd067161caae2eeedbd74976aeff5c4d8f1ccc946'))
end
end
+
+ def test_branches_encoding
+ with_git_repository('git_with_invalid_encoding') do |git|
+ assert_equal true, git.branches.all? { |branch| branch.valid_encoding? }
+ end
+ end
+
+ # `git ls-tree` returns filenames in valid utf8 regardless of their original form.
+ def test_ls_tree_encoding
+ with_git_repository('git_with_invalid_encoding') do |git|
+ assert_equal true, git.ls_tree.all? { |filename| filename.valid_encoding? }
+ end
+ end
+
+ def test_tags
+ with_git_repository('git') do |git|
+ assert_equal git.tags, [['v1.0.0', 'f6e5a894ac4173f8f2a200f2c36df38a1e61121a', Time.parse('2016-07-31T07:58:30+05:30')],
+ ['v2.1.0', '1df547800dcd168e589bb9b26b4039bff3a7f7e4', Time.parse('2006-07-14T16:07:15-07:00')]]
+ end
+ end
+
+ def test_tags_with_non_tagged_repository
+ with_git_repository('git_walk') do |git|
+ assert_equal git.tags, []
+ end
+ end
end
end
diff --git a/test/unit/git_parser_test.rb b/test/unit/git_parser_test.rb
new file mode 100644
index 00000000..5e029c69
--- /dev/null
+++ b/test/unit/git_parser_test.rb
@@ -0,0 +1,59 @@
+require_relative '../test_helper'
+
+module OhlohScm::Parsers
+ class GitParserTest < OhlohScm::Test
+
+ def test_empty_array
+ assert_equal([], GitParser.parse(''))
+ end
+
+ def test_log_parser_default
+sample_log = <
+Date: Fri, 14 Jul 2006 16:07:15 -0700
+
+ moving COPYING
+
+A COPYING
+
+commit 2e9366dd7a786fdb35f211fff1c8ea05c51968b1
+Author: Robin Luckey
+Date: Sun, 11 Jun 2006 11:34:17 -0700
+
+ added some documentation and licensing info
+
+M README
+D helloworld.c
+SAMPLE
+
+ commits = GitParser.parse(sample_log)
+
+ assert commits
+ assert_equal 2, commits.size
+
+ assert_equal '1df547800dcd168e589bb9b26b4039bff3a7f7e4', commits[0].token
+ assert_equal 'Jason Allen', commits[0].author_name
+ assert_equal 'jason@ohloh.net', commits[0].author_email
+ assert_equal "moving COPYING", commits[0].message
+ assert_equal Time.utc(2006,7,14,23,7,15), commits[0].author_date
+ assert_equal 1, commits[0].diffs.size
+
+ assert_equal "A", commits[0].diffs[0].action
+ assert_equal "COPYING", commits[0].diffs[0].path
+
+ assert_equal '2e9366dd7a786fdb35f211fff1c8ea05c51968b1', commits[1].token
+ assert_equal 'Robin Luckey', commits[1].author_name
+ assert_equal 'robin@ohloh.net', commits[1].author_email
+ assert_equal "added some documentation and licensing info", commits[1].message # Note \n at end of comment
+ assert_equal Time.utc(2006,6,11,18,34,17), commits[1].author_date
+ assert_equal 2, commits[1].diffs.size
+
+ assert_equal "M", commits[1].diffs[0].action
+ assert_equal "README", commits[1].diffs[0].path
+ assert_equal "D", commits[1].diffs[1].action
+ assert_equal "helloworld.c", commits[1].diffs[1].path
+ end
+
+ end
+end
diff --git a/test/unit/git_patch_test.rb b/test/unit/git_patch_test.rb
index 0ec6629a..bdcda499 100644
--- a/test/unit/git_patch_test.rb
+++ b/test/unit/git_patch_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitPatchTest < Scm::Test
+module OhlohScm::Adapters
+ class GitPatchTest < OhlohScm::Test
def test_patch_for_commit
with_git_repository('git') do |repo|
commit = repo.verbose_commit('b6e9220c3cabe53a4ed7f32952aeaeb8a822603d')
diff --git a/test/unit/git_pull_test.rb b/test/unit/git_pull_test.rb
index e39e210d..745bc610 100644
--- a/test/unit/git_pull_test.rb
+++ b/test/unit/git_pull_test.rb
@@ -1,11 +1,11 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitPullTest < Scm::Test
+module OhlohScm::Adapters
+ class GitPullTest < OhlohScm::Test
def test_basic_pull
with_git_repository('git') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = GitAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
@@ -18,5 +18,15 @@ def test_basic_pull
end
end
+ def test_basic_pull_with_exception
+ with_svn_repository('svn_empty') do |src|
+ OhlohScm::ScratchDir.new do |dest_dir|
+ dest = GitAdapter.new(:url => dest_dir).normalize
+ assert !dest.exist?
+ err = assert_raises(RuntimeError) { dest.pull(src) }
+ assert_match /Empty repository/, err.message
+ end
+ end
+ end
end
end
diff --git a/test/unit/git_push_test.rb b/test/unit/git_push_test.rb
index 209830c8..8f4b5712 100644
--- a/test/unit/git_push_test.rb
+++ b/test/unit/git_push_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitPushTest < Scm::Test
+module OhlohScm::Adapters
+ class GitPushTest < OhlohScm::Test
def test_hostname
assert_equal "foo", GitAdapter.new(:url => 'foo:/bar').hostname
@@ -23,8 +23,7 @@ def test_local
def test_basic_push
with_git_repository('git') do |src|
- Scm::ScratchDir.new do |dest_dir|
-
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = GitAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
@@ -34,12 +33,12 @@ def test_basic_push
# Now push again. This tests a different code path!
File.open(File.join(src.url, 'foo'), 'w') { }
- src.commit_all(Scm::Commit.new)
+ src.commit_all(OhlohScm::Commit.new)
+ system("cd #{ dest_dir } && git config --bool core.bare true && git config receive.denyCurrentBranch refuse")
src.push(dest)
assert dest.exist?
assert_equal src.log, dest.log
-
end
end
end
diff --git a/test/unit/git_rev_list_test.rb b/test/unit/git_rev_list_test.rb
index 4b48aa3f..e4c6ed77 100644
--- a/test/unit/git_rev_list_test.rb
+++ b/test/unit/git_rev_list_test.rb
@@ -1,13 +1,13 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
+module OhlohScm::Adapters
# Repository git_walk has the following structure:
#
# G -> H -> I -> J -> development
# / \ \
# A -> B -> C -> D -> master
#
- class GitRevListTest < Scm::Test
+ class GitRevListTest < OhlohScm::Test
def test_rev_list
with_git_repository('git_walk') do |git|
@@ -32,10 +32,33 @@ def test_rev_list
end
end
+ def test_trunk_only_rev_list
+ with_git_repository('git_walk') do |git|
+ # Full history to a commit
+ assert_equal [:A], rev_list_trunk(git, nil, :A)
+ assert_equal [:A, :B], rev_list_trunk(git, nil, :B)
+ assert_equal [:A, :B, :C], rev_list_trunk(git, nil, :C)
+ assert_equal [:A, :B, :C, :D], rev_list_trunk(git, nil, :D)
+
+ # Limited history from one commit to another
+ assert_equal [], rev_list_trunk(git, :A, :A)
+ assert_equal [:B], rev_list_trunk(git, :A, :B)
+ assert_equal [:B, :C], rev_list_trunk(git, :A, :C)
+ assert_equal [:B, :C, :D], rev_list_trunk(git, :A, :D)
+ assert_equal [:C, :D], rev_list_trunk(git, :B, :D)
+ assert_equal [:D], rev_list_trunk(git, :C, :D)
+ end
+ end
+
protected
def rev_list_helper(git, from, to)
- to_labels(git.commit_tokens(from_label(from), from_label(to)))
+ to_labels(git.commit_tokens(:after => from_label(from), :up_to => from_label(to)))
+ end
+
+ def rev_list_trunk(git, from, to)
+ to_labels(git.commit_tokens(:after => from_label(from), :up_to => from_label(to),
+ :trunk_only => true))
end
def commit_labels
diff --git a/test/unit/git_styled_parser_test.rb b/test/unit/git_styled_parser_test.rb
new file mode 100644
index 00000000..afe9fdc6
--- /dev/null
+++ b/test/unit/git_styled_parser_test.rb
@@ -0,0 +1,103 @@
+require_relative '../test_helper'
+
+module OhlohScm::Parsers
+ class GitStyledParserTest < OhlohScm::Test
+
+ def test_empty_array
+ assert_equal([], GitStyledParser.parse(''))
+ end
+
+ def test_log_parser_nil_date
+sample_log = <<-SAMPLE
+__BEGIN_COMMIT__
+Commit: 1df547800dcd168e589bb9b26b4039bff3a7f7e4
+Author: Jason Allen
+AuthorEmail: jason@ohloh.net
+Date:
+__BEGIN_COMMENT__
+ moving COPYING
+
+__END_COMMENT__
+SAMPLE
+
+ commits = GitStyledParser.parse(sample_log)
+ assert_equal 1, commits.size
+ assert_equal Time.utc(1970,1,1,0,0,0), commits[0].author_date
+ end
+
+ def test_log_parser_bogus_date
+sample_log = <<-SAMPLE
+__BEGIN_COMMIT__
+Commit: 1df547800dcd168e589bb9b26b4039bff3a7f7e4
+Author: Jason Allen
+AuthorEmail: jason@ohloh.net
+Date: Mon, Jan 01 2012 05:00:00 -0500
+__BEGIN_COMMENT__
+ moving COPYING
+
+__END_COMMENT__
+SAMPLE
+
+ commits = GitStyledParser.parse(sample_log)
+ assert_equal 1, commits.size
+ assert_equal Time.utc(1970,1,1,0,0,0), commits[0].author_date
+ end
+
+ def test_log_parser_default
+sample_log = < dir).normalize
assert !git.read_token
git.init_db
@@ -13,25 +13,33 @@ def test_no_token_returns_nil
end
def test_write_and_read_token
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
git = GitAdapter.new(:url => dir).normalize
git.init_db
git.write_token("FOO")
assert !git.read_token # Token not valid until committed
- git.commit_all(Scm::Commit.new)
+ git.commit_all(OhlohScm::Commit.new)
assert_equal "FOO", git.read_token
end
end
def test_commit_all_includes_write_token
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
git = GitAdapter.new(:url => dir).normalize
git.init_db
- c = Scm::Commit.new
+ c = OhlohScm::Commit.new
c.token = "BAR"
git.commit_all(c)
assert_equal c.token, git.read_token
end
end
+
+ def test_read_token_encoding
+ with_git_repository('git_with_invalid_encoding') do |git|
+ assert_nothing_raised do
+ git.read_token
+ end
+ end
+ end
end
end
diff --git a/test/unit/git_validation_test.rb b/test/unit/git_validation_test.rb
index a404675c..b7492e8d 100644
--- a/test/unit/git_validation_test.rb
+++ b/test/unit/git_validation_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class GitValidationTest < Scm::Test
+module OhlohScm::Adapters
+ class GitValidationTest < OhlohScm::Test
def test_rejected_urls
[ nil, "", "foo", "http:/", "http:://", "http://", "http://a",
"kernel.org/linux/linux.git", # missing a protocol prefix
@@ -23,7 +23,11 @@ def test_accepted_urls
"https://kernel.org/pub/scm/git/git.git",
"https://kernel.org:8080/pub/scm/git/git.git",
"git://kernel.org/~foo/git.git",
- "http://git.onerussian.com/pub/deb/impose+.git"
+ "http://git.onerussian.com/pub/deb/impose+.git",
+ "https://Person@github.com/Person/some_repo.git",
+ "http://Person@github.com/Person/some_repo.git",
+ "https://github.com/Person/some_repo",
+ "http://github.com/Person/some_repo"
].each do |url|
git = GitAdapter.new(:url => url)
assert !git.validate_url
@@ -34,8 +38,56 @@ def test_guess_forge
git = GitAdapter.new(:url => nil)
assert_equal nil, git.guess_forge
+ git = GitAdapter.new(:url => 'git://methabot.git.sourceforge.net/gitroot/methabot')
+ assert_equal 'sourceforge.net', git.guess_forge
+
git = GitAdapter.new( :url => 'http://kernel.org/pub/scm/linux/kernel/git/stable/linux-2.6.17.y.git')
assert_equal 'kernel.org', git.guess_forge
end
+
+ def test_normalize_url
+ assert_equal nil, GitAdapter.new(:url => nil).normalize_url
+ assert_equal '', GitAdapter.new(:url => '').normalize_url
+ assert_equal 'foo', GitAdapter.new(:url => 'foo').normalize_url
+
+ # A non-Github URL: no change
+ assert_equal 'git://kernel.org/pub/scm/git/git.git',
+ GitAdapter.new(:url => 'git://kernel.org/pub/scm/git/git.git').normalize_url
+
+ # A Github read-write URL: converted to read-only
+ assert_equal 'git://github.com/robinluckey/ohcount.git',
+ GitAdapter.new(:url => 'https://robinluckey@github.com/robinluckey/ohcount.git').normalize_url
+
+ # A Github read-write URL: converted to read-only
+ assert_equal 'git://github.com/robinluckey/ohcount.git',
+ GitAdapter.new(:url => 'git@github.com:robinluckey/ohcount.git').normalize_url
+
+ # A Github read-only URL: no change
+ assert_equal 'git://github.com/robinluckey/ohcount.git',
+ GitAdapter.new(:url => 'git@github.com:robinluckey/ohcount.git').normalize_url
+ end
+
+ def test_normalize_converts_to_read_only
+ normalize_url_test('https://robinluckey@github.com/robinluckey/ohcount.git', 'git://github.com/robinluckey/ohcount.git')
+ end
+
+ def test_normalize_handles_https_with_user_at_github_format
+ normalize_url_test('http://Person@github.com/Person/something.git', 'git://github.com/Person/something.git')
+ end
+
+ def test_normalize_handles_https_web_url
+ normalize_url_test('https://github.com/Person/something', 'git://github.com/Person/something')
+ end
+
+ def test_normalize_handles_http_web_url
+ normalize_url_test('http://github.com/Person/something', 'git://github.com/Person/something')
+ end
+
+ private
+ def normalize_url_test(url, result_url)
+ git = GitAdapter.new(:url => url)
+ git.normalize
+ assert_equal result_url, git.url
+ end
end
end
diff --git a/test/unit/hg_cat_file_test.rb b/test/unit/hg_cat_file_test.rb
index b44a9c5f..0b36820e 100644
--- a/test/unit/hg_cat_file_test.rb
+++ b/test/unit/hg_cat_file_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgCatFileTest < Scm::Test
+module OhlohScm::Adapters
+ class HgCatFileTest < OhlohScm::Test
def test_cat_file
with_hg_repository('hg') do |hg|
@@ -21,25 +21,25 @@ def test_cat_file
EXPECTED
# The file was deleted in revision 468336c6671c. Check that it does not exist now, but existed in parent.
- assert_equal nil, hg.cat_file(Scm::Commit.new(:token => '75532c1e1f1d'), Scm::Diff.new(:path => 'helloworld.c'))
- assert_equal expected, hg.cat_file_parent(Scm::Commit.new(:token => '75532c1e1f1d'), Scm::Diff.new(:path => 'helloworld.c'))
- assert_equal expected, hg.cat_file(Scm::Commit.new(:token => '468336c6671c'), Scm::Diff.new(:path => 'helloworld.c'))
+ assert_equal nil, hg.cat_file(OhlohScm::Commit.new(:token => '75532c1e1f1d'), OhlohScm::Diff.new(:path => 'helloworld.c'))
+ assert_equal expected, hg.cat_file_parent(OhlohScm::Commit.new(:token => '75532c1e1f1d'), OhlohScm::Diff.new(:path => 'helloworld.c'))
+ assert_equal expected, hg.cat_file(OhlohScm::Commit.new(:token => '468336c6671c'), OhlohScm::Diff.new(:path => 'helloworld.c'))
end
end
# Ensure that we escape bash-significant characters like ' and & when they appear in the filename
def test_funny_file_name_chars
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
# Make a file with a problematic filename
- funny_name = 'file_name_&\'"'
+ funny_name = '#|file_name` $(&\'")#'
File.open(File.join(dir, funny_name), 'w') { |f| f.write "contents" }
# Add it to an hg repository
- `cd #{dir} && hg init && hg add * && hg commit -m test`
+ `cd #{dir} && hg init && hg add * && hg commit -u tester -m test`
# Confirm that we can read the file back
hg = HgAdapter.new(:url => dir).normalize
- assert_equal "contents", hg.cat_file(hg.head, Scm::Diff.new(:path => funny_name))
+ assert_equal "contents", hg.cat_file(hg.head, OhlohScm::Diff.new(:path => funny_name))
end
end
diff --git a/test/unit/hg_commits_test.rb b/test/unit/hg_commits_test.rb
index 9b14aa10..1c62d1bc 100644
--- a/test/unit/hg_commits_test.rb
+++ b/test/unit/hg_commits_test.rb
@@ -1,36 +1,129 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgCommitsTest < Scm::Test
+module OhlohScm::Adapters
+ class HgCommitsTest < OhlohScm::Test
- def test_commit
+ def test_commit_count
with_hg_repository('hg') do |hg|
- assert_equal 4, hg.commit_count
- assert_equal 2, hg.commit_count('b14fa4692f949940bd1e28da6fb4617de2615484')
- assert_equal 0, hg.commit_count('75532c1e1f1de55c2271f6fd29d98efbe35397c4')
+ assert_equal 5, hg.commit_count
+ assert_equal 3, hg.commit_count(:after => 'b14fa4692f949940bd1e28da6fb4617de2615484')
+ assert_equal 0, hg.commit_count(:after => '655f04cf6ad708ab58c7b941672dce09dd369a18')
+ end
+ end
+
+ def test_commit_count_with_empty_branch
+ with_hg_repository('hg', '') do |hg|
+ assert_equal nil, hg.branch_name
+ assert_equal 5, hg.commit_count
+ assert_equal 3, hg.commit_count(:after => 'b14fa4692f949940bd1e28da6fb4617de2615484')
+ assert_equal 0, hg.commit_count(:after => '655f04cf6ad708ab58c7b941672dce09dd369a18')
+ end
+ end
+
+
+ def test_commit_tokens
+ with_hg_repository('hg') do |hg|
+ assert_equal ['01101d8ef3cea7da9ac6e9a226d645f4418f05c9',
+ 'b14fa4692f949940bd1e28da6fb4617de2615484',
+ '468336c6671cbc58237a259d1b7326866afc2817',
+ '75532c1e1f1de55c2271f6fd29d98efbe35397c4',
+ '655f04cf6ad708ab58c7b941672dce09dd369a18'], hg.commit_tokens
+
+ assert_equal ['655f04cf6ad708ab58c7b941672dce09dd369a18'],
+ hg.commit_tokens(:after => '75532c1e1f1de55c2271f6fd29d98efbe35397c4')
+
+ assert_equal [], hg.commit_tokens(:after => '655f04cf6ad708ab58c7b941672dce09dd369a18')
+ end
+ end
+ def test_commits
+ with_hg_repository('hg') do |hg|
assert_equal ['01101d8ef3cea7da9ac6e9a226d645f4418f05c9',
'b14fa4692f949940bd1e28da6fb4617de2615484',
'468336c6671cbc58237a259d1b7326866afc2817',
- '75532c1e1f1de55c2271f6fd29d98efbe35397c4'], hg.commit_tokens
+ '75532c1e1f1de55c2271f6fd29d98efbe35397c4',
+ '655f04cf6ad708ab58c7b941672dce09dd369a18'], hg.commits.collect { |c| c.token }
+
+ assert_equal ['655f04cf6ad708ab58c7b941672dce09dd369a18'],
+ hg.commits(:after => '75532c1e1f1de55c2271f6fd29d98efbe35397c4').collect { |c| c.token }
- assert_equal ['75532c1e1f1de55c2271f6fd29d98efbe35397c4'],
- hg.commit_tokens('468336c6671cbc58237a259d1b7326866afc2817')
+ # Check that the diffs are not populated
+ assert_equal [], hg.commits(:after => '75532c1e1f1de55c2271f6fd29d98efbe35397c4').first.diffs
- assert_equal [], hg.commit_tokens('75532c1e1f1de55c2271f6fd29d98efbe35397c4')
+ assert_equal [], hg.commits(:after => '655f04cf6ad708ab58c7b941672dce09dd369a18')
+ end
+ end
+ def test_commits_with_branch
+ with_hg_repository('hg', 'develop') do |hg|
assert_equal ['01101d8ef3cea7da9ac6e9a226d645f4418f05c9',
'b14fa4692f949940bd1e28da6fb4617de2615484',
'468336c6671cbc58237a259d1b7326866afc2817',
- '75532c1e1f1de55c2271f6fd29d98efbe35397c4'], hg.commits.collect { |c| c.token }
+ '75532c1e1f1de55c2271f6fd29d98efbe35397c4',
+ '4d54c3f0526a1ec89214a70615a6b1c6129c665c'], hg.commits.collect { |c| c.token }
- assert_equal ['75532c1e1f1de55c2271f6fd29d98efbe35397c4'],
- hg.commits('468336c6671cbc58237a259d1b7326866afc2817').collect { |c| c.token }
+ assert_equal ['4d54c3f0526a1ec89214a70615a6b1c6129c665c'],
+ hg.commits(:after => '75532c1e1f1de55c2271f6fd29d98efbe35397c4').collect { |c| c.token }
# Check that the diffs are not populated
- assert_equal [], hg.commits('468336c6671cbc58237a259d1b7326866afc2817').first.diffs
+ assert_equal [], hg.commits(:after => '75532c1e1f1de55c2271f6fd29d98efbe35397c4').first.diffs
+
+ assert_equal [], hg.commits(:after => '4d54c3f0526a1ec89214a70615a6b1c6129c665c')
+ end
+ end
+
+ def test_trunk_only_commit_count
+ with_hg_repository('hg_dupe_delete') do |hg|
+ assert_equal 4, hg.commit_count(:trunk_only => false)
+ assert_equal 3, hg.commit_count(:trunk_only => true)
+ end
+ end
- assert_equal [], hg.commits('75532c1e1f1de55c2271f6fd29d98efbe35397c4')
+ def test_trunk_only_commit_tokens
+ with_hg_repository('hg_dupe_delete') do |hg|
+ assert_equal ['73e93f57224e3fd828cf014644db8eec5013cd6b',
+ '732345b1d5f4076498132fd4b965b1fec0108a50',
+ '525de321d8085bc1d4a3c7608fda6b4020027985', # On branch
+ '72fe74d643bdcb30b00da3b58796c50f221017d0'],
+ hg.commit_tokens(:trunk_only => false)
+
+ assert_equal ['73e93f57224e3fd828cf014644db8eec5013cd6b',
+ '732345b1d5f4076498132fd4b965b1fec0108a50',
+ # '525de321d8085bc1d4a3c7608fda6b4020027985', # On branch
+ '72fe74d643bdcb30b00da3b58796c50f221017d0'],
+ hg.commit_tokens(:trunk_only => true)
+ end
+ end
+
+ def test_trunk_only_commit_tokens_using_after
+ with_hg_repository('hg_dupe_delete') do |hg|
+ assert_equal ['732345b1d5f4076498132fd4b965b1fec0108a50',
+ '525de321d8085bc1d4a3c7608fda6b4020027985', # On branch
+ '72fe74d643bdcb30b00da3b58796c50f221017d0'],
+ hg.commit_tokens(
+ :after => '73e93f57224e3fd828cf014644db8eec5013cd6b',
+ :trunk_only => false)
+
+ assert_equal ['732345b1d5f4076498132fd4b965b1fec0108a50',
+ # '525de321d8085bc1d4a3c7608fda6b4020027985', # On branch
+ '72fe74d643bdcb30b00da3b58796c50f221017d0'],
+ hg.commit_tokens(
+ :after => '73e93f57224e3fd828cf014644db8eec5013cd6b',
+ :trunk_only => true)
+
+ assert_equal [], hg.commit_tokens(
+ :after => '72fe74d643bdcb30b00da3b58796c50f221017d0',
+ :trunk_only => true)
+ end
+ end
+
+ def test_trunk_only_commits
+ with_hg_repository('hg_dupe_delete') do |hg|
+ assert_equal ['73e93f57224e3fd828cf014644db8eec5013cd6b',
+ '732345b1d5f4076498132fd4b965b1fec0108a50',
+ # '525de321d8085bc1d4a3c7608fda6b4020027985', # On branch
+ '72fe74d643bdcb30b00da3b58796c50f221017d0'],
+ hg.commits(:trunk_only => true).collect { |c| c.token }
end
end
@@ -56,9 +149,66 @@ def test_each_commit
assert_equal ['01101d8ef3cea7da9ac6e9a226d645f4418f05c9',
'b14fa4692f949940bd1e28da6fb4617de2615484',
'468336c6671cbc58237a259d1b7326866afc2817',
- '75532c1e1f1de55c2271f6fd29d98efbe35397c4'], commits.collect { |c| c.token }
+ '75532c1e1f1de55c2271f6fd29d98efbe35397c4',
+ '655f04cf6ad708ab58c7b941672dce09dd369a18'], commits.collect { |c| c.token }
+ end
+ end
+
+ def test_each_commit_for_branch
+ commits = []
+
+ with_hg_repository('hg', 'develop') do |hg|
+ commits = hg.each_commit
+ end
+
+ assert_equal ['01101d8ef3cea7da9ac6e9a226d645f4418f05c9',
+ 'b14fa4692f949940bd1e28da6fb4617de2615484',
+ '468336c6671cbc58237a259d1b7326866afc2817',
+ '75532c1e1f1de55c2271f6fd29d98efbe35397c4',
+ '4d54c3f0526a1ec89214a70615a6b1c6129c665c'], commits.collect { |c| c.token }
+ end
+
+
+ def test_each_commit_after
+ commits = []
+ with_hg_repository('hg') do |hg|
+ hg.each_commit(:after => '468336c6671cbc58237a259d1b7326866afc2817') do |c|
+ commits << c
+ end
+ assert_equal ['75532c1e1f1de55c2271f6fd29d98efbe35397c4',
+ '655f04cf6ad708ab58c7b941672dce09dd369a18'], commits.collect { |c| c.token }
end
end
+
+ def test_open_log_file_encoding
+ with_hg_repository('hg_with_invalid_encoding') do |hg|
+ hg.open_log_file do |io|
+ assert_equal true, io.read.valid_encoding?
+ end
+ end
+ end
+
+ def test_log_encoding
+ with_hg_repository('hg_with_invalid_encoding') do |hg|
+ assert_equal true, hg.log.valid_encoding?
+ end
+ end
+
+ def test_commits_encoding
+ with_hg_repository('hg_with_invalid_encoding') do |hg|
+ assert_nothing_raised do
+ hg.commits
+ end
+ end
+ end
+
+ def test_verbose_commit_encoding
+ with_hg_repository('hg_with_invalid_encoding') do |hg|
+ assert_nothing_raised do
+ hg.verbose_commit('51ea5277ca27')
+ end
+ end
+ end
end
end
diff --git a/test/unit/hg_head_test.rb b/test/unit/hg_head_test.rb
index 4fa5521b..d86c0151 100644
--- a/test/unit/hg_head_test.rb
+++ b/test/unit/hg_head_test.rb
@@ -1,18 +1,24 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgHeadTest < Scm::Test
+module OhlohScm::Adapters
+ class HgHeadTest < OhlohScm::Test
- def test_head_and_parents
+ def test_hg_head_and_parents
with_hg_repository('hg') do |hg|
- assert_equal '75532c1e1f1d', hg.head_token
- assert_equal '75532c1e1f1de55c2271f6fd29d98efbe35397c4', hg.head.token
+ assert_equal '655f04cf6ad708ab58c7b941672dce09dd369a18', hg.head_token
+ assert_equal '655f04cf6ad708ab58c7b941672dce09dd369a18', hg.head.token
assert hg.head.diffs.any? # diffs should be populated
- assert_equal '468336c6671cbc58237a259d1b7326866afc2817', hg.parents(hg.head).first.token
+ assert_equal '75532c1e1f1de55c2271f6fd29d98efbe35397c4', hg.parents(hg.head).first.token
assert hg.parents(hg.head).first.diffs.any?
end
end
+ def test_head_with_branch
+ with_hg_repository('hg', 'develop') do |hg|
+ assert_equal '4d54c3f0526a1ec89214a70615a6b1c6129c665c', hg.head.token
+ assert hg.head.diffs.any?
+ end
+ end
end
end
diff --git a/test/unit/hg_misc_test.rb b/test/unit/hg_misc_test.rb
index 49e1b885..5dab6f01 100644
--- a/test/unit/hg_misc_test.rb
+++ b/test/unit/hg_misc_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgMiscTest < Scm::Test
+module OhlohScm::Adapters
+ class HgMiscTest < OhlohScm::Test
def test_exist
save_hg = nil
@@ -14,9 +14,34 @@ def test_exist
def test_ls_tree
with_hg_repository('hg') do |hg|
- assert_equal ['README','makefile'], hg.ls_tree(hg.head_token).sort
+ assert_equal ['README','makefile', 'two'], hg.ls_tree(hg.head_token).sort
end
end
+ def test_export
+ with_hg_repository('hg') do |hg|
+ OhlohScm::ScratchDir.new do |dir|
+ hg.export(dir)
+ assert_equal ['.', '..', 'README', 'makefile', 'two'], Dir.entries(dir).sort
+ end
+ end
+ end
+
+ def test_ls_tree_encoding
+ with_hg_repository('hg_with_invalid_encoding') do |hg|
+ filenames = hg.ls_tree('51ea5277ca27')
+
+ filenames.each do |filename|
+ assert_equal true, filename.valid_encoding?
+ end
+ end
+ end
+
+ def test_tags
+ with_hg_repository('hg') do |hg|
+ time = Time.parse('Fri Jul 22 18:00:18 2016 +0530')
+ assert_equal [['tip', '5', time]], hg.tags
+ end
+ end
end
end
diff --git a/test/unit/hg_parser_test.rb b/test/unit/hg_parser_test.rb
index 32f5ac55..ae10c700 100644
--- a/test/unit/hg_parser_test.rb
+++ b/test/unit/hg_parser_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Parsers
- class HgParserTest < Scm::Test
+module OhlohScm::Parsers
+ class HgParserTest < OhlohScm::Test
def test_empty_array
assert_equal([], HgParser.parse(''))
@@ -137,13 +137,13 @@ def test_log_parser_verbose
def test_styled_parser
with_hg_repository('hg') do |hg|
assert FileTest.exist?(HgStyledParser.style_path)
- log = hg.run("cd #{hg.url} && hg log --style #{Scm::Parsers::HgStyledParser.style_path}")
- commits = Scm::Parsers::HgStyledParser.parse(log)
+ log = hg.run("cd #{hg.url} && hg log -f --style #{OhlohScm::Parsers::HgStyledParser.style_path}")
+ commits = OhlohScm::Parsers::HgStyledParser.parse(log)
assert_styled_commits(commits, false)
assert FileTest.exist?(HgStyledParser.verbose_style_path)
- log = hg.run("cd #{hg.url} && hg log --style #{Scm::Parsers::HgStyledParser.verbose_style_path}")
- commits = Scm::Parsers::HgStyledParser.parse(log)
+ log = hg.run("cd #{hg.url} && hg log -f --style #{OhlohScm::Parsers::HgStyledParser.verbose_style_path}")
+ commits = OhlohScm::Parsers::HgStyledParser.parse(log)
assert_styled_commits(commits, true)
end
end
@@ -151,31 +151,31 @@ def test_styled_parser
protected
def assert_styled_commits(commits, with_diffs=false)
- assert_equal 4, commits.size
+ assert_equal 5, commits.size
- assert_equal '75532c1e1f1de55c2271f6fd29d98efbe35397c4', commits[0].token
- assert_equal 'Robin Luckey', commits[0].committer_name
- assert_equal 'robin@ohloh.net', commits[0].committer_email
- assert Time.utc(2009,1,20,19,34,53) - commits[0].committer_date < 1 # Don't care about milliseconds
- assert_equal "deleted helloworld.c\n", commits[0].message
+ assert_equal '75532c1e1f1de55c2271f6fd29d98efbe35397c4', commits[1].token
+ assert_equal 'Robin Luckey', commits[1].committer_name
+ assert_equal 'robin@ohloh.net', commits[1].committer_email
+ assert Time.utc(2009,1,20,19,34,53) - commits[1].committer_date < 1 # Don't care about milliseconds
+ assert_equal "deleted helloworld.c\n", commits[1].message
if with_diffs
- assert_equal 1, commits[0].diffs.size
- assert_equal 'D', commits[0].diffs[0].action
- assert_equal 'helloworld.c', commits[0].diffs[0].path
+ assert_equal 1, commits[1].diffs.size
+ assert_equal 'D', commits[1].diffs[0].action
+ assert_equal 'helloworld.c', commits[1].diffs[0].path
else
- assert_equal [], commits[0].diffs
+ assert_equal [], commits[1].diffs
end
- assert_equal '468336c6671cbc58237a259d1b7326866afc2817', commits[1].token
- assert Time.utc(2009, 1,20,19,34,04) - commits[1].committer_date < 1
+ assert_equal '468336c6671cbc58237a259d1b7326866afc2817', commits[2].token
+ assert Time.utc(2009, 1,20,19,34,04) - commits[2].committer_date < 1
if with_diffs
- assert_equal 2, commits[1].diffs.size
- assert_equal 'M', commits[1].diffs[0].action
- assert_equal 'helloworld.c', commits[1].diffs[0].path
- assert_equal 'A', commits[1].diffs[1].action
- assert_equal 'README', commits[1].diffs[1].path
+ assert_equal 2, commits[2].diffs.size
+ assert_equal 'M', commits[2].diffs[0].action
+ assert_equal 'helloworld.c', commits[2].diffs[0].path
+ assert_equal 'A', commits[2].diffs[1].action
+ assert_equal 'README', commits[2].diffs[1].path
else
assert_equal [], commits[0].diffs
end
diff --git a/test/unit/hg_patch_test.rb b/test/unit/hg_patch_test.rb
index af7df910..8673244f 100644
--- a/test/unit/hg_patch_test.rb
+++ b/test/unit/hg_patch_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgPatchTest < Scm::Test
+module OhlohScm::Adapters
+ class HgPatchTest < OhlohScm::Test
def test_patch_for_commit
with_hg_repository('hg') do |repo|
commit = repo.verbose_commit(1)
diff --git a/test/unit/hg_pull_test.rb b/test/unit/hg_pull_test.rb
index aabba011..33bafdb2 100644
--- a/test/unit/hg_pull_test.rb
+++ b/test/unit/hg_pull_test.rb
@@ -1,11 +1,11 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgPullTest < Scm::Test
+module OhlohScm::Adapters
+ class HgPullTest < OhlohScm::Test
def test_pull
with_hg_repository('hg') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = HgAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
diff --git a/test/unit/hg_push_test.rb b/test/unit/hg_push_test.rb
index e73b5b34..65069c84 100644
--- a/test/unit/hg_push_test.rb
+++ b/test/unit/hg_push_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgPushTest < Scm::Test
+module OhlohScm::Adapters
+ class HgPushTest < OhlohScm::Test
def test_hostname
assert !HgAdapter.new.hostname
@@ -36,7 +36,7 @@ def test_hg_path
def test_push
with_hg_repository('hg') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = HgAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
diff --git a/test/unit/hg_rev_list_test.rb b/test/unit/hg_rev_list_test.rb
index b5036cb3..6529a74b 100644
--- a/test/unit/hg_rev_list_test.rb
+++ b/test/unit/hg_rev_list_test.rb
@@ -1,13 +1,13 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
+module OhlohScm::Adapters
# Repository hg_walk has the following structure:
#
# G -> H -> I
# / \ \
# A -> B -> C -> D -> tip
#
- class HgRevListTest < Scm::Test
+ class HgRevListTest < OhlohScm::Test
def test_rev_list
with_hg_repository('hg_walk') do |hg|
@@ -33,7 +33,7 @@ def test_rev_list
protected
def rev_list_helper(hg, from, to)
- to_labels(hg.commit_tokens(from_label(from), from_label(to)))
+ to_labels(hg.commit_tokens(:after => from_label(from), :up_to => from_label(to)))
end
def commit_labels
diff --git a/test/unit/hg_validation_test.rb b/test/unit/hg_validation_test.rb
index 212b441f..517d5490 100644
--- a/test/unit/hg_validation_test.rb
+++ b/test/unit/hg_validation_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class HgValidationTest < Scm::Test
+module OhlohScm::Adapters
+ class HgValidationTest < OhlohScm::Test
def test_rejected_urls
[ nil, "", "foo", "http:/", "http:://", "http://", "http://a",
"www.selenic.com/repo/hello", # missing a protool prefix
@@ -49,6 +49,12 @@ def test_guess_forge
hg = HgAdapter.new( :url => 'http://www.selenic.com/repo/hello')
assert_equal 'www.selenic.com', hg.guess_forge
+
+ hg = HgAdapter.new( :url => 'http://algoc.hg.sourceforge.net:8000/hgroot/algoc')
+ assert_equal 'sourceforge.net', hg.guess_forge
+
+ hg = HgAdapter.new( :url => 'http://poliqarp.sourceforge.net/hg/poliqarp/')
+ assert_equal 'sourceforge.net', hg.guess_forge
end
end
end
diff --git a/test/unit/hglib_cat_file_test.rb b/test/unit/hglib_cat_file_test.rb
new file mode 100644
index 00000000..3b0be631
--- /dev/null
+++ b/test/unit/hglib_cat_file_test.rb
@@ -0,0 +1,47 @@
+require_relative '../test_helper'
+
+module OhlohScm::Adapters
+ class HglibCatFileTest < OhlohScm::Test
+
+ def test_cat_file
+ with_hglib_repository('hg') do |hg|
+expected = <<-EXPECTED
+/* Hello, World! */
+
+/*
+ * This file is not covered by any license, especially not
+ * the GNU General Public License (GPL). Have fun!
+ */
+
+#include
+main()
+{
+ printf("Hello, World!\\n");
+}
+EXPECTED
+
+ # The file was deleted in revision 468336c6671c. Check that it does not exist now, but existed in parent.
+ assert_equal nil, hg.cat_file(OhlohScm::Commit.new(:token => '75532c1e1f1d'), OhlohScm::Diff.new(:path => 'helloworld.c'))
+ assert_equal expected, hg.cat_file_parent(OhlohScm::Commit.new(:token => '75532c1e1f1d'), OhlohScm::Diff.new(:path => 'helloworld.c'))
+ assert_equal expected, hg.cat_file(OhlohScm::Commit.new(:token => '468336c6671c'), OhlohScm::Diff.new(:path => 'helloworld.c'))
+ end
+ end
+
+ # Ensure that we escape bash-significant characters like ' and & when they appear in the filename
+ def test_funny_file_name_chars
+ OhlohScm::ScratchDir.new do |dir|
+ # Make a file with a problematic filename
+ funny_name = '#|file_name` $(&\'")#'
+ File.open(File.join(dir, funny_name), 'w') { |f| f.write "contents" }
+
+ # Add it to an hg repository
+ `cd #{dir} && hg init && hg add * && hg commit -u tester -m test`
+
+ # Confirm that we can read the file back
+ hg = HglibAdapter.new(:url => dir).normalize
+ assert_equal "contents", hg.cat_file(hg.head, OhlohScm::Diff.new(:path => funny_name))
+ end
+ end
+
+ end
+end
diff --git a/test/unit/hglib_head_test.rb b/test/unit/hglib_head_test.rb
new file mode 100644
index 00000000..ab409372
--- /dev/null
+++ b/test/unit/hglib_head_test.rb
@@ -0,0 +1,18 @@
+require_relative '../test_helper'
+
+module OhlohScm::Adapters
+ class HgHeadTest < OhlohScm::Test
+
+ def test_head_and_parents
+ with_hglib_repository('hg') do |hg|
+ assert_equal '655f04cf6ad708ab58c7b941672dce09dd369a18', hg.head_token
+ assert_equal '655f04cf6ad708ab58c7b941672dce09dd369a18', hg.head.token
+ assert hg.head.diffs.any? # diffs should be populated
+
+ assert_equal '75532c1e1f1de55c2271f6fd29d98efbe35397c4', hg.parents(hg.head).first.token
+ assert hg.parents(hg.head).first.diffs.any?
+ end
+ end
+
+ end
+end
diff --git a/test/unit/ohlog_command_line_test.rb b/test/unit/ohlog_command_line_test.rb
index 2c630d0a..34f3724e 100644
--- a/test/unit/ohlog_command_line_test.rb
+++ b/test/unit/ohlog_command_line_test.rb
@@ -1,5 +1,5 @@
-module Scm::Parsers
- class CommandLineTest < Scm::Test
+module OhlohScm::Parsers
+ class CommandLineTest < OhlohScm::Test
def test_cvs_from_file
result = `#{File.dirname(__FILE__) + '/../../bin/ohlog'} --xml --cvs #{DATA_DIR + '/basic.rlog'}`
assert_equal 0, $?
diff --git a/test/unit/shellout_test.rb b/test/unit/shellout_test.rb
new file mode 100644
index 00000000..108ebaf2
--- /dev/null
+++ b/test/unit/shellout_test.rb
@@ -0,0 +1,26 @@
+require_relative '../test_helper'
+require 'timeout'
+
+class ShelloutTest < OhlohScm::Test
+ def test_execute_must_pipe_the_results_accurately
+ status, out, err = Shellout.execute("ruby -e 'puts %[hello world]; STDERR.puts(%[some error])'")
+
+ assert_equal out, "hello world\n"
+ assert_equal err, "some error\n"
+ assert_equal status.success?, true
+ end
+
+ def test_execute_must_return_appropriate_status_for_a_failed_process
+ status, out, err = Shellout.execute("ruby -e 'exit(1)'")
+
+ assert_equal status.success?, false
+ end
+
+ def test_execute_must_not_hang_when_io_buffer_is_full
+ assert_nothing_raised do
+ Timeout::timeout(1) do
+ Shellout.execute("ruby -e 'STDERR.puts(%[some line\n] * 10000)'")
+ end
+ end
+ end
+end
diff --git a/test/unit/string_encoder_command_line_test.rb b/test/unit/string_encoder_command_line_test.rb
new file mode 100644
index 00000000..adecd81d
--- /dev/null
+++ b/test/unit/string_encoder_command_line_test.rb
@@ -0,0 +1,27 @@
+require_relative '../test_helper'
+
+module OhlohScm::Parsers
+ class StringEncoderCommandLineTest < OhlohScm::Test
+ def test_length_of_content_unchanged
+ file_path = File.expand_path('../../data/sample-content', __FILE__)
+ original_content_length = File.size(file_path)
+ original_content_lines = File.readlines(file_path).size
+
+ output = %x[cat #{ file_path } \
+ | #{ OhlohScm::Adapters::AbstractAdapter.new.string_encoder } ]
+
+ assert_equal original_content_length, output.length
+ assert_equal original_content_lines, output.split("\n").length
+ end
+
+ def test_encoding_invalid_characters
+ invalid_utf8_word_path =
+ File.expand_path('../../data/invalid-utf-word', __FILE__)
+
+ string = %x[cat #{ invalid_utf8_word_path } \
+ | #{ OhlohScm::Adapters::AbstractAdapter.new.string_encoder } ]
+
+ assert_equal true, string.valid_encoding?
+ end
+ end
+end
diff --git a/test/unit/svn_cat_file_test.rb b/test/unit/svn_cat_file_test.rb
index acb5908f..b82cafc9 100644
--- a/test/unit/svn_cat_file_test.rb
+++ b/test/unit/svn_cat_file_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class SvnCatFileTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnCatFileTest < OhlohScm::Test
def test_cat_file
with_svn_repository('svn') do |svn|
@@ -13,11 +13,10 @@ def test_cat_file
printf("Hello, World!\\n");
}
EXPECTED
- assert_equal expected, svn.cat_file(Scm::Commit.new(:token => "1"), Scm::Diff.new(:path => "trunk/helloworld.c"))
+ assert_equal expected, svn.cat_file(OhlohScm::Commit.new(:token => 1), OhlohScm::Diff.new(:path => "trunk/helloworld.c"))
- assert_equal nil, svn.cat_file(Scm::Commit.new(:token => "1"), Scm::Diff.new(:path => "file not found"))
+ assert_equal nil, svn.cat_file(OhlohScm::Commit.new(:token => 1), OhlohScm::Diff.new(:path => "file not found"))
end
-
end
end
end
diff --git a/test/unit/svn_chain_cat_file_test.rb b/test/unit/svn_chain_cat_file_test.rb
new file mode 100644
index 00000000..9dc1137e
--- /dev/null
+++ b/test/unit/svn_chain_cat_file_test.rb
@@ -0,0 +1,24 @@
+require_relative '../test_helper'
+
+module OhlohScm::Adapters
+ class SvnChainCatFileTest < OhlohScm::Test
+
+ def test_cat_file_with_chaining
+goodbye = <<-EXPECTED
+#include
+main()
+{
+ printf("Goodbye, world!\\n");
+}
+EXPECTED
+ with_svn_chain_repository('svn_with_branching', '/trunk') do |svn|
+ # The first case asks for the file on the HEAD, so it should easily be found
+ assert_equal goodbye, svn.cat_file(OhlohScm::Commit.new(:token => 8), OhlohScm::Diff.new(:path => "goodbyeworld.c"))
+
+ # The next test asks for the file as it appeared before /branches/development was moved to /trunk,
+ # so this request requires traversal up the chain to the parent SvnAdapter.
+ assert_equal goodbye, svn.cat_file(OhlohScm::Commit.new(:token => 5), OhlohScm::Diff.new(:path => "goodbyeworld.c"))
+ end
+ end
+ end
+end
diff --git a/test/unit/svn_chain_commits_test.rb b/test/unit/svn_chain_commits_test.rb
new file mode 100644
index 00000000..2c9bdb45
--- /dev/null
+++ b/test/unit/svn_chain_commits_test.rb
@@ -0,0 +1,176 @@
+require_relative '../test_helper'
+
+module OhlohScm::Parsers
+ class SvnChainTest < OhlohScm::Test
+
+ def test_chained_commit_tokens
+ with_svn_chain_repository('svn_with_branching', '/trunk') do |svn|
+ assert_equal [1,2,4,5,8,9,11], svn.commit_tokens
+ assert_equal [2,4,5,8,9,11], svn.commit_tokens(:after => 1)
+ assert_equal [4,5,8,9,11], svn.commit_tokens(:after => 2)
+ assert_equal [4,5,8,9,11], svn.commit_tokens(:after => 3)
+ assert_equal [5,8,9,11], svn.commit_tokens(:after => 4)
+ assert_equal [8,9,11], svn.commit_tokens(:after => 5)
+ assert_equal [8,9,11], svn.commit_tokens(:after => 6)
+ assert_equal [8,9,11], svn.commit_tokens(:after => 7)
+ assert_equal [9,11], svn.commit_tokens(:after => 8)
+ assert_equal [11], svn.commit_tokens(:after => 9)
+ assert_equal [], svn.commit_tokens(:after => 11)
+ end
+ end
+
+ def test_chained_commit_count
+ with_svn_chain_repository('svn_with_branching', '/trunk') do |svn|
+ assert_equal 7, svn.commit_count
+ assert_equal 6, svn.commit_count(:after => 1)
+ assert_equal 5, svn.commit_count(:after => 2)
+ assert_equal 5, svn.commit_count(:after => 3)
+ assert_equal 4, svn.commit_count(:after => 4)
+ assert_equal 3, svn.commit_count(:after => 5)
+ assert_equal 3, svn.commit_count(:after => 6)
+ assert_equal 3, svn.commit_count(:after => 7)
+ assert_equal 2, svn.commit_count(:after => 8)
+ assert_equal 1, svn.commit_count(:after => 9)
+ assert_equal 0, svn.commit_count(:after => 11)
+ end
+ end
+
+ def test_chained_commits
+ with_svn_chain_repository('svn_with_branching', '/trunk') do |svn|
+ assert_equal [1,2,4,5,8,9,11], svn.commits.collect { |c| c.token }
+ assert_equal [2,4,5,8,9,11], svn.commits(:after => 1).collect { |c| c.token }
+ assert_equal [4,5,8,9,11], svn.commits(:after => 2).collect { |c| c.token }
+ assert_equal [4,5,8,9,11], svn.commits(:after => 3).collect { |c| c.token }
+ assert_equal [5,8,9,11], svn.commits(:after => 4).collect { |c| c.token }
+ assert_equal [8,9,11], svn.commits(:after => 5).collect { |c| c.token }
+ assert_equal [8,9,11], svn.commits(:after => 6).collect { |c| c.token }
+ assert_equal [8,9,11], svn.commits(:after => 7).collect { |c| c.token }
+ assert_equal [9,11], svn.commits(:after => 8).collect { |c| c.token }
+ assert_equal [11], svn.commits(:after => 9).collect { |c| c.token }
+ assert_equal [], svn.commits(:after => 11).collect { |c| c.token }
+ end
+ end
+
+ # This test is primarly concerned with the checking the diffs
+ # of commits. Specifically, when an entire branch is moved
+ # to a new name, we should not see any diffs. From our
+ # point of view the code is unchanged; only the base directory
+ # has moved.
+ def test_chained_each_commit
+ commits = []
+ with_svn_chain_repository('svn_with_branching', '/trunk') do |svn|
+ svn.each_commit do |c|
+ assert c.scm # To support checkout of chained commits, the
+ # commit must include a link to its containing adapter.
+ commits << c
+ end
+ end
+
+ assert_equal [1,2,4,5,8,9,11], commits.collect { |c| c.token }
+
+ # This repository spends a lot of energy moving directories around.
+ # File edits actually occur in just 3 commits.
+
+ # Revision 1: /trunk directory created, but it is empty
+ assert_equal 0, commits[0].diffs.size
+
+ # Revision 2: /trunk/helloworld.c is added
+ assert_equal 1, commits[1].diffs.size
+ assert_equal 'A', commits[1].diffs.first.action
+ assert_equal '/helloworld.c', commits[1].diffs.first.path
+
+ # Revision 3: /trunk is deleted. We can't see this revision.
+
+ # Revision 4: /trunk is re-created by copying it from revision 2.
+ # From our point of view, there has been no change at all, and thus no diffs.
+ assert_equal 0, commits[2].diffs.size
+
+ # Revision 5: /branches/development is created by copying /trunk.
+ # From our point of view, the contents of the repository are unchanged, so
+ # no diffs result from the copy.
+ # However, /branches/development/goodbyeworld.c is also created, so we should
+ # have a diff for that.
+ assert_equal 1, commits[3].diffs.size
+ assert_equal 'A', commits[3].diffs.first.action
+ assert_equal '/goodbyeworld.c', commits[3].diffs.first.path
+
+ # Revision 6: /trunk/goodbyeworld.c is created, but we only see activity
+ # on /branches/development, so no commit reported.
+
+ # Revision 7: /trunk is deleted, but again we don't see it.
+
+ # Revision 8: /branches/development is moved to become the new /trunk.
+ # The directory contents are unchanged, so no diffs result.
+ assert_equal 0, commits[4].diffs.size
+
+ # Revision 9: an edit to /trunk/helloworld.c
+ assert_equal 1, commits[5].diffs.size
+ assert_equal 'M', commits[5].diffs.first.action
+ assert_equal '/helloworld.c', commits[5].diffs.first.path
+
+ # Revision 10: /trunk/goodbyeworld.c & /trunk/helloworld.c are modified
+ # on branches/development, hence no commit reported.
+
+ # Revision 11: The trunk is reverted back to revision 9.
+ assert_equal 0, commits[6].diffs.size
+ end
+
+ # Specifically tests this case:
+ # Suppose we're importing /myproject/trunk, and the log
+ # contains the following:
+ #
+ # A /myproject (from /all/myproject:1)
+ # D /all/myproject
+ #
+ # We need to make sure we detect the move here, even though
+ # "/myproject" is not an exact match for "/myproject/trunk".
+ def test_tree_move
+ with_svn_chain_repository('svn_with_tree_move', '/myproject/trunk') do |svn|
+ assert_equal svn.url, svn.root + '/myproject/trunk'
+ assert_equal svn.branch_name, '/myproject/trunk'
+
+ p = svn.parent_svn
+ assert_equal p.url, svn.root + '/all/myproject/trunk'
+ assert_equal p.branch_name, '/all/myproject/trunk'
+ assert_equal p.final_token, 1
+
+ assert_equal [1, 2], svn.commit_tokens
+ end
+ end
+
+ def test_verbose_commit_with_chaining
+ with_svn_chain_repository('svn_with_branching','/trunk') do |svn|
+
+ c = svn.verbose_commit(9)
+ assert_equal 'modified helloworld.c', c.message
+ assert_equal ['/helloworld.c'], c.diffs.collect { |d| d.path }
+ assert_equal '/trunk', c.scm.branch_name
+
+ c = svn.verbose_commit(8)
+ assert_equal [], c.diffs
+ assert_equal '/trunk', c.scm.branch_name
+
+ # Reaching these commits requires chaining
+ c = svn.verbose_commit(5)
+ assert_equal 'add a new branch, with goodbyeworld.c', c.message
+ assert_equal ['/goodbyeworld.c'], c.diffs.collect { |d| d.path }
+ assert_equal '/branches/development', c.scm.branch_name
+
+ # Reaching these commits requires chaining twice
+ c = svn.verbose_commit(4)
+ assert_equal [], c.diffs
+ assert_equal '/trunk', c.scm.branch_name
+
+ # And now a fourth chain (to skip over /trunk deletion in rev 3)
+ c = svn.verbose_commit(2)
+ assert_equal 'Added helloworld.c to trunk', c.message
+ assert_equal ['/helloworld.c'], c.diffs.collect { |d| d.path }
+ assert_equal '/trunk', c.scm.branch_name
+
+ c = svn.verbose_commit(1)
+ assert_equal [], c.diffs
+ assert_equal '/trunk', c.scm.branch_name
+ end
+ end
+ end
+end
diff --git a/test/unit/svn_chain_test.rb b/test/unit/svn_chain_test.rb
new file mode 100644
index 00000000..6a3ec508
--- /dev/null
+++ b/test/unit/svn_chain_test.rb
@@ -0,0 +1,77 @@
+require_relative '../test_helper'
+
+module OhlohScm::Parsers
+ class SvnChainTest < OhlohScm::Test
+
+ def test_chain
+ with_svn_chain_repository('svn_with_branching', '/trunk') do |svn|
+ chain = svn.chain
+ assert_equal 5, chain.size
+
+ # In revision 1, the trunk is created.
+ assert_equal '/trunk', chain[0].branch_name
+ assert_equal 1, chain[0].first_token
+ assert_equal 2, chain[0].final_token
+
+ # In revision 3, the trunk was deleted, but restored in revision 4.
+ # This creates the first discontinuity, and the first link in the chain.
+ assert_equal '/trunk', chain[1].branch_name
+ assert_equal 4, chain[1].first_token
+ assert_equal 4, chain[1].final_token
+
+ # In revision 5, the branch is created by copying the trunk from revision 4.
+ assert_equal '/branches/development', chain[2].branch_name
+ assert_equal 5, chain[2].first_token
+ assert_equal 7, chain[2].final_token
+
+ # In revision 8, a new trunk is created by copying the branch.
+ # The next final_token will be 9.
+ assert_equal '/trunk', chain[3].branch_name
+ assert_equal 8, chain[3].first_token
+ assert_equal 9, chain[3].final_token
+
+ # In revision 11, trunk is reverted back to rev 9
+ # This trunk still lives on, so its final_token is nil.
+ assert_equal '/trunk', chain[4].branch_name
+ assert_equal 11, chain[4].first_token
+ assert_nil chain[4].final_token
+ end
+ end
+
+ def test_parent_svn
+ with_svn_chain_repository('svn_with_branching', '/trunk') do |svn|
+ # The first chain is the copy commit from trunk:9 into rev 11.
+ p0 = svn.parent_svn
+ assert_equal 9, p0.final_token
+
+ # In this repository, /branches/development becomes
+ # the /trunk in revision 8. So there should be a parent
+ # will final_token 7.
+ p1 = p0.parent_svn
+ assert_equal p1.url, svn.root + '/branches/development'
+ assert_equal p1.branch_name, '/branches/development'
+ assert_equal p1.final_token, 7
+
+ # There's another move at revision 5, in which /branch/development
+ # is created by copying /trunk from revision 4.
+ p2 = p1.parent_svn
+ assert_equal p2.url, svn.root + '/trunk'
+ assert_equal p2.branch_name, '/trunk'
+ assert_equal p2.final_token, 4
+ end
+ end
+
+ def test_parent_branch_name
+ svn = OhlohScm::Adapters::SvnChainAdapter.new(:branch_name => "/trunk")
+
+ assert_equal "/branches/b", svn.parent_branch_name(OhlohScm::Diff.new(:action => 'A',
+ :path => "/trunk", :from_revision => 1, :from_path => "/branches/b"))
+ end
+
+ def test_next_revision_xml_valid_encoding
+ with_invalid_encoded_svn_repository do |svn|
+ assert_equal true, svn.next_revision_xml(0).valid_encoding?
+ end
+ end
+ end
+end
diff --git a/test/unit/svn_commits_test.rb b/test/unit/svn_commits_test.rb
index ef90191f..c6758594 100644
--- a/test/unit/svn_commits_test.rb
+++ b/test/unit/svn_commits_test.rb
@@ -1,21 +1,21 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class SvnCommitsTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnCommitsTest < OhlohScm::Test
def test_commits
with_svn_repository('svn') do |svn|
assert_equal 5, svn.commit_count
- assert_equal 3, svn.commit_count(2)
- assert_equal 0, svn.commit_count(1000)
+ assert_equal 3, svn.commit_count(:after => 2)
+ assert_equal 0, svn.commit_count(:after => 1000)
assert_equal [1,2,3,4,5], svn.commit_tokens
- assert_equal [3,4,5], svn.commit_tokens(2)
- assert_equal [], svn.commit_tokens(1000)
+ assert_equal [3,4,5], svn.commit_tokens(:after => 2)
+ assert_equal [], svn.commit_tokens(:after => 1000)
assert_equal [1,2,3,4,5], svn.commits.collect { |c| c.token }
- assert_equal [3,4,5], svn.commits(2).collect { |c| c.token }
- assert_equal [], svn.commits(1000)
+ assert_equal [3,4,5], svn.commits(:after => 2).collect { |c| c.token }
+ assert_equal [], svn.commits(:after => 1000)
assert !FileTest.exist?(svn.log_filename)
end
end
@@ -32,8 +32,8 @@ def test_sha1
# Given a commit with diffs, fill in all of the SHA1 values.
def test_populate_sha1
with_svn_repository('svn') do |svn|
- c = Scm::Commit.new(:token => 3)
- c.diffs = [Scm::Diff.new(:path => "/trunk/helloworld.c", :action => "M")]
+ c = OhlohScm::Commit.new(:token => 3)
+ c.diffs = [OhlohScm::Diff.new(:path => "/trunk/helloworld.c", :action => "M")]
svn.populate_commit_sha1s!(c)
assert_equal 'f6adcae4447809b651c787c078d255b2b4e963c5', c.diffs.first.sha1
assert_equal '4c734ad53b272c9b3d719f214372ac497ff6c068', c.diffs.first.parent_sha1
@@ -42,7 +42,7 @@ def test_populate_sha1
def test_strip_commit_branch
svn = SvnAdapter.new(:branch_name => "/trunk")
- commit = Scm::Commit.new
+ commit = OhlohScm::Commit.new
# nil diffs before => nil diffs after
assert !svn.strip_commit_branch(commit).diffs
@@ -52,19 +52,19 @@ def test_strip_commit_branch
assert_equal [], svn.strip_commit_branch(commit).diffs
commit.diffs = [
- Scm::Diff.new(:path => "/trunk"),
- Scm::Diff.new(:path => "/trunk/helloworld.c"),
- Scm::Diff.new(:path => "/branches/a")
+ OhlohScm::Diff.new(:path => "/trunk"),
+ OhlohScm::Diff.new(:path => "/trunk/helloworld.c"),
+ OhlohScm::Diff.new(:path => "/branches/a")
]
assert_equal ['', '/helloworld.c'], svn.strip_commit_branch(commit).diffs.collect { |d| d.path }.sort
end
def test_strip_diff_branch
svn = SvnAdapter.new(:branch_name => "/trunk")
- assert !svn.strip_diff_branch(Scm::Diff.new)
- assert !svn.strip_diff_branch(Scm::Diff.new(:path => "/branches/b"))
- assert_equal '', svn.strip_diff_branch(Scm::Diff.new(:path => "/trunk")).path
- assert_equal '/helloworld.c', svn.strip_diff_branch(Scm::Diff.new(:path => "/trunk/helloworld.c")).path
+ assert !svn.strip_diff_branch(OhlohScm::Diff.new)
+ assert !svn.strip_diff_branch(OhlohScm::Diff.new(:path => "/branches/b"))
+ assert_equal '', svn.strip_diff_branch(OhlohScm::Diff.new(:path => "/trunk")).path
+ assert_equal '/helloworld.c', svn.strip_diff_branch(OhlohScm::Diff.new(:path => "/trunk/helloworld.c")).path
end
def test_strip_path_branch
@@ -88,8 +88,8 @@ def test_strip_path_branch_with_special_chars
def test_remove_dupes_add_modify
svn = SvnAdapter.new
- c = Scm::Commit.new(:diffs => [ Scm::Diff.new(:action => "A", :path => "foo"),
- Scm::Diff.new(:action => "M", :path => "foo") ])
+ c = OhlohScm::Commit.new(:diffs => [ OhlohScm::Diff.new(:action => "A", :path => "foo"),
+ OhlohScm::Diff.new(:action => "M", :path => "foo") ])
svn.remove_dupes(c)
assert_equal 1, c.diffs.size
@@ -98,8 +98,8 @@ def test_remove_dupes_add_modify
def test_remove_dupes_add_replace
svn = SvnAdapter.new
- c = Scm::Commit.new(:diffs => [ Scm::Diff.new(:action => "R", :path => "foo"),
- Scm::Diff.new(:action => "A", :path => "foo") ])
+ c = OhlohScm::Commit.new(:diffs => [ OhlohScm::Diff.new(:action => "R", :path => "foo"),
+ OhlohScm::Diff.new(:action => "A", :path => "foo") ])
svn.remove_dupes(c)
assert_equal 1, c.diffs.size
@@ -147,7 +147,7 @@ def test_deep_commits
# Also, our commits do not include diffs for the actual directories;
# only the files within those directories.
#
- # Also, since we are only tracking the /trunk and not /branches/b, then
+ # Also, after we are only tracking the /trunk and not /branches/b, then
# there should not be anything referring to activity in /branches/b.
assert_equal 3, deep_commits.first.token # Make sure this is the right revision
@@ -207,11 +207,11 @@ def test_each_commit
assert_equal [1, 2, 3, 4, 5], commits.collect { |c| c.token }
assert_equal ['robin','robin','robin','jason','jason'], commits.collect { |c| c.committer_name }
- assert commits[0].committer_date - Time.utc(2006,6,11,18,28,0) < 1 # commits include milliseconds
- assert commits[1].committer_date - Time.utc(2006,6,11,18,32,13) < 1
- assert commits[2].committer_date - Time.utc(2006,6,11,18,34,17) < 1
- assert commits[3].committer_date - Time.utc(2006,7,14,22,17,8) < 1
- assert commits[4].committer_date - Time.utc(2006,7,14,23,7,15) < 1
+ assert_equal Time.utc(2006,6,11,18,28, 0), commits[0].committer_date
+ assert_equal Time.utc(2006,6,11,18,32,14), commits[1].committer_date
+ assert_equal Time.utc(2006,6,11,18,34,18), commits[2].committer_date
+ assert_equal Time.utc(2006,7,14,22,17, 9), commits[3].committer_date
+ assert_equal Time.utc(2006,7,14,23, 7,16), commits[4].committer_date
assert_equal "Initial Checkin\n", commits[0].message
assert_equal "added makefile", commits[1].message
@@ -244,5 +244,32 @@ def test_each_commit
assert_equal '/trunk/COPYING', commits[4].diffs[1].path
end
+ def test_log_valid_encoding
+ with_invalid_encoded_svn_repository do |svn|
+ assert_equal true, svn.log.valid_encoding?
+ end
+ end
+
+ def test_commits_encoding
+ with_invalid_encoded_svn_repository do |svn|
+ assert_nothing_raised do
+ svn.commits rescue raise Exception
+ end
+ end
+ end
+
+ def test_open_log_file_encoding
+ with_invalid_encoded_svn_repository do |svn|
+ svn.open_log_file do |io|
+ assert_equal true, io.read.valid_encoding?
+ end
+ end
+ end
+
+ def test_single_revision_xml_valid_encoding
+ with_invalid_encoded_svn_repository do |svn|
+ assert_equal true, svn.single_revision_xml(:anything).valid_encoding?
+ end
+ end
end
end
diff --git a/test/unit/svn_convert_test.rb b/test/unit/svn_convert_test.rb
index b7c01acb..a8153d76 100644
--- a/test/unit/svn_convert_test.rb
+++ b/test/unit/svn_convert_test.rb
@@ -1,10 +1,10 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class SvnConvertTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnConvertTest < OhlohScm::Test
def test_basic_convert
with_svn_repository('svn') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = GitAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
@@ -16,7 +16,7 @@ def test_basic_convert
# Because Subversion does not track authors (only committers),
# the Subversion committer becomes the Git author.
assert_equal c.committer_name, dest_commits[i].author_name
- assert_equal c.committer_date, dest_commits[i].author_date
+ assert_equal c.committer_date.round, dest_commits[i].author_date
# The svn-to-git conversion process loses the trailing \n for single-line messages
assert_equal c.message.strip, dest_commits[i].message.strip
diff --git a/test/unit/svn_head_test.rb b/test/unit/svn_head_test.rb
index 0edf0cc9..2b94e782 100644
--- a/test/unit/svn_head_test.rb
+++ b/test/unit/svn_head_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class SvnHeadTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnHeadTest < OhlohScm::Test
def test_head_and_parents
with_svn_repository('svn') do |svn|
@@ -14,6 +14,14 @@ def test_head_and_parents
end
end
+ def test_parents_encoding
+ with_invalid_encoded_svn_repository do |svn|
+ assert_nothing_raised do
+ commit = Struct.new(:token).new(:anything)
+ svn.parents(commit) rescue raise Exception
+ end
+ end
+ end
end
end
diff --git a/test/unit/svn_misc_test.rb b/test/unit/svn_misc_test.rb
index 16bd06c5..afdad09b 100644
--- a/test/unit/svn_misc_test.rb
+++ b/test/unit/svn_misc_test.rb
@@ -1,17 +1,34 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class SvnMiscTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnMiscTest < OhlohScm::Test
def test_export
with_svn_repository('svn') do |svn|
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
svn.export(dir)
assert_equal ['.','..','branches','tags','trunk'], Dir.entries(dir).sort
end
end
end
+ def test_export_tag
+ with_svn_repository('svn', 'trunk') do |source_scm|
+ OhlohScm::ScratchDir.new do |svn_working_folder|
+ OhlohScm::ScratchDir.new do |dir|
+ folder_name = source_scm.root.slice(/[^\/]+\/?\Z/)
+ system "cd #{ svn_working_folder } && svn co #{ source_scm.root } && cd #{ folder_name } &&
+ mkdir -p #{ source_scm.root.gsub(/^file:../, '') }/db/transactions
+ svn copy trunk tags/2.0 && svn commit -m 'v2.0' && svn update"
+
+ source_scm.export_tag(dir, '2.0')
+
+ assert_equal ['.','..','COPYING','README','helloworld.c', 'makefile'], Dir.entries(dir).sort
+ end
+ end
+ end
+ end
+
def test_ls_tree
with_svn_repository('svn') do |svn|
assert_equal ['branches/','tags/','trunk/','trunk/helloworld.c','trunk/makefile'], svn.ls_tree(2).sort
@@ -58,7 +75,68 @@ def test_is_directory
with_svn_repository('svn') do |svn|
assert svn.is_directory?('trunk')
assert !svn.is_directory?('trunk/helloworld.c')
+ assert !svn.is_directory?('invalid/path')
+ end
+ end
+
+ def test_restrict_url_to_trunk_descend_no_further
+ with_svn_repository('deep_svn') do |svn|
+ assert_equal svn.root, svn.url
+ assert_equal '', svn.branch_name
+
+ svn.restrict_url_to_trunk
+
+ assert_equal svn.root + '/trunk', svn.url
+ assert_equal "/trunk", svn.branch_name
end
end
- end
+
+ def test_restrict_url_to_trunk
+ with_svn_repository('svn') do |svn|
+ assert_equal svn.root, svn.url
+ assert_equal '', svn.branch_name
+
+ svn.restrict_url_to_trunk
+
+ assert_equal svn.root + '/trunk', svn.url
+ assert_equal "/trunk", svn.branch_name
+ end
+ end
+
+ def test_tags
+ with_svn_repository('svn', 'trunk') do |source_scm|
+ OhlohScm::ScratchDir.new do |svn_working_folder|
+ folder_name = source_scm.root.slice(/[^\/]+\/?\Z/)
+ system "cd #{ svn_working_folder } && svn co #{ source_scm.root } && cd #{ folder_name } &&
+ mkdir -p #{ source_scm.root.gsub(/^file:../, '') }/db/transactions
+ svn copy trunk tags/2.0 && svn commit -m 'v2.0' && svn update"
+
+ assert_equal(['2.0', '6'], source_scm.tags.first[0..1])
+ # Avoid millisecond comparision.
+ assert_equal(Time.now.strftime('%F %R'), source_scm.tags.first[-1].strftime('%F %R'))
+ end
+ end
+ end
+
+ def test_tags_with_whitespaces
+ with_svn_repository('svn', 'trunk') do |source_scm|
+ OhlohScm::ScratchDir.new do |svn_working_folder|
+ folder_name = source_scm.root.slice(/[^\/]+\/?\Z/)
+ system "cd #{ svn_working_folder } && svn co #{ source_scm.root } && cd #{ folder_name } &&
+ mkdir -p #{ source_scm.root.gsub(/^file:../, '') }/db/transactions
+ svn copy trunk tags/'HL7 engine' && svn commit -m 'v2.0' && svn update && svn propset svn:date --revprop -r 'HEAD' 2016-02-12T00:44:04.921324Z"
+
+ assert_equal(['HL7 engine', '6'], source_scm.tags.first[0..1])
+ # Avoid millisecond comparision.
+ assert_equal('2016-02-12', source_scm.tags.first[-1].strftime('%F'))
+ end
+ end
+ end
+
+ def test_tags_with_non_tagged_repository
+ with_svn_repository('svn') do |svn|
+ assert_equal svn.tags, []
+ end
+ end
+ end
end
diff --git a/test/unit/svn_parser_test.rb b/test/unit/svn_parser_test.rb
index ffcaa0b7..83609fa4 100644
--- a/test/unit/svn_parser_test.rb
+++ b/test/unit/svn_parser_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Parsers
- class SvnParserTest < Scm::Test
+module OhlohScm::Parsers
+ class SvnParserTest < OhlohScm::Test
def test_basic
assert_convert(SvnParser, DATA_DIR + '/simple.svn_log', DATA_DIR + '/simple.ohlog')
@@ -133,5 +133,23 @@ def test_log_embedded_in_comments
COMMENT
assert_equal comment, revs[0].message
end
+
+ def test_svn_copy
+ log = <<-LOG
+------------------------------------------------------------------------
+r8 | robin | 2009-02-05 05:40:46 -0800 (Thu, 05 Feb 2009) | 1 line
+Changed paths:
+ A /trunk (from /branches/development:7)
+
+the branch becomes the new trunk
+ LOG
+
+ commits = SvnParser.parse(log)
+ assert_equal 1, commits.size
+ assert_equal 1, commits.first.diffs.size
+ assert_equal "/trunk", commits.first.diffs.first.path
+ assert_equal "/branches/development", commits.first.diffs.first.from_path
+ assert_equal 7, commits.first.diffs.first.from_revision
+ end
end
end
diff --git a/test/unit/svn_patch_test.rb b/test/unit/svn_patch_test.rb
index c7118a46..301aea05 100644
--- a/test/unit/svn_patch_test.rb
+++ b/test/unit/svn_patch_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Adapters
- class SvnPatchTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnPatchTest < OhlohScm::Test
def test_patch_for_commit
with_svn_repository('svn') do |repo|
commit = repo.verbose_commit(2)
diff --git a/test/unit/svn_pull_test.rb b/test/unit/svn_pull_test.rb
index 61de105e..91985079 100644
--- a/test/unit/svn_pull_test.rb
+++ b/test/unit/svn_pull_test.rb
@@ -1,11 +1,11 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
require 'socket'
-module Scm::Adapters
- class SvnPullTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnPullTest < OhlohScm::Test
def test_svnadmin_create
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
url = File.join(dir, "my_svn_repo")
svn = SvnAdapter.new(:url => url).normalize
@@ -14,14 +14,15 @@ def test_svnadmin_create
assert svn.exist?
# Ensure that revision properties are settable
- svn.propset('foo','bar')
- assert_equal 'bar', svn.propget('foo')
+ # Note that only valid properties can be set
+ svn.propset('log','bar')
+ assert_equal 'bar', svn.propget('log')
end
end
def test_basic_pull_using_svnsync
with_svn_repository('svn') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = SvnAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
@@ -35,7 +36,7 @@ def test_basic_pull_using_svnsync
end
def test_svnadmin_create_local
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
svn = SvnAdapter.new(:url => "file://#{dir}")
svn.svnadmin_create_local
assert svn.exist?
@@ -46,7 +47,7 @@ def test_svnadmin_create_local
end
def test_svnadmin_create_remote
- Scm::ScratchDir.new do |dir|
+ OhlohScm::ScratchDir.new do |dir|
svn = SvnAdapter.new(:url => "svn+ssh://#{Socket.gethostname}#{dir}")
svn.svnadmin_create_remote
assert svn.exist?
diff --git a/test/unit/svn_push_test.rb b/test/unit/svn_push_test.rb
index e945e22b..98594265 100644
--- a/test/unit/svn_push_test.rb
+++ b/test/unit/svn_push_test.rb
@@ -1,12 +1,12 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
require 'socket'
-module Scm::Adapters
- class SvnPushTest < Scm::Test
+module OhlohScm::Adapters
+ class SvnPushTest < OhlohScm::Test
def test_basic_push_using_svnsync
with_svn_repository('svn') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = SvnAdapter.new(:url => dest_dir).normalize
assert !dest.exist?
@@ -23,7 +23,7 @@ def test_basic_push_using_svnsync
# Simulates pushing to another server in our cluster.
def test_ssh_push_using_svnsync
with_svn_repository('svn') do |src|
- Scm::ScratchDir.new do |dest_dir|
+ OhlohScm::ScratchDir.new do |dest_dir|
dest = SvnAdapter.new(:url => "svn+ssh://#{Socket.gethostname}#{File.expand_path(dest_dir)}").normalize
assert !dest.exist?
diff --git a/test/unit/svn_validation_test.rb b/test/unit/svn_validation_test.rb
index 6eb07b2d..f8dc8cf2 100644
--- a/test/unit/svn_validation_test.rb
+++ b/test/unit/svn_validation_test.rb
@@ -1,12 +1,27 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
+
+module OhlohScm::Adapters
+ class SvnValidationTest < OhlohScm::Test
+ def test_valid_usernames
+ [nil,'','joe_36','a'*32,'robin@ohloh.net'].each do |username|
+ assert !SvnAdapter.new(:username => username).validate_username
+ end
+ end
+
+ def test_for_blank_svn_urls
+ svn = SvnAdapter.new(:url =>"")
+ assert_nil svn.path_to_file_url(svn.url)
+ end
+
+ def test_for_non_blank_svn_urls
+ svn = SvnAdapter.new(:url =>"/home/rapbhan")
+ assert_equal 'file:///home/rapbhan', svn.path_to_file_url(svn.url)
+ end
-module Scm::Adapters
- class SvnValidationTest < Scm::Test
def test_rejected_urls
[ nil, "", "foo", "http:/", "http:://", "http://",
"sourceforge.net/svn/project/trunk", # missing a protocol prefix
"http://robin@svn.sourceforge.net/", # must not include a username with the url
- "http://svn.sourceforge.net/asdf/asdf/ malicious code", # no spaces allowed
"/home/robin/cvs", # local file paths not allowed
"git://kernel.org/whatever/linux.git", # git protocol is not allowed
":pserver:anonymous:@juicereceiver.cvs.sourceforge.net:/cvsroot/juicereceiver", # pserver is just wrong
@@ -32,7 +47,8 @@ def test_accepted_urls
"http://svn.gnome.org/svn/gtk+/trunk", # + character OK
"http://svn.gnome.org", # no path, no trailing /, just a domain name is OK
"http://brlcad.svn.sourceforge.net/svnroot/brlcad/rt^3/trunk", # a caret ^ is allowed
- "http://www.thus.ch/~patrick/svn/pvalsecc" # ~ is allowed
+ "http://www.thus.ch/~patrick/svn/pvalsecc", # ~ is allowed
+ "http://franklinmath.googlecode.com/svn/trunk/Franklin Math", # space is allowed in path
].each do |url|
# Accepted for both internal and public use
[true, false].each do |p|
@@ -67,6 +83,9 @@ def test_guess_forge
svn = SvnAdapter.new(:url => 'svn://rubyforge.org:3960//var/svn/rubyomf2097')
assert_equal 'rubyforge.org', svn.guess_forge
+ svn = SvnAdapter.new(:url => 'http://bivouac.rubyforge.org/svn/trunk')
+ assert_equal 'rubyforge.org', svn.guess_forge
+
svn = SvnAdapter.new(:url => 'https://svn.sourceforge.net/svnroot/typo3/CoreDocs/trunk')
assert_equal 'sourceforge.net', svn.guess_forge
@@ -76,22 +95,27 @@ def test_guess_forge
svn = SvnAdapter.new(:url => 'https://vegastrike.svn.sourceforge.net/svnroot/vegastrike/trunk')
assert_equal 'sourceforge.net', svn.guess_forge
+ svn = SvnAdapter.new(:url => 'https://svn.code.sf.net/p/gallery/code/trunk/gallery2')
+ assert_equal 'code.sf.net', svn.guess_forge
+
svn = SvnAdapter.new(:url => 'https://appfuse.dev.java.net/svn/appfuse/trunk')
assert_equal 'java.net', svn.guess_forge
svn = SvnAdapter.new(:url => 'http://moulinette.googlecode.com/svn/trunk')
assert_equal 'googlecode.com', svn.guess_forge
+
+ svn = SvnAdapter.new(:url => 'http://moulinette.googlecode.com')
+ assert_equal 'googlecode.com', svn.guess_forge
end
def test_sourceforge_requires_https
- assert_equal 'https://gallery.svn.sourceforge.net/svnroot/gallery/trunk/gallery2',
- SvnAdapter.new(:url => 'http://gallery.svn.sourceforge.net/svnroot/gallery/trunk/gallery2').normalize.url
+ url = '://svn.code.sf.net/p/gallery/code/trunk/gallery2'
+ assert_equal "https#{url}", SvnAdapter.new(:url => "http#{url}").normalize.url
- assert_equal 'https://gallery.svn.sourceforge.net/svnroot/gallery/trunk/gallery2',
- SvnAdapter.new(:url => 'https://gallery.svn.sourceforge.net/svnroot/gallery/trunk/gallery2').normalize.url
+ assert_equal "https#{url}", SvnAdapter.new(:url => "https#{url}").normalize.url
- assert_equal 'http://pianosa.googlecode.com/svn/trunk',
- SvnAdapter.new(:url => 'http://pianosa.googlecode.com/svn/trunk').normalize.url
+ url = 'https://github.com/blackducksw/ohloh_scm/trunk'
+ assert_equal url, SvnAdapter.new(:url => url).normalize.url
end
def test_validate_server_connection
@@ -123,11 +147,30 @@ def test_recalc_branch_name
assert !svn_trunk_with_whack.branch_name
assert_equal '/trunk', svn_trunk_with_whack.recalc_branch_name
assert_equal '/trunk', svn_trunk_with_whack.branch_name
+
+ svn_trunk = SvnAdapter.new(:url => svn.root + '/trunk')
+ assert !svn_trunk.branch_name
+ svn_trunk.normalize # only normalize to ensure branch_name is populated correctly
+ assert_equal '/trunk', svn_trunk.branch_name
+
+ svn_trunk = SvnAdapter.new(:url => svn.root)
+ assert !svn_trunk.branch_name
+ svn_trunk.normalize
+ assert_equal '', svn_trunk.branch_name
end
end
- end
- def test_strip_trailing_whack_from_branch_name
- assert_equal '/trunk', SvnAdapter.new(:branch_name => "/trunk/").normalize.branch_name
+ def test_strip_trailing_whack_from_branch_name
+ with_svn_repository('svn') do |svn|
+ assert_equal '/trunk', SvnAdapter.new(:url => svn.root, :branch_name => "/trunk/").normalize.branch_name
+ end
+ end
+
+ def test_empty_branch_name_with_file_system
+ OhlohScm::ScratchDir.new do |dir|
+ svn = SvnAdapter.new(:url => dir).normalize
+ assert_equal '', svn.branch_name
+ end
+ end
end
end
diff --git a/test/unit/svn_xml_parser_test.rb b/test/unit/svn_xml_parser_test.rb
index de1f12d0..ccb9c066 100644
--- a/test/unit/svn_xml_parser_test.rb
+++ b/test/unit/svn_xml_parser_test.rb
@@ -1,7 +1,7 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require_relative '../test_helper'
-module Scm::Parsers
- class SvnXmlParserTest < Scm::Test
+module OhlohScm::Parsers
+ class SvnXmlParserTest < OhlohScm::Test
def test_basic
assert_convert(SvnXmlParser, DATA_DIR + '/simple.svn_xml_log', DATA_DIR + '/simple.ohlog')
@@ -15,5 +15,31 @@ def test_empty_xml
assert_equal("\n\n\n", SvnXmlParser.parse('', :writer => XmlWriter.new))
end
+ def test_copy_from
+ xml = <<-XML
+
+
+
+robin
+2009-02-05T13:40:46.386190Z
+
+/trunk
+
+the branch becomes the new trunk
+
+
+ XML
+ commits = SvnXmlParser.parse(xml)
+ assert_equal 1, commits.size
+ assert_equal 1, commits.first.diffs.size
+ assert_equal "/trunk", commits.first.diffs.first.path
+ assert_equal "/branches/development", commits.first.diffs.first.from_path
+ assert_equal 7, commits.first.diffs.first.from_revision
+ end
+
end
end