lib/rscm/revision.rb in rscm-0.4.5 vs lib/rscm/revision.rb in rscm-0.5.0

- old
+ new

@@ -1,206 +1,103 @@ require 'rscm/time_ext' require 'rscm/revision_file' +require 'yaml' module RSCM - - # A collection of Revision. - class Revisions + # Represents a collection of RevisionFile that were committed at the + # same time, or "more or less at the same time" for non-atomic + # SCMs (such as CVS and StarTeam). See Revisions for how to emulate + # atomicity for non-atomic SCMs. + class Revision include Enumerable - attr_accessor :revisions - - def initialize(revisions=[]) - @revisions = revisions - end + attr_writer :identifier + attr_accessor :developer + attr_accessor :message - # Accepts a visitor that will receive callbacks while - # iterating over this instance's internal structure. - # The visitor should respond to the following methods: - # - # * visit_revisions(revisions) - # * visit_revision(revision) - # * visit_file(file) - # - def accept(visitor) - visitor.visit_revisions(self) - self.each{|revision| revision.accept(visitor)} + def initialize(identifier=nil, time=nil) + @identifier = identifier + @time = time + @files = [] end - def [](file) - @revisions[file] + def add(file) + raise "Can't add #{file} to this revision" unless accept? file + @files << file + self.developer = file.developer if file.developer + self.message = file.message if file.message end - - def each(&block) - @revisions.each(&block) - end - def reverse - r = clone - r.revisions = @revisions.dup.reverse - r + def identifier(min_or_max = :max) + @identifier || time(min_or_max) end - def length - @revisions.length + # The time of this revision. Depending on the value of +min_or_max+, + # (should be :min or :max), returns the min or max time of this + # revision. (min or max only matters for non-transactional scms) + def time(min_or_max = :max) + @time || self.collect{|file| file.time}.__send__(min_or_max) end - def ==(other) - return false if !other.is_a?(self.class) - @revisions == other.revisions + # Sets the time for this revision. Should only be used by atomic SCMs. + # Non-atomic SCMs should <b>not</b> invoke this method, but instead create + # revisions by adding RscmFile objects to a Revisions object. + def time=(t) + raise "time must be a Time object - it was a #{t.class.name} with the string value #{t}" unless t.is_a?(Time) + raise "can't set time to an inferiour value than the previous value" if @time && (t < @time) + @time = t end - def empty? - @revisions.empty? - end - - # The set of developers that contributed to all of the contained Revision s. - def developers - result = [] - each do |revision| - result << revision.developer unless result.index(revision.developer) - end - result - end - - def first - @revisions.first - end - - def last - @revisions.last - end - - # The latest Revision (with the latest time) - # or nil if there are none. - def latest - result = nil - each do |revision| - result = revision if result.nil? || result.time < revision.time - end - result - end + # Whether +file+ can be added to this instance. + def accept?(file) #:nodoc: + return true if empty? || @time - # Adds a File or a Revision. - # If the argument is a File and no corresponding Revision exists, - # a new Revision is created, added, and the File is added to that Revision - - # and then finally the newly created Revision is returned. - # Otherwise nil is returned. - def add(file_or_revision) - if(file_or_revision.is_a?(Revision)) - @revisions << file_or_revision - return file_or_revision - else - revision = @revisions.find { |a_revision| a_revision.can_contain?(file_or_revision) } - if(revision.nil?) - revision = Revision.new - @revisions << revision - revision << file_or_revision - return revision - end - revision << file_or_revision - return nil - end - end - - def push(*file_or_revisions) - file_or_revisions.each { |file_or_revision| self << (file_or_revision) } - self - end + close_enough_to_min = (time(:min) - file.time).abs <= 60 + close_enough_to_max = (time(:max) - file.time).abs <= 60 + close_enough = close_enough_to_min or close_enough_to_max - # Sorts the revisions according to time - def sort! - @revisions.sort! - self + close_enough and + self.developer == file.developer and + self.message == file.message end - end - - # Represents a collection of File that were committed at the same time. - # Non-transactional SCMs (such as CVS and StarTeam) emulate Revision - # by grouping File s that were committed by the same developer, with the - # same commit message, and within a "reasonably" small timespan. - class Revision - include Enumerable - - attr_reader :files - attr_accessor :identifier - attr_accessor :developer - attr_accessor :message - attr_accessor :time - - def initialize(files=[]) - @files = files + def ==(other) + self.to_s == other.to_s end - - def accept(visitor) - visitor.visit_revision(self) - @files.each{|file| file.accept(visitor)} - end - def << (file) - @files << file - if(self.time.nil? || self.time < file.time unless file.time.nil?) - self.time = file.time - self.identifier = self.time if(self.identifier.nil? || self.identifier.is_a?(Time)) + # String representation that can be used for debugging. + def to_s + if(@to_s.nil?) + min = time(:min) + max = time(:max) + t = (min==max) ? min : "#{min}-#{max}" + @to_s = "#{identifier} | #{developer} | #{t} | #{message}\n" + self.each do |file| + @to_s << " " << file.to_s << "\n" + end + @to_s end - self.developer = file.developer if file.developer - self.message = file.message if file.message + @to_s end - def [] (index) - @files[index] - end - - # Iterates over all the RevisionFile objects def each(&block) @files.each(&block) end - - def pop - @files.pop + + def [](n) + @files[n] end - + def length @files.length end - alias :size :length - def time=(t) - raise "time must be a Time object - it was a #{t.class.name} with the string value #{t}" unless t.is_a?(Time) - raise "can't set time to an inferiour value than the previous value" if @time && (t < @time) - @time = t + def pop + @files.pop end - - def ==(other) - other.is_a?(self.class) && - @developer == other.developer && - @identifier == other.identifier && - @message == other.message && - @time == other.time && - @files == other.files - end - def <=>(other) - @time <=> other.time + def empty? + @files.empty? end - # Whether this instance can contain a File. Used - # by non-transactional SCMs. - def can_contain?(file) #:nodoc: - self.developer == file.developer && - self.message == file.message && - (self.time - file.time).abs < 60 - end - - # String representation that can be used for debugging. - def to_s - result = "#{identifier} | #{developer} | #{time} | #{message}\n" - self.each do |file| - result << " " << file.to_s << "\n" - end - result - end - end - end