summaryrefslogtreecommitdiffstats
path: root/app/models/repository/cvs.rb
blob: d6d4ed317f5661ec2760272ccfe5e088ddc3e160 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# 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)
    entries=scm.entries(path, identifier)
    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
    #not the preferred way with CVS. maybe we should introduce always a cron-job for this
    last_commit = changesets.maximum(:committed_on)
    
    # 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
    
    transaction do
      scm.revisions('', last_commit, nil, :with_paths => true) do |revision|
        # 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
          cs=Changeset.find(:first, :conditions=>{
            :committed_on=>revision.time-time_delta..revision.time+time_delta,
            :committer=>revision.author,
            :comments=>revision.message
          })
        
          # create a new changeset.... 
          unless cs 
            # we use a negative changeset-number here (just for inserting)
            # later on, we calculate a continous positive number
            next_rev = changesets.minimum(:revision)            
            next_rev = 0 if next_rev.nil? or next_rev > 0 
            next_rev = next_rev - 1
            
            cs=Changeset.create(:repository => self,
            :revision => next_rev, 
            :committer => revision.author, 
            :committed_on => revision.time,
            :comments => revision.message)
          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
      
      next_rev = [changesets.maximum(:revision) || 0, 0].max
      changesets.find(:all, :conditions=>["revision < 0"], :order=>"committed_on ASC").each() do |changeset|
        next_rev = next_rev + 1
        changeset.revision = next_rev
        changeset.save!
      end
    end
  end
end