38fbd0c384f862f80966a3d87686e0b03bb9929e

Author: Robin Luckey

Date: 2009-01-19 15:21:49 -0800

[NEW] Initial Revision

diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + 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. diff --git a/README b/README new file mode 100644 index 0000000..e55cc91 --- /dev/null +++ b/README @@ -0,0 +1,99 @@ += 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 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..efe749b --- /dev/null +++ b/Rakefile @@ -0,0 +1,10 @@ +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 diff --git a/bin/ohlog b/bin/ohlog new file mode 100755 index 0000000..b9bb746 --- /dev/null +++ b/bin/ohlog @@ -0,0 +1,118 @@ +#!/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 diff --git a/lib/scm.rb b/lib/scm.rb new file mode 100644 index 0000000..76c81d0 --- /dev/null +++ b/lib/scm.rb @@ -0,0 +1,27 @@ +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' diff --git a/lib/scm/adapters/abstract/system.rb b/lib/scm/adapters/abstract/system.rb new file mode 100644 index 0000000..d3b452a --- /dev/null +++ b/lib/scm/adapters/abstract/system.rb @@ -0,0 +1,41 @@ +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 diff --git a/lib/scm/adapters/abstract/validation.rb b/lib/scm/adapters/abstract/validation.rb new file mode 100644 index 0000000..aa3b3c3 --- /dev/null +++ b/lib/scm/adapters/abstract/validation.rb @@ -0,0 +1,77 @@ +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 diff --git a/lib/scm/adapters/abstract_adapter.rb b/lib/scm/adapters/abstract_adapter.rb new file mode 100644 index 0000000..1dde224 --- /dev/null +++ b/lib/scm/adapters/abstract_adapter.rb @@ -0,0 +1,24 @@ +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' diff --git a/lib/scm/adapters/cvs/commits.rb b/lib/scm/adapters/cvs/commits.rb new file mode 100644 index 0000000..f17721a --- /dev/null +++ b/lib/scm/adapters/cvs/commits.rb @@ -0,0 +1,89 @@ +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 diff --git a/lib/scm/adapters/cvs/misc.rb b/lib/scm/adapters/cvs/misc.rb new file mode 100644 index 0000000..c95d885 --- /dev/null +++ b/lib/scm/adapters/cvs/misc.rb @@ -0,0 +1,160 @@ +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 diff --git a/lib/scm/adapters/cvs/validation.rb b/lib/scm/adapters/cvs/validation.rb new file mode 100644 index 0000000..3cd8ff8 --- /dev/null +++ b/lib/scm/adapters/cvs/validation.rb @@ -0,0 +1,77 @@ +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 diff --git a/lib/scm/adapters/cvs_adapter.rb b/lib/scm/adapters/cvs_adapter.rb new file mode 100644 index 0000000..ce78e70 --- /dev/null +++ b/lib/scm/adapters/cvs_adapter.rb @@ -0,0 +1,18 @@ +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' diff --git a/lib/scm/adapters/git/cat_file.rb b/lib/scm/adapters/git/cat_file.rb new file mode 100644 index 0000000..ee8cd1b --- /dev/null +++ b/lib/scm/adapters/git/cat_file.rb @@ -0,0 +1,15 @@ +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 diff --git a/lib/scm/adapters/git/commit_all.rb b/lib/scm/adapters/git/commit_all.rb new file mode 100644 index 0000000..acf06cf --- /dev/null +++ b/lib/scm/adapters/git/commit_all.rb @@ -0,0 +1,141 @@ +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 diff --git a/lib/scm/adapters/git/commits.rb b/lib/scm/adapters/git/commits.rb new file mode 100644 index 0000000..68045a2 --- /dev/null +++ b/lib/scm/adapters/git/commits.rb @@ -0,0 +1,60 @@ +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 diff --git a/lib/scm/adapters/git/log_parser.rb b/lib/scm/adapters/git/log_parser.rb new file mode 100644 index 0000000..9f5f2c0 --- /dev/null +++ b/lib/scm/adapters/git/log_parser.rb @@ -0,0 +1,70 @@ +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 diff --git a/lib/scm/adapters/git/misc.rb b/lib/scm/adapters/git/misc.rb new file mode 100644 index 0000000..a854a67 --- /dev/null +++ b/lib/scm/adapters/git/misc.rb @@ -0,0 +1,65 @@ +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 diff --git a/lib/scm/adapters/git/pull.rb b/lib/scm/adapters/git/pull.rb new file mode 100644 index 0000000..1b6d7f5 --- /dev/null +++ b/lib/scm/adapters/git/pull.rb @@ -0,0 +1,114 @@ +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 diff --git a/lib/scm/adapters/git/push.rb b/lib/scm/adapters/git/push.rb new file mode 100644 index 0000000..11aa891 --- /dev/null +++ b/lib/scm/adapters/git/push.rb @@ -0,0 +1,39 @@ +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 diff --git a/lib/scm/adapters/git/token.rb b/lib/scm/adapters/git/token.rb new file mode 100644 index 0000000..b102af9 --- /dev/null +++ b/lib/scm/adapters/git/token.rb @@ -0,0 +1,53 @@ +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 diff --git a/lib/scm/adapters/git/validation.rb b/lib/scm/adapters/git/validation.rb new file mode 100644 index 0000000..ef5cc60 --- /dev/null +++ b/lib/scm/adapters/git/validation.rb @@ -0,0 +1,22 @@ +module Scm::Adapters + class GitAdapter < AbstractAdapter + def self.url_regex + /^(http|https|rsync|git|ssh):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/[A-Za-z0-9_\-\.\/\~\+]*$/ + end + + def self.public_url_regex + /^(http|https|git):\/\/(\w+@)?[A-Za-z0-9_\-\.]+(:\d+)?\/[A-Za-z0-9_\-\.\/\~\+]*$/ + end + + def normalize + super + @branch_name = 'master' if @branch_name.to_s == '' + self + end + + def validate_server_connection + return unless valid? + @errors << [:failed, "The server did not respond to the 'git-ls-remote' command. Is the URL correct?"] unless self.exists? + end + end +end diff --git a/lib/scm/adapters/git_adapter.rb b/lib/scm/adapters/git_adapter.rb new file mode 100644 index 0000000..f229a1b --- /dev/null +++ b/lib/scm/adapters/git_adapter.rb @@ -0,0 +1,17 @@ +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' diff --git a/lib/scm/adapters/svn/cat_file.rb b/lib/scm/adapters/svn/cat_file.rb new file mode 100644 index 0000000..635afdf --- /dev/null +++ b/lib/scm/adapters/svn/cat_file.rb @@ -0,0 +1,15 @@ +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 diff --git a/lib/scm/adapters/svn/commits.rb b/lib/scm/adapters/svn/commits.rb new file mode 100644 index 0000000..d3d752a --- /dev/null +++ b/lib/scm/adapters/svn/commits.rb @@ -0,0 +1,240 @@ +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 diff --git a/lib/scm/adapters/svn/misc.rb b/lib/scm/adapters/svn/misc.rb new file mode 100644 index 0000000..9af0d0a --- /dev/null +++ b/lib/scm/adapters/svn/misc.rb @@ -0,0 +1,136 @@ +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 diff --git a/lib/scm/adapters/svn/pre-revprop-change b/lib/scm/adapters/svn/pre-revprop-change new file mode 100755 index 0000000..742e13d --- /dev/null +++ b/lib/scm/adapters/svn/pre-revprop-change @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +exit 0 diff --git a/lib/scm/adapters/svn/pull.rb b/lib/scm/adapters/svn/pull.rb new file mode 100644 index 0000000..16f4211 --- /dev/null +++ b/lib/scm/adapters/svn/pull.rb @@ -0,0 +1,76 @@ +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 diff --git a/lib/scm/adapters/svn/push.rb b/lib/scm/adapters/svn/push.rb new file mode 100644 index 0000000..1a8f99e --- /dev/null +++ b/lib/scm/adapters/svn/push.rb @@ -0,0 +1,15 @@ +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 diff --git a/lib/scm/adapters/svn/validation.rb b/lib/scm/adapters/svn/validation.rb new file mode 100644 index 0000000..a8149fc --- /dev/null +++ b/lib/scm/adapters/svn/validation.rb @@ -0,0 +1,60 @@ +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 diff --git a/lib/scm/adapters/svn_adapter.rb b/lib/scm/adapters/svn_adapter.rb new file mode 100644 index 0000000..0771e2c --- /dev/null +++ b/lib/scm/adapters/svn_adapter.rb @@ -0,0 +1,14 @@ +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' diff --git a/lib/scm/commit.rb b/lib/scm/commit.rb new file mode 100644 index 0000000..af11911 --- /dev/null +++ b/lib/scm/commit.rb @@ -0,0 +1,46 @@ +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 diff --git a/lib/scm/diff.rb b/lib/scm/diff.rb new file mode 100644 index 0000000..82db21a --- /dev/null +++ b/lib/scm/diff.rb @@ -0,0 +1,43 @@ +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 diff --git a/lib/scm/parsers/array_writer.rb b/lib/scm/parsers/array_writer.rb new file mode 100644 index 0000000..5371602 --- /dev/null +++ b/lib/scm/parsers/array_writer.rb @@ -0,0 +1,19 @@ +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 diff --git a/lib/scm/parsers/branch_number.rb b/lib/scm/parsers/branch_number.rb new file mode 100644 index 0000000..6b235c7 --- /dev/null +++ b/lib/scm/parsers/branch_number.rb @@ -0,0 +1,60 @@ +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 diff --git a/lib/scm/parsers/cvs_parser.rb b/lib/scm/parsers/cvs_parser.rb new file mode 100644 index 0000000..17fd427 --- /dev/null +++ b/lib/scm/parsers/cvs_parser.rb @@ -0,0 +1,182 @@ +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 diff --git a/lib/scm/parsers/human_writer.rb b/lib/scm/parsers/human_writer.rb new file mode 100644 index 0000000..992eb23 --- /dev/null +++ b/lib/scm/parsers/human_writer.rb @@ -0,0 +1,55 @@ +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 diff --git a/lib/scm/parsers/parser.rb b/lib/scm/parsers/parser.rb new file mode 100644 index 0000000..a321db8 --- /dev/null +++ b/lib/scm/parsers/parser.rb @@ -0,0 +1,34 @@ +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 diff --git a/lib/scm/parsers/svn_parser.rb b/lib/scm/parsers/svn_parser.rb new file mode 100644 index 0000000..6672f8a --- /dev/null +++ b/lib/scm/parsers/svn_parser.rb @@ -0,0 +1,68 @@ +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 diff --git a/lib/scm/parsers/svn_xml_parser.rb b/lib/scm/parsers/svn_xml_parser.rb new file mode 100644 index 0000000..f8b1783 --- /dev/null +++ b/lib/scm/parsers/svn_xml_parser.rb @@ -0,0 +1,60 @@ +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 diff --git a/lib/scm/parsers/xml_writer.rb b/lib/scm/parsers/xml_writer.rb new file mode 100644 index 0000000..4442e0e --- /dev/null +++ b/lib/scm/parsers/xml_writer.rb @@ -0,0 +1,62 @@ +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 diff --git a/lib/scm/scratch_dir.rb b/lib/scm/scratch_dir.rb new file mode 100644 index 0000000..4eaa06a --- /dev/null +++ b/lib/scm/scratch_dir.rb @@ -0,0 +1,59 @@ +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 diff --git a/lib/scm/systemu.rb b/lib/scm/systemu.rb new file mode 100644 index 0000000..27c0323 --- /dev/null +++ b/lib/scm/systemu.rb @@ -0,0 +1,299 @@ +# vim: ts=2:sw=2:sts=2:et:fdm=marker +require 'tmpdir' +require 'socket' +require 'fileutils' +require 'rbconfig' +require 'thread' +require 'yaml' + +class Object + def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end +end + +class SystemUniversal +# +# constants +# + SystemUniversal::VERSION = '1.2.0' unless defined? SystemUniversal::VERSION + def version() SystemUniversal::VERSION end +# +# class methods +# + + @host = Socket.gethostname + @ppid = Process.ppid + @pid = Process.pid + @turd = ENV['SYSTEMU_TURD'] + + c = ::Config::CONFIG + ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT'] + @ruby = if system('%s -e 42' % ruby) + ruby + else + system('%s -e 42' % 'ruby') ? 'ruby' : warn('no ruby in PATH/CONFIG') + end + + class << self + %w( host ppid pid ruby turd ).each{|a| attr_accessor a} + end + +# +# instance methods +# + + def initialize argv, opts = {}, &block + getopt = getopts opts + + @argv = argv + @block = block + + @stdin = getopt[ ['stdin', 'in', '0', 0] ] + @stdout = getopt[ ['stdout', 'out', '1', 1] ] + @stderr = getopt[ ['stderr', 'err', '2', 2] ] + @env = getopt[ 'env' ] + @cwd = getopt[ 'cwd' ] + + @host = getopt[ 'host', self.class.host ] + @ppid = getopt[ 'ppid', self.class.ppid ] + @pid = getopt[ 'pid', self.class.pid ] + @ruby = getopt[ 'ruby', self.class.ruby ] + end + + def systemu + tmpdir do |tmp| + c = child_setup tmp + status = nil + + begin + thread = nil + + quietly{ + IO.popen "#{ @ruby } #{ c['program'] }", 'r+' do |pipe| + line = pipe.gets + case line + when %r/^pid: \d+$/ + cid = Integer line[%r/\d+/] + else + begin + buf = pipe.read + buf = "#{ line }#{ buf }" + e = Marshal.load buf + raise unless Exception === e + raise e + rescue + raise "wtf?\n#{ buf }\n" + end + end + thread = new_thread cid, @block if @block + pipe.read rescue nil + end + } + status = $? + ensure + if thread + begin + class << status + attr 'thread' + end + status.instance_eval{ @thread = thread } + rescue + 42 + end + end + end + + if @stdout or @stderr + open(c['stdout']){|f| relay f => @stdout} if @stdout + open(c['stderr']){|f| relay f => @stderr} if @stderr + status + else + [status, IO.read(c['stdout']), IO.read(c['stderr'])] + end + end + end + + def new_thread cid, block + q = Queue.new + Thread.new(cid) do |cid| + current = Thread.current + current.abort_on_exception = true + q.push current + block.call cid + end + q.pop + end + + def child_setup tmp + stdin = File.expand_path(File.join(tmp, 'stdin')) + stdout = File.expand_path(File.join(tmp, 'stdout')) + stderr = File.expand_path(File.join(tmp, 'stderr')) + program = File.expand_path(File.join(tmp, 'program')) + config = File.expand_path(File.join(tmp, 'config')) + + if @stdin + open(stdin, 'w'){|f| relay @stdin => f} + else + FileUtils.touch stdin + end + FileUtils.touch stdout + FileUtils.touch stderr + + c = {} + c['argv'] = @argv + c['env'] = @env + c['cwd'] = @cwd + c['stdin'] = stdin + c['stdout'] = stdout + c['stderr'] = stderr + c['program'] = program + open(config, 'w'){|f| YAML.dump c, f} + + open(program, 'w'){|f| f.write child_program(config)} + + c + end + + def quietly + v = $VERBOSE + $VERBOSE = nil + yield + ensure + $VERBOSE = v + end + + def child_program config + <<-program + PIPE = STDOUT.dup + begin + require 'yaml' + + config = YAML.load(IO.read('#{ config }')) + + argv = config['argv'] + env = config['env'] + cwd = config['cwd'] + stdin = config['stdin'] + stdout = config['stdout'] + stderr = config['stderr'] + + Dir.chdir cwd if cwd + env.each{|k,v| ENV[k.to_s] = v.to_s} if env + + STDIN.reopen stdin + STDOUT.reopen stdout + STDERR.reopen stderr + + PIPE.puts "pid: \#{ Process.pid }" + PIPE.flush ### the process is ready yo! + PIPE.close + + exec *argv + rescue Exception => e + PIPE.write Marshal.dump(e) rescue nil + exit 42 + end + program + end + + def relay srcdst + src, dst, ignored = srcdst.to_a.first + if src.respond_to? 'read' + while((buf = src.read(8192))); dst << buf; end + else + src.each{|buf| dst << buf} + end + end + + def tmpdir d = Dir.tmpdir, max = 42, &b + i = -1 and loop{ + i += 1 + + tmp = File.join d, "systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }" + + begin + Dir.mkdir tmp + rescue Errno::EEXIST + raise if i >= max + next + end + + break( + if b + begin + b.call tmp + ensure + FileUtils.rm_rf tmp unless SystemU.turd + end + else + tmp + end + ) + } + end + + def getopts opts = {} + lambda do |*args| + keys, default, ignored = args + catch('opt') do + [keys].flatten.each do |key| + [key, key.to_s, key.to_s.intern].each do |key| + throw 'opt', opts[key] if opts.has_key?(key) + end + end + default + end + end + end +end + +SystemU = SystemUniversal unless defined? SystemU + + + + + + + + + + + + + +if $0 == __FILE__ +# +# date +# + date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " ) + + status, stdout, stderr = systemu date + p [status, stdout, stderr] + + status = systemu date, 1=>(stdout = '') + p [status, stdout] + + status = systemu date, 2=>(stderr = '') + p [status, stderr] +# +# sleep +# + sleep = %q( ruby -e" p(sleep(1)) " ) + status, stdout, stderr = systemu sleep + p [status, stdout, stderr] + + sleep = %q( ruby -e" p(sleep(42)) " ) + status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid} + p [status, stdout, stderr] +# +# env +# + env = %q( ruby -e" p ENV['A'] " ) + status, stdout, stderr = systemu env, :env => {'A' => 42} + p [status, stdout, stderr] +# +# cwd +# + env = %q( ruby -e" p Dir.pwd " ) + status, stdout, stderr = systemu env, :cwd => Dir.tmpdir + p [status, stdout, stderr] +end diff --git a/log/.gitignore b/log/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/log/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/test/data/basic.ohlog b/test/data/basic.ohlog new file mode 100644 index 0000000..95a281e --- /dev/null +++ b/test/data/basic.ohlog @@ -0,0 +1,11 @@ +<?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> diff --git a/test/data/basic.rlog b/test/data/basic.rlog new file mode 100644 index 0000000..8e93905 --- /dev/null +++ b/test/data/basic.rlog @@ -0,0 +1,30 @@ + +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 *** +============================================================================= diff --git a/test/data/file_created_on_branch.rlog b/test/data/file_created_on_branch.rlog new file mode 100644 index 0000000..af28a32 --- /dev/null +++ b/test/data/file_created_on_branch.rlog @@ -0,0 +1,34 @@ + +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 +============================================================================= diff --git a/test/data/helloworld.log b/test/data/helloworld.log new file mode 100644 index 0000000..dbfe448 --- /dev/null +++ b/test/data/helloworld.log @@ -0,0 +1,32 @@ +__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 diff --git a/test/data/intelliglue.rlog b/test/data/intelliglue.rlog new file mode 100644 index 0000000..433563d --- /dev/null +++ b/test/data/intelliglue.rlog @@ -0,0 +1,1216 @@ + +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 +============================================================================= diff --git a/test/data/multiple_commits.rlog b/test/data/multiple_commits.rlog new file mode 100644 index 0000000..506115b --- /dev/null +++ b/test/data/multiple_commits.rlog @@ -0,0 +1,64 @@ + +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 +============================================================================= diff --git a/test/data/multiple_revisions.rlog b/test/data/multiple_revisions.rlog new file mode 100644 index 0000000..506115b --- /dev/null +++ b/test/data/multiple_revisions.rlog @@ -0,0 +1,64 @@ + +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 +============================================================================= diff --git a/test/data/simple.ohlog b/test/data/simple.ohlog new file mode 100644 index 0000000..58fcc4f --- /dev/null +++ b/test/data/simple.ohlog @@ -0,0 +1,44 @@ +<?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> diff --git a/test/data/simple.svn_log b/test/data/simple.svn_log new file mode 100644 index 0000000..5cdc96c --- /dev/null +++ b/test/data/simple.svn_log @@ -0,0 +1,37 @@ +------------------------------------------------------------------------ +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 + +------------------------------------------------------------------------ diff --git a/test/data/simple.svn_xml_log b/test/data/simple.svn_xml_log new file mode 100644 index 0000000..ec87b87 --- /dev/null +++ b/test/data/simple.svn_xml_log @@ -0,0 +1,66 @@ +<?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> diff --git a/test/data/simultaneous_checkins.rlog b/test/data/simultaneous_checkins.rlog new file mode 100644 index 0000000..304172c --- /dev/null +++ b/test/data/simultaneous_checkins.rlog @@ -0,0 +1,22 @@ + +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... +============================================================================= diff --git a/test/data/simultaneous_checkins_2.rlog b/test/data/simultaneous_checkins_2.rlog new file mode 100644 index 0000000..9a18a20 --- /dev/null +++ b/test/data/simultaneous_checkins_2.rlog @@ -0,0 +1,18 @@ + +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 +============================================================================= diff --git a/test/repositories/cvs/CVSROOT/.#checkoutlist b/test/repositories/cvs/CVSROOT/.#checkoutlist new file mode 100644 index 0000000..b04b350 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#checkoutlist @@ -0,0 +1,13 @@ +# 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 '#' diff --git a/test/repositories/cvs/CVSROOT/.#commit_email b/test/repositories/cvs/CVSROOT/.#commit_email new file mode 100644 index 0000000..6386361 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#commit_email @@ -0,0 +1,10 @@ +# The "commit_email" file is used to control templates for emails sent +# during commit and import. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/.#commitinfo b/test/repositories/cvs/CVSROOT/.#commitinfo new file mode 100644 index 0000000..63ea65a --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#commitinfo @@ -0,0 +1,15 @@ +# The "commitinfo" file is used to control pre-commit checks. +# The filter on the right is invoked with the repository name. A list +# of files to check is passed to the standard input of the script. A non-zero +# exit of the filter program will cause the commit to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/.#config b/test/repositories/cvs/CVSROOT/.#config new file mode 100644 index 0000000..af2a28c --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#config @@ -0,0 +1,26 @@ +# Set this to 'no' if pserver shouldn't check system users/passwords +#SystemAuth=yes + +# Set the Acl parsing type (none,compat,normal). +#AclMode=compat + +# Alternate location of CVS LockServer. Set to 'none' to disable.. +#LockServer=localhost:2402 + +# Set 'TopLevelAdmin' to 'yes' to create a CVS directory at the top +# level of the new working directory when using the 'cvs checkout' +# command. +#TopLevelAdmin=no + +# Set 'LogHistory' to 'all' or 'TOFEWGCMAR' to log all transactions to the +# history file, or a subset as needed (ie 'TMAR' logs all write operations) +#LogHistory=TOFEWGCMAR + +# Set 'RereadLogAfterVerify' to control rereading of the log file after a verifymsg +# 'always' or 'yes' to always reread the log regardless +# 'never' or 'no' (default) to never reread the log +#RereadLogAfterVerify=no + +# Set 'Watcher' to set a user who gets all notify events within the repository whether +# or not the ifle is watched. +#Watcher=watch_user \ No newline at end of file diff --git a/test/repositories/cvs/CVSROOT/.#cvsrc b/test/repositories/cvs/CVSROOT/.#cvsrc new file mode 100644 index 0000000..e69de29 diff --git a/test/repositories/cvs/CVSROOT/.#cvswrappers b/test/repositories/cvs/CVSROOT/.#cvswrappers new file mode 100644 index 0000000..11fa3b3 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#cvswrappers @@ -0,0 +1,18 @@ +# This file affects handling of files based on their names. +# +# The -m option specifies whether CVS attempts to merge files. +# +# The -k option specifies keyword expansion (e.g. -kb for binary). +# +# The -t option overrides the default mime type. +# +# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) +# +# wildcard [option value][option value]... +# +# where option is one of +# -k expansion mode value: b, o, kkv, etc. +# +# and value is a single-quote delimited value. +# For example: +#*.gif -kb diff --git a/test/repositories/cvs/CVSROOT/.#historyinfo b/test/repositories/cvs/CVSROOT/.#historyinfo new file mode 100644 index 0000000..889ff1b --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#historyinfo @@ -0,0 +1,15 @@ +# The "historyinfo" file is used to log the history file output. +# The filter on the right is invoked with the repository name. Its +# standard input contains the history line that has just been written +# to the history file (if it exists) +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/.#keywords b/test/repositories/cvs/CVSROOT/.#keywords new file mode 100644 index 0000000..a94adf4 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#keywords @@ -0,0 +1,30 @@ +# The "keywords" file is used to modify the standard set of RCS keywords +# or define entirely new ones. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. Subsequent lines contain keyword definitions, indented by a space +# to separate them from module definitions +.# +# If the repository name does not match any of the definitions in this +# file, the "ALL" section is used, if it is specified. +# +# Any keyword with an empty definition is ignored. This can be used to selectively +# disable individual RCS keywords. +# +# The default defintions are: +# +#ALL +# Author %a +# Date %d +# Header %r/%p/%f %v %d %a %s %l +# CVSHeader %p/%f %v %d %a %s %l +# Id %f %v %d %a %s %l +# Locker %l +# Log %f +# Name %N +# RCSfile %f +# Revision %v +# Source %r/%p/%f +# State %s +# CommitId %C diff --git a/test/repositories/cvs/CVSROOT/.#loginfo b/test/repositories/cvs/CVSROOT/.#loginfo new file mode 100644 index 0000000..f3c4329 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#loginfo @@ -0,0 +1,22 @@ +# The "loginfo" file controls where "cvs commit" log information +# is sent. The first entry on a line is a regular expression which must match +# the directory that the change is being made to, relative to the +# $CVSROOT. If a match is found, then the remainder of the line is a filter +# program that should expect log information on its standard input. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name ALL appears as a regular expression it is always used +# in addition to the first matching regex or DEFAULT. +# +# You may specify a format string as part of the +# filter. The string is composed of a '%' followed +# by a single format character, or followed by a set of format +# characters surrounded by '{' and '}' as separators. The format +# characters are: +# +# s = file name +# V = old version number (pre-checkin) +# v = new version number (post-checkin) +# diff --git a/test/repositories/cvs/CVSROOT/.#modules b/test/repositories/cvs/CVSROOT/.#modules new file mode 100644 index 0000000..cb9e9ef --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#modules @@ -0,0 +1,26 @@ +# Three different line formats are valid: +# key -a aliases... +# key [options] directory +# key [options] directory files... +# +# Where "options" are composed of: +# -i prog Run "prog" on "cvs commit" from top-level of module. +# -o prog Run "prog" on "cvs checkout" of module. +# -e prog Run "prog" on "cvs export" of module. +# -t prog Run "prog" on "cvs rtag" of module. +# -u prog Run "prog" on "cvs update" of module. +# -d dir Place module in directory "dir" instead of module name. +# -l Top-level directory only -- do not recurse. +# +# NOTE: If you change any of the "Run" options above, you'll have to +# release and re-checkout any working directories of these modules. +# +# And "directory" is a path to a directory relative to $CVSROOT. +# +# The "-a" option specifies an alias. An alias is interpreted as if +# everything on the right of the "-a" had been typed on the command line. +# +# You can encode a module within a module by using the special '&' +# character to interpose another module into the current module. This +# can be useful for creating a module that consists of many directories +# spread out over the entire source repository. diff --git a/test/repositories/cvs/CVSROOT/.#modules2 b/test/repositories/cvs/CVSROOT/.#modules2 new file mode 100644 index 0000000..6cdc5ba --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#modules2 @@ -0,0 +1,22 @@ +# *** modules2 currently has 'experimental' status. Testing is encouraged but. +# for greatest stability use the modules file. *** +# +# This file describes the layout of virtual directory structures +# within the repository. +# +# The layout is similar to a Windows .ini file. For example: +# +# [foo] +# dir1/dir2/dir3 = realdir1/realdir2 +# dir1/dir3 = !realdir1/realdir3 (^*js$|^*cpp$) +# +# [bar] +# / = realdir4 +# dir_to_delete = +# foo = foo +# +# The special character '!' stops recursion to directories below the one specified (-l option). +# The special character '+' stops parsing of that line, so that you can avoid infinte loops. +# +# Items in (...) are an extended regular expression applied to the filenames. All files which do not. +# match are ignored. diff --git a/test/repositories/cvs/CVSROOT/.#notify b/test/repositories/cvs/CVSROOT/.#notify new file mode 100644 index 0000000..cdb3320 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#notify @@ -0,0 +1,22 @@ +# The "notify" file controls where notifications from watches set by +# "cvs watch add" or "cvs edit" are sent. The first entry on a line is +# a regular expression which is tested against the directory that the +# change is being made to, relative to the $CVSROOT. If it matches, +# then the remainder of the line is a filter program that should contain +# one occurrence of %s for the user to notify, and information on its +# standard input. +# +# "ALL" or "DEFAULT" can be used in place of the regular expression. +# +# You may specify a format string as part of the +# filter. The format characters are: +# +# s = user being notified +# b = Bug identifier +# m = Message supplied on command line +# d = Date of action +# u = User performing the unedit +# t = tag or branch being edited +# +# For example: +#ALL mail %s -s "CVS notification for bug %b" diff --git a/test/repositories/cvs/CVSROOT/.#notify_email b/test/repositories/cvs/CVSROOT/.#notify_email new file mode 100644 index 0000000..c673ab4 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#notify_email @@ -0,0 +1,10 @@ +# The "notify_email" file is used to control templates for emails sent +# when notifying users. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/.#postcommand b/test/repositories/cvs/CVSROOT/.#postcommand new file mode 100644 index 0000000..ab3ef14 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#postcommand @@ -0,0 +1,14 @@ +# The "postcommand" file is run after a cvs command has finished. +# The filter on the right is invoked with the repository name and +# the name of the command that has been executed. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/.#postmodule b/test/repositories/cvs/CVSROOT/.#postmodule new file mode 100644 index 0000000..b6d258b --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#postmodule @@ -0,0 +1,14 @@ +# The "postmodule" file is run after a cvs module is processed. +# The filter on the right is invoked with the repository name, +# the name of the command that has been executed, and the module name. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/.#precommand b/test/repositories/cvs/CVSROOT/.#precommand new file mode 100644 index 0000000..05c8b89 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#precommand @@ -0,0 +1,18 @@ +# The "precommand" file is run before a cvs command is executed. +# The filter on the right is invoked with the repository name and +# the name of the command that has been executed. A non-zero return +# value with abort the command with an error. +# +# The standard input of the filter receives each command argument, +# separated by linefeeds. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/.#premodule b/test/repositories/cvs/CVSROOT/.#premodule new file mode 100644 index 0000000..4837892 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#premodule @@ -0,0 +1,15 @@ +# The "premodule" file is run before cvs module is processed. +# The filter on the right is invoked with the repository name, +# the name of the command that has been executed, and the module name. +# A non-zero return value with abort the command with an error. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/.#rcsinfo b/test/repositories/cvs/CVSROOT/.#rcsinfo new file mode 100644 index 0000000..27c002f --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#rcsinfo @@ -0,0 +1,10 @@ +# The "rcsinfo" file is used to control templates with which the editor +# is invoked on commit and import. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/.#shadow b/test/repositories/cvs/CVSROOT/.#shadow new file mode 100644 index 0000000..7ccd24e --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#shadow @@ -0,0 +1,7 @@ +# 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. diff --git a/test/repositories/cvs/CVSROOT/.#tag_email b/test/repositories/cvs/CVSROOT/.#tag_email new file mode 100644 index 0000000..48d545f --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#tag_email @@ -0,0 +1,10 @@ +# The "tag_email" file is used to control templates for emails sent +# during tagging operations. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/.#taginfo b/test/repositories/cvs/CVSROOT/.#taginfo new file mode 100644 index 0000000..3beb822 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#taginfo @@ -0,0 +1,21 @@ +# The "taginfo" file is used to control pre-tag checks. +# The filter on the right is invoked with the following arguments: +# +# $1 -- tagname +# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d +# $3 -- repository +# +# The filter is passed a series of filename/version pairs on its standard input +# +# A non-zero exit of the filter program will cause the tag to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/.#triggers b/test/repositories/cvs/CVSROOT/.#triggers new file mode 100644 index 0000000..48a9fc3 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#triggers @@ -0,0 +1,5 @@ +# The "triggers" file lists libraries which handle the events for each +# module. +# +# In addition to the lines here, the default_trigger library is loaded, if available. +# diff --git a/test/repositories/cvs/CVSROOT/.#verifymsg b/test/repositories/cvs/CVSROOT/.#verifymsg new file mode 100644 index 0000000..28b1d6e --- /dev/null +++ b/test/repositories/cvs/CVSROOT/.#verifymsg @@ -0,0 +1,17 @@ +# The "verifymsg" file is used to allow verification of logging +# information. It works best when a template (as specified in the +# rcsinfo file) is provided for the logging procedure. Given a +# template with locations for, a bug-id number, a list of people who +# reviewed the code before it can be checked in, and an external +# process to catalog the differences that were code reviewed, the +# following test can be applied to the code: +# +# Making sure that the entered bug-id number is correct. +# Validating that the code that was reviewed is indeed the code being +# checked in (using the bug-id number or a seperate review +# number to identify this particular code set.). +# +# If any of the above test failed, then the commit would be aborted. +# +# Actions such as mailing a copy of the report to each reviewer are +# better handled by an entry in the loginfo file. diff --git a/test/repositories/cvs/CVSROOT/CVS/fileattr.xml b/test/repositories/cvs/CVSROOT/CVS/fileattr.xml new file mode 100644 index 0000000..83ce5ca --- /dev/null +++ b/test/repositories/cvs/CVSROOT/CVS/fileattr.xml @@ -0,0 +1,15 @@ +<?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> diff --git a/test/repositories/cvs/CVSROOT/checkoutlist b/test/repositories/cvs/CVSROOT/checkoutlist new file mode 100644 index 0000000..b04b350 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/checkoutlist @@ -0,0 +1,13 @@ +# 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 '#' diff --git a/test/repositories/cvs/CVSROOT/checkoutlist,v b/test/repositories/cvs/CVSROOT/checkoutlist,v new file mode 100644 index 0000000..13ddb4f --- /dev/null +++ b/test/repositories/cvs/CVSROOT/checkoutlist,v @@ -0,0 +1,39 @@ +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 '#' +@ + diff --git a/test/repositories/cvs/CVSROOT/commit_email b/test/repositories/cvs/CVSROOT/commit_email new file mode 100644 index 0000000..6386361 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/commit_email @@ -0,0 +1,10 @@ +# The "commit_email" file is used to control templates for emails sent +# during commit and import. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/commit_email,v b/test/repositories/cvs/CVSROOT/commit_email,v new file mode 100644 index 0000000..79135d9 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/commit_email,v @@ -0,0 +1,36 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "commit_email" file is used to control templates for emails sent +# during commit and import. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +@ + diff --git a/test/repositories/cvs/CVSROOT/commitinfo b/test/repositories/cvs/CVSROOT/commitinfo new file mode 100644 index 0000000..63ea65a --- /dev/null +++ b/test/repositories/cvs/CVSROOT/commitinfo @@ -0,0 +1,15 @@ +# The "commitinfo" file is used to control pre-commit checks. +# The filter on the right is invoked with the repository name. A list +# of files to check is passed to the standard input of the script. A non-zero +# exit of the filter program will cause the commit to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/commitinfo,v b/test/repositories/cvs/CVSROOT/commitinfo,v new file mode 100644 index 0000000..62bf695 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/commitinfo,v @@ -0,0 +1,41 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "commitinfo" file is used to control pre-commit checks. +# The filter on the right is invoked with the repository name. A list +# of files to check is passed to the standard input of the script. A non-zero +# exit of the filter program will cause the commit to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +@ + diff --git a/test/repositories/cvs/CVSROOT/config b/test/repositories/cvs/CVSROOT/config new file mode 100644 index 0000000..af2a28c --- /dev/null +++ b/test/repositories/cvs/CVSROOT/config @@ -0,0 +1,26 @@ +# Set this to 'no' if pserver shouldn't check system users/passwords +#SystemAuth=yes + +# Set the Acl parsing type (none,compat,normal). +#AclMode=compat + +# Alternate location of CVS LockServer. Set to 'none' to disable.. +#LockServer=localhost:2402 + +# Set 'TopLevelAdmin' to 'yes' to create a CVS directory at the top +# level of the new working directory when using the 'cvs checkout' +# command. +#TopLevelAdmin=no + +# Set 'LogHistory' to 'all' or 'TOFEWGCMAR' to log all transactions to the +# history file, or a subset as needed (ie 'TMAR' logs all write operations) +#LogHistory=TOFEWGCMAR + +# Set 'RereadLogAfterVerify' to control rereading of the log file after a verifymsg +# 'always' or 'yes' to always reread the log regardless +# 'never' or 'no' (default) to never reread the log +#RereadLogAfterVerify=no + +# Set 'Watcher' to set a user who gets all notify events within the repository whether +# or not the ifle is watched. +#Watcher=watch_user \ No newline at end of file diff --git a/test/repositories/cvs/CVSROOT/config,v b/test/repositories/cvs/CVSROOT/config,v new file mode 100644 index 0000000..80bcd01 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/config,v @@ -0,0 +1,51 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# Set this to 'no' if pserver shouldn't check system users/passwords +#SystemAuth=yes + +# Set the Acl parsing type (none,compat,normal). +#AclMode=compat + +# Alternate location of CVS LockServer. Set to 'none' to disable.. +#LockServer=localhost:2402 + +# Set 'TopLevelAdmin' to 'yes' to create a CVS directory at the top +# level of the new working directory when using the 'cvs checkout' +# command. +#TopLevelAdmin=no + +# Set 'LogHistory' to 'all' or 'TOFEWGCMAR' to log all transactions to the +# history file, or a subset as needed (ie 'TMAR' logs all write operations) +#LogHistory=TOFEWGCMAR + +# Set 'RereadLogAfterVerify' to control rereading of the log file after a verifymsg +# 'always' or 'yes' to always reread the log regardless +# 'never' or 'no' (default) to never reread the log +#RereadLogAfterVerify=no + +# Set 'Watcher' to set a user who gets all notify events within the repository whether +# or not the ifle is watched. +#Watcher=watch_user@ + diff --git a/test/repositories/cvs/CVSROOT/cvsrc b/test/repositories/cvs/CVSROOT/cvsrc new file mode 100644 index 0000000..e69de29 diff --git a/test/repositories/cvs/CVSROOT/cvsrc,v b/test/repositories/cvs/CVSROOT/cvsrc,v new file mode 100644 index 0000000..6e50ecf --- /dev/null +++ b/test/repositories/cvs/CVSROOT/cvsrc,v @@ -0,0 +1,26 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@@ + diff --git a/test/repositories/cvs/CVSROOT/cvswrappers b/test/repositories/cvs/CVSROOT/cvswrappers new file mode 100644 index 0000000..11fa3b3 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/cvswrappers @@ -0,0 +1,18 @@ +# This file affects handling of files based on their names. +# +# The -m option specifies whether CVS attempts to merge files. +# +# The -k option specifies keyword expansion (e.g. -kb for binary). +# +# The -t option overrides the default mime type. +# +# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) +# +# wildcard [option value][option value]... +# +# where option is one of +# -k expansion mode value: b, o, kkv, etc. +# +# and value is a single-quote delimited value. +# For example: +#*.gif -kb diff --git a/test/repositories/cvs/CVSROOT/cvswrappers,v b/test/repositories/cvs/CVSROOT/cvswrappers,v new file mode 100644 index 0000000..badb225 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/cvswrappers,v @@ -0,0 +1,44 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# This file affects handling of files based on their names. +# +# The -m option specifies whether CVS attempts to merge files. +# +# The -k option specifies keyword expansion (e.g. -kb for binary). +# +# The -t option overrides the default mime type. +# +# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) +# +# wildcard [option value][option value]... +# +# where option is one of +# -k expansion mode value: b, o, kkv, etc. +# +# and value is a single-quote delimited value. +# For example: +#*.gif -kb +@ + diff --git a/test/repositories/cvs/CVSROOT/historyinfo b/test/repositories/cvs/CVSROOT/historyinfo new file mode 100644 index 0000000..889ff1b --- /dev/null +++ b/test/repositories/cvs/CVSROOT/historyinfo @@ -0,0 +1,15 @@ +# The "historyinfo" file is used to log the history file output. +# The filter on the right is invoked with the repository name. Its +# standard input contains the history line that has just been written +# to the history file (if it exists) +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/historyinfo,v b/test/repositories/cvs/CVSROOT/historyinfo,v new file mode 100644 index 0000000..645bd87 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/historyinfo,v @@ -0,0 +1,41 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "historyinfo" file is used to log the history file output. +# The filter on the right is invoked with the repository name. Its +# standard input contains the history line that has just been written +# to the history file (if it exists) +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +@ + diff --git a/test/repositories/cvs/CVSROOT/keywords b/test/repositories/cvs/CVSROOT/keywords new file mode 100644 index 0000000..a94adf4 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/keywords @@ -0,0 +1,30 @@ +# The "keywords" file is used to modify the standard set of RCS keywords +# or define entirely new ones. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. Subsequent lines contain keyword definitions, indented by a space +# to separate them from module definitions +.# +# If the repository name does not match any of the definitions in this +# file, the "ALL" section is used, if it is specified. +# +# Any keyword with an empty definition is ignored. This can be used to selectively +# disable individual RCS keywords. +# +# The default defintions are: +# +#ALL +# Author %a +# Date %d +# Header %r/%p/%f %v %d %a %s %l +# CVSHeader %p/%f %v %d %a %s %l +# Id %f %v %d %a %s %l +# Locker %l +# Log %f +# Name %N +# RCSfile %f +# Revision %v +# Source %r/%p/%f +# State %s +# CommitId %C diff --git a/test/repositories/cvs/CVSROOT/keywords,v b/test/repositories/cvs/CVSROOT/keywords,v new file mode 100644 index 0000000..83f97ed --- /dev/null +++ b/test/repositories/cvs/CVSROOT/keywords,v @@ -0,0 +1,56 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "keywords" file is used to modify the standard set of RCS keywords +# or define entirely new ones. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. Subsequent lines contain keyword definitions, indented by a space +# to separate them from module definitions +.# +# If the repository name does not match any of the definitions in this +# file, the "ALL" section is used, if it is specified. +# +# Any keyword with an empty definition is ignored. This can be used to selectively +# disable individual RCS keywords. +# +# The default defintions are: +# +#ALL +# Author %a +# Date %d +# Header %r/%p/%f %v %d %a %s %l +# CVSHeader %p/%f %v %d %a %s %l +# Id %f %v %d %a %s %l +# Locker %l +# Log %f +# Name %N +# RCSfile %f +# Revision %v +# Source %r/%p/%f +# State %s +# CommitId %C +@ + diff --git a/test/repositories/cvs/CVSROOT/loginfo b/test/repositories/cvs/CVSROOT/loginfo new file mode 100644 index 0000000..f3c4329 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/loginfo @@ -0,0 +1,22 @@ +# The "loginfo" file controls where "cvs commit" log information +# is sent. The first entry on a line is a regular expression which must match +# the directory that the change is being made to, relative to the +# $CVSROOT. If a match is found, then the remainder of the line is a filter +# program that should expect log information on its standard input. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name ALL appears as a regular expression it is always used +# in addition to the first matching regex or DEFAULT. +# +# You may specify a format string as part of the +# filter. The string is composed of a '%' followed +# by a single format character, or followed by a set of format +# characters surrounded by '{' and '}' as separators. The format +# characters are: +# +# s = file name +# V = old version number (pre-checkin) +# v = new version number (post-checkin) +# diff --git a/test/repositories/cvs/CVSROOT/loginfo,v b/test/repositories/cvs/CVSROOT/loginfo,v new file mode 100644 index 0000000..aac350e --- /dev/null +++ b/test/repositories/cvs/CVSROOT/loginfo,v @@ -0,0 +1,48 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "loginfo" file controls where "cvs commit" log information +# is sent. The first entry on a line is a regular expression which must match +# the directory that the change is being made to, relative to the +# $CVSROOT. If a match is found, then the remainder of the line is a filter +# program that should expect log information on its standard input. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name ALL appears as a regular expression it is always used +# in addition to the first matching regex or DEFAULT. +# +# You may specify a format string as part of the +# filter. The string is composed of a '%' followed +# by a single format character, or followed by a set of format +# characters surrounded by '{' and '}' as separators. The format +# characters are: +# +# s = file name +# V = old version number (pre-checkin) +# v = new version number (post-checkin) +# +@ + diff --git a/test/repositories/cvs/CVSROOT/modules b/test/repositories/cvs/CVSROOT/modules new file mode 100644 index 0000000..cb9e9ef --- /dev/null +++ b/test/repositories/cvs/CVSROOT/modules @@ -0,0 +1,26 @@ +# Three different line formats are valid: +# key -a aliases... +# key [options] directory +# key [options] directory files... +# +# Where "options" are composed of: +# -i prog Run "prog" on "cvs commit" from top-level of module. +# -o prog Run "prog" on "cvs checkout" of module. +# -e prog Run "prog" on "cvs export" of module. +# -t prog Run "prog" on "cvs rtag" of module. +# -u prog Run "prog" on "cvs update" of module. +# -d dir Place module in directory "dir" instead of module name. +# -l Top-level directory only -- do not recurse. +# +# NOTE: If you change any of the "Run" options above, you'll have to +# release and re-checkout any working directories of these modules. +# +# And "directory" is a path to a directory relative to $CVSROOT. +# +# The "-a" option specifies an alias. An alias is interpreted as if +# everything on the right of the "-a" had been typed on the command line. +# +# You can encode a module within a module by using the special '&' +# character to interpose another module into the current module. This +# can be useful for creating a module that consists of many directories +# spread out over the entire source repository. diff --git a/test/repositories/cvs/CVSROOT/modules,v b/test/repositories/cvs/CVSROOT/modules,v new file mode 100644 index 0000000..bced005 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/modules,v @@ -0,0 +1,52 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# Three different line formats are valid: +# key -a aliases... +# key [options] directory +# key [options] directory files... +# +# Where "options" are composed of: +# -i prog Run "prog" on "cvs commit" from top-level of module. +# -o prog Run "prog" on "cvs checkout" of module. +# -e prog Run "prog" on "cvs export" of module. +# -t prog Run "prog" on "cvs rtag" of module. +# -u prog Run "prog" on "cvs update" of module. +# -d dir Place module in directory "dir" instead of module name. +# -l Top-level directory only -- do not recurse. +# +# NOTE: If you change any of the "Run" options above, you'll have to +# release and re-checkout any working directories of these modules. +# +# And "directory" is a path to a directory relative to $CVSROOT. +# +# The "-a" option specifies an alias. An alias is interpreted as if +# everything on the right of the "-a" had been typed on the command line. +# +# You can encode a module within a module by using the special '&' +# character to interpose another module into the current module. This +# can be useful for creating a module that consists of many directories +# spread out over the entire source repository. +@ + diff --git a/test/repositories/cvs/CVSROOT/modules2 b/test/repositories/cvs/CVSROOT/modules2 new file mode 100644 index 0000000..6cdc5ba --- /dev/null +++ b/test/repositories/cvs/CVSROOT/modules2 @@ -0,0 +1,22 @@ +# *** modules2 currently has 'experimental' status. Testing is encouraged but. +# for greatest stability use the modules file. *** +# +# This file describes the layout of virtual directory structures +# within the repository. +# +# The layout is similar to a Windows .ini file. For example: +# +# [foo] +# dir1/dir2/dir3 = realdir1/realdir2 +# dir1/dir3 = !realdir1/realdir3 (^*js$|^*cpp$) +# +# [bar] +# / = realdir4 +# dir_to_delete = +# foo = foo +# +# The special character '!' stops recursion to directories below the one specified (-l option). +# The special character '+' stops parsing of that line, so that you can avoid infinte loops. +# +# Items in (...) are an extended regular expression applied to the filenames. All files which do not. +# match are ignored. diff --git a/test/repositories/cvs/CVSROOT/modules2,v b/test/repositories/cvs/CVSROOT/modules2,v new file mode 100644 index 0000000..0986d95 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/modules2,v @@ -0,0 +1,48 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# *** modules2 currently has 'experimental' status. Testing is encouraged but. +# for greatest stability use the modules file. *** +# +# This file describes the layout of virtual directory structures +# within the repository. +# +# The layout is similar to a Windows .ini file. For example: +# +# [foo] +# dir1/dir2/dir3 = realdir1/realdir2 +# dir1/dir3 = !realdir1/realdir3 (^*js$|^*cpp$) +# +# [bar] +# / = realdir4 +# dir_to_delete = +# foo = foo +# +# The special character '!' stops recursion to directories below the one specified (-l option). +# The special character '+' stops parsing of that line, so that you can avoid infinte loops. +# +# Items in (...) are an extended regular expression applied to the filenames. All files which do not. +# match are ignored. +@ + diff --git a/test/repositories/cvs/CVSROOT/notify b/test/repositories/cvs/CVSROOT/notify new file mode 100644 index 0000000..cdb3320 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/notify @@ -0,0 +1,22 @@ +# The "notify" file controls where notifications from watches set by +# "cvs watch add" or "cvs edit" are sent. The first entry on a line is +# a regular expression which is tested against the directory that the +# change is being made to, relative to the $CVSROOT. If it matches, +# then the remainder of the line is a filter program that should contain +# one occurrence of %s for the user to notify, and information on its +# standard input. +# +# "ALL" or "DEFAULT" can be used in place of the regular expression. +# +# You may specify a format string as part of the +# filter. The format characters are: +# +# s = user being notified +# b = Bug identifier +# m = Message supplied on command line +# d = Date of action +# u = User performing the unedit +# t = tag or branch being edited +# +# For example: +#ALL mail %s -s "CVS notification for bug %b" diff --git a/test/repositories/cvs/CVSROOT/notify,v b/test/repositories/cvs/CVSROOT/notify,v new file mode 100644 index 0000000..5edf2f5 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/notify,v @@ -0,0 +1,48 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "notify" file controls where notifications from watches set by +# "cvs watch add" or "cvs edit" are sent. The first entry on a line is +# a regular expression which is tested against the directory that the +# change is being made to, relative to the $CVSROOT. If it matches, +# then the remainder of the line is a filter program that should contain +# one occurrence of %s for the user to notify, and information on its +# standard input. +# +# "ALL" or "DEFAULT" can be used in place of the regular expression. +# +# You may specify a format string as part of the +# filter. The format characters are: +# +# s = user being notified +# b = Bug identifier +# m = Message supplied on command line +# d = Date of action +# u = User performing the unedit +# t = tag or branch being edited +# +# For example: +#ALL mail %s -s "CVS notification for bug %b" +@ + diff --git a/test/repositories/cvs/CVSROOT/notify_email b/test/repositories/cvs/CVSROOT/notify_email new file mode 100644 index 0000000..c673ab4 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/notify_email @@ -0,0 +1,10 @@ +# The "notify_email" file is used to control templates for emails sent +# when notifying users. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/notify_email,v b/test/repositories/cvs/CVSROOT/notify_email,v new file mode 100644 index 0000000..e9fcc95 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/notify_email,v @@ -0,0 +1,36 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "notify_email" file is used to control templates for emails sent +# when notifying users. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +@ + diff --git a/test/repositories/cvs/CVSROOT/postcommand b/test/repositories/cvs/CVSROOT/postcommand new file mode 100644 index 0000000..ab3ef14 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/postcommand @@ -0,0 +1,14 @@ +# The "postcommand" file is run after a cvs command has finished. +# The filter on the right is invoked with the repository name and +# the name of the command that has been executed. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/postcommand,v b/test/repositories/cvs/CVSROOT/postcommand,v new file mode 100644 index 0000000..b9a194b --- /dev/null +++ b/test/repositories/cvs/CVSROOT/postcommand,v @@ -0,0 +1,40 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "postcommand" file is run after a cvs command has finished. +# The filter on the right is invoked with the repository name and +# the name of the command that has been executed. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +@ + diff --git a/test/repositories/cvs/CVSROOT/postmodule b/test/repositories/cvs/CVSROOT/postmodule new file mode 100644 index 0000000..b6d258b --- /dev/null +++ b/test/repositories/cvs/CVSROOT/postmodule @@ -0,0 +1,14 @@ +# The "postmodule" file is run after a cvs module is processed. +# The filter on the right is invoked with the repository name, +# the name of the command that has been executed, and the module name. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/postmodule,v b/test/repositories/cvs/CVSROOT/postmodule,v new file mode 100644 index 0000000..f70fae0 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/postmodule,v @@ -0,0 +1,40 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "postmodule" file is run after a cvs module is processed. +# The filter on the right is invoked with the repository name, +# the name of the command that has been executed, and the module name. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +@ + diff --git a/test/repositories/cvs/CVSROOT/precommand b/test/repositories/cvs/CVSROOT/precommand new file mode 100644 index 0000000..05c8b89 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/precommand @@ -0,0 +1,18 @@ +# The "precommand" file is run before a cvs command is executed. +# The filter on the right is invoked with the repository name and +# the name of the command that has been executed. A non-zero return +# value with abort the command with an error. +# +# The standard input of the filter receives each command argument, +# separated by linefeeds. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/precommand,v b/test/repositories/cvs/CVSROOT/precommand,v new file mode 100644 index 0000000..f2a2db5 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/precommand,v @@ -0,0 +1,44 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "precommand" file is run before a cvs command is executed. +# The filter on the right is invoked with the repository name and +# the name of the command that has been executed. A non-zero return +# value with abort the command with an error. +# +# The standard input of the filter receives each command argument, +# separated by linefeeds. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +@ + diff --git a/test/repositories/cvs/CVSROOT/premodule b/test/repositories/cvs/CVSROOT/premodule new file mode 100644 index 0000000..4837892 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/premodule @@ -0,0 +1,15 @@ +# The "premodule" file is run before cvs module is processed. +# The filter on the right is invoked with the repository name, +# the name of the command that has been executed, and the module name. +# A non-zero return value with abort the command with an error. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/premodule,v b/test/repositories/cvs/CVSROOT/premodule,v new file mode 100644 index 0000000..0d1b7c4 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/premodule,v @@ -0,0 +1,41 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "premodule" file is run before cvs module is processed. +# The filter on the right is invoked with the repository name, +# the name of the command that has been executed, and the module name. +# A non-zero return value with abort the command with an error. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +@ + diff --git a/test/repositories/cvs/CVSROOT/rcsinfo b/test/repositories/cvs/CVSROOT/rcsinfo new file mode 100644 index 0000000..27c002f --- /dev/null +++ b/test/repositories/cvs/CVSROOT/rcsinfo @@ -0,0 +1,10 @@ +# The "rcsinfo" file is used to control templates with which the editor +# is invoked on commit and import. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/rcsinfo,v b/test/repositories/cvs/CVSROOT/rcsinfo,v new file mode 100644 index 0000000..a3c803d --- /dev/null +++ b/test/repositories/cvs/CVSROOT/rcsinfo,v @@ -0,0 +1,36 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "rcsinfo" file is used to control templates with which the editor +# is invoked on commit and import. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +@ + diff --git a/test/repositories/cvs/CVSROOT/shadow b/test/repositories/cvs/CVSROOT/shadow new file mode 100644 index 0000000..7ccd24e --- /dev/null +++ b/test/repositories/cvs/CVSROOT/shadow @@ -0,0 +1,7 @@ +# 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. diff --git a/test/repositories/cvs/CVSROOT/shadow,v b/test/repositories/cvs/CVSROOT/shadow,v new file mode 100644 index 0000000..a3c14d9 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/shadow,v @@ -0,0 +1,33 @@ +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. +@ + diff --git a/test/repositories/cvs/CVSROOT/tag_email b/test/repositories/cvs/CVSROOT/tag_email new file mode 100644 index 0000000..48d545f --- /dev/null +++ b/test/repositories/cvs/CVSROOT/tag_email @@ -0,0 +1,10 @@ +# The "tag_email" file is used to control templates for emails sent +# during tagging operations. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. diff --git a/test/repositories/cvs/CVSROOT/tag_email,v b/test/repositories/cvs/CVSROOT/tag_email,v new file mode 100644 index 0000000..3fc9444 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/tag_email,v @@ -0,0 +1,36 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "tag_email" file is used to control templates for emails sent +# during tagging operations. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being made to, relative to the +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +@ + diff --git a/test/repositories/cvs/CVSROOT/taginfo b/test/repositories/cvs/CVSROOT/taginfo new file mode 100644 index 0000000..3beb822 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/taginfo @@ -0,0 +1,21 @@ +# The "taginfo" file is used to control pre-tag checks. +# The filter on the right is invoked with the following arguments: +# +# $1 -- tagname +# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d +# $3 -- repository +# +# The filter is passed a series of filename/version pairs on its standard input +# +# A non-zero exit of the filter program will cause the tag to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". diff --git a/test/repositories/cvs/CVSROOT/taginfo,v b/test/repositories/cvs/CVSROOT/taginfo,v new file mode 100644 index 0000000..f511bab --- /dev/null +++ b/test/repositories/cvs/CVSROOT/taginfo,v @@ -0,0 +1,47 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "taginfo" file is used to control pre-tag checks. +# The filter on the right is invoked with the following arguments: +# +# $1 -- tagname +# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d +# $3 -- repository +# +# The filter is passed a series of filename/version pairs on its standard input +# +# A non-zero exit of the filter program will cause the tag to be aborted. +# +# The first entry on a line is a regular expression which is tested +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. +# +# If the repository name does not match any of the regular expressions in this +# file, the "DEFAULT" line is used, if it is specified. +# +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". +@ + diff --git a/test/repositories/cvs/CVSROOT/triggers b/test/repositories/cvs/CVSROOT/triggers new file mode 100644 index 0000000..48a9fc3 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/triggers @@ -0,0 +1,5 @@ +# The "triggers" file lists libraries which handle the events for each +# module. +# +# In addition to the lines here, the default_trigger library is loaded, if available. +# diff --git a/test/repositories/cvs/CVSROOT/triggers,v b/test/repositories/cvs/CVSROOT/triggers,v new file mode 100644 index 0000000..78a9e62 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/triggers,v @@ -0,0 +1,31 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "triggers" file lists libraries which handle the events for each +# module. +# +# In addition to the lines here, the default_trigger library is loaded, if available. +# +@ + diff --git a/test/repositories/cvs/CVSROOT/val-tags b/test/repositories/cvs/CVSROOT/val-tags new file mode 100644 index 0000000..6f25c26 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/val-tags @@ -0,0 +1,2 @@ +my_branch y +another_branch y diff --git a/test/repositories/cvs/CVSROOT/verifymsg b/test/repositories/cvs/CVSROOT/verifymsg new file mode 100644 index 0000000..28b1d6e --- /dev/null +++ b/test/repositories/cvs/CVSROOT/verifymsg @@ -0,0 +1,17 @@ +# The "verifymsg" file is used to allow verification of logging +# information. It works best when a template (as specified in the +# rcsinfo file) is provided for the logging procedure. Given a +# template with locations for, a bug-id number, a list of people who +# reviewed the code before it can be checked in, and an external +# process to catalog the differences that were code reviewed, the +# following test can be applied to the code: +# +# Making sure that the entered bug-id number is correct. +# Validating that the code that was reviewed is indeed the code being +# checked in (using the bug-id number or a seperate review +# number to identify this particular code set.). +# +# If any of the above test failed, then the commit would be aborted. +# +# Actions such as mailing a copy of the report to each reviewer are +# better handled by an entry in the loginfo file. diff --git a/test/repositories/cvs/CVSROOT/verifymsg,v b/test/repositories/cvs/CVSROOT/verifymsg,v new file mode 100644 index 0000000..78394d8 --- /dev/null +++ b/test/repositories/cvs/CVSROOT/verifymsg,v @@ -0,0 +1,43 @@ +head 1.1; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.19.58; author robin; state Exp; +branches; +next ; +kopt kv; +deltatype text; +permissions 664; + +desc +@@ + + + +1.1 +log +@initial checkin@ +text +@# The "verifymsg" file is used to allow verification of logging +# information. It works best when a template (as specified in the +# rcsinfo file) is provided for the logging procedure. Given a +# template with locations for, a bug-id number, a list of people who +# reviewed the code before it can be checked in, and an external +# process to catalog the differences that were code reviewed, the +# following test can be applied to the code: +# +# Making sure that the entered bug-id number is correct. +# Validating that the code that was reviewed is indeed the code being +# checked in (using the bug-id number or a seperate review +# number to identify this particular code set.). +# +# If any of the above test failed, then the commit would be aborted. +# +# Actions such as mailing a copy of the report to each reviewer are +# better handled by an entry in the loginfo file. +@ + diff --git a/test/repositories/cvs/simple/CVS/fileattr.xml b/test/repositories/cvs/simple/CVS/fileattr.xml new file mode 100644 index 0000000..f8767f2 --- /dev/null +++ b/test/repositories/cvs/simple/CVS/fileattr.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<fileattr> + <directory> + <owner>robin</owner> + </directory> +</fileattr> diff --git a/test/repositories/cvs/simple/another_file.rb,v b/test/repositories/cvs/simple/another_file.rb,v new file mode 100644 index 0000000..638a8f7 --- /dev/null +++ b/test/repositories/cvs/simple/another_file.rb,v @@ -0,0 +1,113 @@ +head 1.2; +access; +symbols + another_branch:1.1.0.2; +locks; strict; +comment @# @; + + +1.2 +date 2006.06.29.18.45.29; author robin; state Exp; +branches; +next 1.1; +deltatype text; +kopt kv; +permissions 644; +commitid db744a41fc8a6ac; +mergepoint1 1.1.2.2; +filename another_file.rb; + +1.1 +date 2006.06.29.18.44.27; author robin; state dead; +branches + 1.1.2.1; +next ; +deltatype text; +kopt kv; +permissions 644; +commitid db044a41f8b01e1; +filename another_file.rb; + +1.1.2.1 +date 2006.06.29.18.44.27; author robin; state Exp; +branches; +next 1.1.2.2; +deltatype text; +kopt kv; +permissions 644; +commitid db044a41f8b01e1; +filename another_file.rb; + +1.1.2.2 +date 2006.06.29.18.44.56; author robin; state Exp; +branches; +next 1.1.2.3; +deltatype text; +kopt kv; +permissions 644; +commitid db244a41fa871cc; +filename another_file.rb; + +1.1.2.3 +date 2006.06.29.18.50.37; author robin; state Exp; +branches; +next ; +deltatype text; +kopt kv; +permissions 600; +commitid dfc44a420fde53f; +mergepoint1 1.2; +filename another_file.rb; + + +desc +@@ + + +1.2 +log +@merged another_file from another_branch onto the HEAD +@ +text +@puts "this file was created on another branch" +puts "I edited this file on another branch only" +@ + + +1.1 +log +@file another_file.rb was initially added on branch another_branch. +@ +text +@d1 2 +@ + + +1.1.2.1 +log +@created another_file on another_branch +@ +text +@a0 1 +puts "this file was created on another branch" +@ + + +1.1.2.2 +log +@edited another_file +@ +text +@a1 1 +puts "I edited this file on another branch only" +@ + + +1.1.2.3 +log +@merged from head onto another_branch. this added file late_addition +@ +text +@@ + + diff --git a/test/repositories/cvs/simple/foo.rb,v b/test/repositories/cvs/simple/foo.rb,v new file mode 100644 index 0000000..6ed04b1 --- /dev/null +++ b/test/repositories/cvs/simple/foo.rb,v @@ -0,0 +1,50 @@ +head 1.1; +branch 1.1.1; +access; +symbols + another_branch:1.1.1.1.0.4 + my_branch:1.1.1.1.0.2 + simple_release_tag:1.1.1.1 + simple_vendor_tag:1.1.1; +locks; strict; +comment @# @; + + +1.1 +date 2006.06.29.16.21.07; author robin; state Exp; +branches + 1.1.1.1; +next ; +deltatype text; +kopt kv; +permissions 644; + +1.1.1.1 +date 2006.06.29.16.21.07; author robin; state Exp; +branches; +next ; +deltatype text; +kopt kv; +permissions 644; + + +desc +@@ + + + +1.1 +log +@Initial revision +@ +text +@puts "Hi, I'm foo.rb!" +@ + + +1.1.1.1 +log +@Initial import. +@ +text +@@ diff --git a/test/repositories/cvs/simple/late_addition.rb,v b/test/repositories/cvs/simple/late_addition.rb,v new file mode 100644 index 0000000..91b4c72 --- /dev/null +++ b/test/repositories/cvs/simple/late_addition.rb,v @@ -0,0 +1,93 @@ +head 1.2; +access; +symbols + another_branch:1.1.0.2; +locks; strict; +comment @# @; + + +1.2 +date 2006.06.29.18.52.23; author robin; state Exp; +branches; +next 1.1; +deltatype text; +kopt kv; +permissions 600; +commitid e0544a421671465; +mergepoint1 1.1.2.2; +filename late_addition.rb; + +1.1 +date 2006.06.29.18.48.54; author robin; state Exp; +branches + 1.1.2.1; +next ; +deltatype text; +kopt kv; +permissions 644; +commitid df344a420967b0e; +filename late_addition.rb; + +1.1.2.1 +date 2006.06.29.18.50.37; author robin; state Exp; +branches; +next 1.1.2.2; +deltatype text; +kopt kv; +permissions 644; +commitid dfc44a420fde53f; +mergepoint1 1.1; +filename late_addition.rb; + +1.1.2.2 +date 2006.06.29.18.51.25; author robin; state Exp; +branches; +next ; +deltatype text; +kopt kv; +permissions 644; +commitid dff44a4212d348f; +filename late_addition.rb; + + +desc +@@ + + +1.2 +log +@merged the change to late_addition from another_branch to the head +@ +text +@puts "This file was created on the HEAD after the branches were already made" +puts "This line of code was added on another_branch after merging from the head" +@ + + +1.1 +log +@created late_addition on the HEAD +@ +text +@d2 1 +@ + + +1.1.2.1 +log +@merged from head onto another_branch. this added file late_addition +@ +text +@@ + + +1.1.2.2 +log +@edited late_addition on another_branch +@ +text +@a1 1 +puts "This line of code was added on another_branch after merging from the head" +@ + + diff --git a/test/repositories/cvs/simple/new_file.rb,v b/test/repositories/cvs/simple/new_file.rb,v new file mode 100644 index 0000000..72d4e57 --- /dev/null +++ b/test/repositories/cvs/simple/new_file.rb,v @@ -0,0 +1,112 @@ +head 1.2; +access; +symbols + another_branch:1.2.0.2 + my_branch:1.1.0.2; +locks; strict; +comment @# @; + + +1.2 +date 2006.06.29.18.14.47; author robin; state Exp; +branches; +next 1.1; +deltatype text; +kopt kv; +permissions 644; +commitid c3944a41896430e; +mergepoint1 1.1.2.1; +filename new_file.rb; + +1.1 +date 2006.06.29.18.05.27; author robin; state dead; +branches + 1.1.2.1; +next ; +deltatype text; +kopt kv; +permissions 644; +commitid bd144a41667e765; +filename new_file.rb; + +1.1.2.1 +date 2006.06.29.18.05.27; author robin; state Exp; +branches; +next 1.1.2.2; +deltatype text; +kopt kv; +permissions 644; +commitid bd144a41667e765; +filename new_file.rb; + +1.1.2.2 +date 2006.06.29.18.17.49; author robin; state Exp; +branches; +next 1.1.2.3; +deltatype text; +kopt kv; +permissions 644; +commitid c7744a4194cefc8; +filename new_file.rb; + +1.1.2.3 +date 2006.06.29.18.40.38; author robin; state dead; +branches; +next ; +deltatype text; +kopt kv; +permissions 444; +commitid d4e44a41ea6477e; +filename new_file.rb; + + +desc +@@ + + +1.2 +log +@merged new_file.rb from branch onto the HEAD +@ +text +@puts "I was created on the branch" +@ + + +1.1 +log +@file new_file.rb was initially added on branch my_branch. +@ +text +@d1 1 +@ + + +1.1.2.1 +log +@added new_file.rb on the branch +@ +text +@a0 1 +puts "I was created on the branch" +@ + + +1.1.2.2 +log +@modifed new_file.rb on the branch only +@ +text +@a1 1 +puts "This file was edited on the branch after the orginal was merged onto the HEAD" +@ + + +1.1.2.3 +log +@removed new_file.rb from the branch only +@ +text +@@ + + diff --git a/test/repositories/deep_svn/README.txt b/test/repositories/deep_svn/README.txt new file mode 100644 index 0000000..3bf5a57 --- /dev/null +++ b/test/repositories/deep_svn/README.txt @@ -0,0 +1,5 @@ +This is a Subversion repository; use the 'svnadmin' tool to examine +it. Do not add, delete, or modify files here unless you know how +to avoid corrupting the repository. + +Visit http://subversion.tigris.org/ for more information. diff --git a/test/repositories/deep_svn/conf/authz b/test/repositories/deep_svn/conf/authz new file mode 100644 index 0000000..78cb28e --- /dev/null +++ b/test/repositories/deep_svn/conf/authz @@ -0,0 +1,21 @@ +### This file is an example authorization file for svnserve. +### Its format is identical to that of mod_authz_svn authorization +### files. +### As shown below each section defines authorizations for the path and +### (optional) repository specified by the section name. +### The authorizations follow. An authorization line can refer to a +### single user, to a group of users defined in a special [groups] +### section, or to anyone using the '*' wildcard. Each definition can +### grant read ('r') access, read-write ('rw') access, or no access +### (''). + +[groups] +# harry_and_sally = harry,sally + +# [/foo/bar] +# harry = rw +# * = + +# [repository:/baz/fuz] +# @harry_and_sally = rw +# * = r diff --git a/test/repositories/deep_svn/conf/passwd b/test/repositories/deep_svn/conf/passwd new file mode 100644 index 0000000..ecaa08d --- /dev/null +++ b/test/repositories/deep_svn/conf/passwd @@ -0,0 +1,8 @@ +### This file is an example password file for svnserve. +### Its format is similar to that of svnserve.conf. As shown in the +### example below it contains one section labelled [users]. +### The name and password for each user follow, one account per line. + +[users] +# harry = harryssecret +# sally = sallyssecret diff --git a/test/repositories/deep_svn/conf/svnserve.conf b/test/repositories/deep_svn/conf/svnserve.conf new file mode 100644 index 0000000..b52bc5a --- /dev/null +++ b/test/repositories/deep_svn/conf/svnserve.conf @@ -0,0 +1,30 @@ +### This file controls the configuration of the svnserve daemon, if you +### use it to allow access to this repository. (If you only allow +### access through http: and/or file: URLs, then this file is +### irrelevant.) + +### Visit http://subversion.tigris.org/ for more information. + +[general] +### These options control access to the repository for unauthenticated +### and authenticated users. Valid values are "write", "read", +### and "none". The sample settings below are the defaults. +# anon-access = read +# auth-access = write +### The password-db option controls the location of the password +### database file. Unless you specify a path starting with a /, +### the file's location is relative to the conf directory. +### Uncomment the line below to use the default password file. +# password-db = passwd +### The authz-db option controls the location of the authorization +### rules for path-based access control. Unless you specify a path +### starting with a /, the file's location is relative to the conf +### directory. If you don't specify an authz-db, no path-based access +### control is done. +### Uncomment the line below to use the default authorization file. +# authz-db = authz +### This option specifies the authentication realm of the repository. +### If two repositories have the same authentication realm, they should +### have the same password database, and vice versa. The default realm +### is repository's uuid. +# realm = My First Repository diff --git a/test/repositories/deep_svn/db/current b/test/repositories/deep_svn/db/current new file mode 100644 index 0000000..270762e --- /dev/null +++ b/test/repositories/deep_svn/db/current @@ -0,0 +1 @@ +4 8 3 diff --git a/test/repositories/deep_svn/db/format b/test/repositories/deep_svn/db/format new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/test/repositories/deep_svn/db/format @@ -0,0 +1 @@ +2 diff --git a/test/repositories/deep_svn/db/fs-type b/test/repositories/deep_svn/db/fs-type new file mode 100644 index 0000000..4fdd953 --- /dev/null +++ b/test/repositories/deep_svn/db/fs-type @@ -0,0 +1 @@ +fsfs diff --git a/test/repositories/deep_svn/db/revprops/0 b/test/repositories/deep_svn/db/revprops/0 new file mode 100644 index 0000000..bd511d2 --- /dev/null +++ b/test/repositories/deep_svn/db/revprops/0 @@ -0,0 +1,5 @@ +K 8 +svn:date +V 27 +2009-01-14T16:05:58.755928Z +END diff --git a/test/repositories/deep_svn/db/revprops/1 b/test/repositories/deep_svn/db/revprops/1 new file mode 100644 index 0000000..24e4f12 --- /dev/null +++ b/test/repositories/deep_svn/db/revprops/1 @@ -0,0 +1,14 @@ +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 diff --git a/test/repositories/deep_svn/db/revprops/2 b/test/repositories/deep_svn/db/revprops/2 new file mode 100644 index 0000000..7e7349e --- /dev/null +++ b/test/repositories/deep_svn/db/revprops/2 @@ -0,0 +1,14 @@ +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 diff --git a/test/repositories/deep_svn/db/revprops/3 b/test/repositories/deep_svn/db/revprops/3 new file mode 100644 index 0000000..9299f2a --- /dev/null +++ b/test/repositories/deep_svn/db/revprops/3 @@ -0,0 +1,14 @@ +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 diff --git a/test/repositories/deep_svn/db/revprops/4 b/test/repositories/deep_svn/db/revprops/4 new file mode 100644 index 0000000..be84872 --- /dev/null +++ b/test/repositories/deep_svn/db/revprops/4 @@ -0,0 +1,14 @@ +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 diff --git a/test/repositories/deep_svn/db/revs/0 b/test/repositories/deep_svn/db/revs/0 new file mode 100644 index 0000000..10f5c45 --- /dev/null +++ b/test/repositories/deep_svn/db/revs/0 @@ -0,0 +1,11 @@ +PLAIN +END +ENDREP +id: 0.0.r0/17 +type: dir +count: 0 +text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e +cpath: / + + +17 107 diff --git a/test/repositories/deep_svn/db/revs/1 b/test/repositories/deep_svn/db/revs/1 new file mode 100644 index 0000000..0569294 --- /dev/null +++ b/test/repositories/deep_svn/db/revs/1 @@ -0,0 +1,122 @@ +DELTA +SVNENDREP +DELTA +SVNENDREP +DELTA +SVNENDREP +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 diff --git a/test/repositories/deep_svn/db/revs/2 b/test/repositories/deep_svn/db/revs/2 new file mode 100644 index 0000000..e7ca0f1 --- /dev/null +++ b/test/repositories/deep_svn/db/revs/2 @@ -0,0 +1,19 @@ +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 diff --git a/test/repositories/deep_svn/db/revs/3 b/test/repositories/deep_svn/db/revs/3 new file mode 100644 index 0000000..b531d59 --- /dev/null +++ b/test/repositories/deep_svn/db/revs/3 @@ -0,0 +1,44 @@ +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 diff --git a/test/repositories/deep_svn/db/revs/4 b/test/repositories/deep_svn/db/revs/4 new file mode 100644 index 0000000..485f33e --- /dev/null +++ b/test/repositories/deep_svn/db/revs/4 @@ -0,0 +1,48 @@ +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 diff --git a/test/repositories/deep_svn/db/uuid b/test/repositories/deep_svn/db/uuid new file mode 100644 index 0000000..7ae337a --- /dev/null +++ b/test/repositories/deep_svn/db/uuid @@ -0,0 +1 @@ +8ab099c8-7f08-4752-91f3-1a221f1f3b2b diff --git a/test/repositories/deep_svn/db/write-lock b/test/repositories/deep_svn/db/write-lock new file mode 100644 index 0000000..e69de29 diff --git a/test/repositories/deep_svn/format b/test/repositories/deep_svn/format new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/test/repositories/deep_svn/format @@ -0,0 +1 @@ +5 diff --git a/test/repositories/deep_svn/hooks/post-commit.tmpl b/test/repositories/deep_svn/hooks/post-commit.tmpl new file mode 100644 index 0000000..b8345c6 --- /dev/null +++ b/test/repositories/deep_svn/hooks/post-commit.tmpl @@ -0,0 +1,51 @@ +#!/bin/sh + +# POST-COMMIT HOOK +# +# The post-commit hook is invoked after a commit. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-commit' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the number of the revision just committed) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# Because the commit has already completed and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# newly-committed tree. +# +# On a Unix system, the normal procedure is to have 'post-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-commit.bat' or 'post-commit.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" + +commit-email.pl "$REPOS" "$REV" commit-watchers@example.org +log-commit.py --repository "$REPOS" --revision "$REV" diff --git a/test/repositories/deep_svn/hooks/post-lock.tmpl b/test/repositories/deep_svn/hooks/post-lock.tmpl new file mode 100644 index 0000000..c779f11 --- /dev/null +++ b/test/repositories/deep_svn/hooks/post-lock.tmpl @@ -0,0 +1,44 @@ +#!/bin/sh + +# POST-LOCK HOOK +# +# The post-lock hook is run after a path is locked. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-lock' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the user who created the lock) +# +# The paths that were just locked are passed to the hook via STDIN (as +# of Subversion 1.2, only one path is passed per invocation, but the +# plan is to pass all locked paths at once, so the hook program +# should be written accordingly). +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# Because the lock has already been created and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# newly-created lock. +# +# On a Unix system, the normal procedure is to have 'post-lock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-lock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-lock.bat' or 'post-lock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +USER="$2" + +# Send email to interested parties, let them know a lock was created: +mailer.py lock "$REPOS" "$USER" /path/to/mailer.conf diff --git a/test/repositories/deep_svn/hooks/post-revprop-change.tmpl b/test/repositories/deep_svn/hooks/post-revprop-change.tmpl new file mode 100644 index 0000000..2ed8b9a --- /dev/null +++ b/test/repositories/deep_svn/hooks/post-revprop-change.tmpl @@ -0,0 +1,56 @@ +#!/bin/sh + +# POST-REVPROP-CHANGE HOOK +# +# The post-revprop-change hook is invoked after a revision property +# has been added, modified or deleted. Subversion runs this hook by +# invoking a program (script, executable, binary, etc.) named +# 'post-revprop-change' (for which this file is a template), with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the revision that was tweaked) +# [3] USER (the username of the person tweaking the property) +# [4] PROPNAME (the property that was changed) +# [5] ACTION (the property was 'A'dded, 'M'odified, or 'D'eleted) +# +# [STDIN] PROPVAL ** the old property value is passed via STDIN. +# +# Because the propchange has already completed and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# new property value. +# +# On a Unix system, the normal procedure is to have 'post-revprop-change' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-revprop-change' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-revprop-change.bat' or 'post-revprop-change.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" +USER="$3" +PROPNAME="$4" +ACTION="$5" + +propchange-email.pl "$REPOS" "$REV" "$USER" "$PROPNAME" watchers@example.org diff --git a/test/repositories/deep_svn/hooks/post-unlock.tmpl b/test/repositories/deep_svn/hooks/post-unlock.tmpl new file mode 100644 index 0000000..ae95c4b --- /dev/null +++ b/test/repositories/deep_svn/hooks/post-unlock.tmpl @@ -0,0 +1,42 @@ +#!/bin/sh + +# POST-UNLOCK HOOK +# +# The post-unlock hook runs after a path is unlocked. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-unlock' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the user who destroyed the lock) +# +# The paths that were just unlocked are passed to the hook via STDIN +# (as of Subversion 1.2, only one path is passed per invocation, but +# the plan is to pass all unlocked paths at once, so the hook program +# should be written accordingly). +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# Because the lock has already been destroyed and cannot be undone, +# the exit code of the hook program is ignored. +# +# On a Unix system, the normal procedure is to have 'post-unlock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-unlock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-unlock.bat' or 'post-unlock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +USER="$2" + +# Send email to interested parties, let them know a lock was removed: +mailer.py unlock "$REPOS" "$USER" /path/to/mailer.conf diff --git a/test/repositories/deep_svn/hooks/pre-commit.tmpl b/test/repositories/deep_svn/hooks/pre-commit.tmpl new file mode 100644 index 0000000..7444c51 --- /dev/null +++ b/test/repositories/deep_svn/hooks/pre-commit.tmpl @@ -0,0 +1,70 @@ +#!/bin/sh + +# PRE-COMMIT HOOK +# +# The pre-commit hook is invoked before a Subversion txn is +# committed. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-commit' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] TXN-NAME (the name of the txn about to be committed) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the txn is committed; but +# if it exits with failure (non-zero), the txn is aborted, no commit +# takes place, and STDERR is returned to the client. The hook +# program can use the 'svnlook' utility to help it examine the txn. +# +# On a Unix system, the normal procedure is to have 'pre-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT *** +# *** FOR REVISION PROPERTIES (like svn:log or svn:author). *** +# +# This is why we recommend using the read-only 'svnlook' utility. +# In the future, Subversion may enforce the rule that pre-commit +# hooks should not modify the versioned data in txns, or else come +# up with a mechanism to make it safe to do so (by informing the +# committing client of the changes). However, right now neither +# mechanism is implemented, so hook writers just have to be careful. +# +# Note that 'pre-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-commit.bat' or 'pre-commit.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +TXN="$2" + +# Make sure that the log message contains some text. +SVNLOOK=/opt/local/bin/svnlook +$SVNLOOK log -t "$TXN" "$REPOS" | \ + grep "[a-zA-Z0-9]" > /dev/null || exit 1 + +# Check that the author of this commit has the rights to perform +# the commit on the files and directories being modified. +commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/test/repositories/deep_svn/hooks/pre-lock.tmpl b/test/repositories/deep_svn/hooks/pre-lock.tmpl new file mode 100644 index 0000000..020d172 --- /dev/null +++ b/test/repositories/deep_svn/hooks/pre-lock.tmpl @@ -0,0 +1,64 @@ +#!/bin/sh + +# PRE-LOCK HOOK +# +# The pre-lock hook is invoked before an exclusive lock is +# created. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-lock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be locked) +# [3] USER (the user creating the lock) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the lock is created; but +# if it exits with failure (non-zero), the lock action is aborted +# and STDERR is returned to the client. + +# On a Unix system, the normal procedure is to have 'pre-lock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-lock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-lock.bat' or 'pre-lock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +PATH="$2" +USER="$3" + +# If a lock exists and is owned by a different person, don't allow it +# to be stolen (e.g., with 'svn lock --force ...'). + +# (Maybe this script could send email to the lock owner?) +SVNLOOK=/opt/local/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, allow the lock to +# happen: +if [ "$LOCK_OWNER" = "" ]; then + exit 0 +fi + +# If the person locking matches the lock's owner, allow the lock to +# happen: +if [ "$LOCK_OWNER" = "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH already locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/test/repositories/deep_svn/hooks/pre-revprop-change.tmpl b/test/repositories/deep_svn/hooks/pre-revprop-change.tmpl new file mode 100644 index 0000000..2f2de98 --- /dev/null +++ b/test/repositories/deep_svn/hooks/pre-revprop-change.tmpl @@ -0,0 +1,66 @@ +#!/bin/sh + +# PRE-REVPROP-CHANGE HOOK +# +# The pre-revprop-change hook is invoked before a revision property +# is added, modified or deleted. Subversion runs this hook by invoking +# a program (script, executable, binary, etc.) named 'pre-revprop-change' +# (for which this file is a template), with the following ordered +# arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] 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 diff --git a/test/repositories/deep_svn/hooks/pre-unlock.tmpl b/test/repositories/deep_svn/hooks/pre-unlock.tmpl new file mode 100644 index 0000000..795f55f --- /dev/null +++ b/test/repositories/deep_svn/hooks/pre-unlock.tmpl @@ -0,0 +1,60 @@ +#!/bin/sh + +# PRE-UNLOCK HOOK +# +# The pre-unlock hook is invoked before an exclusive lock is +# destroyed. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-unlock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be unlocked) +# [3] USER (the user destroying the lock) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the lock is destroyed; but +# if it exits with failure (non-zero), the unlock action is aborted +# and STDERR is returned to the client. + +# On a Unix system, the normal procedure is to have 'pre-unlock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-unlock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-unlock.bat' or 'pre-unlock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +PATH="$2" +USER="$3" + +# If a lock is owned by a different person, don't allow it be broken. +# (Maybe this script could send email to the lock owner?) + +SVNLOOK=/opt/local/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, return success: +if [ "$LOCK_OWNER" = "" ]; then + exit 0 +fi +# If the person unlocking matches the lock's owner, return success: +if [ "$LOCK_OWNER" = "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/test/repositories/deep_svn/hooks/start-commit.tmpl b/test/repositories/deep_svn/hooks/start-commit.tmpl new file mode 100644 index 0000000..348d706 --- /dev/null +++ b/test/repositories/deep_svn/hooks/start-commit.tmpl @@ -0,0 +1,54 @@ +#!/bin/sh + +# START-COMMIT HOOK +# +# The start-commit hook is invoked before a Subversion txn is created +# in the process of doing a commit. Subversion runs this hook +# by invoking a program (script, executable, binary, etc.) named +# 'start-commit' (for which this file is a template) +# with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the authenticated user attempting to commit) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the commit continues; but +# if it exits with failure (non-zero), the commit is stopped before +# a Subversion txn is created, and STDERR is returned to the client. +# +# On a Unix system, the normal procedure is to have 'start-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'start-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'start-commit.bat' or 'start-commit.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +USER="$2" + +commit-allower.pl --repository "$REPOS" --user "$USER" || exit 1 +special-auth-check.py --user "$USER" --auth-level 3 || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/test/repositories/deep_svn/locks/db-logs.lock b/test/repositories/deep_svn/locks/db-logs.lock new file mode 100644 index 0000000..20dd636 --- /dev/null +++ b/test/repositories/deep_svn/locks/db-logs.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/test/repositories/deep_svn/locks/db.lock b/test/repositories/deep_svn/locks/db.lock new file mode 100644 index 0000000..20dd636 --- /dev/null +++ b/test/repositories/deep_svn/locks/db.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/test/repositories/git.tgz b/test/repositories/git.tgz new file mode 100644 index 0000000..5ff058f Binary files /dev/null and b/test/repositories/git.tgz differ diff --git a/test/repositories/svn/README.txt b/test/repositories/svn/README.txt new file mode 100644 index 0000000..3bf5a57 --- /dev/null +++ b/test/repositories/svn/README.txt @@ -0,0 +1,5 @@ +This is a Subversion repository; use the 'svnadmin' tool to examine +it. Do not add, delete, or modify files here unless you know how +to avoid corrupting the repository. + +Visit http://subversion.tigris.org/ for more information. diff --git a/test/repositories/svn/conf/authz b/test/repositories/svn/conf/authz new file mode 100644 index 0000000..29272af --- /dev/null +++ b/test/repositories/svn/conf/authz @@ -0,0 +1,21 @@ +### This file is an example authorization file for svnserve. +### Its format is identical to that of mod_authz_svn authorization +### files. +### As shown below each section defines authorizations for the path and +### (optional) repository specified by the section name. +### The authorizations follow. An authorization line can refer to a +### single user, to a group of users defined in a special [groups] +### section, or to anyone using the '*' wildcard. Each definition can +### grant read ('r') access, read-write ('rw') access, or no access +### (''). + +# [groups] +# harry_and_sally = harry,sally + +# [/foo/bar] +# harry = rw +# * = + +# [repository:/baz/fuz] +# @harry_and_sally = rw +# * = r diff --git a/test/repositories/svn/conf/passwd b/test/repositories/svn/conf/passwd new file mode 100644 index 0000000..f18ac15 --- /dev/null +++ b/test/repositories/svn/conf/passwd @@ -0,0 +1,8 @@ +### This file is an example password file for svnserve. +### Its format is similar to that of svnserve.conf. As shown in the +### example below it contains one section labelled [users]. +### The name and password for each user follow, one account per line. + +# [users] +# harry = harryssecret +# sally = sallyssecret diff --git a/test/repositories/svn/conf/svnserve.conf b/test/repositories/svn/conf/svnserve.conf new file mode 100644 index 0000000..41d9e94 --- /dev/null +++ b/test/repositories/svn/conf/svnserve.conf @@ -0,0 +1,30 @@ +### This file controls the configuration of the svnserve daemon, if you +### use it to allow access to this repository. (If you only allow +### access through http: and/or file: URLs, then this file is +### irrelevant.) + +### Visit http://subversion.tigris.org/ for more information. + +# [general] +### These options control access to the repository for unauthenticated +### and authenticated users. Valid values are "write", "read", +### and "none". The sample settings below are the defaults. +# anon-access = read +# auth-access = write +### The password-db option controls the location of the password +### database file. Unless you specify a path starting with a /, +### the file's location is relative to the conf directory. +### Uncomment the line below to use the default password file. +# password-db = passwd +### The authz-db option controls the location of the authorization +### rules for path-based access control. Unless you specify a path +### starting with a /, the file's location is relative to the conf +### directory. If you don't specify an authz-db, no path-based access +### control is done. +### Uncomment the line below to use the default authorization file. +# authz-db = authz +### This option specifies the authentication realm of the repository. +### If two repositories have the same authentication realm, they should +### have the same password database, and vice versa. The default realm +### is repository's uuid. +# realm = My First Repository diff --git a/test/repositories/svn/db/current b/test/repositories/svn/db/current new file mode 100644 index 0000000..1331681 --- /dev/null +++ b/test/repositories/svn/db/current @@ -0,0 +1 @@ +5 8 2 diff --git a/test/repositories/svn/db/format b/test/repositories/svn/db/format new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/repositories/svn/db/format @@ -0,0 +1 @@ +1 diff --git a/test/repositories/svn/db/fs-type b/test/repositories/svn/db/fs-type new file mode 100644 index 0000000..4fdd953 --- /dev/null +++ b/test/repositories/svn/db/fs-type @@ -0,0 +1 @@ +fsfs diff --git a/test/repositories/svn/db/revprops/0 b/test/repositories/svn/db/revprops/0 new file mode 100644 index 0000000..853fc3c --- /dev/null +++ b/test/repositories/svn/db/revprops/0 @@ -0,0 +1,5 @@ +K 8 +svn:date +V 27 +2006-06-11T18:24:23.808631Z +END diff --git a/test/repositories/svn/db/revprops/1 b/test/repositories/svn/db/revprops/1 new file mode 100644 index 0000000..dcbcbe1 --- /dev/null +++ b/test/repositories/svn/db/revprops/1 @@ -0,0 +1,14 @@ +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 diff --git a/test/repositories/svn/db/revprops/2 b/test/repositories/svn/db/revprops/2 new file mode 100644 index 0000000..5446866 --- /dev/null +++ b/test/repositories/svn/db/revprops/2 @@ -0,0 +1,13 @@ +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 diff --git a/test/repositories/svn/db/revprops/3 b/test/repositories/svn/db/revprops/3 new file mode 100644 index 0000000..50cf1d8 --- /dev/null +++ b/test/repositories/svn/db/revprops/3 @@ -0,0 +1,13 @@ +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 diff --git a/test/repositories/svn/db/revprops/4 b/test/repositories/svn/db/revprops/4 new file mode 100644 index 0000000..1ba88ed --- /dev/null +++ b/test/repositories/svn/db/revprops/4 @@ -0,0 +1,13 @@ +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 diff --git a/test/repositories/svn/db/revprops/5 b/test/repositories/svn/db/revprops/5 new file mode 100644 index 0000000..b9b64b3 --- /dev/null +++ b/test/repositories/svn/db/revprops/5 @@ -0,0 +1,13 @@ +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 diff --git a/test/repositories/svn/db/revs/0 b/test/repositories/svn/db/revs/0 new file mode 100644 index 0000000..10f5c45 --- /dev/null +++ b/test/repositories/svn/db/revs/0 @@ -0,0 +1,11 @@ +PLAIN +END +ENDREP +id: 0.0.r0/17 +type: dir +count: 0 +text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e +cpath: / + + +17 107 diff --git a/test/repositories/svn/db/revs/1 b/test/repositories/svn/db/revs/1 new file mode 100644 index 0000000..c2aa3a8 Binary files /dev/null and b/test/repositories/svn/db/revs/1 differ diff --git a/test/repositories/svn/db/revs/2 b/test/repositories/svn/db/revs/2 new file mode 100644 index 0000000..ff63af3 Binary files /dev/null and b/test/repositories/svn/db/revs/2 differ diff --git a/test/repositories/svn/db/revs/3 b/test/repositories/svn/db/revs/3 new file mode 100644 index 0000000..b33e8a7 Binary files /dev/null and b/test/repositories/svn/db/revs/3 differ diff --git a/test/repositories/svn/db/revs/4 b/test/repositories/svn/db/revs/4 new file mode 100644 index 0000000..4933dbf Binary files /dev/null and b/test/repositories/svn/db/revs/4 differ diff --git a/test/repositories/svn/db/revs/5 b/test/repositories/svn/db/revs/5 new file mode 100644 index 0000000..e7d1c73 --- /dev/null +++ b/test/repositories/svn/db/revs/5 @@ -0,0 +1,64 @@ +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 diff --git a/test/repositories/svn/db/uuid b/test/repositories/svn/db/uuid new file mode 100644 index 0000000..5d2ddb4 --- /dev/null +++ b/test/repositories/svn/db/uuid @@ -0,0 +1 @@ +6a9cefd4-a008-4d2a-a89b-d77e99cd6eb1 diff --git a/test/repositories/svn/db/write-lock b/test/repositories/svn/db/write-lock new file mode 100644 index 0000000..e69de29 diff --git a/test/repositories/svn/format b/test/repositories/svn/format new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/test/repositories/svn/format @@ -0,0 +1 @@ +3 diff --git a/test/repositories/svn/hooks/post-commit.tmpl b/test/repositories/svn/hooks/post-commit.tmpl new file mode 100644 index 0000000..b8345c6 --- /dev/null +++ b/test/repositories/svn/hooks/post-commit.tmpl @@ -0,0 +1,51 @@ +#!/bin/sh + +# POST-COMMIT HOOK +# +# The post-commit hook is invoked after a commit. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-commit' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the number of the revision just committed) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# Because the commit has already completed and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# newly-committed tree. +# +# On a Unix system, the normal procedure is to have 'post-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-commit.bat' or 'post-commit.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" + +commit-email.pl "$REPOS" "$REV" commit-watchers@example.org +log-commit.py --repository "$REPOS" --revision "$REV" diff --git a/test/repositories/svn/hooks/post-lock.tmpl b/test/repositories/svn/hooks/post-lock.tmpl new file mode 100644 index 0000000..c779f11 --- /dev/null +++ b/test/repositories/svn/hooks/post-lock.tmpl @@ -0,0 +1,44 @@ +#!/bin/sh + +# POST-LOCK HOOK +# +# The post-lock hook is run after a path is locked. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-lock' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the user who created the lock) +# +# The paths that were just locked are passed to the hook via STDIN (as +# of Subversion 1.2, only one path is passed per invocation, but the +# plan is to pass all locked paths at once, so the hook program +# should be written accordingly). +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# Because the lock has already been created and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# newly-created lock. +# +# On a Unix system, the normal procedure is to have 'post-lock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-lock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-lock.bat' or 'post-lock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +USER="$2" + +# Send email to interested parties, let them know a lock was created: +mailer.py lock "$REPOS" "$USER" /path/to/mailer.conf diff --git a/test/repositories/svn/hooks/post-revprop-change.tmpl b/test/repositories/svn/hooks/post-revprop-change.tmpl new file mode 100644 index 0000000..2ed8b9a --- /dev/null +++ b/test/repositories/svn/hooks/post-revprop-change.tmpl @@ -0,0 +1,56 @@ +#!/bin/sh + +# POST-REVPROP-CHANGE HOOK +# +# The post-revprop-change hook is invoked after a revision property +# has been added, modified or deleted. Subversion runs this hook by +# invoking a program (script, executable, binary, etc.) named +# 'post-revprop-change' (for which this file is a template), with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the revision that was tweaked) +# [3] USER (the username of the person tweaking the property) +# [4] PROPNAME (the property that was changed) +# [5] ACTION (the property was 'A'dded, 'M'odified, or 'D'eleted) +# +# [STDIN] PROPVAL ** the old property value is passed via STDIN. +# +# Because the propchange has already completed and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# new property value. +# +# On a Unix system, the normal procedure is to have 'post-revprop-change' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-revprop-change' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-revprop-change.bat' or 'post-revprop-change.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" +USER="$3" +PROPNAME="$4" +ACTION="$5" + +propchange-email.pl "$REPOS" "$REV" "$USER" "$PROPNAME" watchers@example.org diff --git a/test/repositories/svn/hooks/post-unlock.tmpl b/test/repositories/svn/hooks/post-unlock.tmpl new file mode 100644 index 0000000..ae95c4b --- /dev/null +++ b/test/repositories/svn/hooks/post-unlock.tmpl @@ -0,0 +1,42 @@ +#!/bin/sh + +# POST-UNLOCK HOOK +# +# The post-unlock hook runs after a path is unlocked. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-unlock' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the user who destroyed the lock) +# +# The paths that were just unlocked are passed to the hook via STDIN +# (as of Subversion 1.2, only one path is passed per invocation, but +# the plan is to pass all unlocked paths at once, so the hook program +# should be written accordingly). +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# Because the lock has already been destroyed and cannot be undone, +# the exit code of the hook program is ignored. +# +# On a Unix system, the normal procedure is to have 'post-unlock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-unlock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-unlock.bat' or 'post-unlock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +USER="$2" + +# Send email to interested parties, let them know a lock was removed: +mailer.py unlock "$REPOS" "$USER" /path/to/mailer.conf diff --git a/test/repositories/svn/hooks/pre-commit.tmpl b/test/repositories/svn/hooks/pre-commit.tmpl new file mode 100644 index 0000000..7444c51 --- /dev/null +++ b/test/repositories/svn/hooks/pre-commit.tmpl @@ -0,0 +1,70 @@ +#!/bin/sh + +# PRE-COMMIT HOOK +# +# The pre-commit hook is invoked before a Subversion txn is +# committed. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-commit' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] TXN-NAME (the name of the txn about to be committed) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the txn is committed; but +# if it exits with failure (non-zero), the txn is aborted, no commit +# takes place, and STDERR is returned to the client. The hook +# program can use the 'svnlook' utility to help it examine the txn. +# +# On a Unix system, the normal procedure is to have 'pre-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT *** +# *** FOR REVISION PROPERTIES (like svn:log or svn:author). *** +# +# This is why we recommend using the read-only 'svnlook' utility. +# In the future, Subversion may enforce the rule that pre-commit +# hooks should not modify the versioned data in txns, or else come +# up with a mechanism to make it safe to do so (by informing the +# committing client of the changes). However, right now neither +# mechanism is implemented, so hook writers just have to be careful. +# +# Note that 'pre-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-commit.bat' or 'pre-commit.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +TXN="$2" + +# Make sure that the log message contains some text. +SVNLOOK=/opt/local/bin/svnlook +$SVNLOOK log -t "$TXN" "$REPOS" | \ + grep "[a-zA-Z0-9]" > /dev/null || exit 1 + +# Check that the author of this commit has the rights to perform +# the commit on the files and directories being modified. +commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/test/repositories/svn/hooks/pre-lock.tmpl b/test/repositories/svn/hooks/pre-lock.tmpl new file mode 100644 index 0000000..2f86bc0 --- /dev/null +++ b/test/repositories/svn/hooks/pre-lock.tmpl @@ -0,0 +1,64 @@ +#!/bin/sh + +# PRE-LOCK HOOK +# +# The pre-lock hook is invoked before an exclusive lock is +# created. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-lock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be locked) +# [3] USER (the user creating the lock) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the lock is created; but +# if it exits with failure (non-zero), the lock action is aborted +# and STDERR is returned to the client. + +# On a Unix system, the normal procedure is to have 'pre-lock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-lock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-lock.bat' or 'pre-lock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +PATH="$2" +USER="$3" + +# If a lock exists and is owned by a different person, don't allow it +# to be stolen (e.g., with 'svn lock --force ...'). + +# (Maybe this script could send email to the lock owner?) +SVNLOOK=/opt/local/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, allow the lock to +# happen: +if [ "$LOCK_OWNER" == "" ]; then + exit 0 +fi + +# If the person locking matches the lock's owner, allow the lock to +# happen: +if [ "$LOCK_OWNER" == "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH already locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/test/repositories/svn/hooks/pre-revprop-change.tmpl b/test/repositories/svn/hooks/pre-revprop-change.tmpl new file mode 100644 index 0000000..2f2de98 --- /dev/null +++ b/test/repositories/svn/hooks/pre-revprop-change.tmpl @@ -0,0 +1,66 @@ +#!/bin/sh + +# PRE-REVPROP-CHANGE HOOK +# +# The pre-revprop-change hook is invoked before a revision property +# is added, modified or deleted. Subversion runs this hook by invoking +# a program (script, executable, binary, etc.) named 'pre-revprop-change' +# (for which this file is a template), with the following ordered +# arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] 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 diff --git a/test/repositories/svn/hooks/pre-unlock.tmpl b/test/repositories/svn/hooks/pre-unlock.tmpl new file mode 100644 index 0000000..209feff --- /dev/null +++ b/test/repositories/svn/hooks/pre-unlock.tmpl @@ -0,0 +1,60 @@ +#!/bin/sh + +# PRE-UNLOCK HOOK +# +# The pre-unlock hook is invoked before an exclusive lock is +# destroyed. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-unlock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be unlocked) +# [3] USER (the user destroying the lock) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the lock is destroyed; but +# if it exits with failure (non-zero), the unlock action is aborted +# and STDERR is returned to the client. + +# On a Unix system, the normal procedure is to have 'pre-unlock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-unlock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-unlock.bat' or 'pre-unlock.exe', +# but the basic idea is the same. +# +# Here is an example hook script, for a Unix /bin/sh interpreter: + +REPOS="$1" +PATH="$2" +USER="$3" + +# If a lock is owned by a different person, don't allow it be broken. +# (Maybe this script could send email to the lock owner?) + +SVNLOOK=/opt/local/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, return success: +if [ "$LOCK_OWNER" == "" ]; then + exit 0 +fi +# If the person unlocking matches the lock's owner, return success: +if [ "$LOCK_OWNER" == "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/test/repositories/svn/hooks/start-commit.tmpl b/test/repositories/svn/hooks/start-commit.tmpl new file mode 100644 index 0000000..348d706 --- /dev/null +++ b/test/repositories/svn/hooks/start-commit.tmpl @@ -0,0 +1,54 @@ +#!/bin/sh + +# START-COMMIT HOOK +# +# The start-commit hook is invoked before a Subversion txn is created +# in the process of doing a commit. Subversion runs this hook +# by invoking a program (script, executable, binary, etc.) named +# 'start-commit' (for which this file is a template) +# with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the authenticated user attempting to commit) +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# If the hook program exits with success, the commit continues; but +# if it exits with failure (non-zero), the commit is stopped before +# a Subversion txn is created, and STDERR is returned to the client. +# +# On a Unix system, the normal procedure is to have 'start-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'start-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'start-commit.bat' or 'start-commit.exe', +# but the basic idea is the same. +# +# The hook program typically does not inherit the environment of +# its parent process. For example, a common problem is for the +# PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/ and +# http://svn.collab.net/repos/svn/trunk/contrib/hook-scripts/ + + +REPOS="$1" +USER="$2" + +commit-allower.pl --repository "$REPOS" --user "$USER" || exit 1 +special-auth-check.py --user "$USER" --auth-level 3 || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/test/repositories/svn/locks/db-logs.lock b/test/repositories/svn/locks/db-logs.lock new file mode 100644 index 0000000..20dd636 --- /dev/null +++ b/test/repositories/svn/locks/db-logs.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/test/repositories/svn/locks/db.lock b/test/repositories/svn/locks/db.lock new file mode 100644 index 0000000..20dd636 --- /dev/null +++ b/test/repositories/svn/locks/db.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..485617b --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,84 @@ +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 diff --git a/test/unit/abstract_adapter_test.rb b/test/unit/abstract_adapter_test.rb new file mode 100644 index 0000000..b637255 --- /dev/null +++ b/test/unit/abstract_adapter_test.rb @@ -0,0 +1,72 @@ +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 diff --git a/test/unit/array_writer_test.rb b/test/unit/array_writer_test.rb new file mode 100644 index 0000000..f8b1686 --- /dev/null +++ b/test/unit/array_writer_test.rb @@ -0,0 +1,33 @@ +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 diff --git a/test/unit/cvs_branch_number_test.rb b/test/unit/cvs_branch_number_test.rb new file mode 100644 index 0000000..272cc8d --- /dev/null +++ b/test/unit/cvs_branch_number_test.rb @@ -0,0 +1,166 @@ +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 diff --git a/test/unit/cvs_commits_test.rb b/test/unit/cvs_commits_test.rb new file mode 100644 index 0000000..83cb55f --- /dev/null +++ b/test/unit/cvs_commits_test.rb @@ -0,0 +1,27 @@ +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 diff --git a/test/unit/cvs_convert_test.rb b/test/unit/cvs_convert_test.rb new file mode 100644 index 0000000..9a3da40 --- /dev/null +++ b/test/unit/cvs_convert_test.rb @@ -0,0 +1,30 @@ +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 diff --git a/test/unit/cvs_misc_test.rb b/test/unit/cvs_misc_test.rb new file mode 100644 index 0000000..59e7ece --- /dev/null +++ b/test/unit/cvs_misc_test.rb @@ -0,0 +1,49 @@ +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 diff --git a/test/unit/cvs_parser_test.rb b/test/unit/cvs_parser_test.rb new file mode 100644 index 0000000..ac3ae67 --- /dev/null +++ b/test/unit/cvs_parser_test.rb @@ -0,0 +1,94 @@ +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 diff --git a/test/unit/cvs_validation_test.rb b/test/unit/cvs_validation_test.rb new file mode 100644 index 0000000..c3fe9bc --- /dev/null +++ b/test/unit/cvs_validation_test.rb @@ -0,0 +1,140 @@ +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 diff --git a/test/unit/git_cat_file_test.rb b/test/unit/git_cat_file_test.rb new file mode 100644 index 0000000..f122e0c --- /dev/null +++ b/test/unit/git_cat_file_test.rb @@ -0,0 +1,21 @@ +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 diff --git a/test/unit/git_commit_all_test.rb b/test/unit/git_commit_all_test.rb new file mode 100644 index 0000000..0fdb111 --- /dev/null +++ b/test/unit/git_commit_all_test.rb @@ -0,0 +1,34 @@ +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 diff --git a/test/unit/git_commits_test.rb b/test/unit/git_commits_test.rb new file mode 100644 index 0000000..dc2c8ae --- /dev/null +++ b/test/unit/git_commits_test.rb @@ -0,0 +1,35 @@ +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 diff --git a/test/unit/git_log_parser_test.rb b/test/unit/git_log_parser_test.rb new file mode 100644 index 0000000..3712bc5 --- /dev/null +++ b/test/unit/git_log_parser_test.rb @@ -0,0 +1,213 @@ +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 diff --git a/test/unit/git_misc_test.rb b/test/unit/git_misc_test.rb new file mode 100644 index 0000000..4d0c138 --- /dev/null +++ b/test/unit/git_misc_test.rb @@ -0,0 +1,25 @@ +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 diff --git a/test/unit/git_pull_test.rb b/test/unit/git_pull_test.rb new file mode 100644 index 0000000..e39e210 --- /dev/null +++ b/test/unit/git_pull_test.rb @@ -0,0 +1,22 @@ +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 diff --git a/test/unit/git_push_test.rb b/test/unit/git_push_test.rb new file mode 100644 index 0000000..209830c --- /dev/null +++ b/test/unit/git_push_test.rb @@ -0,0 +1,47 @@ +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 diff --git a/test/unit/git_token_test.rb b/test/unit/git_token_test.rb new file mode 100644 index 0000000..b237050 --- /dev/null +++ b/test/unit/git_token_test.rb @@ -0,0 +1,37 @@ +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 diff --git a/test/unit/git_validation_test.rb b/test/unit/git_validation_test.rb new file mode 100644 index 0000000..a404675 --- /dev/null +++ b/test/unit/git_validation_test.rb @@ -0,0 +1,41 @@ +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 diff --git a/test/unit/ohlog_command_line_test.rb b/test/unit/ohlog_command_line_test.rb new file mode 100644 index 0000000..2619373 --- /dev/null +++ b/test/unit/ohlog_command_line_test.rb @@ -0,0 +1,33 @@ +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 diff --git a/test/unit/svn_cat_file_test.rb b/test/unit/svn_cat_file_test.rb new file mode 100644 index 0000000..38f665e --- /dev/null +++ b/test/unit/svn_cat_file_test.rb @@ -0,0 +1,21 @@ +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 diff --git a/test/unit/svn_commits_test.rb b/test/unit/svn_commits_test.rb new file mode 100644 index 0000000..5e9c503 --- /dev/null +++ b/test/unit/svn_commits_test.rb @@ -0,0 +1,232 @@ +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 diff --git a/test/unit/svn_convert_test.rb b/test/unit/svn_convert_test.rb new file mode 100644 index 0000000..b7c01ac --- /dev/null +++ b/test/unit/svn_convert_test.rb @@ -0,0 +1,28 @@ +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 diff --git a/test/unit/svn_misc_test.rb b/test/unit/svn_misc_test.rb new file mode 100644 index 0000000..3fd5fe2 --- /dev/null +++ b/test/unit/svn_misc_test.rb @@ -0,0 +1,59 @@ +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 diff --git a/test/unit/svn_parser_test.rb b/test/unit/svn_parser_test.rb new file mode 100644 index 0000000..ffcaa0b --- /dev/null +++ b/test/unit/svn_parser_test.rb @@ -0,0 +1,137 @@ +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 diff --git a/test/unit/svn_pull_test.rb b/test/unit/svn_pull_test.rb new file mode 100644 index 0000000..61de105 --- /dev/null +++ b/test/unit/svn_pull_test.rb @@ -0,0 +1,59 @@ +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 diff --git a/test/unit/svn_push_test.rb b/test/unit/svn_push_test.rb new file mode 100644 index 0000000..e945e22 --- /dev/null +++ b/test/unit/svn_push_test.rb @@ -0,0 +1,40 @@ +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 diff --git a/test/unit/svn_validation_test.rb b/test/unit/svn_validation_test.rb new file mode 100644 index 0000000..966bdad --- /dev/null +++ b/test/unit/svn_validation_test.rb @@ -0,0 +1,97 @@ +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 diff --git a/test/unit/svn_xml_parser_test.rb b/test/unit/svn_xml_parser_test.rb new file mode 100644 index 0000000..de1f12d --- /dev/null +++ b/test/unit/svn_xml_parser_test.rb @@ -0,0 +1,19 @@ +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