##// END OF EJS Templates
Fixed: error when browsing an empty Mercurial repository (#1046)....
Jean-Philippe Lang -
r1328:7aaa538fd9cb
parent child
Show More
@@ -1,92 +1,94
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/mercurial_adapter'
19 19
20 20 class Repository::Mercurial < Repository
21 21 attr_protected :root_url
22 22 validates_presence_of :url
23 23
24 24 def scm_adapter
25 25 Redmine::Scm::Adapters::MercurialAdapter
26 26 end
27 27
28 28 def self.scm_name
29 29 'Mercurial'
30 30 end
31 31
32 32 def entries(path=nil, identifier=nil)
33 33 entries=scm.entries(path, identifier)
34 34 if entries
35 35 entries.each do |entry|
36 36 next unless entry.is_file?
37 37 # Set the filesize unless browsing a specific revision
38 38 if identifier.nil?
39 39 full_path = File.join(root_url, entry.path)
40 40 entry.size = File.stat(full_path).size if File.file?(full_path)
41 41 end
42 42 # Search the DB for the entry's last change
43 43 change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
44 44 if change
45 45 entry.lastrev.identifier = change.changeset.revision
46 46 entry.lastrev.name = change.changeset.revision
47 47 entry.lastrev.author = change.changeset.committer
48 48 entry.lastrev.revision = change.revision
49 49 end
50 50 end
51 51 end
52 52 entries
53 53 end
54 54
55 55 def fetch_changesets
56 56 scm_info = scm.info
57 57 if scm_info
58 58 # latest revision found in database
59 59 db_revision = latest_changeset ? latest_changeset.revision.to_i : -1
60 60 # latest revision in the repository
61 scm_revision = scm_info.lastrev.identifier.to_i
61 latest_revision = scm_info.lastrev
62 return if latest_revision.nil?
63 scm_revision = latest_revision.identifier.to_i
62 64 if db_revision < scm_revision
63 65 logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
64 66 identifier_from = db_revision + 1
65 67 while (identifier_from <= scm_revision)
66 68 # loads changesets by batches of 100
67 69 identifier_to = [identifier_from + 99, scm_revision].min
68 70 revisions = scm.revisions('', identifier_from, identifier_to, :with_paths => true)
69 71 transaction do
70 72 revisions.each do |revision|
71 73 changeset = Changeset.create(:repository => self,
72 74 :revision => revision.identifier,
73 75 :scmid => revision.scmid,
74 76 :committer => revision.author,
75 77 :committed_on => revision.time,
76 78 :comments => revision.message)
77 79
78 80 revision.paths.each do |change|
79 81 Change.create(:changeset => changeset,
80 82 :action => change[:action],
81 83 :path => change[:path],
82 84 :from_path => change[:from_path],
83 85 :from_revision => change[:from_revision])
84 86 end
85 87 end
86 88 end unless revisions.nil?
87 89 identifier_from = identifier_to + 1
88 90 end
89 91 end
90 92 end
91 93 end
92 94 end
@@ -1,206 +1,207
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/abstract_adapter'
19 19
20 20 module Redmine
21 21 module Scm
22 22 module Adapters
23 23 class MercurialAdapter < AbstractAdapter
24 24
25 25 # Mercurial executable name
26 26 HG_BIN = "hg"
27 27
28 28 def info
29 29 cmd = "#{HG_BIN} -R #{target('')} root"
30 30 root_url = nil
31 31 shellout(cmd) do |io|
32 32 root_url = io.gets
33 33 end
34 34 return nil if $? && $?.exitstatus != 0
35 35 info = Info.new({:root_url => root_url.chomp,
36 36 :lastrev => revisions(nil,nil,nil,{:limit => 1}).last
37 37 })
38 38 info
39 39 rescue CommandFailed
40 40 return nil
41 41 end
42 42
43 43 def entries(path=nil, identifier=nil)
44 44 path ||= ''
45 45 entries = Entries.new
46 46 cmd = "#{HG_BIN} -R #{target('')} --cwd #{target(path)} locate"
47 47 cmd << " -r #{identifier.to_i}" if identifier
48 48 cmd << " " + shell_quote('glob:**')
49 49 shellout(cmd) do |io|
50 50 io.each_line do |line|
51 51 e = line.chomp.split(%r{[\/\\]})
52 52 entries << Entry.new({:name => e.first,
53 53 :path => (path.empty? ? e.first : "#{path}/#{e.first}"),
54 54 :kind => (e.size > 1 ? 'dir' : 'file'),
55 55 :lastrev => Revision.new
56 56 }) unless entries.detect{|entry| entry.name == e.first}
57 57 end
58 58 end
59 59 return nil if $? && $?.exitstatus != 0
60 60 entries.sort_by_name
61 61 end
62 62
63 63 def entry(path=nil, identifier=nil)
64 64 path ||= ''
65 65 search_path = path.split('/')[0..-2].join('/')
66 66 entry_name = path.split('/').last
67 67 e = entries(search_path, identifier)
68 68 e ? e.detect{|entry| entry.name == entry_name} : nil
69 69 end
70 70
71 71 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
72 72 revisions = Revisions.new
73 73 cmd = "#{HG_BIN} -v --encoding utf8 -R #{target('')} log"
74 74 if identifier_from && identifier_to
75 75 cmd << " -r #{identifier_from.to_i}:#{identifier_to.to_i}"
76 76 elsif identifier_from
77 77 cmd << " -r #{identifier_from.to_i}:"
78 78 end
79 79 cmd << " --limit #{options[:limit].to_i}" if options[:limit]
80 80 shellout(cmd) do |io|
81 81 changeset = {}
82 82 parsing_descr = false
83 83 line_feeds = 0
84 84
85 85 io.each_line do |line|
86 86 if line =~ /^(\w+):\s*(.*)$/
87 87 key = $1
88 88 value = $2
89 89 if parsing_descr && line_feeds > 1
90 90 parsing_descr = false
91 91 revisions << build_revision_from_changeset(changeset)
92 92 changeset = {}
93 93 end
94 94 if !parsing_descr
95 95 changeset.store key.to_sym, value
96 96 if $1 == "description"
97 97 parsing_descr = true
98 98 line_feeds = 0
99 99 next
100 100 end
101 101 end
102 102 end
103 103 if parsing_descr
104 104 changeset[:description] << line
105 105 line_feeds += 1 if line.chomp.empty?
106 106 end
107 107 end
108 revisions << build_revision_from_changeset(changeset)
108 # Add the last changeset if there is one left
109 revisions << build_revision_from_changeset(changeset) if changeset[:date]
109 110 end
110 111 return nil if $? && $?.exitstatus != 0
111 112 revisions
112 113 end
113 114
114 115 def diff(path, identifier_from, identifier_to=nil, type="inline")
115 116 path ||= ''
116 117 if identifier_to
117 118 identifier_to = identifier_to.to_i
118 119 else
119 120 identifier_to = identifier_from.to_i - 1
120 121 end
121 122 cmd = "#{HG_BIN} -R #{target('')} diff -r #{identifier_to} -r #{identifier_from} --nodates"
122 123 cmd << " -I #{target(path)}" unless path.empty?
123 124 diff = []
124 125 shellout(cmd) do |io|
125 126 io.each_line do |line|
126 127 diff << line
127 128 end
128 129 end
129 130 return nil if $? && $?.exitstatus != 0
130 131 DiffTableList.new diff, type
131 132 end
132 133
133 134 def cat(path, identifier=nil)
134 135 cmd = "#{HG_BIN} -R #{target('')} cat"
135 136 cmd << " -r #{identifier.to_i}" if identifier
136 137 cmd << " #{target(path)}"
137 138 cat = nil
138 139 shellout(cmd) do |io|
139 140 io.binmode
140 141 cat = io.read
141 142 end
142 143 return nil if $? && $?.exitstatus != 0
143 144 cat
144 145 end
145 146
146 147 def annotate(path, identifier=nil)
147 148 path ||= ''
148 149 cmd = "#{HG_BIN} -R #{target('')}"
149 150 cmd << " annotate -n -u"
150 151 cmd << " -r #{identifier.to_i}" if identifier
151 152 cmd << " #{target(path)}"
152 153 blame = Annotate.new
153 154 shellout(cmd) do |io|
154 155 io.each_line do |line|
155 156 next unless line =~ %r{^([^:]+)\s(\d+):(.*)$}
156 157 blame.add_line($3.rstrip, Revision.new(:identifier => $2.to_i, :author => $1.strip))
157 158 end
158 159 end
159 160 return nil if $? && $?.exitstatus != 0
160 161 blame
161 162 end
162 163
163 164 private
164 165
165 166 # Builds a revision objet from the changeset returned by hg command
166 167 def build_revision_from_changeset(changeset)
167 168 rev_id = changeset[:changeset].to_s.split(':').first.to_i
168 169
169 170 # Changes
170 171 paths = (rev_id == 0) ?
171 172 # Can't get changes for revision 0 with hg status
172 173 changeset[:files].to_s.split.collect{|path| {:action => 'A', :path => "/#{path}"}} :
173 174 status(rev_id)
174 175
175 176 Revision.new({:identifier => rev_id,
176 177 :scmid => changeset[:changeset].to_s.split(':').last,
177 178 :author => changeset[:user],
178 179 :time => Time.parse(changeset[:date]),
179 180 :message => changeset[:description],
180 181 :paths => paths
181 182 })
182 183 end
183 184
184 185 # Returns the file changes for a given revision
185 186 def status(rev_id)
186 187 cmd = "#{HG_BIN} -R #{target('')} status --rev #{rev_id.to_i - 1}:#{rev_id.to_i}"
187 188 result = []
188 189 shellout(cmd) do |io|
189 190 io.each_line do |line|
190 191 action, file = line.chomp.split
191 192 next unless action && file
192 193 file.gsub!("\\", "/")
193 194 case action
194 195 when 'R'
195 196 result << { :action => 'D' , :path => "/#{file}" }
196 197 else
197 198 result << { :action => action, :path => "/#{file}" }
198 199 end
199 200 end
200 201 end
201 202 result
202 203 end
203 204 end
204 205 end
205 206 end
206 207 end
General Comments 0
You need to be logged in to leave comments. Login now