##// END OF EJS Templates
vcs: fix typo in MercurialChangeset.committer...
Mads Kiilerich -
r3491:bdb99740 beta
parent child Browse files
Show More
@@ -1,375 +1,375 b''
1 import os
1 import os
2 import posixpath
2 import posixpath
3
3
4 from rhodecode.lib.vcs.backends.base import BaseChangeset
4 from rhodecode.lib.vcs.backends.base import BaseChangeset
5 from rhodecode.lib.vcs.conf import settings
5 from rhodecode.lib.vcs.conf import settings
6 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError, \
6 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError, \
7 ChangesetError, ImproperArchiveTypeError, NodeDoesNotExistError, VCSError
7 ChangesetError, ImproperArchiveTypeError, NodeDoesNotExistError, VCSError
8 from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, \
8 from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, \
9 ChangedFileNodesGenerator, DirNode, FileNode, NodeKind, \
9 ChangedFileNodesGenerator, DirNode, FileNode, NodeKind, \
10 RemovedFileNodesGenerator, RootNode, SubModuleNode
10 RemovedFileNodesGenerator, RootNode, SubModuleNode
11
11
12 from rhodecode.lib.vcs.utils import safe_str, safe_unicode, date_fromtimestamp
12 from rhodecode.lib.vcs.utils import safe_str, safe_unicode, date_fromtimestamp
13 from rhodecode.lib.vcs.utils.lazy import LazyProperty
13 from rhodecode.lib.vcs.utils.lazy import LazyProperty
14 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
14 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
15 from rhodecode.lib.vcs.utils.hgcompat import archival, hex
15 from rhodecode.lib.vcs.utils.hgcompat import archival, hex
16
16
17
17
18 class MercurialChangeset(BaseChangeset):
18 class MercurialChangeset(BaseChangeset):
19 """
19 """
20 Represents state of the repository at the single revision.
20 Represents state of the repository at the single revision.
21 """
21 """
22
22
23 def __init__(self, repository, revision):
23 def __init__(self, repository, revision):
24 self.repository = repository
24 self.repository = repository
25 self.raw_id = revision
25 self.raw_id = revision
26 self._ctx = repository._repo[revision]
26 self._ctx = repository._repo[revision]
27 self.revision = self._ctx._rev
27 self.revision = self._ctx._rev
28 self.nodes = {}
28 self.nodes = {}
29
29
30 @LazyProperty
30 @LazyProperty
31 def tags(self):
31 def tags(self):
32 return map(safe_unicode, self._ctx.tags())
32 return map(safe_unicode, self._ctx.tags())
33
33
34 @LazyProperty
34 @LazyProperty
35 def branch(self):
35 def branch(self):
36 return safe_unicode(self._ctx.branch())
36 return safe_unicode(self._ctx.branch())
37
37
38 @LazyProperty
38 @LazyProperty
39 def bookmarks(self):
39 def bookmarks(self):
40 return map(safe_unicode, self._ctx.bookmarks())
40 return map(safe_unicode, self._ctx.bookmarks())
41
41
42 @LazyProperty
42 @LazyProperty
43 def message(self):
43 def message(self):
44 return safe_unicode(self._ctx.description())
44 return safe_unicode(self._ctx.description())
45
45
46 @LazyProperty
46 @LazyProperty
47 def commiter(self):
47 def commiter(self):
48 return safe_unicode(self.auhtor)
48 return safe_unicode(self.author)
49
49
50 @LazyProperty
50 @LazyProperty
51 def author(self):
51 def author(self):
52 return safe_unicode(self._ctx.user())
52 return safe_unicode(self._ctx.user())
53
53
54 @LazyProperty
54 @LazyProperty
55 def date(self):
55 def date(self):
56 return date_fromtimestamp(*self._ctx.date())
56 return date_fromtimestamp(*self._ctx.date())
57
57
58 @LazyProperty
58 @LazyProperty
59 def _timestamp(self):
59 def _timestamp(self):
60 return self._ctx.date()[0]
60 return self._ctx.date()[0]
61
61
62 @LazyProperty
62 @LazyProperty
63 def status(self):
63 def status(self):
64 """
64 """
65 Returns modified, added, removed, deleted files for current changeset
65 Returns modified, added, removed, deleted files for current changeset
66 """
66 """
67 return self.repository._repo.status(self._ctx.p1().node(),
67 return self.repository._repo.status(self._ctx.p1().node(),
68 self._ctx.node())
68 self._ctx.node())
69
69
70 @LazyProperty
70 @LazyProperty
71 def _file_paths(self):
71 def _file_paths(self):
72 return list(self._ctx)
72 return list(self._ctx)
73
73
74 @LazyProperty
74 @LazyProperty
75 def _dir_paths(self):
75 def _dir_paths(self):
76 p = list(set(get_dirs_for_path(*self._file_paths)))
76 p = list(set(get_dirs_for_path(*self._file_paths)))
77 p.insert(0, '')
77 p.insert(0, '')
78 return p
78 return p
79
79
80 @LazyProperty
80 @LazyProperty
81 def _paths(self):
81 def _paths(self):
82 return self._dir_paths + self._file_paths
82 return self._dir_paths + self._file_paths
83
83
84 @LazyProperty
84 @LazyProperty
85 def id(self):
85 def id(self):
86 if self.last:
86 if self.last:
87 return u'tip'
87 return u'tip'
88 return self.short_id
88 return self.short_id
89
89
90 @LazyProperty
90 @LazyProperty
91 def short_id(self):
91 def short_id(self):
92 return self.raw_id[:12]
92 return self.raw_id[:12]
93
93
94 @LazyProperty
94 @LazyProperty
95 def parents(self):
95 def parents(self):
96 """
96 """
97 Returns list of parents changesets.
97 Returns list of parents changesets.
98 """
98 """
99 return [self.repository.get_changeset(parent.rev())
99 return [self.repository.get_changeset(parent.rev())
100 for parent in self._ctx.parents() if parent.rev() >= 0]
100 for parent in self._ctx.parents() if parent.rev() >= 0]
101
101
102 @LazyProperty
102 @LazyProperty
103 def children(self):
103 def children(self):
104 """
104 """
105 Returns list of children changesets.
105 Returns list of children changesets.
106 """
106 """
107 return [self.repository.get_changeset(child.rev())
107 return [self.repository.get_changeset(child.rev())
108 for child in self._ctx.children() if child.rev() >= 0]
108 for child in self._ctx.children() if child.rev() >= 0]
109
109
110 def next(self, branch=None):
110 def next(self, branch=None):
111
111
112 if branch and self.branch != branch:
112 if branch and self.branch != branch:
113 raise VCSError('Branch option used on changeset not belonging '
113 raise VCSError('Branch option used on changeset not belonging '
114 'to that branch')
114 'to that branch')
115
115
116 def _next(changeset, branch):
116 def _next(changeset, branch):
117 try:
117 try:
118 next_ = changeset.revision + 1
118 next_ = changeset.revision + 1
119 next_rev = changeset.repository.revisions[next_]
119 next_rev = changeset.repository.revisions[next_]
120 except IndexError:
120 except IndexError:
121 raise ChangesetDoesNotExistError
121 raise ChangesetDoesNotExistError
122 cs = changeset.repository.get_changeset(next_rev)
122 cs = changeset.repository.get_changeset(next_rev)
123
123
124 if branch and branch != cs.branch:
124 if branch and branch != cs.branch:
125 return _next(cs, branch)
125 return _next(cs, branch)
126
126
127 return cs
127 return cs
128
128
129 return _next(self, branch)
129 return _next(self, branch)
130
130
131 def prev(self, branch=None):
131 def prev(self, branch=None):
132 if branch and self.branch != branch:
132 if branch and self.branch != branch:
133 raise VCSError('Branch option used on changeset not belonging '
133 raise VCSError('Branch option used on changeset not belonging '
134 'to that branch')
134 'to that branch')
135
135
136 def _prev(changeset, branch):
136 def _prev(changeset, branch):
137 try:
137 try:
138 prev_ = changeset.revision - 1
138 prev_ = changeset.revision - 1
139 if prev_ < 0:
139 if prev_ < 0:
140 raise IndexError
140 raise IndexError
141 prev_rev = changeset.repository.revisions[prev_]
141 prev_rev = changeset.repository.revisions[prev_]
142 except IndexError:
142 except IndexError:
143 raise ChangesetDoesNotExistError
143 raise ChangesetDoesNotExistError
144
144
145 cs = changeset.repository.get_changeset(prev_rev)
145 cs = changeset.repository.get_changeset(prev_rev)
146
146
147 if branch and branch != cs.branch:
147 if branch and branch != cs.branch:
148 return _prev(cs, branch)
148 return _prev(cs, branch)
149
149
150 return cs
150 return cs
151
151
152 return _prev(self, branch)
152 return _prev(self, branch)
153
153
154 def diff(self, ignore_whitespace=True, context=3):
154 def diff(self, ignore_whitespace=True, context=3):
155 return ''.join(self._ctx.diff(git=True,
155 return ''.join(self._ctx.diff(git=True,
156 ignore_whitespace=ignore_whitespace,
156 ignore_whitespace=ignore_whitespace,
157 context=context))
157 context=context))
158
158
159 def _fix_path(self, path):
159 def _fix_path(self, path):
160 """
160 """
161 Paths are stored without trailing slash so we need to get rid off it if
161 Paths are stored without trailing slash so we need to get rid off it if
162 needed. Also mercurial keeps filenodes as str so we need to decode
162 needed. Also mercurial keeps filenodes as str so we need to decode
163 from unicode to str
163 from unicode to str
164 """
164 """
165 if path.endswith('/'):
165 if path.endswith('/'):
166 path = path.rstrip('/')
166 path = path.rstrip('/')
167
167
168 return safe_str(path)
168 return safe_str(path)
169
169
170 def _get_kind(self, path):
170 def _get_kind(self, path):
171 path = self._fix_path(path)
171 path = self._fix_path(path)
172 if path in self._file_paths:
172 if path in self._file_paths:
173 return NodeKind.FILE
173 return NodeKind.FILE
174 elif path in self._dir_paths:
174 elif path in self._dir_paths:
175 return NodeKind.DIR
175 return NodeKind.DIR
176 else:
176 else:
177 raise ChangesetError("Node does not exist at the given path %r"
177 raise ChangesetError("Node does not exist at the given path %r"
178 % (path))
178 % (path))
179
179
180 def _get_filectx(self, path):
180 def _get_filectx(self, path):
181 path = self._fix_path(path)
181 path = self._fix_path(path)
182 if self._get_kind(path) != NodeKind.FILE:
182 if self._get_kind(path) != NodeKind.FILE:
183 raise ChangesetError("File does not exist for revision %r at "
183 raise ChangesetError("File does not exist for revision %r at "
184 " %r" % (self.raw_id, path))
184 " %r" % (self.raw_id, path))
185 return self._ctx.filectx(path)
185 return self._ctx.filectx(path)
186
186
187 def _extract_submodules(self):
187 def _extract_submodules(self):
188 """
188 """
189 returns a dictionary with submodule information from substate file
189 returns a dictionary with submodule information from substate file
190 of hg repository
190 of hg repository
191 """
191 """
192 return self._ctx.substate
192 return self._ctx.substate
193
193
194 def get_file_mode(self, path):
194 def get_file_mode(self, path):
195 """
195 """
196 Returns stat mode of the file at the given ``path``.
196 Returns stat mode of the file at the given ``path``.
197 """
197 """
198 fctx = self._get_filectx(path)
198 fctx = self._get_filectx(path)
199 if 'x' in fctx.flags():
199 if 'x' in fctx.flags():
200 return 0100755
200 return 0100755
201 else:
201 else:
202 return 0100644
202 return 0100644
203
203
204 def get_file_content(self, path):
204 def get_file_content(self, path):
205 """
205 """
206 Returns content of the file at given ``path``.
206 Returns content of the file at given ``path``.
207 """
207 """
208 fctx = self._get_filectx(path)
208 fctx = self._get_filectx(path)
209 return fctx.data()
209 return fctx.data()
210
210
211 def get_file_size(self, path):
211 def get_file_size(self, path):
212 """
212 """
213 Returns size of the file at given ``path``.
213 Returns size of the file at given ``path``.
214 """
214 """
215 fctx = self._get_filectx(path)
215 fctx = self._get_filectx(path)
216 return fctx.size()
216 return fctx.size()
217
217
218 def get_file_changeset(self, path):
218 def get_file_changeset(self, path):
219 """
219 """
220 Returns last commit of the file at the given ``path``.
220 Returns last commit of the file at the given ``path``.
221 """
221 """
222 node = self.get_node(path)
222 node = self.get_node(path)
223 return node.history[0]
223 return node.history[0]
224
224
225 def get_file_history(self, path):
225 def get_file_history(self, path):
226 """
226 """
227 Returns history of file as reversed list of ``Changeset`` objects for
227 Returns history of file as reversed list of ``Changeset`` objects for
228 which file at given ``path`` has been modified.
228 which file at given ``path`` has been modified.
229 """
229 """
230 fctx = self._get_filectx(path)
230 fctx = self._get_filectx(path)
231 nodes = [fctx.filectx(x).node() for x in fctx.filelog()]
231 nodes = [fctx.filectx(x).node() for x in fctx.filelog()]
232 changesets = [self.repository.get_changeset(hex(node))
232 changesets = [self.repository.get_changeset(hex(node))
233 for node in reversed(nodes)]
233 for node in reversed(nodes)]
234 return changesets
234 return changesets
235
235
236 def get_file_annotate(self, path):
236 def get_file_annotate(self, path):
237 """
237 """
238 Returns a generator of four element tuples with
238 Returns a generator of four element tuples with
239 lineno, sha, changeset lazy loader and line
239 lineno, sha, changeset lazy loader and line
240 """
240 """
241
241
242 fctx = self._get_filectx(path)
242 fctx = self._get_filectx(path)
243 for i, annotate_data in enumerate(fctx.annotate()):
243 for i, annotate_data in enumerate(fctx.annotate()):
244 ln_no = i + 1
244 ln_no = i + 1
245 sha = hex(annotate_data[0].node())
245 sha = hex(annotate_data[0].node())
246 yield (ln_no, sha, lambda: self.repository.get_changeset(sha), annotate_data[1],)
246 yield (ln_no, sha, lambda: self.repository.get_changeset(sha), annotate_data[1],)
247
247
248 def fill_archive(self, stream=None, kind='tgz', prefix=None,
248 def fill_archive(self, stream=None, kind='tgz', prefix=None,
249 subrepos=False):
249 subrepos=False):
250 """
250 """
251 Fills up given stream.
251 Fills up given stream.
252
252
253 :param stream: file like object.
253 :param stream: file like object.
254 :param kind: one of following: ``zip``, ``tgz`` or ``tbz2``.
254 :param kind: one of following: ``zip``, ``tgz`` or ``tbz2``.
255 Default: ``tgz``.
255 Default: ``tgz``.
256 :param prefix: name of root directory in archive.
256 :param prefix: name of root directory in archive.
257 Default is repository name and changeset's raw_id joined with dash
257 Default is repository name and changeset's raw_id joined with dash
258 (``repo-tip.<KIND>``).
258 (``repo-tip.<KIND>``).
259 :param subrepos: include subrepos in this archive.
259 :param subrepos: include subrepos in this archive.
260
260
261 :raise ImproperArchiveTypeError: If given kind is wrong.
261 :raise ImproperArchiveTypeError: If given kind is wrong.
262 :raise VcsError: If given stream is None
262 :raise VcsError: If given stream is None
263 """
263 """
264
264
265 allowed_kinds = settings.ARCHIVE_SPECS.keys()
265 allowed_kinds = settings.ARCHIVE_SPECS.keys()
266 if kind not in allowed_kinds:
266 if kind not in allowed_kinds:
267 raise ImproperArchiveTypeError('Archive kind not supported use one'
267 raise ImproperArchiveTypeError('Archive kind not supported use one'
268 'of %s', allowed_kinds)
268 'of %s', allowed_kinds)
269
269
270 if stream is None:
270 if stream is None:
271 raise VCSError('You need to pass in a valid stream for filling'
271 raise VCSError('You need to pass in a valid stream for filling'
272 ' with archival data')
272 ' with archival data')
273
273
274 if prefix is None:
274 if prefix is None:
275 prefix = '%s-%s' % (self.repository.name, self.short_id)
275 prefix = '%s-%s' % (self.repository.name, self.short_id)
276 elif prefix.startswith('/'):
276 elif prefix.startswith('/'):
277 raise VCSError("Prefix cannot start with leading slash")
277 raise VCSError("Prefix cannot start with leading slash")
278 elif prefix.strip() == '':
278 elif prefix.strip() == '':
279 raise VCSError("Prefix cannot be empty")
279 raise VCSError("Prefix cannot be empty")
280
280
281 archival.archive(self.repository._repo, stream, self.raw_id,
281 archival.archive(self.repository._repo, stream, self.raw_id,
282 kind, prefix=prefix, subrepos=subrepos)
282 kind, prefix=prefix, subrepos=subrepos)
283
283
284 if stream.closed and hasattr(stream, 'name'):
284 if stream.closed and hasattr(stream, 'name'):
285 stream = open(stream.name, 'rb')
285 stream = open(stream.name, 'rb')
286 elif hasattr(stream, 'mode') and 'r' not in stream.mode:
286 elif hasattr(stream, 'mode') and 'r' not in stream.mode:
287 stream = open(stream.name, 'rb')
287 stream = open(stream.name, 'rb')
288 else:
288 else:
289 stream.seek(0)
289 stream.seek(0)
290
290
291 def get_nodes(self, path):
291 def get_nodes(self, path):
292 """
292 """
293 Returns combined ``DirNode`` and ``FileNode`` objects list representing
293 Returns combined ``DirNode`` and ``FileNode`` objects list representing
294 state of changeset at the given ``path``. If node at the given ``path``
294 state of changeset at the given ``path``. If node at the given ``path``
295 is not instance of ``DirNode``, ChangesetError would be raised.
295 is not instance of ``DirNode``, ChangesetError would be raised.
296 """
296 """
297
297
298 if self._get_kind(path) != NodeKind.DIR:
298 if self._get_kind(path) != NodeKind.DIR:
299 raise ChangesetError("Directory does not exist for revision %r at "
299 raise ChangesetError("Directory does not exist for revision %r at "
300 " %r" % (self.revision, path))
300 " %r" % (self.revision, path))
301 path = self._fix_path(path)
301 path = self._fix_path(path)
302
302
303 filenodes = [FileNode(f, changeset=self) for f in self._file_paths
303 filenodes = [FileNode(f, changeset=self) for f in self._file_paths
304 if os.path.dirname(f) == path]
304 if os.path.dirname(f) == path]
305 dirs = path == '' and '' or [d for d in self._dir_paths
305 dirs = path == '' and '' or [d for d in self._dir_paths
306 if d and posixpath.dirname(d) == path]
306 if d and posixpath.dirname(d) == path]
307 dirnodes = [DirNode(d, changeset=self) for d in dirs
307 dirnodes = [DirNode(d, changeset=self) for d in dirs
308 if os.path.dirname(d) == path]
308 if os.path.dirname(d) == path]
309
309
310 als = self.repository.alias
310 als = self.repository.alias
311 for k, vals in self._extract_submodules().iteritems():
311 for k, vals in self._extract_submodules().iteritems():
312 #vals = url,rev,type
312 #vals = url,rev,type
313 loc = vals[0]
313 loc = vals[0]
314 cs = vals[1]
314 cs = vals[1]
315 dirnodes.append(SubModuleNode(k, url=loc, changeset=cs,
315 dirnodes.append(SubModuleNode(k, url=loc, changeset=cs,
316 alias=als))
316 alias=als))
317 nodes = dirnodes + filenodes
317 nodes = dirnodes + filenodes
318 # cache nodes
318 # cache nodes
319 for node in nodes:
319 for node in nodes:
320 self.nodes[node.path] = node
320 self.nodes[node.path] = node
321 nodes.sort()
321 nodes.sort()
322
322
323 return nodes
323 return nodes
324
324
325 def get_node(self, path):
325 def get_node(self, path):
326 """
326 """
327 Returns ``Node`` object from the given ``path``. If there is no node at
327 Returns ``Node`` object from the given ``path``. If there is no node at
328 the given ``path``, ``ChangesetError`` would be raised.
328 the given ``path``, ``ChangesetError`` would be raised.
329 """
329 """
330
330
331 path = self._fix_path(path)
331 path = self._fix_path(path)
332
332
333 if not path in self.nodes:
333 if not path in self.nodes:
334 if path in self._file_paths:
334 if path in self._file_paths:
335 node = FileNode(path, changeset=self)
335 node = FileNode(path, changeset=self)
336 elif path in self._dir_paths or path in self._dir_paths:
336 elif path in self._dir_paths or path in self._dir_paths:
337 if path == '':
337 if path == '':
338 node = RootNode(changeset=self)
338 node = RootNode(changeset=self)
339 else:
339 else:
340 node = DirNode(path, changeset=self)
340 node = DirNode(path, changeset=self)
341 else:
341 else:
342 raise NodeDoesNotExistError("There is no file nor directory "
342 raise NodeDoesNotExistError("There is no file nor directory "
343 "at the given path: %r at revision %r"
343 "at the given path: %r at revision %r"
344 % (path, self.short_id))
344 % (path, self.short_id))
345 # cache node
345 # cache node
346 self.nodes[path] = node
346 self.nodes[path] = node
347 return self.nodes[path]
347 return self.nodes[path]
348
348
349 @LazyProperty
349 @LazyProperty
350 def affected_files(self):
350 def affected_files(self):
351 """
351 """
352 Get's a fast accessible file changes for given changeset
352 Get's a fast accessible file changes for given changeset
353 """
353 """
354 return self._ctx.files()
354 return self._ctx.files()
355
355
356 @property
356 @property
357 def added(self):
357 def added(self):
358 """
358 """
359 Returns list of added ``FileNode`` objects.
359 Returns list of added ``FileNode`` objects.
360 """
360 """
361 return AddedFileNodesGenerator([n for n in self.status[1]], self)
361 return AddedFileNodesGenerator([n for n in self.status[1]], self)
362
362
363 @property
363 @property
364 def changed(self):
364 def changed(self):
365 """
365 """
366 Returns list of modified ``FileNode`` objects.
366 Returns list of modified ``FileNode`` objects.
367 """
367 """
368 return ChangedFileNodesGenerator([n for n in self.status[0]], self)
368 return ChangedFileNodesGenerator([n for n in self.status[0]], self)
369
369
370 @property
370 @property
371 def removed(self):
371 def removed(self):
372 """
372 """
373 Returns list of removed ``FileNode`` objects.
373 Returns list of removed ``FileNode`` objects.
374 """
374 """
375 return RemovedFileNodesGenerator([n for n in self.status[2]], self)
375 return RemovedFileNodesGenerator([n for n in self.status[2]], self)
General Comments 0
You need to be logged in to leave comments. Login now