##// END OF EJS Templates
mercurial: expose function to check for unresolved files
marcink -
r798:a5646341 default
parent child Browse files
Show More
@@ -1,969 +1,990 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2019 RhodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import io
19 19 import logging
20 20 import stat
21 21 import urllib
22 22 import urllib2
23 23 import traceback
24 24
25 from hgext import largefiles, rebase
25 from hgext import largefiles, rebase, purge
26 26 from hgext.strip import strip as hgext_strip
27 27 from mercurial import commands
28 28 from mercurial import unionrepo
29 29 from mercurial import verify
30 30
31 31 import vcsserver
32 32 from vcsserver import exceptions
33 33 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
34 34 from vcsserver.hgcompat import (
35 35 archival, bin, clone, config as hgconfig, diffopts, hex, get_ctx,
36 36 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler,
37 37 makepeer, instance, match, memctx, exchange, memfilectx, nullrev, hg_merge,
38 38 patch, peer, revrange, ui, hg_tag, Abort, LookupError, RepoError,
39 39 RepoLookupError, InterventionRequired, RequirementError)
40 40 from vcsserver.vcs_base import RemoteBase
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 def make_ui_from_config(repo_config):
46 46
47 47 class LoggingUI(ui.ui):
48 48 def status(self, *msg, **opts):
49 49 log.info(' '.join(msg).rstrip('\n'))
50 50 super(LoggingUI, self).status(*msg, **opts)
51 51
52 52 def warn(self, *msg, **opts):
53 53 log.warn(' '.join(msg).rstrip('\n'))
54 54 super(LoggingUI, self).warn(*msg, **opts)
55 55
56 56 def error(self, *msg, **opts):
57 57 log.error(' '.join(msg).rstrip('\n'))
58 58 super(LoggingUI, self).error(*msg, **opts)
59 59
60 60 def note(self, *msg, **opts):
61 61 log.info(' '.join(msg).rstrip('\n'))
62 62 super(LoggingUI, self).note(*msg, **opts)
63 63
64 64 def debug(self, *msg, **opts):
65 65 log.debug(' '.join(msg).rstrip('\n'))
66 66 super(LoggingUI, self).debug(*msg, **opts)
67 67
68 68 baseui = LoggingUI()
69 69
70 70 # clean the baseui object
71 71 baseui._ocfg = hgconfig.config()
72 72 baseui._ucfg = hgconfig.config()
73 73 baseui._tcfg = hgconfig.config()
74 74
75 75 for section, option, value in repo_config:
76 76 baseui.setconfig(section, option, value)
77 77
78 78 # make our hgweb quiet so it doesn't print output
79 79 baseui.setconfig('ui', 'quiet', 'true')
80 80
81 81 baseui.setconfig('ui', 'paginate', 'never')
82 82 # for better Error reporting of Mercurial
83 83 baseui.setconfig('ui', 'message-output', 'stderr')
84 84
85 85 # force mercurial to only use 1 thread, otherwise it may try to set a
86 86 # signal in a non-main thread, thus generating a ValueError.
87 87 baseui.setconfig('worker', 'numcpus', 1)
88 88
89 89 # If there is no config for the largefiles extension, we explicitly disable
90 90 # it here. This overrides settings from repositories hgrc file. Recent
91 91 # mercurial versions enable largefiles in hgrc on clone from largefile
92 92 # repo.
93 93 if not baseui.hasconfig('extensions', 'largefiles'):
94 94 log.debug('Explicitly disable largefiles extension for repo.')
95 95 baseui.setconfig('extensions', 'largefiles', '!')
96 96
97 97 return baseui
98 98
99 99
100 100 def reraise_safe_exceptions(func):
101 101 """Decorator for converting mercurial exceptions to something neutral."""
102 102
103 103 def wrapper(*args, **kwargs):
104 104 try:
105 105 return func(*args, **kwargs)
106 106 except (Abort, InterventionRequired) as e:
107 107 raise_from_original(exceptions.AbortException(e))
108 108 except RepoLookupError as e:
109 109 raise_from_original(exceptions.LookupException(e))
110 110 except RequirementError as e:
111 111 raise_from_original(exceptions.RequirementException(e))
112 112 except RepoError as e:
113 113 raise_from_original(exceptions.VcsException(e))
114 114 except LookupError as e:
115 115 raise_from_original(exceptions.LookupException(e))
116 116 except Exception as e:
117 117 if not hasattr(e, '_vcs_kind'):
118 118 log.exception("Unhandled exception in hg remote call")
119 119 raise_from_original(exceptions.UnhandledException(e))
120 120
121 121 raise
122 122 return wrapper
123 123
124 124
125 125 class MercurialFactory(RepoFactory):
126 126 repo_type = 'hg'
127 127
128 128 def _create_config(self, config, hooks=True):
129 129 if not hooks:
130 130 hooks_to_clean = frozenset((
131 131 'changegroup.repo_size', 'preoutgoing.pre_pull',
132 132 'outgoing.pull_logger', 'prechangegroup.pre_push'))
133 133 new_config = []
134 134 for section, option, value in config:
135 135 if section == 'hooks' and option in hooks_to_clean:
136 136 continue
137 137 new_config.append((section, option, value))
138 138 config = new_config
139 139
140 140 baseui = make_ui_from_config(config)
141 141 return baseui
142 142
143 143 def _create_repo(self, wire, create):
144 144 baseui = self._create_config(wire["config"])
145 145 return instance(baseui, wire["path"], create)
146 146
147 147 def repo(self, wire, create=False):
148 148 """
149 149 Get a repository instance for the given path.
150 150 """
151 151 return self._create_repo(wire, create)
152 152
153 153
154 154 class HgRemote(RemoteBase):
155 155
156 156 def __init__(self, factory):
157 157 self._factory = factory
158 158 self._bulk_methods = {
159 159 "affected_files": self.ctx_files,
160 160 "author": self.ctx_user,
161 161 "branch": self.ctx_branch,
162 162 "children": self.ctx_children,
163 163 "date": self.ctx_date,
164 164 "message": self.ctx_description,
165 165 "parents": self.ctx_parents,
166 166 "status": self.ctx_status,
167 167 "obsolete": self.ctx_obsolete,
168 168 "phase": self.ctx_phase,
169 169 "hidden": self.ctx_hidden,
170 170 "_file_paths": self.ctx_list,
171 171 }
172 172
173 173 def _get_ctx(self, repo, ref):
174 174 return get_ctx(repo, ref)
175 175
176 176 @reraise_safe_exceptions
177 177 def discover_hg_version(self):
178 178 from mercurial import util
179 179 return util.version()
180 180
181 181 @reraise_safe_exceptions
182 182 def is_empty(self, wire):
183 183 repo = self._factory.repo(wire)
184 184
185 185 try:
186 186 return len(repo) == 0
187 187 except Exception:
188 188 log.exception("failed to read object_store")
189 189 return False
190 190
191 191 @reraise_safe_exceptions
192 192 def archive_repo(self, archive_path, mtime, file_info, kind):
193 193 if kind == "tgz":
194 194 archiver = archival.tarit(archive_path, mtime, "gz")
195 195 elif kind == "tbz2":
196 196 archiver = archival.tarit(archive_path, mtime, "bz2")
197 197 elif kind == 'zip':
198 198 archiver = archival.zipit(archive_path, mtime)
199 199 else:
200 200 raise exceptions.ArchiveException()(
201 201 'Remote does not support: "%s".' % kind)
202 202
203 203 for f_path, f_mode, f_is_link, f_content in file_info:
204 204 archiver.addfile(f_path, f_mode, f_is_link, f_content)
205 205 archiver.done()
206 206
207 207 @reraise_safe_exceptions
208 208 def bookmarks(self, wire):
209 209 cache_on, context_uid, repo_id = self._cache_on(wire)
210 210 @self.region.conditional_cache_on_arguments(condition=cache_on)
211 211 def _bookmarks(_context_uid, _repo_id):
212 212 repo = self._factory.repo(wire)
213 213 return dict(repo._bookmarks)
214 214
215 215 return _bookmarks(context_uid, repo_id)
216 216
217 217 @reraise_safe_exceptions
218 218 def branches(self, wire, normal, closed):
219 219 cache_on, context_uid, repo_id = self._cache_on(wire)
220 220 @self.region.conditional_cache_on_arguments(condition=cache_on)
221 221 def _branches(_context_uid, _repo_id, _normal, _closed):
222 222 repo = self._factory.repo(wire)
223 223 iter_branches = repo.branchmap().iterbranches()
224 224 bt = {}
225 225 for branch_name, _heads, tip, is_closed in iter_branches:
226 226 if normal and not is_closed:
227 227 bt[branch_name] = tip
228 228 if closed and is_closed:
229 229 bt[branch_name] = tip
230 230
231 231 return bt
232 232
233 233 return _branches(context_uid, repo_id, normal, closed)
234 234
235 235 @reraise_safe_exceptions
236 236 def bulk_request(self, wire, commit_id, pre_load):
237 237 cache_on, context_uid, repo_id = self._cache_on(wire)
238 238 @self.region.conditional_cache_on_arguments(condition=cache_on)
239 239 def _bulk_request(_repo_id, _commit_id, _pre_load):
240 240 result = {}
241 241 for attr in pre_load:
242 242 try:
243 243 method = self._bulk_methods[attr]
244 244 result[attr] = method(wire, commit_id)
245 245 except KeyError as e:
246 246 raise exceptions.VcsException(e)(
247 247 'Unknown bulk attribute: "%s"' % attr)
248 248 return result
249 249
250 250 return _bulk_request(repo_id, commit_id, sorted(pre_load))
251 251
252 252 @reraise_safe_exceptions
253 253 def ctx_branch(self, wire, commit_id):
254 254 cache_on, context_uid, repo_id = self._cache_on(wire)
255 255 @self.region.conditional_cache_on_arguments(condition=cache_on)
256 256 def _ctx_branch(_repo_id, _commit_id):
257 257 repo = self._factory.repo(wire)
258 258 ctx = self._get_ctx(repo, commit_id)
259 259 return ctx.branch()
260 260 return _ctx_branch(repo_id, commit_id)
261 261
262 262 @reraise_safe_exceptions
263 263 def ctx_date(self, wire, commit_id):
264 264 cache_on, context_uid, repo_id = self._cache_on(wire)
265 265 @self.region.conditional_cache_on_arguments(condition=cache_on)
266 266 def _ctx_date(_repo_id, _commit_id):
267 267 repo = self._factory.repo(wire)
268 268 ctx = self._get_ctx(repo, commit_id)
269 269 return ctx.date()
270 270 return _ctx_date(repo_id, commit_id)
271 271
272 272 @reraise_safe_exceptions
273 273 def ctx_description(self, wire, revision):
274 274 repo = self._factory.repo(wire)
275 275 ctx = self._get_ctx(repo, revision)
276 276 return ctx.description()
277 277
278 278 @reraise_safe_exceptions
279 279 def ctx_files(self, wire, commit_id):
280 280 cache_on, context_uid, repo_id = self._cache_on(wire)
281 281 @self.region.conditional_cache_on_arguments(condition=cache_on)
282 282 def _ctx_files(_repo_id, _commit_id):
283 283 repo = self._factory.repo(wire)
284 284 ctx = self._get_ctx(repo, commit_id)
285 285 return ctx.files()
286 286
287 287 return _ctx_files(repo_id, commit_id)
288 288
289 289 @reraise_safe_exceptions
290 290 def ctx_list(self, path, revision):
291 291 repo = self._factory.repo(path)
292 292 ctx = self._get_ctx(repo, revision)
293 293 return list(ctx)
294 294
295 295 @reraise_safe_exceptions
296 296 def ctx_parents(self, wire, commit_id):
297 297 cache_on, context_uid, repo_id = self._cache_on(wire)
298 298 @self.region.conditional_cache_on_arguments(condition=cache_on)
299 299 def _ctx_parents(_repo_id, _commit_id):
300 300 repo = self._factory.repo(wire)
301 301 ctx = self._get_ctx(repo, commit_id)
302 302 return [parent.hex() for parent in ctx.parents()
303 303 if not (parent.hidden() or parent.obsolete())]
304 304
305 305 return _ctx_parents(repo_id, commit_id)
306 306
307 307 @reraise_safe_exceptions
308 308 def ctx_children(self, wire, commit_id):
309 309 cache_on, context_uid, repo_id = self._cache_on(wire)
310 310 @self.region.conditional_cache_on_arguments(condition=cache_on)
311 311 def _ctx_children(_repo_id, _commit_id):
312 312 repo = self._factory.repo(wire)
313 313 ctx = self._get_ctx(repo, commit_id)
314 314 return [child.hex() for child in ctx.children()
315 315 if not (child.hidden() or child.obsolete())]
316 316
317 317 return _ctx_children(repo_id, commit_id)
318 318
319 319 @reraise_safe_exceptions
320 320 def ctx_phase(self, wire, commit_id):
321 321 cache_on, context_uid, repo_id = self._cache_on(wire)
322 322 @self.region.conditional_cache_on_arguments(condition=cache_on)
323 323 def _ctx_phase(_context_uid, _repo_id, _commit_id):
324 324 repo = self._factory.repo(wire)
325 325 ctx = self._get_ctx(repo, commit_id)
326 326 # public=0, draft=1, secret=3
327 327 return ctx.phase()
328 328 return _ctx_phase(context_uid, repo_id, commit_id)
329 329
330 330 @reraise_safe_exceptions
331 331 def ctx_obsolete(self, wire, commit_id):
332 332 cache_on, context_uid, repo_id = self._cache_on(wire)
333 333 @self.region.conditional_cache_on_arguments(condition=cache_on)
334 334 def _ctx_obsolete(_context_uid, _repo_id, _commit_id):
335 335 repo = self._factory.repo(wire)
336 336 ctx = self._get_ctx(repo, commit_id)
337 337 return ctx.obsolete()
338 338 return _ctx_obsolete(context_uid, repo_id, commit_id)
339 339
340 340 @reraise_safe_exceptions
341 341 def ctx_hidden(self, wire, commit_id):
342 342 cache_on, context_uid, repo_id = self._cache_on(wire)
343 343 @self.region.conditional_cache_on_arguments(condition=cache_on)
344 344 def _ctx_hidden(_context_uid, _repo_id, _commit_id):
345 345 repo = self._factory.repo(wire)
346 346 ctx = self._get_ctx(repo, commit_id)
347 347 return ctx.hidden()
348 348 return _ctx_hidden(context_uid, repo_id, commit_id)
349 349
350 350 @reraise_safe_exceptions
351 351 def ctx_substate(self, wire, revision):
352 352 repo = self._factory.repo(wire)
353 353 ctx = self._get_ctx(repo, revision)
354 354 return ctx.substate
355 355
356 356 @reraise_safe_exceptions
357 357 def ctx_status(self, wire, revision):
358 358 repo = self._factory.repo(wire)
359 359 ctx = self._get_ctx(repo, revision)
360 360 status = repo[ctx.p1().node()].status(other=ctx.node())
361 361 # object of status (odd, custom named tuple in mercurial) is not
362 362 # correctly serializable, we make it a list, as the underling
363 363 # API expects this to be a list
364 364 return list(status)
365 365
366 366 @reraise_safe_exceptions
367 367 def ctx_user(self, wire, revision):
368 368 repo = self._factory.repo(wire)
369 369 ctx = self._get_ctx(repo, revision)
370 370 return ctx.user()
371 371
372 372 @reraise_safe_exceptions
373 373 def check_url(self, url, config):
374 374 _proto = None
375 375 if '+' in url[:url.find('://')]:
376 376 _proto = url[0:url.find('+')]
377 377 url = url[url.find('+') + 1:]
378 378 handlers = []
379 379 url_obj = url_parser(url)
380 380 test_uri, authinfo = url_obj.authinfo()
381 381 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
382 382 url_obj.query = obfuscate_qs(url_obj.query)
383 383
384 384 cleaned_uri = str(url_obj)
385 385 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
386 386
387 387 if authinfo:
388 388 # create a password manager
389 389 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
390 390 passmgr.add_password(*authinfo)
391 391
392 392 handlers.extend((httpbasicauthhandler(passmgr),
393 393 httpdigestauthhandler(passmgr)))
394 394
395 395 o = urllib2.build_opener(*handlers)
396 396 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
397 397 ('Accept', 'application/mercurial-0.1')]
398 398
399 399 q = {"cmd": 'between'}
400 400 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
401 401 qs = '?%s' % urllib.urlencode(q)
402 402 cu = "%s%s" % (test_uri, qs)
403 403 req = urllib2.Request(cu, None, {})
404 404
405 405 try:
406 406 log.debug("Trying to open URL %s", cleaned_uri)
407 407 resp = o.open(req)
408 408 if resp.code != 200:
409 409 raise exceptions.URLError()('Return Code is not 200')
410 410 except Exception as e:
411 411 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
412 412 # means it cannot be cloned
413 413 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
414 414
415 415 # now check if it's a proper hg repo, but don't do it for svn
416 416 try:
417 417 if _proto == 'svn':
418 418 pass
419 419 else:
420 420 # check for pure hg repos
421 421 log.debug(
422 422 "Verifying if URL is a Mercurial repository: %s",
423 423 cleaned_uri)
424 424 ui = make_ui_from_config(config)
425 425 peer_checker = makepeer(ui, url)
426 426 peer_checker.lookup('tip')
427 427 except Exception as e:
428 428 log.warning("URL is not a valid Mercurial repository: %s",
429 429 cleaned_uri)
430 430 raise exceptions.URLError(e)(
431 431 "url [%s] does not look like an hg repo org_exc: %s"
432 432 % (cleaned_uri, e))
433 433
434 434 log.info("URL is a valid Mercurial repository: %s", cleaned_uri)
435 435 return True
436 436
437 437 @reraise_safe_exceptions
438 438 def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_git, opt_ignorews, context):
439 439 repo = self._factory.repo(wire)
440 440
441 441 if file_filter:
442 442 match_filter = match(file_filter[0], '', [file_filter[1]])
443 443 else:
444 444 match_filter = file_filter
445 445 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
446 446
447 447 try:
448 448 return "".join(patch.diff(
449 449 repo, node1=commit_id_1, node2=commit_id_2, match=match_filter, opts=opts))
450 450 except RepoLookupError as e:
451 451 raise exceptions.LookupException(e)()
452 452
453 453 @reraise_safe_exceptions
454 454 def node_history(self, wire, revision, path, limit):
455 455 cache_on, context_uid, repo_id = self._cache_on(wire)
456 456 @self.region.conditional_cache_on_arguments(condition=cache_on)
457 457 def _node_history(_context_uid, _repo_id, _revision, _path, _limit):
458 458 repo = self._factory.repo(wire)
459 459
460 460 ctx = self._get_ctx(repo, revision)
461 461 fctx = ctx.filectx(path)
462 462
463 463 def history_iter():
464 464 limit_rev = fctx.rev()
465 465 for obj in reversed(list(fctx.filelog())):
466 466 obj = fctx.filectx(obj)
467 467 ctx = obj.changectx()
468 468 if ctx.hidden() or ctx.obsolete():
469 469 continue
470 470
471 471 if limit_rev >= obj.rev():
472 472 yield obj
473 473
474 474 history = []
475 475 for cnt, obj in enumerate(history_iter()):
476 476 if limit and cnt >= limit:
477 477 break
478 478 history.append(hex(obj.node()))
479 479
480 480 return [x for x in history]
481 481 return _node_history(context_uid, repo_id, revision, path, limit)
482 482
483 483 @reraise_safe_exceptions
484 484 def node_history_untill(self, wire, revision, path, limit):
485 485 cache_on, context_uid, repo_id = self._cache_on(wire)
486 486 @self.region.conditional_cache_on_arguments(condition=cache_on)
487 487 def _node_history_until(_context_uid, _repo_id):
488 488 repo = self._factory.repo(wire)
489 489 ctx = self._get_ctx(repo, revision)
490 490 fctx = ctx.filectx(path)
491 491
492 492 file_log = list(fctx.filelog())
493 493 if limit:
494 494 # Limit to the last n items
495 495 file_log = file_log[-limit:]
496 496
497 497 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
498 498 return _node_history_until(context_uid, repo_id, revision, path, limit)
499 499
500 500 @reraise_safe_exceptions
501 501 def fctx_annotate(self, wire, revision, path):
502 502 repo = self._factory.repo(wire)
503 503 ctx = self._get_ctx(repo, revision)
504 504 fctx = ctx.filectx(path)
505 505
506 506 result = []
507 507 for i, annotate_obj in enumerate(fctx.annotate(), 1):
508 508 ln_no = i
509 509 sha = hex(annotate_obj.fctx.node())
510 510 content = annotate_obj.text
511 511 result.append((ln_no, sha, content))
512 512 return result
513 513
514 514 @reraise_safe_exceptions
515 515 def fctx_node_data(self, wire, revision, path):
516 516 repo = self._factory.repo(wire)
517 517 ctx = self._get_ctx(repo, revision)
518 518 fctx = ctx.filectx(path)
519 519 return fctx.data()
520 520
521 521 @reraise_safe_exceptions
522 522 def fctx_flags(self, wire, commit_id, path):
523 523 cache_on, context_uid, repo_id = self._cache_on(wire)
524 524 @self.region.conditional_cache_on_arguments(condition=cache_on)
525 525 def _fctx_flags(_repo_id, _commit_id, _path):
526 526 repo = self._factory.repo(wire)
527 527 ctx = self._get_ctx(repo, commit_id)
528 528 fctx = ctx.filectx(path)
529 529 return fctx.flags()
530 530
531 531 return _fctx_flags(repo_id, commit_id, path)
532 532
533 533 @reraise_safe_exceptions
534 534 def fctx_size(self, wire, commit_id, path):
535 535 cache_on, context_uid, repo_id = self._cache_on(wire)
536 536 @self.region.conditional_cache_on_arguments(condition=cache_on)
537 537 def _fctx_size(_repo_id, _revision, _path):
538 538 repo = self._factory.repo(wire)
539 539 ctx = self._get_ctx(repo, commit_id)
540 540 fctx = ctx.filectx(path)
541 541 return fctx.size()
542 542 return _fctx_size(repo_id, commit_id, path)
543 543
544 544 @reraise_safe_exceptions
545 545 def get_all_commit_ids(self, wire, name):
546 546 cache_on, context_uid, repo_id = self._cache_on(wire)
547 547 @self.region.conditional_cache_on_arguments(condition=cache_on)
548 548 def _get_all_commit_ids(_context_uid, _repo_id, _name):
549 549 repo = self._factory.repo(wire)
550 550 repo = repo.filtered(name)
551 551 revs = map(lambda x: hex(x[7]), repo.changelog.index)
552 552 return revs
553 553 return _get_all_commit_ids(context_uid, repo_id, name)
554 554
555 555 @reraise_safe_exceptions
556 556 def get_config_value(self, wire, section, name, untrusted=False):
557 557 repo = self._factory.repo(wire)
558 558 return repo.ui.config(section, name, untrusted=untrusted)
559 559
560 560 @reraise_safe_exceptions
561 561 def is_large_file(self, wire, commit_id, path):
562 562 cache_on, context_uid, repo_id = self._cache_on(wire)
563 563 @self.region.conditional_cache_on_arguments(condition=cache_on)
564 564 def _is_large_file(_context_uid, _repo_id, _commit_id, _path):
565 565 return largefiles.lfutil.isstandin(path)
566 566
567 567 return _is_large_file(context_uid, repo_id, commit_id, path)
568 568
569 569 @reraise_safe_exceptions
570 570 def is_binary(self, wire, revision, path):
571 571 cache_on, context_uid, repo_id = self._cache_on(wire)
572 572
573 573 @self.region.conditional_cache_on_arguments(condition=cache_on)
574 574 def _is_binary(_repo_id, _sha, _path):
575 575 repo = self._factory.repo(wire)
576 576 ctx = self._get_ctx(repo, revision)
577 577 fctx = ctx.filectx(path)
578 578 return fctx.isbinary()
579 579
580 580 return _is_binary(repo_id, revision, path)
581 581
582 582 @reraise_safe_exceptions
583 583 def in_largefiles_store(self, wire, sha):
584 584 repo = self._factory.repo(wire)
585 585 return largefiles.lfutil.instore(repo, sha)
586 586
587 587 @reraise_safe_exceptions
588 588 def in_user_cache(self, wire, sha):
589 589 repo = self._factory.repo(wire)
590 590 return largefiles.lfutil.inusercache(repo.ui, sha)
591 591
592 592 @reraise_safe_exceptions
593 593 def store_path(self, wire, sha):
594 594 repo = self._factory.repo(wire)
595 595 return largefiles.lfutil.storepath(repo, sha)
596 596
597 597 @reraise_safe_exceptions
598 598 def link(self, wire, sha, path):
599 599 repo = self._factory.repo(wire)
600 600 largefiles.lfutil.link(
601 601 largefiles.lfutil.usercachepath(repo.ui, sha), path)
602 602
603 603 @reraise_safe_exceptions
604 604 def localrepository(self, wire, create=False):
605 605 self._factory.repo(wire, create=create)
606 606
607 607 @reraise_safe_exceptions
608 608 def lookup(self, wire, revision, both):
609 609 cache_on, context_uid, repo_id = self._cache_on(wire)
610 610 @self.region.conditional_cache_on_arguments(condition=cache_on)
611 611 def _lookup(_context_uid, _repo_id, _revision, _both):
612 612
613 613 repo = self._factory.repo(wire)
614 614 rev = _revision
615 615 if isinstance(rev, int):
616 616 # NOTE(marcink):
617 617 # since Mercurial doesn't support negative indexes properly
618 618 # we need to shift accordingly by one to get proper index, e.g
619 619 # repo[-1] => repo[-2]
620 620 # repo[0] => repo[-1]
621 621 if rev <= 0:
622 622 rev = rev + -1
623 623 try:
624 624 ctx = self._get_ctx(repo, rev)
625 625 except (TypeError, RepoLookupError) as e:
626 626 e._org_exc_tb = traceback.format_exc()
627 627 raise exceptions.LookupException(e)(rev)
628 628 except LookupError as e:
629 629 e._org_exc_tb = traceback.format_exc()
630 630 raise exceptions.LookupException(e)(e.name)
631 631
632 632 if not both:
633 633 return ctx.hex()
634 634
635 635 ctx = repo[ctx.hex()]
636 636 return ctx.hex(), ctx.rev()
637 637
638 638 return _lookup(context_uid, repo_id, revision, both)
639 639
640 640 @reraise_safe_exceptions
641 641 def sync_push(self, wire, url):
642 642 if not self.check_url(url, wire['config']):
643 643 return
644 644
645 645 repo = self._factory.repo(wire)
646 646
647 647 # Disable any prompts for this repo
648 648 repo.ui.setconfig('ui', 'interactive', 'off', '-y')
649 649
650 650 bookmarks = dict(repo._bookmarks).keys()
651 651 remote = peer(repo, {}, url)
652 652 # Disable any prompts for this remote
653 653 remote.ui.setconfig('ui', 'interactive', 'off', '-y')
654 654
655 655 return exchange.push(
656 656 repo, remote, newbranch=True, bookmarks=bookmarks).cgresult
657 657
658 658 @reraise_safe_exceptions
659 659 def revision(self, wire, rev):
660 660 repo = self._factory.repo(wire)
661 661 ctx = self._get_ctx(repo, rev)
662 662 return ctx.rev()
663 663
664 664 @reraise_safe_exceptions
665 665 def rev_range(self, wire, commit_filter):
666 666 cache_on, context_uid, repo_id = self._cache_on(wire)
667 667
668 668 @self.region.conditional_cache_on_arguments(condition=cache_on)
669 669 def _rev_range(_context_uid, _repo_id, _filter):
670 670 repo = self._factory.repo(wire)
671 671 revisions = [rev for rev in revrange(repo, commit_filter)]
672 672 return revisions
673 673
674 674 return _rev_range(context_uid, repo_id, sorted(commit_filter))
675 675
676 676 @reraise_safe_exceptions
677 677 def rev_range_hash(self, wire, node):
678 678 repo = self._factory.repo(wire)
679 679
680 680 def get_revs(repo, rev_opt):
681 681 if rev_opt:
682 682 revs = revrange(repo, rev_opt)
683 683 if len(revs) == 0:
684 684 return (nullrev, nullrev)
685 685 return max(revs), min(revs)
686 686 else:
687 687 return len(repo) - 1, 0
688 688
689 689 stop, start = get_revs(repo, [node + ':'])
690 690 revs = [hex(repo[r].node()) for r in xrange(start, stop + 1)]
691 691 return revs
692 692
693 693 @reraise_safe_exceptions
694 694 def revs_from_revspec(self, wire, rev_spec, *args, **kwargs):
695 695 other_path = kwargs.pop('other_path', None)
696 696
697 697 # case when we want to compare two independent repositories
698 698 if other_path and other_path != wire["path"]:
699 699 baseui = self._factory._create_config(wire["config"])
700 700 repo = unionrepo.makeunionrepository(baseui, other_path, wire["path"])
701 701 else:
702 702 repo = self._factory.repo(wire)
703 703 return list(repo.revs(rev_spec, *args))
704 704
705 705 @reraise_safe_exceptions
706 706 def verify(self, wire,):
707 707 repo = self._factory.repo(wire)
708 708 baseui = self._factory._create_config(wire['config'])
709 709 baseui.setconfig('ui', 'quiet', 'false')
710 710 output = io.BytesIO()
711 711
712 712 def write(data, **unused_kwargs):
713 713 output.write(data)
714 714 baseui.write = write
715 715
716 716 repo.ui = baseui
717 717 verify.verify(repo)
718 718 return output.getvalue()
719 719
720 720 @reraise_safe_exceptions
721 721 def hg_update_cache(self, wire,):
722 722 repo = self._factory.repo(wire)
723 723 baseui = self._factory._create_config(wire['config'])
724 724 baseui.setconfig('ui', 'quiet', 'false')
725 725 output = io.BytesIO()
726 726
727 727 def write(data, **unused_kwargs):
728 728 output.write(data)
729 729 baseui.write = write
730 730
731 731 repo.ui = baseui
732 732 with repo.wlock(), repo.lock():
733 733 repo.updatecaches(full=True)
734 734
735 735 return output.getvalue()
736 736
737 737 @reraise_safe_exceptions
738 738 def tags(self, wire):
739 739 cache_on, context_uid, repo_id = self._cache_on(wire)
740 740 @self.region.conditional_cache_on_arguments(condition=cache_on)
741 741 def _tags(_context_uid, _repo_id):
742 742 repo = self._factory.repo(wire)
743 743 return repo.tags()
744 744
745 745 return _tags(context_uid, repo_id)
746 746
747 747 @reraise_safe_exceptions
748 748 def update(self, wire, node=None, clean=False):
749 749 repo = self._factory.repo(wire)
750 750 baseui = self._factory._create_config(wire['config'])
751 751 commands.update(baseui, repo, node=node, clean=clean)
752 752
753 753 @reraise_safe_exceptions
754 754 def identify(self, wire):
755 755 repo = self._factory.repo(wire)
756 756 baseui = self._factory._create_config(wire['config'])
757 757 output = io.BytesIO()
758 758 baseui.write = output.write
759 759 # This is required to get a full node id
760 760 baseui.debugflag = True
761 761 commands.identify(baseui, repo, id=True)
762 762
763 763 return output.getvalue()
764 764
765 765 @reraise_safe_exceptions
766 766 def heads(self, wire, branch=None):
767 767 repo = self._factory.repo(wire)
768 768 baseui = self._factory._create_config(wire['config'])
769 769 output = io.BytesIO()
770 770
771 771 def write(data, **unused_kwargs):
772 772 output.write(data)
773 773
774 774 baseui.write = write
775 775 if branch:
776 776 args = [branch]
777 777 else:
778 778 args = []
779 779 commands.heads(baseui, repo, template='{node} ', *args)
780 780
781 781 return output.getvalue()
782 782
783 783 @reraise_safe_exceptions
784 784 def ancestor(self, wire, revision1, revision2):
785 785 repo = self._factory.repo(wire)
786 786 changelog = repo.changelog
787 787 lookup = repo.lookup
788 788 a = changelog.ancestor(lookup(revision1), lookup(revision2))
789 789 return hex(a)
790 790
791 791 @reraise_safe_exceptions
792 792 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
793 793 baseui = self._factory._create_config(wire["config"], hooks=hooks)
794 794 clone(baseui, source, dest, noupdate=not update_after_clone)
795 795
796 796 @reraise_safe_exceptions
797 797 def commitctx(self, wire, message, parents, commit_time, commit_timezone, user, files, extra, removed, updated):
798 798
799 799 repo = self._factory.repo(wire)
800 800 baseui = self._factory._create_config(wire['config'])
801 801 publishing = baseui.configbool('phases', 'publish')
802 802 if publishing:
803 803 new_commit = 'public'
804 804 else:
805 805 new_commit = 'draft'
806 806
807 807 def _filectxfn(_repo, ctx, path):
808 808 """
809 809 Marks given path as added/changed/removed in a given _repo. This is
810 810 for internal mercurial commit function.
811 811 """
812 812
813 813 # check if this path is removed
814 814 if path in removed:
815 815 # returning None is a way to mark node for removal
816 816 return None
817 817
818 818 # check if this path is added
819 819 for node in updated:
820 820 if node['path'] == path:
821 821 return memfilectx(
822 822 _repo,
823 823 changectx=ctx,
824 824 path=node['path'],
825 825 data=node['content'],
826 826 islink=False,
827 827 isexec=bool(node['mode'] & stat.S_IXUSR),
828 828 copysource=False)
829 829
830 830 raise exceptions.AbortException()(
831 831 "Given path haven't been marked as added, "
832 832 "changed or removed (%s)" % path)
833 833
834 834 with repo.ui.configoverride({('phases', 'new-commit'): new_commit}):
835 835
836 836 commit_ctx = memctx(
837 837 repo=repo,
838 838 parents=parents,
839 839 text=message,
840 840 files=files,
841 841 filectxfn=_filectxfn,
842 842 user=user,
843 843 date=(commit_time, commit_timezone),
844 844 extra=extra)
845 845
846 846 n = repo.commitctx(commit_ctx)
847 847 new_id = hex(n)
848 848
849 849 return new_id
850 850
851 851 @reraise_safe_exceptions
852 852 def pull(self, wire, url, commit_ids=None):
853 853 repo = self._factory.repo(wire)
854 854 # Disable any prompts for this repo
855 855 repo.ui.setconfig('ui', 'interactive', 'off', '-y')
856 856
857 857 remote = peer(repo, {}, url)
858 858 # Disable any prompts for this remote
859 859 remote.ui.setconfig('ui', 'interactive', 'off', '-y')
860 860
861 861 if commit_ids:
862 862 commit_ids = [bin(commit_id) for commit_id in commit_ids]
863 863
864 864 return exchange.pull(
865 865 repo, remote, heads=commit_ids, force=None).cgresult
866 866
867 867 @reraise_safe_exceptions
868 868 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None, hooks=True):
869 869 repo = self._factory.repo(wire)
870 870 baseui = self._factory._create_config(wire['config'], hooks=hooks)
871 871
872 872 # Mercurial internally has a lot of logic that checks ONLY if
873 873 # option is defined, we just pass those if they are defined then
874 874 opts = {}
875 875 if bookmark:
876 876 opts['bookmark'] = bookmark
877 877 if branch:
878 878 opts['branch'] = branch
879 879 if revision:
880 880 opts['rev'] = revision
881 881
882 882 commands.pull(baseui, repo, source, **opts)
883 883
884 884 @reraise_safe_exceptions
885 885 def push(self, wire, revisions, dest_path, hooks=True, push_branches=False):
886 886 repo = self._factory.repo(wire)
887 887 baseui = self._factory._create_config(wire['config'], hooks=hooks)
888 888 commands.push(baseui, repo, dest=dest_path, rev=revisions,
889 889 new_branch=push_branches)
890 890
891 891 @reraise_safe_exceptions
892 892 def strip(self, wire, revision, update, backup):
893 893 repo = self._factory.repo(wire)
894 894 ctx = self._get_ctx(repo, revision)
895 895 hgext_strip(
896 896 repo.baseui, repo, ctx.node(), update=update, backup=backup)
897 897
898 898 @reraise_safe_exceptions
899 def get_unresolved_files(self, wire):
900 repo = self._factory.repo(wire)
901
902 log.debug('Calculating unresolved files for repo: %s', repo)
903 output = io.BytesIO()
904
905 def write(data, **unused_kwargs):
906 output.write(data)
907
908 baseui = self._factory._create_config(wire['config'])
909 baseui.write = write
910
911 commands.resolve(baseui, repo, list=True)
912 unresolved = output.getvalue().splitlines(0)
913 return unresolved
914
915 @reraise_safe_exceptions
899 916 def merge(self, wire, revision):
900 917 repo = self._factory.repo(wire)
901 918 baseui = self._factory._create_config(wire['config'])
902 919 repo.ui.setconfig('ui', 'merge', 'internal:dump')
903 920
904 921 # In case of sub repositories are used mercurial prompts the user in
905 922 # case of merge conflicts or different sub repository sources. By
906 923 # setting the interactive flag to `False` mercurial doesn't prompt the
907 924 # used but instead uses a default value.
908 925 repo.ui.setconfig('ui', 'interactive', False)
909 926 commands.merge(baseui, repo, rev=revision)
910 927
911 928 @reraise_safe_exceptions
912 929 def merge_state(self, wire):
913 930 repo = self._factory.repo(wire)
914 931 repo.ui.setconfig('ui', 'merge', 'internal:dump')
915 932
916 933 # In case of sub repositories are used mercurial prompts the user in
917 934 # case of merge conflicts or different sub repository sources. By
918 935 # setting the interactive flag to `False` mercurial doesn't prompt the
919 936 # used but instead uses a default value.
920 937 repo.ui.setconfig('ui', 'interactive', False)
921 938 ms = hg_merge.mergestate(repo)
922 939 return [x for x in ms.unresolved()]
923 940
924 941 @reraise_safe_exceptions
925 942 def commit(self, wire, message, username, close_branch=False):
926 943 repo = self._factory.repo(wire)
927 944 baseui = self._factory._create_config(wire['config'])
928 945 repo.ui.setconfig('ui', 'username', username)
929 946 commands.commit(baseui, repo, message=message, close_branch=close_branch)
930 947
931 948 @reraise_safe_exceptions
932 949 def rebase(self, wire, source=None, dest=None, abort=False):
933 950 repo = self._factory.repo(wire)
934 951 baseui = self._factory._create_config(wire['config'])
935 952 repo.ui.setconfig('ui', 'merge', 'internal:dump')
936 rebase.rebase(
937 baseui, repo, base=source, dest=dest, abort=abort, keep=not abort)
953 # In case of sub repositories are used mercurial prompts the user in
954 # case of merge conflicts or different sub repository sources. By
955 # setting the interactive flag to `False` mercurial doesn't prompt the
956 # used but instead uses a default value.
957 repo.ui.setconfig('ui', 'interactive', False)
958 rebase.rebase(baseui, repo, base=source, dest=dest, abort=abort, keep=not abort)
938 959
939 960 @reraise_safe_exceptions
940 961 def tag(self, wire, name, revision, message, local, user, tag_time, tag_timezone):
941 962 repo = self._factory.repo(wire)
942 963 ctx = self._get_ctx(repo, revision)
943 964 node = ctx.node()
944 965
945 966 date = (tag_time, tag_timezone)
946 967 try:
947 968 hg_tag.tag(repo, name, node, message, local, user, date)
948 969 except Abort as e:
949 970 log.exception("Tag operation aborted")
950 971 # Exception can contain unicode which we convert
951 972 raise exceptions.AbortException(e)(repr(e))
952 973
953 974 @reraise_safe_exceptions
954 975 def bookmark(self, wire, bookmark, revision=None):
955 976 repo = self._factory.repo(wire)
956 977 baseui = self._factory._create_config(wire['config'])
957 978 commands.bookmark(baseui, repo, bookmark, rev=revision, force=True)
958 979
959 980 @reraise_safe_exceptions
960 981 def install_hooks(self, wire, force=False):
961 982 # we don't need any special hooks for Mercurial
962 983 pass
963 984
964 985 @reraise_safe_exceptions
965 986 def get_hooks_info(self, wire):
966 987 return {
967 988 'pre_version': vcsserver.__version__,
968 989 'post_version': vcsserver.__version__,
969 990 }
General Comments 0
You need to be logged in to leave comments. Login now