2007-06-13 00:12:05 +04:00
# redMine - project management software
# Copyright (C) 2006-2007 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
def scm_adapter
Redmine :: Scm :: Adapters :: CvsAdapter
end
def self . scm_name
'CVS'
end
def entry ( path , identifier )
e = entries ( path , identifier )
e ? e . first : nil
end
def entries ( path = nil , identifier = nil )
2008-04-06 14:35:55 +04:00
rev = identifier . nil? ? nil : changesets . find_by_revision ( identifier )
entries = scm . entries ( path , rev . nil? ? nil : rev . committed_on )
2007-06-13 00:12:05 +04:00
if entries
entries . each ( ) do | entry |
unless entry . lastrev . nil? || entry . lastrev . identifier
change = changes . find_by_revision_and_path ( entry . lastrev . revision , scm . with_leading_slash ( entry . path ) )
if change
entry . lastrev . identifier = change . changeset . revision
entry . lastrev . author = change . changeset . committer
entry . lastrev . revision = change . revision
entry . lastrev . branch = change . branch
end
end
end
end
entries
end
def diff ( path , rev , rev_to , type )
#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 . changes . each ( ) do | change_from |
revision_from = nil
revision_to = nil
revision_from = change_from . revision if path . nil? || ( change_from . path . starts_with? scm . with_leading_slash ( path ) )
if revision_from
if changeset_to
changeset_to . changes . 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
diff = diff + scm . diff ( change_from . path , revision_from , revision_to , type )
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
2008-03-12 23:28:49 +03:00
fetch_since = latest_changeset ? latest_changeset . committed_on : nil
2007-06-13 00:12:05 +04:00
transaction do
2008-03-12 23:28:49 +03:00
tmp_rev_num = 1
scm . revisions ( '' , fetch_since , nil , :with_paths = > true ) do | revision |
2007-06-13 00:12:05 +04:00
# only add the change to the database, if it doen't exists. the cvs log
# is not exclusive at all.
unless changes . find_by_path_and_revision ( scm . with_leading_slash ( revision . paths [ 0 ] [ :path ] ) , revision . paths [ 0 ] [ :revision ] )
revision
2007-08-01 21:53:05 +04:00
cs = changesets . find ( :first , :conditions = > {
2007-06-13 00:12:05 +04:00
:committed_on = > revision . time - time_delta .. revision . time + time_delta ,
:committer = > revision . author ,
:comments = > revision . message
} )
# create a new changeset....
2008-03-12 23:28:49 +03:00
unless cs
# we use a temporaray revision number here (just for inserting)
2007-06-13 00:12:05 +04:00
# later on, we calculate a continous positive number
2008-03-12 23:28:49 +03:00
latest = changesets . find ( :first , :order = > 'id DESC' )
cs = Changeset . create ( :repository = > self ,
:revision = > " _ #{ tmp_rev_num } " ,
:committer = > revision . author ,
:committed_on = > revision . time ,
:comments = > revision . message )
tmp_rev_num += 1
2007-06-13 00:12:05 +04:00
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
2008-03-12 23:28:49 +03:00
# Renumber new changesets in chronological order
changesets . find ( :all , :order = > 'committed_on ASC, id ASC' , :conditions = > " revision LIKE '_%' " ) . each do | changeset |
2008-04-24 21:28:55 +04:00
changeset . update_attribute :revision , next_revision_number
2007-06-13 00:12:05 +04:00
end
2008-03-12 23:28:49 +03:00
end # transaction
2007-06-13 00:12:05 +04:00
end
2008-04-24 21:28:55 +04:00
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
@current_revision_number || = ( connection . select_values ( " SELECT revision FROM #{ Changeset . table_name } WHERE repository_id = #{ id } AND revision NOT LIKE '_%' " ) . collect ( & :to_i ) . max || 0 )
@current_revision_number += 1
end
2007-06-13 00:12:05 +04:00
end