redmine / app / models / repository / cvs.rb

Toshi MARUYAMA 8afd5b4 
jplang 11d9ce3 
jplang 1dd9ae2 




Toshi MARUYAMA 8afd5b4 
jplang 1dd9ae2 



Toshi MARUYAMA 8afd5b4 
jplang 1dd9ae2 







Toshi MARUYAMA 12be6f1 
jplang 1dd9ae2 
jplang 6d53baf 

jplang e55f7e6 
jplang 172a561 
Toshi MARUYAMA ac15238 
Toshi MARUYAMA 47dae3c 




jplang 172a561 
Toshi MARUYAMA 16619d0 

Toshi MARUYAMA c164578 
jplang 1dd9ae2 

Toshi MARUYAMA c164578 
jplang 1dd9ae2 


Toshi MARUYAMA c164578 
jplang 034f9d0 


jplang 1dd9ae2 
Toshi MARUYAMA f9e7fb6 
jplang 1dd9ae2 
Toshi MARUYAMA f9e7fb6 




jplang 5cdd191 
jplang 1dd9ae2 

Toshi MARUYAMA aa72838 
jplang 51db2d1 
Toshi MARUYAMA 8737d3a 

jplang 1dd9ae2 
Toshi MARUYAMA 8737d3a 
Toshi MARUYAMA aa72838 
Toshi MARUYAMA 8737d3a 
Toshi MARUYAMA aa72838 
jplang 1dd9ae2 



jplang 5ca19f4 
jplang 1dd9ae2 

Toshi MARUYAMA 1e0b834 
jplang 034f9d0 
Toshi MARUYAMA 7446879 




jplang 034f9d0 

Toshi MARUYAMA 1e0b834 









jplang 8f4b79c 
Toshi MARUYAMA 60abed4 
jplang 1dd9ae2 
Toshi MARUYAMA 60abed4 
Toshi MARUYAMA 8afd5b4 
Toshi MARUYAMA 60abed4 
jplang 1dd9ae2 
jplang 51db2d1 
Toshi MARUYAMA 24fe0de 
Toshi MARUYAMA 8afd5b4 
Toshi MARUYAMA 24fe0de 
Toshi MARUYAMA 60abed4 
Toshi MARUYAMA 24fe0de 
jplang 1dd9ae2 

jplang 51db2d1 
Toshi MARUYAMA 8afd5b4 
jplang 1dd9ae2 


Toshi MARUYAMA 8afd5b4 
jplang 1dd9ae2 
jplang 8cc13de 

jplang 1dd9ae2 



Toshi MARUYAMA 24fe0de 
jplang 1dd9ae2 

Toshi MARUYAMA 24fe0de 

jplang 1dd9ae2 
Toshi MARUYAMA 8afd5b4 

jplang 1dd9ae2 

Toshi MARUYAMA 6468ad3 
jplang 6ae10de 
jplang 1dd9ae2 
jplang 6ae10de 
Toshi MARUYAMA 32e8a26 
jplang 1dd9ae2 
Toshi MARUYAMA 8afd5b4 
Toshi MARUYAMA f893a93 
jplang 51db2d1 
Toshi MARUYAMA 6468ad3 


Toshi MARUYAMA 153e2c1 
Toshi MARUYAMA 9744e89 
Toshi MARUYAMA 6468ad3 



Toshi MARUYAMA 9744e89 
Toshi MARUYAMA 6468ad3 


Toshi MARUYAMA 8afd5b4 
jplang 6ae10de 

jplang 1dd9ae2 
Toshi MARUYAMA f893a93 
Toshi MARUYAMA 6468ad3 




Toshi MARUYAMA 8afd5b4 
Toshi MARUYAMA f893a93 
Toshi MARUYAMA 6468ad3 
jplang 6ae10de 
jplang 1dd9ae2 
Toshi MARUYAMA 6468ad3 






jplang 1dd9ae2 
Toshi MARUYAMA 24fe0de 






jplang 1dd9ae2 

Toshi MARUYAMA 8afd5b4 
jplang 6ae10de 
jplang fad9ccb 
Toshi MARUYAMA 24fe0de 
jplang fad9ccb 
Toshi MARUYAMA 27bbdb5 
jplang 148a94d 
jplang 1dd9ae2 
jplang 6ae10de 
Toshi MARUYAMA 19d1ab3 
jplang 1dd9ae2 
Toshi MARUYAMA 24fe0de 
jplang 148a94d 
Toshi MARUYAMA 24fe0de 
jplang 148a94d 


Toshi MARUYAMA 19d1ab3 


jplang 148a94d 

jplang 1dd9ae2 
# Redmine - project management software
# Copyright (C) 2006-2012  Jean-Philippe Lang
#
# 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.

require 'redmine/scm/adapters/cvs_adapter'
require 'digest/sha1'

