To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / app / models / repository / cvs.rb @ 441:cbce1fd3b1b7

History | View | Annotate | Download (7.47 KB)

1 441:cbce1fd3b1b7 Chris
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 0:513646585e45 Chris
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8 441:cbce1fd3b1b7 Chris
#
9 0:513646585e45 Chris
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13 441:cbce1fd3b1b7 Chris
#
14 0:513646585e45 Chris
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17
18
require 'redmine/scm/adapters/cvs_adapter'
19
require 'digest/sha1'
20
21
class Repository::Cvs < Repository
22 245:051f544170fe Chris
  validates_presence_of :url, :root_url, :log_encoding
23 0:513646585e45 Chris
24 245:051f544170fe Chris
  def self.human_attribute_name(attribute_key_name)
25 441:cbce1fd3b1b7 Chris
    attr_name = attribute_key_name
26
    if attr_name == "root_url"
27
      attr_name = "cvsroot"
28
    elsif attr_name == "url"
29
      attr_name = "cvs_module"
30
    end
31
    super(attr_name)
32 245:051f544170fe Chris
  end
33
34
  def self.scm_adapter_class
35 0:513646585e45 Chris
    Redmine::Scm::Adapters::CvsAdapter
36
  end
37 245:051f544170fe Chris
38 0:513646585e45 Chris
  def self.scm_name
39
    'CVS'
40
  end
41 245:051f544170fe Chris
42 0:513646585e45 Chris
  def entry(path=nil, identifier=nil)
43
    rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
44
    scm.entry(path, rev.nil? ? nil : rev.committed_on)
45
  end
46 441:cbce1fd3b1b7 Chris
47 0:513646585e45 Chris
  def entries(path=nil, identifier=nil)
48 441:cbce1fd3b1b7 Chris
    rev = nil
49
    if ! identifier.nil?
50
      rev = changesets.find_by_revision(identifier)
51
      return nil if rev.nil?
52
    end
53 0:513646585e45 Chris
    entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
54
    if entries
55
      entries.each() do |entry|
56 441:cbce1fd3b1b7 Chris
        if ( ! entry.lastrev.nil? ) && ( ! entry.lastrev.revision.nil? )
57
          change=changes.find_by_revision_and_path(
58
                     entry.lastrev.revision,
59
                     scm.with_leading_slash(entry.path) )
60 0:513646585e45 Chris
          if change
61 441:cbce1fd3b1b7 Chris
            entry.lastrev.identifier = change.changeset.revision
62
            entry.lastrev.revision   = change.changeset.revision
63
            entry.lastrev.author     = change.changeset.committer
64
            # entry.lastrev.branch     = change.branch
65 0:513646585e45 Chris
          end
66
        end
67
      end
68
    end
69
    entries
70
  end
71 441:cbce1fd3b1b7 Chris
72 0:513646585e45 Chris
  def cat(path, identifier=nil)
73 441:cbce1fd3b1b7 Chris
    rev = nil
74
    if ! identifier.nil?
75
      rev = changesets.find_by_revision(identifier)
76
      return nil if rev.nil?
77
    end
78 0:513646585e45 Chris
    scm.cat(path, rev.nil? ? nil : rev.committed_on)
79
  end
80 441:cbce1fd3b1b7 Chris
81
  def annotate(path, identifier=nil)
82
    rev = nil
83
    if ! identifier.nil?
84
      rev = changesets.find_by_revision(identifier)
85
      return nil if rev.nil?
86
    end
87
    scm.annotate(path, rev.nil? ? nil : rev.committed_on)
88
  end
89
90 0:513646585e45 Chris
  def diff(path, rev, rev_to)
91 441:cbce1fd3b1b7 Chris
    # convert rev to revision. CVS can't handle changesets here
92 0:513646585e45 Chris
    diff=[]
93 441:cbce1fd3b1b7 Chris
    changeset_from = changesets.find_by_revision(rev)
94
    if rev_to.to_i > 0
95
      changeset_to = changesets.find_by_revision(rev_to)
96 0:513646585e45 Chris
    end
97
    changeset_from.changes.each() do |change_from|
98 441:cbce1fd3b1b7 Chris
      revision_from = nil
99
      revision_to   = nil
100
      if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
101
        revision_from = change_from.revision
102
      end
103 0:513646585e45 Chris
      if revision_from
104
        if changeset_to
105
          changeset_to.changes.each() do |change_to|
106 441:cbce1fd3b1b7 Chris
            revision_to = change_to.revision if change_to.path == change_from.path
107 0:513646585e45 Chris
          end
108
        end
109
        unless revision_to
110 441:cbce1fd3b1b7 Chris
          revision_to = scm.get_previous_revision(revision_from)
111 0:513646585e45 Chris
        end
112
        file_diff = scm.diff(change_from.path, revision_from, revision_to)
113
        diff = diff + file_diff unless file_diff.nil?
114
      end
115
    end
116
    return diff
117
  end
118 441:cbce1fd3b1b7 Chris
119 0:513646585e45 Chris
  def fetch_changesets
