##// END OF EJS Templates
svn: fixed problem with special characters inside subdirectories.
dan -
r4338:f83fb4e5 default
parent child Browse files
Show More
@@ -1,254 +1,253 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 SVN commit module
23 23 """
24 24
25 25
26 26 import dateutil.parser
27 27 from zope.cachedescriptors.property import Lazy as LazyProperty
28 28
29 29 from rhodecode.lib.utils import safe_str, safe_unicode
30 30 from rhodecode.lib.vcs import nodes, path as vcspath
31 31 from rhodecode.lib.vcs.backends import base
32 32 from rhodecode.lib.vcs.exceptions import CommitError, NodeDoesNotExistError
33 33
34 34
35 35 _SVN_PROP_TRUE = '*'
36 36
37 37
38 38 class SubversionCommit(base.BaseCommit):
39 39 """
40 40 Subversion specific implementation of commits
41 41
42 42 .. attribute:: branch
43 43
44 44 The Subversion backend does not support to assign branches to
45 45 specific commits. This attribute has always the value `None`.
46 46
47 47 """
48 48
49 49 def __init__(self, repository, commit_id):
50 50 self.repository = repository
51 51 self.idx = self.repository._get_commit_idx(commit_id)
52 52 self._svn_rev = self.idx + 1
53 53 self._remote = repository._remote
54 54 # TODO: handling of raw_id should be a method on repository itself,
55 55 # which knows how to translate commit index and commit id
56 56 self.raw_id = commit_id
57 57 self.short_id = commit_id
58 58 self.id = 'r%s' % (commit_id, )
59 59
60 60 # TODO: Implement the following placeholder attributes
61 61 self.nodes = {}
62 62 self.tags = []
63 63
64 64 @property
65 65 def author(self):
66 66 return safe_unicode(self._properties.get('svn:author'))
67 67
68 68 @property
69 69 def date(self):
70 70 return _date_from_svn_properties(self._properties)
71 71
72 72 @property
73 73 def message(self):
74 74 return safe_unicode(self._properties.get('svn:log'))
75 75
76 76 @LazyProperty
77 77 def _properties(self):
78 78 return self._remote.revision_properties(self._svn_rev)
79 79
80 80 @LazyProperty
81 81 def parents(self):
82 82 parent_idx = self.idx - 1
83 83 if parent_idx >= 0:
84 84 parent = self.repository.get_commit(commit_idx=parent_idx)
85 85 return [parent]
86 86 return []
87 87
88 88 @LazyProperty
89 89 def children(self):
90 90 child_idx = self.idx + 1
91 91 if child_idx < len(self.repository.commit_ids):
92 92 child = self.repository.get_commit(commit_idx=child_idx)
93 93 return [child]
94 94 return []
95 95
96 96 def get_file_mode(self, path):
97 97 # Note: Subversion flags files which are executable with a special
98 98 # property `svn:executable` which is set to the value ``"*"``.
99 99 if self._get_file_property(path, 'svn:executable') == _SVN_PROP_TRUE:
100 100 return base.FILEMODE_EXECUTABLE
101 101 else:
102 102 return base.FILEMODE_DEFAULT
103 103
104 104 def is_link(self, path):
105 105 # Note: Subversion has a flag for special files, the content of the
106 106 # file contains the type of that file.
107 107 if self._get_file_property(path, 'svn:special') == _SVN_PROP_TRUE:
108 108 return self.get_file_content(path).startswith('link')
109 109 return False
110 110
111 111 def is_node_binary(self, path):
112 112 path = self._fix_path(path)
113 113 return self._remote.is_binary(self._svn_rev, safe_str(path))
114 114
115 115 def _get_file_property(self, path, name):
116 116 file_properties = self._remote.node_properties(
117 117 safe_str(path), self._svn_rev)
118 118 return file_properties.get(name)
119 119
120 120 def get_file_content(self, path):
121 121 path = self._fix_path(path)
122 122 return self._remote.get_file_content(safe_str(path), self._svn_rev)
123 123
124 124 def get_file_content_streamed(self, path):
125 125 path = self._fix_path(path)
126 126 stream_method = getattr(self._remote, 'stream:get_file_content')
127 127 return stream_method(safe_str(path), self._svn_rev)
128 128
129 129 def get_file_size(self, path):
130 130 path = self._fix_path(path)
131 131 return self._remote.get_file_size(safe_str(path), self._svn_rev)
132 132
133 133 def get_path_history(self, path, limit=None, pre_load=None):
134 134 path = safe_str(self._fix_path(path))
135 135 history = self._remote.node_history(path, self._svn_rev, limit)
136 136 return [
137 137 self.repository.get_commit(commit_id=str(svn_rev))
138 138 for svn_rev in history]
139 139
140 140 def get_file_annotate(self, path, pre_load=None):
141 141 result = self._remote.file_annotate(safe_str(path), self._svn_rev)
142 142
143 143 for zero_based_line_no, svn_rev, content in result:
144 144 commit_id = str(svn_rev)
145 145 line_no = zero_based_line_no + 1
146 146 yield (
147 147 line_no,
148 148 commit_id,
149 149 lambda: self.repository.get_commit(commit_id=commit_id),
150 150 content)
151 151
152 152 def get_node(self, path, pre_load=None):
153 153 path = self._fix_path(path)
154 154 if path not in self.nodes:
155 155
156 156 if path == '':
157 157 node = nodes.RootNode(commit=self)
158 158 else:
159 159 node_type = self._remote.get_node_type(
160 160 safe_str(path), self._svn_rev)
161 161 if node_type == 'dir':
162 162 node = nodes.DirNode(path, commit=self)
163 163 elif node_type == 'file':
164 164 node = nodes.FileNode(path, commit=self, pre_load=pre_load)
165 165 else:
166 166 raise self.no_node_at_path(path)
167 167
168 168 self.nodes[path] = node
169 169 return self.nodes[path]
170 170
171 171 def get_nodes(self, path):
172 172 if self._get_kind(path) != nodes.NodeKind.DIR:
173 173 raise CommitError(
174 174 "Directory does not exist for commit %s at "
175 175 " '%s'" % (self.raw_id, path))
176 path = self._fix_path(path)
176 path = safe_str(self._fix_path(path))
177 177
178 178 path_nodes = []
179 for name, kind in self._remote.get_nodes(
180 safe_str(path), revision=self._svn_rev):
179 for name, kind in self._remote.get_nodes(path, revision=self._svn_rev):
181 180 node_path = vcspath.join(path, name)
182 181 if kind == 'dir':
183 182 node = nodes.DirNode(node_path, commit=self)
184 183 elif kind == 'file':
185 184 node = nodes.FileNode(node_path, commit=self)
186 185 else:
187 186 raise ValueError("Node kind %s not supported." % (kind, ))
188 187 self.nodes[node_path] = node
189 188 path_nodes.append(node)
190 189
191 190 return path_nodes
192 191
193 192 def _get_kind(self, path):
194 193 path = self._fix_path(path)
195 194 kind = self._remote.get_node_type(path, self._svn_rev)
196 195 if kind == 'file':
197 196 return nodes.NodeKind.FILE
198 197 elif kind == 'dir':
199 198 return nodes.NodeKind.DIR
200 199 else:
201 200 raise CommitError(
202 201 "Node does not exist at the given path '%s'" % (path, ))
203 202
204 203 @LazyProperty
205 204 def _changes_cache(self):
206 205 return self._remote.revision_changes(self._svn_rev)
207 206
208 207 @LazyProperty
209 208 def affected_files(self):
210 209 changed_files = set()
211 210 for files in self._changes_cache.itervalues():
212 211 changed_files.update(files)
213 212 return list(changed_files)
214 213
215 214 @LazyProperty
216 215 def id(self):
217 216 return self.raw_id
218 217
219 218 @property
220 219 def added(self):
221 220 return nodes.AddedFileNodesGenerator(self.added_paths, self)
222 221
223 222 @LazyProperty
224 223 def added_paths(self):
225 224 return [n for n in self._changes_cache['added']]
226 225
227 226 @property
228 227 def changed(self):
229 228 return nodes.ChangedFileNodesGenerator(self.changed_paths, self)
230 229
231 230 @LazyProperty
232 231 def changed_paths(self):
233 232 return [n for n in self._changes_cache['changed']]
234 233
235 234 @property
236 235 def removed(self):
237 236 return nodes.RemovedFileNodesGenerator(self.removed_paths, self)
238 237
239 238 @LazyProperty
240 239 def removed_paths(self):
241 240 return [n for n in self._changes_cache['removed']]
242 241
243 242
244 243 def _date_from_svn_properties(properties):
245 244 """
246 245 Parses the date out of given svn properties.
247 246
248 247 :return: :class:`datetime.datetime` instance. The object is naive.
249 248 """
250 249
251 250 aware_date = dateutil.parser.parse(properties.get('svn:date'))
252 251 # final_date = aware_date.astimezone(dateutil.tz.tzlocal())
253 252 final_date = aware_date
254 253 return final_date.replace(tzinfo=None)
General Comments 0
You need to be logged in to leave comments. Login now