class Repository::Cvs < Repository
  validates_presence_of :url, :root_url, :log_encoding

  safe_attributes 'root_url',
    :if => lambda {|repository, user| repository.new_record?}

  def self.human_attribute_name(attribute_key_name, *args)
    attr_name = attribute_key_name.to_s
    if attr_name == "root_url"
      attr_name = "cvsroot"
    elsif attr_name == "url"
      attr_name = "cvs_module"
    end
    super(attr_name, *args)
  end

  def self.scm_adapter_class
    Redmine::Scm::Adapters::CvsAdapter
  end

  def self.scm_name
    'CVS'
  end

  def entry(path=nil, identifier=nil)
    rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
    scm.entry(path, rev.nil? ? nil : rev.committed_on)
  end

  def entries(path=nil, identifier=nil)
    rev = nil
    if ! identifier.nil?
      rev = changesets.find_by_revision(identifier)
      return nil if rev.nil?
    end
    entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
    if entries
      entries.each() do |entry|
        if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? )
          change = filechanges.find_by_revision_and_path(
                     entry.lastrev.revision,
                     scm.with_leading_slash(entry.path) )
          if change
            entry.lastrev.identifier = change.changeset.revision
            entry.lastrev.revision   = change.changeset.revision
            entry.lastrev.author     = change.changeset.committer
            # entry.lastrev.branch     = change.branch
          end
        end
      end
    end
    load_entries_changesets(entries)
    entries
  end

  def cat(path, identifier=nil)
    rev = nil
    if ! identifier.nil?
      rev = changesets.find_by_revision(identifier)
      return nil if rev.nil?
    end
    scm.cat(path, rev.nil? ? nil : rev.committed_on)
  end

  def annotate(path, identifier=nil)
    rev = nil
    if ! identifier.nil?
      rev = changesets.find_by_revision(identifier)
      return nil if rev.nil?
    end
    scm.annotate(path, rev.nil? ? nil : rev.committed_on)
  end

  def diff(path, rev, rev_to)
    # convert rev to revision. CVS can't handle changesets here
    diff=[]
    changeset_from = changesets.find_by_revision(rev)
    if rev_to.to_i > 0
      changeset_to = changesets.find_by_revision(rev_to)
    end
    changeset_from.filechanges.each() do |change_from|
      revision_from = nil
      revision_to   = nil
      if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
        revision_from = change_from.revision
      end
      if revision_from
        if changeset_to
          changeset_to.filechanges.each() do |change_to|
            revision_to = change_to.revision if change_to.path == change_from.path
          end
        end
        unless revision_to
          revision_to = scm.get_previous_revision(revision_from)
        end
        file_diff = scm.diff(change_from.path, revision_from, revision_to)
        diff = diff + file_diff unless file_diff.nil?
      end
    end
    return diff
  end

  def fetch_changesets
    # some nifty bits to introduce a commit-id with cvs
    # natively cvs doesn't provide any kind of changesets,
    # there is only a revision per file.
    # we now take a guess using the author, the commitlog and the commit-date.

    # last one is the next step to take. the commit-date is not equal for all
    # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
    # we use a small delta here, to merge all changes belonging to _one_ changeset
    time_delta  = 10.seconds
    fetch_since = latest_changeset ? latest_changeset.committed_on : nil
    transaction do
      tmp_rev_num = 1
      scm.revisions('', fetch_since, nil, :log_encoding => repo_log_encoding) do |revision|
        # only add the change to the database, if it doen't exists. the cvs log
        # is not exclusive at all.
        tmp_time = revision.time.clone
        unless filechanges.find_by_path_and_revision(
	                         scm.with_leading_slash(revision.paths[0][:path]),
	                         revision.paths[0][:revision]
	                           )
          cmt = Changeset.normalize_comments(revision.message, repo_log_encoding)
          author_utf8 = Changeset.to_utf8(revision.author, repo_log_encoding)
          cs  = changesets.find(
            :first,
            :conditions => {
                :committed_on => tmp_time - time_delta .. tmp_time + time_delta,
                :committer    => author_utf8,
                :comments     => cmt
                }
             )
          # create a new changeset....
          unless cs
            # we use a temporaray revision number here (just for inserting)
            # later on, we calculate a continous positive number
            tmp_time2 = tmp_time.clone.gmtime
            branch    = revision.paths[0][:branch]
            scmid     = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
            cs = Changeset.create(:repository   => self,
                                  :revision     => "tmp#{tmp_rev_num}",
                                  :scmid        => scmid,
                                  :committer    => revision.author,
                                  :committed_on => tmp_time,
                                  :comments     => revision.message)
            tmp_rev_num += 1
          end
          # convert CVS-File-States to internal Action-abbrevations
          # default action is (M)odified
          action = "M"
          if revision.paths[0][:action] == "Exp" && revision.paths[0][:revision] == "1.1"
            action = "A" # add-action always at first revision (= 1.1)
          elsif revision.paths[0][:action] == "dead"
            action = "D" # dead-state is similar to Delete
          end
          Change.create(
             :changeset => cs,
             :action    => action,
             :path      => scm.with_leading_slash(revision.paths[0][:path]),
             :revision  => revision.paths[0][:revision],
             :branch    => revision.paths[0][:branch]
              )
        end
      end

      # Renumber new changesets in chronological order
      Changeset.all(
              :order => 'committed_on ASC, id ASC',
              :conditions => ["repository_id = ? AND revision LIKE 'tmp%'", id]
           ).each do |changeset|
        changeset.update_attribute :revision, next_revision_number
      end
    end # transaction
    @current_revision_number = nil
  end

  private

  # Returns the next revision number to assign to a CVS changeset
  def next_revision_number
    # Need to retrieve existing revision numbers to sort them as integers
    sql = "SELECT revision FROM #{Changeset.table_name} "
    sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
    @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
    @current_revision_number += 1
  end
end
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.