120
    # some nifty bits to introduce a commit-id with cvs
121 441:cbce1fd3b1b7 Chris
    # natively cvs doesn't provide any kind of changesets,
122
    # there is only a revision per file.
123 0:513646585e45 Chris
    # we now take a guess using the author, the commitlog and the commit-date.
124 441:cbce1fd3b1b7 Chris
125
    # last one is the next step to take. the commit-date is not equal for all
126 0:513646585e45 Chris
    # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
127
    # we use a small delta here, to merge all changes belonging to _one_ changeset
128 441:cbce1fd3b1b7 Chris
    time_delta  = 10.seconds
129 0:513646585e45 Chris
    fetch_since = latest_changeset ? latest_changeset.committed_on : nil
130
    transaction do
131
      tmp_rev_num = 1
132 441:cbce1fd3b1b7 Chris
      scm.revisions('', fetch_since, nil, :log_encoding => repo_log_encoding) do |revision|
133 0:513646585e45 Chris
        # only add the change to the database, if it doen't exists. the cvs log
134 441:cbce1fd3b1b7 Chris
        # is not exclusive at all.
135 210:0579821a129a Chris
        tmp_time = revision.time.clone
136
        unless changes.find_by_path_and_revision(
137 441:cbce1fd3b1b7 Chris
                                 scm.with_leading_slash(revision.paths[0][:path]),
138
                                 revision.paths[0][:revision]
139
                                   )
140 245:051f544170fe Chris
          cmt = Changeset.normalize_comments(revision.message, repo_log_encoding)
141 441:cbce1fd3b1b7 Chris
          author_utf8 = Changeset.to_utf8(revision.author, repo_log_encoding)
142
          cs  = changesets.find(
143
            :first,
144
            :conditions => {
145
                :committed_on => tmp_time - time_delta .. tmp_time + time_delta,
146
                :committer    => author_utf8,
147
                :comments     => cmt
148
                }
149
             )
150
          # create a new changeset....
151 0:513646585e45 Chris
          unless cs
152
            # we use a temporaray revision number here (just for inserting)
153
            # later on, we calculate a continous positive number
154 210:0579821a129a Chris
            tmp_time2 = tmp_time.clone.gmtime
155 441:cbce1fd3b1b7 Chris
            branch    = revision.paths[0][:branch]
156
            scmid     = branch + "-" + tmp_time2.strftime("%Y%m%d-%H%M%S")
157
            cs = Changeset.create(:repository   => self,
158
                                  :revision     => "tmp#{tmp_rev_num}",
159
                                  :scmid        => scmid,
160
                                  :committer    => revision.author,
161 210:0579821a129a Chris
                                  :committed_on => tmp_time,
162 441:cbce1fd3b1b7 Chris
                                  :comments     => revision.message)
163 0:513646585e45 Chris
            tmp_rev_num += 1
164
          end
165 441:cbce1fd3b1b7 Chris
          # convert CVS-File-States to internal Action-abbrevations
166
          # default action is (M)odified
167
          action = "M"
168
          if revision.paths[0][:action] == "Exp" && revision.paths[0][:revision] == "1.1"
169
            action = "A" # add-action always at first revision (= 1.1)
170
          elsif revision.paths[0][:action] == "dead"
171
            action = "D" # dead-state is similar to Delete
172 0:513646585e45 Chris
          end
173 441:cbce1fd3b1b7 Chris
          Change.create(
174
             :changeset => cs,
175
             :action    => action,
176
             :path      => scm.with_leading_slash(revision.paths[0][:path]),
177
             :revision  => revision.paths[0][:revision],
178
             :branch    => revision.paths[0][:branch]
179
              )
180 0:513646585e45 Chris
        end
181
      end
182 441:cbce1fd3b1b7 Chris
183 0:513646585e45 Chris
      # Renumber new changesets in chronological order
184 210:0579821a129a Chris
      changesets.find(
185 441:cbce1fd3b1b7 Chris
              :all,
186
              :order => 'committed_on ASC, id ASC',
187
              :conditions => "revision LIKE 'tmp%'"
188 210:0579821a129a Chris
           ).each do |changeset|
189 0:513646585e45 Chris
        changeset.update_attribute :revision, next_revision_number
190
      end
191
    end # transaction
192 210:0579821a129a Chris
    @current_revision_number = nil
193 0:513646585e45 Chris
  end
194 441:cbce1fd3b1b7 Chris
195 0:513646585e45 Chris
  private
196 441:cbce1fd3b1b7 Chris
197 0:513646585e45 Chris
  # Returns the next revision number to assign to a CVS changeset
198
  def next_revision_number
199
    # Need to retrieve existing revision numbers to sort them as integers
200 210:0579821a129a Chris
    sql = "SELECT revision FROM #{Changeset.table_name} "
201
    sql << "WHERE repository_id = #{id} AND revision NOT LIKE 'tmp%'"
202
    @current_revision_number ||= (connection.select_values(sql).collect(&:to_i).max || 0)
203 0:513646585e45 Chris
    @current_revision_number += 1
204
  end
205
end