--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+= Ohloh SCM
+
+The Ohloh source control management library
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License Version 2 as
+published by the Free Software Foundation.
+
+Ohcount is specifically licensed under GPL v2.0, and no later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+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 <http://www.gnu.org/licenses/>.
+
+== Overview
+
+Ohloh SCM is an abstraction layer for source control management systems,
+allowing an application to interoperate with various SCMs using a
+single interface.
+
+It was originally developed at Ohloh, and is used to generate
+the reports at www.ohloh.net.
+
+== 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.
+
+Ohloh SCM does not support Windows.
+
+Ohloh SCM targets Ruby 1.8.6 and Rake 0.8.1
+
+Ohloh SCM interfaces with CVSNT, Subversion, and Git through the 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
+
+If you are using CVS instead of CVSNT, you can potentially try creating
+a shell alias or symlink mapping 'cvsnt' to 'cvs'.
+
+== Running
+
+Ensure that cvsnt, svn, svnadmin, svnsync, and git are all on your path.
+Then you can run the unit tests:
+
+$ rake
+
+You can load the library into your own Ruby application by requiring lib/scm.rb.
+
+= 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
+systems. The SCM library manages all required updates to a mirror, and reports
+the contents of the mirror in standardized ways.
+
+Each mirror is assigned a dedicated directory, and the SCM library adapter may
+store any content it desires in that directory. Usually, it's a direct clone of
+the original repository, but in the case of CVS or some Subversion servers, it
+is a conversion of the original repository to Git.
+
+The main Ohloh application orchestrates the scheduling of all updates and
+backups. On demand, the SCM library adapter then performs the following basic
+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
+
+For more information visit the Ohloh website:
+ http://labs.ohloh.net
+
+You can reach Ohloh via email at:
+ info@ohloh.net
--- /dev/null
+require 'rake'
+require 'rake/clean'
+require 'rake/testtask'
+
+
+Rake::TestTask.new :unit_tests do |t|
+ t.test_files = FileList[File.dirname(__FILE__) + '/test/unit/**/*_test.rb']
+end
+
+task :default => :unit_tests
--- /dev/null
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../lib/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
+ class CommandLine
+ attr_accessor :paths, :writer
+
+ def initialize(args=[])
+ args = args.clone # Because shift is destructive
+ set_option(args.shift) while args.first =~ /^-/
+ self.paths = args
+ end
+
+ def help
+ puts <<HELP
+Usage: ohlog [option] [paths]
+
+Ohloh source control log parser
+ http://www.ohloh.net/
+
+[option] can be one of the following:
+
+ --cvs Parse a CVS rlog
+ --svn Parse a Subversion log
+ --svn-xml Parse a Subversion XML log
+
+ -h, --human Output result as a human-readable log (default)
+ -x, --xml Output result as an XML log
+
+ -?, --help Display this message
+
+[paths] must be one or more source control log filenames.
+ If no path is given, input will be read from STDIN.
+
+Examples:
+
+ cvsnt -d :pserver:anonymous:@cvs-mirror.mozilla.org:/cvsroot rlog mozilla/browser | ohlog --cvs
+
+ svn log --xml --verbose -r2:3 http://svn.collab.net/repos/svn/trunk | ohlog --svn-xml
+
+HELP
+ end
+
+ def cvs
+ parse CvsParser
+ end
+
+ def svn
+ parse SvnParser
+ end
+
+ def svn_xml
+ parse SvnXmlParser
+ end
+
+ def parse(parser)
+ self.writer ||= HumanWriter.new(STDOUT)
+
+ if self.paths.any?
+ self.paths.each do |path|
+ parser.parse File.new(path,'r'), :writer => self.writer
+ end
+ else
+ parser.parse STDIN, :writer => self.writer
+ end
+ end
+
+ def subcommand=(s)
+ if @subcommand
+ STDERR.puts "Error: Multiple commands specified."
+ exit 1
+ else
+ @subcommand=s
+ end
+ end
+
+ def subcommand
+ @subcommand
+ end
+
+ def set_option(option)
+ case option
+ when '--cvs'
+ self.subcommand = :cvs
+ when '--svn'
+ self.subcommand = :svn
+ when '--svn-xml'
+ self.subcommand = :svn_xml
+ when '-h', '--human'
+ self.writer = HumanWriter.new(STDOUT)
+ when '-x', '--xml'
+ self.writer = XmlWriter.new(STDOUT)
+ when '-?', '--help'
+ self.subcommand = :help
+ else
+ STDERR.puts "Type 'ohlog -?' for usage."
+ exit 1
+ end
+ end
+
+ def run!
+ self.subcommand ||= :summary
+ if self.respond_to?(self.subcommand)
+ self.send(self.subcommand)
+ else
+ STDERR.puts "Type 'ohlog -?' for usage."
+ exit 1
+ end
+ end
+ end
+
+ CommandLine.new(ARGV).run!
+end
--- /dev/null
+module Scm
+end
+
+require 'rbconfig'
+
+$: << File.join(File.dirname(__FILE__),"..")
+
+NULL_SHA1 = '0000000000000000000000000000000000000000' unless defined?(NULL_SHA1)
+
+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/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/array_writer'
+require 'lib/scm/parsers/xml_writer'
+require 'lib/scm/parsers/human_writer'
--- /dev/null
+module Scm::Adapters
+ require 'logger'
+ class AbstractAdapter
+ def self.logger
+ @@logger ||= Logger.new(STDERR)
+ end
+
+ def self.logger=(val)
+ @@logger = val
+ end
+
+ def logger
+ self.class.logger
+ end
+
+ # Custom implementation of shell execution, does not block when the "pipe is full."
+ # Raises an exception if the shell returns non-zero exit code.
+ def self.run(cmd)
+ logger.debug { cmd }
+ status, out, err = systemu(cmd)
+ raise RuntimeError.new("#{cmd} failed: #{out}\n#{err}") if status.exitstatus != 0
+ out
+ end
+
+ def run(cmd)
+ AbstractAdapter::run(cmd)
+ end
+
+ # As above, but does not raise an exception when an error occurs.
+ # Returns two values, stdout and stderr.
+ def self.run_with_err(cmd)
+ logger.debug { cmd }
+ status, out, err = systemu(cmd)
+ [out, err]
+ end
+
+ def run_with_err(cmd)
+ AbstractAdapter::run_with_err(cmd)
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class AbstractAdapter
+ # The full regex that permits all possible URLs supported by the source control system.
+ def self.url_regex
+ /.+/
+ end
+
+ # A limited regex that permits only URLs that are publicly addressable on the web.
+ # This regex should refuse access to local disk files.
+ def self.public_url_regex
+ /.+/
+ end
+
+ def validate
+ @errors = []
+ @errors << validate_url
+ @errors << validate_branch_name
+ @errors << validate_username
+ @errors << validate_password
+ @errors.compact!
+ end
+
+ def validate_url
+ return [:url, "The URL can't be blank."] unless @url and @url.length > 0
+ return [:url, "The URL must not be longer than 120 characters."] unless @url.length <= 120
+
+ regex = @public_urls_only ? self.class.public_url_regex : self.class.url_regex
+ return [:url, "The URL does not appear to be a valid server connection string."] unless @url =~ regex
+ end
+
+ 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 may contain only letters, numbers, spaces, and the special characters '_', '-', '+', '/', '^', and '.'"] unless @branch_name =~ /^[A-Za-z0-9_^\-\+\.\/\ ]+$/
+ end
+
+ def validate_username
+ return nil unless @username
+ return [:username, "The username must not be longer than 32 characters."] unless @username.length <= 32
+ return [:username, "The username may contain only A-Z, a-z, 0-9, and underscore (_)"] unless @username =~ /^\w*$/
+ end
+
+ def validate_password
+ return nil unless @password
+ return [:password, "The password must not be longer than 32 characters."] unless @password.length <= 32
+ return [:password, "The password contains illegal characters"] unless @password =~ /^[\w!@\#$%^&*\(\)\{\}\[\]\;\?\|\+\-\=]*$/
+ end
+
+ def valid?
+ validate
+ errors.empty?
+ end
+
+ def exists?
+ exist?
+ end
+
+ # Ping the remote server to ensure it is responding and that the desired branch exists.
+ def validate_server_connection
+ end
+
+ # Give the object a chance to massage/cleanup its input attributes
+ def normalize
+ @url.strip! if @url
+ @branch_name.strip! if @branch_name
+ @username.strip! if @username
+ @password.strip! if @password
+ self
+ end
+
+ # Based on the URL, return the domain name of the forge hosting this code.
+ def guess_forge
+ # This is a very general rule for systems using HTTP-style URLs.
+ url =~ /:\/\/([^\/]+)\// ? $1 : nil
+ end
+ end
+end
--- /dev/null
+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'
--- /dev/null
+module Scm::Adapters
+ class CvsAdapter
+ def commits(since=nil)
+ result = []
+ open_log_file(since) do |io|
+ result = Scm::Parsers::CvsParser.parse(io, :branch_name => branch_name)
+ end
+
+ 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.
+
+ # We must now remove any duplicates caused by timestamp fudge factors,
+ # and only return commits with timestamp > since.
+
+ # 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)
+
+ # 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(/[\/-]/, '.'))
+
+ result.each_index do |i|
+ if result[i].token =~ re # We found the match for since
+ if i == result.size-1
+ return [] # There aren't any new commits.
+ else
+ return result[i+1..-1]
+ end
+ end
+ end
+
+ # Something bad is going on: 'since' 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
+ # undetected problem (CVS was in an intermediate state?) so the list of timestamps we
+ # calculated last time does not match the list of timestamps we calculated this time.
+ #
+ # 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.")
+ end
+
+ # Gets the rlog of the repository and saves it in a temporary file.
+ # If you pass a timestamp token, then only commits after the timestamp will be returned.
+ #
+ # Warning!
+ #
+ # CVS servers are apparently unreliable when you truncate the log by timestamp -- perhaps round-off error?
+ # 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)
+ begin
+ run "cvsnt -d #{self.url} rlog #{opt_branch} #{opt_time(since)} '#{self.module_name}' > #{rlog_filename}"
+ File.open(rlog_filename, 'r') do |file|
+ yield file
+ end
+ ensure
+ File.delete rlog_filename if FileTest.exists?(rlog_filename)
+ end
+ end
+
+ def opt_time(since=nil)
+ if since
+ most_recent_time = parse_time(since) - 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
+ ""
+ end
+ end
+
+ def rlog_filename
+ File.join('/tmp', (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
+ def parse_time(token)
+ case token
+ when /(\d\d\d\d).(\d\d).(\d\d) (\d\d):(\d\d):(\d\d)/
+ Time.gm( $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i )
+ end
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class CvsAdapter
+ # Returns an array of file and directory names from the remote server.
+ # Directory names will end with a trailing '/' character.
+ #
+ # Directories named "CVSROOT" are always ignored, and thus 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)
+ path = File.join(@module_name, path.to_s)
+
+ cmd = "cvsnt -q -d #{url} ls -e '#{path}'"
+
+ stdout, stderr = run_with_err(cmd)
+
+ files = []
+ stdout.each_line do |s|
+ s.strip!
+ s = $1 + '/' if s =~ /^D\/(.*)\/\/\/\/$/
+ s = $1 if s =~ /^\/(.*)\/.*\/.*\/.*\/$/
+ next if s == "CVSROOT/"
+ files << s if s and s.length > 0
+ end
+
+ # Some of the cvs 'errors' are just harmless problems with some directories.
+ # If we recognize all the error messages, then nothing is really wrong.
+ # If some error messages go unhandled, then there really is an error.
+ stderr.each_line do |s|
+ s.strip!
+ error_handled = false
+
+ ignored_error_messages = [
+ /Listing modules on server/,
+ /Listing module: #{Regexp.escape(path.to_s)}/,
+ /-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/
+ ]
+
+ # 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'
+
+ if s.length == 0
+ error_handled = true
+ elsif s =~ /cvs server: New directory `(#{Regexp.escape(path.to_s)}\/)?(.*)' -- ignored/
+ files << "#{$2}/"
+ error_handled = true
+ end
+
+ ignored_error_messages.each do |m|
+ error_handled = true if s =~ m
+ end
+
+ logger.warn { "'#{cmd}' resulted in unhandled error '#{s}'" } unless error_handled
+ return nil unless error_handled
+ end
+
+ files.sort
+ end
+
+ def log(most_recent_token=nil)
+ run "cvsnt -d #{self.url} rlog #{opt_branch} #{opt_time(most_recent_token)} '#{self.module_name}'"
+ end
+
+ def checkout(r, local_directory)
+ opt_D = r.token ? "-D'#{r.token}Z'" : ""
+
+ if FileTest.exists?(local_directory + '/CVS/Root')
+ # We already have a local enlistment, so do a quick update.
+ if r.directories.size > 0
+ build_ordered_directory_list(r.directories).each do |d|
+ if d.length == 0
+ run "cd #{local_directory} && cvsnt update -d -l -C #{opt_D} ."
+ else
+ run "cd #{local_directory} && cvsnt update -d -l -C #{opt_D} '#{d}'"
+ end
+ end
+ else
+ # Brute force: get all updates
+ logger.warn("Revision #{r.token} did not contain any directories. Using brute force update of entire module.")
+ run "cd #{local_directory} && cvsnt update -d -R -C #{opt_D}"
+ end
+ else
+ # We do not have a local enlistment, so do a slow checkout to create one.
+ # Silly cvsnt won't accept an absolute path. We'll have to play some games and cd to the parent directory.
+ parent_path, checkout_dir = File.split(local_directory)
+ FileUtils.mkdir_p(parent_path) unless FileTest.exist?(parent_path)
+ run "cd #{parent_path} && cvsnt -d #{self.url} checkout #{opt_D} -A -d'#{checkout_dir}' '#{self.module_name}'"
+ end
+ end
+
+ # A revision can contain an arbitrary collection of directories.
+ # We need to ensure that for every directory we want to fetch, we also have its parent directories.
+ def build_ordered_directory_list(directories)
+ # Integration Test Limitation
+ # cvsnt has problems with absolute path names, so we are stuck with
+ # using cvs modules that are only a single directory deep when testing.
+ # We'll check if the url begins with '/' to detect an integration test,
+ # then return an empty string (ie, the default root directory) if so.
+ return [''] if self.url =~ /^\//
+
+ list = []
+ directories.collect{ |a| trim_directory(a.to_s).to_s }.each do |d|
+ # We always ignore Attic directories, which just contain deleted files
+ # Update the parent directory of the Attic instead.
+ if d =~ /^(.*)Attic$/
+ d = $1
+ d = d[0..-2] if d.length > 0 and d[-1,1]=='/'
+ end
+
+ unless list.include? d
+ list << d
+ # We also need to include every parent directory of the directory
+ # we are interested in, all the way up to the root.
+ while d.rindex('/') and d.rindex('/') > 0 do
+ d = d[0..(d.rindex('/')-1)]
+ if list.include? d
+ break
+ else
+ list << d
+ end
+ end
+ end
+ end
+ # Sort the list by length because we need to update parent directories before children
+ list.sort! { |a,b| a.length <=> b.length }
+ end
+
+ def trim_directory(d)
+ # If we are connecting to a remote server (basically anytime we are not
+ # running the integration test) then we need to create a relative path
+ # by trimming the prefix from the directory.
+ # The prefix can be determined by examining the url and the module name.
+ # For example, if url = ':pserver:anonymous:@moodle.cvs.sourceforge.net:/cvsroot/moodle'
+ # and module = 'contrib', then the directory prefix = '/cvsroot/moodle/contrib/'
+ if root
+ d[root.length..-1]
+ else
+ d # If not remote, just leave the directory name as-is
+ end
+ end
+
+ def root
+ "#{$2}/#{self.module_name}/" if self.url =~ /^:pserver:.*@[^:]+:(\d+)?(\/.*)$/
+ end
+
+ def opt_branch
+ if branch_name != nil and branch_name.length > 0 and branch_name != 'HEAD'
+ "-r'#{branch_name}'"
+ else
+ "-b -r1:"
+ end
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class CvsAdapter
+ def self.url_regex
+ /^(:pserver:[\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_\-\+\.\/]*$/
+ end
+
+ def validate
+ super
+ @errors << validate_module_name
+ @errors.compact!
+ end
+
+ def validate_module_name
+ return [:module_name, "The module name can't be blank."] if @module_name.to_s.length == 0
+ return [:module_name, "The module name must not be longer than 120 characters."] unless @module_name.length <= 120
+ return [:module_name, "The module name may contain only letters, numbers, spaces, and the special characters '_', '-', '+', '/', and '.'"] unless @module_name =~ /^[A-Za-z0-9_\-\+\.\/\ ]+$/
+ end
+
+ def normalize
+ super
+ @module_name = @module_name.strip if @module_name
+
+ # Some CVS forges publish an URL which is actually a symlink, which causes CVSNT to crash.
+ # For some forges, we can work around this by using an alternate directory.
+ case guess_forge
+ when 'java.net', 'netbeans.org'
+ @url.gsub!(/:\/cvs\/?$/, ':/shared/data/ccvs/repository')
+ when 'gna.org'
+ @url.gsub!(/:\/cvs\b/, ':/var/cvs')
+ end
+
+ sync_pserver_username_password
+
+ self
+ end
+
+ # This bit of code patches up any inconsistencies that may arise because there
+ # is both a @password attribute and a password embedded in the :pserver: url.
+ # This method guarantees that they are both the same.
+ #
+ # It's assumed that if the user specified a @password attribute, then that is
+ # the preferred value and it should take precedence over any password found
+ # in the :pserver: url.
+ #
+ # If the user did not specify a @password attribute, then the value
+ # found in the :pserver: url is assigned to both.
+ def sync_pserver_username_password
+ # Do nothing unless pserver connection string is well-formed.
+ return unless self.url =~ /:pserver:([\w\-\_]*)(:([\w\-\_]*))?@(.*)$/
+
+ pserver_username = $1
+ pserver_password = $3
+ pserver_remainder = $4
+
+ @username = pserver_username if @username.to_s == ''
+ @password = pserver_password if @password.to_s == ''
+
+ self.url = ":pserver:#{@username}:#{password}@#{pserver_remainder}"
+ end
+
+ def validate_server_connection
+ return unless valid?
+ if ls.nil?
+ @errors << [:failed, "The cvs server did not respond to an 'ls' command. Are the URL and module name correct?"]
+ end
+ end
+
+ # 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
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class CvsAdapter < AbstractAdapter
+ attr_accessor :module_name
+
+ def english_name
+ 'CVS'
+ end
+
+ def initialize(params={})
+ super
+ @module_name = params[:module_name]
+ end
+ end
+end
+
+require 'lib/scm/adapters/cvs/validation'
+require 'lib/scm/adapters/cvs/commits'
+require 'lib/scm/adapters/cvs/misc'
--- /dev/null
+module Scm::Adapters
+ class GitAdapter < AbstractAdapter
+ def cat_file(commit, diff)
+ cat(diff.sha1)
+ end
+
+ def cat_file_parent(commit, diff)
+ cat(diff.parent_sha1)
+ end
+
+ def cat(sha1)
+ run "cd '#{url}' && git cat-file -p #{sha1}"
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class GitAdapter < AbstractAdapter
+
+ #---------------------------------------------------------------------------
+ # COMMIT-RELATED CODE
+ #-------------------------------------------------------------------------
+
+ # Commit all changes in the working directory, using metadata from the passed commit.
+ def commit_all(commit=Commit.new)
+ init_db
+ ensure_gitignore
+ write_token(commit.token)
+
+ # Establish the author, email, message, etc. for the git-commit.
+ message_filename = set_commit_metadata(commit)
+
+ run "cd '#{self.url}' && git add ."
+ if anything_to_commit?
+ run " cd '#{self.url}' && git commit -a -F #{message_filename}"
+ else
+ logger.info { "nothing to commit" }
+ end
+ end
+
+ # Store all of the commit metadata in the GIT environment variables
+ # where they will be picked up by the git-commit command.
+ #
+ # Commit info is required.
+ # Author info is optional, and defaults to committer info.
+ def set_commit_metadata(commit)
+
+ ENV['GIT_COMMITTER_NAME'] = commit.committer_name || '[anonymous]'
+ ENV['GIT_AUTHOR_NAME'] = commit.author_name || ENV['GIT_COMMITTER_NAME']
+
+ ENV['GIT_COMMITTER_EMAIL'] = commit.committer_email || ENV['GIT_COMMITTER_NAME']
+ ENV['GIT_AUTHOR_EMAIL'] = commit.author_email || ENV['GIT_AUTHOR_NAME']
+
+ ENV['GIT_COMMITTER_DATE'] = commit.committer_date.to_s
+ ENV['GIT_AUTHOR_DATE'] = (commit.author_date || commit.committer_date).to_s
+
+ # This is a one-off fix for DrJava, which includes some escape characters
+ # in one of its Subversion messages. This might lead to a more generalized
+ # cleanup of message text, but for now...
+ commit.message.gsub!(/\\027/,'') if commit.message
+
+ # Git requires a non-empty message
+ if commit.message.nil? || commit.message =~ /\A\s*\z/
+ commit.message = '[no message]'
+ end
+
+ # We need to store the message in a file in case it contains crazy characters
+ # that would corrupt a bash command line.
+ File.open(message_filename, 'w') do |f|
+ f.write commit.message
+ end
+ message_filename
+ end
+
+ # By hiding the message file inside the .git directory, we
+ # avoid it being found by the commit-all.
+ def message_filename
+ File.expand_path(File.join(git_path, 'ohloh_message'))
+ end
+
+ # 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
+ end
+
+ # Ensures that the repository directory exists, and that the git database has been initialized.
+ def init_db
+ unless FileTest.exist? url
+ run "mkdir -p '#{url}'"
+ end
+ unless FileTest.exist? git_path
+ run "cd '#{url}' && git init-db"
+ end
+ end
+
+
+ #-------------------------------------------------------------------------
+ # GIT IGNORE CODE
+ # Ensures that we do not waste storage space on non-source code files
+ #-------------------------------------------------------------------------
+
+ unless defined?(IGNORE)
+ IGNORE = [
+ ".svn",
+ "CVS",
+ "*.jar",
+ "*.tar",
+ "*.gz",
+ "*.tgz",
+ "*.zip",
+ "*.gif",
+ "*.jpg",
+ "*.jpeg",
+ "*.bmp",
+ "*.png",
+ "*.tif",
+ "*.tiff",
+ "*.ogg",
+ "*.aiff",
+ "*.wav",
+ "*.mp3",
+ "*.au",
+ "*.ra",
+ "*.m4a",
+ "*.pdf",
+ "*.mpg",
+ "*.mov",
+ "*.qt",
+ "*.avi",
+ "*.xbm"
+ ]
+ end
+
+ # The .gitignore file will be created if it does not exist.
+ # If our desired filespec is not found in .gitignore, it will be appended
+ # to the end of .gitignore.
+ def ensure_gitignore
+ IGNORE.each do |ignore|
+ gitignore_filename = File.join(self.url, '.gitignore')
+ found = false
+ File.open(gitignore_filename, File::CREAT | File::RDONLY) do |io|
+ io.readlines.each do |l|
+ if l.chomp == ignore
+ found = true
+ break
+ end
+ end
+ end
+ unless found
+ File.open(gitignore_filename, File::APPEND | File::WRONLY) do |io|
+ io.puts ignore
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+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)
+ run(rev_list_command(since)).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)
+ Git::LogParser.parse(log(since)) do |e|
+ yield e
+ end
+ end
+
+ # Command line to output a single commit.
+ # We use a special format string so that we can get the date in a format Ruby can parse.
+ WHATCHANGED="git whatchanged --root --abbrev=40 --max-count=1 --pretty=format:'__BEGIN_COMMIT__%nCommit: %H%nAuthor: %an%nAuthorEmail: %ae%nDate: %aD%n__BEGIN_COMMENT__%n%s%n%b%n__END_COMMENT__'" unless defined?(WHATCHANGED)
+
+ REV_LIST_OPTIONS=" --root --reverse --no-merges --topo-order " unless defined?(REV_LIST_OPTIONS)
+
+ # Retrieves the git log in the format expected by Git::LogParser.
+ # We get the log forward chronological order (oldest first)
+ def log(since=nil)
+ if has_branch?
+ if since && since==self.head
+ '' # Nothing new.
+ else
+ run "#{rev_list_command(since)} | xargs -n 1 #{WHATCHANGED}"
+ end
+ else
+ ''
+ end
+ end
+
+ def rev_list_command(since=nil)
+ if since
+ "cd '#{self.url}' && git rev-list #{REV_LIST_OPTIONS} #{since}..HEAD #{self.branch_name}"
+ else
+ "cd '#{self.url}' && git rev-list #{REV_LIST_OPTIONS} #{self.branch_name}"
+ end
+ end
+
+ end
+end
--- /dev/null
+module Scm::Adapters::Git
+ class LogParser
+
+ NO_AUTHOR='(no author)'
+
+ def self.parse io
+ e = nil
+ state = :key_values
+
+ io.each do |line|
+ line.chomp!
+
+ # Kind of a hack: the diffs section is not always present.
+ # If we are expecting a line of diffs, but instead find a line
+ # starting with "Commit: ", that means the diffs section
+ # is missing for this commit, and we need to fix up our state.
+ if state == :diffs and line =~ /^Commit: ([a-z0-9]+)$/
+ state = :key_values
+ end
+
+ if state == :key_values
+ if line =~ /^Commit: ([a-z0-9]+)$/
+ sha1 = $1
+ yield e if e
+ e = Scm::Commit.new
+ e.diffs = []
+ e.token = sha1
+ e.author_name = NO_AUTHOR
+ 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
+ elsif line == "__BEGIN_COMMENT__"
+ state = :message
+ elsif line =~ /^AuthorEmail: (.+)$/
+ e.author_email = $1
+ # 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 if e.author_name.to_s.empty? || e.author_name == NO_AUTHOR
+ end
+
+ elsif state == :message
+ if line == "__END_COMMENT__"
+ state = :diffs
+ elsif line != "<unknown>"
+ if e.message
+ e.message << "\n" << line
+ else
+ e.message = line
+ end
+ end
+
+ elsif state == :diffs
+ if line == "__BEGIN_COMMIT__"
+ state = :key_values
+ 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'
+ end
+
+ else
+ raise RuntimeError("Unknown parser state #{state.to_s}")
+ end
+ end
+
+ yield e if e
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class GitAdapter < AbstractAdapter
+ def git_path
+ File.join(self.url, '/.git')
+ end
+
+ def exist?
+ begin
+ !!(head)
+ rescue
+ logger.debug { $! }
+ false
+ end
+ end
+
+ def head
+ run("git ls-remote --heads '#{url}' #{branch_name}") =~ /^(^[a-z0-9]{40})\s+\S+$/
+ $1
+ end
+
+ def export(dest_dir, commit_id = 'HEAD')
+ run "cd #{url} && git archive #{commit_id} | tar -C #{ dest_dir } -x"
+ end
+
+ # Moves us the correct branch and checks out the most recent files.
+ #
+ # Anything not tracked by Git is deleted.
+ #
+ # This method may not seem like the most efficient way to accomplish this,
+ # but we need very high reliability and this sequence gets the job done every time.
+ def checkout
+ if FileTest.exist? git_path
+ run "cd '#{url}' && git clean -f -d -x"
+ if self.has_branch?
+ run "cd '#{url}' && git reset --hard #{self.branch_name} --"
+ run "cd '#{url}' && git checkout #{self.branch_name} --"
+ end
+ end
+ end
+
+ #---------------------------------------------------------------------------
+ # BRANCH-RELATED CODE
+ #-------------------------------------------------------------------------
+
+ # Returns an array of all branch names
+ def branches
+ run("cd '#{self.url}' && git branch").split.collect { |b| b =~ /\b(.+)$/ ; $1 }.compact
+ end
+
+ def has_branch?(name=self.branch_name)
+ return false unless FileTest.exist?(self.git_path)
+ self.branches.include?(name)
+ end
+
+ # Create a new local branch to mirror the remote one
+ # If a branch of this name already exist, nothing happens.
+ def create_tracking_branch(name)
+ return if name.to_s == ''
+
+ unless self.branches.include? name
+ run "cd '#{self.url}' && git branch -f #{name} origin/#{name}"
+ end
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class GitAdapter < AbstractAdapter
+
+ def pull(from, &block)
+ logger.info { "Pulling #{from.url}" }
+ case from
+ when GitAdapter
+ clone_or_fetch(from, &block)
+ when CvsAdapter, SvnAdapter
+ convert(from, &block)
+ end
+ end
+
+ # Clone source_scm as new repository.
+ # If a local repository already exists, update the local repository with the latest changes.
+ def clone_or_fetch(source_scm)
+ raise ArgumentError.new("Cannot pull from #{source_scm.inspect}") unless source_scm.is_a?(GitAdapter)
+
+ yield(0,1) if block_given? # Progress bar callback
+
+ unless self.exist? && self.has_branch?
+ run "mkdir -p '#{self.url}'"
+ run "rm -rf '#{self.url}'"
+ run "git clone -q -n '#{source_scm.url}' '#{self.url}' >/dev/null 2>&1"
+ create_tracking_branch(source_scm.branch_name) # ensure the correct branch exist locally
+ checkout # switch to the correct branch
+ else
+ checkout # should already be on correct branch, but some old repositories were stricken by a bug
+ run "cd '#{self.url}' && git fetch --update-head-ok '#{source_scm.url}' #{self.branch_name}:#{self.branch_name}"
+ end
+ clean_up_disk
+
+ yield(1,1) if block_given? # Progress bar callback
+ end
+
+ # Apply all recent changes from source_scm, converting to Git in the process.
+ #
+ # Progress is reported by yielding [current_step, total_steps] pairs to a provided block.
+ #
+ # There are two design goals here:
+ #
+ # First, minimize adminstrator burden. The conversion process is
+ # idempotent: if it fails, Ohloh's first recourse is always to try again.
+ # For this reason, it is important that multiple pulls do not result in
+ # duplicate data. No matter how badly a previous attempt may be screwed up
+ # the contents on disk, a second attempt should be able to gracefully
+ # resume.
+ #
+ # Second, maximize compatibility with a wild array of public source control
+ # servers. CVS in particular has a broad spectrum of server capabilities.
+ # Therefore, this conversion algorithm is brute-force simplicity itself. We
+ # require a minimum number of features: a log feature to get the list of
+ # commits, and the checkout feature to get the contents of each commit.
+ #
+ # The basic idea for conversion is that we sequentially check out each
+ # revision from the original repository into a local directory, which also
+ # happens to be a local Git repository. As each revision is checked out
+ # from the original repository, we commit it into the local Git repository.
+ # By walking forward through each commit, we build a Git equivalent to the
+ # original repository.
+ #
+ # It's not fast, but it almost always succeeds.
+ #
+ # The original commit ID (CVS timestamp or Subversion revision number) is
+ # stored in a special file called 'ohloh_token' and checked in as part of
+ # each Git commit. This enables us to match code to original source
+ # commit, and also enables us to pick up where we left off for incremental
+ # updates.
+ #
+ # Only a single branch from the original repository is converted.
+ 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)
+ 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
+ checkout
+
+ commits.each_with_index do |r,i|
+ yield(i,commits.size) if block_given? # Progress bar callback
+
+ logger.info { "Downloading revision #{r.token} (#{i+1} of #{commits.size})... " }
+ begin
+ source_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)
+ end
+
+ logger.debug { "Committing revision #{r.token} (#{i+1} of #{commits.size})... " }
+ commit_all(r)
+ end
+ yield(commits.size, commits.size) if block_given?
+ else
+ logger.info { "Already up-to-date." }
+ end
+ end
+
+ # Deletes everything in the working directory.
+ # All pending changes are discarded.
+ # Only the hidden git folder will remain.
+ def clean_up_disk
+ if FileTest.exist? url
+ run "cd #{url} && find . -maxdepth 1 -not -name .git -not -name . -print0 | xargs -0 rm -rf --"
+ end
+ end
+ end
+end
--- /dev/null
+require 'socket'
+
+module Scm::Adapters
+ class GitAdapter < AbstractAdapter
+
+ COMMITTER_NAME = 'ohloh_slave' unless defined?(COMMITTER_NAME)
+
+ def push(to)
+ logger.info { "Pushing to #{to.url}" }
+
+ if to.exist?
+ ENV['GIT_COMMITTER_NAME'] = COMMITTER_NAME
+ run "cd '#{self.url}' && git push '#{to.url}' #{self.branch_name}:#{to.branch_name}"
+ else
+ if to.local?
+ # Create a new repo on the same local machine. Just use existing pull code in reverse.
+ to.pull(self)
+ else
+ run "ssh #{to.hostname} 'mkdir -p #{to.path}'"
+ run "scp -rpqB #{git_path} #{to.hostname}:#{to.path}"
+ end
+ end
+ end
+
+ def local?
+ return true if hostname == Socket.gethostname
+ return false if url =~ /:/
+ true
+ end
+
+ def hostname
+ url =~ /^([^:^\/]+):(.+)/ ? $1 : nil
+ end
+
+ def path
+ url =~ /^([^:^\/]+):(.+)/ ? $2 : nil
+ end
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class GitAdapter < AbstractAdapter
+
+ #---------------------------------------------------------------------------
+ # TOKEN-RELATED CODE
+ #
+ # As CVS and Subversion are converted to Git, the unique ID of each commit
+ # from the original source control system is stored in a special token file.
+ #
+ # Whenever Ohloh needs to incrementally update our local copy of the code with
+ # the latest commits, we just check the token file to see at which point we need
+ # to restart the process.
+ #---------------------------------------------------------------------------
+
+ def token_filename
+ 'ohloh_token'
+ end
+
+ def token_path
+ File.join(self.url, token_filename)
+ end
+
+ # Determine the most recent revision that was safely stored in the GIT archive.
+ # Resets the token file on disk to the most recent version stored in the repository.
+ def read_token
+ token = nil
+ if self.exist?
+ begin
+ token = run("cd '#{url}' && git cat-file -p `git ls-tree HEAD #{token_filename} | cut -c 13-51`").strip
+ rescue RuntimeError => e
+ # If the git repository doesn't have a token file yet, it will error out.
+ # We want to just quietly return nil.
+ if e.message =~ /pathspec '#{token_filename}' did not match any file\(s\) known to git/
+ return nil
+ else
+ raise
+ end
+ end
+ end
+ token
+ end
+
+ # Saves the new token in a well-known file.
+ # If the passed token is empty, this method silently does nothing.
+ def write_token(token)
+ if token and token.to_s.length > 0
+ File.open(token_path, 'w') do |f|
+ f.write token.to_s
+ end
+ end
+ end
+ end
+end
--- /dev/null
+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
--- /dev/null
+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/log_parser'
+require 'lib/scm/adapters/git/token'
+require 'lib/scm/adapters/git/push'
+require 'lib/scm/adapters/git/pull'
+require 'lib/scm/adapters/git/misc'
--- /dev/null
+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')
+ run "svn cat -r #{revision} '#{SvnAdapter.uri_encode(File.join(self.root, self.branch_name.to_s, path.to_s))}@#{revision}'"
+ end
+ end
+end
--- /dev/null
+require 'rexml/document'
+require 'sha1'
+
+module Scm::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.
+ #
+ # 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-"
+ 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)
+ 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
+ end
+
+ # Yields each commit following revision number 'since'. 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
+ # contents. Since Ohloh requires the list of every individual file affected, and doesn't care about
+ # directories, the complexity (and time) of this method comes in expanding directories with a recursion
+ # through every file in the directory.
+ #
+ # Additionally, Ohloh requires the SHA1 hash of every file, as well as the SHA1 of its prior version.
+ # These must match the SHA1 values that would be generated by Git.
+ #
+ def each_commit(since=nil)
+ commit_tokens(since).each do |rev|
+ yield populate_sha1s!(deepen_commit(strip_commit_branch(verbose_commit(rev))))
+ end
+ end
+
+ # For a given commit, replace any diffs that point to directories with diffs for each file that
+ # the directory contains.
+ def deepen_commit(commit)
+ deep_commit = commit.clone
+ 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 }
+ deep_commit
+ 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)
+ # 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)
+ recurse_files(diff.path, recurse_rev).collect do |f|
+ Scm::Diff.new(:action => diff.action, :path => File.join(diff.path, f))
+ end
+ else
+ diff
+ end
+ end
+
+ # Strip all paths in this commit to remove the leading branch_name.
+ # Throw away any diffs in the commit that don't lie in the branch_name we care about.
+ def strip_commit_branch(commit)
+ stripped_commit = commit.clone
+ if commit.diffs
+ stripped_commit.diffs = commit.diffs.collect { |d| strip_diff_branch(d) }.compact
+ end
+ stripped_commit
+ end
+
+ # Return a new diff whose path excludes the leading branch_name.
+ # If the diff is not in the branch, return nil.
+ def strip_diff_branch(diff)
+ stripped_diff = diff.clone
+ stripped_diff.path = strip_path_branch(diff.path)
+ stripped_diff.path && stripped_diff
+ end
+
+ # Returns only the portion of the path following branch_name.
+ # Returns nil if the path is not within the branch.
+ def strip_path_branch(path)
+ if path == self.branch_name.to_s
+ ''
+ else
+ $1 if path =~ /^#{Regexp.escape(self.branch_name.to_s)}(\/.*)$/
+ 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
+ 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}"
+ end
+
+ def open_log_file(since=0)
+ begin
+ if (since.to_i + 1) <= max_revision
+ run "svn log --xml --stop-on-copy -r #{since.to_i + 1}:HEAD '#{SvnAdapter.uri_encode(self.url)}' #{opt_auth} > #{log_filename}"
+ else
+ # 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 '<?xml version="1.0"?>' }
+ end
+ File.open(log_filename, 'r') { |io| yield io }
+ rescue
+ File.delete(log_filename) if FileTest.exist?(log_filename)
+ raise
+ end
+ end
+
+ def log_filename
+ File.join('/tmp', (self.url).gsub(/\W/,'') + '.log')
+ end
+
+ 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}'"
+ end
+
+ # Recurses the entire repository and returns an array of file names.
+ # Directories are not returned.
+ # 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')
+ begin
+ stdout = run "svn ls -r #{revision} --recursive #{opt_auth} '#{SvnAdapter.uri_encode(File.join(root, branch_name.to_s, path.to_s))}@#{revision}'"
+ rescue
+ puts $!.inspect
+ return nil
+ end
+
+ files = []
+ stdout.each_line do |s|
+ s.chomp!
+ files << s if s.length > 0 and s !~ /CVSROOT\// and s[-1..-1] != '/'
+ end
+ files.sort
+ end
+
+ #---------------------------------------------------------------------
+ # SHA1 Computation
+ # Generates SHA1 hashes for file contents, matching Git's method.
+ #---------------------------------------------------------------------
+
+ # Populates the SHA1 values for each diff in a commit.
+ def populate_sha1s!(commit)
+ commit.diffs.each do |diff|
+ populate_sha1!(diff, commit.token)
+ end
+ commit
+ end
+
+ # Populates the SHA1 values for a single diff.
+ def populate_sha1!(diff, rev)
+ diff.sha1 =
+ case diff.action
+ when 'D'
+ NULL_SHA1
+ else
+ get_sha1(diff.path, rev)
+ end
+
+ diff.parent_sha1 =
+ case diff.action
+ when 'A'
+ NULL_SHA1
+ else
+ # It is possible for Subversion to report deletion or modification of a file
+ # which didn't previously exist. This happens, for instance, when a directory
+ # is copied and some contents of that directory are deleted during
+ # the same commit. We don't care about these deletions and don't need to report them.
+ # The parent_sha1 computation might fail, so use the 'try' version.
+ try_get_sha1(diff.path, rev-1)
+ end
+
+ diff
+ end
+
+
+ # Use in cases where we're not sure that the file actually exists.
+ # Returns NULL_SHA1 if the file is not found.
+ def try_get_sha1(path=nil, revision='HEAD')
+ begin
+ get_sha1(path, revision)
+ rescue
+ if $!.message =~ /svn: (File not found|.* is not a directory in filesystem)/
+ NULL_SHA1
+ else
+ raise
+ end
+ end
+ end
+
+ # Use in cases where the file should actually exist.
+ # Raises an exception if the file is not found.
+ def get_sha1(path=nil, revision='HEAD')
+ generate_sha1(cat(path, revision))
+ end
+
+ def generate_sha1(contents)
+ contents.to_s == '' ? NULL_SHA1 : SHA1.sha1("blob #{contents.length}\0#{contents}").to_s
+ end
+ end
+end
--- /dev/null
+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?
+ # If it's a local directory, first just check that the dir exists
+ return false if url =~ /^file:\/\/(.+)/ && !FileTest.exist?(File.join($1, 'db'))
+
+ begin
+ !!(max_revision)
+ 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
+
+ def max_revision
+ self.info =~ /^Revision: (\d+)$/ ? $1.to_i : nil
+ 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 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
--- /dev/null
+#!/usr/bin/env bash
+exit 0
--- /dev/null
+module Scm::Adapters
+ class SvnAdapter < AbstractAdapter
+
+ def pull(from)
+ logger.warn { "Pulling #{from.url}" }
+ yield(0,1) if block_given? # Progress bar callback
+
+ unless self.exist?
+ svnadmin_create
+ svnsync_init(from)
+ end
+ SvnAdapter.svnsync_sync(from, self)
+
+ yield(1,1) if block_given? # Progress bar callback
+ end
+
+ # Initialize a new Subversion repository on disk.
+ #
+ # This method can work either locally (file://) or on another machine
+ # in the server cluster (svn+ssh://).
+ def svnadmin_create
+ return if exist?
+ if hostname
+ svnadmin_create_remote
+ else
+ svnadmin_create_local
+ end
+ end
+
+ # The local template for the Subversion hook
+ def pre_revprop_change_template
+ File.join(File.dirname(__FILE__), 'pre-revprop-change')
+ end
+
+ # The destination location for the Subversion hook
+ def pre_revprop_change_path
+ File.join(path, 'hooks', 'pre-revprop-change')
+ end
+
+ def svnadmin_create_local
+ FileUtils.mkdir_p path
+ FileUtils.rmdir path
+ run "svnadmin create #{path}"
+ FileUtils.cp pre_revprop_change_template, pre_revprop_change_path
+ end
+
+ def svnadmin_create_remote
+ run "ssh #{hostname} 'mkdir -p #{path} && rmdir #{path} && svnadmin create #{path}'"
+ run "scp #{pre_revprop_change_template} #{hostname}:#{pre_revprop_change_path}"
+ end
+
+ def svnsync_init(from)
+ run "svnsync init #{from.opt_auth} '#{url}' #{from.root}"
+ end
+
+ def self.svnsync_sync(src, dest)
+ # We might not be pulling from the same repository we pulled from last time.
+ # We use svnsync to manage multiple backups on our server cluster, as well as to
+ # pull from the well-known public repository.
+ # Therefore we have to set the root and UUID of the svnsync every time.
+ 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)}'"
+ end
+
+ def propget(propname)
+ run("svn propget #{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)}'")
+ end
+
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class SvnAdapter < AbstractAdapter
+
+ def push(to)
+ logger.warn { "Pushing #{to.url}" }
+
+ unless to.exist?
+ to.svnadmin_create
+ to.svnsync_init(self)
+ end
+ SvnAdapter.svnsync_sync(self, to)
+ end
+
+ end
+end
--- /dev/null
+module Scm::Adapters
+ class SvnAdapter < AbstractAdapter
+ def self.url_regex
+ /^(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_\-\.\/\+%^~]*)?$/
+ end
+
+ def normalize
+ super
+ @url = path_to_file_url(@url)
+ @url = force_https_if_sourceforge(@url)
+ self
+ end
+
+ # If the URL is a simple directory path, make sure it is prefixed by file://
+ def path_to_file_url(path)
+ 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
+ end
+
+ def validate_server_connection
+ return unless valid?
+ begin
+ if max_revision.nil?
+ @errors << [:failed, "The server did not respond to a 'svn info' command. Is the URL correct?"]
+ elsif !self.url.starts_with?(root)
+ @errors << [:failed, "The URL did not match the Subversion root #{root}. Is the URL correct?"]
+ elsif ls.nil?
+ @errors << [:failed, "The server did not respond to a 'svn ls' command. Is the URL correct?"]
+ end
+ rescue
+ @errors << [:failed, "An error occured connecting to the server. Check the URL, username, and password."]
+ end
+ end
+
+ # 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
+ end
+
+ def guess_forge
+ u = @url =~ /:\/\/(.*\.?svn\.)?([^\/^:]+)(:\d+)?\// ? $2 : nil
+ case u
+ when /(googlecode\.com$)/, /(tigris\.org$)/, /(sunsource\.net$)/, /(java\.net$)/,
+ /(openoffice\.org$)/, /(netbeans\.org$)/, /(dev2dev\.bea\.com$)/
+ $1
+ else
+ u
+ end
+ end
+ end
+end
--- /dev/null
+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/misc'
--- /dev/null
+module Scm
+ # A commit is a collection of diffs united by a single timestamp, author, and
+ # message.
+ #
+ # Ohloh's internal data model assumes that commits are immutable, and exist
+ # in a singly-linked list. That is, commits can be nicely numbered a la
+ # Subversion, and new commits are always added to the end of the list.
+ #
+ # This works for CVS and Subversion, but unfortunately, does not at all map
+ # to the DAG used by Git, which allows a commit to have multiple parents and
+ # children, and which allows new commits to appear during a pull which have
+ # timestamps older than previously known commits.
+ #
+ # This means that Ohloh's support for systems like Git is crude at best. For
+ # the near future, it is the job of the adapter to make the Git commit chain
+ # appear as much like a single array as possible.
+ #
+ class Commit
+ # This object supports the idea of distinct authors and committers, a la
+ # Git. However, Ohloh will retain only one of them in its database. It
+ # prefers author, but will fall back to committer if no author is given.
+ attr_accessor :author_name, :author_email, :author_date, :committer_name, :committer_email, :committer_date
+
+ attr_accessor :message
+
+ attr_accessor :diffs
+
+ # The token is used to uniquely identify a commit, and can be any type of
+ # adapter-specific data.
+ #
+ # For Subversion, the token is the revision number.
+ # For Git, the token is the commit SHA1 hash.
+ # For CVS, which does not support atomic commits with unique IDs, we use
+ # the approximate timestamp of the change.
+ attr_accessor :token
+
+ # 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.
+ attr_accessor :directories
+
+ def initialize(params={})
+ params.each { |k,v| send(k.to_s + '=', v) if respond_to?(k.to_s + '=') }
+ end
+ end
+end
--- /dev/null
+module Scm
+ # 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
+ # console tool 'diff'. This object doesn't have anything to do the actual contents
+ # of the file; it's better to think of this object as representing a single line
+ # item from a source control log.
+ class Diff
+ # The filename of the changed file, relative to the root of the repository.
+ attr_accessor :path
+
+ # An action code describing the type of change made to the file.
+ # Action codes are copied directly from the Git standard.
+ # The action code can be...
+ # 'A' added
+ # '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
+
+ def initialize(params={})
+ params.each { |k,v| send(k.to_s + '=', v) if respond_to?(k.to_s + '=') }
+ end
+
+ # eql?() and hash() are implemented so that [].uniq() will work on an array of Diffs.
+ def eql?(a)
+ @action.eql?(a.action) && @path.eql?(a.path) && @sha1.eql?(a.sha1) && @parent_sha1.eql?(a.parent_sha1)
+ end
+
+ def hash
+ "#{action}|#{path}|#{sha1}|#{parent_sha1}".hash
+ end
+ end
+end
--- /dev/null
+module Scm::Parsers
+ class ArrayWriter
+
+ attr_accessor :buffer
+ def initialize(buffer=[])
+ @buffer = buffer
+ end
+
+ def write_preamble(opts = {})
+ end
+
+ def write_commit(commit)
+ @buffer << commit
+ end
+
+ def write_postamble
+ end
+ end
+end
--- /dev/null
+module Scm::Parsers
+ class BranchNumber
+ def initialize(s)
+ @a = s.split('.').collect { |i| i.to_i }
+ # Accomodate CVS magic branch numbers by swapping the magic zero
+ # That is, 1.1.0.2 => 1.1.2.0
+ if @a.size > 2 and @a[-2]==0
+ @a[-1],@a[-2] = @a[-2],@a[-1]
+ end
+ end
+
+ def to_s
+ @a.join('.')
+ end
+
+ def to_a
+ @a
+ end
+
+ # Returns true if <branch_number> is an ancestor of this object.
+ # Also returns true if <branch_number> is the same as this object.
+ def inherits_from?(branch_number)
+ b = branch_number.to_a
+
+ return false if b.size > @a.size
+
+ if b.size == 2
+ return false if b[0] > @a[0]
+ return false if b[0] == @a[0] and b[1] > @a[1]
+ else
+ 0.upto(b.size-2) do |i|
+ return false if b[i] != @a[i]
+ end
+ return false if b[-1] > @a[b.size-1]
+ end
+
+ true
+ end
+
+ # Returns true if <branch_number> is an ancestor of this object,
+ # or if this object follows <branch_number> on the same line.
+ def on_same_line?(branch_number)
+ b = branch_number.to_a
+
+ if b.size > @a.size
+ # b has been branched more times than this object.
+ return false
+ elsif b.size == @a.size
+ # b and a have the same number of branch events.
+ # If either one inherits from the other then they
+ # are on the same line.
+ return (inherits_from?(branch_number) or branch_number.inherits_from?(self))
+ elsif b.size < @a.size
+ # b has not been branched as often as this object.
+ # That's OK if b is an ancestor of this object.
+ return inherits_from?(branch_number)
+ end
+ end
+ end
+end
--- /dev/null
+module Scm::Parsers
+ class CvsParser < Parser
+
+ def self.scm
+ 'cvs'
+ end
+
+ # Given an IO to a CVS rlog, returns a list of
+ # commits (developer/date/message).
+ # If a branch_name is specified, only commits along that branch will be returned,
+ # otherwise only commits along the head will be returned.
+ def self.internal_parse(io, opts)
+ commits = {}
+ branch_name = opts[:branch_name]
+ branch_name = nil if branch_name == 'HEAD' or branch_name == ''
+
+ read_files(io, branch_name) do |c|
+ # As commits are yielded by the parser, we sort them into bins.
+ #
+ # The 'bins' are arrays of timestamps. We keep a separate array of
+ # timestamps for each developer/message combination.
+ #
+ # If a commit lies near in time to another commit with the same
+ # developer/message combination, then we merge them and store only
+ # the later of the two timestamps.
+ #
+ # Typically, we end up with only a single timestamp for each developer/message
+ # combination. However, if a developer repeatedly uses the same message
+ # a number of separate times, we may end up with several timestamps for
+ # that combination.
+
+ key = c.committer_name + ":" + c.message
+ if commits.has_key? key
+ # We have already seen this developer/message combination
+ match = false
+ commits[key].each_index do |i|
+ # Does the new commit lie near in time to a known one in our list?
+ if near?(commits[key][i].committer_date, c.committer_date)
+ match = true
+ # Yes. Choose the most recent timestamp, and add the new
+ # directory name to our list.
+ if commits[key][i].committer_date < c.committer_date
+ commits[key][i].committer_date = c.committer_date
+ commits[key][i].token = c.token
+ end
+ commits[key][i].directories << c.directories[0] unless commits[key][i].directories.include? c.directories[0]
+ break
+ end
+ end
+ # This commit lies a long time away from any one we know.
+ # Add it to the list as a new checkin event.
+ commits[key] << c unless match
+ else
+ # We have never seen this developer/message combination. Start a new list.
+ commits[key] = [c]
+ end
+ end
+ # Pull all of the commits out of the hash and return them as a single sorted list.
+ result = commits.values.flatten.sort! { |a,b| a.committer_date <=> b.committer_date }
+
+ # If we have two commits with identical timestamps, arbitrarily choose the first
+ (result.size-1).downto(1) do |i|
+ result.delete_at(i) if result[i].committer_date == result[i-1].committer_date
+ end
+
+ if block_given?
+ result.each { |r| yield r }
+ end
+ end
+
+ # Accepts two dates and determines wether they are close enough together to consider simultaneous.
+ def self.near?(a,b)
+ ((a-b).abs < 30*60) # Less than 30 minutes counts as 'near'
+ end
+
+ def self.read_files(io, branch_name, &block)
+ io.each_line do |l|
+ if l =~ /^RCS file: (.*),.$/
+ filename = $1
+ read_file(io, branch_name, filename, &block)
+ end
+ end
+ end
+
+ def self.read_file(io, branch_name, filename, &block)
+ branch_number = nil
+ io.each_line do |l|
+ if l =~ /^head: ([\d\.]+)/
+ unless branch_name
+ branch_number = BranchNumber.new($1)
+ end
+ elsif l =~ /^symbolic names:/
+ if branch_name
+ branch_number = read_symbolic_names(io, branch_name)
+ end
+ elsif l =~ /^----------------------------/
+ read_commits(io, branch_number, filename, &block)
+ return
+ end
+ end
+ end
+
+ def self.read_symbolic_names(io, branch_name)
+ branch_number = nil
+ io.each_line do |l|
+ if l =~ /^\s+([^:]+): ([\d\.]+)/
+ branch_number = BranchNumber.new($2) if $1 == branch_name
+ else
+ return branch_number
+ end
+ end
+ end
+
+ def self.read_commits(io, branch_number, filename, &block)
+ should_yield = nil
+ io.each_line do |l|
+ if l =~ /^\s$/
+ return
+ elsif l =~ /^revision ([\d.]+)/
+ commit_number = $1
+ if branch_number
+ should_yield = branch_number.on_same_line?(BranchNumber.new(commit_number))
+ else
+ should_yield = false
+ end
+ read_commit(io, filename, commit_number, should_yield, &block)
+ end
+ end
+ end
+
+ def self.read_commit(io, filename, commit_number, should_yield)
+ io.each_line do |l|
+ if l =~ /^date: (.*); author: ([^;]+); state: (\w+);/
+ committer_date = $1
+ committer_name = $2
+ state = $3
+ # CVS creates a "phantom" dead file at 1.1 on the head if a file
+ # is created on a branch. Ignore this file.
+ should_yield = false if commit_number == '1.1' and state == 'dead'
+ message = read_message(io)
+ if should_yield
+ commit = Scm::Commit.new
+ commit.token = committer_date[0..18]
+ commit.committer_date = Time.parse(committer_date[0..18] + ' +0000').utc
+ commit.committer_name = committer_name
+ commit.message = message
+ commit.directories = [File.dirname(filename).intern]
+ yield commit
+ end
+ return
+ end
+ end
+ end
+
+ def self.read_message(io)
+ message = ''
+ first_line = true
+ io.each_line do |l|
+ if l =~ /^branches: / and first_line # the first line might be 'branches:', skip it.
+ # do nothing
+ else
+ l.chomp!
+ if l == '=============================================================================' or
+ l == '----------------------------'
+ return message
+ end
+ message += "\n" if message.length != 0
+ message += l
+ end
+ first_line = false
+ end
+ message
+ end
+ end
+
+ if $0 == __FILE__
+ require File.dirname(__FILE__) + '/../../config/environment'
+ LogParser.parse(STDIN).each do |r|
+ r.pretty_print(STDOUT)
+ end
+ end
+end
--- /dev/null
+module Scm::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.
+
+ attr_accessor :buffer
+ def initialize(buffer='')
+ @buffer = buffer
+ end
+
+ def write_preamble(opts = {})
+ end
+
+ def write_commit(commit)
+ @buffer << "-" * 72 + "\n"
+
+ @buffer << commit.token.to_s
+ @buffer << ' | Committer: '
+ @buffer << commit.committer_name.to_s.ljust(24)
+ @buffer << ' | '
+ @buffer << commit.committer_date.to_s
+ @buffer << "\n"
+
+ if commit.author_name
+ @buffer << ' ' * commit.token.to_s.length
+ @buffer << ' | Author: '
+ @buffer << commit.author_name.to_s.ljust(24)
+ @buffer << ' | '
+ @buffer << commit.author_date.to_s
+ @buffer << "\n"
+ end
+
+ if commit.diffs && commit.diffs.any?
+ commit.diffs.each { |diff| write_diff(diff) }
+ end
+
+ if commit.directories && commit.directories.any?
+ commit.directories.each do |d|
+ @buffer << "\t#{d}\n"
+ end
+ end
+
+ if commit.message
+ @buffer << "\n#{commit.message}\n"
+ end
+ end
+
+ def write_diff(diff)
+ @buffer << "\t#{diff.action} #{diff.path}\n"
+ end
+
+ def write_postamble
+ end
+ end
+end
--- /dev/null
+require 'stringio'
+
+module Scm::Parsers
+ class Parser
+ def self.parse(buffer='', opts={})
+ buffer = StringIO.new(buffer) if buffer.is_a? String
+ opts = opts.merge(:scm => self.scm)
+
+ writer = (opts[:writer] || ArrayWriter.new) unless block_given?
+ writer.write_preamble(opts) if writer
+
+ internal_parse(buffer, opts) do |commit|
+ if commit
+ yield commit if block_given?
+ writer.write_commit(commit) if writer
+ end
+ end
+
+ if writer
+ writer.write_postamble
+ writer.buffer
+ else
+ nil
+ end
+ end
+
+ def self.internal_parse
+ end
+
+ def self.scm
+ nil
+ end
+ end
+end
--- /dev/null
+require 'parsedate'
+
+module Scm::Parsers
+ class SvnParser < Parser
+ def self.scm
+ 'svn'
+ end
+
+ def self.internal_parse(buffer, opts)
+ e = nil
+ state = :data
+
+ buffer.each_line do |l|
+ l.chomp!
+ next_state = state
+ if state == :data
+ if l =~ /^r(\d+) \| (.*) \| (\d+-\d+-\d+ .*) \(.*\) \| .*/
+ e = Scm::Commit.new
+ e.token = $1.to_i
+ e.committer_name = $2
+ e.committer_date = Time.local(*ParseDate.parsedate($3)).utc
+ elsif l == "Changed paths:"
+ next_state = :diffs
+ elsif l.empty?
+ next_state = :comment
+ end
+
+ elsif state == :diffs
+ if l =~ /^ (\w) ([^\(\)]+)( \(from .+:\d+\))?$/
+ e.diffs ||= []
+ e.diffs << Scm::Diff.new(:action => $1, :path => $2)
+ else
+ next_state = :comment
+ end
+
+ # The :log_embedded_within_comment state is special-case code to fix the Wireshark project, which
+ # includes fragments of svn logs within its comment blocks, which really confuses the parser.
+ # I am not sure whether only Wireshark does this, but I suspect it happens because there is a tool
+ # out there somethere to generate these embedded log comments.
+ elsif state == :log_embedded_within_comment
+ e.message << "\n"
+ e.message << l
+ next_state = :comment if l =~ /============================ .* log end =+/
+
+ elsif state == :comment
+ if l =~ /------------------------------------------------------------------------/
+ yield e if block_given?
+ e = nil
+ next_state = :data
+ elsif l =~ /============================ .* log start =+/
+ e.message << "\n"
+ e.message << l
+ next_state = :log_embedded_within_comment
+ else
+ if e.message
+ e.message << "\n"
+ e.message << l
+ else
+ e.message = l
+ end
+ end
+ end
+ state = next_state
+ end
+ yield e if block_given?
+ end
+ end
+end
--- /dev/null
+require 'parsedate'
+require 'rexml/document'
+require 'rexml/streamlistener'
+
+module Scm::Parsers
+ class SubversionListener
+ include REXML::StreamListener
+
+ attr_accessor :callback
+ def initialize(callback)
+ @callback = callback
+ end
+
+ attr_accessor :text, :commit, :diff
+
+ def tag_start(name, attrs)
+ case name
+ when 'logentry'
+ @commit = Scm::Commit.new
+ @commit.token = attrs['revision'].to_i
+ when 'path'
+ @diff = Scm::Diff.new(:action => attrs['action'])
+ end
+ end
+
+ def tag_end(name)
+ case name
+ when 'logentry'
+ @callback.call(@commit)
+ when 'author'
+ @commit.committer_name = @text
+ when 'date'
+ @commit.committer_date = Time.utc(*ParseDate.parsedate(@text))
+ when 'path'
+ @diff.path = @text
+ @commit.diffs ||= []
+ @commit.diffs << @diff
+ when 'msg'
+ @commit.message = @text
+ end
+ end
+
+ def text(text)
+ @text = text
+ end
+ end
+
+ class SvnXmlParser < Parser
+ def self.internal_parse(buffer, opts)
+ begin
+ REXML::Document.parse_stream(buffer, SubversionListener.new(Proc.new { |c| yield c if block_given? }))
+ rescue EOFError
+ end
+ end
+
+ def self.scm
+ 'svn'
+ end
+ end
+end
--- /dev/null
+module Scm::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.
+
+ attr_accessor :buffer
+ def initialize(buffer='')
+ @buffer = buffer
+ end
+
+ def write_preamble(opts = {})
+ @buffer << "<?xml version=\"1.0\"?>\n"
+ @buffer << "<ohloh_log"
+ opts.each_key do |key|
+ next if key.to_s == 'writer'
+ @buffer << " #{key}=\"#{opts[key]}\""
+ end
+ @buffer << ">\n"
+ end
+
+ def write_commit(commit)
+ @buffer << " <commit token=\"#{commit.token}\">\n"
+
+ if commit.author_name
+ @buffer << " <author name=\"#{commit.author_name}\" date=\"#{xml_time(commit.author_date)}\" />\n"
+ end
+
+ if commit.committer_name
+ @buffer << " <committer name=\"#{commit.committer_name}\" date=\"#{xml_time(commit.committer_date)}\" />\n"
+ end
+
+ if commit.message
+ @buffer << " <message>#{commit.message}</message>\n"
+ end
+
+ if commit.diffs && commit.diffs.any?
+ @buffer << " <diffs>\n"
+ commit.diffs.each { |diff| write_diff(diff) }
+ @buffer << " </diffs>\n"
+ end
+
+ @buffer << " </commit>\n"
+ end
+
+ def write_diff(diff)
+ @buffer << " <diff action=\"#{diff.action}\" path=\"#{diff.path}\" />\n"
+ end
+
+ def write_postamble
+ @buffer << "</ohloh_log>\n"
+ end
+
+ def xml_time(time)
+ case time
+ when Time
+ time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
+ when String
+ time
+ end
+ end
+ end
+end
--- /dev/null
+require 'fileutils'
+
+# A utility class to manage the creation and automatic cleanup of temporary directories.
+module Scm
+ class ScratchDir
+ attr_reader :path
+
+ # Creates a uniquely named directory in the system tmp directory.
+ #
+ # If a block is passed to the constructor, the path to the created directory
+ # will be yielded to the block. The directory will then be deleted
+ # when this block returns.
+ #
+ # Sample usage:
+ #
+ # ScratchDir.new do |path|
+ # # Do some work in the new directory
+ # File.new( path + '/foobaz', 'w' ) do
+ # # ...
+ # end
+ # end # Scratch directory is deleted here
+ #
+ def initialize
+ @path = `mktemp -d /tmp/ohloh_scm_XXXXXX`.strip
+ if block_given?
+ begin
+ return yield(@path)
+ ensure
+ FileUtils.rm_rf(@path)
+ end
+ end
+ end
+ end
+
+ if $0 == __FILE__
+ path = nil
+
+ ScratchDir.new do |d|
+ path = d
+ filename = File.join(d,"test")
+ File.open(filename, "w") do |io|
+ io.write "test"
+ end
+ end
+ raise RuntimeError.new("Directory wasn't cleaned up") if FileTest.directory?(path)
+
+ begin
+ ScratchDir.new do |d|
+ path = d
+ STDOUT.puts "Created scratch direcory #{d}"
+ raise RuntimeError.new("This error should not prevent cleanup")
+ end
+ rescue
+ end
+ raise RuntimeError.new("Directory wasn't cleaned up") if FileTest.directory?(path)
+
+ STDOUT.puts "Tests passed."
+ end
+end
--- /dev/null
+# 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
--- /dev/null
+<?xml version="1.0"?>
+<ohloh_log scm="cvs">
+ <commit token="2005/07/25 17:09:59">
+ <committer name="pizzandre" date="2005-07-25T17:09:59Z" />
+ <message>*** empty log message ***</message>
+ </commit>
+ <commit token="2005/07/25 17:11:06">
+ <committer name="pizzandre" date="2005-07-25T17:11:06Z" />
+ <message>Addin UNL file with using example-</message>
+ </commit>
+</ohloh_log>
--- /dev/null
+
+RCS file: /shared/data/ccvs/repository/intelliglue/UML/intelliglue.zuml,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 17:11:06; author: pizzandre; state: Exp;
+Addin UNL file with using example-
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/articles/IoC_Regras.pdf,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 17:09:59; author: pizzandre; state: Exp;
+*** empty log message ***
+=============================================================================
--- /dev/null
+
+RCS file: /Users/robin/cvs_repo//simple/new_file.rb,v
+head: 1.2
+branch:
+locks: strict
+access list:
+symbolic names:
+ another_branch: 1.2.0.2
+ my_branch: 1.1.0.2
+keyword substitution: kv
+total revisions: 5; selected revisions: 5
+description:
+----------------------------
+revision 1.2
+date: 2006/06/29 18:14:47; author: robin; state: Exp; lines: +1 -0; kopt: kv; commitid: c3944a41896430e; mergepoint: 1.1.2.1; filename: new_file.rb;
+merged new_file.rb from branch onto the HEAD
+----------------------------
+revision 1.1
+date: 2006/06/29 18:05:27; author: robin; state: dead; kopt: kv; commitid: bd144a41667e765; filename: new_file.rb;
+branches: 1.1.2;
+file new_file.rb was initially added on branch my_branch.
+----------------------------
+revision 1.1.2.3
+date: 2006/06/29 18:40:38; author: robin; state: dead; lines: +0 -0; kopt: kv; commitid: d4e44a41ea6477e; filename: new_file.rb;
+removed new_file.rb from the branch only
+----------------------------
+revision 1.1.2.2
+date: 2006/06/29 18:17:49; author: robin; state: Exp; lines: +1 -0; kopt: kv; commitid: c7744a4194cefc8; filename: new_file.rb;
+modifed new_file.rb on the branch only
+----------------------------
+revision 1.1.2.1
+date: 2006/06/29 18:05:27; author: robin; state: Exp; lines: +1 -0; kopt: kv; commitid: bd144a41667e765; filename: new_file.rb;
+added new_file.rb on the branch
+=============================================================================
--- /dev/null
+__BEGIN_COMMIT__
+Commit: 089c527c61235bd0793c49109b5bd34d439848c6
+Author: robin
+Date: Sun, 11 Jun 2006 11:28:00 +0000
+__BEGIN_COMMENT__
+Initial Checkin
+<unknown>
+__END_COMMENT__
+:000000 100644 0000000000000000000000000000000000000000 482d295e4e4f85cdde2e7d8ae7d8ce257192b9e8 A .gitignore
+:000000 100644 0000000000000000000000000000000000000000 4c734ad53b272c9b3d719f214372ac497ff6c068 A helloworld.c
+:000000 100644 0000000000000000000000000000000000000000 56a6051ca2b02b04ef92d5150c9ef600403cb1de A ohloh_token
+__BEGIN_COMMIT__
+Commit: b6e9220c3cabe53a4ed7f32952aeaeb8a822603d
+Author: robin
+Date: Sun, 11 Jun 2006 11:32:13 -0700
+__BEGIN_COMMENT__
+added makefile
+<unknown>
+__END_COMMENT__
+:000000 100644 0000000000000000000000000000000000000000 af2dfd5070b01a19b672861e595de98c101c49cc A makefile
+:100644 100644 56a6051ca2b02b04ef92d5150c9ef600403cb1de d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 M ohloh_token
+__BEGIN_COMMIT__
+Commit: 2e9366dd7a786fdb35f211fff1c8ea05c51968b1
+Author: robin
+Date: Sun, 11 Jun 2006 11:34:17 +0200
+__BEGIN_COMMENT__
+added some documentation and licensing info
+<unknown>
+__END_COMMENT__
+:000000 100644 0000000000000000000000000000000000000000 f0547ce063095e66be74618bc410989df226d2d2 A README
+:100644 100644 4c734ad53b272c9b3d719f214372ac497ff6c068 f6adcae4447809b651c787c078d255b2b4e963c5 M helloworld.c
+:100644 100644 d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 e440e5c842586965a7fb77deda2eca68612b1f53 M ohloh_token
--- /dev/null
+
+RCS file: /shared/data/ccvs/repository/intelliglue/UML/intelliglue.zuml,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 17:11:06; author: pizzandre; state: Exp;
+Addin UNL file with using example-
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/articles/IoC_Regras.pdf,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 17:09:59; author: pizzandre; state: Exp;
+*** empty log message ***
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/etc/Configuration.txt,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:29:27; author: pizzandre; state: Exp;
+Adding Configuration.txt-
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/antlr-2.7.2.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:21; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-base-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:21; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-core-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:22; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-examples-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:22; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-groovy-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:22; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-io-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:22; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-java-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:22; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-jsr94-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:22; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-python-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:23; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-smf-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:23; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/drools-smftest-2.0-beta-17.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:23; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/lib/drools/janino-2.0.5.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:14:23; author: pizzandre; state: Exp;
+Adding drools dependencies to rule engine
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/components/sequence/common/Sequence.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:09; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/exceptions/PluginInstanciationException.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:11; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/execution/Principal.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:13; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/plugin/factory/PluginFactory.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:14; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/plugin/factory/PluginFactorySetup.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:14; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/plugin/factory/rules/integration/IntegrationRules.java.drl,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:16; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/plugin/factory/rules/integration/ruleset/sequence.java.drl,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:17; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/intelliglue_eclipse/src/plugin/handler/ComponentHandler.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/25 20:36:18; author: pizzandre; state: Exp;
+Issue number: 1
+Obtained from: intelligent plugin
+Submitted by: andre piza
+Reviewed by: andre piza
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/index.html,v
+head: 1.9
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 9; selected revisions: 9
+description:
+----------------------------
+revision 1.9
+date: 2005/07/26 20:39:16; author: pizzandre; state: Exp; lines: +2 -2
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+Completing and fixing milestones texts
+----------------------------
+revision 1.8
+date: 2005/07/26 20:35:13; author: pizzandre; state: Exp; lines: +2 -2
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+Adding current milestones-
+----------------------------
+revision 1.7
+date: 2005/07/26 20:30:41; author: pizzandre; state: Exp; lines: +1 -1
+Issue number:pizzandre
+Obtained from:pizzandre
+Submitted by:pizzandre
+Reviewed by:pizzandre
+Adding link to release 1.0
+----------------------------
+revision 1.6
+date: 2005/07/26 20:16:13; author: pizzandre; state: Exp; lines: +1 -1
+Issue number: 1
+Obtained from: pizzandre
+Submitted by: pizzandre
+Reviewed by: pizzandre
+
+Adding milestone 1 info
+----------------------------
+revision 1.5
+date: 2005/07/15 17:16:15; author: pizzandre; state: Exp; lines: +7 -26
+*** empty log message ***
+----------------------------
+revision 1.4
+date: 2005/07/15 16:46:35; author: pizzandre; state: Exp; lines: +4 -4
+*** empty log message ***
+----------------------------
+revision 1.3
+date: 2005/07/15 16:40:17; author: pizzandre; state: Exp; lines: +1 -282
+*** empty log message ***
+----------------------------
+revision 1.2
+date: 2005/07/15 16:33:54; author: pizzandre; state: Exp; lines: +349 -41
+*** empty log message ***
+----------------------------
+revision 1.1
+date: 2005/07/15 11:53:30; author: httpd; state: Exp;
+Initial data for the intelliglue project
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/Attic/sequence.zip,v
+head: 1.2
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1.2
+date: 2006/02/05 23:10:33; author: pizzandre; state: dead; lines: +0 -0
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+----------------------------
+revision 1.1
+date: 2006/01/30 15:53:05; author: pizzandre; state: Exp;
+
+Primeira versao do plugin sequence do netbeans
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/build.xml,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:18; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/manifest.mf,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:19; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/nbproject/build-impl.xml,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:19; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/nbproject/genfiles.properties,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:20; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/nbproject/platform.properties,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:21; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/nbproject/project.xml,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:21; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/nbproject/private/platform-private.properties,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:22; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/nbproject/private/private.xml,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:23; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/action/Bundle.properties,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:23; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/action/ModelGeneratorAction.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:24; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/action/Service.gif,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:25; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/resources/Bundle.properties,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:25; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/resources/layer.xml,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:26; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/SequenceModelGenerator.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:27; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/callgraph/AbstractSequenceModelGraph.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:27; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/callgraph/SequenceModelCallGraph.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:28; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/callgraph/SequenceModelCallGraphFactory.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:28; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/callgraph/SequenceModelJTreeCallGraph.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:29; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/model/SequenceMethod.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:30; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/model/SequenceModelFilter.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:30; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/sequence/ui/SequenceModelUI.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:31; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/plugin/ModelGenerator/src/org/pizzandre/modelgen/util/MethodUtil.java,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2006/02/05 23:10:32; author: pizzandre; state: Exp;
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+CVS: ----------------------------------------------------------------------
+CVS: Issue number:
+CVS: If this change addresses one or more issues,
+CVS: then enter the issue number(s) here.
+CVS: Obtained from:
+CVS: If this change has been taken from another system,
+CVS: then name the system in this line, otherwise delete it.
+CVS: Submitted by:
+CVS: If this code has been contributed to the project by someone else; i.e.,
+CVS: they sent us a patch or a set of diffs, then include their name/email
+CVS: address here. If this is your work then delete this line.
+CVS: Reviewed by:
+CVS: If we are doing pre-commit code reviews and someone else has
+CVS: reviewed your changes, include their name(s) here.
+CVS: If you have not had it reviewed then delete this line.
+=============================================================================
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/release/1.0/intelliglue-1.0.jar,v
+head: 1.1
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: b
+total revisions: 1; selected revisions: 1
+description:
+----------------------------
+revision 1.1
+date: 2005/07/26 20:26:42; author: pizzandre; state: Exp;
+Issue number:1
+Obtained from:pizzandre
+Submitted by:pizzandre
+Reviewed by:pizzandre
+Adding release 1.0 with example but not dependencies
+=============================================================================
--- /dev/null
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/index.html,v
+head: 1.9
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 9; selected revisions: 9
+description:
+----------------------------
+revision 1.9
+date: 2005/07/26 20:39:16; author: pizzandre; state: Exp; lines: +2 -2
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+Completing and fixing milestones texts
+----------------------------
+revision 1.8
+date: 2005/07/26 20:35:13; author: pizzandre; state: Exp; lines: +2 -2
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+Adding current milestones-
+----------------------------
+revision 1.7
+date: 2005/07/26 20:30:41; author: pizzandre; state: Exp; lines: +1 -1
+Issue number:pizzandre
+Obtained from:pizzandre
+Submitted by:pizzandre
+Reviewed by:pizzandre
+Adding link to release 1.0
+----------------------------
+revision 1.6
+date: 2005/07/26 20:16:13; author: pizzandre; state: Exp; lines: +1 -1
+Issue number: 1
+Obtained from: pizzandre
+Submitted by: pizzandre
+Reviewed by: pizzandre
+
+Adding milestone 1 info
+----------------------------
+revision 1.5
+date: 2005/07/15 17:16:15; author: pizzandre; state: Exp; lines: +7 -26
+*** empty log message ***
+----------------------------
+revision 1.4
+date: 2005/07/15 16:46:35; author: pizzandre; state: Exp; lines: +4 -4
+*** empty log message ***
+----------------------------
+revision 1.3
+date: 2005/07/15 16:40:17; author: pizzandre; state: Exp; lines: +1 -282
+*** empty log message ***
+----------------------------
+revision 1.2
+date: 2005/07/15 16:33:54; author: pizzandre; state: Exp; lines: +349 -41
+*** empty log message ***
+----------------------------
+revision 1.1
+date: 2005/07/15 11:53:30; author: httpd; state: Exp;
+Initial data for the intelliglue project
+=============================================================================
--- /dev/null
+
+RCS file: /shared/data/ccvs/repository/intelliglue/www/index.html,v
+head: 1.9
+branch:
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 9; selected revisions: 9
+description:
+----------------------------
+revision 1.9
+date: 2005/07/26 20:39:16; author: pizzandre; state: Exp; lines: +2 -2
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+Completing and fixing milestones texts
+----------------------------
+revision 1.8
+date: 2005/07/26 20:35:13; author: pizzandre; state: Exp; lines: +2 -2
+Issue number:
+Obtained from:
+Submitted by:
+Reviewed by:
+Adding current milestones-
+----------------------------
+revision 1.7
+date: 2005/07/26 20:30:41; author: pizzandre; state: Exp; lines: +1 -1
+Issue number:pizzandre
+Obtained from:pizzandre
+Submitted by:pizzandre
+Reviewed by:pizzandre
+Adding link to release 1.0
+----------------------------
+revision 1.6
+date: 2005/07/26 20:16:13; author: pizzandre; state: Exp; lines: +1 -1
+Issue number: 1
+Obtained from: pizzandre
+Submitted by: pizzandre
+Reviewed by: pizzandre
+
+Adding milestone 1 info
+----------------------------
+revision 1.5
+date: 2005/07/15 17:16:15; author: pizzandre; state: Exp; lines: +7 -26
+*** empty log message ***
+----------------------------
+revision 1.4
+date: 2005/07/15 16:46:35; author: pizzandre; state: Exp; lines: +4 -4
+*** empty log message ***
+----------------------------
+revision 1.3
+date: 2005/07/15 16:40:17; author: pizzandre; state: Exp; lines: +1 -282
+*** empty log message ***
+----------------------------
+revision 1.2
+date: 2005/07/15 16:33:54; author: pizzandre; state: Exp; lines: +349 -41
+*** empty log message ***
+----------------------------
+revision 1.1
+date: 2005/07/15 11:53:30; author: httpd; state: Exp;
+Initial data for the intelliglue project
+=============================================================================
--- /dev/null
+<?xml version="1.0"?>
+<ohloh_log scm="svn">
+ <commit token="5">
+ <committer name="jason" date="2006-07-14T23:07:15Z" />
+ <message>moving COPYING</message>
+ <diffs>
+ <diff action="D" path="/COPYING" />
+ <diff action="A" path="/trunk/COPYING" />
+ </diffs>
+ </commit>
+ <commit token="4">
+ <committer name="jason" date="2006-07-14T22:17:08Z" />
+ <message>added bs COPYING to catch global licenses</message>
+ <diffs>
+ <diff action="A" path="/COPYING" />
+ </diffs>
+ </commit>
+ <commit token="3">
+ <committer name="robin" date="2006-06-11T18:34:17Z" />
+ <message>added some documentation and licensing info</message>
+ <diffs>
+ <diff action="A" path="/trunk/README" />
+ <diff action="M" path="/trunk/helloworld.c" />
+ </diffs>
+ </commit>
+ <commit token="2">
+ <committer name="robin" date="2006-06-11T18:32:13Z" />
+ <message>added makefile</message>
+ <diffs>
+ <diff action="A" path="/trunk/makefile" />
+ </diffs>
+ </commit>
+ <commit token="1">
+ <committer name="robin" date="2006-06-11T18:28:00Z" />
+ <message>Initial Checkin
+</message>
+ <diffs>
+ <diff action="A" path="/branches" />
+ <diff action="A" path="/tags" />
+ <diff action="A" path="/trunk" />
+ <diff action="A" path="/trunk/helloworld.c" />
+ </diffs>
+ </commit>
+</ohloh_log>
--- /dev/null
+------------------------------------------------------------------------
+r5 | jason | 2006-07-14 16:07:15 -0700 (Fri, 14 Jul 2006) | 1 line
+Changed paths:
+ D /COPYING
+ A /trunk/COPYING (from /COPYING:4)
+
+moving COPYING
+------------------------------------------------------------------------
+r4 | jason | 2006-07-14 15:17:08 -0700 (Fri, 14 Jul 2006) | 1 line
+Changed paths:
+ A /COPYING
+
+added bs COPYING to catch global licenses
+------------------------------------------------------------------------
+r3 | robin | 2006-06-11 11:34:17 -0700 (Sun, 11 Jun 2006) | 1 line
+Changed paths:
+ A /trunk/README
+ M /trunk/helloworld.c
+
+added some documentation and licensing info
+------------------------------------------------------------------------
+r2 | robin | 2006-06-11 11:32:13 -0700 (Sun, 11 Jun 2006) | 1 line
+Changed paths:
+ A /trunk/makefile
+
+added makefile
+------------------------------------------------------------------------
+r1 | robin | 2006-06-11 11:28:00 -0700 (Sun, 11 Jun 2006) | 2 lines
+Changed paths:
+ A /branches
+ A /tags
+ A /trunk
+ A /trunk/helloworld.c
+
+Initial Checkin
+
+------------------------------------------------------------------------
--- /dev/null
+<?xml version="1.0"?>
+<log>
+<logentry
+ revision="5">
+<author>jason</author>
+<date>2006-07-14T23:07:15Z</date>
+<paths>
+<path
+ action="D">/COPYING</path>
+<path
+ copyfrom-path="/COPYING"
+ copyfrom-rev="4"
+ action="A">/trunk/COPYING</path>
+</paths>
+<msg>moving COPYING</msg>
+</logentry>
+<logentry
+ revision="4">
+<author>jason</author>
+<date>2006-07-14T22:17:08Z</date>
+<paths>
+<path
+ action="A">/COPYING</path>
+</paths>
+<msg>added bs COPYING to catch global licenses</msg>
+</logentry>
+<logentry
+ revision="3">
+<author>robin</author>
+<date>2006-06-11T18:34:17Z</date>
+<paths>
+<path
+ action="A">/trunk/README</path>
+<path
+ action="M">/trunk/helloworld.c</path>
+</paths>
+<msg>added some documentation and licensing info</msg>
+</logentry>
+<logentry
+ revision="2">
+<author>robin</author>
+<date>2006-06-11T18:32:13Z</date>
+<paths>
+<path
+ action="A">/trunk/makefile</path>
+</paths>
+<msg>added makefile</msg>
+</logentry>
+<logentry
+ revision="1">
+<author>robin</author>
+<date>2006-06-11T18:28:00Z</date>
+<paths>
+<path
+ action="A">/branches</path>
+<path
+ action="A">/tags</path>
+<path
+ action="A">/trunk</path>
+<path
+ action="A">/trunk/helloworld.c</path>
+</paths>
+<msg>Initial Checkin
+</msg>
+</logentry>
+</log>
--- /dev/null
+
+RCS file: /cvsroot/wikipedia/extensions/icpagent/Makefile,v
+head: 1.1
+branch: 1.1.1
+locks: strict
+access list:
+symbolic names:
+ start: 1.1.1.1
+ dammit: 1.1.1
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1.1
+date: 2005/01/02 20:07:30; author: midom; state: Exp;
+branches: 1.1.1;
+Initial revision
+----------------------------
+revision 1.1.1.1
+date: 2005/01/02 20:07:30; author: midom; state: Exp; lines: +0 -0
+ICP agent, for delayed ICP responses on loaded systems...
+=============================================================================
--- /dev/null
+
+RCS file: /cvsroot/wikipedia/extensions/icpagent/Makefile,v
+head: 1.2
+locks: strict
+access list:
+symbolic names:
+keyword substitution: kv
+total revisions: 2; selected revisions: 2
+description:
+----------------------------
+revision 1.1
+date: 2005/01/02 20:07:30; author: midom; state: Exp;
+Initial revision
+----------------------------
+revision 1.2
+date: 2005/01/02 20:07:30; author: foo_author; state: Exp; lines: +0 -0
+I have the same timestamp as my predecessor
+=============================================================================
--- /dev/null
+# 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:
+#
+# [<whitespace>]<filename><whitespace><error message><end-of-line>
+#
+# comment lines begin with '#'
--- /dev/null
+# 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.
--- /dev/null
+# 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".
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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".
--- /dev/null
+# 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
--- /dev/null
+# 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)
+#
--- /dev/null
+# 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.
--- /dev/null
+# *** 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.
--- /dev/null
+# 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"
--- /dev/null
+# 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.
--- /dev/null
+# 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".
--- /dev/null
+# 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".
--- /dev/null
+# 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".
--- /dev/null
+# 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".
--- /dev/null
+# 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.
--- /dev/null
+# The "shadow" file is used to control automatic checkouts.
+#
+# Each line has 3 parts:
+# <module> <tag> <directory>
+#
+# In common with other commit support files, use forward slashes
+# and escape any spaces in filenames.
--- /dev/null
+# 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.
--- /dev/null
+# 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".
--- /dev/null
+# 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.
+#
--- /dev/null
+# 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.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<fileattr>
+ <directory>
+ <owner>robin</owner>
+ <acl user="robin">
+ <all />
+ </acl>
+ <acl user="admin">
+ <all />
+ </acl>
+ <acl>
+ <all deny="1" />
+ </acl>
+ </directory>
+</fileattr>
--- /dev/null
+# 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:
+#
+# [<whitespace>]<filename><whitespace><error message><end-of-line>
+#
+# comment lines begin with '#'
--- /dev/null
+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:
+#
+# [<whitespace>]<filename><whitespace><error message><end-of-line>
+#
+# comment lines begin with '#'
+@
+
--- /dev/null
+# 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.
--- /dev/null
+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.
+@
+
--- /dev/null
+# 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".
--- /dev/null
+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".
+@
+
--- /dev/null
+# 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
--- /dev/null
+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@
+
--- /dev/null
+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
+@@
+
--- /dev/null
+# 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
--- /dev/null
+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
+@
+
--- /dev/null
+# 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".
--- /dev/null
+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".
+@
+
--- /dev/null
+# 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
--- /dev/null
+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
+@
+
--- /dev/null
+# 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)
+#
--- /dev/null
+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)
+#
+@
+
--- /dev/null
+# 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.
--- /dev/null
+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.
+@
+
--- /dev/null
+# *** 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.
--- /dev/null
+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.
+@
+
--- /dev/null
+# 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"
--- /dev/null
+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"
+@
+
--- /dev/null
+# 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.
--- /dev/null
+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.
+@
+
--- /dev/null
+# 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".
--- /dev/null
+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".
+@
+
--- /dev/null
+# 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".
--- /dev/null
+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".
+@
+
--- /dev/null
+# 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".
--- /dev/null
+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".
+@
+
--- /dev/null
+# 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".
--- /dev/null
+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".
+@
+
--- /dev/null
+# 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.
--- /dev/null
+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.
+@
+
--- /dev/null
+# The "shadow" file is used to control automatic checkouts.
+#
+# Each line has 3 parts:
+# <module> <tag> <directory>
+#
+# In common with other commit support files, use forward slashes
+# and escape any spaces in filenames.
--- /dev/null
+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:
+# <module> <tag> <directory>
+#
+# In common with other commit support files, use forward slashes
+# and escape any spaces in filenames.
+@
+
--- /dev/null
+# 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.
--- /dev/null
+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.
+@
+
--- /dev/null
+# 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".
--- /dev/null
+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".
+@
+
--- /dev/null
+# 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.
+#
--- /dev/null
+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.
+#
+@
+
--- /dev/null
+my_branch y
+another_branch y
--- /dev/null
+# 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.
--- /dev/null
+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.
+@
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<fileattr>
+ <directory>
+ <owner>robin</owner>
+ </directory>
+</fileattr>
--- /dev/null
+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
+@@
+
+
--- /dev/null
+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
+@@
--- /dev/null
+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"
+@
+
+
--- /dev/null
+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
+@@
+
+
--- /dev/null
+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.
--- /dev/null
+### 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
--- /dev/null
+### 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
--- /dev/null
+### 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
--- /dev/null
+K 8
+svn:date
+V 27
+2009-01-14T16:05:58.755928Z
+END
--- /dev/null
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2009-01-14T16:07:40.471018Z
+K 7
+svn:log
+V 17
+Initial Revision
+
+END
--- /dev/null
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2009-01-14T16:16:56.442589Z
+K 7
+svn:log
+V 21
+Delete the old trunk
+
+END
--- /dev/null
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2009-01-14T16:17:29.977940Z
+K 7
+svn:log
+V 32
+Replace the trunk with a branch
+
+END
--- /dev/null
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2009-01-14T16:18:25.528386Z
+K 7
+svn:log
+V 23
+Rename a subdirectory.
+
+END
--- /dev/null
+PLAIN
+END
+ENDREP
+id: 0.0.r0/17
+type: dir
+count: 0
+text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e
+cpath: /
+
+
+17 107
--- /dev/null
+DELTA
+SVN\ 1ENDREP
+DELTA
+SVN\ 1ENDREP
+DELTA
+SVN\ 1ENDREP
+id: 7.0.r1/51
+type: file
+count: 0
+text: 1 0 4 0 d41d8cd98f00b204e9800998ecf8427e
+cpath: /trunk/old_file.rb
+copyroot: 0 /
+
+PLAIN
+K 11
+old_file.rb
+V 14
+file 7.0.r1/51
+END
+ENDREP
+id: 6.0.r1/227
+type: dir
+count: 0
+text: 1 173 41 41 63b852a950801663e65e081caea7602d
+cpath: /trunk
+copyroot: 0 /
+
+id: 5.0.r1/341
+type: file
+count: 0
+text: 1 17 4 0 d41d8cd98f00b204e9800998ecf8427e
+cpath: /branches/b/subdir/foo.rb
+copyroot: 0 /
+
+id: 4.0.r1/472
+type: file
+count: 0
+text: 1 34 4 0 d41d8cd98f00b204e9800998ecf8427e
+cpath: /branches/b/subdir/bar.rb
+copyroot: 0 /
+
+PLAIN
+K 6
+bar.rb
+V 15
+file 4.0.r1/472
+K 6
+foo.rb
+V 15
+file 5.0.r1/341
+END
+ENDREP
+id: 3.0.r1/684
+type: dir
+count: 0
+text: 1 603 68 68 6aaba93c23a2dabf6a6f95f54b0a7d5a
+cpath: /branches/b/subdir
+copyroot: 0 /
+
+PLAIN
+K 6
+subdir
+V 14
+dir 3.0.r1/684
+END
+ENDREP
+id: 2.0.r1/858
+type: dir
+count: 0
+text: 1 810 35 35 b12ae5d0f9a9744de382d115f90562e2
+cpath: /branches/b
+copyroot: 0 /
+
+PLAIN
+K 1
+b
+V 14
+dir 2.0.r1/858
+END
+ENDREP
+id: 1.0.r1/1020
+type: dir
+count: 0
+text: 1 977 30 30 447b66a3170c93068d9ab9cf65c63c55
+cpath: /branches
+copyroot: 0 /
+
+PLAIN
+K 8
+branches
+V 15
+dir 1.0.r1/1020
+K 5
+trunk
+V 14
+dir 6.0.r1/227
+END
+ENDREP
+id: 0.0.r1/1219
+type: dir
+pred: 0.0.r0/17
+count: 1
+text: 1 1138 68 68 fd28449b68d85ed021460dd72fa4c392
+cpath: /
+copyroot: 0 /
+
+_6.0.t0-1 add true false /trunk/old_file.rb
+
+_4.0.t0-1 add true false /branches/b/subdir/foo.rb
+
+_5.0.t0-1 add false false /trunk
+
+_3.0.t0-1 add true false /branches/b/subdir/bar.rb
+
+_0.0.t0-1 add false false /branches
+
+_1.0.t0-1 add false false /branches/b
+
+_2.0.t0-1 add false false /branches/b/subdir
+
+
+1219 1346
--- /dev/null
+PLAIN
+K 8
+branches
+V 15
+dir 1.0.r1/1020
+END
+ENDREP
+id: 0.0.r2/51
+type: dir
+pred: 0.0.r1/1219
+count: 2
+text: 2 0 38 38 c733c53690687023e18fd0245000d5bd
+cpath: /
+copyroot: 0 /
+
+6.0.r1/227 delete false false /trunk
+
+
+51 175
--- /dev/null
+id: 2.1.r3/0
+type: dir
+pred: 2.0.r1/858
+count: 1
+text: 1 810 35 35 b12ae5d0f9a9744de382d115f90562e2
+cpath: /trunk
+copyfrom: 2 /branches/b
+
+PLAIN
+END
+ENDREP
+id: 1.0.r3/156
+type: dir
+pred: 1.0.r1/1020
+count: 1
+text: 3 139 4 4 2d2977d1c96f487abe4a1e202dd03b4e
+cpath: /branches
+copyroot: 0 /
+
+PLAIN
+K 8
+branches
+V 14
+dir 1.0.r3/156
+K 5
+trunk
+V 12
+dir 2.1.r3/0
+END
+ENDREP
+id: 0.0.r3/367
+type: dir
+pred: 0.0.r2/51
+count: 3
+text: 3 289 65 65 d5facf43cc213a293424d83b651f6306
+cpath: /
+copyroot: 0 /
+
+2._0.t2-1 add false false /trunk
+2 /branches/b
+2.0.r1/858 delete false false /branches/b
+
+
+367 492
--- /dev/null
+id: 3.2.r4/0
+type: dir
+pred: 3.0.r1/684
+count: 1
+text: 1 603 68 68 6aaba93c23a2dabf6a6f95f54b0a7d5a
+cpath: /trunk/newdir
+copyfrom: 3 /trunk/subdir
+
+PLAIN
+K 6
+newdir
+V 12
+dir 3.2.r4/0
+END
+ENDREP
+id: 2.1.r4/194
+type: dir
+pred: 2.1.r3/0
+count: 2
+text: 4 148 33 33 7cde7a8425af32fbe5f76c5240f43c74
+cpath: /trunk
+copyroot: 3 /trunk
+
+PLAIN
+K 8
+branches
+V 14
+dir 1.0.r3/156
+K 5
+trunk
+V 14
+dir 2.1.r4/194
+END
+ENDREP
+id: 0.0.r4/408
+type: dir
+pred: 0.0.r3/367
+count: 4
+text: 4 328 67 67 3818dc120a6ec64e2453306ab5a827f3
+cpath: /
+copyroot: 0 /
+
+3._0.t3-1 add false false /trunk/newdir
+3 /trunk/subdir
+3.0.r1/684 delete false false /trunk/subdir
+
+
+408 534
--- /dev/null
+8ab099c8-7f08-4752-91f3-1a221f1f3b2b
--- /dev/null
+#!/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"
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
+
+echo "Changing revision properties other than svn:log is prohibited" >&2
+exit 1
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+### 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
--- /dev/null
+### 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
--- /dev/null
+### 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
--- /dev/null
+K 8
+svn:date
+V 27
+2006-06-11T18:24:23.808631Z
+END
--- /dev/null
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2006-06-11T18:28:00.288498Z
+K 7
+svn:log
+V 16
+Initial Checkin
+
+END
--- /dev/null
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2006-06-11T18:32:13.933350Z
+K 7
+svn:log
+V 14
+added makefile
+END
--- /dev/null
+K 10
+svn:author
+V 5
+robin
+K 8
+svn:date
+V 27
+2006-06-11T18:34:17.555480Z
+K 7
+svn:log
+V 43
+added some documentation and licensing info
+END
--- /dev/null
+K 10
+svn:author
+V 5
+jason
+K 8
+svn:date
+V 27
+2006-07-14T22:17:08.940982Z
+K 7
+svn:log
+V 41
+added bs COPYING to catch global licenses
+END
--- /dev/null
+K 10
+svn:author
+V 5
+jason
+K 8
+svn:date
+V 27
+2006-07-14T23:07:15.813350Z
+K 7
+svn:log
+V 14
+moving COPYING
+END
--- /dev/null
+PLAIN
+END
+ENDREP
+id: 0.0.r0/17
+type: dir
+count: 0
+text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e
+cpath: /
+
+
+17 107
--- /dev/null
+id: 7.1.r5/0
+type: file
+pred: 7.0.r4/10502
+count: 1
+text: 4 0 10489 18787 d1504d81b4282ee3ebcf3cdaf7f93238
+cpath: /trunk/COPYING
+copyfrom: 4 /COPYING
+
+PLAIN
+K 7
+COPYING
+V 13
+file 7.1.r5/0
+K 6
+README
+V 15
+file 6.0.r3/400
+K 12
+helloworld.c
+V 15
+file 2.0.r3/256
+K 8
+makefile
+V 14
+file 5.0.r2/59
+END
+ENDREP
+id: 1.0.r5/303
+type: dir
+pred: 1.0.r3/643
+count: 3
+text: 5 151 139 139 f64dae99765c4ad6dd4f923d5c122b03
+cpath: /trunk
+copyroot: 0 /
+
+PLAIN
+K 8
+branches
+V 14
+dir 3.0.r1/385
+K 4
+tags
+V 14
+dir 4.0.r1/451
+K 5
+trunk
+V 14
+dir 1.0.r5/303
+END
+ENDREP
+id: 0.0.r5/545
+type: dir
+pred: 0.0.r4/10769
+count: 5
+text: 5 436 96 96 2d909fa92b9b195db0fd14e3f7732c24
+cpath: /
+copyroot: 0 /
+
+7.0.r4/10502 delete false false /COPYING
+
+7._0.t4-1 add false false /trunk/COPYING
+4 /COPYING
+
+545 673
--- /dev/null
+6a9cefd4-a008-4d2a-a89b-d77e99cd6eb1
--- /dev/null
+#!/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"
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
+
+echo "Changing revision properties other than svn:log is prohibited" >&2
+exit 1
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+require 'test/unit'
+require 'fileutils'
+require 'find'
+
+unless defined?(TEST_DIR)
+ TEST_DIR = File.dirname(__FILE__)
+end
+require TEST_DIR + '/../lib/scm'
+
+Scm::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'))
+end
+
+unless defined?(DATA_DIR)
+ DATA_DIR = File.expand_path(File.join(TEST_DIR, 'data'))
+end
+
+class Scm::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
+ end
+
+ def assert_convert(parser, log, expected)
+ result = ''
+ parser.parse File.new(log), :writer => Scm::Parsers::XmlWriter.new(result)
+ assert_buffers_equal File.read(expected), result
+ end
+
+ # assert_equal just dumps the massive strings to the console, which is not helpful.
+ # Instead we try to indentify the line of the first error.
+ def assert_buffers_equal(expected, actual)
+ return if expected == actual
+
+ expected_lines = expected.split("\n")
+ actual_lines = actual.split("\n")
+
+ expected_lines.each_with_index do |line, i|
+ if line != actual_lines[i]
+ assert_equal line, actual_lines[i], "at line #{i} of the reference buffer"
+ end
+ end
+
+ # We couldnt' find the mismatch. Just bail.
+ assert_equal expected_lines, actual_lines
+ end
+
+ # Expands a tarballed git repository and yields a GitAdapter that points to it.
+ def with_git_repository(name)
+ archive = name + '.tgz'
+ if Dir.entries(REPO_DIR).include?(archive)
+ Scm::ScratchDir.new do |dir|
+ `tar xzf #{File.join(REPO_DIR, archive)} --directory #{dir}`
+ yield Scm::Adapters::GitAdapter.new(:url => File.join(dir, name)).normalize
+ end
+ else
+ raise RuntimeError.new("Repository archive #{File.join(REPO_DIR, archive)} not found.")
+ end
+ end
+
+ def with_svn_repository(name)
+ if Dir.entries(REPO_DIR).include?(name)
+ Scm::ScratchDir.new do |dir|
+ `cp -R #{File.join(REPO_DIR, name)} #{dir}`
+ yield Scm::Adapters::SvnAdapter.new(:url => File.join(dir, name)).normalize
+ end
+ else
+ raise RuntimeError.new("Repository archive #{File.join(REPO_DIR, name)} not found.")
+ end
+ end
+
+ def with_cvs_repository(name)
+ if Dir.entries(REPO_DIR).include?(name)
+ Scm::ScratchDir.new do |dir|
+ `cp -R #{File.join(REPO_DIR, name)} #{dir}`
+ yield Scm::Adapters::CvsAdapter.new(:url => File.join(dir, name)).normalize
+ end
+ else
+ raise RuntimeError.new("Repository archive #{File.join(REPO_DIR, name)} not found.")
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class AbstractAdapterTest < Scm::Test
+ def test_simple_validation
+ scm = AbstractAdapter.new()
+ assert !scm.valid?
+ assert_equal [[:url, "The URL can't be blank."]], scm.errors
+
+ scm.url = "http://www.test.org/test"
+ assert scm.valid?
+ assert scm.errors.empty?
+ end
+
+ def test_valid_urls
+ ['http://www.ohloh.net'].each do |url|
+ assert !AbstractAdapter.new(:url => url).validate_url
+ end
+ end
+
+ def test_invalid_urls
+ [nil, '', '*' * 121].each do |url|
+ assert AbstractAdapter.new(:url => url).validate_url.any?
+ end
+ end
+
+ def test_invalid_usernames
+ ['no spaces allowed', '/', ':', 'a'*33].each do |username|
+ assert AbstractAdapter.new(:username => username).validate_username.any?
+ end
+ end
+
+ def test_valid_usernames
+ [nil,'','joe_36','a'*32].each do |username|
+ assert !AbstractAdapter.new(:username => username).validate_username
+ end
+ end
+
+ def test_invalid_passwords
+ ['no spaces allowed', 'a'*33].each do |password|
+ assert AbstractAdapter.new(:password => password).validate_password.any?
+ end
+ end
+
+ def test_valid_passwords
+ [nil,'','abc','a'*32].each do |password|
+ assert !AbstractAdapter.new(:password => password).validate_password
+ end
+ end
+
+ def test_invalid_branch_names
+ ['%','a'*51].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|
+ assert !AbstractAdapter.new(:branch_name => branch_name).validate_branch_name
+ end
+ end
+
+ def test_normalize
+ scm = AbstractAdapter.new(:url => " http://www.test.org/test ", :username => " joe ", :password => " abc ", :branch_name => " trunk ")
+ scm.normalize
+ assert_equal "http://www.test.org/test", scm.url
+ assert_equal "trunk", scm.branch_name
+ assert_equal "joe", scm.username
+ assert_equal "abc", scm.password
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Parsers
+ class ArrayWriterTest < Scm::Test
+
+ def test_basic
+
+ log = <<-LOG
+------------------------------------------------------------------------
+r3 | robin | 2006-06-11 11:34:17 -0700 (Sun, 11 Jun 2006) | 1 line
+Changed paths:
+ A /trunk/README
+ M /trunk/helloworld.c
+
+added some documentation and licensing info
+------------------------------------------------------------------------
+ LOG
+
+ # By default, the ArrayWriter is used, and an empty string is parsed
+ assert_equal [], SvnParser.parse
+ assert_equal [], SvnParser.parse('')
+ assert_equal [], SvnParser.parse('', :writer => ArrayWriter.new)
+
+ result = SvnParser.parse(log, :writer => ArrayWriter.new)
+ assert_equal 1, result.size
+ assert_equal 'robin', result.first.committer_name
+ assert_equal 3, result.first.token
+ assert_equal 2, result.first.diffs.size
+ assert_equal '/trunk/README', result.first.diffs.first.path
+ assert_equal 'A', result.first.diffs.first.action
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Parsers
+ class CvsBranchNumberTest < Scm::Test
+ def test_basic
+ assert_equal [1,1], BranchNumber.new('1.1').to_a
+ assert_equal [1234,1234], BranchNumber.new('1234.1234').to_a
+ assert_equal [1,2,3,4], BranchNumber.new('1.2.3.4').to_a
+ end
+
+ def test_simple_inherits_from
+ b = BranchNumber.new('1.3')
+
+ assert b.inherits_from?(BranchNumber.new('1.2'))
+ assert b.inherits_from?(BranchNumber.new('1.1'))
+ assert b.inherits_from?(BranchNumber.new('1.3'))
+
+ assert !b.inherits_from?(BranchNumber.new('1.4'))
+ assert !b.inherits_from?(BranchNumber.new('1.1.2.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.2.2.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.2.1'))
+ end
+
+ def test_complex_inherits_from
+ b = BranchNumber.new('1.3.6.3.2.3')
+
+ assert b.inherits_from?(BranchNumber.new('1.2'))
+ assert b.inherits_from?(BranchNumber.new('1.1'))
+ assert b.inherits_from?(BranchNumber.new('1.3'))
+ assert b.inherits_from?(BranchNumber.new('1.3.6.1'))
+ assert b.inherits_from?(BranchNumber.new('1.3.6.2'))
+ assert b.inherits_from?(BranchNumber.new('1.3.6.3'))
+ assert b.inherits_from?(BranchNumber.new('1.3.6.3.2.1'))
+ assert b.inherits_from?(BranchNumber.new('1.3.6.3.2.2'))
+ assert b.inherits_from?(BranchNumber.new('1.3.6.3.2.3'))
+
+ assert !b.inherits_from?(BranchNumber.new('1.4'))
+ assert !b.inherits_from?(BranchNumber.new('1.1.2.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.2.2.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.2.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.4.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.6.1.2.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.6.4'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.6.3.4.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.6.3.2.2.2.1'))
+ assert !b.inherits_from?(BranchNumber.new('1.3.6.3.2.4'))
+ end
+
+ def test_primary_revision_number_change
+ b = BranchNumber.new('2.3')
+
+ assert b.inherits_from?(BranchNumber.new('2.2'))
+ assert b.inherits_from?(BranchNumber.new('2.1'))
+ assert b.inherits_from?(BranchNumber.new('1.1'))
+ assert b.inherits_from?(BranchNumber.new('1.9999'))
+
+ assert !b.inherits_from?(BranchNumber.new('2.4'))
+ assert !b.inherits_from?(BranchNumber.new('3.1'))
+ end
+
+ def test_complex_primary_revision_number_change
+ b = BranchNumber.new('2.3.2.1')
+
+ assert b.inherits_from?(BranchNumber.new('2.3'))
+ assert b.inherits_from?(BranchNumber.new('2.2'))
+ assert b.inherits_from?(BranchNumber.new('1.1'))
+ assert b.inherits_from?(BranchNumber.new('1.9999'))
+
+ 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')
+
+ assert b.on_same_line?(BranchNumber.new('1.2'))
+ assert b.on_same_line?(BranchNumber.new('1.1'))
+ assert b.on_same_line?(BranchNumber.new('1.3'))
+ assert b.on_same_line?(BranchNumber.new('1.4'))
+
+ assert !b.on_same_line?(BranchNumber.new('1.1.2.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.2.2.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.3.2.1'))
+ end
+
+ def test_complex_on_same_line
+ b = BranchNumber.new('1.3.6.3.2.3')
+
+ assert b.on_same_line?(BranchNumber.new('1.1'))
+ assert b.on_same_line?(BranchNumber.new('1.2'))
+ assert b.on_same_line?(BranchNumber.new('1.3'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.1'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.2'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.3'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.3.2.1'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.3.2.2'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.3.2.3'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.3.2.4'))
+ assert b.on_same_line?(BranchNumber.new('1.3.6.3.2.99'))
+
+ assert !b.on_same_line?(BranchNumber.new('1.4'))
+ assert !b.on_same_line?(BranchNumber.new('1.1.2.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.2.2.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.3.2.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.3.4.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.3.6.1.2.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.3.6.4'))
+ assert !b.on_same_line?(BranchNumber.new('1.3.6.3.4.1'))
+ assert !b.on_same_line?(BranchNumber.new('1.3.6.3.2.2.2.1'))
+ 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
+ assert BranchNumber.new('1.1.2.1').on_same_line?(BranchNumber.new('1.1.0.2'))
+ assert BranchNumber.new('1.1.2.1.2.1').on_same_line?(BranchNumber.new('1.1.0.2'))
+
+ assert BranchNumber.new('1.1.0.2').on_same_line?(BranchNumber.new('1.1'))
+ assert !BranchNumber.new('1.1.0.2').on_same_line?(BranchNumber.new('1.2'))
+ assert BranchNumber.new('1.1.0.2').on_same_line?(BranchNumber.new('1.1.2.1'))
+
+ assert BranchNumber.new('1.1.0.4').on_same_line?(BranchNumber.new('1.1'))
+ assert !BranchNumber.new('1.1.0.4').on_same_line?(BranchNumber.new('1.1.2.1'))
+ assert !BranchNumber.new('1.1.0.4').on_same_line?(BranchNumber.new('1.1.0.2'))
+ assert BranchNumber.new('1.1.0.4').on_same_line?(BranchNumber.new('1.1.4.1'))
+ assert !BranchNumber.new('1.1.0.4').on_same_line?(BranchNumber.new('1.1.0.6'))
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class CvsCommitsTest < Scm::Test
+
+ def test_commits
+ with_cvs_repository('cvs') 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 }
+
+ # 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 [], cvs.commits('2006/06/29 18:52:23').collect { |c| c.token }
+ end
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class CvsConvertTest < Scm::Test
+
+ def test_basic_convert
+ with_cvs_repository('cvs') do |src|
+ Scm::ScratchDir.new do |dest_dir|
+ dest = GitAdapter.new(:url => dest_dir).normalize
+ assert !dest.exist?
+
+ dest.pull(src)
+ assert dest.exist?
+
+ dest_commits = dest.commits
+ src.commits.each_with_index do |c, i|
+ # Because CVS does not track authors (only committers),
+ # the CVS committer becomes the Git author.
+ assert_equal c.committer_date, dest_commits[i].author_date
+ assert_equal c.committer_name, dest_commits[i].author_name
+
+ # Depending upon version of Git used, we may or may not have a trailing \n.
+ # We don't really care, so just compare the stripped versions.
+ assert_equal c.message.strip, dest_commits[i].message.strip
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class CvsMiscTest < Scm::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')
+ end
+
+ def test_remote_directory_trim
+ r = CvsAdapter.new(:url => ':pserver:anonymous:@moodle.cvs.sourceforge.net:/cvsroot/moodle', :module_name => "contrib")
+ assert_equal "foo.rb", r.trim_directory('/cvsroot/moodle/contrib/foo.rb')
+ end
+
+ def test_remote_directory_trim_with_port_number
+ r = CvsAdapter.new(:url => ':pserver:anoncvs:anoncvs@libvirt.org:2401/data/cvs', :module_name => "libvirt")
+ assert_equal "docs/html/Attic", r.trim_directory('/data/cvs/libvirt/docs/html/Attic')
+ end
+
+ def test_ordered_directory_list
+ r = CvsAdapter.new(:url => ':pserver:anonymous:@moodle.cvs.sourceforge.net:/cvsroot/moodle', :module_name => "contrib")
+
+ l = r.build_ordered_directory_list(["/cvsroot/moodle/contrib/foo/bar".intern,
+ "/cvsroot/moodle/contrib".intern,
+ "/cvsroot/moodle/contrib/hello".intern,
+ "/cvsroot/moodle/contrib/hello".intern])
+
+ assert_equal 4,l.size
+ assert_equal "", l[0]
+ assert_equal "foo", l[1]
+ assert_equal "hello", l[2]
+ assert_equal "foo/bar", l[3]
+ end
+
+ def test_ordered_directory_list_ignores_Attic
+ r = CvsAdapter.new(:url => ':pserver:anonymous:@moodle.cvs.sourceforge.net:/cvsroot/moodle', :module_name => 'contrib')
+
+ l = r.build_ordered_directory_list(["/cvsroot/moodle/contrib/foo/bar".intern,
+ "/cvsroot/moodle/contrib/Attic".intern,
+ "/cvsroot/moodle/contrib/hello/Attic".intern])
+
+ assert_equal 4,l.size
+ assert_equal "", l[0]
+ assert_equal "foo", l[1]
+ assert_equal "hello", l[2]
+ assert_equal "foo/bar", l[3]
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Parsers
+ class CvsParserTest < Scm::Test
+
+ def test_basic
+ assert_convert(CvsParser, DATA_DIR + '/basic.rlog', DATA_DIR + '/basic.ohlog')
+ end
+
+ def test_empty_array
+ assert_equal([], CvsParser.parse(''))
+ end
+
+ def test_empty_xml
+ assert_equal("<?xml version=\"1.0\"?>\n<ohloh_log scm=\"cvs\">\n</ohloh_log>\n", CvsParser.parse('', :writer => XmlWriter.new))
+ end
+
+ def test_log_parser
+ revisions = CvsParser.parse File.read(DATA_DIR + '/basic.rlog')
+
+ assert_equal 2, revisions.size
+
+ assert_equal '2005/07/25 17:09:59', revisions[0].token
+ assert_equal 'pizzandre', revisions[0].committer_name
+ assert_equal Time.utc(2005,07,25,17,9,59), revisions[0].committer_date
+ assert_equal '*** empty log message ***', revisions[0].message
+
+ assert_equal '2005/07/25 17:11:06', revisions[1].token
+ assert_equal 'pizzandre', revisions[1].committer_name
+ assert_equal Time.utc(2005,07,25,17,11,6), revisions[1].committer_date
+ assert_equal 'Addin UNL file with using example-', revisions[1].message
+ end
+
+ # One file with several revisions
+ def test_multiple_revisions
+ revisions = CvsParser.parse File.read(DATA_DIR + '/multiple_revisions.rlog')
+
+ # There are 9 revisions in the rlog, but some of them are close together with the same message.
+ # Therefore we bin them together into only 7 revisions.
+ assert_equal 7, revisions.size
+
+ assert_equal '2005/07/15 11:53:30', revisions[0].token
+ assert_equal 'httpd', revisions[0].committer_name
+ assert_equal 'Initial data for the intelliglue project', revisions[0].message
+
+ assert_equal '2005/07/15 16:40:17', revisions[1].token
+ assert_equal 'pizzandre', revisions[1].committer_name
+ assert_equal '*** empty log message ***', revisions[1].message
+
+ assert_equal '2005/07/26 20:35:13', revisions[5].token
+ assert_equal 'pizzandre', revisions[5].committer_name
+ assert_equal "Issue number:\nObtained from:\nSubmitted by:\nReviewed by:\nAdding current milestones-", revisions[5].message
+
+ assert_equal '2005/07/26 20:39:16', revisions[6].token
+ assert_equal 'pizzandre', revisions[6].committer_name
+ assert_equal "Issue number:\nObtained from:\nSubmitted by:\nReviewed by:\nCompleting and fixing milestones texts", revisions[6].message
+ end
+
+ # A file is created and modified on the branch, then merged to the trunk, then deleted from the branch.
+ # From the trunk's point of view, we should see only the merge event.
+ def test_file_created_on_branch_as_seen_from_trunk
+ revisions = CvsParser.parse File.read(DATA_DIR + '/file_created_on_branch.rlog'), :branch_name => 'HEAD'
+ assert_equal 1, revisions.size
+ assert_equal 'merged new_file.rb from branch onto the HEAD', revisions[0].message
+ end
+
+ # A file is created and modified on the branch, then merged to the trunk, then deleted from the branch.
+ # From the branch's point of view, we should see the add, modify, and delete only.
+ def test_file_created_on_branch_as_seen_from_branch
+ revisions = CvsParser.parse File.read(DATA_DIR + '/file_created_on_branch.rlog'), :branch_name => 'my_branch'
+ assert_equal 3, revisions.size
+ assert_equal 'added new_file.rb on the branch', revisions[0].message
+ assert_equal 'modifed new_file.rb on the branch only', revisions[1].message
+ assert_equal 'removed new_file.rb from the branch only', revisions[2].message
+ end
+
+ # A file is created on the vender branch. This causes a simultaneous checkin on HEAD
+ # with a different message ('Initial revision') but same committer_name name and timestamp.
+ # We should only pick up one of these checkins.
+ def test_simultaneous_checkins
+ revisions = CvsParser.parse File.read(DATA_DIR + '/simultaneous_checkins.rlog')
+ assert_equal 1, revisions.size
+ assert_equal 'Initial revision', revisions[0].message
+ end
+
+ # Two different authors check in with two different messages at the exact same moment.
+ # How this happens is a mystery, but I have seen it in rlogs.
+ # We arbitrarily choose the first one if so.
+ def test_simultaneous_checkins_2
+ revisions = CvsParser.parse File.read(DATA_DIR + '/simultaneous_checkins_2.rlog')
+ assert_equal 1, revisions.size
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class CvsValidationTest < Scm::Test
+ def test_rejected_urls
+ [ nil, "", "foo", "http:/", "http:://", "http://", "http://a",
+ ":pserver", # that's not enough
+ ":pserver:anonymous", #still not enough
+ ":pserver:anonymous:@ipodder.cvs.sourceforge.net", # missing the path
+ ":pserver:anonymous:::@ipodder.cvs.sourceforge.net:/cvsroot/ipodder", # too many colons
+ ":pserver@ipodder.cvs.sourceforge.net:/cvsroot/ipodder", # not enough colons
+ ":pserver:anonymous:@ipodder.cvs.sourceforge.net/cvsroot/ipodder", # hostname and path not separated by colon
+ ":pserver:anonymous:@ipodder.cvs.source/forge.net:/cvsroot/ipodder", # slash in hostname
+ ":pserver:anonymous:ipodder.cvs.sourceforge.net:/cvsroot/ipodder", # missing @
+ ":pserver:anonymous:@ipodder.cvs.sourceforge.net:cvsroot/ipodder", # path does not begin at root
+ ":pserver:anonymous:@ipodder.cvs.sourceforge.net:/cvsr%23oot/ipodder", # no encoded chars allowed
+ ":pserver:anonymous:@ipodder.cvs.sourceforge.net:/cvsroot/ipodder;asdf", # no ; in url
+ ":pserver:anonymous:@ipodder.cvs.sourceforge.net:/cvsroot/ipodder malicious code", # spaces not allowed
+ "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
+ ].each do |url|
+ # Rejected for both internal and public use
+ [true, false].each do |p|
+ cvs = CvsAdapter.new(:url => url, :public_urls_only => p)
+ assert cvs.validate_url
+ end
+ end
+ end
+
+ def test_accepted_urls
+ [ ":pserver:anonymous:@ipodder.cvs.sourceforge.net:/cvsroot/ipodder",
+ ":pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot",
+ ":pserver:anonymous:@cvs-mirror.mozilla.org:/cvsroot",
+ ":pserver:guest:@cvs.dev.java.net:/shared/data/ccvs/repository",
+ ":pserver:anoncvs:password@anoncvs.postgresql.org:/projects/cvsroot",
+ ":pserver:anonymous:@rubyeclipse.cvs.sourceforge.net:/cvsroot/rubyeclipse",
+ ":pserver:cvs:cvs@cvs.winehq.org:/home/wine",
+ ":pserver:tcpdump:anoncvs@cvs.tcpdump.org:/tcpdump/master",
+ ":pserver:anonymous:@user-mode-linux.cvs.sourceforge.net:/cvsroot/user-mode-linux",
+ ":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
+ ].each do |url|
+ # Valid for both internal and public use
+ [true, false].each do |p|
+ cvs = CvsAdapter.new(:url => url, :public_urls_only => p)
+ assert !cvs.validate_url
+ end
+ end
+ end
+
+ # Local files not accepted for public URLs
+ def test_local_file_url
+ cvs = CvsAdapter.new(:url => "/root")
+ assert !cvs.validate_url
+
+ cvs = CvsAdapter.new(:url => "/root", :public_urls_only => true)
+ assert cvs.validate_url
+ end
+
+ def test_rejected_module_names
+ [nil,"","%",";","&","\n","\t"].each do |x|
+ cvs = CvsAdapter.new(:url => ":pserver:cvs:cvs@cvs.test.org:/test", :module_name => x)
+ assert !cvs.valid?
+ assert cvs.errors.first[0] = :module_name
+ end
+ end
+
+ def test_accepted_module_names
+ ["myproject","my/project","my/project/2.0","my_project","0","My .Net Module", "my-module", "my-module++"].each do |x|
+ cvs = CvsAdapter.new(:url => ":pserver:cvs:cvs@cvs.test.org:/test", :module_name => x)
+ assert cvs.valid?
+ end
+ end
+
+ def test_symlink_fixup
+ cvs = CvsAdapter.new(:url => ":pserver:anoncvs:@cvs.netbeans.org:/cvs")
+ assert_equal ":pserver:anoncvs:@cvs.netbeans.org:/shared/data/ccvs/repository", cvs.normalize.url
+
+ cvs = CvsAdapter.new(:url => ":pserver:anoncvs:@cvs.netbeans.org:/cvs/")
+ assert_equal ":pserver:anoncvs:@cvs.netbeans.org:/shared/data/ccvs/repository", cvs.normalize.url
+
+ cvs = CvsAdapter.new(:url => ":pserver:anoncvs:@cvs.dev.java.net:/cvs")
+ assert_equal ":pserver:anoncvs:@cvs.dev.java.net:/shared/data/ccvs/repository", cvs.normalize.url
+
+ cvs = CvsAdapter.new(:url => ":PSERVER:ANONCVS:@CVS.DEV.JAVA.NET:/cvs")
+ assert_equal ":PSERVER:ANONCVS:@CVS.DEV.JAVA.NET:/shared/data/ccvs/repository", cvs.normalize.url
+
+ cvs = CvsAdapter.new(:url => ":pserver:anonymous:@cvs.gna.org:/cvs/eagleusb")
+ assert_equal ":pserver:anonymous:@cvs.gna.org:/var/cvs/eagleusb", cvs.normalize.url
+ end
+
+ def test_sync_pserver_username_password
+ # Pull username only from url
+ cvs = CvsAdapter.new(:url => ":pserver:guest:@ohloh.net:/test")
+ cvs.normalize
+ assert_equal ':pserver:guest:@ohloh.net:/test', cvs.url
+ assert_equal 'guest', cvs.username
+ assert_equal '', cvs.password
+
+ # Pull username and password from url
+ cvs = CvsAdapter.new(:url => ":pserver:guest:secret@ohloh.net:/test")
+ cvs.normalize
+ assert_equal ':pserver:guest:secret@ohloh.net:/test', cvs.url
+ assert_equal 'guest', cvs.username
+ assert_equal 'secret', cvs.password
+
+ # Apply username and password to url
+ cvs = CvsAdapter.new(:url => ":pserver::@ohloh.net:/test", :username => "guest", :password => "secret")
+ cvs.normalize
+ assert_equal ':pserver:guest:secret@ohloh.net:/test', cvs.url
+ assert_equal 'guest', cvs.username
+ assert_equal 'secret', cvs.password
+
+ # Passwords disagree, use :password attribute
+ cvs = CvsAdapter.new(:url => ":pserver:guest:old@ohloh.net:/test", :username => "guest", :password => "new")
+ cvs.normalize
+ assert_equal ':pserver:guest:new@ohloh.net:/test', cvs.url
+ assert_equal 'guest', cvs.username
+ assert_equal 'new', cvs.password
+ end
+
+ def test_guess_forge
+ cvs = CvsAdapter.new(:url => nil)
+ assert_equal nil, cvs.guess_forge
+
+ cvs = CvsAdapter.new(:url => "garbage_in_garbage_out")
+ assert_equal nil, 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
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitMiscTest < Scm::Test
+
+ def test_cat_file
+ with_git_repository('git') do |git|
+expected = <<-EXPECTED
+/* Hello, World! */
+#include <stdio.h>
+main()
+{
+ printf("Hello, World!\\n");
+}
+EXPECTED
+ assert_equal expected, git.cat_file(nil, Scm::Diff.new(:sha1 => '4c734ad53b272c9b3d719f214372ac497ff6c068'))
+ end
+ end
+
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitCommitAllTest < Scm::Test
+
+ def test_commit_all
+ Scm::ScratchDir.new do |dir|
+ git = GitAdapter.new(:url => dir).normalize
+
+ git.init_db
+ assert !git.anything_to_commit?
+
+ File.open(File.join(dir, 'README'), 'w') {}
+ assert git.anything_to_commit?
+
+ c = Scm::Commit.new
+ c.author_name = "John Q. Developer"
+ c.message = "Initial checkin."
+ git.commit_all(c)
+ assert !git.anything_to_commit?
+
+ assert_equal 1, git.commits.size
+
+ assert_equal c.author_name, git.commits.first.author_name
+ # Depending on version of Git used, we may or may not have trailing \n.
+ # We don't really care, so just compare the stripped versions.
+ assert_equal c.message.strip, git.commits.first.message.strip
+
+ assert_equal ['.gitignore', 'README'], git.commits.first.diffs.collect { |d| d.path }.sort
+ end
+ end
+
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitCommitsTest < Scm::Test
+
+ def test_commit
+ 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 ['089c527c61235bd0793c49109b5bd34d439848c6',
+ 'b6e9220c3cabe53a4ed7f32952aeaeb8a822603d',
+ '2e9366dd7a786fdb35f211fff1c8ea05c51968b1',
+ '1df547800dcd168e589bb9b26b4039bff3a7f7e4'], git.commit_tokens
+
+ assert_equal ['1df547800dcd168e589bb9b26b4039bff3a7f7e4'],
+ git.commit_tokens('2e9366dd7a786fdb35f211fff1c8ea05c51968b1')
+
+ assert_equal [], git.commit_tokens('1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+
+ assert_equal ['089c527c61235bd0793c49109b5bd34d439848c6',
+ 'b6e9220c3cabe53a4ed7f32952aeaeb8a822603d',
+ '2e9366dd7a786fdb35f211fff1c8ea05c51968b1',
+ '1df547800dcd168e589bb9b26b4039bff3a7f7e4'], git.commits.collect { |c| c.token }
+
+ assert_equal ['1df547800dcd168e589bb9b26b4039bff3a7f7e4'],
+ git.commits('2e9366dd7a786fdb35f211fff1c8ea05c51968b1').collect { |c| c.token }
+
+ assert_equal [], git.commits('1df547800dcd168e589bb9b26b4039bff3a7f7e4')
+ end
+ end
+
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+require 'date'
+
+module Scm::Adapters
+ class GitLogParserTest < Scm::Test
+
+ def test_basic
+ commits = []
+
+ helloworld = File.new(File.dirname(__FILE__) + '/../data/helloworld.log').read
+
+ Git::LogParser.parse( helloworld ) do |commit|
+ commits << commit
+ end
+
+ assert commits
+ assert_equal 3, commits.size
+
+ commits.each do |commit|
+ # puts commit.inspect
+ assert_equal 40, commit.token.length
+
+ # 00000000.... is ok for parent_sha1 (if we have no parent), but not for us!
+ assert_not_equal "0000000000000000000000000000000000000000", commit.token
+
+ assert_equal "robin", commit.author_name
+
+ commit.diffs.each do |d|
+ assert_equal 40, d.sha1.length
+ assert_equal 40, d.parent_sha1.length
+ # 00000000.... is ok for our parent's sha1 (if we have no parent), but not for us!
+ assert_not_equal "0000000000000000000000000000000000000000", d.sha1
+
+ assert d.path.length > 0
+ assert d.action =~ /[ACDMRTUXB]/
+ end
+ end
+
+ assert_equal Time.gm(2006,6,11,11,28,0), commits[0].author_date
+ assert_equal Time.gm(2006,6,11,18,32,13), commits[1].author_date
+ assert_equal Time.gm(2006,6,11, 9,34,17), commits[2].author_date
+
+ assert_equal "Initial Checkin", commits[0].message
+ assert_equal "added makefile", commits[1].message
+ assert_equal "added some documentation and licensing info", commits[2].message
+
+ assert_equal '.gitignore', commits[0].diffs[0].path
+ assert_equal 'A', commits[0].diffs[0].action
+ assert_equal 'helloworld.c', commits[0].diffs[1].path
+ assert_equal 'A', commits[0].diffs[1].action
+ assert_equal 'ohloh_token', commits[0].diffs[2].path
+ assert_equal 'A', commits[0].diffs[2].action
+
+ assert_equal 'makefile', commits[1].diffs[0].path
+ assert_equal 'A', commits[1].diffs[0].action
+ assert_equal 'ohloh_token', commits[1].diffs[1].path
+ assert_equal 'M', commits[1].diffs[1].action
+
+ assert_equal 'README', commits[2].diffs[0].path
+ assert_equal 'A', commits[2].diffs[0].action
+ assert_equal 'helloworld.c', commits[2].diffs[1].path
+ assert_equal 'M', commits[2].diffs[1].action
+ assert_equal 'ohloh_token', commits[2].diffs[2].path
+ assert_equal 'M', commits[2].diffs[2].action
+ end
+
+ # If the filename includes non-ASCII characters, the filename is in double quotes.
+ # The quotes must be stripped.
+ def test_filename_in_quotes
+ log = <<-LOG
+__BEGIN_COMMIT__
+Commit: 0546fa73b6951be72956bf4c72c37255034d8bdc
+Author: e2jk
+Date: Tue, Mar 13 2007 17:08:49 -0700
+__BEGIN_COMMENT__
+Supprime le dossier des bibliotheques du projet
+<unknown>
+__END_COMMENT__
+:100644 100644 8ffcfcbb647ab353e7e885fb3fd897eef719d64f e4eaafd3ed351461cef016bf606f0ce6af057380 M "Cin\303\251 Library/Cin\303\251 Library.nsi"
+ LOG
+
+ commits = []
+ Git::LogParser.parse( log ) do |commit|
+ commits << commit
+ end
+ assert_equal "Cin\303\251 Library/Cin\303\251 Library.nsi", commits[0].diffs[0].path
+ end
+
+ # Not all commits include file diffs. Need to support that case.
+ def test_commit_without_diffs
+ log = <<-LOG
+__BEGIN_COMMIT__
+Commit: 9abc3b26e395ea5199362d6e19c705eb58842cd8
+Author: troth
+Date: Tue Feb 11 19:03:03 2003 +0000
+__BEGIN_COMMENT__
+Remove reference to avr-gcc in depend rule (cut & paste error).
+<unknown>
+__END_COMMENT__
+:100644 100644 a35924054b56a3dd308ac92505b811bdfecee777 f4f4738ae0f49a56d97ba61d7feb09aa35d9e69d M Makefile
+__BEGIN_COMMIT__
+Commit: 10ed46d82c279d090b664c48a88a95e7ad76de2f
+Author: bdean
+Date: Sun Feb 9 13:36:47 2003 +0000
+__BEGIN_COMMENT__
+Test commit in new public repository. Before this time this repo
+existed on a private system. Commits made by 'bsd' on the old system
+were made by Brian Dean (bdean on the current system).
+__END_COMMENT__
+__BEGIN_COMMIT__
+Commit: 213c3220ff91eedda7323187fed0552e07069400
+Author: bsd
+Date: Sat Feb 8 04:20:39 2003 +0000
+__BEGIN_COMMENT__
+The last part of that last commit message should read:
+
+All others - modify program description.
+
+__END_COMMENT__
+ LOG
+
+ commits = []
+ Git::LogParser.parse( log ) do |commit|
+ commits << commit
+ end
+
+ assert commits
+ assert_equal 3, commits.size
+
+ assert_equal "Remove reference to avr-gcc in depend rule (cut & paste error).", commits[0].message
+ assert_equal "Test commit in new public repository. Before this time this repo\n"+
+ "existed on a private system. Commits made by 'bsd' on the old system\n"+
+ "were made by Brian Dean (bdean on the current system).", commits[1].message
+
+ assert_equal "The last part of that last commit message should read:\n\nAll others - modify program description.\n", commits[2].message
+
+ assert_equal 1, commits[0].diffs.size
+ assert_equal 0, commits[1].diffs.size
+ assert_equal 0, commits[2].diffs.size
+ end
+
+ def test_ignore_submodules
+ log = <<-LOG
+__BEGIN_COMMIT__
+Commit: 9abc3b26e395ea5199362d6e19c705eb58842cd8
+Author: troth
+Date: Tue Feb 11 19:03:03 2003 +0000
+__BEGIN_COMMENT__
+Remove a submodule from the project
+__END_COMMENT__
+:160000 000000 f4f4738ae0f49a56d97ba61d7feb09aa35d9e69d 0000000000000000000000000000000000000000 D submodule
+__BEGIN_COMMIT__
+Commit: 10ed46d82c279d090b664c48a88a95e7ad76de2f
+Author: bdean
+Date: Sun Feb 9 13:36:47 2003 +0000
+__BEGIN_COMMENT__
+Add a submodule to the project
+__END_COMMENT__
+:000000 160000 0000000000000000000000000000000000000000 f4f4738ae0f49a56d97ba61d7feb09aa35d9e69d A submodule
+ LOG
+
+ commits = []
+ Git::LogParser.parse( log ) do |commit|
+ commits << commit
+ end
+
+ assert commits
+ assert_equal 2, commits.size
+
+ commits.each do |commit|
+ assert_equal 0, commit.diffs.size
+ end
+ end
+
+ def test_use_email_when_names_are_missing
+ log = <<-LOG
+__BEGIN_COMMIT__
+Commit: ea26f7280956f1112a8e68610cb9d6336a94585d
+Author: mickeyl
+AuthorEmail: mickeyl@openembedded.org
+Date: Wed, 11 Jun 2008 00:37:47 +0000
+__BEGIN_COMMENT__
+fso-image: remove openmoko-sound-system2 in favour of pulseaudio-meta
+<unknown>
+__END_COMMENT__
+:100644 100644 a5bd9a39acc1567586372b63f347fb4df4f20957 72e6bb0df6f21387b2a3e8e1519e4aefea6339a0 M packages/images/fso-image.bb
+
+__BEGIN_COMMIT__
+Commit: fa3ee9d4cefc2db81adadf36da9cacbe92ce96f1
+Author:
+AuthorEmail: mickeyl@openembedded.org
+Date: Wed, 11 Jun 2008 00:37:06 +0000
+__BEGIN_COMMENT__
+gst-plugins-good 0.10.7 add missing dependency to esound
+<unknown>
+__END_COMMENT__
+:100644 100644 e84c4801f1d7acb0606e37a3a5b8c681182b3659 fb551f5176419f07b7901fb76493c8bb75de20ff M packages/gstreamer/gst-plugins-good_0.10
+
+ LOG
+
+ commits = []
+ Git::LogParser.parse( log ) do |commit|
+ commits << commit
+ end
+
+ assert commits
+ assert_equal 2, commits.size
+
+ assert_equal 'mickeyl', commits.first.author_name # Use name when present
+ assert_equal 'mickeyl@openembedded.org', commits.last.author_name # Else use email
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitMiscTest < Scm::Test
+
+ def test_export
+ with_git_repository('git') do |git|
+ Scm::ScratchDir.new do |dir|
+ git.export(dir)
+ assert_equal ['.','..','.gitignore','COPYING','README','helloworld.c','makefile','ohloh_token'], Dir.entries(dir).sort
+ end
+ end
+ end
+
+ def test_head
+ with_git_repository('git') do |git|
+ assert git.exist?
+ assert_equal '1df547800dcd168e589bb9b26b4039bff3a7f7e4', git.head
+ assert_equal ['master'], git.branches
+ assert git.has_branch?('master')
+ end
+ end
+
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitPullTest < Scm::Test
+
+ def test_basic_pull
+ with_git_repository('git') do |src|
+ Scm::ScratchDir.new do |dest_dir|
+
+ dest = GitAdapter.new(:url => dest_dir).normalize
+ assert !dest.exist?
+
+ dest.pull(src)
+ assert dest.exist?
+
+ assert_equal src.log, dest.log
+ end
+ end
+ end
+
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitPushTest < Scm::Test
+
+ def test_hostname
+ assert_equal "foo", GitAdapter.new(:url => 'foo:/bar').hostname
+ assert_equal "/bar", GitAdapter.new(:url => 'foo:/bar').path
+
+ assert !GitAdapter.new.hostname
+ assert !GitAdapter.new(:url => '/bar').hostname
+ assert_equal 'http', GitAdapter.new(:url => 'http://www.ohloh.net/bar').hostname
+ end
+
+ def test_local
+ assert !GitAdapter.new(:url => "foo:/bar").local? # Assuming your machine is not named "foo" :-)
+ assert !GitAdapter.new(:url => "http://www.ohloh.net/foo").local?
+ assert GitAdapter.new(:url => "src").local?
+ assert GitAdapter.new(:url => "/Users/robin/src").local?
+ assert GitAdapter.new(:url => "#{`hostname`.strip}:src").local?
+ assert GitAdapter.new(:url => "#{`hostname`.strip}:/Users/robin/src").local?
+ end
+
+ def test_basic_push
+ with_git_repository('git') do |src|
+ Scm::ScratchDir.new do |dest_dir|
+
+ dest = GitAdapter.new(:url => dest_dir).normalize
+ assert !dest.exist?
+
+ src.push(dest)
+ assert dest.exist?
+ assert_equal src.log, dest.log
+
+ # Now push again. This tests a different code path!
+ File.open(File.join(src.url, 'foo'), 'w') { }
+ src.commit_all(Scm::Commit.new)
+
+ src.push(dest)
+ assert dest.exist?
+ assert_equal src.log, dest.log
+
+ end
+ end
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitTokenTest < Scm::Test
+
+ def test_no_token_returns_nil
+ Scm::ScratchDir.new do |dir|
+ git = GitAdapter.new(:url => dir).normalize
+ assert !git.read_token
+ git.init_db
+ assert !git.read_token
+ end
+ end
+
+ def test_write_and_read_token
+ Scm::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)
+ assert_equal "FOO", git.read_token
+ end
+ end
+
+ def test_commit_all_includes_write_token
+ Scm::ScratchDir.new do |dir|
+ git = GitAdapter.new(:url => dir).normalize
+ git.init_db
+ c = Scm::Commit.new
+ c.token = "BAR"
+ git.commit_all(c)
+ assert_equal c.token, git.read_token
+ end
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class GitValidationTest < Scm::Test
+ def test_rejected_urls
+ [ nil, "", "foo", "http:/", "http:://", "http://", "http://a",
+ "kernel.org/linux/linux.git", # missing a protocol prefix
+ "http://kernel.org/linux/lin%32ux.git", # no encoded strings allowed
+ "http://kernel.org/linux/linux.git malicious code", # no spaces allowed
+ "svn://svn.mythtv.org/svn/trunk", # svn protocol is not allowed
+ "/home/robin/cvs", # local file paths not allowed
+ "file:///home/robin/cvs", # file protocol is not allowed
+ ":pserver:anonymous:@juicereceiver.cvs.sourceforge.net:/cvsroot/juicereceiver" # pserver is just wrong
+ ].each do |url|
+ git = GitAdapter.new(:url => url)
+ assert git.validate_url.any?
+ end
+ end
+
+ def test_accepted_urls
+ [ "http://kernel.org/pub/scm/git/git.git",
+ "git://kernel.org/pub/scm/git/git.git",
+ "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"
+ ].each do |url|
+ git = GitAdapter.new(:url => url)
+ assert !git.validate_url
+ end
+ end
+
+ def test_guess_forge
+ git = GitAdapter.new(:url => nil)
+ assert_equal nil, 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
+ end
+end
--- /dev/null
+module Scm::Parsers
+ class CommandLineTest < Scm::Test
+ def test_cvs_from_file
+ result = `#{File.dirname(__FILE__) + '/../../bin/ohlog'} --xml --cvs #{DATA_DIR + '/basic.rlog'}`
+ assert_equal 0, $?
+ assert_buffers_equal File.read(DATA_DIR + '/basic.ohlog'), result
+ end
+
+ def test_cvs_from_pipe
+ result = `cat #{DATA_DIR + '/basic.rlog'} | #{File.dirname(__FILE__) + '/../../bin/ohlog'} --xml --cvs`
+ assert_equal 0, $?
+ assert_buffers_equal File.read(DATA_DIR + '/basic.ohlog'), result
+ end
+
+ def test_svn_from_file
+ result = `#{File.dirname(__FILE__) + '/../../bin/ohlog'} --xml --svn #{DATA_DIR + '/simple.svn_log'}`
+ assert_equal 0, $?
+ assert_buffers_equal File.read(DATA_DIR + '/simple.ohlog'), result
+ end
+
+ def test_svn_xml_from_file
+ result = `#{File.dirname(__FILE__) + '/../../bin/ohlog'} --xml --svn-xml #{DATA_DIR + '/simple.svn_xml_log'}`
+ assert_equal 0, $?
+ assert_buffers_equal File.read(DATA_DIR + '/simple.ohlog'), result
+ end
+
+ def test_help
+ result = `#{File.dirname(__FILE__) + '/../../bin/ohlog'} -?`
+ assert_equal 0, $?
+ assert result =~ /Examples:/
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class SvnCatFileTest < Scm::Test
+
+ def test_cat_file
+ with_svn_repository('svn') do |svn|
+expected = <<-EXPECTED
+/* Hello, World! */
+#include <stdio.h>
+main()
+{
+ printf("Hello, World!\\n");
+}
+EXPECTED
+ assert_equal expected, svn.cat_file(Scm::Commit.new(:token => "1"), Scm::Diff.new(:path => "trunk/helloworld.c"))
+ end
+
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class SvnCommitsTest < Scm::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 [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 [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)
+ end
+ end
+
+ # Confirms that the sha1 matches those created by git exactly
+ def test_sha1
+ with_svn_repository('svn') do |svn|
+ assert_equal '0000000000000000000000000000000000000000', svn.try_get_sha1('/trunk/file_not_found')
+ assert_equal 'f6adcae4447809b651c787c078d255b2b4e963c5', svn.try_get_sha1('/trunk/helloworld.c')
+ end
+ end
+
+ # 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")]
+ svn.populate_sha1s!(c)
+ assert_equal 'f6adcae4447809b651c787c078d255b2b4e963c5', c.diffs.first.sha1
+ assert_equal '4c734ad53b272c9b3d719f214372ac497ff6c068', c.diffs.first.parent_sha1
+ end
+ end
+
+ def test_strip_commit_branch
+ svn = SvnAdapter.new(:branch_name => "/trunk")
+ commit = Scm::Commit.new
+
+ # nil diffs before => nil diffs after
+ assert !svn.strip_commit_branch(commit).diffs
+
+ # [] diffs before => [] diffs after
+ commit.diffs = []
+ 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")
+ ]
+ 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
+ end
+
+ def test_strip_path_branch
+ # Returns nil for any path outside of SvnAdapter::branch_name
+ assert !SvnAdapter.new.strip_path_branch(nil)
+ assert !SvnAdapter.new(:branch_name => "/trunk").strip_path_branch("/branches/foo")
+ assert !SvnAdapter.new(:branch_name => "/trunk").strip_path_branch("/t")
+
+ # If branch_name is empty or root, returns path unchanged
+ assert_equal '', SvnAdapter.new.strip_path_branch('')
+ assert_equal '/trunk', SvnAdapter.new.strip_path_branch('/trunk')
+
+ # If path is equal to or is a subdirectory of branch_name, returns subdirectory portion only.
+ assert_equal '', SvnAdapter.new(:branch_name => "/trunk").strip_path_branch('/trunk')
+ assert_equal '/foo', SvnAdapter.new(:branch_name => "/trunk").strip_path_branch('/trunk/foo')
+ end
+
+ def test_strip_path_branch_with_special_chars
+ assert_equal '/foo', SvnAdapter.new(:branch_name => '/trunk/hamcrest-c++').strip_path_branch('/trunk/hamcrest-c++/foo')
+ end
+
+
+ def test_deep_commits
+ with_svn_repository('deep_svn') do |svn|
+
+ # The full repository contains 4 revisions...
+ assert_equal 4, svn.commit_count
+
+ # ...however, the current trunk contains only revisions 3 and 4.
+ # That's because the branch was moved to replace the trunk at revision 3.
+ #
+ # Even though there was a different trunk directory present in
+ # revisions 1 and 2, it is not visible to Ohloh.
+
+ trunk = SvnAdapter.new(:url => File.join(svn.url,'trunk'), :branch_name => '/trunk').normalize
+ assert_equal 2, trunk.commit_count
+ assert_equal [3,4], trunk.commit_tokens
+
+
+ deep_commits = []
+ trunk.each_commit { |c| deep_commits << c }
+
+ # When the branch is moved to replace the trunk in revision 3,
+ # the Subversion log shows
+ #
+ # D /branches/b
+ # A /trunk (from /branches/b:2)
+ #
+ # However, there are files in those directories. Make sure the commits
+ # that we generate include all of those files not shown by the log.
+ #
+ # 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
+ # 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
+ assert_equal 2, deep_commits.first.diffs.size # Two files seen
+
+ assert_equal 'A', deep_commits.first.diffs[0].action
+ assert_equal '/subdir/bar.rb', deep_commits.first.diffs[0].path
+ assert_equal 'A', deep_commits.first.diffs[1].action
+ assert_equal '/subdir/foo.rb', deep_commits.first.diffs[1].path
+
+ # In Revision 4, a directory is renamed. This shows in the Subversion log as
+ #
+ # A /trunk/newdir (from /trunk/subdir:3)
+ # D /trunk/subdir
+ #
+ # Again, there are files in this directory, so make sure our commit includes
+ # both delete and add events for all of the files in this directory, but does
+ # not actually refer to the directories themselves.
+
+ assert_equal 4, deep_commits.last.token # Make sure we're checking the right revision
+
+ # There should be 2 files removed and two files added
+ assert_equal 4, deep_commits.last.diffs.size
+
+ assert_equal 'A', deep_commits.last.diffs[0].action
+ assert_equal '/newdir/bar.rb', deep_commits.last.diffs[0].path
+ assert_equal 'A', deep_commits.last.diffs[1].action
+ assert_equal '/newdir/foo.rb', deep_commits.last.diffs[1].path
+
+ assert_equal 'D', deep_commits.last.diffs[2].action
+ assert_equal '/subdir/bar.rb', deep_commits.last.diffs[2].path
+ assert_equal 'D', deep_commits.last.diffs[3].action
+ assert_equal '/subdir/foo.rb', deep_commits.last.diffs[3].path
+ end
+ end
+
+ # A mini-integration test.
+ # Check that SHA1 values are populated, directories are recursed, and outside branches are ignored.
+ def test_each_commit
+ commits = []
+ with_svn_repository('svn') do |svn|
+ svn.each_commit do |e|
+ commits << e
+ assert e.token
+ assert e.committer_name
+ assert e.committer_date
+ assert e.message
+ assert e.diffs
+ e.diffs.each do |d|
+ assert d.action.length == 1
+ assert d.path.length > 0
+ end
+ end
+ end
+
+ 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 "Initial Checkin\n", commits[0].message
+ assert_equal "added makefile", commits[1].message
+ assert_equal "added some documentation and licensing info", commits[2].message
+ assert_equal "added bs COPYING to catch global licenses", commits[3].message
+ assert_equal "moving COPYING", commits[4].message
+
+ assert_equal 1, commits[0].diffs.size
+ assert_equal 'A', commits[0].diffs[0].action
+ assert_equal '/trunk/helloworld.c', commits[0].diffs[0].path
+ assert_equal '4c734ad53b272c9b3d719f214372ac497ff6c068', commits[0].diffs[0].sha1
+ assert_equal '0000000000000000000000000000000000000000', commits[0].diffs[0].parent_sha1
+
+ assert_equal 1, commits[1].diffs.size
+ assert_equal 'A', commits[1].diffs[0].action
+ assert_equal '/trunk/makefile', commits[1].diffs[0].path
+ assert_equal 'af2dfd5070b01a19b672861e595de98c101c49cc', commits[1].diffs[0].sha1
+ assert_equal '0000000000000000000000000000000000000000', commits[1].diffs[0].parent_sha1
+
+ assert_equal 2, commits[2].diffs.size
+ assert_equal 'A', commits[2].diffs[0].action
+ assert_equal '/trunk/README', commits[2].diffs[0].path
+ assert_equal 'f0547ce063095e66be74618bc410989df226d2d2', commits[2].diffs[0].sha1
+ assert_equal '0000000000000000000000000000000000000000', commits[2].diffs[0].parent_sha1
+ assert_equal 'M', commits[2].diffs[1].action
+ assert_equal '/trunk/helloworld.c', commits[2].diffs[1].path
+ assert_equal 'f6adcae4447809b651c787c078d255b2b4e963c5', commits[2].diffs[1].sha1
+ assert_equal '4c734ad53b272c9b3d719f214372ac497ff6c068', commits[2].diffs[1].parent_sha1
+
+ assert_equal 1, commits[3].diffs.size
+ assert_equal 'A', commits[3].diffs[0].action
+ assert_equal '/COPYING', commits[3].diffs[0].path
+ assert_equal '6ff87c4664981e4397625791c8ea3bbb5f2279a3', commits[3].diffs[0].sha1
+ assert_equal '0000000000000000000000000000000000000000', commits[3].diffs[0].parent_sha1
+
+ assert_equal 2, commits[4].diffs.size
+ assert_equal 'D', commits[4].diffs[0].action
+ assert_equal '/COPYING', commits[4].diffs[0].path
+ assert_equal '0000000000000000000000000000000000000000', commits[4].diffs[0].sha1
+ assert_equal '6ff87c4664981e4397625791c8ea3bbb5f2279a3', commits[4].diffs[0].parent_sha1
+ assert_equal 'A', commits[4].diffs[1].action
+ assert_equal '/trunk/COPYING', commits[4].diffs[1].path
+ assert_equal '6ff87c4664981e4397625791c8ea3bbb5f2279a3', commits[4].diffs[1].sha1
+ assert_equal '0000000000000000000000000000000000000000', commits[4].diffs[1].parent_sha1
+ end
+
+
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class SvnConvertTest < Scm::Test
+ def test_basic_convert
+ with_svn_repository('svn') do |src|
+ Scm::ScratchDir.new do |dest_dir|
+ dest = GitAdapter.new(:url => dest_dir).normalize
+ assert !dest.exist?
+
+ dest.pull(src)
+ assert dest.exist?
+
+ dest_commits = dest.commits
+ src.commits.each_with_index do |c, i|
+ # 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
+
+ # The svn-to-git conversion process loses the trailing \n for single-line messages
+ assert_equal c.message.strip, dest_commits[i].message.strip
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Adapters
+ class SvnMiscTest < Scm::Test
+
+ def test_export
+ with_svn_repository('svn') do |svn|
+ Scm::ScratchDir.new do |dir|
+ svn.export(dir)
+ assert_equal ['.','..','branches','tags','trunk'], Dir.entries(dir).sort
+ end
+ end
+ end
+
+ def test_path
+ assert !SvnAdapter.new(:url => "http://svn.collab.net/repos/svn/trunk").path
+ assert !SvnAdapter.new(:url => "svn://svn.collab.net/repos/svn/trunk").path
+ assert_equal "/foo/bar", SvnAdapter.new(:url => "file:///foo/bar").path
+ assert_equal "foo/bar", SvnAdapter.new(:url => "file://foo/bar").path
+ assert_equal "/foo/bar", SvnAdapter.new(:url => "svn+ssh://server/foo/bar").path
+ end
+
+ def test_hostname
+ assert !SvnAdapter.new(:url => "http://svn.collab.net/repos/svn/trunk").hostname
+ assert !SvnAdapter.new(:url => "svn://svn.collab.net/repos/svn/trunk").hostname
+ assert !SvnAdapter.new(:url => "file:///foo/bar").hostname
+ assert_equal "server", SvnAdapter.new(:url => "svn+ssh://server/foo/bar").hostname
+ end
+
+ def test_info
+ with_svn_repository('svn') do |svn|
+ assert_equal svn.url, svn.root
+ assert_equal "6a9cefd4-a008-4d2a-a89b-d77e99cd6eb1", svn.uuid
+ assert_equal 5, svn.max_revision
+ assert_equal 'directory', svn.node_kind
+
+ assert_equal 'file', svn.node_kind('trunk/helloworld.c',1)
+ end
+ end
+
+ def test_ls
+ with_svn_repository('svn') do |svn|
+ assert_equal ['branches/', 'tags/', 'trunk/'], svn.ls
+ assert_equal ['COPYING','README','helloworld.c','makefile'], svn.ls('trunk')
+ assert_equal ['helloworld.c'], svn.ls('trunk', 1)
+
+ assert_equal ['trunk/helloworld.c'], svn.recurse_files(nil, 1)
+ assert_equal ['helloworld.c'], svn.recurse_files('/trunk', 1)
+ end
+ end
+
+ def test_is_directory
+ with_svn_repository('svn') do |svn|
+ assert svn.is_directory?('trunk')
+ assert !svn.is_directory?('trunk/helloworld.c')
+ end
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Parsers
+ class SvnParserTest < Scm::Test
+
+ def test_basic
+ assert_convert(SvnParser, DATA_DIR + '/simple.svn_log', DATA_DIR + '/simple.ohlog')
+ end
+
+ def test_empty_array
+ assert_equal([], SvnParser.parse(''))
+ end
+
+ def test_empty_xml
+ assert_equal("<?xml version=\"1.0\"?>\n<ohloh_log scm=\"svn\">\n</ohloh_log>\n", SvnParser.parse('', :writer => XmlWriter.new))
+ end
+
+ def test_yield_instead_of_writer
+ commits = []
+ result = SvnParser.parse(File.read(DATA_DIR + '/simple.svn_log')) do |commit|
+ commits << commit.token
+ end
+ assert_nil result
+ assert_equal [5, 4, 3, 2, 1], commits
+ end
+
+ def test_log_parser
+ sample_log = <<SAMPLE
+------------------------------------------------------------------------
+r1 | robin | 2006-06-11 11:28:00 -0700 (Sun, 11 Jun 2006) | 2 lines
+
+Initial Checkin
+
+------------------------------------------------------------------------
+r2 | jason | 2006-06-11 11:32:13 -0700 (Sun, 11 Jun 2006) | 1 line
+
+added makefile
+------------------------------------------------------------------------
+r3 | robin | 2006-06-11 11:34:17 -0700 (Sun, 11 Jun 2006) | 1 line
+
+added some documentation and licensing info
+------------------------------------------------------------------------
+SAMPLE
+
+ revs = SvnParser.parse(sample_log)
+
+ assert revs
+ assert_equal 3, revs.size
+
+ assert_equal 1, revs[0].token
+ assert_equal 'robin', revs[0].committer_name
+ assert_equal "Initial Checkin\n", revs[0].message # Note \n at end of comment
+ assert_equal Time.utc(2006,6,11,18,28,00), revs[0].committer_date
+
+ assert_equal 2, revs[1].token
+ assert_equal 'jason', revs[1].committer_name
+ assert_equal "added makefile", revs[1].message # Note no \n at end of comment
+ assert_equal Time.utc(2006,6,11,18,32,13), revs[1].committer_date
+
+ assert_equal 3, revs[2].token
+ assert_equal 'robin', revs[2].committer_name
+ assert_equal "added some documentation and licensing info", revs[2].message
+ assert_equal Time.utc(2006,6,11,18,34,17), revs[2].committer_date
+ end
+
+ # This is an excerpt from the log for Wireshark. It includes Subversion log excerpts in
+ # its comments, which really screwed us up. This test confirms that I've fixed the
+ # parser to ignore log excerpts in the comments.
+ def test_log_embedded_in_comments
+ log = <<LOG
+------------------------------------------------------------------------
+r21932 | jmayer | 2007-05-25 01:34:15 -0700 (Fri, 25 May 2007) | 22 lines
+
+Update from samba tree revision 23054 to 23135
+============================ Samba log start ============
+------------------------------------------------------------------------
+r23069 | metze | 2007-05-22 13:23:36 +0200 (Tue, 22 May 2007) | 3 lines
+Changed paths:
+ M /branches/SAMBA_4_0/source/pidl/tests/Util.pm
+
+print out the command, to find out the problem on host 'tridge'
+
+metze
+------------------------------------------------------------------------
+r23071 | metze | 2007-05-22 14:45:58 +0200 (Tue, 22 May 2007) | 3 lines
+Changed paths:
+ M /branches/SAMBA_4_0/source/pidl/tests/Util.pm
+
+print the command on failure only
+
+metze
+------------------------------------------------------------------------
+------------------------------------------------------------------------
+============================ Samba log end ==============
+
+------------------------------------------------------------------------
+r21931 | kukosa | 2007-05-24 23:54:39 -0700 (Thu, 24 May 2007) | 2 lines
+
+UMTS RRC updated to 3GPP TS 25.331 V7.4.0 (2007-03) and moved to one directory
+
+------------------------------------------------------------------------
+LOG
+ revs = SvnParser.parse(log)
+
+ assert revs
+ assert_equal 2, revs.size
+
+ assert_equal 21932, revs[0].token
+ assert_equal 21931, revs[1].token
+
+ comment = <<COMMENT
+Update from samba tree revision 23054 to 23135
+============================ Samba log start ============
+------------------------------------------------------------------------
+r23069 | metze | 2007-05-22 13:23:36 +0200 (Tue, 22 May 2007) | 3 lines
+Changed paths:
+ M /branches/SAMBA_4_0/source/pidl/tests/Util.pm
+
+print out the command, to find out the problem on host 'tridge'
+
+metze
+------------------------------------------------------------------------
+r23071 | metze | 2007-05-22 14:45:58 +0200 (Tue, 22 May 2007) | 3 lines
+Changed paths:
+ M /branches/SAMBA_4_0/source/pidl/tests/Util.pm
+
+print the command on failure only
+
+metze
+------------------------------------------------------------------------
+------------------------------------------------------------------------
+============================ Samba log end ==============
+COMMENT
+ assert_equal comment, revs[0].message
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+require 'socket'
+
+module Scm::Adapters
+ class SvnPullTest < Scm::Test
+
+ def test_svnadmin_create
+ Scm::ScratchDir.new do |dir|
+ url = File.join(dir, "my_svn_repo")
+ svn = SvnAdapter.new(:url => url).normalize
+
+ assert !svn.exist?
+ svn.svnadmin_create
+ assert svn.exist?
+
+ # Ensure that revision properties are settable
+ svn.propset('foo','bar')
+ assert_equal 'bar', svn.propget('foo')
+ end
+ end
+
+ def test_basic_pull_using_svnsync
+ with_svn_repository('svn') do |src|
+ Scm::ScratchDir.new do |dest_dir|
+
+ dest = SvnAdapter.new(:url => dest_dir).normalize
+ assert !dest.exist?
+
+ dest.pull(src)
+ assert dest.exist?
+
+ assert_equal src.log, dest.log
+ end
+ end
+ end
+
+ def test_svnadmin_create_local
+ Scm::ScratchDir.new do |dir|
+ svn = SvnAdapter.new(:url => "file://#{dir}")
+ svn.svnadmin_create_local
+ assert svn.exist?
+ assert FileTest.exist?(File.join(dir, 'hooks', 'pre-revprop-change'))
+ assert FileTest.executable?(File.join(dir, 'hooks', 'pre-revprop-change'))
+ svn.run File.join(dir, 'hooks', 'pre-revprop-change')
+ end
+ end
+
+ def test_svnadmin_create_remote
+ Scm::ScratchDir.new do |dir|
+ svn = SvnAdapter.new(:url => "svn+ssh://#{Socket.gethostname}#{dir}")
+ svn.svnadmin_create_remote
+ assert svn.exist?
+ assert FileTest.exist?(File.join(dir, 'hooks', 'pre-revprop-change'))
+ assert FileTest.executable?(File.join(dir, 'hooks', 'pre-revprop-change'))
+ svn.run File.join(dir, 'hooks', 'pre-revprop-change')
+ end
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+require 'socket'
+
+module Scm::Adapters
+ class SvnPushTest < Scm::Test
+
+ def test_basic_push_using_svnsync
+ with_svn_repository('svn') do |src|
+ Scm::ScratchDir.new do |dest_dir|
+
+ dest = SvnAdapter.new(:url => dest_dir).normalize
+ assert !dest.exist?
+
+ src.push(dest)
+ assert dest.exist?
+
+ assert_equal src.log, dest.log
+ end
+ end
+ end
+
+ # Triggers the "ssh" code path by using svn+ssh:// protocol instead of file:// protocol.
+ # 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|
+
+ dest = SvnAdapter.new(:url => "svn+ssh://#{Socket.gethostname}#{File.expand_path(dest_dir)}").normalize
+ assert !dest.exist?
+
+ src.push(dest)
+ assert dest.exist?
+
+ assert_equal src.log, dest.log
+ end
+ end
+ end
+
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+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
+ "svn://svn.gajim.org:/gajim/trunk", # invalid port number
+ "svn://svn.gajim.org:abc/gajim/trunk", # invalid port number
+ "svn log https://svn.sourceforge.net/svnroot/myserver/trunk"
+ ].each do |url|
+ # Rejected for both internal and public use
+ [true, false].each do |p|
+ svn = SvnAdapter.new(:url => url, :public_urls_only => p)
+ assert svn.validate_url
+ end
+ end
+ end
+
+ def test_accepted_urls
+ [ "https://svn.sourceforge.net/svnroot/opende/trunk", # https protocol OK
+ "svn://svn.gajim.org/gajim/trunk", # svn protocol OK
+ "http://svn.mythtv.org/svn/trunk/mythtv", # http protocol OK
+ "https://svn.sourceforge.net/svnroot/vienna-rss/trunk/2.0.0", # periods, numbers and dashes OK
+ "svn://svn.gajim.org:3690/gajim/trunk", # port number OK
+ "http://svn.mythtv.org:80/svn/trunk/mythtv", # port number OK
+ "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
+ ].each do |url|
+ # Accepted for both internal and public use
+ [true, false].each do |p|
+ svn = SvnAdapter.new(:url => url, :public_urls_only => p)
+ assert !svn.validate_url
+ end
+ end
+ end
+
+ # These urls are not available to the public
+ def test_rejected_public_urls
+ [ "file:///home/robin/svn"
+ ].each do |url|
+ svn = SvnAdapter.new(:url => url, :public_urls_only => true)
+ assert svn.validate_url
+
+ svn = SvnAdapter.new(:url => url)
+ assert !svn.validate_url
+ end
+ end
+
+ def test_guess_forge
+ svn = SvnAdapter.new(:url => nil)
+ assert_equal nil, svn.guess_forge
+
+ svn = SvnAdapter.new(:url => 'garbage_in_garbage_out')
+ assert_equal nil, svn.guess_forge
+
+ svn = SvnAdapter.new(:url => 'svn://rubyforge.org//var/svn/rubyomf2097')
+ assert_equal 'rubyforge.org', svn.guess_forge
+
+ svn = SvnAdapter.new(:url => 'svn://rubyforge.org:3960//var/svn/rubyomf2097')
+ 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
+
+ svn = SvnAdapter.new(:url => 'https://svn.sourceforge.net:80/svnroot/typo3/CoreDocs/trunk')
+ assert_equal 'sourceforge.net', svn.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://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
+ 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
+
+ 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 'http://pianosa.googlecode.com/svn/trunk',
+ SvnAdapter.new(:url => 'http://pianosa.googlecode.com/svn/trunk').normalize.url
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+module Scm::Parsers
+ class SvnXmlParserTest < Scm::Test
+
+ def test_basic
+ assert_convert(SvnXmlParser, DATA_DIR + '/simple.svn_xml_log', DATA_DIR + '/simple.ohlog')
+ end
+
+ def test_empty_array
+ assert_equal([], SvnXmlParser.parse(''))
+ end
+
+ def test_empty_xml
+ assert_equal("<?xml version=\"1.0\"?>\n<ohloh_log scm=\"svn\">\n</ohloh_log>\n", SvnXmlParser.parse('', :writer => XmlWriter.new))
+ end
+
+ end
+end