##// END OF EJS Templates
mercurial: enable showfunc diff options to make it similar like git backend....
dan -
r849:f812d434 default
parent child
Show More
@@ -1,1009 +1,1009
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2019 RhodeCode GmbH
2 # Copyright (C) 2014-2019 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (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 Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import io
18 import io
19 import logging
19 import logging
20 import stat
20 import stat
21 import urllib
21 import urllib
22 import urllib2
22 import urllib2
23 import traceback
23 import traceback
24
24
25 from hgext import largefiles, rebase, purge
25 from hgext import largefiles, rebase, purge
26 from hgext.strip import strip as hgext_strip
26 from hgext.strip import strip as hgext_strip
27 from mercurial import commands
27 from mercurial import commands
28 from mercurial import unionrepo
28 from mercurial import unionrepo
29 from mercurial import verify
29 from mercurial import verify
30 from mercurial import repair
30 from mercurial import repair
31
31
32 import vcsserver
32 import vcsserver
33 from vcsserver import exceptions
33 from vcsserver import exceptions
34 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
34 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
35 from vcsserver.hgcompat import (
35 from vcsserver.hgcompat import (
36 archival, bin, clone, config as hgconfig, diffopts, hex, get_ctx,
36 archival, bin, clone, config as hgconfig, diffopts, hex, get_ctx,
37 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler,
37 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler,
38 makepeer, instance, match, memctx, exchange, memfilectx, nullrev, hg_merge,
38 makepeer, instance, match, memctx, exchange, memfilectx, nullrev, hg_merge,
39 patch, peer, revrange, ui, hg_tag, Abort, LookupError, RepoError,
39 patch, peer, revrange, ui, hg_tag, Abort, LookupError, RepoError,
40 RepoLookupError, InterventionRequired, RequirementError)
40 RepoLookupError, InterventionRequired, RequirementError)
41 from vcsserver.vcs_base import RemoteBase
41 from vcsserver.vcs_base import RemoteBase
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 def make_ui_from_config(repo_config):
46 def make_ui_from_config(repo_config):
47
47
48 class LoggingUI(ui.ui):
48 class LoggingUI(ui.ui):
49 def status(self, *msg, **opts):
49 def status(self, *msg, **opts):
50 log.info(' '.join(msg).rstrip('\n'))
50 log.info(' '.join(msg).rstrip('\n'))
51 super(LoggingUI, self).status(*msg, **opts)
51 super(LoggingUI, self).status(*msg, **opts)
52
52
53 def warn(self, *msg, **opts):
53 def warn(self, *msg, **opts):
54 log.warn(' '.join(msg).rstrip('\n'))
54 log.warn(' '.join(msg).rstrip('\n'))
55 super(LoggingUI, self).warn(*msg, **opts)
55 super(LoggingUI, self).warn(*msg, **opts)
56
56
57 def error(self, *msg, **opts):
57 def error(self, *msg, **opts):
58 log.error(' '.join(msg).rstrip('\n'))
58 log.error(' '.join(msg).rstrip('\n'))
59 super(LoggingUI, self).error(*msg, **opts)
59 super(LoggingUI, self).error(*msg, **opts)
60
60
61 def note(self, *msg, **opts):
61 def note(self, *msg, **opts):
62 log.info(' '.join(msg).rstrip('\n'))
62 log.info(' '.join(msg).rstrip('\n'))
63 super(LoggingUI, self).note(*msg, **opts)
63 super(LoggingUI, self).note(*msg, **opts)
64
64
65 def debug(self, *msg, **opts):
65 def debug(self, *msg, **opts):
66 log.debug(' '.join(msg).rstrip('\n'))
66 log.debug(' '.join(msg).rstrip('\n'))
67 super(LoggingUI, self).debug(*msg, **opts)
67 super(LoggingUI, self).debug(*msg, **opts)
68
68
69 baseui = LoggingUI()
69 baseui = LoggingUI()
70
70
71 # clean the baseui object
71 # clean the baseui object
72 baseui._ocfg = hgconfig.config()
72 baseui._ocfg = hgconfig.config()
73 baseui._ucfg = hgconfig.config()
73 baseui._ucfg = hgconfig.config()
74 baseui._tcfg = hgconfig.config()
74 baseui._tcfg = hgconfig.config()
75
75
76 for section, option, value in repo_config:
76 for section, option, value in repo_config:
77 baseui.setconfig(section, option, value)
77 baseui.setconfig(section, option, value)
78
78
79 # make our hgweb quiet so it doesn't print output
79 # make our hgweb quiet so it doesn't print output
80 baseui.setconfig('ui', 'quiet', 'true')
80 baseui.setconfig('ui', 'quiet', 'true')
81
81
82 baseui.setconfig('ui', 'paginate', 'never')
82 baseui.setconfig('ui', 'paginate', 'never')
83 # for better Error reporting of Mercurial
83 # for better Error reporting of Mercurial
84 baseui.setconfig('ui', 'message-output', 'stderr')
84 baseui.setconfig('ui', 'message-output', 'stderr')
85
85
86 # force mercurial to only use 1 thread, otherwise it may try to set a
86 # force mercurial to only use 1 thread, otherwise it may try to set a
87 # signal in a non-main thread, thus generating a ValueError.
87 # signal in a non-main thread, thus generating a ValueError.
88 baseui.setconfig('worker', 'numcpus', 1)
88 baseui.setconfig('worker', 'numcpus', 1)
89
89
90 # If there is no config for the largefiles extension, we explicitly disable
90 # If there is no config for the largefiles extension, we explicitly disable
91 # it here. This overrides settings from repositories hgrc file. Recent
91 # it here. This overrides settings from repositories hgrc file. Recent
92 # mercurial versions enable largefiles in hgrc on clone from largefile
92 # mercurial versions enable largefiles in hgrc on clone from largefile
93 # repo.
93 # repo.
94 if not baseui.hasconfig('extensions', 'largefiles'):
94 if not baseui.hasconfig('extensions', 'largefiles'):
95 log.debug('Explicitly disable largefiles extension for repo.')
95 log.debug('Explicitly disable largefiles extension for repo.')
96 baseui.setconfig('extensions', 'largefiles', '!')
96 baseui.setconfig('extensions', 'largefiles', '!')
97
97
98 return baseui
98 return baseui
99
99
100
100
101 def reraise_safe_exceptions(func):
101 def reraise_safe_exceptions(func):
102 """Decorator for converting mercurial exceptions to something neutral."""
102 """Decorator for converting mercurial exceptions to something neutral."""
103
103
104 def wrapper(*args, **kwargs):
104 def wrapper(*args, **kwargs):
105 try:
105 try:
106 return func(*args, **kwargs)
106 return func(*args, **kwargs)
107 except (Abort, InterventionRequired) as e:
107 except (Abort, InterventionRequired) as e:
108 raise_from_original(exceptions.AbortException(e))
108 raise_from_original(exceptions.AbortException(e))
109 except RepoLookupError as e:
109 except RepoLookupError as e:
110 raise_from_original(exceptions.LookupException(e))
110 raise_from_original(exceptions.LookupException(e))
111 except RequirementError as e:
111 except RequirementError as e:
112 raise_from_original(exceptions.RequirementException(e))
112 raise_from_original(exceptions.RequirementException(e))
113 except RepoError as e:
113 except RepoError as e:
114 raise_from_original(exceptions.VcsException(e))
114 raise_from_original(exceptions.VcsException(e))
115 except LookupError as e:
115 except LookupError as e:
116 raise_from_original(exceptions.LookupException(e))
116 raise_from_original(exceptions.LookupException(e))
117 except Exception as e:
117 except Exception as e:
118 if not hasattr(e, '_vcs_kind'):
118 if not hasattr(e, '_vcs_kind'):
119 log.exception("Unhandled exception in hg remote call")
119 log.exception("Unhandled exception in hg remote call")
120 raise_from_original(exceptions.UnhandledException(e))
120 raise_from_original(exceptions.UnhandledException(e))
121
121
122 raise
122 raise
123 return wrapper
123 return wrapper
124
124
125
125
126 class MercurialFactory(RepoFactory):
126 class MercurialFactory(RepoFactory):
127 repo_type = 'hg'
127 repo_type = 'hg'
128
128
129 def _create_config(self, config, hooks=True):
129 def _create_config(self, config, hooks=True):
130 if not hooks:
130 if not hooks:
131 hooks_to_clean = frozenset((
131 hooks_to_clean = frozenset((
132 'changegroup.repo_size', 'preoutgoing.pre_pull',
132 'changegroup.repo_size', 'preoutgoing.pre_pull',
133 'outgoing.pull_logger', 'prechangegroup.pre_push'))
133 'outgoing.pull_logger', 'prechangegroup.pre_push'))
134 new_config = []
134 new_config = []
135 for section, option, value in config:
135 for section, option, value in config:
136 if section == 'hooks' and option in hooks_to_clean:
136 if section == 'hooks' and option in hooks_to_clean:
137 continue
137 continue
138 new_config.append((section, option, value))
138 new_config.append((section, option, value))
139 config = new_config
139 config = new_config
140
140
141 baseui = make_ui_from_config(config)
141 baseui = make_ui_from_config(config)
142 return baseui
142 return baseui
143
143
144 def _create_repo(self, wire, create):
144 def _create_repo(self, wire, create):
145 baseui = self._create_config(wire["config"])
145 baseui = self._create_config(wire["config"])
146 return instance(baseui, wire["path"], create)
146 return instance(baseui, wire["path"], create)
147
147
148 def repo(self, wire, create=False):
148 def repo(self, wire, create=False):
149 """
149 """
150 Get a repository instance for the given path.
150 Get a repository instance for the given path.
151 """
151 """
152 return self._create_repo(wire, create)
152 return self._create_repo(wire, create)
153
153
154
154
155 def patch_ui_message_output(baseui):
155 def patch_ui_message_output(baseui):
156 baseui.setconfig('ui', 'quiet', 'false')
156 baseui.setconfig('ui', 'quiet', 'false')
157 output = io.BytesIO()
157 output = io.BytesIO()
158
158
159 def write(data, **unused_kwargs):
159 def write(data, **unused_kwargs):
160 output.write(data)
160 output.write(data)
161
161
162 baseui.status = write
162 baseui.status = write
163 baseui.write = write
163 baseui.write = write
164 baseui.warn = write
164 baseui.warn = write
165 baseui.debug = write
165 baseui.debug = write
166
166
167 return baseui, output
167 return baseui, output
168
168
169
169
170 class HgRemote(RemoteBase):
170 class HgRemote(RemoteBase):
171
171
172 def __init__(self, factory):
172 def __init__(self, factory):
173 self._factory = factory
173 self._factory = factory
174 self._bulk_methods = {
174 self._bulk_methods = {
175 "affected_files": self.ctx_files,
175 "affected_files": self.ctx_files,
176 "author": self.ctx_user,
176 "author": self.ctx_user,
177 "branch": self.ctx_branch,
177 "branch": self.ctx_branch,
178 "children": self.ctx_children,
178 "children": self.ctx_children,
179 "date": self.ctx_date,
179 "date": self.ctx_date,
180 "message": self.ctx_description,
180 "message": self.ctx_description,
181 "parents": self.ctx_parents,
181 "parents": self.ctx_parents,
182 "status": self.ctx_status,
182 "status": self.ctx_status,
183 "obsolete": self.ctx_obsolete,
183 "obsolete": self.ctx_obsolete,
184 "phase": self.ctx_phase,
184 "phase": self.ctx_phase,
185 "hidden": self.ctx_hidden,
185 "hidden": self.ctx_hidden,
186 "_file_paths": self.ctx_list,
186 "_file_paths": self.ctx_list,
187 }
187 }
188
188
189 def _get_ctx(self, repo, ref):
189 def _get_ctx(self, repo, ref):
190 return get_ctx(repo, ref)
190 return get_ctx(repo, ref)
191
191
192 @reraise_safe_exceptions
192 @reraise_safe_exceptions
193 def discover_hg_version(self):
193 def discover_hg_version(self):
194 from mercurial import util
194 from mercurial import util
195 return util.version()
195 return util.version()
196
196
197 @reraise_safe_exceptions
197 @reraise_safe_exceptions
198 def is_empty(self, wire):
198 def is_empty(self, wire):
199 repo = self._factory.repo(wire)
199 repo = self._factory.repo(wire)
200
200
201 try:
201 try:
202 return len(repo) == 0
202 return len(repo) == 0
203 except Exception:
203 except Exception:
204 log.exception("failed to read object_store")
204 log.exception("failed to read object_store")
205 return False
205 return False
206
206
207 @reraise_safe_exceptions
207 @reraise_safe_exceptions
208 def archive_repo(self, archive_path, mtime, file_info, kind):
208 def archive_repo(self, archive_path, mtime, file_info, kind):
209 if kind == "tgz":
209 if kind == "tgz":
210 archiver = archival.tarit(archive_path, mtime, "gz")
210 archiver = archival.tarit(archive_path, mtime, "gz")
211 elif kind == "tbz2":
211 elif kind == "tbz2":
212 archiver = archival.tarit(archive_path, mtime, "bz2")
212 archiver = archival.tarit(archive_path, mtime, "bz2")
213 elif kind == 'zip':
213 elif kind == 'zip':
214 archiver = archival.zipit(archive_path, mtime)
214 archiver = archival.zipit(archive_path, mtime)
215 else:
215 else:
216 raise exceptions.ArchiveException()(
216 raise exceptions.ArchiveException()(
217 'Remote does not support: "%s".' % kind)
217 'Remote does not support: "%s".' % kind)
218
218
219 for f_path, f_mode, f_is_link, f_content in file_info:
219 for f_path, f_mode, f_is_link, f_content in file_info:
220 archiver.addfile(f_path, f_mode, f_is_link, f_content)
220 archiver.addfile(f_path, f_mode, f_is_link, f_content)
221 archiver.done()
221 archiver.done()
222
222
223 @reraise_safe_exceptions
223 @reraise_safe_exceptions
224 def bookmarks(self, wire):
224 def bookmarks(self, wire):
225 cache_on, context_uid, repo_id = self._cache_on(wire)
225 cache_on, context_uid, repo_id = self._cache_on(wire)
226 @self.region.conditional_cache_on_arguments(condition=cache_on)
226 @self.region.conditional_cache_on_arguments(condition=cache_on)
227 def _bookmarks(_context_uid, _repo_id):
227 def _bookmarks(_context_uid, _repo_id):
228 repo = self._factory.repo(wire)
228 repo = self._factory.repo(wire)
229 return dict(repo._bookmarks)
229 return dict(repo._bookmarks)
230
230
231 return _bookmarks(context_uid, repo_id)
231 return _bookmarks(context_uid, repo_id)
232
232
233 @reraise_safe_exceptions
233 @reraise_safe_exceptions
234 def branches(self, wire, normal, closed):
234 def branches(self, wire, normal, closed):
235 cache_on, context_uid, repo_id = self._cache_on(wire)
235 cache_on, context_uid, repo_id = self._cache_on(wire)
236 @self.region.conditional_cache_on_arguments(condition=cache_on)
236 @self.region.conditional_cache_on_arguments(condition=cache_on)
237 def _branches(_context_uid, _repo_id, _normal, _closed):
237 def _branches(_context_uid, _repo_id, _normal, _closed):
238 repo = self._factory.repo(wire)
238 repo = self._factory.repo(wire)
239 iter_branches = repo.branchmap().iterbranches()
239 iter_branches = repo.branchmap().iterbranches()
240 bt = {}
240 bt = {}
241 for branch_name, _heads, tip, is_closed in iter_branches:
241 for branch_name, _heads, tip, is_closed in iter_branches:
242 if normal and not is_closed:
242 if normal and not is_closed:
243 bt[branch_name] = tip
243 bt[branch_name] = tip
244 if closed and is_closed:
244 if closed and is_closed:
245 bt[branch_name] = tip
245 bt[branch_name] = tip
246
246
247 return bt
247 return bt
248
248
249 return _branches(context_uid, repo_id, normal, closed)
249 return _branches(context_uid, repo_id, normal, closed)
250
250
251 @reraise_safe_exceptions
251 @reraise_safe_exceptions
252 def bulk_request(self, wire, commit_id, pre_load):
252 def bulk_request(self, wire, commit_id, pre_load):
253 cache_on, context_uid, repo_id = self._cache_on(wire)
253 cache_on, context_uid, repo_id = self._cache_on(wire)
254 @self.region.conditional_cache_on_arguments(condition=cache_on)
254 @self.region.conditional_cache_on_arguments(condition=cache_on)
255 def _bulk_request(_repo_id, _commit_id, _pre_load):
255 def _bulk_request(_repo_id, _commit_id, _pre_load):
256 result = {}
256 result = {}
257 for attr in pre_load:
257 for attr in pre_load:
258 try:
258 try:
259 method = self._bulk_methods[attr]
259 method = self._bulk_methods[attr]
260 result[attr] = method(wire, commit_id)
260 result[attr] = method(wire, commit_id)
261 except KeyError as e:
261 except KeyError as e:
262 raise exceptions.VcsException(e)(
262 raise exceptions.VcsException(e)(
263 'Unknown bulk attribute: "%s"' % attr)
263 'Unknown bulk attribute: "%s"' % attr)
264 return result
264 return result
265
265
266 return _bulk_request(repo_id, commit_id, sorted(pre_load))
266 return _bulk_request(repo_id, commit_id, sorted(pre_load))
267
267
268 @reraise_safe_exceptions
268 @reraise_safe_exceptions
269 def ctx_branch(self, wire, commit_id):
269 def ctx_branch(self, wire, commit_id):
270 cache_on, context_uid, repo_id = self._cache_on(wire)
270 cache_on, context_uid, repo_id = self._cache_on(wire)
271 @self.region.conditional_cache_on_arguments(condition=cache_on)
271 @self.region.conditional_cache_on_arguments(condition=cache_on)
272 def _ctx_branch(_repo_id, _commit_id):
272 def _ctx_branch(_repo_id, _commit_id):
273 repo = self._factory.repo(wire)
273 repo = self._factory.repo(wire)
274 ctx = self._get_ctx(repo, commit_id)
274 ctx = self._get_ctx(repo, commit_id)
275 return ctx.branch()
275 return ctx.branch()
276 return _ctx_branch(repo_id, commit_id)
276 return _ctx_branch(repo_id, commit_id)
277
277
278 @reraise_safe_exceptions
278 @reraise_safe_exceptions
279 def ctx_date(self, wire, commit_id):
279 def ctx_date(self, wire, commit_id):
280 cache_on, context_uid, repo_id = self._cache_on(wire)
280 cache_on, context_uid, repo_id = self._cache_on(wire)
281 @self.region.conditional_cache_on_arguments(condition=cache_on)
281 @self.region.conditional_cache_on_arguments(condition=cache_on)
282 def _ctx_date(_repo_id, _commit_id):
282 def _ctx_date(_repo_id, _commit_id):
283 repo = self._factory.repo(wire)
283 repo = self._factory.repo(wire)
284 ctx = self._get_ctx(repo, commit_id)
284 ctx = self._get_ctx(repo, commit_id)
285 return ctx.date()
285 return ctx.date()
286 return _ctx_date(repo_id, commit_id)
286 return _ctx_date(repo_id, commit_id)
287
287
288 @reraise_safe_exceptions
288 @reraise_safe_exceptions
289 def ctx_description(self, wire, revision):
289 def ctx_description(self, wire, revision):
290 repo = self._factory.repo(wire)
290 repo = self._factory.repo(wire)
291 ctx = self._get_ctx(repo, revision)
291 ctx = self._get_ctx(repo, revision)
292 return ctx.description()
292 return ctx.description()
293
293
294 @reraise_safe_exceptions
294 @reraise_safe_exceptions
295 def ctx_files(self, wire, commit_id):
295 def ctx_files(self, wire, commit_id):
296 cache_on, context_uid, repo_id = self._cache_on(wire)
296 cache_on, context_uid, repo_id = self._cache_on(wire)
297 @self.region.conditional_cache_on_arguments(condition=cache_on)
297 @self.region.conditional_cache_on_arguments(condition=cache_on)
298 def _ctx_files(_repo_id, _commit_id):
298 def _ctx_files(_repo_id, _commit_id):
299 repo = self._factory.repo(wire)
299 repo = self._factory.repo(wire)
300 ctx = self._get_ctx(repo, commit_id)
300 ctx = self._get_ctx(repo, commit_id)
301 return ctx.files()
301 return ctx.files()
302
302
303 return _ctx_files(repo_id, commit_id)
303 return _ctx_files(repo_id, commit_id)
304
304
305 @reraise_safe_exceptions
305 @reraise_safe_exceptions
306 def ctx_list(self, path, revision):
306 def ctx_list(self, path, revision):
307 repo = self._factory.repo(path)
307 repo = self._factory.repo(path)
308 ctx = self._get_ctx(repo, revision)
308 ctx = self._get_ctx(repo, revision)
309 return list(ctx)
309 return list(ctx)
310
310
311 @reraise_safe_exceptions
311 @reraise_safe_exceptions
312 def ctx_parents(self, wire, commit_id):
312 def ctx_parents(self, wire, commit_id):
313 cache_on, context_uid, repo_id = self._cache_on(wire)
313 cache_on, context_uid, repo_id = self._cache_on(wire)
314 @self.region.conditional_cache_on_arguments(condition=cache_on)
314 @self.region.conditional_cache_on_arguments(condition=cache_on)
315 def _ctx_parents(_repo_id, _commit_id):
315 def _ctx_parents(_repo_id, _commit_id):
316 repo = self._factory.repo(wire)
316 repo = self._factory.repo(wire)
317 ctx = self._get_ctx(repo, commit_id)
317 ctx = self._get_ctx(repo, commit_id)
318 return [parent.hex() for parent in ctx.parents()
318 return [parent.hex() for parent in ctx.parents()
319 if not (parent.hidden() or parent.obsolete())]
319 if not (parent.hidden() or parent.obsolete())]
320
320
321 return _ctx_parents(repo_id, commit_id)
321 return _ctx_parents(repo_id, commit_id)
322
322
323 @reraise_safe_exceptions
323 @reraise_safe_exceptions
324 def ctx_children(self, wire, commit_id):
324 def ctx_children(self, wire, commit_id):
325 cache_on, context_uid, repo_id = self._cache_on(wire)
325 cache_on, context_uid, repo_id = self._cache_on(wire)
326 @self.region.conditional_cache_on_arguments(condition=cache_on)
326 @self.region.conditional_cache_on_arguments(condition=cache_on)
327 def _ctx_children(_repo_id, _commit_id):
327 def _ctx_children(_repo_id, _commit_id):
328 repo = self._factory.repo(wire)
328 repo = self._factory.repo(wire)
329 ctx = self._get_ctx(repo, commit_id)
329 ctx = self._get_ctx(repo, commit_id)
330 return [child.hex() for child in ctx.children()
330 return [child.hex() for child in ctx.children()
331 if not (child.hidden() or child.obsolete())]
331 if not (child.hidden() or child.obsolete())]
332
332
333 return _ctx_children(repo_id, commit_id)
333 return _ctx_children(repo_id, commit_id)
334
334
335 @reraise_safe_exceptions
335 @reraise_safe_exceptions
336 def ctx_phase(self, wire, commit_id):
336 def ctx_phase(self, wire, commit_id):
337 cache_on, context_uid, repo_id = self._cache_on(wire)
337 cache_on, context_uid, repo_id = self._cache_on(wire)
338 @self.region.conditional_cache_on_arguments(condition=cache_on)
338 @self.region.conditional_cache_on_arguments(condition=cache_on)
339 def _ctx_phase(_context_uid, _repo_id, _commit_id):
339 def _ctx_phase(_context_uid, _repo_id, _commit_id):
340 repo = self._factory.repo(wire)
340 repo = self._factory.repo(wire)
341 ctx = self._get_ctx(repo, commit_id)
341 ctx = self._get_ctx(repo, commit_id)
342 # public=0, draft=1, secret=3
342 # public=0, draft=1, secret=3
343 return ctx.phase()
343 return ctx.phase()
344 return _ctx_phase(context_uid, repo_id, commit_id)
344 return _ctx_phase(context_uid, repo_id, commit_id)
345
345
346 @reraise_safe_exceptions
346 @reraise_safe_exceptions
347 def ctx_obsolete(self, wire, commit_id):
347 def ctx_obsolete(self, wire, commit_id):
348 cache_on, context_uid, repo_id = self._cache_on(wire)
348 cache_on, context_uid, repo_id = self._cache_on(wire)
349 @self.region.conditional_cache_on_arguments(condition=cache_on)
349 @self.region.conditional_cache_on_arguments(condition=cache_on)
350 def _ctx_obsolete(_context_uid, _repo_id, _commit_id):
350 def _ctx_obsolete(_context_uid, _repo_id, _commit_id):
351 repo = self._factory.repo(wire)
351 repo = self._factory.repo(wire)
352 ctx = self._get_ctx(repo, commit_id)
352 ctx = self._get_ctx(repo, commit_id)
353 return ctx.obsolete()
353 return ctx.obsolete()
354 return _ctx_obsolete(context_uid, repo_id, commit_id)
354 return _ctx_obsolete(context_uid, repo_id, commit_id)
355
355
356 @reraise_safe_exceptions
356 @reraise_safe_exceptions
357 def ctx_hidden(self, wire, commit_id):
357 def ctx_hidden(self, wire, commit_id):
358 cache_on, context_uid, repo_id = self._cache_on(wire)
358 cache_on, context_uid, repo_id = self._cache_on(wire)
359 @self.region.conditional_cache_on_arguments(condition=cache_on)
359 @self.region.conditional_cache_on_arguments(condition=cache_on)
360 def _ctx_hidden(_context_uid, _repo_id, _commit_id):
360 def _ctx_hidden(_context_uid, _repo_id, _commit_id):
361 repo = self._factory.repo(wire)
361 repo = self._factory.repo(wire)
362 ctx = self._get_ctx(repo, commit_id)
362 ctx = self._get_ctx(repo, commit_id)
363 return ctx.hidden()
363 return ctx.hidden()
364 return _ctx_hidden(context_uid, repo_id, commit_id)
364 return _ctx_hidden(context_uid, repo_id, commit_id)
365
365
366 @reraise_safe_exceptions
366 @reraise_safe_exceptions
367 def ctx_substate(self, wire, revision):
367 def ctx_substate(self, wire, revision):
368 repo = self._factory.repo(wire)
368 repo = self._factory.repo(wire)
369 ctx = self._get_ctx(repo, revision)
369 ctx = self._get_ctx(repo, revision)
370 return ctx.substate
370 return ctx.substate
371
371
372 @reraise_safe_exceptions
372 @reraise_safe_exceptions
373 def ctx_status(self, wire, revision):
373 def ctx_status(self, wire, revision):
374 repo = self._factory.repo(wire)
374 repo = self._factory.repo(wire)
375 ctx = self._get_ctx(repo, revision)
375 ctx = self._get_ctx(repo, revision)
376 status = repo[ctx.p1().node()].status(other=ctx.node())
376 status = repo[ctx.p1().node()].status(other=ctx.node())
377 # object of status (odd, custom named tuple in mercurial) is not
377 # object of status (odd, custom named tuple in mercurial) is not
378 # correctly serializable, we make it a list, as the underling
378 # correctly serializable, we make it a list, as the underling
379 # API expects this to be a list
379 # API expects this to be a list
380 return list(status)
380 return list(status)
381
381
382 @reraise_safe_exceptions
382 @reraise_safe_exceptions
383 def ctx_user(self, wire, revision):
383 def ctx_user(self, wire, revision):
384 repo = self._factory.repo(wire)
384 repo = self._factory.repo(wire)
385 ctx = self._get_ctx(repo, revision)
385 ctx = self._get_ctx(repo, revision)
386 return ctx.user()
386 return ctx.user()
387
387
388 @reraise_safe_exceptions
388 @reraise_safe_exceptions
389 def check_url(self, url, config):
389 def check_url(self, url, config):
390 _proto = None
390 _proto = None
391 if '+' in url[:url.find('://')]:
391 if '+' in url[:url.find('://')]:
392 _proto = url[0:url.find('+')]
392 _proto = url[0:url.find('+')]
393 url = url[url.find('+') + 1:]
393 url = url[url.find('+') + 1:]
394 handlers = []
394 handlers = []
395 url_obj = url_parser(url)
395 url_obj = url_parser(url)
396 test_uri, authinfo = url_obj.authinfo()
396 test_uri, authinfo = url_obj.authinfo()
397 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
397 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
398 url_obj.query = obfuscate_qs(url_obj.query)
398 url_obj.query = obfuscate_qs(url_obj.query)
399
399
400 cleaned_uri = str(url_obj)
400 cleaned_uri = str(url_obj)
401 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
401 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
402
402
403 if authinfo:
403 if authinfo:
404 # create a password manager
404 # create a password manager
405 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
405 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
406 passmgr.add_password(*authinfo)
406 passmgr.add_password(*authinfo)
407
407
408 handlers.extend((httpbasicauthhandler(passmgr),
408 handlers.extend((httpbasicauthhandler(passmgr),
409 httpdigestauthhandler(passmgr)))
409 httpdigestauthhandler(passmgr)))
410
410
411 o = urllib2.build_opener(*handlers)
411 o = urllib2.build_opener(*handlers)
412 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
412 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
413 ('Accept', 'application/mercurial-0.1')]
413 ('Accept', 'application/mercurial-0.1')]
414
414
415 q = {"cmd": 'between'}
415 q = {"cmd": 'between'}
416 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
416 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
417 qs = '?%s' % urllib.urlencode(q)
417 qs = '?%s' % urllib.urlencode(q)
418 cu = "%s%s" % (test_uri, qs)
418 cu = "%s%s" % (test_uri, qs)
419 req = urllib2.Request(cu, None, {})
419 req = urllib2.Request(cu, None, {})
420
420
421 try:
421 try:
422 log.debug("Trying to open URL %s", cleaned_uri)
422 log.debug("Trying to open URL %s", cleaned_uri)
423 resp = o.open(req)
423 resp = o.open(req)
424 if resp.code != 200:
424 if resp.code != 200:
425 raise exceptions.URLError()('Return Code is not 200')
425 raise exceptions.URLError()('Return Code is not 200')
426 except Exception as e:
426 except Exception as e:
427 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
427 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
428 # means it cannot be cloned
428 # means it cannot be cloned
429 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
429 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
430
430
431 # now check if it's a proper hg repo, but don't do it for svn
431 # now check if it's a proper hg repo, but don't do it for svn
432 try:
432 try:
433 if _proto == 'svn':
433 if _proto == 'svn':
434 pass
434 pass
435 else:
435 else:
436 # check for pure hg repos
436 # check for pure hg repos
437 log.debug(
437 log.debug(
438 "Verifying if URL is a Mercurial repository: %s",
438 "Verifying if URL is a Mercurial repository: %s",
439 cleaned_uri)
439 cleaned_uri)
440 ui = make_ui_from_config(config)
440 ui = make_ui_from_config(config)
441 peer_checker = makepeer(ui, url)
441 peer_checker = makepeer(ui, url)
442 peer_checker.lookup('tip')
442 peer_checker.lookup('tip')
443 except Exception as e:
443 except Exception as e:
444 log.warning("URL is not a valid Mercurial repository: %s",
444 log.warning("URL is not a valid Mercurial repository: %s",
445 cleaned_uri)
445 cleaned_uri)
446 raise exceptions.URLError(e)(
446 raise exceptions.URLError(e)(
447 "url [%s] does not look like an hg repo org_exc: %s"
447 "url [%s] does not look like an hg repo org_exc: %s"
448 % (cleaned_uri, e))
448 % (cleaned_uri, e))
449
449
450 log.info("URL is a valid Mercurial repository: %s", cleaned_uri)
450 log.info("URL is a valid Mercurial repository: %s", cleaned_uri)
451 return True
451 return True
452
452
453 @reraise_safe_exceptions
453 @reraise_safe_exceptions
454 def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_git, opt_ignorews, context):
454 def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_git, opt_ignorews, context):
455 repo = self._factory.repo(wire)
455 repo = self._factory.repo(wire)
456
456
457 if file_filter:
457 if file_filter:
458 match_filter = match(file_filter[0], '', [file_filter[1]])
458 match_filter = match(file_filter[0], '', [file_filter[1]])
459 else:
459 else:
460 match_filter = file_filter
460 match_filter = file_filter
461 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
461 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context, showfunc=1)
462
462
463 try:
463 try:
464 return "".join(patch.diff(
464 return "".join(patch.diff(
465 repo, node1=commit_id_1, node2=commit_id_2, match=match_filter, opts=opts))
465 repo, node1=commit_id_1, node2=commit_id_2, match=match_filter, opts=opts))
466 except RepoLookupError as e:
466 except RepoLookupError as e:
467 raise exceptions.LookupException(e)()
467 raise exceptions.LookupException(e)()
468
468
469 @reraise_safe_exceptions
469 @reraise_safe_exceptions
470 def node_history(self, wire, revision, path, limit):
470 def node_history(self, wire, revision, path, limit):
471 cache_on, context_uid, repo_id = self._cache_on(wire)
471 cache_on, context_uid, repo_id = self._cache_on(wire)
472 @self.region.conditional_cache_on_arguments(condition=cache_on)
472 @self.region.conditional_cache_on_arguments(condition=cache_on)
473 def _node_history(_context_uid, _repo_id, _revision, _path, _limit):
473 def _node_history(_context_uid, _repo_id, _revision, _path, _limit):
474 repo = self._factory.repo(wire)
474 repo = self._factory.repo(wire)
475
475
476 ctx = self._get_ctx(repo, revision)
476 ctx = self._get_ctx(repo, revision)
477 fctx = ctx.filectx(path)
477 fctx = ctx.filectx(path)
478
478
479 def history_iter():
479 def history_iter():
480 limit_rev = fctx.rev()
480 limit_rev = fctx.rev()
481 for obj in reversed(list(fctx.filelog())):
481 for obj in reversed(list(fctx.filelog())):
482 obj = fctx.filectx(obj)
482 obj = fctx.filectx(obj)
483 ctx = obj.changectx()
483 ctx = obj.changectx()
484 if ctx.hidden() or ctx.obsolete():
484 if ctx.hidden() or ctx.obsolete():
485 continue
485 continue
486
486
487 if limit_rev >= obj.rev():
487 if limit_rev >= obj.rev():
488 yield obj
488 yield obj
489
489
490 history = []
490 history = []
491 for cnt, obj in enumerate(history_iter()):
491 for cnt, obj in enumerate(history_iter()):
492 if limit and cnt >= limit:
492 if limit and cnt >= limit:
493 break
493 break
494 history.append(hex(obj.node()))
494 history.append(hex(obj.node()))
495
495
496 return [x for x in history]
496 return [x for x in history]
497 return _node_history(context_uid, repo_id, revision, path, limit)
497 return _node_history(context_uid, repo_id, revision, path, limit)
498
498
499 @reraise_safe_exceptions
499 @reraise_safe_exceptions
500 def node_history_untill(self, wire, revision, path, limit):
500 def node_history_untill(self, wire, revision, path, limit):
501 cache_on, context_uid, repo_id = self._cache_on(wire)
501 cache_on, context_uid, repo_id = self._cache_on(wire)
502 @self.region.conditional_cache_on_arguments(condition=cache_on)
502 @self.region.conditional_cache_on_arguments(condition=cache_on)
503 def _node_history_until(_context_uid, _repo_id):
503 def _node_history_until(_context_uid, _repo_id):
504 repo = self._factory.repo(wire)
504 repo = self._factory.repo(wire)
505 ctx = self._get_ctx(repo, revision)
505 ctx = self._get_ctx(repo, revision)
506 fctx = ctx.filectx(path)
506 fctx = ctx.filectx(path)
507
507
508 file_log = list(fctx.filelog())
508 file_log = list(fctx.filelog())
509 if limit:
509 if limit:
510 # Limit to the last n items
510 # Limit to the last n items
511 file_log = file_log[-limit:]
511 file_log = file_log[-limit:]
512
512
513 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
513 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
514 return _node_history_until(context_uid, repo_id, revision, path, limit)
514 return _node_history_until(context_uid, repo_id, revision, path, limit)
515
515
516 @reraise_safe_exceptions
516 @reraise_safe_exceptions
517 def fctx_annotate(self, wire, revision, path):
517 def fctx_annotate(self, wire, revision, path):
518 repo = self._factory.repo(wire)
518 repo = self._factory.repo(wire)
519 ctx = self._get_ctx(repo, revision)
519 ctx = self._get_ctx(repo, revision)
520 fctx = ctx.filectx(path)
520 fctx = ctx.filectx(path)
521
521
522 result = []
522 result = []
523 for i, annotate_obj in enumerate(fctx.annotate(), 1):
523 for i, annotate_obj in enumerate(fctx.annotate(), 1):
524 ln_no = i
524 ln_no = i
525 sha = hex(annotate_obj.fctx.node())
525 sha = hex(annotate_obj.fctx.node())
526 content = annotate_obj.text
526 content = annotate_obj.text
527 result.append((ln_no, sha, content))
527 result.append((ln_no, sha, content))
528 return result
528 return result
529
529
530 @reraise_safe_exceptions
530 @reraise_safe_exceptions
531 def fctx_node_data(self, wire, revision, path):
531 def fctx_node_data(self, wire, revision, path):
532 repo = self._factory.repo(wire)
532 repo = self._factory.repo(wire)
533 ctx = self._get_ctx(repo, revision)
533 ctx = self._get_ctx(repo, revision)
534 fctx = ctx.filectx(path)
534 fctx = ctx.filectx(path)
535 return fctx.data()
535 return fctx.data()
536
536
537 @reraise_safe_exceptions
537 @reraise_safe_exceptions
538 def fctx_flags(self, wire, commit_id, path):
538 def fctx_flags(self, wire, commit_id, path):
539 cache_on, context_uid, repo_id = self._cache_on(wire)
539 cache_on, context_uid, repo_id = self._cache_on(wire)
540 @self.region.conditional_cache_on_arguments(condition=cache_on)
540 @self.region.conditional_cache_on_arguments(condition=cache_on)
541 def _fctx_flags(_repo_id, _commit_id, _path):
541 def _fctx_flags(_repo_id, _commit_id, _path):
542 repo = self._factory.repo(wire)
542 repo = self._factory.repo(wire)
543 ctx = self._get_ctx(repo, commit_id)
543 ctx = self._get_ctx(repo, commit_id)
544 fctx = ctx.filectx(path)
544 fctx = ctx.filectx(path)
545 return fctx.flags()
545 return fctx.flags()
546
546
547 return _fctx_flags(repo_id, commit_id, path)
547 return _fctx_flags(repo_id, commit_id, path)
548
548
549 @reraise_safe_exceptions
549 @reraise_safe_exceptions
550 def fctx_size(self, wire, commit_id, path):
550 def fctx_size(self, wire, commit_id, path):
551 cache_on, context_uid, repo_id = self._cache_on(wire)
551 cache_on, context_uid, repo_id = self._cache_on(wire)
552 @self.region.conditional_cache_on_arguments(condition=cache_on)
552 @self.region.conditional_cache_on_arguments(condition=cache_on)
553 def _fctx_size(_repo_id, _revision, _path):
553 def _fctx_size(_repo_id, _revision, _path):
554 repo = self._factory.repo(wire)
554 repo = self._factory.repo(wire)
555 ctx = self._get_ctx(repo, commit_id)
555 ctx = self._get_ctx(repo, commit_id)
556 fctx = ctx.filectx(path)
556 fctx = ctx.filectx(path)
557 return fctx.size()
557 return fctx.size()
558 return _fctx_size(repo_id, commit_id, path)
558 return _fctx_size(repo_id, commit_id, path)
559
559
560 @reraise_safe_exceptions
560 @reraise_safe_exceptions
561 def get_all_commit_ids(self, wire, name):
561 def get_all_commit_ids(self, wire, name):
562 cache_on, context_uid, repo_id = self._cache_on(wire)
562 cache_on, context_uid, repo_id = self._cache_on(wire)
563 @self.region.conditional_cache_on_arguments(condition=cache_on)
563 @self.region.conditional_cache_on_arguments(condition=cache_on)
564 def _get_all_commit_ids(_context_uid, _repo_id, _name):
564 def _get_all_commit_ids(_context_uid, _repo_id, _name):
565 repo = self._factory.repo(wire)
565 repo = self._factory.repo(wire)
566 repo = repo.filtered(name)
566 repo = repo.filtered(name)
567 revs = map(lambda x: hex(x[7]), repo.changelog.index)
567 revs = map(lambda x: hex(x[7]), repo.changelog.index)
568 return revs