= cloudrcs
Get the code at:
http://github.com/hallettj/cloudrcs/tree/master
File bugs at:
http://hallettj.lighthouseapp.com/projects/11392/home
== DESCRIPTION:
CloudRCS is a revision control system implemented in pure Ruby and
based on darcs. It can be used as plugin for Ruby on Rails; or it can
be used without Rails, provided ActiveRecord is available.
== FEATURES/PROBLEMS:
CloudRCS is based on darcs; thus employing the same powerful theory of
patches that darcs does. It aims for darcs interoperability wherever
possible. However, I plan to implement features that are not available
with darcs, such as the ability to check out a subdirectory of a
repository, and the ability to provide partial access to a repository
according to a permissions system.
CloudRCS can be easily extended with new patch types. To introduce a
new patch type, it is only necessary to define a subclass of
CloudRCS::PrimitivePatch that implements the methods of PrmitivePatch
that indicate that they should be overridden.
CloudRCS is not yet complete. Patch types for manipulating directories
have not been implemented; so currently CloudRCS is limited flat
repositories. However, binary patches have recently been implemented;
and patch types for directories should follow shortly.
In addition, the merge algorithm is implemented and working - but it
is not quite finished. It will fail in certain cases where multiple
pushes are performed without any intervening pulls.
Finally, there is no intelligent support for resolving conflicts
yet. If a conflict is encountered, CloudRCS will raise an exception.
== SYNOPSIS:
CloudRCS includes a number of ActiveRecord models that represent named
and primitive patches. Add the line `require 'cloudrcs'` to the
environment.rb file of a Rails application, and those models will be
made available in the application automatically.
To generate a patch representing the changes made to any file, call
CloudRCS::Patch.generate(orig_file, changed_file) - where changed_file
is the file object in question, and orig_file is an unchanged version
of the same file. The generate method will return an unsaved instance
of CloudRCS::Patch.
To output a patch in darcs-compatible format, call
patch.gzipped_contents. That will yield the contents of a patch file
as a string compressed using gzip. The darcs format also specifies the
filename for any patch file. The appropriate file name can be
generated by calling patch.file_name. This gzipped format is handy for
sending the patch along to any other repository.
When receiving a patch file, the file can be translated into CloudRCS
objects by calling CloudRCS::Patch.parse(contents) - where contents
should be the contents of a patch file. The contents may be gzipped,
or in clear text. The parse method will return an unsaved
CloudRCS::Patch instance.
To apply any patch to your repository, call patch.apply! Any changes
represented by the patch will be applied and saved to the database. If
you want to apply the patch to a particular file without saving the
changes, then call patch.apply_to(file) - which will return a file
object with the unsaved changes.
The CloudRCS models are namespaced. So instead of making calls to
Patch, you will have to make calls to CloudRCS::Patch. If you want to
bypass the namespacing, you can create a model in your Rails
application called patch.rb that contains this line:
Patch = CloudRCS::Patch
You can use a name other than Patch if you want to, as long as the
name of the file and the constant name match.
Defining such a model in your application also allows you to extend
CloudRCS. For example, if your application will be handling multiple
repositories and you want to differentiate the repositories by user,
then define a Patch model that looks something like this:
Patch = CloudRCS::Patch
class Patch
belongs_to :user
validates_presence_of :user_id
end
class CloudRCS::PrimitivePatch
def locate_file(path)
MyAppFileClass.locate(path, :owner => patch.owner)
end
def self.file_class
MyAppFileClass
end
end
The extension to CloudRCS::PrimitivePatch informs CloudRCS of how to
locate files in your application. It also informs CloudRCS what class
you are using to represent files.
CloudRCS is currently not capable of accessing files using Ruby's File
class. It expects whatever file_class you use to implement the
following methods:
path : Return the path of the file relative to the repository root
path=(new_path) : Moves the file to new_path
contents : Returns the contents of the file as a string
contents=(c) : Sets the file's contents to c
= Writing new patch types
CloudRCS uses darcs' notion of named and primitive patches. A named
patch contains a header with the author of the patch, the date it was
created, and a description of the changes it describes. Named patches
can contain multiple changes affecting any number of files. They are
represented in CloudRCS by the class, CloudRCS::Patch.
A primitive patch represents a single change to a single file. The
could be the creation, movement, or deletion of a file. It could also
be a single changed hunk of a text file. CloudRCS represents primitive
patches with the class, CloudRCS::PrimitivePatch.
New patch types come in the form of new primitive patches types. To
define a primitive patch type, create a subclass of
CloudRCS::PrimitivePatch. The new class must override the following
methods:
apply_to(file) : Given a file object, applies the changes described by
the patch and returns the modified file. If file is
an instance of an ActiveRecord model, the changes
should not be saved by apply_to. Saving changes to
the database will be handled by the apply! method.
inverse : Returns the inverse of the patch. And inverse patch
completely undoes the effects of the original patch. So for
example, the inverse of a move from foo to bar is a move
from bar to foo.
Primitive patches have an attribute, `inverted`, that must
be set to true when a patch is generated as an inverse.
commute(patch) : Given another prmitive patch, generates the commuted
versions of the two patches. This is critical to
applying the theory of patches. Commuted patches
perform the same changes as the the originals, but
are applied in the reversed order. The commute method
assumes that the receiver is applied before the given
patch. So it returns [patch_prime, self_prime], where
patch_prime is the commuted version of the other
patch, and self_prime is the commuted version of the
receiver.
The commuted patches must be modified from the
originals so that they apply cleanly after being
reordered. For example, hunk patches operate on a
given line number within a file by removing lines
from that position, and then adding lines to the same
position. If two hunk patches that affect the same
file are commuted, the line numbers must be updated
in the commuted versions. If patch removes two lines
starting at line 3, and self adds to lines starting
at line 10, then when they are commuted those line
numbers will no longer be appropriate. Since
patch_prime will be aplied first, the position was
previously indicated as line 10 gets shifted up by
two lines and becomes line 8. So self_prime has to
operate on line 8 instead of line 10.
to_s : Returns a representation of the patch as a string for outptting
to a patch file. The string representation must begin with a
token representing the type of the patch, which is the
lowercase of the class name. So the token representing
CloudRCS::Hunk is 'hunk'.
The string representation must contain any patch type tokens at
the beginning of any line after the first. Beginning lines with
a non-alphanumeric character like '+', '-', or a space is a
good way to ensure that this restriction is respected.
In addition to the above instance methods, the patch type must also
override these class methods:
generate(orig_file, changed_file) :
Given a changed file and an unchanged version of the same file,
this method returns a patch that represents one change between the
two files, or an array of patches that represent one change
each. It is not necessary for the patches returned to represent
all of the changes between the two files - only the changes that
are well described by this patch type.
If there is no change between the two files that is well described
by this patch type, then this method should return nil or an empty
array.
Note that either orig_file or changed_file may be nil. If
orig_file is nil, then that represents the creation of a new
file. And when changed_file is nil, that represents a deletion.
After one or more patches have been generated, they should be
applied to orig_file using apply_to. This will prevent some other
patch type that is called later from generating another patch that
describes the same changes.
When CloudRCS::Patch.generate is invoked, it will call the
generate method for each primitive patch class in turn. After
that, the generated patches applied to orig_file should match
changed_file exactly.
parse(contents) :
Given a string representation of this patch type, parse returns a
new instance of this class with the same information contained in
the string representation. parse must be able to parse any string
representation outputted by the to_s method, and any output
generated by to_s must be parsable by parse.
You can also optionally define a class method called priority. This
method should return an integer. When CloudRCS::Patch.generate is
called, it will call the generate method of each patch type in turn,
ordered by the values returned by the priority method of each patch
type. So if your patch type is likely to conflict with other patch
types if is applied early in the process, then set its priority method
to a high value. Alternately, if it is important that the patch type
be processed early, then assign it a low priority.
To provide an example, addfile patches should always be generated
before hunks, which should be generated before rmfile. So addfile has
a low priority of 10, hunk has the default priority of 50, and rmfile
has a high priority of 90.
Finally, to inform CloudRCS of the existence of the new patch type,
this line must be executed:
CloudRCS::PATCH_TYPES << NewPatchType
where NewPatchType should be replaced with the new class.
In the CloudRCS gem, patch types are kept under
lib/cloud_rcs/patch_types/.
== REQUIREMENTS:
* activerecord
* diff-lcs
== ADDITIONAL BUILD-TIME REQUIREMENTS:
* hoe
* newgem
* trowel
== INSTALL FROM SOURCE:
sudo rake install_gem
== LICENSE:
(The MIT License)
Copyright (c) 2008 Jesse Hallett <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Get the code at:
http://github.com/hallettj/cloudrcs/tree/master
File bugs at:
http://hallettj.lighthouseapp.com/projects/11392/home
== DESCRIPTION:
CloudRCS is a revision control system implemented in pure Ruby and
based on darcs. It can be used as plugin for Ruby on Rails; or it can
be used without Rails, provided ActiveRecord is available.
== FEATURES/PROBLEMS:
CloudRCS is based on darcs; thus employing the same powerful theory of
patches that darcs does. It aims for darcs interoperability wherever
possible. However, I plan to implement features that are not available
with darcs, such as the ability to check out a subdirectory of a
repository, and the ability to provide partial access to a repository
according to a permissions system.
CloudRCS can be easily extended with new patch types. To introduce a
new patch type, it is only necessary to define a subclass of
CloudRCS::PrimitivePatch that implements the methods of PrmitivePatch
that indicate that they should be overridden.
CloudRCS is not yet complete. Patch types for manipulating directories
have not been implemented; so currently CloudRCS is limited flat
repositories. However, binary patches have recently been implemented;
and patch types for directories should follow shortly.
In addition, the merge algorithm is implemented and working - but it
is not quite finished. It will fail in certain cases where multiple
pushes are performed without any intervening pulls.
Finally, there is no intelligent support for resolving conflicts
yet. If a conflict is encountered, CloudRCS will raise an exception.
== SYNOPSIS:
CloudRCS includes a number of ActiveRecord models that represent named
and primitive patches. Add the line `require 'cloudrcs'` to the
environment.rb file of a Rails application, and those models will be
made available in the application automatically.
To generate a patch representing the changes made to any file, call
CloudRCS::Patch.generate(orig_file, changed_file) - where changed_file
is the file object in question, and orig_file is an unchanged version
of the same file. The generate method will return an unsaved instance
of CloudRCS::Patch.
To output a patch in darcs-compatible format, call
patch.gzipped_contents. That will yield the contents of a patch file
as a string compressed using gzip. The darcs format also specifies the
filename for any patch file. The appropriate file name can be
generated by calling patch.file_name. This gzipped format is handy for
sending the patch along to any other repository.
When receiving a patch file, the file can be translated into CloudRCS
objects by calling CloudRCS::Patch.parse(contents) - where contents
should be the contents of a patch file. The contents may be gzipped,
or in clear text. The parse method will return an unsaved
CloudRCS::Patch instance.
To apply any patch to your repository, call patch.apply! Any changes
represented by the patch will be applied and saved to the database. If
you want to apply the patch to a particular file without saving the
changes, then call patch.apply_to(file) - which will return a file
object with the unsaved changes.
The CloudRCS models are namespaced. So instead of making calls to
Patch, you will have to make calls to CloudRCS::Patch. If you want to
bypass the namespacing, you can create a model in your Rails
application called patch.rb that contains this line:
Patch = CloudRCS::Patch
You can use a name other than Patch if you want to, as long as the
name of the file and the constant name match.
Defining such a model in your application also allows you to extend
CloudRCS. For example, if your application will be handling multiple
repositories and you want to differentiate the repositories by user,
then define a Patch model that looks something like this:
Patch = CloudRCS::Patch
class Patch
belongs_to :user
validates_presence_of :user_id
end
class CloudRCS::PrimitivePatch
def locate_file(path)
MyAppFileClass.locate(path, :owner => patch.owner)
end
def self.file_class
MyAppFileClass
end
end
The extension to CloudRCS::PrimitivePatch informs CloudRCS of how to
locate files in your application. It also informs CloudRCS what class
you are using to represent files.
CloudRCS is currently not capable of accessing files using Ruby's File
class. It expects whatever file_class you use to implement the
following methods:
path : Return the path of the file relative to the repository root
path=(new_path) : Moves the file to new_path
contents : Returns the contents of the file as a string
contents=(c) : Sets the file's contents to c
= Writing new patch types
CloudRCS uses darcs' notion of named and primitive patches. A named
patch contains a header with the author of the patch, the date it was
created, and a description of the changes it describes. Named patches
can contain multiple changes affecting any number of files. They are
represented in CloudRCS by the class, CloudRCS::Patch.
A primitive patch represents a single change to a single file. The
could be the creation, movement, or deletion of a file. It could also
be a single changed hunk of a text file. CloudRCS represents primitive
patches with the class, CloudRCS::PrimitivePatch.
New patch types come in the form of new primitive patches types. To
define a primitive patch type, create a subclass of
CloudRCS::PrimitivePatch. The new class must override the following
methods:
apply_to(file) : Given a file object, applies the changes described by
the patch and returns the modified file. If file is
an instance of an ActiveRecord model, the changes
should not be saved by apply_to. Saving changes to
the database will be handled by the apply! method.
inverse : Returns the inverse of the patch. And inverse patch
completely undoes the effects of the original patch. So for
example, the inverse of a move from foo to bar is a move
from bar to foo.
Primitive patches have an attribute, `inverted`, that must
be set to true when a patch is generated as an inverse.
commute(patch) : Given another prmitive patch, generates the commuted
versions of the two patches. This is critical to
applying the theory of patches. Commuted patches
perform the same changes as the the originals, but
are applied in the reversed order. The commute method
assumes that the receiver is applied before the given
patch. So it returns [patch_prime, self_prime], where
patch_prime is the commuted version of the other
patch, and self_prime is the commuted version of the
receiver.
The commuted patches must be modified from the
originals so that they apply cleanly after being
reordered. For example, hunk patches operate on a
given line number within a file by removing lines
from that position, and then adding lines to the same
position. If two hunk patches that affect the same
file are commuted, the line numbers must be updated
in the commuted versions. If patch removes two lines
starting at line 3, and self adds to lines starting
at line 10, then when they are commuted those line
numbers will no longer be appropriate. Since
patch_prime will be aplied first, the position was
previously indicated as line 10 gets shifted up by
two lines and becomes line 8. So self_prime has to
operate on line 8 instead of line 10.
to_s : Returns a representation of the patch as a string for outptting
to a patch file. The string representation must begin with a
token representing the type of the patch, which is the
lowercase of the class name. So the token representing
CloudRCS::Hunk is 'hunk'.
The string representation must contain any patch type tokens at
the beginning of any line after the first. Beginning lines with
a non-alphanumeric character like '+', '-', or a space is a
good way to ensure that this restriction is respected.
In addition to the above instance methods, the patch type must also
override these class methods:
generate(orig_file, changed_file) :
Given a changed file and an unchanged version of the same file,
this method returns a patch that represents one change between the
two files, or an array of patches that represent one change
each. It is not necessary for the patches returned to represent
all of the changes between the two files - only the changes that
are well described by this patch type.
If there is no change between the two files that is well described
by this patch type, then this method should return nil or an empty
array.
Note that either orig_file or changed_file may be nil. If
orig_file is nil, then that represents the creation of a new
file. And when changed_file is nil, that represents a deletion.
After one or more patches have been generated, they should be
applied to orig_file using apply_to. This will prevent some other
patch type that is called later from generating another patch that
describes the same changes.
When CloudRCS::Patch.generate is invoked, it will call the
generate method for each primitive patch class in turn. After
that, the generated patches applied to orig_file should match
changed_file exactly.
parse(contents) :
Given a string representation of this patch type, parse returns a
new instance of this class with the same information contained in
the string representation. parse must be able to parse any string
representation outputted by the to_s method, and any output
generated by to_s must be parsable by parse.
You can also optionally define a class method called priority. This
method should return an integer. When CloudRCS::Patch.generate is
called, it will call the generate method of each patch type in turn,
ordered by the values returned by the priority method of each patch
type. So if your patch type is likely to conflict with other patch
types if is applied early in the process, then set its priority method
to a high value. Alternately, if it is important that the patch type
be processed early, then assign it a low priority.
To provide an example, addfile patches should always be generated
before hunks, which should be generated before rmfile. So addfile has
a low priority of 10, hunk has the default priority of 50, and rmfile
has a high priority of 90.
Finally, to inform CloudRCS of the existence of the new patch type,
this line must be executed:
CloudRCS::PATCH_TYPES << NewPatchType
where NewPatchType should be replaced with the new class.
In the CloudRCS gem, patch types are kept under
lib/cloud_rcs/patch_types/.
== REQUIREMENTS:
* activerecord
* diff-lcs
== ADDITIONAL BUILD-TIME REQUIREMENTS:
* hoe
* newgem
* trowel
== INSTALL FROM SOURCE:
sudo rake install_gem
== LICENSE:
(The MIT License)
Copyright (c) 2008 Jesse Hallett <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.