##// END OF EJS Templates
Mercurial: changes for hg 4.9 support...
marcink -
r3534:eda9cd19 default
parent child Browse files
Show More
@@ -1,388 +1,381 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2019 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 HG commit module
23 23 """
24 24
25 25 import os
26 26
27 27 from zope.cachedescriptors.property import Lazy as LazyProperty
28 28
29 29 from rhodecode.lib.datelib import utcdate_fromtimestamp
30 30 from rhodecode.lib.utils import safe_str, safe_unicode
31 31 from rhodecode.lib.vcs import path as vcspath
32 32 from rhodecode.lib.vcs.backends import base
33 33 from rhodecode.lib.vcs.backends.hg.diff import MercurialDiff
34 34 from rhodecode.lib.vcs.exceptions import CommitError
35 35 from rhodecode.lib.vcs.nodes import (
36 36 AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode,
37 37 NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode,
38 38 LargeFileNode, LARGEFILE_PREFIX)
39 39 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
40 40
41 41
42 42 class MercurialCommit(base.BaseCommit):
43 43 """
44 44 Represents state of the repository at the single commit.
45 45 """
46 46
47 47 _filter_pre_load = [
48 48 # git specific property not supported here
49 49 "_commit",
50 50 ]
51 51
52 52 def __init__(self, repository, raw_id, idx, pre_load=None):
53 53 raw_id = safe_str(raw_id)
54 54
55 55 self.repository = repository
56 56 self._remote = repository._remote
57 57
58 58 self.raw_id = raw_id
59 59 self.idx = idx
60 60
61 61 self._set_bulk_properties(pre_load)
62 62
63 63 # caches
64 64 self.nodes = {}
65 65
66 66 def _set_bulk_properties(self, pre_load):
67 67 if not pre_load:
68 68 return
69 69 pre_load = [entry for entry in pre_load
70 70 if entry not in self._filter_pre_load]
71 71 if not pre_load:
72 72 return
73 73
74 74 result = self._remote.bulk_request(self.idx, pre_load)
75 75 for attr, value in result.items():
76 76 if attr in ["author", "branch", "message"]:
77 77 value = safe_unicode(value)
78 78 elif attr == "affected_files":
79 79 value = map(safe_unicode, value)
80 80 elif attr == "date":
81 81 value = utcdate_fromtimestamp(*value)
82 82 elif attr in ["children", "parents"]:
83 83 value = self._make_commits(value)
84 84 elif attr in ["phase"]:
85 85 value = self._get_phase_text(value)
86 86 self.__dict__[attr] = value
87 87
88 88 @LazyProperty
89 89 def tags(self):
90 90 tags = [name for name, commit_id in self.repository.tags.iteritems()
91 91 if commit_id == self.raw_id]
92 92 return tags
93 93
94 94 @LazyProperty
95 95 def branch(self):
96 96 return safe_unicode(self._remote.ctx_branch(self.idx))
97 97
98 98 @LazyProperty
99 99 def bookmarks(self):
100 100 bookmarks = [
101 101 name for name, commit_id in self.repository.bookmarks.iteritems()
102 102 if commit_id == self.raw_id]
103 103 return bookmarks
104 104
105 105 @LazyProperty
106 106 def message(self):
107 107 return safe_unicode(self._remote.ctx_description(self.idx))
108 108
109 109 @LazyProperty
110 110 def committer(self):
111 111 return safe_unicode(self.author)
112 112
113 113 @LazyProperty
114 114 def author(self):
115 115 return safe_unicode(self._remote.ctx_user(self.idx))
116 116
117 117 @LazyProperty
118 118 def date(self):
119 119 return utcdate_fromtimestamp(*self._remote.ctx_date(self.idx))
120 120
121 121 @LazyProperty
122 122 def status(self):
123 123 """
124 124 Returns modified, added, removed, deleted files for current commit
125 125 """
126 126 return self._remote.ctx_status(self.idx)
127 127
128 128 @LazyProperty
129 129 def _file_paths(self):
130 130 return self._remote.ctx_list(self.idx)
131 131
132 132 @LazyProperty
133 133 def _dir_paths(self):
134 134 p = list(set(get_dirs_for_path(*self._file_paths)))
135 135 p.insert(0, '')
136 136 return p
137 137
138 138 @LazyProperty
139 139 def _paths(self):
140 140 return self._dir_paths + self._file_paths
141 141
142 142 @LazyProperty
143 143 def id(self):
144 144 if self.last:
145 145 return u'tip'
146 146 return self.short_id
147 147
148 148 @LazyProperty
149 149 def short_id(self):
150 150 return self.raw_id[:12]
151 151
152 152 def _make_commits(self, indexes, pre_load=None):
153 153 return [self.repository.get_commit(commit_idx=idx, pre_load=pre_load)
154 154 for idx in indexes if idx >= 0]
155 155
156 156 @LazyProperty
157 157 def parents(self):
158 158 """
159 159 Returns list of parent commits.
160 160 """
161 161 parents = self._remote.ctx_parents(self.idx)
162 162 return self._make_commits(parents)
163 163
164 164 def _get_phase_text(self, phase_id):
165 165 return {
166 166 0: 'public',
167 167 1: 'draft',
168 168 2: 'secret',
169 169 }.get(phase_id) or ''
170 170
171 171 @LazyProperty
172 172 def phase(self):
173 173 phase_id = self._remote.ctx_phase(self.idx)
174 174 phase_text = self._get_phase_text(phase_id)
175 175
176 176 return safe_unicode(phase_text)
177 177
178 178 @LazyProperty
179 179 def obsolete(self):
180 180 obsolete = self._remote.ctx_obsolete(self.idx)
181 181 return obsolete
182 182
183 183 @LazyProperty
184 184 def hidden(self):
185 185 hidden = self._remote.ctx_hidden(self.idx)
186 186 return hidden
187 187
188 188 @LazyProperty
189 189 def children(self):
190 190 """
191 191 Returns list of child commits.
192 192 """
193 193 children = self._remote.ctx_children(self.idx)
194 194 return self._make_commits(children)
195 195
196 def diff(self, ignore_whitespace=True, context=3):
197 result = self._remote.ctx_diff(
198 self.idx,
199 git=True, ignore_whitespace=ignore_whitespace, context=context)
200 diff = ''.join(result)
201 return MercurialDiff(diff)
202
203 196 def _fix_path(self, path):
204 197 """
205 198 Mercurial keeps filenodes as str so we need to encode from unicode
206 199 to str.
207 200 """
208 201 return safe_str(super(MercurialCommit, self)._fix_path(path))
209 202
210 203 def _get_kind(self, path):
211 204 path = self._fix_path(path)
212 205 if path in self._file_paths:
213 206 return NodeKind.FILE
214 207 elif path in self._dir_paths:
215 208 return NodeKind.DIR
216 209 else:
217 210 raise CommitError(
218 211 "Node does not exist at the given path '%s'" % (path, ))
219 212
220 213 def _get_filectx(self, path):
221 214 path = self._fix_path(path)
222 215 if self._get_kind(path) != NodeKind.FILE:
223 216 raise CommitError(
224 217 "File does not exist for idx %s at '%s'" % (self.raw_id, path))
225 218 return path
226 219
227 220 def get_file_mode(self, path):
228 221 """
229 222 Returns stat mode of the file at the given ``path``.
230 223 """
231 224 path = self._get_filectx(path)
232 225 if 'x' in self._remote.fctx_flags(self.idx, path):
233 226 return base.FILEMODE_EXECUTABLE
234 227 else:
235 228 return base.FILEMODE_DEFAULT
236 229
237 230 def is_link(self, path):
238 231 path = self._get_filectx(path)
239 232 return 'l' in self._remote.fctx_flags(self.idx, path)
240 233
241 234 def get_file_content(self, path):
242 235 """
243 236 Returns content of the file at given ``path``.
244 237 """
245 238 path = self._get_filectx(path)
246 239 return self._remote.fctx_data(self.idx, path)
247 240
248 241 def get_file_size(self, path):
249 242 """
250 243 Returns size of the file at given ``path``.
251 244 """
252 245 path = self._get_filectx(path)
253 246 return self._remote.fctx_size(self.idx, path)
254 247
255 248 def get_path_history(self, path, limit=None, pre_load=None):
256 249 """
257 250 Returns history of file as reversed list of `MercurialCommit` objects
258 251 for which file at given ``path`` has been modified.
259 252 """
260 253 path = self._get_filectx(path)
261 254 hist = self._remote.node_history(self.idx, path, limit)
262 255 return [
263 256 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
264 257 for commit_id in hist]
265 258
266 259 def get_file_annotate(self, path, pre_load=None):
267 260 """
268 261 Returns a generator of four element tuples with
269 262 lineno, commit_id, commit lazy loader and line
270 263 """
271 264 result = self._remote.fctx_annotate(self.idx, path)
272 265
273 266 for ln_no, commit_id, content in result:
274 267 yield (
275 268 ln_no, commit_id,
276 269 lambda: self.repository.get_commit(commit_id=commit_id,
277 270 pre_load=pre_load),
278 271 content)
279 272
280 273 def get_nodes(self, path):
281 274 """
282 275 Returns combined ``DirNode`` and ``FileNode`` objects list representing
283 276 state of commit at the given ``path``. If node at the given ``path``
284 277 is not instance of ``DirNode``, CommitError would be raised.
285 278 """
286 279
287 280 if self._get_kind(path) != NodeKind.DIR:
288 281 raise CommitError(
289 282 "Directory does not exist for idx %s at '%s'" %
290 283 (self.idx, path))
291 284 path = self._fix_path(path)
292 285
293 286 filenodes = [
294 287 FileNode(f, commit=self) for f in self._file_paths
295 288 if os.path.dirname(f) == path]
296 289 # TODO: johbo: Check if this can be done in a more obvious way
297 290 dirs = path == '' and '' or [
298 291 d for d in self._dir_paths
299 292 if d and vcspath.dirname(d) == path]
300 293 dirnodes = [
301 294 DirNode(d, commit=self) for d in dirs
302 295 if os.path.dirname(d) == path]
303 296
304 297 alias = self.repository.alias
305 298 for k, vals in self._submodules.iteritems():
306 299 if vcspath.dirname(k) == path:
307 300 loc = vals[0]
308 301 commit = vals[1]
309 302 dirnodes.append(SubModuleNode(k, url=loc, commit=commit, alias=alias))
310 303 nodes = dirnodes + filenodes
311 304 # cache nodes
312 305 for node in nodes:
313 306 self.nodes[node.path] = node
314 307 nodes.sort()
315 308
316 309 return nodes
317 310
318 311 def get_node(self, path, pre_load=None):
319 312 """
320 313 Returns `Node` object from the given `path`. If there is no node at
321 314 the given `path`, `NodeDoesNotExistError` would be raised.
322 315 """
323 316 path = self._fix_path(path)
324 317
325 318 if path not in self.nodes:
326 319 if path in self._file_paths:
327 320 node = FileNode(path, commit=self, pre_load=pre_load)
328 321 elif path in self._dir_paths:
329 322 if path == '':
330 323 node = RootNode(commit=self)
331 324 else:
332 325 node = DirNode(path, commit=self)
333 326 else:
334 327 raise self.no_node_at_path(path)
335 328
336 329 # cache node
337 330 self.nodes[path] = node
338 331 return self.nodes[path]
339 332
340 333 def get_largefile_node(self, path):
341 334
342 335 if self._remote.is_large_file(path):
343 336 # content of that file regular FileNode is the hash of largefile
344 337 file_id = self.get_file_content(path).strip()
345 338
346 339 if self._remote.in_largefiles_store(file_id):
347 340 lf_path = self._remote.store_path(file_id)
348 341 return LargeFileNode(lf_path, commit=self, org_path=path)
349 342 elif self._remote.in_user_cache(file_id):
350 343 lf_path = self._remote.store_path(file_id)
351 344 self._remote.link(file_id, path)
352 345 return LargeFileNode(lf_path, commit=self, org_path=path)
353 346
354 347 @LazyProperty
355 348 def _submodules(self):
356 349 """
357 350 Returns a dictionary with submodule information from substate file
358 351 of hg repository.
359 352 """
360 353 return self._remote.ctx_substate(self.idx)
361 354
362 355 @LazyProperty
363 356 def affected_files(self):
364 357 """
365 358 Gets a fast accessible file changes for given commit
366 359 """
367 360 return self._remote.ctx_files(self.idx)
368 361
369 362 @property
370 363 def added(self):
371 364 """
372 365 Returns list of added ``FileNode`` objects.
373 366 """
374 367 return AddedFileNodesGenerator([n for n in self.status[1]], self)
375 368
376 369 @property
377 370 def changed(self):
378 371 """
379 372 Returns list of modified ``FileNode`` objects.
380 373 """
381 374 return ChangedFileNodesGenerator([n for n in self.status[0]], self)
382 375
383 376 @property
384 377 def removed(self):
385 378 """
386 379 Returns list of removed ``FileNode`` objects.
387 380 """
388 381 return RemovedFileNodesGenerator([n for n in self.status[2]], self)
General Comments 0
You need to be logged in to leave comments. Login now