##// END OF EJS Templates
ui: replace obsolete default-push with default:pushurl (issue5485)...
Rishabh Madan -
r31064:4431add9 stable
parent child Browse files
Show More
@@ -1,1431 +1,1431
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import contextlib
11 11 import errno
12 12 import getpass
13 13 import inspect
14 14 import os
15 15 import re
16 16 import socket
17 17 import sys
18 18 import tempfile
19 19 import traceback
20 20
21 21 from .i18n import _
22 22 from .node import hex
23 23
24 24 from . import (
25 25 config,
26 26 encoding,
27 27 error,
28 28 formatter,
29 29 progress,
30 30 pycompat,
31 31 scmutil,
32 32 util,
33 33 )
34 34
35 35 urlreq = util.urlreq
36 36
37 37 samplehgrcs = {
38 38 'user':
39 39 """# example user config (see 'hg help config' for more info)
40 40 [ui]
41 41 # name and email, e.g.
42 42 # username = Jane Doe <jdoe@example.com>
43 43 username =
44 44
45 45 [extensions]
46 46 # uncomment these lines to enable some popular extensions
47 47 # (see 'hg help extensions' for more info)
48 48 #
49 49 # pager =
50 50 # color =""",
51 51
52 52 'cloned':
53 53 """# example repository config (see 'hg help config' for more info)
54 54 [paths]
55 55 default = %s
56 56
57 57 # path aliases to other clones of this repo in URLs or filesystem paths
58 58 # (see 'hg help config.paths' for more info)
59 59 #
60 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
61 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
62 # my-clone = /home/jdoe/jdoes-clone
60 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
61 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
62 # my-clone = /home/jdoe/jdoes-clone
63 63
64 64 [ui]
65 65 # name and email (local to this repository, optional), e.g.
66 66 # username = Jane Doe <jdoe@example.com>
67 67 """,
68 68
69 69 'local':
70 70 """# example repository config (see 'hg help config' for more info)
71 71 [paths]
72 72 # path aliases to other clones of this repo in URLs or filesystem paths
73 73 # (see 'hg help config.paths' for more info)
74 74 #
75 # default = http://example.com/hg/example-repo
76 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
77 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
78 # my-clone = /home/jdoe/jdoes-clone
75 # default = http://example.com/hg/example-repo
76 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
77 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
78 # my-clone = /home/jdoe/jdoes-clone
79 79
80 80 [ui]
81 81 # name and email (local to this repository, optional), e.g.
82 82 # username = Jane Doe <jdoe@example.com>
83 83 """,
84 84
85 85 'global':
86 86 """# example system-wide hg config (see 'hg help config' for more info)
87 87
88 88 [extensions]
89 89 # uncomment these lines to enable some popular extensions
90 90 # (see 'hg help extensions' for more info)
91 91 #
92 92 # blackbox =
93 93 # color =
94 94 # pager =""",
95 95 }
96 96
97 97 class ui(object):
98 98 def __init__(self, src=None):
99 99 """Create a fresh new ui object if no src given
100 100
101 101 Use uimod.ui.load() to create a ui which knows global and user configs.
102 102 In most cases, you should use ui.copy() to create a copy of an existing
103 103 ui object.
104 104 """
105 105 # _buffers: used for temporary capture of output
106 106 self._buffers = []
107 107 # 3-tuple describing how each buffer in the stack behaves.
108 108 # Values are (capture stderr, capture subprocesses, apply labels).
109 109 self._bufferstates = []
110 110 # When a buffer is active, defines whether we are expanding labels.
111 111 # This exists to prevent an extra list lookup.
112 112 self._bufferapplylabels = None
113 113 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
114 114 self._reportuntrusted = True
115 115 self._ocfg = config.config() # overlay
116 116 self._tcfg = config.config() # trusted
117 117 self._ucfg = config.config() # untrusted
118 118 self._trustusers = set()
119 119 self._trustgroups = set()
120 120 self.callhooks = True
121 121 # Insecure server connections requested.
122 122 self.insecureconnections = False
123 123
124 124 if src:
125 125 self.fout = src.fout
126 126 self.ferr = src.ferr
127 127 self.fin = src.fin
128 128
129 129 self._tcfg = src._tcfg.copy()
130 130 self._ucfg = src._ucfg.copy()
131 131 self._ocfg = src._ocfg.copy()
132 132 self._trustusers = src._trustusers.copy()
133 133 self._trustgroups = src._trustgroups.copy()
134 134 self.environ = src.environ
135 135 self.callhooks = src.callhooks
136 136 self.insecureconnections = src.insecureconnections
137 137 self.fixconfig()
138 138
139 139 self.httppasswordmgrdb = src.httppasswordmgrdb
140 140 else:
141 141 self.fout = util.stdout
142 142 self.ferr = util.stderr
143 143 self.fin = util.stdin
144 144
145 145 # shared read-only environment
146 146 self.environ = encoding.environ
147 147
148 148 self.httppasswordmgrdb = urlreq.httppasswordmgrwithdefaultrealm()
149 149
150 150 allowed = self.configlist('experimental', 'exportableenviron')
151 151 if '*' in allowed:
152 152 self._exportableenviron = self.environ
153 153 else:
154 154 self._exportableenviron = {}
155 155 for k in allowed:
156 156 if k in self.environ:
157 157 self._exportableenviron[k] = self.environ[k]
158 158
159 159 @classmethod
160 160 def load(cls):
161 161 """Create a ui and load global and user configs"""
162 162 u = cls()
163 163 # we always trust global config files
164 164 for f in scmutil.rcpath():
165 165 u.readconfig(f, trust=True)
166 166 return u
167 167
168 168 def copy(self):
169 169 return self.__class__(self)
170 170
171 171 def resetstate(self):
172 172 """Clear internal state that shouldn't persist across commands"""
173 173 if self._progbar:
174 174 self._progbar.resetstate() # reset last-print time of progress bar
175 175 self.httppasswordmgrdb = urlreq.httppasswordmgrwithdefaultrealm()
176 176
177 177 def formatter(self, topic, opts):
178 178 return formatter.formatter(self, topic, opts)
179 179
180 180 def _trusted(self, fp, f):
181 181 st = util.fstat(fp)
182 182 if util.isowner(st):
183 183 return True
184 184
185 185 tusers, tgroups = self._trustusers, self._trustgroups
186 186 if '*' in tusers or '*' in tgroups:
187 187 return True
188 188
189 189 user = util.username(st.st_uid)
190 190 group = util.groupname(st.st_gid)
191 191 if user in tusers or group in tgroups or user == util.username():
192 192 return True
193 193
194 194 if self._reportuntrusted:
195 195 self.warn(_('not trusting file %s from untrusted '
196 196 'user %s, group %s\n') % (f, user, group))
197 197 return False
198 198
199 199 def readconfig(self, filename, root=None, trust=False,
200 200 sections=None, remap=None):
201 201 try:
202 202 fp = open(filename, u'rb')
203 203 except IOError:
204 204 if not sections: # ignore unless we were looking for something
205 205 return
206 206 raise
207 207
208 208 cfg = config.config()
209 209 trusted = sections or trust or self._trusted(fp, filename)
210 210
211 211 try:
212 212 cfg.read(filename, fp, sections=sections, remap=remap)
213 213 fp.close()
214 214 except error.ConfigError as inst:
215 215 if trusted:
216 216 raise
217 217 self.warn(_("ignored: %s\n") % str(inst))
218 218
219 219 if self.plain():
220 220 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
221 221 'logtemplate', 'statuscopies', 'style',
222 222 'traceback', 'verbose'):
223 223 if k in cfg['ui']:
224 224 del cfg['ui'][k]
225 225 for k, v in cfg.items('defaults'):
226 226 del cfg['defaults'][k]
227 227 # Don't remove aliases from the configuration if in the exceptionlist
228 228 if self.plain('alias'):
229 229 for k, v in cfg.items('alias'):
230 230 del cfg['alias'][k]
231 231 if self.plain('revsetalias'):
232 232 for k, v in cfg.items('revsetalias'):
233 233 del cfg['revsetalias'][k]
234 234 if self.plain('templatealias'):
235 235 for k, v in cfg.items('templatealias'):
236 236 del cfg['templatealias'][k]
237 237
238 238 if trusted:
239 239 self._tcfg.update(cfg)
240 240 self._tcfg.update(self._ocfg)
241 241 self._ucfg.update(cfg)
242 242 self._ucfg.update(self._ocfg)
243 243
244 244 if root is None:
245 245 root = os.path.expanduser('~')
246 246 self.fixconfig(root=root)
247 247
248 248 def fixconfig(self, root=None, section=None):
249 249 if section in (None, 'paths'):
250 250 # expand vars and ~
251 251 # translate paths relative to root (or home) into absolute paths
252 252 root = root or pycompat.getcwd()
253 253 for c in self._tcfg, self._ucfg, self._ocfg:
254 254 for n, p in c.items('paths'):
255 255 # Ignore sub-options.
256 256 if ':' in n:
257 257 continue
258 258 if not p:
259 259 continue
260 260 if '%%' in p:
261 261 s = self.configsource('paths', n) or 'none'
262 262 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
263 263 % (n, p, s))
264 264 p = p.replace('%%', '%')
265 265 p = util.expandpath(p)
266 266 if not util.hasscheme(p) and not os.path.isabs(p):
267 267 p = os.path.normpath(os.path.join(root, p))
268 268 c.set("paths", n, p)
269 269
270 270 if section in (None, 'ui'):
271 271 # update ui options
272 272 self.debugflag = self.configbool('ui', 'debug')
273 273 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
274 274 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
275 275 if self.verbose and self.quiet:
276 276 self.quiet = self.verbose = False
277 277 self._reportuntrusted = self.debugflag or self.configbool("ui",
278 278 "report_untrusted", True)
279 279 self.tracebackflag = self.configbool('ui', 'traceback', False)
280 280
281 281 if section in (None, 'trusted'):
282 282 # update trust information
283 283 self._trustusers.update(self.configlist('trusted', 'users'))
284 284 self._trustgroups.update(self.configlist('trusted', 'groups'))
285 285
286 286 def backupconfig(self, section, item):
287 287 return (self._ocfg.backup(section, item),
288 288 self._tcfg.backup(section, item),
289 289 self._ucfg.backup(section, item),)
290 290 def restoreconfig(self, data):
291 291 self._ocfg.restore(data[0])
292 292 self._tcfg.restore(data[1])
293 293 self._ucfg.restore(data[2])
294 294
295 295 def setconfig(self, section, name, value, source=''):
296 296 for cfg in (self._ocfg, self._tcfg, self._ucfg):
297 297 cfg.set(section, name, value, source)
298 298 self.fixconfig(section=section)
299 299
300 300 def _data(self, untrusted):
301 301 return untrusted and self._ucfg or self._tcfg
302 302
303 303 def configsource(self, section, name, untrusted=False):
304 304 return self._data(untrusted).source(section, name)
305 305
306 306 def config(self, section, name, default=None, untrusted=False):
307 307 if isinstance(name, list):
308 308 alternates = name
309 309 else:
310 310 alternates = [name]
311 311
312 312 for n in alternates:
313 313 value = self._data(untrusted).get(section, n, None)
314 314 if value is not None:
315 315 name = n
316 316 break
317 317 else:
318 318 value = default
319 319
320 320 if self.debugflag and not untrusted and self._reportuntrusted:
321 321 for n in alternates:
322 322 uvalue = self._ucfg.get(section, n)
323 323 if uvalue is not None and uvalue != value:
324 324 self.debug("ignoring untrusted configuration option "
325 325 "%s.%s = %s\n" % (section, n, uvalue))
326 326 return value
327 327
328 328 def configsuboptions(self, section, name, default=None, untrusted=False):
329 329 """Get a config option and all sub-options.
330 330
331 331 Some config options have sub-options that are declared with the
332 332 format "key:opt = value". This method is used to return the main
333 333 option and all its declared sub-options.
334 334
335 335 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
336 336 is a dict of defined sub-options where keys and values are strings.
337 337 """
338 338 data = self._data(untrusted)
339 339 main = data.get(section, name, default)
340 340 if self.debugflag and not untrusted and self._reportuntrusted:
341 341 uvalue = self._ucfg.get(section, name)
342 342 if uvalue is not None and uvalue != main:
343 343 self.debug('ignoring untrusted configuration option '
344 344 '%s.%s = %s\n' % (section, name, uvalue))
345 345
346 346 sub = {}
347 347 prefix = '%s:' % name
348 348 for k, v in data.items(section):
349 349 if k.startswith(prefix):
350 350 sub[k[len(prefix):]] = v
351 351
352 352 if self.debugflag and not untrusted and self._reportuntrusted:
353 353 for k, v in sub.items():
354 354 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
355 355 if uvalue is not None and uvalue != v:
356 356 self.debug('ignoring untrusted configuration option '
357 357 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
358 358
359 359 return main, sub
360 360
361 361 def configpath(self, section, name, default=None, untrusted=False):
362 362 'get a path config item, expanded relative to repo root or config file'
363 363 v = self.config(section, name, default, untrusted)
364 364 if v is None:
365 365 return None
366 366 if not os.path.isabs(v) or "://" not in v:
367 367 src = self.configsource(section, name, untrusted)
368 368 if ':' in src:
369 369 base = os.path.dirname(src.rsplit(':')[0])
370 370 v = os.path.join(base, os.path.expanduser(v))
371 371 return v
372 372
373 373 def configbool(self, section, name, default=False, untrusted=False):
374 374 """parse a configuration element as a boolean
375 375
376 376 >>> u = ui(); s = 'foo'
377 377 >>> u.setconfig(s, 'true', 'yes')
378 378 >>> u.configbool(s, 'true')
379 379 True
380 380 >>> u.setconfig(s, 'false', 'no')
381 381 >>> u.configbool(s, 'false')
382 382 False
383 383 >>> u.configbool(s, 'unknown')
384 384 False
385 385 >>> u.configbool(s, 'unknown', True)
386 386 True
387 387 >>> u.setconfig(s, 'invalid', 'somevalue')
388 388 >>> u.configbool(s, 'invalid')
389 389 Traceback (most recent call last):
390 390 ...
391 391 ConfigError: foo.invalid is not a boolean ('somevalue')
392 392 """
393 393
394 394 v = self.config(section, name, None, untrusted)
395 395 if v is None:
396 396 return default
397 397 if isinstance(v, bool):
398 398 return v
399 399 b = util.parsebool(v)
400 400 if b is None:
401 401 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
402 402 % (section, name, v))
403 403 return b
404 404
405 405 def configint(self, section, name, default=None, untrusted=False):
406 406 """parse a configuration element as an integer
407 407
408 408 >>> u = ui(); s = 'foo'
409 409 >>> u.setconfig(s, 'int1', '42')
410 410 >>> u.configint(s, 'int1')
411 411 42
412 412 >>> u.setconfig(s, 'int2', '-42')
413 413 >>> u.configint(s, 'int2')
414 414 -42
415 415 >>> u.configint(s, 'unknown', 7)
416 416 7
417 417 >>> u.setconfig(s, 'invalid', 'somevalue')
418 418 >>> u.configint(s, 'invalid')
419 419 Traceback (most recent call last):
420 420 ...
421 421 ConfigError: foo.invalid is not an integer ('somevalue')
422 422 """
423 423
424 424 v = self.config(section, name, None, untrusted)
425 425 if v is None:
426 426 return default
427 427 try:
428 428 return int(v)
429 429 except ValueError:
430 430 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
431 431 % (section, name, v))
432 432
433 433 def configbytes(self, section, name, default=0, untrusted=False):
434 434 """parse a configuration element as a quantity in bytes
435 435
436 436 Units can be specified as b (bytes), k or kb (kilobytes), m or
437 437 mb (megabytes), g or gb (gigabytes).
438 438
439 439 >>> u = ui(); s = 'foo'
440 440 >>> u.setconfig(s, 'val1', '42')
441 441 >>> u.configbytes(s, 'val1')
442 442 42
443 443 >>> u.setconfig(s, 'val2', '42.5 kb')
444 444 >>> u.configbytes(s, 'val2')
445 445 43520
446 446 >>> u.configbytes(s, 'unknown', '7 MB')
447 447 7340032
448 448 >>> u.setconfig(s, 'invalid', 'somevalue')
449 449 >>> u.configbytes(s, 'invalid')
450 450 Traceback (most recent call last):
451 451 ...
452 452 ConfigError: foo.invalid is not a byte quantity ('somevalue')
453 453 """
454 454
455 455 value = self.config(section, name)
456 456 if value is None:
457 457 if not isinstance(default, str):
458 458 return default
459 459 value = default
460 460 try:
461 461 return util.sizetoint(value)
462 462 except error.ParseError:
463 463 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
464 464 % (section, name, value))
465 465
466 466 def configlist(self, section, name, default=None, untrusted=False):
467 467 """parse a configuration element as a list of comma/space separated
468 468 strings
469 469
470 470 >>> u = ui(); s = 'foo'
471 471 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
472 472 >>> u.configlist(s, 'list1')
473 473 ['this', 'is', 'a small', 'test']
474 474 """
475 475
476 476 def _parse_plain(parts, s, offset):
477 477 whitespace = False
478 478 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
479 479 whitespace = True
480 480 offset += 1
481 481 if offset >= len(s):
482 482 return None, parts, offset
483 483 if whitespace:
484 484 parts.append('')
485 485 if s[offset] == '"' and not parts[-1]:
486 486 return _parse_quote, parts, offset + 1
487 487 elif s[offset] == '"' and parts[-1][-1] == '\\':
488 488 parts[-1] = parts[-1][:-1] + s[offset]
489 489 return _parse_plain, parts, offset + 1
490 490 parts[-1] += s[offset]
491 491 return _parse_plain, parts, offset + 1
492 492
493 493 def _parse_quote(parts, s, offset):
494 494 if offset < len(s) and s[offset] == '"': # ""
495 495 parts.append('')
496 496 offset += 1
497 497 while offset < len(s) and (s[offset].isspace() or
498 498 s[offset] == ','):
499 499 offset += 1
500 500 return _parse_plain, parts, offset
501 501
502 502 while offset < len(s) and s[offset] != '"':
503 503 if (s[offset] == '\\' and offset + 1 < len(s)
504 504 and s[offset + 1] == '"'):
505 505 offset += 1
506 506 parts[-1] += '"'
507 507 else:
508 508 parts[-1] += s[offset]
509 509 offset += 1
510 510
511 511 if offset >= len(s):
512 512 real_parts = _configlist(parts[-1])
513 513 if not real_parts:
514 514 parts[-1] = '"'
515 515 else:
516 516 real_parts[0] = '"' + real_parts[0]
517 517 parts = parts[:-1]
518 518 parts.extend(real_parts)
519 519 return None, parts, offset
520 520
521 521 offset += 1
522 522 while offset < len(s) and s[offset] in [' ', ',']:
523 523 offset += 1
524 524
525 525 if offset < len(s):
526 526 if offset + 1 == len(s) and s[offset] == '"':
527 527 parts[-1] += '"'
528 528 offset += 1
529 529 else:
530 530 parts.append('')
531 531 else:
532 532 return None, parts, offset
533 533
534 534 return _parse_plain, parts, offset
535 535
536 536 def _configlist(s):
537 537 s = s.rstrip(' ,')
538 538 if not s:
539 539 return []
540 540 parser, parts, offset = _parse_plain, [''], 0
541 541 while parser:
542 542 parser, parts, offset = parser(parts, s, offset)
543 543 return parts
544 544
545 545 result = self.config(section, name, untrusted=untrusted)
546 546 if result is None:
547 547 result = default or []
548 548 if isinstance(result, bytes):
549 549 result = _configlist(result.lstrip(' ,\n'))
550 550 if result is None:
551 551 result = default or []
552 552 return result
553 553
554 554 def hasconfig(self, section, name, untrusted=False):
555 555 return self._data(untrusted).hasitem(section, name)
556 556
557 557 def has_section(self, section, untrusted=False):
558 558 '''tell whether section exists in config.'''
559 559 return section in self._data(untrusted)
560 560
561 561 def configitems(self, section, untrusted=False, ignoresub=False):
562 562 items = self._data(untrusted).items(section)
563 563 if ignoresub:
564 564 newitems = {}
565 565 for k, v in items:
566 566 if ':' not in k:
567 567 newitems[k] = v
568 568 items = newitems.items()
569 569 if self.debugflag and not untrusted and self._reportuntrusted:
570 570 for k, v in self._ucfg.items(section):
571 571 if self._tcfg.get(section, k) != v:
572 572 self.debug("ignoring untrusted configuration option "
573 573 "%s.%s = %s\n" % (section, k, v))
574 574 return items
575 575
576 576 def walkconfig(self, untrusted=False):
577 577 cfg = self._data(untrusted)
578 578 for section in cfg.sections():
579 579 for name, value in self.configitems(section, untrusted):
580 580 yield section, name, value
581 581
582 582 def plain(self, feature=None):
583 583 '''is plain mode active?
584 584
585 585 Plain mode means that all configuration variables which affect
586 586 the behavior and output of Mercurial should be
587 587 ignored. Additionally, the output should be stable,
588 588 reproducible and suitable for use in scripts or applications.
589 589
590 590 The only way to trigger plain mode is by setting either the
591 591 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
592 592
593 593 The return value can either be
594 594 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
595 595 - True otherwise
596 596 '''
597 597 if ('HGPLAIN' not in encoding.environ and
598 598 'HGPLAINEXCEPT' not in encoding.environ):
599 599 return False
600 600 exceptions = encoding.environ.get('HGPLAINEXCEPT',
601 601 '').strip().split(',')
602 602 if feature and exceptions:
603 603 return feature not in exceptions
604 604 return True
605 605
606 606 def username(self):
607 607 """Return default username to be used in commits.
608 608
609 609 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
610 610 and stop searching if one of these is set.
611 611 If not found and ui.askusername is True, ask the user, else use
612 612 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
613 613 """
614 614 user = encoding.environ.get("HGUSER")
615 615 if user is None:
616 616 user = self.config("ui", ["username", "user"])
617 617 if user is not None:
618 618 user = os.path.expandvars(user)
619 619 if user is None:
620 620 user = encoding.environ.get("EMAIL")
621 621 if user is None and self.configbool("ui", "askusername"):
622 622 user = self.prompt(_("enter a commit username:"), default=None)
623 623 if user is None and not self.interactive():
624 624 try:
625 625 user = '%s@%s' % (util.getuser(), socket.getfqdn())
626 626 self.warn(_("no username found, using '%s' instead\n") % user)
627 627 except KeyError:
628 628 pass
629 629 if not user:
630 630 raise error.Abort(_('no username supplied'),
631 631 hint=_("use 'hg config --edit' "
632 632 'to set your username'))
633 633 if "\n" in user:
634 634 raise error.Abort(_("username %s contains a newline\n")
635 635 % repr(user))
636 636 return user
637 637
638 638 def shortuser(self, user):
639 639 """Return a short representation of a user name or email address."""
640 640 if not self.verbose:
641 641 user = util.shortuser(user)
642 642 return user
643 643
644 644 def expandpath(self, loc, default=None):
645 645 """Return repository location relative to cwd or from [paths]"""
646 646 try:
647 647 p = self.paths.getpath(loc)
648 648 if p:
649 649 return p.rawloc
650 650 except error.RepoError:
651 651 pass
652 652
653 653 if default:
654 654 try:
655 655 p = self.paths.getpath(default)
656 656 if p:
657 657 return p.rawloc
658 658 except error.RepoError:
659 659 pass
660 660
661 661 return loc
662 662
663 663 @util.propertycache
664 664 def paths(self):
665 665 return paths(self)
666 666
667 667 def pushbuffer(self, error=False, subproc=False, labeled=False):
668 668 """install a buffer to capture standard output of the ui object
669 669
670 670 If error is True, the error output will be captured too.
671 671
672 672 If subproc is True, output from subprocesses (typically hooks) will be
673 673 captured too.
674 674
675 675 If labeled is True, any labels associated with buffered
676 676 output will be handled. By default, this has no effect
677 677 on the output returned, but extensions and GUI tools may
678 678 handle this argument and returned styled output. If output
679 679 is being buffered so it can be captured and parsed or
680 680 processed, labeled should not be set to True.
681 681 """
682 682 self._buffers.append([])
683 683 self._bufferstates.append((error, subproc, labeled))
684 684 self._bufferapplylabels = labeled
685 685
686 686 def popbuffer(self):
687 687 '''pop the last buffer and return the buffered output'''
688 688 self._bufferstates.pop()
689 689 if self._bufferstates:
690 690 self._bufferapplylabels = self._bufferstates[-1][2]
691 691 else:
692 692 self._bufferapplylabels = None
693 693
694 694 return "".join(self._buffers.pop())
695 695
696 696 def write(self, *args, **opts):
697 697 '''write args to output
698 698
699 699 By default, this method simply writes to the buffer or stdout,
700 700 but extensions or GUI tools may override this method,
701 701 write_err(), popbuffer(), and label() to style output from
702 702 various parts of hg.
703 703
704 704 An optional keyword argument, "label", can be passed in.
705 705 This should be a string containing label names separated by
706 706 space. Label names take the form of "topic.type". For example,
707 707 ui.debug() issues a label of "ui.debug".
708 708
709 709 When labeling output for a specific command, a label of
710 710 "cmdname.type" is recommended. For example, status issues
711 711 a label of "status.modified" for modified files.
712 712 '''
713 713 if self._buffers and not opts.get('prompt', False):
714 714 self._buffers[-1].extend(a for a in args)
715 715 else:
716 716 self._progclear()
717 717 for a in args:
718 718 self.fout.write(a)
719 719
720 720 def write_err(self, *args, **opts):
721 721 self._progclear()
722 722 try:
723 723 if self._bufferstates and self._bufferstates[-1][0]:
724 724 return self.write(*args, **opts)
725 725 if not getattr(self.fout, 'closed', False):
726 726 self.fout.flush()
727 727 for a in args:
728 728 self.ferr.write(a)
729 729 # stderr may be buffered under win32 when redirected to files,
730 730 # including stdout.
731 731 if not getattr(self.ferr, 'closed', False):
732 732 self.ferr.flush()
733 733 except IOError as inst:
734 734 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
735 735 raise
736 736
737 737 def flush(self):
738 738 try: self.fout.flush()
739 739 except (IOError, ValueError): pass
740 740 try: self.ferr.flush()
741 741 except (IOError, ValueError): pass
742 742
743 743 def _isatty(self, fh):
744 744 if self.configbool('ui', 'nontty', False):
745 745 return False
746 746 return util.isatty(fh)
747 747
748 748 def interface(self, feature):
749 749 """what interface to use for interactive console features?
750 750
751 751 The interface is controlled by the value of `ui.interface` but also by
752 752 the value of feature-specific configuration. For example:
753 753
754 754 ui.interface.histedit = text
755 755 ui.interface.chunkselector = curses
756 756
757 757 Here the features are "histedit" and "chunkselector".
758 758
759 759 The configuration above means that the default interfaces for commands
760 760 is curses, the interface for histedit is text and the interface for
761 761 selecting chunk is crecord (the best curses interface available).
762 762
763 763 Consider the following example:
764 764 ui.interface = curses
765 765 ui.interface.histedit = text
766 766
767 767 Then histedit will use the text interface and chunkselector will use
768 768 the default curses interface (crecord at the moment).
769 769 """
770 770 alldefaults = frozenset(["text", "curses"])
771 771
772 772 featureinterfaces = {
773 773 "chunkselector": [
774 774 "text",
775 775 "curses",
776 776 ]
777 777 }
778 778
779 779 # Feature-specific interface
780 780 if feature not in featureinterfaces.keys():
781 781 # Programming error, not user error
782 782 raise ValueError("Unknown feature requested %s" % feature)
783 783
784 784 availableinterfaces = frozenset(featureinterfaces[feature])
785 785 if alldefaults > availableinterfaces:
786 786 # Programming error, not user error. We need a use case to
787 787 # define the right thing to do here.
788 788 raise ValueError(
789 789 "Feature %s does not handle all default interfaces" %
790 790 feature)
791 791
792 792 if self.plain():
793 793 return "text"
794 794
795 795 # Default interface for all the features
796 796 defaultinterface = "text"
797 797 i = self.config("ui", "interface", None)
798 798 if i in alldefaults:
799 799 defaultinterface = i
800 800
801 801 choseninterface = defaultinterface
802 802 f = self.config("ui", "interface.%s" % feature, None)
803 803 if f in availableinterfaces:
804 804 choseninterface = f
805 805
806 806 if i is not None and defaultinterface != i:
807 807 if f is not None:
808 808 self.warn(_("invalid value for ui.interface: %s\n") %
809 809 (i,))
810 810 else:
811 811 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
812 812 (i, choseninterface))
813 813 if f is not None and choseninterface != f:
814 814 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
815 815 (feature, f, choseninterface))
816 816
817 817 return choseninterface
818 818
819 819 def interactive(self):
820 820 '''is interactive input allowed?
821 821
822 822 An interactive session is a session where input can be reasonably read
823 823 from `sys.stdin'. If this function returns false, any attempt to read
824 824 from stdin should fail with an error, unless a sensible default has been
825 825 specified.
826 826
827 827 Interactiveness is triggered by the value of the `ui.interactive'
828 828 configuration variable or - if it is unset - when `sys.stdin' points
829 829 to a terminal device.
830 830
831 831 This function refers to input only; for output, see `ui.formatted()'.
832 832 '''
833 833 i = self.configbool("ui", "interactive", None)
834 834 if i is None:
835 835 # some environments replace stdin without implementing isatty
836 836 # usually those are non-interactive
837 837 return self._isatty(self.fin)
838 838
839 839 return i
840 840
841 841 def termwidth(self):
842 842 '''how wide is the terminal in columns?
843 843 '''
844 844 if 'COLUMNS' in encoding.environ:
845 845 try:
846 846 return int(encoding.environ['COLUMNS'])
847 847 except ValueError:
848 848 pass
849 849 return scmutil.termsize(self)[0]
850 850
851 851 def formatted(self):
852 852 '''should formatted output be used?
853 853
854 854 It is often desirable to format the output to suite the output medium.
855 855 Examples of this are truncating long lines or colorizing messages.
856 856 However, this is not often not desirable when piping output into other
857 857 utilities, e.g. `grep'.
858 858
859 859 Formatted output is triggered by the value of the `ui.formatted'
860 860 configuration variable or - if it is unset - when `sys.stdout' points
861 861 to a terminal device. Please note that `ui.formatted' should be
862 862 considered an implementation detail; it is not intended for use outside
863 863 Mercurial or its extensions.
864 864
865 865 This function refers to output only; for input, see `ui.interactive()'.
866 866 This function always returns false when in plain mode, see `ui.plain()'.
867 867 '''
868 868 if self.plain():
869 869 return False
870 870
871 871 i = self.configbool("ui", "formatted", None)
872 872 if i is None:
873 873 # some environments replace stdout without implementing isatty
874 874 # usually those are non-interactive
875 875 return self._isatty(self.fout)
876 876
877 877 return i
878 878
879 879 def _readline(self, prompt=''):
880 880 if self._isatty(self.fin):
881 881 try:
882 882 # magically add command line editing support, where
883 883 # available
884 884 import readline
885 885 # force demandimport to really load the module
886 886 readline.read_history_file
887 887 # windows sometimes raises something other than ImportError
888 888 except Exception:
889 889 pass
890 890
891 891 # call write() so output goes through subclassed implementation
892 892 # e.g. color extension on Windows
893 893 self.write(prompt, prompt=True)
894 894
895 895 # instead of trying to emulate raw_input, swap (self.fin,
896 896 # self.fout) with (sys.stdin, sys.stdout)
897 897 oldin = sys.stdin
898 898 oldout = sys.stdout
899 899 sys.stdin = self.fin
900 900 sys.stdout = self.fout
901 901 # prompt ' ' must exist; otherwise readline may delete entire line
902 902 # - http://bugs.python.org/issue12833
903 903 line = raw_input(' ')
904 904 sys.stdin = oldin
905 905 sys.stdout = oldout
906 906
907 907 # When stdin is in binary mode on Windows, it can cause
908 908 # raw_input() to emit an extra trailing carriage return
909 909 if os.linesep == '\r\n' and line and line[-1] == '\r':
910 910 line = line[:-1]
911 911 return line
912 912
913 913 def prompt(self, msg, default="y"):
914 914 """Prompt user with msg, read response.
915 915 If ui is not interactive, the default is returned.
916 916 """
917 917 if not self.interactive():
918 918 self.write(msg, ' ', default or '', "\n")
919 919 return default
920 920 try:
921 921 r = self._readline(self.label(msg, 'ui.prompt'))
922 922 if not r:
923 923 r = default
924 924 if self.configbool('ui', 'promptecho'):
925 925 self.write(r, "\n")
926 926 return r
927 927 except EOFError:
928 928 raise error.ResponseExpected()
929 929
930 930 @staticmethod
931 931 def extractchoices(prompt):
932 932 """Extract prompt message and list of choices from specified prompt.
933 933
934 934 This returns tuple "(message, choices)", and "choices" is the
935 935 list of tuple "(response character, text without &)".
936 936
937 937 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
938 938 ('awake? ', [('y', 'Yes'), ('n', 'No')])
939 939 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
940 940 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
941 941 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
942 942 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
943 943 """
944 944
945 945 # Sadly, the prompt string may have been built with a filename
946 946 # containing "$$" so let's try to find the first valid-looking
947 947 # prompt to start parsing. Sadly, we also can't rely on
948 948 # choices containing spaces, ASCII, or basically anything
949 949 # except an ampersand followed by a character.
950 950 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
951 951 msg = m.group(1)
952 952 choices = [p.strip(' ') for p in m.group(2).split('$$')]
953 953 return (msg,
954 954 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
955 955 for s in choices])
956 956
957 957 def promptchoice(self, prompt, default=0):
958 958 """Prompt user with a message, read response, and ensure it matches
959 959 one of the provided choices. The prompt is formatted as follows:
960 960
961 961 "would you like fries with that (Yn)? $$ &Yes $$ &No"
962 962
963 963 The index of the choice is returned. Responses are case
964 964 insensitive. If ui is not interactive, the default is
965 965 returned.
966 966 """
967 967
968 968 msg, choices = self.extractchoices(prompt)
969 969 resps = [r for r, t in choices]
970 970 while True:
971 971 r = self.prompt(msg, resps[default])
972 972 if r.lower() in resps:
973 973 return resps.index(r.lower())
974 974 self.write(_("unrecognized response\n"))
975 975
976 976 def getpass(self, prompt=None, default=None):
977 977 if not self.interactive():
978 978 return default
979 979 try:
980 980 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
981 981 # disable getpass() only if explicitly specified. it's still valid
982 982 # to interact with tty even if fin is not a tty.
983 983 if self.configbool('ui', 'nontty'):
984 984 l = self.fin.readline()
985 985 if not l:
986 986 raise EOFError
987 987 return l.rstrip('\n')
988 988 else:
989 989 return getpass.getpass('')
990 990 except EOFError:
991 991 raise error.ResponseExpected()
992 992 def status(self, *msg, **opts):
993 993 '''write status message to output (if ui.quiet is False)
994 994
995 995 This adds an output label of "ui.status".
996 996 '''
997 997 if not self.quiet:
998 998 opts['label'] = opts.get('label', '') + ' ui.status'
999 999 self.write(*msg, **opts)
1000 1000 def warn(self, *msg, **opts):
1001 1001 '''write warning message to output (stderr)
1002 1002
1003 1003 This adds an output label of "ui.warning".
1004 1004 '''
1005 1005 opts['label'] = opts.get('label', '') + ' ui.warning'
1006 1006 self.write_err(*msg, **opts)
1007 1007 def note(self, *msg, **opts):
1008 1008 '''write note to output (if ui.verbose is True)
1009 1009
1010 1010 This adds an output label of "ui.note".
1011 1011 '''
1012 1012 if self.verbose:
1013 1013 opts['label'] = opts.get('label', '') + ' ui.note'
1014 1014 self.write(*msg, **opts)
1015 1015 def debug(self, *msg, **opts):
1016 1016 '''write debug message to output (if ui.debugflag is True)
1017 1017
1018 1018 This adds an output label of "ui.debug".
1019 1019 '''
1020 1020 if self.debugflag:
1021 1021 opts['label'] = opts.get('label', '') + ' ui.debug'
1022 1022 self.write(*msg, **opts)
1023 1023
1024 1024 def edit(self, text, user, extra=None, editform=None, pending=None,
1025 1025 repopath=None):
1026 1026 extra_defaults = {
1027 1027 'prefix': 'editor',
1028 1028 'suffix': '.txt',
1029 1029 }
1030 1030 if extra is not None:
1031 1031 extra_defaults.update(extra)
1032 1032 extra = extra_defaults
1033 1033
1034 1034 rdir = None
1035 1035 if self.configbool('experimental', 'editortmpinhg'):
1036 1036 rdir = repopath
1037 1037 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1038 1038 suffix=extra['suffix'], text=True,
1039 1039 dir=rdir)
1040 1040 try:
1041 1041 f = os.fdopen(fd, "w")
1042 1042 f.write(text)
1043 1043 f.close()
1044 1044
1045 1045 environ = {'HGUSER': user}
1046 1046 if 'transplant_source' in extra:
1047 1047 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1048 1048 for label in ('intermediate-source', 'source', 'rebase_source'):
1049 1049 if label in extra:
1050 1050 environ.update({'HGREVISION': extra[label]})
1051 1051 break
1052 1052 if editform:
1053 1053 environ.update({'HGEDITFORM': editform})
1054 1054 if pending:
1055 1055 environ.update({'HG_PENDING': pending})
1056 1056
1057 1057 editor = self.geteditor()
1058 1058
1059 1059 self.system("%s \"%s\"" % (editor, name),
1060 1060 environ=environ,
1061 1061 onerr=error.Abort, errprefix=_("edit failed"))
1062 1062
1063 1063 f = open(name)
1064 1064 t = f.read()
1065 1065 f.close()
1066 1066 finally:
1067 1067 os.unlink(name)
1068 1068
1069 1069 return t
1070 1070
1071 1071 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
1072 1072 '''execute shell command with appropriate output stream. command
1073 1073 output will be redirected if fout is not stdout.
1074 1074 '''
1075 1075 out = self.fout
1076 1076 if any(s[1] for s in self._bufferstates):
1077 1077 out = self
1078 1078 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
1079 1079 errprefix=errprefix, out=out)
1080 1080
1081 1081 def traceback(self, exc=None, force=False):
1082 1082 '''print exception traceback if traceback printing enabled or forced.
1083 1083 only to call in exception handler. returns true if traceback
1084 1084 printed.'''
1085 1085 if self.tracebackflag or force:
1086 1086 if exc is None:
1087 1087 exc = sys.exc_info()
1088 1088 cause = getattr(exc[1], 'cause', None)
1089 1089
1090 1090 if cause is not None:
1091 1091 causetb = traceback.format_tb(cause[2])
1092 1092 exctb = traceback.format_tb(exc[2])
1093 1093 exconly = traceback.format_exception_only(cause[0], cause[1])
1094 1094
1095 1095 # exclude frame where 'exc' was chained and rethrown from exctb
1096 1096 self.write_err('Traceback (most recent call last):\n',
1097 1097 ''.join(exctb[:-1]),
1098 1098 ''.join(causetb),
1099 1099 ''.join(exconly))
1100 1100 else:
1101 1101 output = traceback.format_exception(exc[0], exc[1], exc[2])
1102 1102 self.write_err(''.join(output))
1103 1103 return self.tracebackflag or force
1104 1104
1105 1105 def geteditor(self):
1106 1106 '''return editor to use'''
1107 1107 if pycompat.sysplatform == 'plan9':
1108 1108 # vi is the MIPS instruction simulator on Plan 9. We
1109 1109 # instead default to E to plumb commit messages to
1110 1110 # avoid confusion.
1111 1111 editor = 'E'
1112 1112 else:
1113 1113 editor = 'vi'
1114 1114 return (encoding.environ.get("HGEDITOR") or
1115 1115 self.config("ui", "editor") or
1116 1116 encoding.environ.get("VISUAL") or
1117 1117 encoding.environ.get("EDITOR", editor))
1118 1118
1119 1119 @util.propertycache
1120 1120 def _progbar(self):
1121 1121 """setup the progbar singleton to the ui object"""
1122 1122 if (self.quiet or self.debugflag
1123 1123 or self.configbool('progress', 'disable', False)
1124 1124 or not progress.shouldprint(self)):
1125 1125 return None
1126 1126 return getprogbar(self)
1127 1127
1128 1128 def _progclear(self):
1129 1129 """clear progress bar output if any. use it before any output"""
1130 1130 if '_progbar' not in vars(self): # nothing loaded yet
1131 1131 return
1132 1132 if self._progbar is not None and self._progbar.printed:
1133 1133 self._progbar.clear()
1134 1134
1135 1135 def progress(self, topic, pos, item="", unit="", total=None):
1136 1136 '''show a progress message
1137 1137
1138 1138 By default a textual progress bar will be displayed if an operation
1139 1139 takes too long. 'topic' is the current operation, 'item' is a
1140 1140 non-numeric marker of the current position (i.e. the currently
1141 1141 in-process file), 'pos' is the current numeric position (i.e.
1142 1142 revision, bytes, etc.), unit is a corresponding unit label,
1143 1143 and total is the highest expected pos.
1144 1144
1145 1145 Multiple nested topics may be active at a time.
1146 1146
1147 1147 All topics should be marked closed by setting pos to None at
1148 1148 termination.
1149 1149 '''
1150 1150 if self._progbar is not None:
1151 1151 self._progbar.progress(topic, pos, item=item, unit=unit,
1152 1152 total=total)
1153 1153 if pos is None or not self.configbool('progress', 'debug'):
1154 1154 return
1155 1155
1156 1156 if unit:
1157 1157 unit = ' ' + unit
1158 1158 if item:
1159 1159 item = ' ' + item
1160 1160
1161 1161 if total:
1162 1162 pct = 100.0 * pos / total
1163 1163 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1164 1164 % (topic, item, pos, total, unit, pct))
1165 1165 else:
1166 1166 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1167 1167
1168 1168 def log(self, service, *msg, **opts):
1169 1169 '''hook for logging facility extensions
1170 1170
1171 1171 service should be a readily-identifiable subsystem, which will
1172 1172 allow filtering.
1173 1173
1174 1174 *msg should be a newline-terminated format string to log, and
1175 1175 then any values to %-format into that format string.
1176 1176
1177 1177 **opts currently has no defined meanings.
1178 1178 '''
1179 1179
1180 1180 def label(self, msg, label):
1181 1181 '''style msg based on supplied label
1182 1182
1183 1183 Like ui.write(), this just returns msg unchanged, but extensions
1184 1184 and GUI tools can override it to allow styling output without
1185 1185 writing it.
1186 1186
1187 1187 ui.write(s, 'label') is equivalent to
1188 1188 ui.write(ui.label(s, 'label')).
1189 1189 '''
1190 1190 return msg
1191 1191
1192 1192 def develwarn(self, msg, stacklevel=1, config=None):
1193 1193 """issue a developer warning message
1194 1194
1195 1195 Use 'stacklevel' to report the offender some layers further up in the
1196 1196 stack.
1197 1197 """
1198 1198 if not self.configbool('devel', 'all-warnings'):
1199 1199 if config is not None and not self.configbool('devel', config):
1200 1200 return
1201 1201 msg = 'devel-warn: ' + msg
1202 1202 stacklevel += 1 # get in develwarn
1203 1203 if self.tracebackflag:
1204 1204 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1205 1205 self.log('develwarn', '%s at:\n%s' %
1206 1206 (msg, ''.join(util.getstackframes(stacklevel))))
1207 1207 else:
1208 1208 curframe = inspect.currentframe()
1209 1209 calframe = inspect.getouterframes(curframe, 2)
1210 1210 self.write_err('%s at: %s:%s (%s)\n'
1211 1211 % ((msg,) + calframe[stacklevel][1:4]))
1212 1212 self.log('develwarn', '%s at: %s:%s (%s)\n',
1213 1213 msg, *calframe[stacklevel][1:4])
1214 1214 curframe = calframe = None # avoid cycles
1215 1215
1216 1216 def deprecwarn(self, msg, version):
1217 1217 """issue a deprecation warning
1218 1218
1219 1219 - msg: message explaining what is deprecated and how to upgrade,
1220 1220 - version: last version where the API will be supported,
1221 1221 """
1222 1222 if not (self.configbool('devel', 'all-warnings')
1223 1223 or self.configbool('devel', 'deprec-warn')):
1224 1224 return
1225 1225 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1226 1226 " update your code.)") % version
1227 1227 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1228 1228
1229 1229 def exportableenviron(self):
1230 1230 """The environment variables that are safe to export, e.g. through
1231 1231 hgweb.
1232 1232 """
1233 1233 return self._exportableenviron
1234 1234
1235 1235 @contextlib.contextmanager
1236 1236 def configoverride(self, overrides, source=""):
1237 1237 """Context manager for temporary config overrides
1238 1238 `overrides` must be a dict of the following structure:
1239 1239 {(section, name) : value}"""
1240 1240 backups = {}
1241 1241 try:
1242 1242 for (section, name), value in overrides.items():
1243 1243 backups[(section, name)] = self.backupconfig(section, name)
1244 1244 self.setconfig(section, name, value, source)
1245 1245 yield
1246 1246 finally:
1247 1247 for __, backup in backups.items():
1248 1248 self.restoreconfig(backup)
1249 1249 # just restoring ui.quiet config to the previous value is not enough
1250 1250 # as it does not update ui.quiet class member
1251 1251 if ('ui', 'quiet') in overrides:
1252 1252 self.fixconfig(section='ui')
1253 1253
1254 1254 class paths(dict):
1255 1255 """Represents a collection of paths and their configs.
1256 1256
1257 1257 Data is initially derived from ui instances and the config files they have
1258 1258 loaded.
1259 1259 """
1260 1260 def __init__(self, ui):
1261 1261 dict.__init__(self)
1262 1262
1263 1263 for name, loc in ui.configitems('paths', ignoresub=True):
1264 1264 # No location is the same as not existing.
1265 1265 if not loc:
1266 1266 continue
1267 1267 loc, sub = ui.configsuboptions('paths', name)
1268 1268 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1269 1269
1270 1270 def getpath(self, name, default=None):
1271 1271 """Return a ``path`` from a string, falling back to default.
1272 1272
1273 1273 ``name`` can be a named path or locations. Locations are filesystem
1274 1274 paths or URIs.
1275 1275
1276 1276 Returns None if ``name`` is not a registered path, a URI, or a local
1277 1277 path to a repo.
1278 1278 """
1279 1279 # Only fall back to default if no path was requested.
1280 1280 if name is None:
1281 1281 if not default:
1282 1282 default = ()
1283 1283 elif not isinstance(default, (tuple, list)):
1284 1284 default = (default,)
1285 1285 for k in default:
1286 1286 try:
1287 1287 return self[k]
1288 1288 except KeyError:
1289 1289 continue
1290 1290 return None
1291 1291
1292 1292 # Most likely empty string.
1293 1293 # This may need to raise in the future.
1294 1294 if not name:
1295 1295 return None
1296 1296
1297 1297 try:
1298 1298 return self[name]
1299 1299 except KeyError:
1300 1300 # Try to resolve as a local path or URI.
1301 1301 try:
1302 1302 # We don't pass sub-options in, so no need to pass ui instance.
1303 1303 return path(None, None, rawloc=name)
1304 1304 except ValueError:
1305 1305 raise error.RepoError(_('repository %s does not exist') %
1306 1306 name)
1307 1307
1308 1308 _pathsuboptions = {}
1309 1309
1310 1310 def pathsuboption(option, attr):
1311 1311 """Decorator used to declare a path sub-option.
1312 1312
1313 1313 Arguments are the sub-option name and the attribute it should set on
1314 1314 ``path`` instances.
1315 1315
1316 1316 The decorated function will receive as arguments a ``ui`` instance,
1317 1317 ``path`` instance, and the string value of this option from the config.
1318 1318 The function should return the value that will be set on the ``path``
1319 1319 instance.
1320 1320
1321 1321 This decorator can be used to perform additional verification of
1322 1322 sub-options and to change the type of sub-options.
1323 1323 """
1324 1324 def register(func):
1325 1325 _pathsuboptions[option] = (attr, func)
1326 1326 return func
1327 1327 return register
1328 1328
1329 1329 @pathsuboption('pushurl', 'pushloc')
1330 1330 def pushurlpathoption(ui, path, value):
1331 1331 u = util.url(value)
1332 1332 # Actually require a URL.
1333 1333 if not u.scheme:
1334 1334 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1335 1335 return None
1336 1336
1337 1337 # Don't support the #foo syntax in the push URL to declare branch to
1338 1338 # push.
1339 1339 if u.fragment:
1340 1340 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1341 1341 'ignoring)\n') % path.name)
1342 1342 u.fragment = None
1343 1343
1344 1344 return str(u)
1345 1345
1346 1346 @pathsuboption('pushrev', 'pushrev')
1347 1347 def pushrevpathoption(ui, path, value):
1348 1348 return value
1349 1349
1350 1350 class path(object):
1351 1351 """Represents an individual path and its configuration."""
1352 1352
1353 1353 def __init__(self, ui, name, rawloc=None, suboptions=None):
1354 1354 """Construct a path from its config options.
1355 1355
1356 1356 ``ui`` is the ``ui`` instance the path is coming from.
1357 1357 ``name`` is the symbolic name of the path.
1358 1358 ``rawloc`` is the raw location, as defined in the config.
1359 1359 ``pushloc`` is the raw locations pushes should be made to.
1360 1360
1361 1361 If ``name`` is not defined, we require that the location be a) a local
1362 1362 filesystem path with a .hg directory or b) a URL. If not,
1363 1363 ``ValueError`` is raised.
1364 1364 """
1365 1365 if not rawloc:
1366 1366 raise ValueError('rawloc must be defined')
1367 1367
1368 1368 # Locations may define branches via syntax <base>#<branch>.
1369 1369 u = util.url(rawloc)
1370 1370 branch = None
1371 1371 if u.fragment:
1372 1372 branch = u.fragment
1373 1373 u.fragment = None
1374 1374
1375 1375 self.url = u
1376 1376 self.branch = branch
1377 1377
1378 1378 self.name = name
1379 1379 self.rawloc = rawloc
1380 1380 self.loc = str(u)
1381 1381
1382 1382 # When given a raw location but not a symbolic name, validate the
1383 1383 # location is valid.
1384 1384 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1385 1385 raise ValueError('location is not a URL or path to a local '
1386 1386 'repo: %s' % rawloc)
1387 1387
1388 1388 suboptions = suboptions or {}
1389 1389
1390 1390 # Now process the sub-options. If a sub-option is registered, its
1391 1391 # attribute will always be present. The value will be None if there
1392 1392 # was no valid sub-option.
1393 1393 for suboption, (attr, func) in _pathsuboptions.iteritems():
1394 1394 if suboption not in suboptions:
1395 1395 setattr(self, attr, None)
1396 1396 continue
1397 1397
1398 1398 value = func(ui, self, suboptions[suboption])
1399 1399 setattr(self, attr, value)
1400 1400
1401 1401 def _isvalidlocalpath(self, path):
1402 1402 """Returns True if the given path is a potentially valid repository.
1403 1403 This is its own function so that extensions can change the definition of
1404 1404 'valid' in this case (like when pulling from a git repo into a hg
1405 1405 one)."""
1406 1406 return os.path.isdir(os.path.join(path, '.hg'))
1407 1407
1408 1408 @property
1409 1409 def suboptions(self):
1410 1410 """Return sub-options and their values for this path.
1411 1411
1412 1412 This is intended to be used for presentation purposes.
1413 1413 """
1414 1414 d = {}
1415 1415 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1416 1416 value = getattr(self, attr)
1417 1417 if value is not None:
1418 1418 d[subopt] = value
1419 1419 return d
1420 1420
1421 1421 # we instantiate one globally shared progress bar to avoid
1422 1422 # competing progress bars when multiple UI objects get created
1423 1423 _progresssingleton = None
1424 1424
1425 1425 def getprogbar(ui):
1426 1426 global _progresssingleton
1427 1427 if _progresssingleton is None:
1428 1428 # passing 'ui' object to the singleton is fishy,
1429 1429 # this is how the extension used to work but feel free to rework it.
1430 1430 _progresssingleton = progress.progbar(ui)
1431 1431 return _progresssingleton
@@ -1,231 +1,231
1 1 hide outer repo
2 2 $ hg init
3 3
4 4 Use hgrc within $TESTTMP
5 5
6 6 $ HGRCPATH=`pwd`/hgrc
7 7 $ export HGRCPATH
8 8
9 9 Use an alternate var for scribbling on hgrc to keep check-code from
10 10 complaining about the important settings we may be overwriting:
11 11
12 12 $ HGRC=`pwd`/hgrc
13 13 $ export HGRC
14 14
15 15 Basic syntax error
16 16
17 17 $ echo "invalid" > $HGRC
18 18 $ hg version
19 19 hg: parse error at $TESTTMP/hgrc:1: invalid
20 20 [255]
21 21 $ echo "" > $HGRC
22 22
23 23 Issue1199: Can't use '%' in hgrc (eg url encoded username)
24 24
25 25 $ hg init "foo%bar"
26 26 $ hg clone "foo%bar" foobar
27 27 updating to branch default
28 28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 29 $ cd foobar
30 30 $ cat .hg/hgrc
31 31 # example repository config (see 'hg help config' for more info)
32 32 [paths]
33 33 default = $TESTTMP/foo%bar (glob)
34 34
35 35 # path aliases to other clones of this repo in URLs or filesystem paths
36 36 # (see 'hg help config.paths' for more info)
37 37 #
38 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
40 # my-clone = /home/jdoe/jdoes-clone
38 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
40 # my-clone = /home/jdoe/jdoes-clone
41 41
42 42 [ui]
43 43 # name and email (local to this repository, optional), e.g.
44 44 # username = Jane Doe <jdoe@example.com>
45 45 $ hg paths
46 46 default = $TESTTMP/foo%bar (glob)
47 47 $ hg showconfig
48 48 bundle.mainreporoot=$TESTTMP/foobar (glob)
49 49 paths.default=$TESTTMP/foo%bar (glob)
50 50 $ cd ..
51 51
52 52 issue1829: wrong indentation
53 53
54 54 $ echo '[foo]' > $HGRC
55 55 $ echo ' x = y' >> $HGRC
56 56 $ hg version
57 57 hg: parse error at $TESTTMP/hgrc:2: x = y
58 58 unexpected leading whitespace
59 59 [255]
60 60
61 61 $ $PYTHON -c "print '[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n'" \
62 62 > > $HGRC
63 63 $ hg showconfig foo
64 64 foo.bar=a\nb\nc\nde\nfg
65 65 foo.baz=bif cb
66 66
67 67 $ FAKEPATH=/path/to/nowhere
68 68 $ export FAKEPATH
69 69 $ echo '%include $FAKEPATH/no-such-file' > $HGRC
70 70 $ hg version
71 71 Mercurial Distributed SCM (version *) (glob)
72 72 (see https://mercurial-scm.org for more information)
73 73
74 74 Copyright (C) 2005-* Matt Mackall and others (glob)
75 75 This is free software; see the source for copying conditions. There is NO
76 76 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
77 77 $ unset FAKEPATH
78 78
79 79 make sure global options given on the cmdline take precedence
80 80
81 81 $ hg showconfig --config ui.verbose=True --quiet
82 82 bundle.mainreporoot=$TESTTMP
83 83 ui.verbose=False
84 84 ui.debug=False
85 85 ui.quiet=True
86 86
87 87 $ touch foobar/untracked
88 88 $ cat >> foobar/.hg/hgrc <<EOF
89 89 > [ui]
90 90 > verbose=True
91 91 > EOF
92 92 $ hg -R foobar st -q
93 93
94 94 username expansion
95 95
96 96 $ olduser=$HGUSER
97 97 $ unset HGUSER
98 98
99 99 $ FAKEUSER='John Doe'
100 100 $ export FAKEUSER
101 101 $ echo '[ui]' > $HGRC
102 102 $ echo 'username = $FAKEUSER' >> $HGRC
103 103
104 104 $ hg init usertest
105 105 $ cd usertest
106 106 $ touch bar
107 107 $ hg commit --addremove --quiet -m "added bar"
108 108 $ hg log --template "{author}\n"
109 109 John Doe
110 110 $ cd ..
111 111
112 112 $ hg showconfig
113 113 bundle.mainreporoot=$TESTTMP
114 114 ui.username=$FAKEUSER
115 115
116 116 $ unset FAKEUSER
117 117 $ HGUSER=$olduser
118 118 $ export HGUSER
119 119
120 120 showconfig with multiple arguments
121 121
122 122 $ echo "[alias]" > $HGRC
123 123 $ echo "log = log -g" >> $HGRC
124 124 $ echo "[defaults]" >> $HGRC
125 125 $ echo "identify = -n" >> $HGRC
126 126 $ hg showconfig alias defaults
127 127 alias.log=log -g
128 128 defaults.identify=-n
129 129 $ hg showconfig alias defaults.identify
130 130 abort: only one config item permitted
131 131 [255]
132 132 $ hg showconfig alias.log defaults.identify
133 133 abort: only one config item permitted
134 134 [255]
135 135
136 136 HGPLAIN
137 137
138 138 $ echo "[ui]" > $HGRC
139 139 $ echo "debug=true" >> $HGRC
140 140 $ echo "fallbackencoding=ASCII" >> $HGRC
141 141 $ echo "quiet=true" >> $HGRC
142 142 $ echo "slash=true" >> $HGRC
143 143 $ echo "traceback=true" >> $HGRC
144 144 $ echo "verbose=true" >> $HGRC
145 145 $ echo "style=~/.hgstyle" >> $HGRC
146 146 $ echo "logtemplate={node}" >> $HGRC
147 147 $ echo "[defaults]" >> $HGRC
148 148 $ echo "identify=-n" >> $HGRC
149 149 $ echo "[alias]" >> $HGRC
150 150 $ echo "log=log -g" >> $HGRC
151 151
152 152 customized hgrc
153 153
154 154 $ hg showconfig
155 155 read config from: $TESTTMP/hgrc
156 156 $TESTTMP/hgrc:13: alias.log=log -g
157 157 repo: bundle.mainreporoot=$TESTTMP
158 158 $TESTTMP/hgrc:11: defaults.identify=-n
159 159 $TESTTMP/hgrc:2: ui.debug=true
160 160 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
161 161 $TESTTMP/hgrc:4: ui.quiet=true
162 162 $TESTTMP/hgrc:5: ui.slash=true
163 163 $TESTTMP/hgrc:6: ui.traceback=true
164 164 $TESTTMP/hgrc:7: ui.verbose=true
165 165 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
166 166 $TESTTMP/hgrc:9: ui.logtemplate={node}
167 167
168 168 plain hgrc
169 169
170 170 $ HGPLAIN=; export HGPLAIN
171 171 $ hg showconfig --config ui.traceback=True --debug
172 172 read config from: $TESTTMP/hgrc
173 173 repo: bundle.mainreporoot=$TESTTMP
174 174 --config: ui.traceback=True
175 175 --verbose: ui.verbose=False
176 176 --debug: ui.debug=True
177 177 --quiet: ui.quiet=False
178 178
179 179 plain mode with exceptions
180 180
181 181 $ cat > plain.py <<EOF
182 182 > from mercurial import commands, extensions
183 183 > def _config(orig, ui, repo, *values, **opts):
184 184 > ui.write('plain: %r\n' % ui.plain())
185 185 > return orig(ui, repo, *values, **opts)
186 186 > def uisetup(ui):
187 187 > extensions.wrapcommand(commands.table, 'config', _config)
188 188 > EOF
189 189 $ echo "[extensions]" >> $HGRC
190 190 $ echo "plain=./plain.py" >> $HGRC
191 191 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
192 192 $ hg showconfig --config ui.traceback=True --debug
193 193 plain: True
194 194 read config from: $TESTTMP/hgrc
195 195 repo: bundle.mainreporoot=$TESTTMP
196 196 $TESTTMP/hgrc:15: extensions.plain=./plain.py
197 197 --config: ui.traceback=True
198 198 --verbose: ui.verbose=False
199 199 --debug: ui.debug=True
200 200 --quiet: ui.quiet=False
201 201 $ unset HGPLAIN
202 202 $ hg showconfig --config ui.traceback=True --debug
203 203 plain: True
204 204 read config from: $TESTTMP/hgrc
205 205 repo: bundle.mainreporoot=$TESTTMP
206 206 $TESTTMP/hgrc:15: extensions.plain=./plain.py
207 207 --config: ui.traceback=True
208 208 --verbose: ui.verbose=False
209 209 --debug: ui.debug=True
210 210 --quiet: ui.quiet=False
211 211 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
212 212 $ hg showconfig --config ui.traceback=True --debug
213 213 plain: True
214 214 read config from: $TESTTMP/hgrc
215 215 repo: bundle.mainreporoot=$TESTTMP
216 216 $TESTTMP/hgrc:15: extensions.plain=./plain.py
217 217 --config: ui.traceback=True
218 218 --verbose: ui.verbose=False
219 219 --debug: ui.debug=True
220 220 --quiet: ui.quiet=False
221 221
222 222 source of paths is not mangled
223 223
224 224 $ cat >> $HGRCPATH <<EOF
225 225 > [paths]
226 226 > foo = bar
227 227 > EOF
228 228 $ hg showconfig --debug paths
229 229 plain: True
230 230 read config from: $TESTTMP/hgrc
231 231 $TESTTMP/hgrc:17: paths.foo=$TESTTMP/bar (glob)
@@ -1,80 +1,80
1 1 #require killdaemons
2 2
3 3 $ hg init test
4 4 $ cd test
5 5 $ echo a > a
6 6 $ hg ci -Ama
7 7 adding a
8 8 $ cd ..
9 9 $ hg clone test test2
10 10 updating to branch default
11 11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 12 $ cd test2
13 13 $ echo a >> a
14 14 $ hg ci -mb
15 15
16 16 Cloning with a password in the URL should not save the password in .hg/hgrc:
17 17
18 18 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
19 19 $ cat hg.pid >> $DAEMON_PIDS
20 20 $ hg clone http://foo:xyzzy@localhost:$HGPORT/ test3
21 21 requesting all changes
22 22 adding changesets
23 23 adding manifests
24 24 adding file changes
25 25 added 2 changesets with 2 changes to 1 files
26 26 updating to branch default
27 27 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 28 $ cat test3/.hg/hgrc
29 29 # example repository config (see 'hg help config' for more info)
30 30 [paths]
31 31 default = http://foo@localhost:$HGPORT/
32 32
33 33 # path aliases to other clones of this repo in URLs or filesystem paths
34 34 # (see 'hg help config.paths' for more info)
35 35 #
36 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
37 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-clone = /home/jdoe/jdoes-clone
36 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
37 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-clone = /home/jdoe/jdoes-clone
39 39
40 40 [ui]
41 41 # name and email (local to this repository, optional), e.g.
42 42 # username = Jane Doe <jdoe@example.com>
43 43 $ killdaemons.py
44 44
45 45 expect error, cloning not allowed
46 46
47 47 $ echo '[web]' > .hg/hgrc
48 48 $ echo 'allowpull = false' >> .hg/hgrc
49 49 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
50 50 $ cat hg.pid >> $DAEMON_PIDS
51 51 $ hg clone http://localhost:$HGPORT/ test4 # bundle2+
52 52 requesting all changes
53 53 abort: authorization failed
54 54 [255]
55 55 $ hg clone http://localhost:$HGPORT/ test4 --config devel.legacy.exchange=bundle1
56 56 abort: authorization failed
57 57 [255]
58 58 $ killdaemons.py
59 59
60 60 serve errors
61 61
62 62 $ cat errors.log
63 63 $ req() {
64 64 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
65 65 > cat hg.pid >> $DAEMON_PIDS
66 66 > hg --cwd ../test pull http://localhost:$HGPORT/
67 67 > killdaemons.py hg.pid
68 68 > echo % serve errors
69 69 > cat errors.log
70 70 > }
71 71
72 72 expect error, pulling not allowed
73 73
74 74 $ req
75 75 pulling from http://localhost:$HGPORT/
76 76 searching for changes
77 77 abort: authorization failed
78 78 % serve errors
79 79
80 80 $ cd ..
@@ -1,116 +1,116
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [alias]
3 3 > tlog = log --template "{rev}:{node|short}: '{desc}' {branches}\n"
4 4 > tglog = tlog -G
5 5 > tout = out --template "{rev}:{node|short}: '{desc}' {branches}\n"
6 6 > EOF
7 7
8 8 $ hg init a
9 9 $ cd a
10 10
11 11 $ echo a > a
12 12 $ hg ci -Aqm0
13 13
14 14 $ echo foo >> a
15 15 $ hg ci -Aqm1
16 16
17 17 $ hg up -q 0
18 18
19 19 $ hg branch stable
20 20 marked working directory as branch stable
21 21 (branches are permanent and global, did you want a bookmark?)
22 22 $ echo bar >> a
23 23 $ hg ci -qm2
24 24
25 25 $ hg tglog
26 26 @ 2:7bee6c3bea3a: '2' stable
27 27 |
28 28 | o 1:3560197d8331: '1'
29 29 |/
30 30 o 0:f7b1eb17ad24: '0'
31 31
32 32
33 33 $ cd ..
34 34
35 35 $ hg clone -q a#stable b
36 36
37 37 $ cd b
38 38 $ cat .hg/hgrc
39 39 # example repository config (see 'hg help config' for more info)
40 40 [paths]
41 41 default = $TESTTMP/a#stable (glob)
42 42
43 43 # path aliases to other clones of this repo in URLs or filesystem paths
44 44 # (see 'hg help config.paths' for more info)
45 45 #
46 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
47 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
48 # my-clone = /home/jdoe/jdoes-clone
46 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
47 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
48 # my-clone = /home/jdoe/jdoes-clone
49 49
50 50 [ui]
51 51 # name and email (local to this repository, optional), e.g.
52 52 # username = Jane Doe <jdoe@example.com>
53 53
54 54 $ echo red >> a
55 55 $ hg ci -qm3
56 56
57 57 $ hg up -q default
58 58
59 59 $ echo blue >> a
60 60 $ hg ci -qm4
61 61
62 62 $ hg tglog
63 63 @ 3:f0461977a3db: '4'
64 64 |
65 65 | o 2:1d4099801a4e: '3' stable
66 66 | |
67 67 | o 1:7bee6c3bea3a: '2' stable
68 68 |/
69 69 o 0:f7b1eb17ad24: '0'
70 70
71 71
72 72 $ hg tout
73 73 comparing with $TESTTMP/a (glob)
74 74 searching for changes
75 75 2:1d4099801a4e: '3' stable
76 76
77 77 $ hg tlog -r 'outgoing()'
78 78 2:1d4099801a4e: '3' stable
79 79
80 80 $ hg tout ../a#default
81 81 comparing with ../a
82 82 searching for changes
83 83 3:f0461977a3db: '4'
84 84
85 85 $ hg tlog -r 'outgoing("../a#default")'
86 86 3:f0461977a3db: '4'
87 87
88 88 $ echo "green = ../a#default" >> .hg/hgrc
89 89
90 90 $ cat .hg/hgrc
91 91 # example repository config (see 'hg help config' for more info)
92 92 [paths]
93 93 default = $TESTTMP/a#stable (glob)
94 94
95 95 # path aliases to other clones of this repo in URLs or filesystem paths
96 96 # (see 'hg help config.paths' for more info)
97 97 #
98 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
99 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
100 # my-clone = /home/jdoe/jdoes-clone
98 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
99 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
100 # my-clone = /home/jdoe/jdoes-clone
101 101
102 102 [ui]
103 103 # name and email (local to this repository, optional), e.g.
104 104 # username = Jane Doe <jdoe@example.com>
105 105 green = ../a#default
106 106
107 107 $ hg tout green
108 108 comparing with green
109 109 abort: repository green not found!
110 110 [255]
111 111
112 112 $ hg tlog -r 'outgoing("green")'
113 113 abort: repository green not found!
114 114 [255]
115 115
116 116 $ cd ..
@@ -1,1097 +1,1097
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extdiff]
3 3 > # for portability:
4 4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 5 > [progress]
6 6 > disable=False
7 7 > assume-tty = 1
8 8 > delay = 0
9 9 > # set changedelay really large so we don't see nested topics
10 10 > changedelay = 30000
11 11 > format = topic bar number
12 12 > refresh = 0
13 13 > width = 60
14 14 > EOF
15 15
16 16 Preparing the subrepository 'sub2'
17 17
18 18 $ hg init sub2
19 19 $ echo sub2 > sub2/sub2
20 20 $ hg add -R sub2
21 21 adding sub2/sub2 (glob)
22 22 $ hg commit -R sub2 -m "sub2 import"
23 23
24 24 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
25 25
26 26 $ hg init sub1
27 27 $ echo sub1 > sub1/sub1
28 28 $ echo "sub2 = ../sub2" > sub1/.hgsub
29 29 $ hg clone sub2 sub1/sub2
30 30 \r (no-eol) (esc)
31 31 linking [ <=> ] 1\r (no-eol) (esc)
32 32 linking [ <=> ] 2\r (no-eol) (esc)
33 33 linking [ <=> ] 3\r (no-eol) (esc)
34 34 linking [ <=> ] 4\r (no-eol) (esc)
35 35 linking [ <=> ] 5\r (no-eol) (esc)
36 36 linking [ <=> ] 6\r (no-eol) (esc)
37 37 \r (no-eol) (esc)
38 38 \r (no-eol) (esc)
39 39 updating [===========================================>] 1/1\r (no-eol) (esc)
40 40 \r (no-eol) (esc)
41 41 updating to branch default
42 42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 43 $ hg add -R sub1
44 44 adding sub1/.hgsub (glob)
45 45 adding sub1/sub1 (glob)
46 46 $ hg commit -R sub1 -m "sub1 import"
47 47
48 48 Preparing the 'main' repo which depends on the subrepo 'sub1'
49 49
50 50 $ hg init main
51 51 $ echo main > main/main
52 52 $ echo "sub1 = ../sub1" > main/.hgsub
53 53 $ hg clone sub1 main/sub1
54 54 \r (no-eol) (esc)
55 55 linking [ <=> ] 1\r (no-eol) (esc)
56 56 linking [ <=> ] 2\r (no-eol) (esc)
57 57 linking [ <=> ] 3\r (no-eol) (esc)
58 58 linking [ <=> ] 4\r (no-eol) (esc)
59 59 linking [ <=> ] 5\r (no-eol) (esc)
60 60 linking [ <=> ] 6\r (no-eol) (esc)
61 61 linking [ <=> ] 7\r (no-eol) (esc)
62 62 linking [ <=> ] 8\r (no-eol) (esc)
63 63 \r (no-eol) (esc)
64 64 \r (no-eol) (esc)
65 65 updating [===========================================>] 3/3\r (no-eol) (esc)
66 66 updating [===========================================>] 1/1\r (no-eol) (esc)
67 67 \r (no-eol) (esc)
68 68 updating to branch default
69 69 cloning subrepo sub2 from $TESTTMP/sub2
70 70 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 71 $ hg add -R main
72 72 adding main/.hgsub (glob)
73 73 adding main/main (glob)
74 74 $ hg commit -R main -m "main import"
75 75
76 76 Cleaning both repositories, just as a clone -U
77 77
78 78 $ hg up -C -R sub2 null
79 79 \r (no-eol) (esc)
80 80 updating [===========================================>] 1/1\r (no-eol) (esc)
81 81 \r (no-eol) (esc)
82 82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 83 $ hg up -C -R sub1 null
84 84 \r (no-eol) (esc)
85 85 updating [===========================================>] 1/1\r (no-eol) (esc)
86 86 \r (no-eol) (esc)
87 87 \r (no-eol) (esc)
88 88 updating [===========================================>] 3/3\r (no-eol) (esc)
89 89 \r (no-eol) (esc)
90 90 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
91 91 $ hg up -C -R main null
92 92 \r (no-eol) (esc)
93 93 updating [===========================================>] 1/1\r (no-eol) (esc)
94 94 \r (no-eol) (esc)
95 95 \r (no-eol) (esc)
96 96 updating [===========================================>] 3/3\r (no-eol) (esc)
97 97 \r (no-eol) (esc)
98 98 \r (no-eol) (esc)
99 99 updating [===========================================>] 3/3\r (no-eol) (esc)
100 100 \r (no-eol) (esc)
101 101 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
102 102 $ rm -rf main/sub1
103 103 $ rm -rf sub1/sub2
104 104
105 105 Clone main
106 106
107 107 $ hg --config extensions.largefiles= clone main cloned
108 108 \r (no-eol) (esc)
109 109 linking [ <=> ] 1\r (no-eol) (esc)
110 110 linking [ <=> ] 2\r (no-eol) (esc)
111 111 linking [ <=> ] 3\r (no-eol) (esc)
112 112 linking [ <=> ] 4\r (no-eol) (esc)
113 113 linking [ <=> ] 5\r (no-eol) (esc)
114 114 linking [ <=> ] 6\r (no-eol) (esc)
115 115 linking [ <=> ] 7\r (no-eol) (esc)
116 116 linking [ <=> ] 8\r (no-eol) (esc)
117 117 \r (no-eol) (esc)
118 118 \r (no-eol) (esc)
119 119 updating [===========================================>] 3/3\r (no-eol) (esc)
120 120 updating [===========================================>] 3/3\r (no-eol) (esc)
121 121 updating [===========================================>] 1/1\r (no-eol) (esc)
122 122 \r (no-eol) (esc)
123 123 updating to branch default
124 124 cloning subrepo sub1 from $TESTTMP/sub1
125 125 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
126 126 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 127
128 128 Largefiles is NOT enabled in the clone if the source repo doesn't require it
129 129 $ cat cloned/.hg/hgrc
130 130 # example repository config (see 'hg help config' for more info)
131 131 [paths]
132 132 default = $TESTTMP/main (glob)
133 133
134 134 # path aliases to other clones of this repo in URLs or filesystem paths
135 135 # (see 'hg help config.paths' for more info)
136 136 #
137 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
139 # my-clone = /home/jdoe/jdoes-clone
137 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
139 # my-clone = /home/jdoe/jdoes-clone
140 140
141 141 [ui]
142 142 # name and email (local to this repository, optional), e.g.
143 143 # username = Jane Doe <jdoe@example.com>
144 144
145 145 Checking cloned repo ids
146 146
147 147 $ printf "cloned " ; hg id -R cloned
148 148 cloned 7f491f53a367 tip
149 149 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
150 150 cloned/sub1 fc3b4ce2696f tip
151 151 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
152 152 cloned/sub1/sub2 c57a0840e3ba tip
153 153
154 154 debugsub output for main and sub1
155 155
156 156 $ hg debugsub -R cloned
157 157 path sub1
158 158 source ../sub1
159 159 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
160 160 $ hg debugsub -R cloned/sub1
161 161 path sub2
162 162 source ../sub2
163 163 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
164 164
165 165 Modifying deeply nested 'sub2'
166 166
167 167 $ echo modified > cloned/sub1/sub2/sub2
168 168 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
169 169 committing subrepository sub1
170 170 committing subrepository sub1/sub2 (glob)
171 171
172 172 Checking modified node ids
173 173
174 174 $ printf "cloned " ; hg id -R cloned
175 175 cloned ffe6649062fe tip
176 176 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
177 177 cloned/sub1 2ecb03bf44a9 tip
178 178 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
179 179 cloned/sub1/sub2 53dd3430bcaf tip
180 180
181 181 debugsub output for main and sub1
182 182
183 183 $ hg debugsub -R cloned
184 184 path sub1
185 185 source ../sub1
186 186 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
187 187 $ hg debugsub -R cloned/sub1
188 188 path sub2
189 189 source ../sub2
190 190 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
191 191
192 192 Check that deep archiving works
193 193
194 194 $ cd cloned
195 195 $ echo 'test' > sub1/sub2/test.txt
196 196 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
197 197 $ mkdir sub1/sub2/folder
198 198 $ echo 'subfolder' > sub1/sub2/folder/test.txt
199 199 $ hg ci -ASm "add test.txt"
200 200 adding sub1/sub2/folder/test.txt
201 201 committing subrepository sub1
202 202 committing subrepository sub1/sub2 (glob)
203 203
204 204 .. but first take a detour through some deep removal testing
205 205
206 206 $ hg remove -S -I 're:.*.txt' .
207 207 \r (no-eol) (esc)
208 208 searching [==========================================>] 1/1\r (no-eol) (esc)
209 209 searching [==========================================>] 1/1\r (no-eol) (esc)
210 210 \r (no-eol) (esc)
211 211 \r (no-eol) (esc)
212 212 deleting [=====================> ] 1/2\r (no-eol) (esc)
213 213 \r (no-eol) (esc)
214 214 \r (no-eol) (esc)
215 215 deleting [===========================================>] 2/2\r (no-eol) (esc)
216 216 \r (no-eol) (esc)
217 217 removing sub1/sub2/folder/test.txt (glob)
218 218 removing sub1/sub2/test.txt (glob)
219 219 $ hg status -S
220 220 R sub1/sub2/folder/test.txt
221 221 R sub1/sub2/test.txt
222 222 $ hg update -Cq
223 223 $ hg remove -I 're:.*.txt' sub1
224 224 \r (no-eol) (esc)
225 225 searching [==========================================>] 1/1\r (no-eol) (esc)
226 226 \r (no-eol) (esc)
227 227 \r (no-eol) (esc)
228 228 deleting [===========================================>] 1/1\r (no-eol) (esc)
229 229 \r (no-eol) (esc)
230 230 $ hg status -S
231 231 $ hg remove sub1/sub2/folder/test.txt
232 232 \r (no-eol) (esc)
233 233 searching [==========================================>] 1/1\r (no-eol) (esc)
234 234 searching [==========================================>] 1/1\r (no-eol) (esc)
235 235 \r (no-eol) (esc)
236 236 \r (no-eol) (esc)
237 237 deleting [===========================================>] 1/1\r (no-eol) (esc)
238 238 \r (no-eol) (esc)
239 239 \r (no-eol) (esc)
240 240 deleting [===========================================>] 1/1\r (no-eol) (esc)
241 241 \r (no-eol) (esc)
242 242 \r (no-eol) (esc)
243 243 deleting [===========================================>] 1/1\r (no-eol) (esc)
244 244 \r (no-eol) (esc)
245 245 $ hg remove sub1/.hgsubstate
246 246 \r (no-eol) (esc)
247 247 searching [==========================================>] 1/1\r (no-eol) (esc)
248 248 \r (no-eol) (esc)
249 249 \r (no-eol) (esc)
250 250 deleting [===========================================>] 1/1\r (no-eol) (esc)
251 251 \r (no-eol) (esc)
252 252 \r (no-eol) (esc)
253 253 deleting [===========================================>] 1/1\r (no-eol) (esc)
254 254 \r (no-eol) (esc)
255 255 $ mv sub1/.hgsub sub1/x.hgsub
256 256 $ hg status -S
257 257 warning: subrepo spec file 'sub1/.hgsub' not found
258 258 R sub1/.hgsubstate
259 259 R sub1/sub2/folder/test.txt
260 260 ! sub1/.hgsub
261 261 ? sub1/x.hgsub
262 262 $ mv sub1/x.hgsub sub1/.hgsub
263 263 $ hg update -Cq
264 264 $ touch sub1/foo
265 265 $ hg forget sub1/sub2/folder/test.txt
266 266 $ rm sub1/sub2/test.txt
267 267
268 268 Test relative path printing + subrepos
269 269 $ mkdir -p foo/bar
270 270 $ cd foo
271 271 $ touch bar/abc
272 272 $ hg addremove -S ..
273 273 \r (no-eol) (esc)
274 274 searching for exact renames [ ] 0/1\r (no-eol) (esc)
275 275 \r (no-eol) (esc)
276 276 adding ../sub1/sub2/folder/test.txt (glob)
277 277 removing ../sub1/sub2/test.txt (glob)
278 278 adding ../sub1/foo (glob)
279 279 adding bar/abc (glob)
280 280 $ cd ..
281 281 $ hg status -S
282 282 A foo/bar/abc
283 283 A sub1/foo
284 284 R sub1/sub2/test.txt
285 285
286 286 Archive wdir() with subrepos
287 287 $ hg rm main
288 288 \r (no-eol) (esc)
289 289 deleting [===========================================>] 1/1\r (no-eol) (esc)
290 290 \r (no-eol) (esc)
291 291 $ hg archive -S -r 'wdir()' ../wdir
292 292 \r (no-eol) (esc)
293 293 archiving [ ] 0/3\r (no-eol) (esc)
294 294 archiving [=============> ] 1/3\r (no-eol) (esc)
295 295 archiving [===========================> ] 2/3\r (no-eol) (esc)
296 296 archiving [==========================================>] 3/3\r (no-eol) (esc)
297 297 \r (no-eol) (esc)
298 298 \r (no-eol) (esc)
299 299 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
300 300 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
301 301 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
302 302 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
303 303 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
304 304 \r (no-eol) (esc)
305 305 \r (no-eol) (esc)
306 306 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
307 307 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
308 308 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
309 309 \r (no-eol) (esc)
310 310 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
311 311 Only in ../wdir: .hg_archival.txt
312 312
313 313 $ find ../wdir -type f | sort
314 314 ../wdir/.hg_archival.txt
315 315 ../wdir/.hgsub
316 316 ../wdir/.hgsubstate
317 317 ../wdir/foo/bar/abc
318 318 ../wdir/sub1/.hgsub
319 319 ../wdir/sub1/.hgsubstate
320 320 ../wdir/sub1/foo
321 321 ../wdir/sub1/sub1
322 322 ../wdir/sub1/sub2/folder/test.txt
323 323 ../wdir/sub1/sub2/sub2
324 324
325 325 $ cat ../wdir/.hg_archival.txt
326 326 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
327 327 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
328 328 branch: default
329 329 latesttag: null
330 330 latesttagdistance: 4
331 331 changessincelatesttag: 4
332 332
333 333 Attempting to archive 'wdir()' with a missing file is handled gracefully
334 334 $ rm sub1/sub1
335 335 $ rm -r ../wdir
336 336 $ hg archive -v -S -r 'wdir()' ../wdir
337 337 \r (no-eol) (esc)
338 338 archiving [ ] 0/3\r (no-eol) (esc)
339 339 archiving [=============> ] 1/3\r (no-eol) (esc)
340 340 archiving [===========================> ] 2/3\r (no-eol) (esc)
341 341 archiving [==========================================>] 3/3\r (no-eol) (esc)
342 342 \r (no-eol) (esc)
343 343 \r (no-eol) (esc)
344 344 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
345 345 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
346 346 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
347 347 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
348 348 \r (no-eol) (esc)
349 349 \r (no-eol) (esc)
350 350 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
351 351 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
352 352 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
353 353 \r (no-eol) (esc)
354 354 $ find ../wdir -type f | sort
355 355 ../wdir/.hg_archival.txt
356 356 ../wdir/.hgsub
357 357 ../wdir/.hgsubstate
358 358 ../wdir/foo/bar/abc
359 359 ../wdir/sub1/.hgsub
360 360 ../wdir/sub1/.hgsubstate
361 361 ../wdir/sub1/foo
362 362 ../wdir/sub1/sub2/folder/test.txt
363 363 ../wdir/sub1/sub2/sub2
364 364
365 365 Continue relative path printing + subrepos
366 366 $ hg update -Cq
367 367 $ rm -r ../wdir
368 368 $ hg archive -S -r 'wdir()' ../wdir
369 369 \r (no-eol) (esc)
370 370 archiving [ ] 0/3\r (no-eol) (esc)
371 371 archiving [=============> ] 1/3\r (no-eol) (esc)
372 372 archiving [===========================> ] 2/3\r (no-eol) (esc)
373 373 archiving [==========================================>] 3/3\r (no-eol) (esc)
374 374 \r (no-eol) (esc)
375 375 \r (no-eol) (esc)
376 376 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
377 377 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
378 378 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
379 379 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
380 380 \r (no-eol) (esc)
381 381 \r (no-eol) (esc)
382 382 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
383 383 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
384 384 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
385 385 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
386 386 \r (no-eol) (esc)
387 387 $ cat ../wdir/.hg_archival.txt
388 388 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
389 389 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
390 390 branch: default
391 391 latesttag: null
392 392 latesttagdistance: 4
393 393 changessincelatesttag: 4
394 394
395 395 $ touch sub1/sub2/folder/bar
396 396 $ hg addremove sub1/sub2
397 397 adding sub1/sub2/folder/bar (glob)
398 398 $ hg status -S
399 399 A sub1/sub2/folder/bar
400 400 ? foo/bar/abc
401 401 ? sub1/foo
402 402 $ hg update -Cq
403 403 $ hg addremove sub1
404 404 adding sub1/sub2/folder/bar (glob)
405 405 adding sub1/foo (glob)
406 406 $ hg update -Cq
407 407 $ rm sub1/sub2/folder/test.txt
408 408 $ rm sub1/sub2/test.txt
409 409 $ hg ci -ASm "remove test.txt"
410 410 adding sub1/sub2/folder/bar
411 411 removing sub1/sub2/folder/test.txt
412 412 removing sub1/sub2/test.txt
413 413 adding sub1/foo
414 414 adding foo/bar/abc
415 415 committing subrepository sub1
416 416 committing subrepository sub1/sub2 (glob)
417 417
418 418 $ hg forget sub1/sub2/sub2
419 419 $ echo x > sub1/sub2/x.txt
420 420 $ hg add sub1/sub2/x.txt
421 421
422 422 Files sees uncommitted adds and removes in subrepos
423 423 $ hg files -S
424 424 .hgsub
425 425 .hgsubstate
426 426 foo/bar/abc (glob)
427 427 main
428 428 sub1/.hgsub (glob)
429 429 sub1/.hgsubstate (glob)
430 430 sub1/foo (glob)
431 431 sub1/sub1 (glob)
432 432 sub1/sub2/folder/bar (glob)
433 433 sub1/sub2/x.txt (glob)
434 434
435 435 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
436 436 .hgsub
437 437 .hgsubstate
438 438 foo/bar/abc (glob)
439 439 main
440 440 sub1/.hgsub (glob)
441 441 sub1/.hgsubstate (glob)
442 442 sub1/foo (glob)
443 443 sub1/sub1 (glob)
444 444 sub1/sub2/folder/bar (glob)
445 445 sub1/sub2/x.txt (glob)
446 446
447 447 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
448 448 .hgsub
449 449 .hgsubstate
450 450 main
451 451 sub1/.hgsub (glob)
452 452 sub1/.hgsubstate (glob)
453 453 sub1/sub1 (glob)
454 454 sub1/sub2/folder/test.txt (glob)
455 455 sub1/sub2/sub2 (glob)
456 456 sub1/sub2/test.txt (glob)
457 457
458 458 $ hg files sub1
459 459 sub1/.hgsub (glob)
460 460 sub1/.hgsubstate (glob)
461 461 sub1/foo (glob)
462 462 sub1/sub1 (glob)
463 463 sub1/sub2/folder/bar (glob)
464 464 sub1/sub2/x.txt (glob)
465 465
466 466 $ hg files sub1/sub2
467 467 sub1/sub2/folder/bar (glob)
468 468 sub1/sub2/x.txt (glob)
469 469
470 470 $ hg files
471 471 .hgsub
472 472 .hgsubstate
473 473 foo/bar/abc (glob)
474 474 main
475 475
476 476 $ hg files -S -r '.^' sub1/sub2/folder
477 477 sub1/sub2/folder/test.txt (glob)
478 478
479 479 $ hg files -S -r '.^' sub1/sub2/missing
480 480 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
481 481 [1]
482 482
483 483 $ hg files -r '.^' sub1/
484 484 sub1/.hgsub (glob)
485 485 sub1/.hgsubstate (glob)
486 486 sub1/sub1 (glob)
487 487 sub1/sub2/folder/test.txt (glob)
488 488 sub1/sub2/sub2 (glob)
489 489 sub1/sub2/test.txt (glob)
490 490
491 491 $ hg files -r '.^' sub1/sub2
492 492 sub1/sub2/folder/test.txt (glob)
493 493 sub1/sub2/sub2 (glob)
494 494 sub1/sub2/test.txt (glob)
495 495
496 496 $ hg rollback -q
497 497 $ hg up -Cq
498 498
499 499 $ hg --config extensions.largefiles=! archive -S ../archive_all
500 500 \r (no-eol) (esc)
501 501 archiving [ ] 0/3\r (no-eol) (esc)
502 502 archiving [=============> ] 1/3\r (no-eol) (esc)
503 503 archiving [===========================> ] 2/3\r (no-eol) (esc)
504 504 archiving [==========================================>] 3/3\r (no-eol) (esc)
505 505 \r (no-eol) (esc)
506 506 \r (no-eol) (esc)
507 507 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
508 508 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
509 509 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
510 510 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
511 511 \r (no-eol) (esc)
512 512 \r (no-eol) (esc)
513 513 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
514 514 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
515 515 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
516 516 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
517 517 \r (no-eol) (esc)
518 518 $ find ../archive_all | sort
519 519 ../archive_all
520 520 ../archive_all/.hg_archival.txt
521 521 ../archive_all/.hgsub
522 522 ../archive_all/.hgsubstate
523 523 ../archive_all/main
524 524 ../archive_all/sub1
525 525 ../archive_all/sub1/.hgsub
526 526 ../archive_all/sub1/.hgsubstate
527 527 ../archive_all/sub1/sub1
528 528 ../archive_all/sub1/sub2
529 529 ../archive_all/sub1/sub2/folder
530 530 ../archive_all/sub1/sub2/folder/test.txt
531 531 ../archive_all/sub1/sub2/sub2
532 532 ../archive_all/sub1/sub2/test.txt
533 533
534 534 Check that archive -X works in deep subrepos
535 535
536 536 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
537 537 \r (no-eol) (esc)
538 538 archiving [ ] 0/3\r (no-eol) (esc)
539 539 archiving [=============> ] 1/3\r (no-eol) (esc)
540 540 archiving [===========================> ] 2/3\r (no-eol) (esc)
541 541 archiving [==========================================>] 3/3\r (no-eol) (esc)
542 542 \r (no-eol) (esc)
543 543 \r (no-eol) (esc)
544 544 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
545 545 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
546 546 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
547 547 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
548 548 \r (no-eol) (esc)
549 549 \r (no-eol) (esc)
550 550 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
551 551 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
552 552 \r (no-eol) (esc)
553 553 $ find ../archive_exclude | sort
554 554 ../archive_exclude
555 555 ../archive_exclude/.hg_archival.txt
556 556 ../archive_exclude/.hgsub
557 557 ../archive_exclude/.hgsubstate
558 558 ../archive_exclude/main
559 559 ../archive_exclude/sub1
560 560 ../archive_exclude/sub1/.hgsub
561 561 ../archive_exclude/sub1/.hgsubstate
562 562 ../archive_exclude/sub1/sub1
563 563 ../archive_exclude/sub1/sub2
564 564 ../archive_exclude/sub1/sub2/sub2
565 565
566 566 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
567 567 \r (no-eol) (esc)
568 568 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
569 569 \r (no-eol) (esc)
570 570 \r (no-eol) (esc)
571 571 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc)
572 572 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc)
573 573 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc)
574 574 \r (no-eol) (esc)
575 575 $ find ../archive_include | sort
576 576 ../archive_include
577 577 ../archive_include/sub1
578 578 ../archive_include/sub1/sub2
579 579 ../archive_include/sub1/sub2/folder
580 580 ../archive_include/sub1/sub2/folder/test.txt
581 581 ../archive_include/sub1/sub2/test.txt
582 582
583 583 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
584 584 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
585 585 subrepos are archived properly.
586 586 Note that add --large through a subrepo currently adds the file as a normal file
587 587
588 588 $ echo "large" > sub1/sub2/large.bin
589 589 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
590 590 $ echo "large" > large.bin
591 591 $ hg --config extensions.largefiles= add --large large.bin
592 592 $ hg --config extensions.largefiles= ci -S -m "add large files"
593 593 committing subrepository sub1
594 594 committing subrepository sub1/sub2 (glob)
595 595
596 596 $ hg --config extensions.largefiles= archive -S ../archive_lf
597 597 $ find ../archive_lf | sort
598 598 ../archive_lf
599 599 ../archive_lf/.hg_archival.txt
600 600 ../archive_lf/.hgsub
601 601 ../archive_lf/.hgsubstate
602 602 ../archive_lf/large.bin
603 603 ../archive_lf/main
604 604 ../archive_lf/sub1
605 605 ../archive_lf/sub1/.hgsub
606 606 ../archive_lf/sub1/.hgsubstate
607 607 ../archive_lf/sub1/sub1
608 608 ../archive_lf/sub1/sub2
609 609 ../archive_lf/sub1/sub2/folder
610 610 ../archive_lf/sub1/sub2/folder/test.txt
611 611 ../archive_lf/sub1/sub2/large.bin
612 612 ../archive_lf/sub1/sub2/sub2
613 613 ../archive_lf/sub1/sub2/test.txt
614 614 $ rm -rf ../archive_lf
615 615
616 616 Exclude large files from main and sub-sub repo
617 617
618 618 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
619 619 $ find ../archive_lf | sort
620 620 ../archive_lf
621 621 ../archive_lf/.hg_archival.txt
622 622 ../archive_lf/.hgsub
623 623 ../archive_lf/.hgsubstate
624 624 ../archive_lf/main
625 625 ../archive_lf/sub1
626 626 ../archive_lf/sub1/.hgsub
627 627 ../archive_lf/sub1/.hgsubstate
628 628 ../archive_lf/sub1/sub1
629 629 ../archive_lf/sub1/sub2
630 630 ../archive_lf/sub1/sub2/folder
631 631 ../archive_lf/sub1/sub2/folder/test.txt
632 632 ../archive_lf/sub1/sub2/sub2
633 633 ../archive_lf/sub1/sub2/test.txt
634 634 $ rm -rf ../archive_lf
635 635
636 636 Exclude normal files from main and sub-sub repo
637 637
638 638 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
639 639 $ tar -tzf ../archive_lf.tgz | sort
640 640 .hgsub
641 641 .hgsubstate
642 642 large.bin
643 643 main
644 644 sub1/.hgsub
645 645 sub1/.hgsubstate
646 646 sub1/sub1
647 647 sub1/sub2/large.bin
648 648 sub1/sub2/sub2
649 649
650 650 Include normal files from within a largefiles subrepo
651 651
652 652 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
653 653 $ find ../archive_lf | sort
654 654 ../archive_lf
655 655 ../archive_lf/.hg_archival.txt
656 656 ../archive_lf/sub1
657 657 ../archive_lf/sub1/sub2
658 658 ../archive_lf/sub1/sub2/folder
659 659 ../archive_lf/sub1/sub2/folder/test.txt
660 660 ../archive_lf/sub1/sub2/test.txt
661 661 $ rm -rf ../archive_lf
662 662
663 663 Include large files from within a largefiles subrepo
664 664
665 665 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
666 666 $ find ../archive_lf | sort
667 667 ../archive_lf
668 668 ../archive_lf/large.bin
669 669 ../archive_lf/sub1
670 670 ../archive_lf/sub1/sub2
671 671 ../archive_lf/sub1/sub2/large.bin
672 672 $ rm -rf ../archive_lf
673 673
674 674 Find an exact largefile match in a largefiles subrepo
675 675
676 676 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
677 677 $ find ../archive_lf | sort
678 678 ../archive_lf
679 679 ../archive_lf/sub1
680 680 ../archive_lf/sub1/sub2
681 681 ../archive_lf/sub1/sub2/large.bin
682 682 $ rm -rf ../archive_lf
683 683
684 684 The local repo enables largefiles if a largefiles repo is cloned
685 685 $ hg showconfig extensions
686 686 abort: repository requires features unknown to this Mercurial: largefiles!
687 687 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
688 688 [255]
689 689 $ hg --config extensions.largefiles= clone -qU . ../lfclone
690 690 $ cat ../lfclone/.hg/hgrc
691 691 # example repository config (see 'hg help config' for more info)
692 692 [paths]
693 693 default = $TESTTMP/cloned (glob)
694 694
695 695 # path aliases to other clones of this repo in URLs or filesystem paths
696 696 # (see 'hg help config.paths' for more info)
697 697 #
698 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
699 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
700 # my-clone = /home/jdoe/jdoes-clone
698 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
699 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
700 # my-clone = /home/jdoe/jdoes-clone
701 701
702 702 [ui]
703 703 # name and email (local to this repository, optional), e.g.
704 704 # username = Jane Doe <jdoe@example.com>
705 705
706 706 [extensions]
707 707 largefiles=
708 708
709 709 Find an exact match to a standin (should archive nothing)
710 710 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
711 711 $ find ../archive_lf 2> /dev/null | sort
712 712
713 713 $ cat >> $HGRCPATH <<EOF
714 714 > [extensions]
715 715 > largefiles=
716 716 > [largefiles]
717 717 > patterns=glob:**.dat
718 718 > EOF
719 719
720 720 Test forget through a deep subrepo with the largefiles extension, both a
721 721 largefile and a normal file. Then a largefile that hasn't been committed yet.
722 722 $ touch sub1/sub2/untracked.txt
723 723 $ touch sub1/sub2/large.dat
724 724 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
725 725 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
726 726 [1]
727 727 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
728 728 adding sub1/sub2/untracked.txt as a largefile (glob)
729 729 $ hg add --large -v sub1/sub2/untracked.txt
730 730 adding sub1/sub2/untracked.txt as a largefile (glob)
731 731 $ hg add --normal -v sub1/sub2/large.dat
732 732 adding sub1/sub2/large.dat (glob)
733 733 $ hg forget -v sub1/sub2/untracked.txt
734 734 removing sub1/sub2/untracked.txt (glob)
735 735 $ hg status -S
736 736 A sub1/sub2/large.dat
737 737 R sub1/sub2/large.bin
738 738 R sub1/sub2/test.txt
739 739 ? foo/bar/abc
740 740 ? sub1/sub2/untracked.txt
741 741 ? sub1/sub2/x.txt
742 742 $ hg add sub1/sub2
743 743
744 744 $ hg archive -S -r 'wdir()' ../wdir2
745 745 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
746 746 Only in ../wdir2: .hg_archival.txt
747 747 Only in .: .hglf
748 748 Only in .: foo
749 749 Only in ./sub1/sub2: large.bin
750 750 Only in ./sub1/sub2: test.txt
751 751 Only in ./sub1/sub2: untracked.txt
752 752 Only in ./sub1/sub2: x.txt
753 753 $ find ../wdir2 -type f | sort
754 754 ../wdir2/.hg_archival.txt
755 755 ../wdir2/.hgsub
756 756 ../wdir2/.hgsubstate
757 757 ../wdir2/large.bin
758 758 ../wdir2/main
759 759 ../wdir2/sub1/.hgsub
760 760 ../wdir2/sub1/.hgsubstate
761 761 ../wdir2/sub1/sub1
762 762 ../wdir2/sub1/sub2/folder/test.txt
763 763 ../wdir2/sub1/sub2/large.dat
764 764 ../wdir2/sub1/sub2/sub2
765 765 $ hg status -S -mac -n | sort
766 766 .hgsub
767 767 .hgsubstate
768 768 large.bin
769 769 main
770 770 sub1/.hgsub
771 771 sub1/.hgsubstate
772 772 sub1/sub1
773 773 sub1/sub2/folder/test.txt
774 774 sub1/sub2/large.dat
775 775 sub1/sub2/sub2
776 776
777 777 $ hg ci -Sqm 'forget testing'
778 778
779 779 Test 'wdir()' modified file archiving with largefiles
780 780 $ echo 'mod' > main
781 781 $ echo 'mod' > large.bin
782 782 $ echo 'mod' > sub1/sub2/large.dat
783 783 $ hg archive -S -r 'wdir()' ../wdir3
784 784 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
785 785 Only in ../wdir3: .hg_archival.txt
786 786 Only in .: .hglf
787 787 Only in .: foo
788 788 Only in ./sub1/sub2: large.bin
789 789 Only in ./sub1/sub2: test.txt
790 790 Only in ./sub1/sub2: untracked.txt
791 791 Only in ./sub1/sub2: x.txt
792 792 $ find ../wdir3 -type f | sort
793 793 ../wdir3/.hg_archival.txt
794 794 ../wdir3/.hgsub
795 795 ../wdir3/.hgsubstate
796 796 ../wdir3/large.bin
797 797 ../wdir3/main
798 798 ../wdir3/sub1/.hgsub
799 799 ../wdir3/sub1/.hgsubstate
800 800 ../wdir3/sub1/sub1
801 801 ../wdir3/sub1/sub2/folder/test.txt
802 802 ../wdir3/sub1/sub2/large.dat
803 803 ../wdir3/sub1/sub2/sub2
804 804 $ hg up -Cq
805 805
806 806 Test issue4330: commit a directory where only normal files have changed
807 807 $ touch foo/bar/large.dat
808 808 $ hg add --large foo/bar/large.dat
809 809 $ hg ci -m 'add foo/bar/large.dat'
810 810 $ touch a.txt
811 811 $ touch a.dat
812 812 $ hg add -v foo/bar/abc a.txt a.dat
813 813 adding a.dat as a largefile
814 814 adding a.txt
815 815 adding foo/bar/abc (glob)
816 816 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
817 817 $ hg status
818 818 A a.dat
819 819 A a.txt
820 820
821 821 Test a directory commit with a changed largefile and a changed normal file
822 822 $ echo changed > foo/bar/large.dat
823 823 $ echo changed > foo/bar/abc
824 824 $ hg ci -m 'dir commit with normal and lf file deltas' foo
825 825 $ hg status
826 826 A a.dat
827 827 A a.txt
828 828
829 829 $ hg ci -m "add a.*"
830 830 $ hg mv a.dat b.dat
831 831 $ hg mv foo/bar/abc foo/bar/def
832 832 $ hg status -C
833 833 A b.dat
834 834 a.dat
835 835 A foo/bar/def
836 836 foo/bar/abc
837 837 R a.dat
838 838 R foo/bar/abc
839 839
840 840 $ hg ci -m "move large and normal"
841 841 $ hg status -C --rev '.^' --rev .
842 842 A b.dat
843 843 a.dat
844 844 A foo/bar/def
845 845 foo/bar/abc
846 846 R a.dat
847 847 R foo/bar/abc
848 848
849 849
850 850 $ echo foo > main
851 851 $ hg ci -m "mod parent only"
852 852 $ hg init sub3
853 853 $ echo "sub3 = sub3" >> .hgsub
854 854 $ echo xyz > sub3/a.txt
855 855 $ hg add sub3/a.txt
856 856 $ hg ci -Sm "add sub3"
857 857 committing subrepository sub3
858 858 $ cat .hgsub | grep -v sub3 > .hgsub1
859 859 $ mv .hgsub1 .hgsub
860 860 $ hg ci -m "remove sub3"
861 861
862 862 $ hg log -r "subrepo()" --style compact
863 863 0 7f491f53a367 1970-01-01 00:00 +0000 test
864 864 main import
865 865
866 866 1 ffe6649062fe 1970-01-01 00:00 +0000 test
867 867 deep nested modif should trigger a commit
868 868
869 869 2 9bb10eebee29 1970-01-01 00:00 +0000 test
870 870 add test.txt
871 871
872 872 3 7c64f035294f 1970-01-01 00:00 +0000 test
873 873 add large files
874 874
875 875 4 f734a59e2e35 1970-01-01 00:00 +0000 test
876 876 forget testing
877 877
878 878 11 9685a22af5db 1970-01-01 00:00 +0000 test
879 879 add sub3
880 880
881 881 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
882 882 remove sub3
883 883
884 884 $ hg log -r "subrepo('sub3')" --style compact
885 885 11 9685a22af5db 1970-01-01 00:00 +0000 test
886 886 add sub3
887 887
888 888 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
889 889 remove sub3
890 890
891 891 $ hg log -r "subrepo('bogus')" --style compact
892 892
893 893
894 894 Test .hgsubstate in the R state
895 895
896 896 $ hg rm .hgsub .hgsubstate
897 897 \r (no-eol) (esc)
898 898 deleting [=====================> ] 1/2\r (no-eol) (esc)
899 899 deleting [===========================================>] 2/2\r (no-eol) (esc)
900 900 \r (no-eol) (esc)
901 901 $ hg ci -m 'trash subrepo tracking'
902 902
903 903 $ hg log -r "subrepo('re:sub\d+')" --style compact
904 904 0 7f491f53a367 1970-01-01 00:00 +0000 test
905 905 main import
906 906
907 907 1 ffe6649062fe 1970-01-01 00:00 +0000 test
908 908 deep nested modif should trigger a commit
909 909
910 910 2 9bb10eebee29 1970-01-01 00:00 +0000 test
911 911 add test.txt
912 912
913 913 3 7c64f035294f 1970-01-01 00:00 +0000 test
914 914 add large files
915 915
916 916 4 f734a59e2e35 1970-01-01 00:00 +0000 test
917 917 forget testing
918 918
919 919 11 9685a22af5db 1970-01-01 00:00 +0000 test
920 920 add sub3
921 921
922 922 12 2e0485b475b9 1970-01-01 00:00 +0000 test
923 923 remove sub3
924 924
925 925 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
926 926 trash subrepo tracking
927 927
928 928
929 929 Restore the trashed subrepo tracking
930 930
931 931 $ hg rollback -q
932 932 $ hg update -Cq .
933 933
934 934 Interaction with extdiff, largefiles and subrepos
935 935
936 936 $ hg --config extensions.extdiff= pdiff -S
937 937
938 938 $ hg --config extensions.extdiff= pdiff -r '.^' -S
939 939 \r (no-eol) (esc)
940 940 archiving [ ] 0/2\r (no-eol) (esc)
941 941 archiving [====================> ] 1/2\r (no-eol) (esc)
942 942 archiving [==========================================>] 2/2\r (no-eol) (esc)
943 943 \r (no-eol) (esc)
944 944 \r (no-eol) (esc)
945 945 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
946 946 \r (no-eol) (esc)
947 947 \r (no-eol) (esc)
948 948 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc)
949 949 \r (no-eol) (esc)
950 950 \r (no-eol) (esc)
951 951 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
952 952 \r (no-eol) (esc)
953 953 \r (no-eol) (esc)
954 954 archiving [ ] 0/2\r (no-eol) (esc)
955 955 archiving [====================> ] 1/2\r (no-eol) (esc)
956 956 archiving [==========================================>] 2/2\r (no-eol) (esc)
957 957 \r (no-eol) (esc)
958 958 \r (no-eol) (esc)
959 959 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
960 960 \r (no-eol) (esc)
961 961 \r (no-eol) (esc)
962 962 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc)
963 963 \r (no-eol) (esc)
964 964 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
965 965 --- cloned.*/.hgsub * (glob)
966 966 +++ cloned/.hgsub * (glob)
967 967 @@ -1,2 +1* @@ (glob)
968 968 sub1 = ../sub1
969 969 -sub3 = sub3
970 970 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
971 971 --- cloned.*/.hgsubstate * (glob)
972 972 +++ cloned/.hgsubstate * (glob)
973 973 @@ -1,2 +1* @@ (glob)
974 974 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
975 975 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
976 976 [1]
977 977
978 978 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
979 979 \r (no-eol) (esc)
980 980 archiving [ ] 0/3\r (no-eol) (esc)
981 981 archiving [=============> ] 1/3\r (no-eol) (esc)
982 982 archiving [===========================> ] 2/3\r (no-eol) (esc)
983 983 archiving [==========================================>] 3/3\r (no-eol) (esc)
984 984 \r (no-eol) (esc)
985 985 \r (no-eol) (esc)
986 986 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
987 987 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
988 988 \r (no-eol) (esc)
989 989 \r (no-eol) (esc)
990 990 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
991 991 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
992 992 \r (no-eol) (esc)
993 993 \r (no-eol) (esc)
994 994 archiving [ ] 0/8\r (no-eol) (esc)
995 995 archiving [====> ] 1/8\r (no-eol) (esc)
996 996 archiving [=========> ] 2/8\r (no-eol) (esc)
997 997 archiving [===============> ] 3/8\r (no-eol) (esc)
998 998 archiving [====================> ] 4/8\r (no-eol) (esc)
999 999 archiving [=========================> ] 5/8\r (no-eol) (esc)
1000 1000 archiving [===============================> ] 6/8\r (no-eol) (esc)
1001 1001 archiving [====================================> ] 7/8\r (no-eol) (esc)
1002 1002 archiving [==========================================>] 8/8\r (no-eol) (esc)
1003 1003 \r (no-eol) (esc)
1004 1004 \r (no-eol) (esc)
1005 1005 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1006 1006 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1007 1007 \r (no-eol) (esc)
1008 1008 \r (no-eol) (esc)
1009 1009 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc)
1010 1010 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc)
1011 1011 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc)
1012 1012 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc)
1013 1013 \r (no-eol) (esc)
1014 1014 \r (no-eol) (esc)
1015 1015 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1016 1016 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1017 1017 \r (no-eol) (esc)
1018 1018 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1019 1019 --- cloned.*/.hglf/b.dat * (glob)
1020 1020 +++ cloned.*/.hglf/b.dat * (glob)
1021 1021 @@ -*,0 +1* @@ (glob)
1022 1022 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1023 1023 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1024 1024 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1025 1025 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1026 1026 @@ -*,0 +1* @@ (glob)
1027 1027 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1028 1028 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1029 1029 --- cloned.*/.hglf/large.bin * (glob)
1030 1030 +++ cloned.*/.hglf/large.bin * (glob)
1031 1031 @@ -*,0 +1* @@ (glob)
1032 1032 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1033 1033 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1034 1034 --- cloned.*/.hgsub * (glob)
1035 1035 +++ cloned.*/.hgsub * (glob)
1036 1036 @@ -1* +1,2 @@ (glob)
1037 1037 sub1 = ../sub1
1038 1038 +sub3 = sub3
1039 1039 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1040 1040 --- cloned.*/.hgsubstate * (glob)
1041 1041 +++ cloned.*/.hgsubstate * (glob)
1042 1042 @@ -1* +1,2 @@ (glob)
1043 1043 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1044 1044 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1045 1045 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1046 1046 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1047 1047 --- cloned.*/foo/bar/def * (glob)
1048 1048 +++ cloned.*/foo/bar/def * (glob)
1049 1049 @@ -*,0 +1* @@ (glob)
1050 1050 +changed
1051 1051 diff -Nru cloned.*/main cloned.*/main (glob)
1052 1052 --- cloned.*/main * (glob)
1053 1053 +++ cloned.*/main * (glob)
1054 1054 @@ -1* +1* @@ (glob)
1055 1055 -main
1056 1056 +foo
1057 1057 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1058 1058 --- cloned.*/sub1/.hgsubstate * (glob)
1059 1059 +++ cloned.*/sub1/.hgsubstate * (glob)
1060 1060 @@ -1* +1* @@ (glob)
1061 1061 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1062 1062 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1063 1063 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1064 1064 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1065 1065 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1066 1066 @@ -*,0 +1* @@ (glob)
1067 1067 +subfolder
1068 1068 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1069 1069 --- cloned.*/sub1/sub2/sub2 * (glob)
1070 1070 +++ cloned.*/sub1/sub2/sub2 * (glob)
1071 1071 @@ -1* +1* @@ (glob)
1072 1072 -sub2
1073 1073 +modified
1074 1074 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1075 1075 --- cloned.*/sub3/a.txt * (glob)
1076 1076 +++ cloned.*/sub3/a.txt * (glob)
1077 1077 @@ -*,0 +1* @@ (glob)
1078 1078 +xyz
1079 1079 [1]
1080 1080
1081 1081 $ echo mod > sub1/sub2/sub2
1082 1082 $ hg --config extensions.extdiff= pdiff -S
1083 1083 \r (no-eol) (esc)
1084 1084 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1085 1085 \r (no-eol) (esc)
1086 1086 \r (no-eol) (esc)
1087 1087 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc)
1088 1088 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc)
1089 1089 \r (no-eol) (esc)
1090 1090 --- */cloned.*/sub1/sub2/sub2 * (glob)
1091 1091 +++ */cloned/sub1/sub2/sub2 * (glob)
1092 1092 @@ -1* +1* @@ (glob)
1093 1093 -modified
1094 1094 +mod
1095 1095 [1]
1096 1096
1097 1097 $ cd ..
@@ -1,331 +1,331
1 1 Test basic functionality of url#rev syntax
2 2
3 3 $ hg init repo
4 4 $ cd repo
5 5 $ echo a > a
6 6 $ hg ci -qAm 'add a'
7 7 $ hg branch foo
8 8 marked working directory as branch foo
9 9 (branches are permanent and global, did you want a bookmark?)
10 10 $ echo >> a
11 11 $ hg ci -m 'change a'
12 12 $ cd ..
13 13
14 14 $ hg clone 'repo#foo' clone
15 15 adding changesets
16 16 adding manifests
17 17 adding file changes
18 18 added 2 changesets with 2 changes to 1 files
19 19 updating to branch foo
20 20 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 21
22 22 $ hg --cwd clone heads
23 23 changeset: 1:cd2a86ecc814
24 24 branch: foo
25 25 tag: tip
26 26 user: test
27 27 date: Thu Jan 01 00:00:00 1970 +0000
28 28 summary: change a
29 29
30 30 changeset: 0:1f0dee641bb7
31 31 user: test
32 32 date: Thu Jan 01 00:00:00 1970 +0000
33 33 summary: add a
34 34
35 35 $ hg --cwd clone parents
36 36 changeset: 1:cd2a86ecc814
37 37 branch: foo
38 38 tag: tip
39 39 user: test
40 40 date: Thu Jan 01 00:00:00 1970 +0000
41 41 summary: change a
42 42
43 43 $ cat clone/.hg/hgrc
44 44 # example repository config (see 'hg help config' for more info)
45 45 [paths]
46 46 default = $TESTTMP/repo#foo (glob)
47 47
48 48 # path aliases to other clones of this repo in URLs or filesystem paths
49 49 # (see 'hg help config.paths' for more info)
50 50 #
51 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
52 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
53 # my-clone = /home/jdoe/jdoes-clone
51 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
52 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
53 # my-clone = /home/jdoe/jdoes-clone
54 54
55 55 [ui]
56 56 # name and email (local to this repository, optional), e.g.
57 57 # username = Jane Doe <jdoe@example.com>
58 58
59 59 Changing original repo:
60 60
61 61 $ cd repo
62 62
63 63 $ echo >> a
64 64 $ hg ci -m 'new head of branch foo'
65 65
66 66 $ hg up -qC default
67 67 $ echo bar > bar
68 68 $ hg ci -qAm 'add bar'
69 69
70 70 $ hg log
71 71 changeset: 3:4cd725637392
72 72 tag: tip
73 73 parent: 0:1f0dee641bb7
74 74 user: test
75 75 date: Thu Jan 01 00:00:00 1970 +0000
76 76 summary: add bar
77 77
78 78 changeset: 2:faba9097cad4
79 79 branch: foo
80 80 user: test
81 81 date: Thu Jan 01 00:00:00 1970 +0000
82 82 summary: new head of branch foo
83 83
84 84 changeset: 1:cd2a86ecc814
85 85 branch: foo
86 86 user: test
87 87 date: Thu Jan 01 00:00:00 1970 +0000
88 88 summary: change a
89 89
90 90 changeset: 0:1f0dee641bb7
91 91 user: test
92 92 date: Thu Jan 01 00:00:00 1970 +0000
93 93 summary: add a
94 94
95 95 $ hg -q outgoing '../clone'
96 96 2:faba9097cad4
97 97 3:4cd725637392
98 98 $ hg summary --remote --config paths.default='../clone'
99 99 parent: 3:4cd725637392 tip
100 100 add bar
101 101 branch: default
102 102 commit: (clean)
103 103 update: (current)
104 104 phases: 4 draft
105 105 remote: 2 outgoing
106 106 $ hg -q outgoing '../clone#foo'
107 107 2:faba9097cad4
108 108 $ hg summary --remote --config paths.default='../clone#foo'
109 109 parent: 3:4cd725637392 tip
110 110 add bar
111 111 branch: default
112 112 commit: (clean)
113 113 update: (current)
114 114 phases: 4 draft
115 115 remote: 1 outgoing
116 116
117 117 $ hg -q --cwd ../clone incoming '../repo#foo'
118 118 2:faba9097cad4
119 119 $ hg --cwd ../clone summary --remote --config paths.default='../repo#foo'
120 120 parent: 1:cd2a86ecc814 tip
121 121 change a
122 122 branch: foo
123 123 commit: (clean)
124 124 update: (current)
125 125 remote: 1 or more incoming
126 126
127 127 $ hg -q push '../clone#foo'
128 128
129 129 $ hg --cwd ../clone heads
130 130 changeset: 2:faba9097cad4
131 131 branch: foo
132 132 tag: tip
133 133 user: test
134 134 date: Thu Jan 01 00:00:00 1970 +0000
135 135 summary: new head of branch foo
136 136
137 137 changeset: 0:1f0dee641bb7
138 138 user: test
139 139 date: Thu Jan 01 00:00:00 1970 +0000
140 140 summary: add a
141 141
142 142 $ hg -q --cwd ../clone incoming '../repo#foo'
143 143 [1]
144 144 $ hg --cwd ../clone summary --remote --config paths.default='../repo#foo'
145 145 parent: 1:cd2a86ecc814
146 146 change a
147 147 branch: foo
148 148 commit: (clean)
149 149 update: 1 new changesets (update)
150 150 remote: (synced)
151 151
152 152 $ cd ..
153 153
154 154 $ cd clone
155 155 $ hg rollback
156 156 repository tip rolled back to revision 1 (undo push)
157 157
158 158 $ hg -q incoming
159 159 2:faba9097cad4
160 160
161 161 $ hg -q pull
162 162
163 163 $ hg heads
164 164 changeset: 2:faba9097cad4
165 165 branch: foo
166 166 tag: tip
167 167 user: test
168 168 date: Thu Jan 01 00:00:00 1970 +0000
169 169 summary: new head of branch foo
170 170
171 171 changeset: 0:1f0dee641bb7
172 172 user: test
173 173 date: Thu Jan 01 00:00:00 1970 +0000
174 174 summary: add a
175 175
176 176 Pull should not have updated:
177 177
178 178 $ hg parents -q
179 179 1:cd2a86ecc814
180 180
181 181 Going back to the default branch:
182 182
183 183 $ hg up -C 0
184 184 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
185 185
186 186 $ hg parents
187 187 changeset: 0:1f0dee641bb7
188 188 user: test
189 189 date: Thu Jan 01 00:00:00 1970 +0000
190 190 summary: add a
191 191
192 192 No new revs, no update:
193 193
194 194 $ hg pull -qu
195 195
196 196 $ hg parents -q
197 197 0:1f0dee641bb7
198 198
199 199 $ hg rollback
200 200 repository tip rolled back to revision 1 (undo pull)
201 201
202 202 $ hg parents -q
203 203 0:1f0dee641bb7
204 204
205 205 Pull -u takes us back to branch foo:
206 206
207 207 $ hg pull -qu
208 208
209 209 $ hg parents
210 210 changeset: 2:faba9097cad4
211 211 branch: foo
212 212 tag: tip
213 213 user: test
214 214 date: Thu Jan 01 00:00:00 1970 +0000
215 215 summary: new head of branch foo
216 216
217 217 $ hg rollback
218 218 repository tip rolled back to revision 1 (undo pull)
219 219 working directory now based on revision 0
220 220
221 221 $ hg up -C 0
222 222 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 223
224 224 $ hg parents -q
225 225 0:1f0dee641bb7
226 226
227 227 $ hg heads -q
228 228 1:cd2a86ecc814
229 229 0:1f0dee641bb7
230 230
231 231 $ hg pull -qur default default
232 232
233 233 $ hg parents
234 234 changeset: 3:4cd725637392
235 235 tag: tip
236 236 parent: 0:1f0dee641bb7
237 237 user: test
238 238 date: Thu Jan 01 00:00:00 1970 +0000
239 239 summary: add bar
240 240
241 241 $ hg heads
242 242 changeset: 3:4cd725637392
243 243 tag: tip
244 244 parent: 0:1f0dee641bb7
245 245 user: test
246 246 date: Thu Jan 01 00:00:00 1970 +0000
247 247 summary: add bar
248 248
249 249 changeset: 2:faba9097cad4
250 250 branch: foo
251 251 user: test
252 252 date: Thu Jan 01 00:00:00 1970 +0000
253 253 summary: new head of branch foo
254 254
255 255 Test handling of invalid urls
256 256
257 257 $ hg id http://foo/?bar
258 258 abort: unsupported URL component: "bar"
259 259 [255]
260 260
261 261 $ cd ..
262 262
263 263 Test handling common incoming revisions between "default" and
264 264 "default-push"
265 265
266 266 $ hg -R clone rollback
267 267 repository tip rolled back to revision 1 (undo pull)
268 268 working directory now based on revision 0
269 269
270 270 $ cd repo
271 271
272 272 $ hg update -q -C default
273 273 $ echo modified >> bar
274 274 $ hg commit -m "new head to push current default head"
275 275 $ hg -q push -r ".^1" '../clone'
276 276
277 277 $ hg -q outgoing '../clone'
278 278 2:faba9097cad4
279 279 4:d515801a8f3d
280 280
281 281 $ hg summary --remote --config paths.default='../clone#default' --config paths.default-push='../clone#foo'
282 282 parent: 4:d515801a8f3d tip
283 283 new head to push current default head
284 284 branch: default
285 285 commit: (clean)
286 286 update: (current)
287 287 phases: 1 draft
288 288 remote: 1 outgoing
289 289
290 290 $ hg summary --remote --config paths.default='../clone#foo' --config paths.default-push='../clone'
291 291 parent: 4:d515801a8f3d tip
292 292 new head to push current default head
293 293 branch: default
294 294 commit: (clean)
295 295 update: (current)
296 296 phases: 1 draft
297 297 remote: 2 outgoing
298 298
299 299 $ hg summary --remote --config paths.default='../clone' --config paths.default-push='../clone#foo'
300 300 parent: 4:d515801a8f3d tip
301 301 new head to push current default head
302 302 branch: default
303 303 commit: (clean)
304 304 update: (current)
305 305 phases: 1 draft
306 306 remote: 1 outgoing
307 307
308 308 $ hg clone -q -r 0 . ../another
309 309 $ hg -q outgoing '../another#default'
310 310 3:4cd725637392
311 311 4:d515801a8f3d
312 312
313 313 $ hg summary --remote --config paths.default='../another#default' --config paths.default-push='../clone#default'
314 314 parent: 4:d515801a8f3d tip
315 315 new head to push current default head
316 316 branch: default
317 317 commit: (clean)
318 318 update: (current)
319 319 phases: 1 draft
320 320 remote: 1 outgoing
321 321
322 322 $ cd ..
323 323
324 324 Test url#rev syntax of local destination path, which should be taken as
325 325 a 'url#rev' path
326 326
327 327 $ hg clone repo '#foo'
328 328 updating to branch default
329 329 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 330 $ hg root -R '#foo'
331 331 $TESTTMP/#foo (glob)
General Comments 0
You need to be logged in to leave comments. Login now