##// END OF EJS Templates

Compare Commits r97:e424cba775fe...r100:2582dee7dd4c

Target:

Source:

Time Author Commit Description
Martin Bornhold
r97:e424cba775fe
subrepo: Add patch to turn off mercurial subrepo handling.
Martin Bornhold
r98:2fa39df61008
subrepo: Add custom exception to indicate subrepo merge conflict.
Martin Bornhold
r99:e1008af8f033
subrepo: Turn off interactive mode when merging mercurial repo. If there is a merge conflict in the sub repositories mercurial will prompt the used to decide which version to use.
Martin Bornhold
r100:2582dee7dd4c
subrepo: Apply mercurial sub repository patch.
@@ -1,68 +1,70
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-2016 RodeCode GmbH
2 # Copyright (C) 2014-2016 RodeCode 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 """
18 """
19 Special exception handling over the wire.
19 Special exception handling over the wire.
20
20
21 Since we cannot assume that our client is able to import our exception classes,
21 Since we cannot assume that our client is able to import our exception classes,
22 this module provides a "wrapping" mechanism to raise plain exceptions
22 this module provides a "wrapping" mechanism to raise plain exceptions
23 which contain an extra attribute `_vcs_kind` to allow a client to distinguish
23 which contain an extra attribute `_vcs_kind` to allow a client to distinguish
24 different error conditions.
24 different error conditions.
25 """
25 """
26
26
27 import functools
27 import functools
28 from pyramid.httpexceptions import HTTPLocked
28 from pyramid.httpexceptions import HTTPLocked
29
29
30
30
31 def _make_exception(kind, *args):
31 def _make_exception(kind, *args):
32 """
32 """
33 Prepares a base `Exception` instance to be sent over the wire.
33 Prepares a base `Exception` instance to be sent over the wire.
34
34
35 To give our caller a hint what this is about, it will attach an attribute
35 To give our caller a hint what this is about, it will attach an attribute
36 `_vcs_kind` to the exception.
36 `_vcs_kind` to the exception.
37 """
37 """
38 exc = Exception(*args)
38 exc = Exception(*args)
39 exc._vcs_kind = kind
39 exc._vcs_kind = kind
40 return exc
40 return exc
41
41
42
42
43 AbortException = functools.partial(_make_exception, 'abort')
43 AbortException = functools.partial(_make_exception, 'abort')
44
44
45 ArchiveException = functools.partial(_make_exception, 'archive')
45 ArchiveException = functools.partial(_make_exception, 'archive')
46
46
47 LookupException = functools.partial(_make_exception, 'lookup')
47 LookupException = functools.partial(_make_exception, 'lookup')
48
48
49 VcsException = functools.partial(_make_exception, 'error')
49 VcsException = functools.partial(_make_exception, 'error')
50
50
51 RepositoryLockedException = functools.partial(_make_exception, 'repo_locked')
51 RepositoryLockedException = functools.partial(_make_exception, 'repo_locked')
52
52
53 RequirementException = functools.partial(_make_exception, 'requirement')
53 RequirementException = functools.partial(_make_exception, 'requirement')
54
54
55 UnhandledException = functools.partial(_make_exception, 'unhandled')
55 UnhandledException = functools.partial(_make_exception, 'unhandled')
56
56
57 URLError = functools.partial(_make_exception, 'url_error')
57 URLError = functools.partial(_make_exception, 'url_error')
58
58
59 SubrepoMergeException = functools.partial(_make_exception, 'subrepo_merge_error')
60
59
61
60 class HTTPRepoLocked(HTTPLocked):
62 class HTTPRepoLocked(HTTPLocked):
61 """
63 """
62 Subclass of HTTPLocked response that allows to set the title and status
64 Subclass of HTTPLocked response that allows to set the title and status
63 code via constructor arguments.
65 code via constructor arguments.
64 """
66 """
65 def __init__(self, title, status_code=None, **kwargs):
67 def __init__(self, title, status_code=None, **kwargs):
66 self.code = status_code or HTTPLocked.code
68 self.code = status_code or HTTPLocked.code
67 self.title = title
69 self.title = title
68 super(HTTPRepoLocked, self).__init__(**kwargs)
70 super(HTTPRepoLocked, self).__init__(**kwargs)
@@ -1,707 +1,714
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-2016 RodeCode GmbH
2 # Copyright (C) 2014-2016 RodeCode 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 sys
21 import sys
22 import urllib
22 import urllib
23 import urllib2
23 import urllib2
24
24
25 from hgext import largefiles, rebase
25 from hgext import largefiles, rebase
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
29
30 from vcsserver import exceptions
30 from vcsserver import exceptions
31 from vcsserver.base import RepoFactory
31 from vcsserver.base import RepoFactory
32 from vcsserver.hgcompat import (
32 from vcsserver.hgcompat import (
33 archival, bin, clone, config as hgconfig, diffopts, hex, hg_url,
33 archival, bin, clone, config as hgconfig, diffopts, hex, hg_url,
34 httpbasicauthhandler, httpdigestauthhandler, httppeer, localrepository,
34 httpbasicauthhandler, httpdigestauthhandler, httppeer, localrepository,
35 match, memctx, exchange, memfilectx, nullrev, patch, peer, revrange, ui,
35 match, memctx, exchange, memfilectx, nullrev, patch, peer, revrange, ui,
36 Abort, LookupError, RepoError, RepoLookupError, InterventionRequired,
36 Abort, LookupError, RepoError, RepoLookupError, InterventionRequired,
37 RequirementError)
37 RequirementError)
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 def make_ui_from_config(repo_config):
42 def make_ui_from_config(repo_config):
43 baseui = ui.ui()
43 baseui = ui.ui()
44
44
45 # clean the baseui object
45 # clean the baseui object
46 baseui._ocfg = hgconfig.config()
46 baseui._ocfg = hgconfig.config()
47 baseui._ucfg = hgconfig.config()
47 baseui._ucfg = hgconfig.config()
48 baseui._tcfg = hgconfig.config()
48 baseui._tcfg = hgconfig.config()
49
49
50 for section, option, value in repo_config:
50 for section, option, value in repo_config:
51 baseui.setconfig(section, option, value)
51 baseui.setconfig(section, option, value)
52
52
53 # make our hgweb quiet so it doesn't print output
53 # make our hgweb quiet so it doesn't print output
54 baseui.setconfig('ui', 'quiet', 'true')
54 baseui.setconfig('ui', 'quiet', 'true')
55
55
56 # force mercurial to only use 1 thread, otherwise it may try to set a
56 # force mercurial to only use 1 thread, otherwise it may try to set a
57 # signal in a non-main thread, thus generating a ValueError.
57 # signal in a non-main thread, thus generating a ValueError.
58 baseui.setconfig('worker', 'numcpus', 1)
58 baseui.setconfig('worker', 'numcpus', 1)
59
59
60 # If there is no config for the largefiles extension, we explicitly disable
60 # If there is no config for the largefiles extension, we explicitly disable
61 # it here. This overrides settings from repositories hgrc file. Recent
61 # it here. This overrides settings from repositories hgrc file. Recent
62 # mercurial versions enable largefiles in hgrc on clone from largefile
62 # mercurial versions enable largefiles in hgrc on clone from largefile
63 # repo.
63 # repo.
64 if not baseui.hasconfig('extensions', 'largefiles'):
64 if not baseui.hasconfig('extensions', 'largefiles'):
65 log.debug('Explicitly disable largefiles extension for repo.')
65 log.debug('Explicitly disable largefiles extension for repo.')
66 baseui.setconfig('extensions', 'largefiles', '!')
66 baseui.setconfig('extensions', 'largefiles', '!')
67
67
68 return baseui
68 return baseui
69
69
70
70
71 def reraise_safe_exceptions(func):
71 def reraise_safe_exceptions(func):
72 """Decorator for converting mercurial exceptions to something neutral."""
72 """Decorator for converting mercurial exceptions to something neutral."""
73 def wrapper(*args, **kwargs):
73 def wrapper(*args, **kwargs):
74 try:
74 try:
75 return func(*args, **kwargs)
75 return func(*args, **kwargs)
76 except (Abort, InterventionRequired):
76 except (Abort, InterventionRequired):
77 raise_from_original(exceptions.AbortException)
77 raise_from_original(exceptions.AbortException)
78 except RepoLookupError:
78 except RepoLookupError:
79 raise_from_original(exceptions.LookupException)
79 raise_from_original(exceptions.LookupException)
80 except RequirementError:
80 except RequirementError:
81 raise_from_original(exceptions.RequirementException)
81 raise_from_original(exceptions.RequirementException)
82 except RepoError:
82 except RepoError:
83 raise_from_original(exceptions.VcsException)
83 raise_from_original(exceptions.VcsException)
84 except LookupError:
84 except LookupError:
85 raise_from_original(exceptions.LookupException)
85 raise_from_original(exceptions.LookupException)
86 except Exception as e:
86 except Exception as e:
87 if not hasattr(e, '_vcs_kind'):
87 if not hasattr(e, '_vcs_kind'):
88 log.exception("Unhandled exception in hg remote call")
88 log.exception("Unhandled exception in hg remote call")
89 raise_from_original(exceptions.UnhandledException)
89 raise_from_original(exceptions.UnhandledException)
90 raise
90 raise
91 return wrapper
91 return wrapper
92
92
93
93
94 def raise_from_original(new_type):
94 def raise_from_original(new_type):
95 """
95 """
96 Raise a new exception type with original args and traceback.
96 Raise a new exception type with original args and traceback.
97 """
97 """
98 _, original, traceback = sys.exc_info()
98 _, original, traceback = sys.exc_info()
99 try:
99 try:
100 raise new_type(*original.args), None, traceback
100 raise new_type(*original.args), None, traceback
101 finally:
101 finally:
102 del traceback
102 del traceback
103
103
104
104
105 class MercurialFactory(RepoFactory):
105 class MercurialFactory(RepoFactory):
106
106
107 def _create_config(self, config, hooks=True):
107 def _create_config(self, config, hooks=True):
108 if not hooks:
108 if not hooks:
109 hooks_to_clean = frozenset((
109 hooks_to_clean = frozenset((
110 'changegroup.repo_size', 'preoutgoing.pre_pull',
110 'changegroup.repo_size', 'preoutgoing.pre_pull',
111 'outgoing.pull_logger', 'prechangegroup.pre_push'))
111 'outgoing.pull_logger', 'prechangegroup.pre_push'))
112 new_config = []
112 new_config = []
113 for section, option, value in config:
113 for section, option, value in config:
114 if section == 'hooks' and option in hooks_to_clean:
114 if section == 'hooks' and option in hooks_to_clean:
115 continue
115 continue
116 new_config.append((section, option, value))
116 new_config.append((section, option, value))
117 config = new_config
117 config = new_config
118
118
119 baseui = make_ui_from_config(config)
119 baseui = make_ui_from_config(config)
120 return baseui
120 return baseui
121
121
122 def _create_repo(self, wire, create):
122 def _create_repo(self, wire, create):
123 baseui = self._create_config(wire["config"])
123 baseui = self._create_config(wire["config"])
124 return localrepository(baseui, wire["path"], create)
124 return localrepository(baseui, wire["path"], create)
125
125
126
126
127 class HgRemote(object):
127 class HgRemote(object):
128
128
129 def __init__(self, factory):
129 def __init__(self, factory):
130 self._factory = factory
130 self._factory = factory
131
131
132 self._bulk_methods = {
132 self._bulk_methods = {
133 "affected_files": self.ctx_files,
133 "affected_files": self.ctx_files,
134 "author": self.ctx_user,
134 "author": self.ctx_user,
135 "branch": self.ctx_branch,
135 "branch": self.ctx_branch,
136 "children": self.ctx_children,
136 "children": self.ctx_children,
137 "date": self.ctx_date,
137 "date": self.ctx_date,
138 "message": self.ctx_description,
138 "message": self.ctx_description,
139 "parents": self.ctx_parents,
139 "parents": self.ctx_parents,
140 "status": self.ctx_status,
140 "status": self.ctx_status,
141 "_file_paths": self.ctx_list,
141 "_file_paths": self.ctx_list,
142 }
142 }
143
143
144 @reraise_safe_exceptions
144 @reraise_safe_exceptions
145 def archive_repo(self, archive_path, mtime, file_info, kind):
145 def archive_repo(self, archive_path, mtime, file_info, kind):
146 if kind == "tgz":
146 if kind == "tgz":
147 archiver = archival.tarit(archive_path, mtime, "gz")
147 archiver = archival.tarit(archive_path, mtime, "gz")
148 elif kind == "tbz2":
148 elif kind == "tbz2":
149 archiver = archival.tarit(archive_path, mtime, "bz2")
149 archiver = archival.tarit(archive_path, mtime, "bz2")
150 elif kind == 'zip':
150 elif kind == 'zip':
151 archiver = archival.zipit(archive_path, mtime)
151 archiver = archival.zipit(archive_path, mtime)
152 else:
152 else:
153 raise exceptions.ArchiveException(
153 raise exceptions.ArchiveException(
154 'Remote does not support: "%s".' % kind)
154 'Remote does not support: "%s".' % kind)
155
155
156 for f_path, f_mode, f_is_link, f_content in file_info:
156 for f_path, f_mode, f_is_link, f_content in file_info:
157 archiver.addfile(f_path, f_mode, f_is_link, f_content)
157 archiver.addfile(f_path, f_mode, f_is_link, f_content)
158 archiver.done()
158 archiver.done()
159
159
160 @reraise_safe_exceptions
160 @reraise_safe_exceptions
161 def bookmarks(self, wire):
161 def bookmarks(self, wire):
162 repo = self._factory.repo(wire)
162 repo = self._factory.repo(wire)
163 return dict(repo._bookmarks)
163 return dict(repo._bookmarks)
164
164
165 @reraise_safe_exceptions
165 @reraise_safe_exceptions
166 def branches(self, wire, normal, closed):
166 def branches(self, wire, normal, closed):
167 repo = self._factory.repo(wire)
167 repo = self._factory.repo(wire)
168 iter_branches = repo.branchmap().iterbranches()
168 iter_branches = repo.branchmap().iterbranches()
169 bt = {}
169 bt = {}
170 for branch_name, _heads, tip, is_closed in iter_branches:
170 for branch_name, _heads, tip, is_closed in iter_branches:
171 if normal and not is_closed:
171 if normal and not is_closed:
172 bt[branch_name] = tip
172 bt[branch_name] = tip
173 if closed and is_closed:
173 if closed and is_closed:
174 bt[branch_name] = tip
174 bt[branch_name] = tip
175
175
176 return bt
176 return bt
177
177
178 @reraise_safe_exceptions
178 @reraise_safe_exceptions
179 def bulk_request(self, wire, rev, pre_load):
179 def bulk_request(self, wire, rev, pre_load):
180 result = {}
180 result = {}
181 for attr in pre_load:
181 for attr in pre_load:
182 try:
182 try:
183 method = self._bulk_methods[attr]
183 method = self._bulk_methods[attr]
184 result[attr] = method(wire, rev)
184 result[attr] = method(wire, rev)
185 except KeyError:
185 except KeyError:
186 raise exceptions.VcsException(
186 raise exceptions.VcsException(
187 'Unknown bulk attribute: "%s"' % attr)
187 'Unknown bulk attribute: "%s"' % attr)
188 return result
188 return result
189
189
190 @reraise_safe_exceptions
190 @reraise_safe_exceptions
191 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
191 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
192 baseui = self._factory._create_config(wire["config"], hooks=hooks)
192 baseui = self._factory._create_config(wire["config"], hooks=hooks)
193 clone(baseui, source, dest, noupdate=not update_after_clone)
193 clone(baseui, source, dest, noupdate=not update_after_clone)
194
194
195 @reraise_safe_exceptions
195 @reraise_safe_exceptions
196 def commitctx(
196 def commitctx(
197 self, wire, message, parents, commit_time, commit_timezone,
197 self, wire, message, parents, commit_time, commit_timezone,
198 user, files, extra, removed, updated):
198 user, files, extra, removed, updated):
199
199
200 def _filectxfn(_repo, memctx, path):
200 def _filectxfn(_repo, memctx, path):
201 """
201 """
202 Marks given path as added/changed/removed in a given _repo. This is
202 Marks given path as added/changed/removed in a given _repo. This is
203 for internal mercurial commit function.
203 for internal mercurial commit function.
204 """
204 """
205
205
206 # check if this path is removed
206 # check if this path is removed
207 if path in removed:
207 if path in removed:
208 # returning None is a way to mark node for removal
208 # returning None is a way to mark node for removal
209 return None
209 return None
210
210
211 # check if this path is added
211 # check if this path is added
212 for node in updated:
212 for node in updated:
213 if node['path'] == path:
213 if node['path'] == path:
214 return memfilectx(
214 return memfilectx(
215 _repo,
215 _repo,
216 path=node['path'],
216 path=node['path'],
217 data=node['content'],
217 data=node['content'],
218 islink=False,
218 islink=False,
219 isexec=bool(node['mode'] & stat.S_IXUSR),
219 isexec=bool(node['mode'] & stat.S_IXUSR),
220 copied=False,
220 copied=False,
221 memctx=memctx)
221 memctx=memctx)
222
222
223 raise exceptions.AbortException(
223 raise exceptions.AbortException(
224 "Given path haven't been marked as added, "
224 "Given path haven't been marked as added, "
225 "changed or removed (%s)" % path)
225 "changed or removed (%s)" % path)
226
226
227 repo = self._factory.repo(wire)
227 repo = self._factory.repo(wire)
228
228
229 commit_ctx = memctx(
229 commit_ctx = memctx(
230 repo=repo,
230 repo=repo,
231 parents=parents,
231 parents=parents,
232 text=message,
232 text=message,
233 files=files,
233 files=files,
234 filectxfn=_filectxfn,
234 filectxfn=_filectxfn,
235 user=user,
235 user=user,
236 date=(commit_time, commit_timezone),
236 date=(commit_time, commit_timezone),
237 extra=extra)
237 extra=extra)
238
238
239 n = repo.commitctx(commit_ctx)
239 n = repo.commitctx(commit_ctx)
240 new_id = hex(n)
240 new_id = hex(n)
241
241
242 return new_id
242 return new_id
243
243
244 @reraise_safe_exceptions
244 @reraise_safe_exceptions
245 def ctx_branch(self, wire, revision):
245 def ctx_branch(self, wire, revision):
246 repo = self._factory.repo(wire)
246 repo = self._factory.repo(wire)
247 ctx = repo[revision]
247 ctx = repo[revision]
248 return ctx.branch()
248 return ctx.branch()
249
249
250 @reraise_safe_exceptions
250 @reraise_safe_exceptions
251 def ctx_children(self, wire, revision):
251 def ctx_children(self, wire, revision):
252 repo = self._factory.repo(wire)
252 repo = self._factory.repo(wire)
253 ctx = repo[revision]
253 ctx = repo[revision]
254 return [child.rev() for child in ctx.children()]
254 return [child.rev() for child in ctx.children()]
255
255
256 @reraise_safe_exceptions
256 @reraise_safe_exceptions
257 def ctx_date(self, wire, revision):
257 def ctx_date(self, wire, revision):
258 repo = self._factory.repo(wire)
258 repo = self._factory.repo(wire)
259 ctx = repo[revision]
259 ctx = repo[revision]
260 return ctx.date()
260 return ctx.date()
261
261
262 @reraise_safe_exceptions
262 @reraise_safe_exceptions
263 def ctx_description(self, wire, revision):
263 def ctx_description(self, wire, revision):
264 repo = self._factory.repo(wire)
264 repo = self._factory.repo(wire)
265 ctx = repo[revision]
265 ctx = repo[revision]
266 return ctx.description()
266 return ctx.description()
267
267
268 @reraise_safe_exceptions
268 @reraise_safe_exceptions
269 def ctx_diff(
269 def ctx_diff(
270 self, wire, revision, git=True, ignore_whitespace=True, context=3):
270 self, wire, revision, git=True, ignore_whitespace=True, context=3):
271 repo = self._factory.repo(wire)
271 repo = self._factory.repo(wire)
272 ctx = repo[revision]
272 ctx = repo[revision]
273 result = ctx.diff(
273 result = ctx.diff(
274 git=git, ignore_whitespace=ignore_whitespace, context=context)
274 git=git, ignore_whitespace=ignore_whitespace, context=context)
275 return list(result)
275 return list(result)
276
276
277 @reraise_safe_exceptions
277 @reraise_safe_exceptions
278 def ctx_files(self, wire, revision):
278 def ctx_files(self, wire, revision):
279 repo = self._factory.repo(wire)
279 repo = self._factory.repo(wire)
280 ctx = repo[revision]
280 ctx = repo[revision]
281 return ctx.files()
281 return ctx.files()
282
282
283 @reraise_safe_exceptions
283 @reraise_safe_exceptions
284 def ctx_list(self, path, revision):
284 def ctx_list(self, path, revision):
285 repo = self._factory.repo(path)
285 repo = self._factory.repo(path)
286 ctx = repo[revision]
286 ctx = repo[revision]
287 return list(ctx)
287 return list(ctx)
288
288
289 @reraise_safe_exceptions
289 @reraise_safe_exceptions
290 def ctx_parents(self, wire, revision):
290 def ctx_parents(self, wire, revision):
291 repo = self._factory.repo(wire)
291 repo = self._factory.repo(wire)
292 ctx = repo[revision]
292 ctx = repo[revision]
293 return [parent.rev() for parent in ctx.parents()]
293 return [parent.rev() for parent in ctx.parents()]
294
294
295 @reraise_safe_exceptions
295 @reraise_safe_exceptions
296 def ctx_substate(self, wire, revision):
296 def ctx_substate(self, wire, revision):
297 repo = self._factory.repo(wire)
297 repo = self._factory.repo(wire)
298 ctx = repo[revision]
298 ctx = repo[revision]
299 return ctx.substate
299 return ctx.substate
300
300
301 @reraise_safe_exceptions
301 @reraise_safe_exceptions
302 def ctx_status(self, wire, revision):
302 def ctx_status(self, wire, revision):
303 repo = self._factory.repo(wire)
303 repo = self._factory.repo(wire)
304 ctx = repo[revision]
304 ctx = repo[revision]
305 status = repo[ctx.p1().node()].status(other=ctx.node())
305 status = repo[ctx.p1().node()].status(other=ctx.node())
306 # object of status (odd, custom named tuple in mercurial) is not
306 # object of status (odd, custom named tuple in mercurial) is not
307 # correctly serializable via Pyro, we make it a list, as the underling
307 # correctly serializable via Pyro, we make it a list, as the underling
308 # API expects this to be a list
308 # API expects this to be a list
309 return list(status)
309 return list(status)
310
310
311 @reraise_safe_exceptions
311 @reraise_safe_exceptions
312 def ctx_user(self, wire, revision):
312 def ctx_user(self, wire, revision):
313 repo = self._factory.repo(wire)
313 repo = self._factory.repo(wire)
314 ctx = repo[revision]
314 ctx = repo[revision]
315 return ctx.user()
315 return ctx.user()
316
316
317 @reraise_safe_exceptions
317 @reraise_safe_exceptions
318 def check_url(self, url, config):
318 def check_url(self, url, config):
319 log.info("Checking URL for remote cloning/import: %s", url)
319 log.info("Checking URL for remote cloning/import: %s", url)
320 _proto = None
320 _proto = None
321 if '+' in url[:url.find('://')]:
321 if '+' in url[:url.find('://')]:
322 _proto = url[0:url.find('+')]
322 _proto = url[0:url.find('+')]
323 url = url[url.find('+') + 1:]
323 url = url[url.find('+') + 1:]
324 handlers = []
324 handlers = []
325 url_obj = hg_url(url)
325 url_obj = hg_url(url)
326 test_uri, authinfo = url_obj.authinfo()
326 test_uri, authinfo = url_obj.authinfo()
327 url_obj.passwd = '*****'
327 url_obj.passwd = '*****'
328 cleaned_uri = str(url_obj)
328 cleaned_uri = str(url_obj)
329
329
330 if authinfo:
330 if authinfo:
331 # create a password manager
331 # create a password manager
332 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
332 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
333 passmgr.add_password(*authinfo)
333 passmgr.add_password(*authinfo)
334
334
335 handlers.extend((httpbasicauthhandler(passmgr),
335 handlers.extend((httpbasicauthhandler(passmgr),
336 httpdigestauthhandler(passmgr)))
336 httpdigestauthhandler(passmgr)))
337
337
338 o = urllib2.build_opener(*handlers)
338 o = urllib2.build_opener(*handlers)
339 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
339 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
340 ('Accept', 'application/mercurial-0.1')]
340 ('Accept', 'application/mercurial-0.1')]
341
341
342 q = {"cmd": 'between'}
342 q = {"cmd": 'between'}
343 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
343 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
344 qs = '?%s' % urllib.urlencode(q)
344 qs = '?%s' % urllib.urlencode(q)
345 cu = "%s%s" % (test_uri, qs)
345 cu = "%s%s" % (test_uri, qs)
346 req = urllib2.Request(cu, None, {})
346 req = urllib2.Request(cu, None, {})
347
347
348 try:
348 try:
349 log.debug("Trying to open URL %s", url)
349 log.debug("Trying to open URL %s", url)
350 resp = o.open(req)
350 resp = o.open(req)
351 if resp.code != 200:
351 if resp.code != 200:
352 raise exceptions.URLError('Return Code is not 200')
352 raise exceptions.URLError('Return Code is not 200')
353 except Exception as e:
353 except Exception as e:
354 log.warning("URL cannot be opened: %s", url, exc_info=True)
354 log.warning("URL cannot be opened: %s", url, exc_info=True)
355 # means it cannot be cloned
355 # means it cannot be cloned
356 raise exceptions.URLError("[%s] org_exc: %s" % (cleaned_uri, e))
356 raise exceptions.URLError("[%s] org_exc: %s" % (cleaned_uri, e))
357
357
358 # now check if it's a proper hg repo, but don't do it for svn
358 # now check if it's a proper hg repo, but don't do it for svn
359 try:
359 try:
360 if _proto == 'svn':
360 if _proto == 'svn':
361 pass
361 pass
362 else:
362 else:
363 # check for pure hg repos
363 # check for pure hg repos
364 log.debug(
364 log.debug(
365 "Verifying if URL is a Mercurial repository: %s", url)
365 "Verifying if URL is a Mercurial repository: %s", url)
366 httppeer(make_ui_from_config(config), url).lookup('tip')
366 httppeer(make_ui_from_config(config), url).lookup('tip')
367 except Exception as e:
367 except Exception as e:
368 log.warning("URL is not a valid Mercurial repository: %s", url)
368 log.warning("URL is not a valid Mercurial repository: %s", url)
369 raise exceptions.URLError(
369 raise exceptions.URLError(
370 "url [%s] does not look like an hg repo org_exc: %s"
370 "url [%s] does not look like an hg repo org_exc: %s"
371 % (cleaned_uri, e))
371 % (cleaned_uri, e))
372
372
373 log.info("URL is a valid Mercurial repository: %s", url)
373 log.info("URL is a valid Mercurial repository: %s", url)
374 return True
374 return True
375
375
376 @reraise_safe_exceptions
376 @reraise_safe_exceptions
377 def diff(
377 def diff(
378 self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews,
378 self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews,
379 context):
379 context):
380 repo = self._factory.repo(wire)
380 repo = self._factory.repo(wire)
381
381
382 if file_filter:
382 if file_filter:
383 filter = match(file_filter[0], '', [file_filter[1]])
383 filter = match(file_filter[0], '', [file_filter[1]])
384 else:
384 else:
385 filter = file_filter
385 filter = file_filter
386 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
386 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
387
387
388 try:
388 try:
389 return "".join(patch.diff(
389 return "".join(patch.diff(
390 repo, node1=rev1, node2=rev2, match=filter, opts=opts))
390 repo, node1=rev1, node2=rev2, match=filter, opts=opts))
391 except RepoLookupError:
391 except RepoLookupError:
392 raise exceptions.LookupException()
392 raise exceptions.LookupException()
393
393
394 @reraise_safe_exceptions
394 @reraise_safe_exceptions
395 def file_history(self, wire, revision, path, limit):
395 def file_history(self, wire, revision, path, limit):
396 repo = self._factory.repo(wire)
396 repo = self._factory.repo(wire)
397
397
398 ctx = repo[revision]
398 ctx = repo[revision]
399 fctx = ctx.filectx(path)
399 fctx = ctx.filectx(path)
400
400
401 def history_iter():
401 def history_iter():
402 limit_rev = fctx.rev()
402 limit_rev = fctx.rev()
403 for obj in reversed(list(fctx.filelog())):
403 for obj in reversed(list(fctx.filelog())):
404 obj = fctx.filectx(obj)
404 obj = fctx.filectx(obj)
405 if limit_rev >= obj.rev():
405 if limit_rev >= obj.rev():
406 yield obj
406 yield obj
407
407
408 history = []
408 history = []
409 for cnt, obj in enumerate(history_iter()):
409 for cnt, obj in enumerate(history_iter()):
410 if limit and cnt >= limit:
410 if limit and cnt >= limit:
411 break
411 break
412 history.append(hex(obj.node()))
412 history.append(hex(obj.node()))
413
413
414 return [x for x in history]
414 return [x for x in history]
415
415
416 @reraise_safe_exceptions
416 @reraise_safe_exceptions
417 def file_history_untill(self, wire, revision, path, limit):
417 def file_history_untill(self, wire, revision, path, limit):
418 repo = self._factory.repo(wire)
418 repo = self._factory.repo(wire)
419 ctx = repo[revision]
419 ctx = repo[revision]
420 fctx = ctx.filectx(path)
420 fctx = ctx.filectx(path)
421
421
422 file_log = list(fctx.filelog())
422 file_log = list(fctx.filelog())
423 if limit:
423 if limit:
424 # Limit to the last n items
424 # Limit to the last n items
425 file_log = file_log[-limit:]
425 file_log = file_log[-limit:]
426
426
427 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
427 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
428
428
429 @reraise_safe_exceptions
429 @reraise_safe_exceptions
430 def fctx_annotate(self, wire, revision, path):
430 def fctx_annotate(self, wire, revision, path):
431 repo = self._factory.repo(wire)
431 repo = self._factory.repo(wire)
432 ctx = repo[revision]
432 ctx = repo[revision]
433 fctx = ctx.filectx(path)
433 fctx = ctx.filectx(path)
434
434
435 result = []
435 result = []
436 for i, annotate_data in enumerate(fctx.annotate()):
436 for i, annotate_data in enumerate(fctx.annotate()):
437 ln_no = i + 1
437 ln_no = i + 1
438 sha = hex(annotate_data[0].node())
438 sha = hex(annotate_data[0].node())
439 result.append((ln_no, sha, annotate_data[1]))
439 result.append((ln_no, sha, annotate_data[1]))
440 return result
440 return result
441
441
442 @reraise_safe_exceptions
442 @reraise_safe_exceptions
443 def fctx_data(self, wire, revision, path):
443 def fctx_data(self, wire, revision, path):
444 repo = self._factory.repo(wire)
444 repo = self._factory.repo(wire)
445 ctx = repo[revision]
445 ctx = repo[revision]
446 fctx = ctx.filectx(path)
446 fctx = ctx.filectx(path)
447 return fctx.data()
447 return fctx.data()
448
448
449 @reraise_safe_exceptions
449 @reraise_safe_exceptions
450 def fctx_flags(self, wire, revision, path):
450 def fctx_flags(self, wire, revision, path):
451 repo = self._factory.repo(wire)
451 repo = self._factory.repo(wire)
452 ctx = repo[revision]
452 ctx = repo[revision]
453 fctx = ctx.filectx(path)
453 fctx = ctx.filectx(path)
454 return fctx.flags()
454 return fctx.flags()
455
455
456 @reraise_safe_exceptions
456 @reraise_safe_exceptions
457 def fctx_size(self, wire, revision, path):
457 def fctx_size(self, wire, revision, path):
458 repo = self._factory.repo(wire)
458 repo = self._factory.repo(wire)
459 ctx = repo[revision]
459 ctx = repo[revision]
460 fctx = ctx.filectx(path)
460 fctx = ctx.filectx(path)
461 return fctx.size()
461 return fctx.size()
462
462
463 @reraise_safe_exceptions
463 @reraise_safe_exceptions
464 def get_all_commit_ids(self, wire, name):
464 def get_all_commit_ids(self, wire, name):
465 repo = self._factory.repo(wire)
465 repo = self._factory.repo(wire)
466 revs = repo.filtered(name).changelog.index
466 revs = repo.filtered(name).changelog.index
467 return map(lambda x: hex(x[7]), revs)[:-1]
467 return map(lambda x: hex(x[7]), revs)[:-1]
468
468
469 @reraise_safe_exceptions
469 @reraise_safe_exceptions
470 def get_config_value(self, wire, section, name, untrusted=False):
470 def get_config_value(self, wire, section, name, untrusted=False):
471 repo = self._factory.repo(wire)
471 repo = self._factory.repo(wire)
472 return repo.ui.config(section, name, untrusted=untrusted)
472 return repo.ui.config(section, name, untrusted=untrusted)
473
473
474 @reraise_safe_exceptions
474 @reraise_safe_exceptions
475 def get_config_bool(self, wire, section, name, untrusted=False):
475 def get_config_bool(self, wire, section, name, untrusted=False):
476 repo = self._factory.repo(wire)
476 repo = self._factory.repo(wire)
477 return repo.ui.configbool(section, name, untrusted=untrusted)
477 return repo.ui.configbool(section, name, untrusted=untrusted)
478
478
479 @reraise_safe_exceptions
479 @reraise_safe_exceptions
480 def get_config_list(self, wire, section, name, untrusted=False):
480 def get_config_list(self, wire, section, name, untrusted=False):
481 repo = self._factory.repo(wire)
481 repo = self._factory.repo(wire)
482 return repo.ui.configlist(section, name, untrusted=untrusted)
482 return repo.ui.configlist(section, name, untrusted=untrusted)
483
483
484 @reraise_safe_exceptions
484 @reraise_safe_exceptions
485 def is_large_file(self, wire, path):
485 def is_large_file(self, wire, path):
486 return largefiles.lfutil.isstandin(path)
486 return largefiles.lfutil.isstandin(path)
487
487
488 @reraise_safe_exceptions
488 @reraise_safe_exceptions
489 def in_store(self, wire, sha):
489 def in_store(self, wire, sha):
490 repo = self._factory.repo(wire)
490 repo = self._factory.repo(wire)
491 return largefiles.lfutil.instore(repo, sha)
491 return largefiles.lfutil.instore(repo, sha)
492
492
493 @reraise_safe_exceptions
493 @reraise_safe_exceptions
494 def in_user_cache(self, wire, sha):
494 def in_user_cache(self, wire, sha):
495 repo = self._factory.repo(wire)
495 repo = self._factory.repo(wire)
496 return largefiles.lfutil.inusercache(repo.ui, sha)
496 return largefiles.lfutil.inusercache(repo.ui, sha)
497
497
498 @reraise_safe_exceptions
498 @reraise_safe_exceptions
499 def store_path(self, wire, sha):
499 def store_path(self, wire, sha):
500 repo = self._factory.repo(wire)
500 repo = self._factory.repo(wire)
501 return largefiles.lfutil.storepath(repo, sha)
501 return largefiles.lfutil.storepath(repo, sha)
502
502
503 @reraise_safe_exceptions
503 @reraise_safe_exceptions
504 def link(self, wire, sha, path):
504 def link(self, wire, sha, path):
505 repo = self._factory.repo(wire)
505 repo = self._factory.repo(wire)
506 largefiles.lfutil.link(
506 largefiles.lfutil.link(
507 largefiles.lfutil.usercachepath(repo.ui, sha), path)
507 largefiles.lfutil.usercachepath(repo.ui, sha), path)
508
508
509 @reraise_safe_exceptions
509 @reraise_safe_exceptions
510 def localrepository(self, wire, create=False):
510 def localrepository(self, wire, create=False):
511 self._factory.repo(wire, create=create)
511 self._factory.repo(wire, create=create)
512
512
513 @reraise_safe_exceptions
513 @reraise_safe_exceptions
514 def lookup(self, wire, revision, both):
514 def lookup(self, wire, revision, both):
515 # TODO Paris: Ugly hack to "deserialize" long for msgpack
515 # TODO Paris: Ugly hack to "deserialize" long for msgpack
516 if isinstance(revision, float):
516 if isinstance(revision, float):
517