##// END OF EJS Templates
ui: add support for fully printing chained exception stacks in ui.traceback()...
Matt Harbison -
r18965:0062508b default
parent child Browse files
Show More
@@ -1,761 +1,773 b''
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 i18n import _
9 9 import errno, getpass, os, socket, sys, tempfile, traceback
10 10 import config, scmutil, util, error, formatter
11 11
12 12 class ui(object):
13 13 def __init__(self, src=None):
14 14 self._buffers = []
15 15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 16 self._reportuntrusted = True
17 17 self._ocfg = config.config() # overlay
18 18 self._tcfg = config.config() # trusted
19 19 self._ucfg = config.config() # untrusted
20 20 self._trustusers = set()
21 21 self._trustgroups = set()
22 22 self.callhooks = True
23 23
24 24 if src:
25 25 self.fout = src.fout
26 26 self.ferr = src.ferr
27 27 self.fin = src.fin
28 28
29 29 self._tcfg = src._tcfg.copy()
30 30 self._ucfg = src._ucfg.copy()
31 31 self._ocfg = src._ocfg.copy()
32 32 self._trustusers = src._trustusers.copy()
33 33 self._trustgroups = src._trustgroups.copy()
34 34 self.environ = src.environ
35 35 self.callhooks = src.callhooks
36 36 self.fixconfig()
37 37 else:
38 38 self.fout = sys.stdout
39 39 self.ferr = sys.stderr
40 40 self.fin = sys.stdin
41 41
42 42 # shared read-only environment
43 43 self.environ = os.environ
44 44 # we always trust global config files
45 45 for f in scmutil.rcpath():
46 46 self.readconfig(f, trust=True)
47 47
48 48 def copy(self):
49 49 return self.__class__(self)
50 50
51 51 def formatter(self, topic, opts):
52 52 return formatter.formatter(self, topic, opts)
53 53
54 54 def _trusted(self, fp, f):
55 55 st = util.fstat(fp)
56 56 if util.isowner(st):
57 57 return True
58 58
59 59 tusers, tgroups = self._trustusers, self._trustgroups
60 60 if '*' in tusers or '*' in tgroups:
61 61 return True
62 62
63 63 user = util.username(st.st_uid)
64 64 group = util.groupname(st.st_gid)
65 65 if user in tusers or group in tgroups or user == util.username():
66 66 return True
67 67
68 68 if self._reportuntrusted:
69 69 self.warn(_('not trusting file %s from untrusted '
70 70 'user %s, group %s\n') % (f, user, group))
71 71 return False
72 72
73 73 def readconfig(self, filename, root=None, trust=False,
74 74 sections=None, remap=None):
75 75 try:
76 76 fp = open(filename)
77 77 except IOError:
78 78 if not sections: # ignore unless we were looking for something
79 79 return
80 80 raise
81 81
82 82 cfg = config.config()
83 83 trusted = sections or trust or self._trusted(fp, filename)
84 84
85 85 try:
86 86 cfg.read(filename, fp, sections=sections, remap=remap)
87 87 fp.close()
88 88 except error.ConfigError, inst:
89 89 if trusted:
90 90 raise
91 91 self.warn(_("ignored: %s\n") % str(inst))
92 92
93 93 if self.plain():
94 94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
95 95 'logtemplate', 'style',
96 96 'traceback', 'verbose'):
97 97 if k in cfg['ui']:
98 98 del cfg['ui'][k]
99 99 for k, v in cfg.items('defaults'):
100 100 del cfg['defaults'][k]
101 101 # Don't remove aliases from the configuration if in the exceptionlist
102 102 if self.plain('alias'):
103 103 for k, v in cfg.items('alias'):
104 104 del cfg['alias'][k]
105 105
106 106 if trusted:
107 107 self._tcfg.update(cfg)
108 108 self._tcfg.update(self._ocfg)
109 109 self._ucfg.update(cfg)
110 110 self._ucfg.update(self._ocfg)
111 111
112 112 if root is None:
113 113 root = os.path.expanduser('~')
114 114 self.fixconfig(root=root)
115 115
116 116 def fixconfig(self, root=None, section=None):
117 117 if section in (None, 'paths'):
118 118 # expand vars and ~
119 119 # translate paths relative to root (or home) into absolute paths
120 120 root = root or os.getcwd()
121 121 for c in self._tcfg, self._ucfg, self._ocfg:
122 122 for n, p in c.items('paths'):
123 123 if not p:
124 124 continue
125 125 if '%%' in p:
126 126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
127 127 % (n, p, self.configsource('paths', n)))
128 128 p = p.replace('%%', '%')
129 129 p = util.expandpath(p)
130 130 if not util.hasscheme(p) and not os.path.isabs(p):
131 131 p = os.path.normpath(os.path.join(root, p))
132 132 c.set("paths", n, p)
133 133
134 134 if section in (None, 'ui'):
135 135 # update ui options
136 136 self.debugflag = self.configbool('ui', 'debug')
137 137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
138 138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
139 139 if self.verbose and self.quiet:
140 140 self.quiet = self.verbose = False
141 141 self._reportuntrusted = self.debugflag or self.configbool("ui",
142 142 "report_untrusted", True)
143 143 self.tracebackflag = self.configbool('ui', 'traceback', False)
144 144
145 145 if section in (None, 'trusted'):
146 146 # update trust information
147 147 self._trustusers.update(self.configlist('trusted', 'users'))
148 148 self._trustgroups.update(self.configlist('trusted', 'groups'))
149 149
150 150 def backupconfig(self, section, item):
151 151 return (self._ocfg.backup(section, item),
152 152 self._tcfg.backup(section, item),
153 153 self._ucfg.backup(section, item),)
154 154 def restoreconfig(self, data):
155 155 self._ocfg.restore(data[0])
156 156 self._tcfg.restore(data[1])
157 157 self._ucfg.restore(data[2])
158 158
159 159 def setconfig(self, section, name, value, overlay=True):
160 160 if overlay:
161 161 self._ocfg.set(section, name, value)
162 162 self._tcfg.set(section, name, value)
163 163 self._ucfg.set(section, name, value)
164 164 self.fixconfig(section=section)
165 165
166 166 def _data(self, untrusted):
167 167 return untrusted and self._ucfg or self._tcfg
168 168
169 169 def configsource(self, section, name, untrusted=False):
170 170 return self._data(untrusted).source(section, name) or 'none'
171 171
172 172 def config(self, section, name, default=None, untrusted=False):
173 173 if isinstance(name, list):
174 174 alternates = name
175 175 else:
176 176 alternates = [name]
177 177
178 178 for n in alternates:
179 179 value = self._data(untrusted).get(section, name, None)
180 180 if value is not None:
181 181 name = n
182 182 break
183 183 else:
184 184 value = default
185 185
186 186 if self.debugflag and not untrusted and self._reportuntrusted:
187 187 uvalue = self._ucfg.get(section, name)
188 188 if uvalue is not None and uvalue != value:
189 189 self.debug("ignoring untrusted configuration option "
190 190 "%s.%s = %s\n" % (section, name, uvalue))
191 191 return value
192 192
193 193 def configpath(self, section, name, default=None, untrusted=False):
194 194 'get a path config item, expanded relative to repo root or config file'
195 195 v = self.config(section, name, default, untrusted)
196 196 if v is None:
197 197 return None
198 198 if not os.path.isabs(v) or "://" not in v:
199 199 src = self.configsource(section, name, untrusted)
200 200 if ':' in src:
201 201 base = os.path.dirname(src.rsplit(':')[0])
202 202 v = os.path.join(base, os.path.expanduser(v))
203 203 return v
204 204
205 205 def configbool(self, section, name, default=False, untrusted=False):
206 206 """parse a configuration element as a boolean
207 207
208 208 >>> u = ui(); s = 'foo'
209 209 >>> u.setconfig(s, 'true', 'yes')
210 210 >>> u.configbool(s, 'true')
211 211 True
212 212 >>> u.setconfig(s, 'false', 'no')
213 213 >>> u.configbool(s, 'false')
214 214 False
215 215 >>> u.configbool(s, 'unknown')
216 216 False
217 217 >>> u.configbool(s, 'unknown', True)
218 218 True
219 219 >>> u.setconfig(s, 'invalid', 'somevalue')
220 220 >>> u.configbool(s, 'invalid')
221 221 Traceback (most recent call last):
222 222 ...
223 223 ConfigError: foo.invalid is not a boolean ('somevalue')
224 224 """
225 225
226 226 v = self.config(section, name, None, untrusted)
227 227 if v is None:
228 228 return default
229 229 if isinstance(v, bool):
230 230 return v
231 231 b = util.parsebool(v)
232 232 if b is None:
233 233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
234 234 % (section, name, v))
235 235 return b
236 236
237 237 def configint(self, section, name, default=None, untrusted=False):
238 238 """parse a configuration element as an integer
239 239
240 240 >>> u = ui(); s = 'foo'
241 241 >>> u.setconfig(s, 'int1', '42')
242 242 >>> u.configint(s, 'int1')
243 243 42
244 244 >>> u.setconfig(s, 'int2', '-42')
245 245 >>> u.configint(s, 'int2')
246 246 -42
247 247 >>> u.configint(s, 'unknown', 7)
248 248 7
249 249 >>> u.setconfig(s, 'invalid', 'somevalue')
250 250 >>> u.configint(s, 'invalid')
251 251 Traceback (most recent call last):
252 252 ...
253 253 ConfigError: foo.invalid is not an integer ('somevalue')
254 254 """
255 255
256 256 v = self.config(section, name, None, untrusted)
257 257 if v is None:
258 258 return default
259 259 try:
260 260 return int(v)
261 261 except ValueError:
262 262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
263 263 % (section, name, v))
264 264
265 265 def configlist(self, section, name, default=None, untrusted=False):
266 266 """parse a configuration element as a list of comma/space separated
267 267 strings
268 268
269 269 >>> u = ui(); s = 'foo'
270 270 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
271 271 >>> u.configlist(s, 'list1')
272 272 ['this', 'is', 'a small', 'test']
273 273 """
274 274
275 275 def _parse_plain(parts, s, offset):
276 276 whitespace = False
277 277 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
278 278 whitespace = True
279 279 offset += 1
280 280 if offset >= len(s):
281 281 return None, parts, offset
282 282 if whitespace:
283 283 parts.append('')
284 284 if s[offset] == '"' and not parts[-1]:
285 285 return _parse_quote, parts, offset + 1
286 286 elif s[offset] == '"' and parts[-1][-1] == '\\':
287 287 parts[-1] = parts[-1][:-1] + s[offset]
288 288 return _parse_plain, parts, offset + 1
289 289 parts[-1] += s[offset]
290 290 return _parse_plain, parts, offset + 1
291 291
292 292 def _parse_quote(parts, s, offset):
293 293 if offset < len(s) and s[offset] == '"': # ""
294 294 parts.append('')
295 295 offset += 1
296 296 while offset < len(s) and (s[offset].isspace() or
297 297 s[offset] == ','):
298 298 offset += 1
299 299 return _parse_plain, parts, offset
300 300
301 301 while offset < len(s) and s[offset] != '"':
302 302 if (s[offset] == '\\' and offset + 1 < len(s)
303 303 and s[offset + 1] == '"'):
304 304 offset += 1
305 305 parts[-1] += '"'
306 306 else:
307 307 parts[-1] += s[offset]
308 308 offset += 1
309 309
310 310 if offset >= len(s):
311 311 real_parts = _configlist(parts[-1])
312 312 if not real_parts:
313 313 parts[-1] = '"'
314 314 else:
315 315 real_parts[0] = '"' + real_parts[0]
316 316 parts = parts[:-1]
317 317 parts.extend(real_parts)
318 318 return None, parts, offset
319 319
320 320 offset += 1
321 321 while offset < len(s) and s[offset] in [' ', ',']:
322 322 offset += 1
323 323
324 324 if offset < len(s):
325 325 if offset + 1 == len(s) and s[offset] == '"':
326 326 parts[-1] += '"'
327 327 offset += 1
328 328 else:
329 329 parts.append('')
330 330 else:
331 331 return None, parts, offset
332 332
333 333 return _parse_plain, parts, offset
334 334
335 335 def _configlist(s):
336 336 s = s.rstrip(' ,')
337 337 if not s:
338 338 return []
339 339 parser, parts, offset = _parse_plain, [''], 0
340 340 while parser:
341 341 parser, parts, offset = parser(parts, s, offset)
342 342 return parts
343 343
344 344 result = self.config(section, name, untrusted=untrusted)
345 345 if result is None:
346 346 result = default or []
347 347 if isinstance(result, basestring):
348 348 result = _configlist(result.lstrip(' ,\n'))
349 349 if result is None:
350 350 result = default or []
351 351 return result
352 352
353 353 def has_section(self, section, untrusted=False):
354 354 '''tell whether section exists in config.'''
355 355 return section in self._data(untrusted)
356 356
357 357 def configitems(self, section, untrusted=False):
358 358 items = self._data(untrusted).items(section)
359 359 if self.debugflag and not untrusted and self._reportuntrusted:
360 360 for k, v in self._ucfg.items(section):
361 361 if self._tcfg.get(section, k) != v:
362 362 self.debug("ignoring untrusted configuration option "
363 363 "%s.%s = %s\n" % (section, k, v))
364 364 return items
365 365
366 366 def walkconfig(self, untrusted=False):
367 367 cfg = self._data(untrusted)
368 368 for section in cfg.sections():
369 369 for name, value in self.configitems(section, untrusted):
370 370 yield section, name, value
371 371
372 372 def plain(self, feature=None):
373 373 '''is plain mode active?
374 374
375 375 Plain mode means that all configuration variables which affect
376 376 the behavior and output of Mercurial should be
377 377 ignored. Additionally, the output should be stable,
378 378 reproducible and suitable for use in scripts or applications.
379 379
380 380 The only way to trigger plain mode is by setting either the
381 381 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
382 382
383 383 The return value can either be
384 384 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
385 385 - True otherwise
386 386 '''
387 387 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
388 388 return False
389 389 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
390 390 if feature and exceptions:
391 391 return feature not in exceptions
392 392 return True
393 393
394 394 def username(self):
395 395 """Return default username to be used in commits.
396 396
397 397 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
398 398 and stop searching if one of these is set.
399 399 If not found and ui.askusername is True, ask the user, else use
400 400 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
401 401 """
402 402 user = os.environ.get("HGUSER")
403 403 if user is None:
404 404 user = self.config("ui", "username")
405 405 if user is not None:
406 406 user = os.path.expandvars(user)
407 407 if user is None:
408 408 user = os.environ.get("EMAIL")
409 409 if user is None and self.configbool("ui", "askusername"):
410 410 user = self.prompt(_("enter a commit username:"), default=None)
411 411 if user is None and not self.interactive():
412 412 try:
413 413 user = '%s@%s' % (util.getuser(), socket.getfqdn())
414 414 self.warn(_("no username found, using '%s' instead\n") % user)
415 415 except KeyError:
416 416 pass
417 417 if not user:
418 418 raise util.Abort(_('no username supplied (see "hg help config")'))
419 419 if "\n" in user:
420 420 raise util.Abort(_("username %s contains a newline\n") % repr(user))
421 421 return user
422 422
423 423 def shortuser(self, user):
424 424 """Return a short representation of a user name or email address."""
425 425 if not self.verbose:
426 426 user = util.shortuser(user)
427 427 return user
428 428
429 429 def expandpath(self, loc, default=None):
430 430 """Return repository location relative to cwd or from [paths]"""
431 431 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
432 432 return loc
433 433
434 434 path = self.config('paths', loc)
435 435 if not path and default is not None:
436 436 path = self.config('paths', default)
437 437 return path or loc
438 438
439 439 def pushbuffer(self):
440 440 self._buffers.append([])
441 441
442 442 def popbuffer(self, labeled=False):
443 443 '''pop the last buffer and return the buffered output
444 444
445 445 If labeled is True, any labels associated with buffered
446 446 output will be handled. By default, this has no effect
447 447 on the output returned, but extensions and GUI tools may
448 448 handle this argument and returned styled output. If output
449 449 is being buffered so it can be captured and parsed or
450 450 processed, labeled should not be set to True.
451 451 '''
452 452 return "".join(self._buffers.pop())
453 453
454 454 def write(self, *args, **opts):
455 455 '''write args to output
456 456
457 457 By default, this method simply writes to the buffer or stdout,
458 458 but extensions or GUI tools may override this method,
459 459 write_err(), popbuffer(), and label() to style output from
460 460 various parts of hg.
461 461
462 462 An optional keyword argument, "label", can be passed in.
463 463 This should be a string containing label names separated by
464 464 space. Label names take the form of "topic.type". For example,
465 465 ui.debug() issues a label of "ui.debug".
466 466
467 467 When labeling output for a specific command, a label of
468 468 "cmdname.type" is recommended. For example, status issues
469 469 a label of "status.modified" for modified files.
470 470 '''
471 471 if self._buffers:
472 472 self._buffers[-1].extend([str(a) for a in args])
473 473 else:
474 474 for a in args:
475 475 self.fout.write(str(a))
476 476
477 477 def write_err(self, *args, **opts):
478 478 try:
479 479 if not getattr(self.fout, 'closed', False):
480 480 self.fout.flush()
481 481 for a in args:
482 482 self.ferr.write(str(a))
483 483 # stderr may be buffered under win32 when redirected to files,
484 484 # including stdout.
485 485 if not getattr(self.ferr, 'closed', False):
486 486 self.ferr.flush()
487 487 except IOError, inst:
488 488 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
489 489 raise
490 490
491 491 def flush(self):
492 492 try: self.fout.flush()
493 493 except (IOError, ValueError): pass
494 494 try: self.ferr.flush()
495 495 except (IOError, ValueError): pass
496 496
497 497 def _isatty(self, fh):
498 498 if self.configbool('ui', 'nontty', False):
499 499 return False
500 500 return util.isatty(fh)
501 501
502 502 def interactive(self):
503 503 '''is interactive input allowed?
504 504
505 505 An interactive session is a session where input can be reasonably read
506 506 from `sys.stdin'. If this function returns false, any attempt to read
507 507 from stdin should fail with an error, unless a sensible default has been
508 508 specified.
509 509
510 510 Interactiveness is triggered by the value of the `ui.interactive'
511 511 configuration variable or - if it is unset - when `sys.stdin' points
512 512 to a terminal device.
513 513
514 514 This function refers to input only; for output, see `ui.formatted()'.
515 515 '''
516 516 i = self.configbool("ui", "interactive", None)
517 517 if i is None:
518 518 # some environments replace stdin without implementing isatty
519 519 # usually those are non-interactive
520 520 return self._isatty(self.fin)
521 521
522 522 return i
523 523
524 524 def termwidth(self):
525 525 '''how wide is the terminal in columns?
526 526 '''
527 527 if 'COLUMNS' in os.environ:
528 528 try:
529 529 return int(os.environ['COLUMNS'])
530 530 except ValueError:
531 531 pass
532 532 return util.termwidth()
533 533
534 534 def formatted(self):
535 535 '''should formatted output be used?
536 536
537 537 It is often desirable to format the output to suite the output medium.
538 538 Examples of this are truncating long lines or colorizing messages.
539 539 However, this is not often not desirable when piping output into other
540 540 utilities, e.g. `grep'.
541 541
542 542 Formatted output is triggered by the value of the `ui.formatted'
543 543 configuration variable or - if it is unset - when `sys.stdout' points
544 544 to a terminal device. Please note that `ui.formatted' should be
545 545 considered an implementation detail; it is not intended for use outside
546 546 Mercurial or its extensions.
547 547
548 548 This function refers to output only; for input, see `ui.interactive()'.
549 549 This function always returns false when in plain mode, see `ui.plain()'.
550 550 '''
551 551 if self.plain():
552 552 return False
553 553
554 554 i = self.configbool("ui", "formatted", None)
555 555 if i is None:
556 556 # some environments replace stdout without implementing isatty
557 557 # usually those are non-interactive
558 558 return self._isatty(self.fout)
559 559
560 560 return i
561 561
562 562 def _readline(self, prompt=''):
563 563 if self._isatty(self.fin):
564 564 try:
565 565 # magically add command line editing support, where
566 566 # available
567 567 import readline
568 568 # force demandimport to really load the module
569 569 readline.read_history_file
570 570 # windows sometimes raises something other than ImportError
571 571 except Exception:
572 572 pass
573 573
574 574 # call write() so output goes through subclassed implementation
575 575 # e.g. color extension on Windows
576 576 self.write(prompt)
577 577
578 578 # instead of trying to emulate raw_input, swap (self.fin,
579 579 # self.fout) with (sys.stdin, sys.stdout)
580 580 oldin = sys.stdin
581 581 oldout = sys.stdout
582 582 sys.stdin = self.fin
583 583 sys.stdout = self.fout
584 584 line = raw_input(' ')
585 585 sys.stdin = oldin
586 586 sys.stdout = oldout
587 587
588 588 # When stdin is in binary mode on Windows, it can cause
589 589 # raw_input() to emit an extra trailing carriage return
590 590 if os.linesep == '\r\n' and line and line[-1] == '\r':
591 591 line = line[:-1]
592 592 return line
593 593
594 594 def prompt(self, msg, default="y"):
595 595 """Prompt user with msg, read response.
596 596 If ui is not interactive, the default is returned.
597 597 """
598 598 if not self.interactive():
599 599 self.write(msg, ' ', default, "\n")
600 600 return default
601 601 try:
602 602 r = self._readline(self.label(msg, 'ui.prompt'))
603 603 if not r:
604 604 return default
605 605 return r
606 606 except EOFError:
607 607 raise util.Abort(_('response expected'))
608 608
609 609 def promptchoice(self, msg, choices, default=0):
610 610 """Prompt user with msg, read response, and ensure it matches
611 611 one of the provided choices. The index of the choice is returned.
612 612 choices is a sequence of acceptable responses with the format:
613 613 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
614 614 If ui is not interactive, the default is returned.
615 615 """
616 616 resps = [s[s.index('&') + 1].lower() for s in choices]
617 617 while True:
618 618 r = self.prompt(msg, resps[default])
619 619 if r.lower() in resps:
620 620 return resps.index(r.lower())
621 621 self.write(_("unrecognized response\n"))
622 622
623 623 def getpass(self, prompt=None, default=None):
624 624 if not self.interactive():
625 625 return default
626 626 try:
627 627 return getpass.getpass(prompt or _('password: '))
628 628 except EOFError:
629 629 raise util.Abort(_('response expected'))
630 630 def status(self, *msg, **opts):
631 631 '''write status message to output (if ui.quiet is False)
632 632
633 633 This adds an output label of "ui.status".
634 634 '''
635 635 if not self.quiet:
636 636 opts['label'] = opts.get('label', '') + ' ui.status'
637 637 self.write(*msg, **opts)
638 638 def warn(self, *msg, **opts):
639 639 '''write warning message to output (stderr)
640 640
641 641 This adds an output label of "ui.warning".
642 642 '''
643 643 opts['label'] = opts.get('label', '') + ' ui.warning'
644 644 self.write_err(*msg, **opts)
645 645 def note(self, *msg, **opts):
646 646 '''write note to output (if ui.verbose is True)
647 647
648 648 This adds an output label of "ui.note".
649 649 '''
650 650 if self.verbose:
651 651 opts['label'] = opts.get('label', '') + ' ui.note'
652 652 self.write(*msg, **opts)
653 653 def debug(self, *msg, **opts):
654 654 '''write debug message to output (if ui.debugflag is True)
655 655
656 656 This adds an output label of "ui.debug".
657 657 '''
658 658 if self.debugflag:
659 659 opts['label'] = opts.get('label', '') + ' ui.debug'
660 660 self.write(*msg, **opts)
661 661 def edit(self, text, user):
662 662 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
663 663 text=True)
664 664 try:
665 665 f = os.fdopen(fd, "w")
666 666 f.write(text)
667 667 f.close()
668 668
669 669 editor = self.geteditor()
670 670
671 671 util.system("%s \"%s\"" % (editor, name),
672 672 environ={'HGUSER': user},
673 673 onerr=util.Abort, errprefix=_("edit failed"),
674 674 out=self.fout)
675 675
676 676 f = open(name)
677 677 t = f.read()
678 678 f.close()
679 679 finally:
680 680 os.unlink(name)
681 681
682 682 return t
683 683
684 684 def traceback(self, exc=None):
685 685 '''print exception traceback if traceback printing enabled.
686 686 only to call in exception handler. returns true if traceback
687 687 printed.'''
688 688 if self.tracebackflag:
689 if exc:
689 if exc is None:
690 exc = sys.exc_info()
691 cause = getattr(exc[1], 'cause', None)
692
693 if cause is not None:
694 causetb = traceback.format_tb(cause[2])
695 exctb = traceback.format_tb(exc[2])
696 exconly = traceback.format_exception_only(cause[0], cause[1])
697
698 # exclude frame where 'exc' was chained and rethrown from exctb
699 self.write_err('Traceback (most recent call last):\n',
700 ''.join(exctb[:-1]),
701 ''.join(causetb),
702 ''.join(exconly))
703 else:
690 704 traceback.print_exception(exc[0], exc[1], exc[2],
691 705 file=self.ferr)
692 else:
693 traceback.print_exc(file=self.ferr)
694 706 return self.tracebackflag
695 707
696 708 def geteditor(self):
697 709 '''return editor to use'''
698 710 if sys.platform == 'plan9':
699 711 # vi is the MIPS instruction simulator on Plan 9. We
700 712 # instead default to E to plumb commit messages to
701 713 # avoid confusion.
702 714 editor = 'E'
703 715 else:
704 716 editor = 'vi'
705 717 return (os.environ.get("HGEDITOR") or
706 718 self.config("ui", "editor") or
707 719 os.environ.get("VISUAL") or
708 720 os.environ.get("EDITOR", editor))
709 721
710 722 def progress(self, topic, pos, item="", unit="", total=None):
711 723 '''show a progress message
712 724
713 725 With stock hg, this is simply a debug message that is hidden
714 726 by default, but with extensions or GUI tools it may be
715 727 visible. 'topic' is the current operation, 'item' is a
716 728 non-numeric marker of the current position (i.e. the currently
717 729 in-process file), 'pos' is the current numeric position (i.e.
718 730 revision, bytes, etc.), unit is a corresponding unit label,
719 731 and total is the highest expected pos.
720 732
721 733 Multiple nested topics may be active at a time.
722 734
723 735 All topics should be marked closed by setting pos to None at
724 736 termination.
725 737 '''
726 738
727 739 if pos is None or not self.debugflag:
728 740 return
729 741
730 742 if unit:
731 743 unit = ' ' + unit
732 744 if item:
733 745 item = ' ' + item
734 746
735 747 if total:
736 748 pct = 100.0 * pos / total
737 749 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
738 750 % (topic, item, pos, total, unit, pct))
739 751 else:
740 752 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
741 753
742 754 def log(self, service, *msg, **opts):
743 755 '''hook for logging facility extensions
744 756
745 757 service should be a readily-identifiable subsystem, which will
746 758 allow filtering.
747 759 message should be a newline-terminated string to log.
748 760 '''
749 761 pass
750 762
751 763 def label(self, msg, label):
752 764 '''style msg based on supplied label
753 765
754 766 Like ui.write(), this just returns msg unchanged, but extensions
755 767 and GUI tools can override it to allow styling output without
756 768 writing it.
757 769
758 770 ui.write(s, 'label') is equivalent to
759 771 ui.write(ui.label(s, 'label')).
760 772 '''
761 773 return msg
@@ -1,1211 +1,1216 b''
1 1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
2 2
3 3 $ echo "[ui]" >> $HGRCPATH
4 4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
5 5
6 6 $ hg init t
7 7 $ cd t
8 8
9 9 first revision, no sub
10 10
11 11 $ echo a > a
12 12 $ hg ci -Am0
13 13 adding a
14 14
15 15 add first sub
16 16
17 17 $ echo s = s > .hgsub
18 18 $ hg add .hgsub
19 19 $ hg init s
20 20 $ echo a > s/a
21 21
22 22 Issue2232: committing a subrepo without .hgsub
23 23
24 24 $ hg ci -mbad s
25 25 abort: can't commit subrepos without .hgsub
26 26 [255]
27 27
28 28 $ hg -R s ci -Ams0
29 29 adding a
30 30 $ hg sum
31 31 parent: 0:f7b1eb17ad24 tip
32 32 0
33 33 branch: default
34 34 commit: 1 added, 1 subrepos
35 35 update: (current)
36 36 $ hg ci -m1
37 37
38 38 Revert subrepo and test subrepo fileset keyword:
39 39
40 40 $ echo b > s/a
41 41 $ hg revert "set:subrepo('glob:s*')"
42 42 reverting subrepo s
43 43 reverting s/a (glob)
44 44 $ rm s/a.orig
45 45
46 46 Revert subrepo with no backup. The "reverting s/a" line is gone since
47 47 we're really running 'hg update' in the subrepo:
48 48
49 49 $ echo b > s/a
50 50 $ hg revert --no-backup s
51 51 reverting subrepo s
52 52
53 53 Issue2022: update -C
54 54
55 55 $ echo b > s/a
56 56 $ hg sum
57 57 parent: 1:7cf8cfea66e4 tip
58 58 1
59 59 branch: default
60 60 commit: 1 subrepos
61 61 update: (current)
62 62 $ hg co -C 1
63 63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64 $ hg sum
65 65 parent: 1:7cf8cfea66e4 tip
66 66 1
67 67 branch: default
68 68 commit: (clean)
69 69 update: (current)
70 70
71 71 commands that require a clean repo should respect subrepos
72 72
73 73 $ echo b >> s/a
74 74 $ hg backout tip
75 75 abort: uncommitted changes in subrepo s
76 76 [255]
77 77 $ hg revert -C -R s s/a
78 78
79 79 add sub sub
80 80
81 81 $ echo ss = ss > s/.hgsub
82 82 $ hg init s/ss
83 83 $ echo a > s/ss/a
84 84 $ hg -R s add s/.hgsub
85 85 $ hg -R s/ss add s/ss/a
86 86 $ hg sum
87 87 parent: 1:7cf8cfea66e4 tip
88 88 1
89 89 branch: default
90 90 commit: 1 subrepos
91 91 update: (current)
92 92 $ hg ci -m2
93 93 committing subrepository s
94 94 committing subrepository s/ss (glob)
95 95 $ hg sum
96 96 parent: 2:df30734270ae tip
97 97 2
98 98 branch: default
99 99 commit: (clean)
100 100 update: (current)
101 101
102 102 bump sub rev (and check it is ignored by ui.commitsubrepos)
103 103
104 104 $ echo b > s/a
105 105 $ hg -R s ci -ms1
106 106 $ hg --config ui.commitsubrepos=no ci -m3
107 107
108 108 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
109 109
110 110 $ echo c > s/a
111 111 $ hg --config ui.commitsubrepos=no ci -m4
112 112 abort: uncommitted changes in subrepo s
113 113 (use --subrepos for recursive commit)
114 114 [255]
115 115 $ hg id
116 116 f6affe3fbfaa+ tip
117 117 $ hg -R s ci -mc
118 118 $ hg id
119 119 f6affe3fbfaa+ tip
120 120 $ echo d > s/a
121 121 $ hg ci -m4
122 122 committing subrepository s
123 123 $ hg tip -R s
124 124 changeset: 4:02dcf1d70411
125 125 tag: tip
126 126 user: test
127 127 date: Thu Jan 01 00:00:00 1970 +0000
128 128 summary: 4
129 129
130 130
131 131 check caching
132 132
133 133 $ hg co 0
134 134 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
135 135 $ hg debugsub
136 136
137 137 restore
138 138
139 139 $ hg co
140 140 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 141 $ hg debugsub
142 142 path s
143 143 source s
144 144 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
145 145
146 146 new branch for merge tests
147 147
148 148 $ hg co 1
149 149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 $ echo t = t >> .hgsub
151 151 $ hg init t
152 152 $ echo t > t/t
153 153 $ hg -R t add t
154 154 adding t/t (glob)
155 155
156 156 5
157 157
158 158 $ hg ci -m5 # add sub
159 159 committing subrepository t
160 160 created new head
161 161 $ echo t2 > t/t
162 162
163 163 6
164 164
165 165 $ hg st -R s
166 166 $ hg ci -m6 # change sub
167 167 committing subrepository t
168 168 $ hg debugsub
169 169 path s
170 170 source s
171 171 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
172 172 path t
173 173 source t
174 174 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
175 175 $ echo t3 > t/t
176 176
177 177 7
178 178
179 179 $ hg ci -m7 # change sub again for conflict test
180 180 committing subrepository t
181 181 $ hg rm .hgsub
182 182
183 183 8
184 184
185 185 $ hg ci -m8 # remove sub
186 186
187 187 merge tests
188 188
189 189 $ hg co -C 3
190 190 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 191 $ hg merge 5 # test adding
192 192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 193 (branch merge, don't forget to commit)
194 194 $ hg debugsub
195 195 path s
196 196 source s
197 197 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
198 198 path t
199 199 source t
200 200 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
201 201 $ hg ci -m9
202 202 created new head
203 203 $ hg merge 6 --debug # test change
204 204 searching for copies back to rev 2
205 205 resolving manifests
206 206 branchmerge: True, force: False, partial: False
207 207 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
208 208 .hgsubstate: versions differ -> m
209 209 updating: .hgsubstate 1/1 files (100.00%)
210 210 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
211 211 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
212 212 getting subrepo t
213 213 resolving manifests
214 214 branchmerge: False, force: False, partial: False
215 215 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
216 216 t: remote is newer -> g
217 217 getting t
218 218 updating: t 1/1 files (100.00%)
219 219 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 220 (branch merge, don't forget to commit)
221 221 $ hg debugsub
222 222 path s
223 223 source s
224 224 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
225 225 path t
226 226 source t
227 227 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
228 228 $ echo conflict > t/t
229 229 $ hg ci -m10
230 230 committing subrepository t
231 231 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
232 232 searching for copies back to rev 2
233 233 resolving manifests
234 234 branchmerge: True, force: False, partial: False
235 235 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
236 236 .hgsubstate: versions differ -> m
237 237 updating: .hgsubstate 1/1 files (100.00%)
238 238 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
239 239 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
240 240 merging subrepo t
241 241 searching for copies back to rev 2
242 242 resolving manifests
243 243 branchmerge: True, force: False, partial: False
244 244 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
245 245 t: versions differ -> m
246 246 preserving t for resolve of t
247 247 updating: t 1/1 files (100.00%)
248 248 picked tool 'internal:merge' for t (binary False symlink False)
249 249 merging t
250 250 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
251 251 warning: conflicts during merge.
252 252 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
253 253 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
254 254 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
255 255 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 256 (branch merge, don't forget to commit)
257 257
258 258 should conflict
259 259
260 260 $ cat t/t
261 261 <<<<<<< local
262 262 conflict
263 263 =======
264 264 t3
265 265 >>>>>>> other
266 266
267 267 clone
268 268
269 269 $ cd ..
270 270 $ hg clone t tc
271 271 updating to branch default
272 272 cloning subrepo s from $TESTTMP/t/s
273 273 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
274 274 cloning subrepo t from $TESTTMP/t/t
275 275 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 276 $ cd tc
277 277 $ hg debugsub
278 278 path s
279 279 source s
280 280 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
281 281 path t
282 282 source t
283 283 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
284 284
285 285 push
286 286
287 287 $ echo bah > t/t
288 288 $ hg ci -m11
289 289 committing subrepository t
290 290 $ hg push
291 291 pushing to $TESTTMP/t (glob)
292 292 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
293 293 no changes made to subrepo s since last push to $TESTTMP/t/s
294 294 pushing subrepo t to $TESTTMP/t/t
295 295 searching for changes
296 296 adding changesets
297 297 adding manifests
298 298 adding file changes
299 299 added 1 changesets with 1 changes to 1 files
300 300 searching for changes
301 301 adding changesets
302 302 adding manifests
303 303 adding file changes
304 304 added 1 changesets with 1 changes to 1 files
305 305
306 306 push -f
307 307
308 308 $ echo bah > s/a
309 309 $ hg ci -m12
310 310 committing subrepository s
311 311 $ hg push
312 312 pushing to $TESTTMP/t (glob)
313 313 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
314 314 pushing subrepo s to $TESTTMP/t/s
315 315 searching for changes
316 316 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
317 317 (did you forget to merge? use push -f to force)
318 318 [255]
319 319 $ hg push -f
320 320 pushing to $TESTTMP/t (glob)
321 321 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
322 322 searching for changes
323 323 no changes found
324 324 pushing subrepo s to $TESTTMP/t/s
325 325 searching for changes
326 326 adding changesets
327 327 adding manifests
328 328 adding file changes
329 329 added 1 changesets with 1 changes to 1 files (+1 heads)
330 330 pushing subrepo t to $TESTTMP/t/t
331 331 searching for changes
332 332 no changes found
333 333 searching for changes
334 334 adding changesets
335 335 adding manifests
336 336 adding file changes
337 337 added 1 changesets with 1 changes to 1 files
338 338
339 339 check that unmodified subrepos are not pushed
340 340
341 341 $ hg clone . ../tcc
342 342 updating to branch default
343 343 cloning subrepo s from $TESTTMP/tc/s
344 344 cloning subrepo s/ss from $TESTTMP/tc/s/ss
345 345 cloning subrepo t from $TESTTMP/tc/t
346 346 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 347
348 348 the subrepos on the new clone have nothing to push to its source
349 349
350 350 $ hg push -R ../tcc .
351 351 pushing to .
352 352 no changes made to subrepo s/ss since last push to s/ss
353 353 no changes made to subrepo s since last push to s
354 354 no changes made to subrepo t since last push to t
355 355 searching for changes
356 356 no changes found
357 357 [1]
358 358
359 359 the subrepos on the source do not have a clean store versus the clone target
360 360 because they were never explicitly pushed to the source
361 361
362 362 $ hg push ../tcc
363 363 pushing to ../tcc
364 364 pushing subrepo s/ss to ../tcc/s/ss
365 365 searching for changes
366 366 no changes found
367 367 pushing subrepo s to ../tcc/s
368 368 searching for changes
369 369 no changes found
370 370 pushing subrepo t to ../tcc/t
371 371 searching for changes
372 372 no changes found
373 373 searching for changes
374 374 no changes found
375 375 [1]
376 376
377 377 after push their stores become clean
378 378
379 379 $ hg push ../tcc
380 380 pushing to ../tcc
381 381 no changes made to subrepo s/ss since last push to ../tcc/s/ss
382 382 no changes made to subrepo s since last push to ../tcc/s
383 383 no changes made to subrepo t since last push to ../tcc/t
384 384 searching for changes
385 385 no changes found
386 386 [1]
387 387
388 388 updating a subrepo to a different revision or changing
389 389 its working directory does not make its store dirty
390 390
391 391 $ hg -R s update '.^'
392 392 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
393 393 $ hg push
394 394 pushing to $TESTTMP/t
395 395 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
396 396 no changes made to subrepo s since last push to $TESTTMP/t/s
397 397 no changes made to subrepo t since last push to $TESTTMP/t/t
398 398 searching for changes
399 399 no changes found
400 400 [1]
401 401 $ echo foo >> s/a
402 402 $ hg push
403 403 pushing to $TESTTMP/t
404 404 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
405 405 no changes made to subrepo s since last push to $TESTTMP/t/s
406 406 no changes made to subrepo t since last push to $TESTTMP/t/t
407 407 searching for changes
408 408 no changes found
409 409 [1]
410 410 $ hg -R s update -C tip
411 411 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
412 412
413 413 committing into a subrepo makes its store (but not its parent's store) dirty
414 414
415 415 $ echo foo >> s/ss/a
416 416 $ hg -R s/ss commit -m 'test dirty store detection'
417 417 $ hg push
418 418 pushing to $TESTTMP/t
419 419 pushing subrepo s/ss to $TESTTMP/t/s/ss
420 420 searching for changes
421 421 adding changesets
422 422 adding manifests
423 423 adding file changes
424 424 added 1 changesets with 1 changes to 1 files
425 425 no changes made to subrepo s since last push to $TESTTMP/t/s
426 426 no changes made to subrepo t since last push to $TESTTMP/t/t
427 427 searching for changes
428 428 no changes found
429 429 [1]
430 430
431 431 a subrepo store may be clean versus one repo but not versus another
432 432
433 433 $ hg push
434 434 pushing to $TESTTMP/t
435 435 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss
436 436 no changes made to subrepo s since last push to $TESTTMP/t/s
437 437 no changes made to subrepo t since last push to $TESTTMP/t/t
438 438 searching for changes
439 439 no changes found
440 440 [1]
441 441 $ hg push ../tcc
442 442 pushing to ../tcc
443 443 pushing subrepo s/ss to ../tcc/s/ss
444 444 searching for changes
445 445 adding changesets
446 446 adding manifests
447 447 adding file changes
448 448 added 1 changesets with 1 changes to 1 files
449 449 no changes made to subrepo s since last push to ../tcc/s
450 450 no changes made to subrepo t since last push to ../tcc/t
451 451 searching for changes
452 452 no changes found
453 453 [1]
454 454
455 455 update
456 456
457 457 $ cd ../t
458 458 $ hg up -C # discard our earlier merge
459 459 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
460 460 $ echo blah > t/t
461 461 $ hg ci -m13
462 462 committing subrepository t
463 463
464 464 backout calls revert internally with minimal opts, which should not raise
465 465 KeyError
466 466
467 467 $ hg backout ".^"
468 468 reverting .hgsubstate
469 469 reverting subrepo s
470 470 reverting s/a
471 471 reverting subrepo ss
472 472 reverting subrepo t
473 473 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
474 474
475 475 $ hg up -C # discard changes
476 476 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
477 477
478 478 pull
479 479
480 480 $ cd ../tc
481 481 $ hg pull
482 482 pulling from $TESTTMP/t (glob)
483 483 searching for changes
484 484 adding changesets
485 485 adding manifests
486 486 adding file changes
487 487 added 1 changesets with 1 changes to 1 files
488 488 (run 'hg update' to get a working copy)
489 489
490 490 should pull t
491 491
492 492 $ hg up
493 493 pulling subrepo t from $TESTTMP/t/t
494 494 searching for changes
495 495 adding changesets
496 496 adding manifests
497 497 adding file changes
498 498 added 1 changesets with 1 changes to 1 files
499 499 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
500 500 $ cat t/t
501 501 blah
502 502
503 503 bogus subrepo path aborts
504 504
505 505 $ echo 'bogus=[boguspath' >> .hgsub
506 506 $ hg ci -m 'bogus subrepo path'
507 507 abort: missing ] in subrepo source
508 508 [255]
509 509
510 510 Issue1986: merge aborts when trying to merge a subrepo that
511 511 shouldn't need merging
512 512
513 513 # subrepo layout
514 514 #
515 515 # o 5 br
516 516 # /|
517 517 # o | 4 default
518 518 # | |
519 519 # | o 3 br
520 520 # |/|
521 521 # o | 2 default
522 522 # | |
523 523 # | o 1 br
524 524 # |/
525 525 # o 0 default
526 526
527 527 $ cd ..
528 528 $ rm -rf sub
529 529 $ hg init main
530 530 $ cd main
531 531 $ hg init s
532 532 $ cd s
533 533 $ echo a > a
534 534 $ hg ci -Am1
535 535 adding a
536 536 $ hg branch br
537 537 marked working directory as branch br
538 538 (branches are permanent and global, did you want a bookmark?)
539 539 $ echo a >> a
540 540 $ hg ci -m1
541 541 $ hg up default
542 542 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
543 543 $ echo b > b
544 544 $ hg ci -Am1
545 545 adding b
546 546 $ hg up br
547 547 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
548 548 $ hg merge tip
549 549 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
550 550 (branch merge, don't forget to commit)
551 551 $ hg ci -m1
552 552 $ hg up 2
553 553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 554 $ echo c > c
555 555 $ hg ci -Am1
556 556 adding c
557 557 $ hg up 3
558 558 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
559 559 $ hg merge 4
560 560 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 561 (branch merge, don't forget to commit)
562 562 $ hg ci -m1
563 563
564 564 # main repo layout:
565 565 #
566 566 # * <-- try to merge default into br again
567 567 # .`|
568 568 # . o 5 br --> substate = 5
569 569 # . |
570 570 # o | 4 default --> substate = 4
571 571 # | |
572 572 # | o 3 br --> substate = 2
573 573 # |/|
574 574 # o | 2 default --> substate = 2
575 575 # | |
576 576 # | o 1 br --> substate = 3
577 577 # |/
578 578 # o 0 default --> substate = 2
579 579
580 580 $ cd ..
581 581 $ echo 's = s' > .hgsub
582 582 $ hg -R s up 2
583 583 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
584 584 $ hg ci -Am1
585 585 adding .hgsub
586 586 $ hg branch br
587 587 marked working directory as branch br
588 588 (branches are permanent and global, did you want a bookmark?)
589 589 $ echo b > b
590 590 $ hg -R s up 3
591 591 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
592 592 $ hg ci -Am1
593 593 adding b
594 594 $ hg up default
595 595 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
596 596 $ echo c > c
597 597 $ hg ci -Am1
598 598 adding c
599 599 $ hg up 1
600 600 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
601 601 $ hg merge 2
602 602 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
603 603 (branch merge, don't forget to commit)
604 604 $ hg ci -m1
605 605 $ hg up 2
606 606 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
607 607 $ hg -R s up 4
608 608 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
609 609 $ echo d > d
610 610 $ hg ci -Am1
611 611 adding d
612 612 $ hg up 3
613 613 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
614 614 $ hg -R s up 5
615 615 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
616 616 $ echo e > e
617 617 $ hg ci -Am1
618 618 adding e
619 619
620 620 $ hg up 5
621 621 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
622 622 $ hg merge 4 # try to merge default into br again
623 623 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
624 624 (branch merge, don't forget to commit)
625 625 $ cd ..
626 626
627 627 test subrepo delete from .hgsubstate
628 628
629 629 $ hg init testdelete
630 630 $ mkdir testdelete/nested testdelete/nested2
631 631 $ hg init testdelete/nested
632 632 $ hg init testdelete/nested2
633 633 $ echo test > testdelete/nested/foo
634 634 $ echo test > testdelete/nested2/foo
635 635 $ hg -R testdelete/nested add
636 636 adding testdelete/nested/foo (glob)
637 637 $ hg -R testdelete/nested2 add
638 638 adding testdelete/nested2/foo (glob)
639 639 $ hg -R testdelete/nested ci -m test
640 640 $ hg -R testdelete/nested2 ci -m test
641 641 $ echo nested = nested > testdelete/.hgsub
642 642 $ echo nested2 = nested2 >> testdelete/.hgsub
643 643 $ hg -R testdelete add
644 644 adding testdelete/.hgsub (glob)
645 645 $ hg -R testdelete ci -m "nested 1 & 2 added"
646 646 $ echo nested = nested > testdelete/.hgsub
647 647 $ hg -R testdelete ci -m "nested 2 deleted"
648 648 $ cat testdelete/.hgsubstate
649 649 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
650 650 $ hg -R testdelete remove testdelete/.hgsub
651 651 $ hg -R testdelete ci -m ".hgsub deleted"
652 652 $ cat testdelete/.hgsubstate
653 653 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
654 654
655 655 test repository cloning
656 656
657 657 $ mkdir mercurial mercurial2
658 658 $ hg init nested_absolute
659 659 $ echo test > nested_absolute/foo
660 660 $ hg -R nested_absolute add
661 661 adding nested_absolute/foo (glob)
662 662 $ hg -R nested_absolute ci -mtest
663 663 $ cd mercurial
664 664 $ hg init nested_relative
665 665 $ echo test2 > nested_relative/foo2
666 666 $ hg -R nested_relative add
667 667 adding nested_relative/foo2 (glob)
668 668 $ hg -R nested_relative ci -mtest2
669 669 $ hg init main
670 670 $ echo "nested_relative = ../nested_relative" > main/.hgsub
671 671 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
672 672 $ hg -R main add
673 673 adding main/.hgsub (glob)
674 674 $ hg -R main ci -m "add subrepos"
675 675 $ cd ..
676 676 $ hg clone mercurial/main mercurial2/main
677 677 updating to branch default
678 678 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
679 679 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
680 680 > mercurial2/main/nested_relative/.hg/hgrc
681 681 [paths]
682 682 default = $TESTTMP/mercurial/nested_absolute
683 683 [paths]
684 684 default = $TESTTMP/mercurial/nested_relative
685 685 $ rm -rf mercurial mercurial2
686 686
687 687 Issue1977: multirepo push should fail if subrepo push fails
688 688
689 689 $ hg init repo
690 690 $ hg init repo/s
691 691 $ echo a > repo/s/a
692 692 $ hg -R repo/s ci -Am0
693 693 adding a
694 694 $ echo s = s > repo/.hgsub
695 695 $ hg -R repo ci -Am1
696 696 adding .hgsub
697 697 $ hg clone repo repo2
698 698 updating to branch default
699 699 cloning subrepo s from $TESTTMP/repo/s
700 700 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
701 701 $ hg -q -R repo2 pull -u
702 702 $ echo 1 > repo2/s/a
703 703 $ hg -R repo2/s ci -m2
704 704 $ hg -q -R repo2/s push
705 705 $ hg -R repo2/s up -C 0
706 706 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
707 707 $ echo 2 > repo2/s/b
708 708 $ hg -R repo2/s ci -m3 -A
709 709 adding b
710 710 created new head
711 711 $ hg -R repo2 ci -m3
712 712 $ hg -q -R repo2 push
713 713 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
714 714 (did you forget to merge? use push -f to force)
715 715 [255]
716 716 $ hg -R repo update
717 717 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
718 718
719 719 test if untracked file is not overwritten
720 720
721 721 $ echo issue3276_ok > repo/s/b
722 722 $ hg -R repo2 push -f -q
723 723 $ hg -R repo update
724 724 b: untracked file differs
725 725 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
726 726 [255]
727 727
728 728 $ cat repo/s/b
729 729 issue3276_ok
730 730 $ rm repo/s/b
731 731 $ hg -R repo revert --all
732 732 reverting repo/.hgsubstate (glob)
733 733 reverting subrepo s
734 734 $ hg -R repo update
735 735 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
736 736 $ cat repo/s/b
737 737 2
738 738 $ rm -rf repo2 repo
739 739
740 740
741 741 Issue1852 subrepos with relative paths always push/pull relative to default
742 742
743 743 Prepare a repo with subrepo
744 744
745 745 $ hg init issue1852a
746 746 $ cd issue1852a
747 747 $ hg init sub/repo
748 748 $ echo test > sub/repo/foo
749 749 $ hg -R sub/repo add sub/repo/foo
750 750 $ echo sub/repo = sub/repo > .hgsub
751 751 $ hg add .hgsub
752 752 $ hg ci -mtest
753 753 committing subrepository sub/repo (glob)
754 754 $ echo test >> sub/repo/foo
755 755 $ hg ci -mtest
756 756 committing subrepository sub/repo (glob)
757 757 $ cd ..
758 758
759 759 Create repo without default path, pull top repo, and see what happens on update
760 760
761 761 $ hg init issue1852b
762 762 $ hg -R issue1852b pull issue1852a
763 763 pulling from issue1852a
764 764 requesting all changes
765 765 adding changesets
766 766 adding manifests
767 767 adding file changes
768 768 added 2 changesets with 3 changes to 2 files
769 769 (run 'hg update' to get a working copy)
770 770 $ hg -R issue1852b update
771 771 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
772 772 [255]
773 773
774 Ensure a full traceback, not just the SubrepoAbort part
775
776 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise util\.Abort'
777 raise util.Abort(_("default path for subrepository not found"))
778
774 779 Pull -u now doesn't help
775 780
776 781 $ hg -R issue1852b pull -u issue1852a
777 782 pulling from issue1852a
778 783 searching for changes
779 784 no changes found
780 785
781 786 Try the same, but with pull -u
782 787
783 788 $ hg init issue1852c
784 789 $ hg -R issue1852c pull -r0 -u issue1852a
785 790 pulling from issue1852a
786 791 adding changesets
787 792 adding manifests
788 793 adding file changes
789 794 added 1 changesets with 2 changes to 2 files
790 795 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
791 796 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
792 797
793 798 Try to push from the other side
794 799
795 800 $ hg -R issue1852a push `pwd`/issue1852c
796 801 pushing to $TESTTMP/issue1852c
797 802 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
798 803 searching for changes
799 804 no changes found
800 805 searching for changes
801 806 adding changesets
802 807 adding manifests
803 808 adding file changes
804 809 added 1 changesets with 1 changes to 1 files
805 810
806 811 Incoming and outgoing should not use the default path:
807 812
808 813 $ hg clone -q issue1852a issue1852d
809 814 $ hg -R issue1852d outgoing --subrepos issue1852c
810 815 comparing with issue1852c
811 816 searching for changes
812 817 no changes found
813 818 comparing with issue1852c/sub/repo
814 819 searching for changes
815 820 no changes found
816 821 [1]
817 822 $ hg -R issue1852d incoming --subrepos issue1852c
818 823 comparing with issue1852c
819 824 searching for changes
820 825 no changes found
821 826 comparing with issue1852c/sub/repo
822 827 searching for changes
823 828 no changes found
824 829 [1]
825 830
826 831 Check status of files when none of them belong to the first
827 832 subrepository:
828 833
829 834 $ hg init subrepo-status
830 835 $ cd subrepo-status
831 836 $ hg init subrepo-1
832 837 $ hg init subrepo-2
833 838 $ cd subrepo-2
834 839 $ touch file
835 840 $ hg add file
836 841 $ cd ..
837 842 $ echo subrepo-1 = subrepo-1 > .hgsub
838 843 $ echo subrepo-2 = subrepo-2 >> .hgsub
839 844 $ hg add .hgsub
840 845 $ hg ci -m 'Added subrepos'
841 846 committing subrepository subrepo-2
842 847 $ hg st subrepo-2/file
843 848
844 849 Check that share works with subrepo
845 850 $ hg --config extensions.share= share . ../shared
846 851 updating working directory
847 852 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
848 853 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
849 854 $ test -f ../shared/subrepo-1/.hg/sharedpath
850 855 [1]
851 856 $ hg -R ../shared in
852 857 abort: repository default not found!
853 858 [255]
854 859 $ hg -R ../shared/subrepo-2 showconfig paths
855 860 paths.default=$TESTTMP/subrepo-status/subrepo-2
856 861 $ hg -R ../shared/subrepo-1 sum --remote
857 862 parent: -1:000000000000 tip (empty repository)
858 863 branch: default
859 864 commit: (clean)
860 865 update: (current)
861 866 remote: (synced)
862 867
863 868 Check hg update --clean
864 869 $ cd $TESTTMP/t
865 870 $ rm -r t/t.orig
866 871 $ hg status -S --all
867 872 C .hgsub
868 873 C .hgsubstate
869 874 C a
870 875 C s/.hgsub
871 876 C s/.hgsubstate
872 877 C s/a
873 878 C s/ss/a
874 879 C t/t
875 880 $ echo c1 > s/a
876 881 $ cd s
877 882 $ echo c1 > b
878 883 $ echo c1 > c
879 884 $ hg add b
880 885 $ cd ..
881 886 $ hg status -S
882 887 M s/a
883 888 A s/b
884 889 ? s/c
885 890 $ hg update -C
886 891 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
887 892 $ hg status -S
888 893 ? s/b
889 894 ? s/c
890 895
891 896 Sticky subrepositories, no changes
892 897 $ cd $TESTTMP/t
893 898 $ hg id
894 899 925c17564ef8 tip
895 900 $ hg -R s id
896 901 12a213df6fa9 tip
897 902 $ hg -R t id
898 903 52c0adc0515a tip
899 904 $ hg update 11
900 905 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
901 906 $ hg id
902 907 365661e5936a
903 908 $ hg -R s id
904 909 fc627a69481f
905 910 $ hg -R t id
906 911 e95bcfa18a35
907 912
908 913 Sticky subrepositorys, file changes
909 914 $ touch s/f1
910 915 $ touch t/f1
911 916 $ hg add -S s/f1
912 917 $ hg add -S t/f1
913 918 $ hg id
914 919 365661e5936a+
915 920 $ hg -R s id
916 921 fc627a69481f+
917 922 $ hg -R t id
918 923 e95bcfa18a35+
919 924 $ hg update tip
920 925 subrepository sources for s differ
921 926 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)?
922 927 l
923 928 subrepository sources for t differ
924 929 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)?
925 930 l
926 931 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
927 932 $ hg id
928 933 925c17564ef8+ tip
929 934 $ hg -R s id
930 935 fc627a69481f+
931 936 $ hg -R t id
932 937 e95bcfa18a35+
933 938 $ hg update --clean tip
934 939 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
935 940
936 941 Sticky subrepository, revision updates
937 942 $ hg id
938 943 925c17564ef8 tip
939 944 $ hg -R s id
940 945 12a213df6fa9 tip
941 946 $ hg -R t id
942 947 52c0adc0515a tip
943 948 $ cd s
944 949 $ hg update -r -2
945 950 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
946 951 $ cd ../t
947 952 $ hg update -r 2
948 953 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
949 954 $ cd ..
950 955 $ hg update 10
951 956 subrepository sources for t differ (in checked out version)
952 957 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)?
953 958 l
954 959 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
955 960 $ hg id
956 961 e45c8b14af55+
957 962 $ hg -R s id
958 963 02dcf1d70411
959 964 $ hg -R t id
960 965 7af322bc1198
961 966
962 967 Sticky subrepository, file changes and revision updates
963 968 $ touch s/f1
964 969 $ touch t/f1
965 970 $ hg add -S s/f1
966 971 $ hg add -S t/f1
967 972 $ hg id
968 973 e45c8b14af55+
969 974 $ hg -R s id
970 975 02dcf1d70411+
971 976 $ hg -R t id
972 977 7af322bc1198+
973 978 $ hg update tip
974 979 subrepository sources for s differ
975 980 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)?
976 981 l
977 982 subrepository sources for t differ
978 983 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)?
979 984 l
980 985 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
981 986 $ hg id
982 987 925c17564ef8+ tip
983 988 $ hg -R s id
984 989 02dcf1d70411+
985 990 $ hg -R t id
986 991 7af322bc1198+
987 992
988 993 Sticky repository, update --clean
989 994 $ hg update --clean tip
990 995 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
991 996 $ hg id
992 997 925c17564ef8 tip
993 998 $ hg -R s id
994 999 12a213df6fa9 tip
995 1000 $ hg -R t id
996 1001 52c0adc0515a tip
997 1002
998 1003 Test subrepo already at intended revision:
999 1004 $ cd s
1000 1005 $ hg update fc627a69481f
1001 1006 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1002 1007 $ cd ..
1003 1008 $ hg update 11
1004 1009 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1005 1010 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1006 1011 $ hg id -n
1007 1012 11+
1008 1013 $ hg -R s id
1009 1014 fc627a69481f
1010 1015 $ hg -R t id
1011 1016 e95bcfa18a35
1012 1017
1013 1018 Test that removing .hgsubstate doesn't break anything:
1014 1019
1015 1020 $ hg rm -f .hgsubstate
1016 1021 $ hg ci -mrm
1017 1022 nothing changed
1018 1023 [1]
1019 1024 $ hg log -vr tip
1020 1025 changeset: 13:925c17564ef8
1021 1026 tag: tip
1022 1027 user: test
1023 1028 date: Thu Jan 01 00:00:00 1970 +0000
1024 1029 files: .hgsubstate
1025 1030 description:
1026 1031 13
1027 1032
1028 1033
1029 1034
1030 1035 Test that removing .hgsub removes .hgsubstate:
1031 1036
1032 1037 $ hg rm .hgsub
1033 1038 $ hg ci -mrm2
1034 1039 created new head
1035 1040 $ hg log -vr tip
1036 1041 changeset: 14:2400bccd50af
1037 1042 tag: tip
1038 1043 parent: 11:365661e5936a
1039 1044 user: test
1040 1045 date: Thu Jan 01 00:00:00 1970 +0000
1041 1046 files: .hgsub .hgsubstate
1042 1047 description:
1043 1048 rm2
1044 1049
1045 1050
1046 1051 Test issue3153: diff -S with deleted subrepos
1047 1052
1048 1053 $ hg diff --nodates -S -c .
1049 1054 diff -r 365661e5936a -r 2400bccd50af .hgsub
1050 1055 --- a/.hgsub
1051 1056 +++ /dev/null
1052 1057 @@ -1,2 +0,0 @@
1053 1058 -s = s
1054 1059 -t = t
1055 1060 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1056 1061 --- a/.hgsubstate
1057 1062 +++ /dev/null
1058 1063 @@ -1,2 +0,0 @@
1059 1064 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1060 1065 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1061 1066
1062 1067 Test behavior of add for explicit path in subrepo:
1063 1068 $ cd ..
1064 1069 $ hg init explicit
1065 1070 $ cd explicit
1066 1071 $ echo s = s > .hgsub
1067 1072 $ hg add .hgsub
1068 1073 $ hg init s
1069 1074 $ hg ci -m0
1070 1075 Adding with an explicit path in a subrepo adds the file
1071 1076 $ echo c1 > f1
1072 1077 $ echo c2 > s/f2
1073 1078 $ hg st -S
1074 1079 ? f1
1075 1080 ? s/f2
1076 1081 $ hg add s/f2
1077 1082 $ hg st -S
1078 1083 A s/f2
1079 1084 ? f1
1080 1085 $ hg ci -R s -m0
1081 1086 $ hg ci -Am1
1082 1087 adding f1
1083 1088 Adding with an explicit path in a subrepo with -S has the same behavior
1084 1089 $ echo c3 > f3
1085 1090 $ echo c4 > s/f4
1086 1091 $ hg st -S
1087 1092 ? f3
1088 1093 ? s/f4
1089 1094 $ hg add -S s/f4
1090 1095 $ hg st -S
1091 1096 A s/f4
1092 1097 ? f3
1093 1098 $ hg ci -R s -m1
1094 1099 $ hg ci -Ama2
1095 1100 adding f3
1096 1101 Adding without a path or pattern silently ignores subrepos
1097 1102 $ echo c5 > f5
1098 1103 $ echo c6 > s/f6
1099 1104 $ echo c7 > s/f7
1100 1105 $ hg st -S
1101 1106 ? f5
1102 1107 ? s/f6
1103 1108 ? s/f7
1104 1109 $ hg add
1105 1110 adding f5
1106 1111 $ hg st -S
1107 1112 A f5
1108 1113 ? s/f6
1109 1114 ? s/f7
1110 1115 $ hg ci -R s -Am2
1111 1116 adding f6
1112 1117 adding f7
1113 1118 $ hg ci -m3
1114 1119 Adding without a path or pattern with -S also adds files in subrepos
1115 1120 $ echo c8 > f8
1116 1121 $ echo c9 > s/f9
1117 1122 $ echo c10 > s/f10
1118 1123 $ hg st -S
1119 1124 ? f8
1120 1125 ? s/f10
1121 1126 ? s/f9
1122 1127 $ hg add -S
1123 1128 adding f8
1124 1129 adding s/f10 (glob)
1125 1130 adding s/f9 (glob)
1126 1131 $ hg st -S
1127 1132 A f8
1128 1133 A s/f10
1129 1134 A s/f9
1130 1135 $ hg ci -R s -m3
1131 1136 $ hg ci -m4
1132 1137 Adding with a pattern silently ignores subrepos
1133 1138 $ echo c11 > fm11
1134 1139 $ echo c12 > fn12
1135 1140 $ echo c13 > s/fm13
1136 1141 $ echo c14 > s/fn14
1137 1142 $ hg st -S
1138 1143 ? fm11
1139 1144 ? fn12
1140 1145 ? s/fm13
1141 1146 ? s/fn14
1142 1147 $ hg add 'glob:**fm*'
1143 1148 adding fm11
1144 1149 $ hg st -S
1145 1150 A fm11
1146 1151 ? fn12
1147 1152 ? s/fm13
1148 1153 ? s/fn14
1149 1154 $ hg ci -R s -Am4
1150 1155 adding fm13
1151 1156 adding fn14
1152 1157 $ hg ci -Am5
1153 1158 adding fn12
1154 1159 Adding with a pattern with -S also adds matches in subrepos
1155 1160 $ echo c15 > fm15
1156 1161 $ echo c16 > fn16
1157 1162 $ echo c17 > s/fm17
1158 1163 $ echo c18 > s/fn18
1159 1164 $ hg st -S
1160 1165 ? fm15
1161 1166 ? fn16
1162 1167 ? s/fm17
1163 1168 ? s/fn18
1164 1169 $ hg add -S 'glob:**fm*'
1165 1170 adding fm15
1166 1171 adding s/fm17 (glob)
1167 1172 $ hg st -S
1168 1173 A fm15
1169 1174 A s/fm17
1170 1175 ? fn16
1171 1176 ? s/fn18
1172 1177 $ hg ci -R s -Am5
1173 1178 adding fn18
1174 1179 $ hg ci -Am6
1175 1180 adding fn16
1176 1181
1177 1182 Test behavior of forget for explicit path in subrepo:
1178 1183 Forgetting an explicit path in a subrepo untracks the file
1179 1184 $ echo c19 > s/f19
1180 1185 $ hg add s/f19
1181 1186 $ hg st -S
1182 1187 A s/f19
1183 1188 $ hg forget s/f19
1184 1189 $ hg st -S
1185 1190 ? s/f19
1186 1191 $ rm s/f19
1187 1192 $ cd ..
1188 1193
1189 1194 Courtesy phases synchronisation to publishing server does not block the push
1190 1195 (issue3781)
1191 1196
1192 1197 $ cp -r main issue3781
1193 1198 $ cp -r main issue3781-dest
1194 1199 $ cd issue3781-dest/s
1195 1200 $ hg phase tip # show we have draft changeset
1196 1201 5: draft
1197 1202 $ chmod a-w .hg/store/phaseroots # prevent phase push
1198 1203 $ cd ../../issue3781
1199 1204 $ cat >> .hg/hgrc << EOF
1200 1205 > [paths]
1201 1206 > default=../issue3781-dest/
1202 1207 > EOF
1203 1208 $ hg push
1204 1209 pushing to $TESTTMP/issue3781-dest (glob)
1205 1210 pushing subrepo s to $TESTTMP/issue3781-dest/s
1206 1211 searching for changes
1207 1212 no changes found
1208 1213 searching for changes
1209 1214 no changes found
1210 1215 [1]
1211 1216
General Comments 0
You need to be logged in to leave comments. Login now