##// END OF EJS Templates
CVS duplicate key violation fix (#996, #1098)....
Jean-Philippe Lang -
r1340:76b8d3eff26b
parent child
Show More
@@ -1,149 +1,155
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redmine/scm/adapters/cvs_adapter'
18 require 'redmine/scm/adapters/cvs_adapter'
19 require 'digest/sha1'
19 require 'digest/sha1'
20
20
21 class Repository::Cvs < Repository
21 class Repository::Cvs < Repository
22 validates_presence_of :url, :root_url
22 validates_presence_of :url, :root_url
23
23
24 def scm_adapter
24 def scm_adapter
25 Redmine::Scm::Adapters::CvsAdapter
25 Redmine::Scm::Adapters::CvsAdapter
26 end
26 end
27
27
28 def self.scm_name
28 def self.scm_name
29 'CVS'
29 'CVS'
30 end
30 end
31
31
32 def entry(path, identifier)
32 def entry(path, identifier)
33 e = entries(path, identifier)
33 e = entries(path, identifier)
34 e ? e.first : nil
34 e ? e.first : nil
35 end
35 end
36
36
37 def entries(path=nil, identifier=nil)
37 def entries(path=nil, identifier=nil)
38 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
38 rev = identifier.nil? ? nil : changesets.find_by_revision(identifier)
39 entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
39 entries = scm.entries(path, rev.nil? ? nil : rev.committed_on)
40 if entries
40 if entries
41 entries.each() do |entry|
41 entries.each() do |entry|
42 unless entry.lastrev.nil? || entry.lastrev.identifier
42 unless entry.lastrev.nil? || entry.lastrev.identifier
43 change=changes.find_by_revision_and_path( entry.lastrev.revision, scm.with_leading_slash(entry.path) )
43 change=changes.find_by_revision_and_path( entry.lastrev.revision, scm.with_leading_slash(entry.path) )
44 if change
44 if change
45 entry.lastrev.identifier=change.changeset.revision
45 entry.lastrev.identifier=change.changeset.revision
46 entry.lastrev.author=change.changeset.committer
46 entry.lastrev.author=change.changeset.committer
47 entry.lastrev.revision=change.revision
47 entry.lastrev.revision=change.revision
48 entry.lastrev.branch=change.branch
48 entry.lastrev.branch=change.branch
49 end
49 end
50 end
50 end
51 end
51 end
52 end
52 end
53 entries
53 entries
54 end
54 end
55
55
56 def diff(path, rev, rev_to, type)
56 def diff(path, rev, rev_to, type)
57 #convert rev to revision. CVS can't handle changesets here
57 #convert rev to revision. CVS can't handle changesets here
58 diff=[]
58 diff=[]
59 changeset_from=changesets.find_by_revision(rev)
59 changeset_from=changesets.find_by_revision(rev)
60 if rev_to.to_i > 0
60 if rev_to.to_i > 0
61 changeset_to=changesets.find_by_revision(rev_to)
61 changeset_to=changesets.find_by_revision(rev_to)
62 end
62 end
63 changeset_from.changes.each() do |change_from|
63 changeset_from.changes.each() do |change_from|
64
64
65 revision_from=nil
65 revision_from=nil
66 revision_to=nil
66 revision_to=nil
67
67
68 revision_from=change_from.revision if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
68 revision_from=change_from.revision if path.nil? || (change_from.path.starts_with? scm.with_leading_slash(path))
69
69
70 if revision_from
70 if revision_from
71 if changeset_to
71 if changeset_to
72 changeset_to.changes.each() do |change_to|
72 changeset_to.changes.each() do |change_to|
73 revision_to=change_to.revision if change_to.path==change_from.path
73 revision_to=change_to.revision if change_to.path==change_from.path
74 end
74 end
75 end
75 end
76 unless revision_to
76 unless revision_to
77 revision_to=scm.get_previous_revision(revision_from)
77 revision_to=scm.get_previous_revision(revision_from)
78 end
78 end
79 diff=diff+scm.diff(change_from.path, revision_from, revision_to, type)
79 diff=diff+scm.diff(change_from.path, revision_from, revision_to, type)
80 end
80 end
81 end
81 end
82 return diff
82 return diff
83 end
83 end
84
84
85 def fetch_changesets
85 def fetch_changesets
86 # some nifty bits to introduce a commit-id with cvs
86 # some nifty bits to introduce a commit-id with cvs
87 # natively cvs doesn't provide any kind of changesets, there is only a revision per file.
87 # natively cvs doesn't provide any kind of changesets, there is only a revision per file.
88 # we now take a guess using the author, the commitlog and the commit-date.
88 # we now take a guess using the author, the commitlog and the commit-date.
89
89
90 # last one is the next step to take. the commit-date is not equal for all
90 # last one is the next step to take. the commit-date is not equal for all
91 # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
91 # commits in one changeset. cvs update the commit-date when the *,v file was touched. so
92 # we use a small delta here, to merge all changes belonging to _one_ changeset
92 # we use a small delta here, to merge all changes belonging to _one_ changeset
93 time_delta=10.seconds
93 time_delta=10.seconds
94
94
95 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
95 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
96 transaction do
96 transaction do
97 tmp_rev_num = 1
97 tmp_rev_num = 1
98 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
98 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
99 # only add the change to the database, if it doen't exists. the cvs log
99 # only add the change to the database, if it doen't exists. the cvs log
100 # is not exclusive at all.
100 # is not exclusive at all.
101 unless changes.find_by_path_and_revision(scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
101 unless changes.find_by_path_and_revision(scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
102 revision
102 revision
103 cs = changesets.find(:first, :conditions=>{
103 cs = changesets.find(:first, :conditions=>{
104 :committed_on=>revision.time-time_delta..revision.time+time_delta,
104 :committed_on=>revision.time-time_delta..revision.time+time_delta,
105 :committer=>revision.author,
105 :committer=>revision.author,
106 :comments=>revision.message
106 :comments=>revision.message
107 })
107 })
108
108
109 # create a new changeset....
109 # create a new changeset....
110 unless cs
110 unless cs
111 # we use a temporaray revision number here (just for inserting)
111 # we use a temporaray revision number here (just for inserting)
112 # later on, we calculate a continous positive number
112 # later on, we calculate a continous positive number
113 latest = changesets.find(:first, :order => 'id DESC')
113 latest = changesets.find(:first, :order => 'id DESC')
114 cs = Changeset.create(:repository => self,
114 cs = Changeset.create(:repository => self,
115 :revision => "_#{tmp_rev_num}",
115 :revision => "_#{tmp_rev_num}",
116 :committer => revision.author,
116 :committer => revision.author,
117 :committed_on => revision.time,
117 :committed_on => revision.time,
118 :comments => revision.message)
118 :comments => revision.message)
119 tmp_rev_num += 1
119 tmp_rev_num += 1
120 end
120 end
121
121
122 #convert CVS-File-States to internal Action-abbrevations
122 #convert CVS-File-States to internal Action-abbrevations
123 #default action is (M)odified
123 #default action is (M)odified
124 action="M"
124 action="M"
125 if revision.paths[0][:action]=="Exp" && revision.paths[0][:revision]=="1.1"
125 if revision.paths[0][:action]=="Exp" && revision.paths[0][:revision]=="1.1"
126 action="A" #add-action always at first revision (= 1.1)
126 action="A" #add-action always at first revision (= 1.1)
127 elsif revision.paths[0][:action]=="dead"
127 elsif revision.paths[0][:action]=="dead"
128 action="D" #dead-state is similar to Delete
128 action="D" #dead-state is similar to Delete
129 end
129 end
130
130
131 Change.create(:changeset => cs,
131 Change.create(:changeset => cs,
132 :action => action,
132 :action => action,
133 :path => scm.with_leading_slash(revision.paths[0][:path]),
133 :path => scm.with_leading_slash(revision.paths[0][:path]),
134 :revision => revision.paths[0][:revision],
134 :revision => revision.paths[0][:revision],
135 :branch => revision.paths[0][:branch]
135 :branch => revision.paths[0][:branch]
136 )
136 )
137 end
137 end
138 end
138 end
139
139
140 # Renumber new changesets in chronological order
140 # Renumber new changesets in chronological order
141 c = changesets.find(:first, :order => 'committed_on DESC, id DESC', :conditions => "revision NOT LIKE '_%'")
142 next_rev = c.nil? ? 1 : (c.revision.to_i + 1)
143 changesets.find(:all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE '_%'").each do |changeset|
141 changesets.find(:all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE '_%'").each do |changeset|
144 changeset.update_attribute :revision, next_rev
142 changeset.update_attribute :revision, next_revision_number
145 next_rev += 1
146 end
143 end
147 end # transaction
144 end # transaction
148 end
145 end
146
147 private
148
149 # Returns the next revision number to assign to a CVS changeset
150 def next_revision_number
151 # Need to retrieve existing revision numbers to sort them as integers
152 @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)
153 @current_revision_number += 1
154 end
149 end
155 end
General Comments 0
You need to be logged in to leave comments. Login now