##// END OF EJS Templates
scmutil: use util.shellquote instead of %r...
Augie Fackler -
r33795:2cd5aba5 default
parent child Browse files
Show More
@@ -1,1105 +1,1105
1 1 # scmutil.py - Mercurial core utility functions
2 2 #
3 3 # Copyright 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 errno
11 11 import glob
12 12 import hashlib
13 13 import os
14 14 import re
15 15 import socket
16 16 import weakref
17 17
18 18 from .i18n import _
19 19 from .node import (
20 20 hex,
21 21 nullid,
22 22 wdirid,
23 23 wdirrev,
24 24 )
25 25
26 26 from . import (
27 27 encoding,
28 28 error,
29 29 match as matchmod,
30 30 obsolete,
31 31 obsutil,
32 32 pathutil,
33 33 phases,
34 34 pycompat,
35 35 revsetlang,
36 36 similar,
37 37 util,
38 38 )
39 39
40 40 if pycompat.osname == 'nt':
41 41 from . import scmwindows as scmplatform
42 42 else:
43 43 from . import scmposix as scmplatform
44 44
45 45 termsize = scmplatform.termsize
46 46
47 47 class status(tuple):
48 48 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
49 49 and 'ignored' properties are only relevant to the working copy.
50 50 '''
51 51
52 52 __slots__ = ()
53 53
54 54 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
55 55 clean):
56 56 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
57 57 ignored, clean))
58 58
59 59 @property
60 60 def modified(self):
61 61 '''files that have been modified'''
62 62 return self[0]
63 63
64 64 @property
65 65 def added(self):
66 66 '''files that have been added'''
67 67 return self[1]
68 68
69 69 @property
70 70 def removed(self):
71 71 '''files that have been removed'''
72 72 return self[2]
73 73
74 74 @property
75 75 def deleted(self):
76 76 '''files that are in the dirstate, but have been deleted from the
77 77 working copy (aka "missing")
78 78 '''
79 79 return self[3]
80 80
81 81 @property
82 82 def unknown(self):
83 83 '''files not in the dirstate that are not ignored'''
84 84 return self[4]
85 85
86 86 @property
87 87 def ignored(self):
88 88 '''files not in the dirstate that are ignored (by _dirignore())'''
89 89 return self[5]
90 90
91 91 @property
92 92 def clean(self):
93 93 '''files that have not been modified'''
94 94 return self[6]
95 95
96 96 def __repr__(self, *args, **kwargs):
97 97 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
98 98 'unknown=%r, ignored=%r, clean=%r>') % self)
99 99
100 100 def itersubrepos(ctx1, ctx2):
101 101 """find subrepos in ctx1 or ctx2"""
102 102 # Create a (subpath, ctx) mapping where we prefer subpaths from
103 103 # ctx1. The subpaths from ctx2 are important when the .hgsub file
104 104 # has been modified (in ctx2) but not yet committed (in ctx1).
105 105 subpaths = dict.fromkeys(ctx2.substate, ctx2)
106 106 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
107 107
108 108 missing = set()
109 109
110 110 for subpath in ctx2.substate:
111 111 if subpath not in ctx1.substate:
112 112 del subpaths[subpath]
113 113 missing.add(subpath)
114 114
115 115 for subpath, ctx in sorted(subpaths.iteritems()):
116 116 yield subpath, ctx.sub(subpath)
117 117
118 118 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
119 119 # status and diff will have an accurate result when it does
120 120 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
121 121 # against itself.
122 122 for subpath in missing:
123 123 yield subpath, ctx2.nullsub(subpath, ctx1)
124 124
125 125 def nochangesfound(ui, repo, excluded=None):
126 126 '''Report no changes for push/pull, excluded is None or a list of
127 127 nodes excluded from the push/pull.
128 128 '''
129 129 secretlist = []
130 130 if excluded:
131 131 for n in excluded:
132 132 ctx = repo[n]
133 133 if ctx.phase() >= phases.secret and not ctx.extinct():
134 134 secretlist.append(n)
135 135
136 136 if secretlist:
137 137 ui.status(_("no changes found (ignored %d secret changesets)\n")
138 138 % len(secretlist))
139 139 else:
140 140 ui.status(_("no changes found\n"))
141 141
142 142 def callcatch(ui, func):
143 143 """call func() with global exception handling
144 144
145 145 return func() if no exception happens. otherwise do some error handling
146 146 and return an exit code accordingly. does not handle all exceptions.
147 147 """
148 148 try:
149 149 try:
150 150 return func()
151 151 except: # re-raises
152 152 ui.traceback()
153 153 raise
154 154 # Global exception handling, alphabetically
155 155 # Mercurial-specific first, followed by built-in and library exceptions
156 156 except error.LockHeld as inst:
157 157 if inst.errno == errno.ETIMEDOUT:
158 158 reason = _('timed out waiting for lock held by %r') % inst.locker
159 159 else:
160 160 reason = _('lock held by %r') % inst.locker
161 161 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
162 162 if not inst.locker:
163 163 ui.warn(_("(lock might be very busy)\n"))
164 164 except error.LockUnavailable as inst:
165 165 ui.warn(_("abort: could not lock %s: %s\n") %
166 166 (inst.desc or inst.filename, inst.strerror))
167 167 except error.OutOfBandError as inst:
168 168 if inst.args:
169 169 msg = _("abort: remote error:\n")
170 170 else:
171 171 msg = _("abort: remote error\n")
172 172 ui.warn(msg)
173 173 if inst.args:
174 174 ui.warn(''.join(inst.args))
175 175 if inst.hint:
176 176 ui.warn('(%s)\n' % inst.hint)
177 177 except error.RepoError as inst:
178 178 ui.warn(_("abort: %s!\n") % inst)
179 179 if inst.hint:
180 180 ui.warn(_("(%s)\n") % inst.hint)
181 181 except error.ResponseError as inst:
182 182 ui.warn(_("abort: %s") % inst.args[0])
183 183 if not isinstance(inst.args[1], basestring):
184 184 ui.warn(" %r\n" % (inst.args[1],))
185 185 elif not inst.args[1]:
186 186 ui.warn(_(" empty string\n"))
187 187 else:
188 188 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
189 189 except error.CensoredNodeError as inst:
190 190 ui.warn(_("abort: file censored %s!\n") % inst)
191 191 except error.RevlogError as inst:
192 192 ui.warn(_("abort: %s!\n") % inst)
193 193 except error.InterventionRequired as inst:
194 194 ui.warn("%s\n" % inst)
195 195 if inst.hint:
196 196 ui.warn(_("(%s)\n") % inst.hint)
197 197 return 1
198 198 except error.WdirUnsupported:
199 199 ui.warn(_("abort: working directory revision cannot be specified\n"))
200 200 except error.Abort as inst:
201 201 ui.warn(_("abort: %s\n") % inst)
202 202 if inst.hint:
203 203 ui.warn(_("(%s)\n") % inst.hint)
204 204 except ImportError as inst:
205 205 ui.warn(_("abort: %s!\n") % inst)
206 206 m = str(inst).split()[-1]
207 207 if m in "mpatch bdiff".split():
208 208 ui.warn(_("(did you forget to compile extensions?)\n"))
209 209 elif m in "zlib".split():
210 210 ui.warn(_("(is your Python install correct?)\n"))
211 211 except IOError as inst:
212 212 if util.safehasattr(inst, "code"):
213 213 ui.warn(_("abort: %s\n") % inst)
214 214 elif util.safehasattr(inst, "reason"):
215 215 try: # usually it is in the form (errno, strerror)
216 216 reason = inst.reason.args[1]
217 217 except (AttributeError, IndexError):
218 218 # it might be anything, for example a string
219 219 reason = inst.reason
220 220 if isinstance(reason, unicode):
221 221 # SSLError of Python 2.7.9 contains a unicode
222 222 reason = encoding.unitolocal(reason)
223 223 ui.warn(_("abort: error: %s\n") % reason)
224 224 elif (util.safehasattr(inst, "args")
225 225 and inst.args and inst.args[0] == errno.EPIPE):
226 226 pass
227 227 elif getattr(inst, "strerror", None):
228 228 if getattr(inst, "filename", None):
229 229 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
230 230 else:
231 231 ui.warn(_("abort: %s\n") % inst.strerror)
232 232 else:
233 233 raise
234 234 except OSError as inst:
235 235 if getattr(inst, "filename", None) is not None:
236 236 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
237 237 else:
238 238 ui.warn(_("abort: %s\n") % inst.strerror)
239 239 except MemoryError:
240 240 ui.warn(_("abort: out of memory\n"))
241 241 except SystemExit as inst:
242 242 # Commands shouldn't sys.exit directly, but give a return code.
243 243 # Just in case catch this and and pass exit code to caller.
244 244 return inst.code
245 245 except socket.error as inst:
246 246 ui.warn(_("abort: %s\n") % inst.args[-1])
247 247
248 248 return -1
249 249
250 250 def checknewlabel(repo, lbl, kind):
251 251 # Do not use the "kind" parameter in ui output.
252 252 # It makes strings difficult to translate.
253 253 if lbl in ['tip', '.', 'null']:
254 254 raise error.Abort(_("the name '%s' is reserved") % lbl)
255 255 for c in (':', '\0', '\n', '\r'):
256 256 if c in lbl:
257 257 raise error.Abort(_("%r cannot be used in a name") % c)
258 258 try:
259 259 int(lbl)
260 260 raise error.Abort(_("cannot use an integer as a name"))
261 261 except ValueError:
262 262 pass
263 263
264 264 def checkfilename(f):
265 265 '''Check that the filename f is an acceptable filename for a tracked file'''
266 266 if '\r' in f or '\n' in f:
267 267 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
268 268
269 269 def checkportable(ui, f):
270 270 '''Check if filename f is portable and warn or abort depending on config'''
271 271 checkfilename(f)
272 272 abort, warn = checkportabilityalert(ui)
273 273 if abort or warn:
274 274 msg = util.checkwinfilename(f)
275 275 if msg:
276 msg = "%s: %r" % (msg, f)
276 msg = "%s: %s" % (msg, util.shellquote(f))
277 277 if abort:
278 278 raise error.Abort(msg)
279 279 ui.warn(_("warning: %s\n") % msg)
280 280
281 281 def checkportabilityalert(ui):
282 282 '''check if the user's config requests nothing, a warning, or abort for
283 283 non-portable filenames'''
284 284 val = ui.config('ui', 'portablefilenames')
285 285 lval = val.lower()
286 286 bval = util.parsebool(val)
287 287 abort = pycompat.osname == 'nt' or lval == 'abort'
288 288 warn = bval or lval == 'warn'
289 289 if bval is None and not (warn or abort or lval == 'ignore'):
290 290 raise error.ConfigError(
291 291 _("ui.portablefilenames value is invalid ('%s')") % val)
292 292 return abort, warn
293 293
294 294 class casecollisionauditor(object):
295 295 def __init__(self, ui, abort, dirstate):
296 296 self._ui = ui
297 297 self._abort = abort
298 298 allfiles = '\0'.join(dirstate._map)
299 299 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
300 300 self._dirstate = dirstate
301 301 # The purpose of _newfiles is so that we don't complain about
302 302 # case collisions if someone were to call this object with the
303 303 # same filename twice.
304 304 self._newfiles = set()
305 305
306 306 def __call__(self, f):
307 307 if f in self._newfiles:
308 308 return
309 309 fl = encoding.lower(f)
310 310 if fl in self._loweredfiles and f not in self._dirstate:
311 311 msg = _('possible case-folding collision for %s') % f
312 312 if self._abort:
313 313 raise error.Abort(msg)
314 314 self._ui.warn(_("warning: %s\n") % msg)
315 315 self._loweredfiles.add(fl)
316 316 self._newfiles.add(f)
317 317
318 318 def filteredhash(repo, maxrev):
319 319 """build hash of filtered revisions in the current repoview.
320 320
321 321 Multiple caches perform up-to-date validation by checking that the
322 322 tiprev and tipnode stored in the cache file match the current repository.
323 323 However, this is not sufficient for validating repoviews because the set
324 324 of revisions in the view may change without the repository tiprev and
325 325 tipnode changing.
326 326
327 327 This function hashes all the revs filtered from the view and returns
328 328 that SHA-1 digest.
329 329 """
330 330 cl = repo.changelog
331 331 if not cl.filteredrevs:
332 332 return None
333 333 key = None
334 334 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
335 335 if revs:
336 336 s = hashlib.sha1()
337 337 for rev in revs:
338 338 s.update('%d;' % rev)
339 339 key = s.digest()
340 340 return key
341 341
342 342 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
343 343 '''yield every hg repository under path, always recursively.
344 344 The recurse flag will only control recursion into repo working dirs'''
345 345 def errhandler(err):
346 346 if err.filename == path:
347 347 raise err
348 348 samestat = getattr(os.path, 'samestat', None)
349 349 if followsym and samestat is not None:
350 350 def adddir(dirlst, dirname):
351 351 match = False
352 352 dirstat = os.stat(dirname)
353 353 for lstdirstat in dirlst:
354 354 if samestat(dirstat, lstdirstat):
355 355 match = True
356 356 break
357 357 if not match:
358 358 dirlst.append(dirstat)
359 359 return not match
360 360 else:
361 361 followsym = False
362 362
363 363 if (seen_dirs is None) and followsym:
364 364 seen_dirs = []
365 365 adddir(seen_dirs, path)
366 366 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
367 367 dirs.sort()
368 368 if '.hg' in dirs:
369 369 yield root # found a repository
370 370 qroot = os.path.join(root, '.hg', 'patches')
371 371 if os.path.isdir(os.path.join(qroot, '.hg')):
372 372 yield qroot # we have a patch queue repo here
373 373 if recurse:
374 374 # avoid recursing inside the .hg directory
375 375 dirs.remove('.hg')
376 376 else:
377 377 dirs[:] = [] # don't descend further
378 378 elif followsym:
379 379 newdirs = []
380 380 for d in dirs:
381 381 fname = os.path.join(root, d)
382 382 if adddir(seen_dirs, fname):
383 383 if os.path.islink(fname):
384 384 for hgname in walkrepos(fname, True, seen_dirs):
385 385 yield hgname
386 386 else:
387 387 newdirs.append(d)
388 388 dirs[:] = newdirs
389 389
390 390 def binnode(ctx):
391 391 """Return binary node id for a given basectx"""
392 392 node = ctx.node()
393 393 if node is None:
394 394 return wdirid
395 395 return node
396 396
397 397 def intrev(ctx):
398 398 """Return integer for a given basectx that can be used in comparison or
399 399 arithmetic operation"""
400 400 rev = ctx.rev()
401 401 if rev is None:
402 402 return wdirrev
403 403 return rev
404 404
405 405 def revsingle(repo, revspec, default='.'):
406 406 if not revspec and revspec != 0:
407 407 return repo[default]
408 408
409 409 l = revrange(repo, [revspec])
410 410 if not l:
411 411 raise error.Abort(_('empty revision set'))
412 412 return repo[l.last()]
413 413
414 414 def _pairspec(revspec):
415 415 tree = revsetlang.parse(revspec)
416 416 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
417 417
418 418 def revpair(repo, revs):
419 419 if not revs:
420 420 return repo.dirstate.p1(), None
421 421
422 422 l = revrange(repo, revs)
423 423
424 424 if not l:
425 425 first = second = None
426 426 elif l.isascending():
427 427 first = l.min()
428 428 second = l.max()
429 429 elif l.isdescending():
430 430 first = l.max()
431 431 second = l.min()
432 432 else:
433 433 first = l.first()
434 434 second = l.last()
435 435
436 436 if first is None:
437 437 raise error.Abort(_('empty revision range'))
438 438 if (first == second and len(revs) >= 2
439 439 and not all(revrange(repo, [r]) for r in revs)):
440 440 raise error.Abort(_('empty revision on one side of range'))
441 441
442 442 # if top-level is range expression, the result must always be a pair
443 443 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
444 444 return repo.lookup(first), None
445 445
446 446 return repo.lookup(first), repo.lookup(second)
447 447
448 448 def revrange(repo, specs):
449 449 """Execute 1 to many revsets and return the union.
450 450
451 451 This is the preferred mechanism for executing revsets using user-specified
452 452 config options, such as revset aliases.
453 453
454 454 The revsets specified by ``specs`` will be executed via a chained ``OR``
455 455 expression. If ``specs`` is empty, an empty result is returned.
456 456
457 457 ``specs`` can contain integers, in which case they are assumed to be
458 458 revision numbers.
459 459
460 460 It is assumed the revsets are already formatted. If you have arguments
461 461 that need to be expanded in the revset, call ``revsetlang.formatspec()``
462 462 and pass the result as an element of ``specs``.
463 463
464 464 Specifying a single revset is allowed.
465 465
466 466 Returns a ``revset.abstractsmartset`` which is a list-like interface over
467 467 integer revisions.
468 468 """
469 469 allspecs = []
470 470 for spec in specs:
471 471 if isinstance(spec, int):
472 472 spec = revsetlang.formatspec('rev(%d)', spec)
473 473 allspecs.append(spec)
474 474 return repo.anyrevs(allspecs, user=True)
475 475
476 476 def meaningfulparents(repo, ctx):
477 477 """Return list of meaningful (or all if debug) parentrevs for rev.
478 478
479 479 For merges (two non-nullrev revisions) both parents are meaningful.
480 480 Otherwise the first parent revision is considered meaningful if it
481 481 is not the preceding revision.
482 482 """
483 483 parents = ctx.parents()
484 484 if len(parents) > 1:
485 485 return parents
486 486 if repo.ui.debugflag:
487 487 return [parents[0], repo['null']]
488 488 if parents[0].rev() >= intrev(ctx) - 1:
489 489 return []
490 490 return parents
491 491
492 492 def expandpats(pats):
493 493 '''Expand bare globs when running on windows.
494 494 On posix we assume it already has already been done by sh.'''
495 495 if not util.expandglobs:
496 496 return list(pats)
497 497 ret = []
498 498 for kindpat in pats:
499 499 kind, pat = matchmod._patsplit(kindpat, None)
500 500 if kind is None:
501 501 try:
502 502 globbed = glob.glob(pat)
503 503 except re.error:
504 504 globbed = [pat]
505 505 if globbed:
506 506 ret.extend(globbed)
507 507 continue
508 508 ret.append(kindpat)
509 509 return ret
510 510
511 511 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
512 512 badfn=None):
513 513 '''Return a matcher and the patterns that were used.
514 514 The matcher will warn about bad matches, unless an alternate badfn callback
515 515 is provided.'''
516 516 if pats == ("",):
517 517 pats = []
518 518 if opts is None:
519 519 opts = {}
520 520 if not globbed and default == 'relpath':
521 521 pats = expandpats(pats or [])
522 522
523 523 def bad(f, msg):
524 524 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
525 525
526 526 if badfn is None:
527 527 badfn = bad
528 528
529 529 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
530 530 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
531 531
532 532 if m.always():
533 533 pats = []
534 534 return m, pats
535 535
536 536 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
537 537 badfn=None):
538 538 '''Return a matcher that will warn about bad matches.'''
539 539 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
540 540
541 541 def matchall(repo):
542 542 '''Return a matcher that will efficiently match everything.'''
543 543 return matchmod.always(repo.root, repo.getcwd())
544 544
545 545 def matchfiles(repo, files, badfn=None):
546 546 '''Return a matcher that will efficiently match exactly these files.'''
547 547 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
548 548
549 549 def origpath(ui, repo, filepath):
550 550 '''customize where .orig files are created
551 551
552 552 Fetch user defined path from config file: [ui] origbackuppath = <path>
553 553 Fall back to default (filepath) if not specified
554 554 '''
555 555 origbackuppath = ui.config('ui', 'origbackuppath')
556 556 if origbackuppath is None:
557 557 return filepath + ".orig"
558 558
559 559 filepathfromroot = os.path.relpath(filepath, start=repo.root)
560 560 fullorigpath = repo.wjoin(origbackuppath, filepathfromroot)
561 561
562 562 origbackupdir = repo.vfs.dirname(fullorigpath)
563 563 if not repo.vfs.exists(origbackupdir):
564 564 ui.note(_('creating directory: %s\n') % origbackupdir)
565 565 util.makedirs(origbackupdir)
566 566
567 567 return fullorigpath + ".orig"
568 568
569 569 class _containsnode(object):
570 570 """proxy __contains__(node) to container.__contains__ which accepts revs"""
571 571
572 572 def __init__(self, repo, revcontainer):
573 573 self._torev = repo.changelog.rev
574 574 self._revcontains = revcontainer.__contains__
575 575
576 576 def __contains__(self, node):
577 577 return self._revcontains(self._torev(node))
578 578
579 579 def cleanupnodes(repo, mapping, operation):
580 580 """do common cleanups when old nodes are replaced by new nodes
581 581
582 582 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
583 583 (we might also want to move working directory parent in the future)
584 584
585 585 mapping is {oldnode: [newnode]} or a iterable of nodes if they do not have
586 586 replacements. operation is a string, like "rebase".
587 587 """
588 588 if not util.safehasattr(mapping, 'items'):
589 589 mapping = {n: () for n in mapping}
590 590
591 591 with repo.transaction('cleanup') as tr:
592 592 # Move bookmarks
593 593 bmarks = repo._bookmarks
594 594 bmarkchanges = []
595 595 allnewnodes = [n for ns in mapping.values() for n in ns]
596 596 for oldnode, newnodes in mapping.items():
597 597 oldbmarks = repo.nodebookmarks(oldnode)
598 598 if not oldbmarks:
599 599 continue
600 600 from . import bookmarks # avoid import cycle
601 601 if len(newnodes) > 1:
602 602 # usually a split, take the one with biggest rev number
603 603 newnode = next(repo.set('max(%ln)', newnodes)).node()
604 604 elif len(newnodes) == 0:
605 605 # move bookmark backwards
606 606 roots = list(repo.set('max((::%n) - %ln)', oldnode,
607 607 list(mapping)))
608 608 if roots:
609 609 newnode = roots[0].node()
610 610 else:
611 611 newnode = nullid
612 612 else:
613 613 newnode = newnodes[0]
614 614 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
615 615 (oldbmarks, hex(oldnode), hex(newnode)))
616 616 # Delete divergent bookmarks being parents of related newnodes
617 617 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
618 618 allnewnodes, newnode, oldnode)
619 619 deletenodes = _containsnode(repo, deleterevs)
620 620 for name in oldbmarks:
621 621 bmarkchanges.append((name, newnode))
622 622 for b in bookmarks.divergent2delete(repo, deletenodes, name):
623 623 bmarkchanges.append((b, None))
624 624
625 625 if bmarkchanges:
626 626 bmarks.applychanges(repo, tr, bmarkchanges)
627 627
628 628 # Obsolete or strip nodes
629 629 if obsolete.isenabled(repo, obsolete.createmarkersopt):
630 630 # If a node is already obsoleted, and we want to obsolete it
631 631 # without a successor, skip that obssolete request since it's
632 632 # unnecessary. That's the "if s or not isobs(n)" check below.
633 633 # Also sort the node in topology order, that might be useful for
634 634 # some obsstore logic.
635 635 # NOTE: the filtering and sorting might belong to createmarkers.
636 636 # Unfiltered repo is needed since nodes in mapping might be hidden.
637 637 unfi = repo.unfiltered()
638 638 isobs = unfi.obsstore.successors.__contains__
639 639 torev = unfi.changelog.rev
640 640 sortfunc = lambda ns: torev(ns[0])
641 641 rels = [(unfi[n], tuple(unfi[m] for m in s))
642 642 for n, s in sorted(mapping.items(), key=sortfunc)
643 643 if s or not isobs(n)]
644 644 obsolete.createmarkers(repo, rels, operation=operation)
645 645 else:
646 646 from . import repair # avoid import cycle
647 647 repair.delayedstrip(repo.ui, repo, list(mapping), operation)
648 648
649 649 def addremove(repo, matcher, prefix, opts=None, dry_run=None, similarity=None):
650 650 if opts is None:
651 651 opts = {}
652 652 m = matcher
653 653 if dry_run is None:
654 654 dry_run = opts.get('dry_run')
655 655 if similarity is None:
656 656 similarity = float(opts.get('similarity') or 0)
657 657
658 658 ret = 0
659 659 join = lambda f: os.path.join(prefix, f)
660 660
661 661 wctx = repo[None]
662 662 for subpath in sorted(wctx.substate):
663 663 submatch = matchmod.subdirmatcher(subpath, m)
664 664 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
665 665 sub = wctx.sub(subpath)
666 666 try:
667 667 if sub.addremove(submatch, prefix, opts, dry_run, similarity):
668 668 ret = 1
669 669 except error.LookupError:
670 670 repo.ui.status(_("skipping missing subrepository: %s\n")
671 671 % join(subpath))
672 672
673 673 rejected = []
674 674 def badfn(f, msg):
675 675 if f in m.files():
676 676 m.bad(f, msg)
677 677 rejected.append(f)
678 678
679 679 badmatch = matchmod.badmatch(m, badfn)
680 680 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
681 681 badmatch)
682 682
683 683 unknownset = set(unknown + forgotten)
684 684 toprint = unknownset.copy()
685 685 toprint.update(deleted)
686 686 for abs in sorted(toprint):
687 687 if repo.ui.verbose or not m.exact(abs):
688 688 if abs in unknownset:
689 689 status = _('adding %s\n') % m.uipath(abs)
690 690 else:
691 691 status = _('removing %s\n') % m.uipath(abs)
692 692 repo.ui.status(status)
693 693
694 694 renames = _findrenames(repo, m, added + unknown, removed + deleted,
695 695 similarity)
696 696
697 697 if not dry_run:
698 698 _markchanges(repo, unknown + forgotten, deleted, renames)
699 699
700 700 for f in rejected:
701 701 if f in m.files():
702 702 return 1
703 703 return ret
704 704
705 705 def marktouched(repo, files, similarity=0.0):
706 706 '''Assert that files have somehow been operated upon. files are relative to
707 707 the repo root.'''
708 708 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
709 709 rejected = []
710 710
711 711 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
712 712
713 713 if repo.ui.verbose:
714 714 unknownset = set(unknown + forgotten)
715 715 toprint = unknownset.copy()
716 716 toprint.update(deleted)
717 717 for abs in sorted(toprint):
718 718 if abs in unknownset:
719 719 status = _('adding %s\n') % abs
720 720 else:
721 721 status = _('removing %s\n') % abs
722 722 repo.ui.status(status)
723 723
724 724 renames = _findrenames(repo, m, added + unknown, removed + deleted,
725 725 similarity)
726 726
727 727 _markchanges(repo, unknown + forgotten, deleted, renames)
728 728
729 729 for f in rejected:
730 730 if f in m.files():
731 731 return 1
732 732 return 0
733 733
734 734 def _interestingfiles(repo, matcher):
735 735 '''Walk dirstate with matcher, looking for files that addremove would care
736 736 about.
737 737
738 738 This is different from dirstate.status because it doesn't care about
739 739 whether files are modified or clean.'''
740 740 added, unknown, deleted, removed, forgotten = [], [], [], [], []
741 741 audit_path = pathutil.pathauditor(repo.root, cached=True)
742 742
743 743 ctx = repo[None]
744 744 dirstate = repo.dirstate
745 745 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
746 746 full=False)
747 747 for abs, st in walkresults.iteritems():
748 748 dstate = dirstate[abs]
749 749 if dstate == '?' and audit_path.check(abs):
750 750 unknown.append(abs)
751 751 elif dstate != 'r' and not st:
752 752 deleted.append(abs)
753 753 elif dstate == 'r' and st:
754 754 forgotten.append(abs)
755 755 # for finding renames
756 756 elif dstate == 'r' and not st:
757 757 removed.append(abs)
758 758 elif dstate == 'a':
759 759 added.append(abs)
760 760
761 761 return added, unknown, deleted, removed, forgotten
762 762
763 763 def _findrenames(repo, matcher, added, removed, similarity):
764 764 '''Find renames from removed files to added ones.'''
765 765 renames = {}
766 766 if similarity > 0:
767 767 for old, new, score in similar.findrenames(repo, added, removed,
768 768 similarity):
769 769 if (repo.ui.verbose or not matcher.exact(old)
770 770 or not matcher.exact(new)):
771 771 repo.ui.status(_('recording removal of %s as rename to %s '
772 772 '(%d%% similar)\n') %
773 773 (matcher.rel(old), matcher.rel(new),
774 774 score * 100))
775 775 renames[new] = old
776 776 return renames
777 777
778 778 def _markchanges(repo, unknown, deleted, renames):
779 779 '''Marks the files in unknown as added, the files in deleted as removed,
780 780 and the files in renames as copied.'''
781 781 wctx = repo[None]
782 782 with repo.wlock():
783 783 wctx.forget(deleted)
784 784 wctx.add(unknown)
785 785 for new, old in renames.iteritems():
786 786 wctx.copy(old, new)
787 787
788 788 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
789 789 """Update the dirstate to reflect the intent of copying src to dst. For
790 790 different reasons it might not end with dst being marked as copied from src.
791 791 """
792 792 origsrc = repo.dirstate.copied(src) or src
793 793 if dst == origsrc: # copying back a copy?
794 794 if repo.dirstate[dst] not in 'mn' and not dryrun:
795 795 repo.dirstate.normallookup(dst)
796 796 else:
797 797 if repo.dirstate[origsrc] == 'a' and origsrc == src:
798 798 if not ui.quiet:
799 799 ui.warn(_("%s has not been committed yet, so no copy "
800 800 "data will be stored for %s.\n")
801 801 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
802 802 if repo.dirstate[dst] in '?r' and not dryrun:
803 803 wctx.add([dst])
804 804 elif not dryrun:
805 805 wctx.copy(origsrc, dst)
806 806
807 807 def readrequires(opener, supported):
808 808 '''Reads and parses .hg/requires and checks if all entries found
809 809 are in the list of supported features.'''
810 810 requirements = set(opener.read("requires").splitlines())
811 811 missings = []
812 812 for r in requirements:
813 813 if r not in supported:
814 814 if not r or not r[0].isalnum():
815 815 raise error.RequirementError(_(".hg/requires file is corrupt"))
816 816 missings.append(r)
817 817 missings.sort()
818 818 if missings:
819 819 raise error.RequirementError(
820 820 _("repository requires features unknown to this Mercurial: %s")
821 821 % " ".join(missings),
822 822 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
823 823 " for more information"))
824 824 return requirements
825 825
826 826 def writerequires(opener, requirements):
827 827 with opener('requires', 'w') as fp:
828 828 for r in sorted(requirements):
829 829 fp.write("%s\n" % r)
830 830
831 831 class filecachesubentry(object):
832 832 def __init__(self, path, stat):
833 833 self.path = path
834 834 self.cachestat = None
835 835 self._cacheable = None
836 836
837 837 if stat:
838 838 self.cachestat = filecachesubentry.stat(self.path)
839 839
840 840 if self.cachestat:
841 841 self._cacheable = self.cachestat.cacheable()
842 842 else:
843 843 # None means we don't know yet
844 844 self._cacheable = None
845 845
846 846 def refresh(self):
847 847 if self.cacheable():
848 848 self.cachestat = filecachesubentry.stat(self.path)
849 849
850 850 def cacheable(self):
851 851 if self._cacheable is not None:
852 852 return self._cacheable
853 853
854 854 # we don't know yet, assume it is for now
855 855 return True
856 856
857 857 def changed(self):
858 858 # no point in going further if we can't cache it
859 859 if not self.cacheable():
860 860 return True
861 861
862 862 newstat = filecachesubentry.stat(self.path)
863 863
864 864 # we may not know if it's cacheable yet, check again now
865 865 if newstat and self._cacheable is None:
866 866 self._cacheable = newstat.cacheable()
867 867
868 868 # check again
869 869 if not self._cacheable:
870 870 return True
871 871
872 872 if self.cachestat != newstat:
873 873 self.cachestat = newstat
874 874 return True
875 875 else:
876 876 return False
877 877
878 878 @staticmethod
879 879 def stat(path):
880 880 try:
881 881 return util.cachestat(path)
882 882 except OSError as e:
883 883 if e.errno != errno.ENOENT:
884 884 raise
885 885
886 886 class filecacheentry(object):
887 887 def __init__(self, paths, stat=True):
888 888 self._entries = []
889 889 for path in paths:
890 890 self._entries.append(filecachesubentry(path, stat))
891 891
892 892 def changed(self):
893 893 '''true if any entry has changed'''
894 894 for entry in self._entries:
895 895 if entry.changed():
896 896 return True
897 897 return False
898 898
899 899 def refresh(self):
900 900 for entry in self._entries:
901 901 entry.refresh()
902 902
903 903 class filecache(object):
904 904 '''A property like decorator that tracks files under .hg/ for updates.
905 905
906 906 Records stat info when called in _filecache.
907 907
908 908 On subsequent calls, compares old stat info with new info, and recreates the
909 909 object when any of the files changes, updating the new stat info in
910 910 _filecache.
911 911
912 912 Mercurial either atomic renames or appends for files under .hg,
913 913 so to ensure the cache is reliable we need the filesystem to be able
914 914 to tell us if a file has been replaced. If it can't, we fallback to
915 915 recreating the object on every call (essentially the same behavior as
916 916 propertycache).
917 917
918 918 '''
919 919 def __init__(self, *paths):
920 920 self.paths = paths
921 921
922 922 def join(self, obj, fname):
923 923 """Used to compute the runtime path of a cached file.
924 924
925 925 Users should subclass filecache and provide their own version of this
926 926 function to call the appropriate join function on 'obj' (an instance
927 927 of the class that its member function was decorated).
928 928 """
929 929 raise NotImplementedError
930 930
931 931 def __call__(self, func):
932 932 self.func = func
933 933 self.name = func.__name__.encode('ascii')
934 934 return self
935 935
936 936 def __get__(self, obj, type=None):
937 937 # if accessed on the class, return the descriptor itself.
938 938 if obj is None:
939 939 return self
940 940 # do we need to check if the file changed?
941 941 if self.name in obj.__dict__:
942 942 assert self.name in obj._filecache, self.name
943 943 return obj.__dict__[self.name]
944 944
945 945 entry = obj._filecache.get(self.name)
946 946
947 947 if entry:
948 948 if entry.changed():
949 949 entry.obj = self.func(obj)
950 950 else:
951 951 paths = [self.join(obj, path) for path in self.paths]
952 952
953 953 # We stat -before- creating the object so our cache doesn't lie if
954 954 # a writer modified between the time we read and stat
955 955 entry = filecacheentry(paths, True)
956 956 entry.obj = self.func(obj)
957 957
958 958 obj._filecache[self.name] = entry
959 959
960 960 obj.__dict__[self.name] = entry.obj
961 961 return entry.obj
962 962
963 963 def __set__(self, obj, value):
964 964 if self.name not in obj._filecache:
965 965 # we add an entry for the missing value because X in __dict__
966 966 # implies X in _filecache
967 967 paths = [self.join(obj, path) for path in self.paths]
968 968 ce = filecacheentry(paths, False)
969 969 obj._filecache[self.name] = ce
970 970 else:
971 971 ce = obj._filecache[self.name]
972 972
973 973 ce.obj = value # update cached copy
974 974 obj.__dict__[self.name] = value # update copy returned by obj.x
975 975
976 976 def __delete__(self, obj):
977 977 try:
978 978 del obj.__dict__[self.name]
979 979 except KeyError:
980 980 raise AttributeError(self.name)
981 981
982 982 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
983 983 if lock is None:
984 984 raise error.LockInheritanceContractViolation(
985 985 'lock can only be inherited while held')
986 986 if environ is None:
987 987 environ = {}
988 988 with lock.inherit() as locker:
989 989 environ[envvar] = locker
990 990 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
991 991
992 992 def wlocksub(repo, cmd, *args, **kwargs):
993 993 """run cmd as a subprocess that allows inheriting repo's wlock
994 994
995 995 This can only be called while the wlock is held. This takes all the
996 996 arguments that ui.system does, and returns the exit code of the
997 997 subprocess."""
998 998 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
999 999 **kwargs)
1000 1000
1001 1001 def gdinitconfig(ui):
1002 1002 """helper function to know if a repo should be created as general delta
1003 1003 """
1004 1004 # experimental config: format.generaldelta
1005 1005 return (ui.configbool('format', 'generaldelta')
1006 1006 or ui.configbool('format', 'usegeneraldelta'))
1007 1007
1008 1008 def gddeltaconfig(ui):
1009 1009 """helper function to know if incoming delta should be optimised
1010 1010 """
1011 1011 # experimental config: format.generaldelta
1012 1012 return ui.configbool('format', 'generaldelta')
1013 1013
1014 1014 class simplekeyvaluefile(object):
1015 1015 """A simple file with key=value lines
1016 1016
1017 1017 Keys must be alphanumerics and start with a letter, values must not
1018 1018 contain '\n' characters"""
1019 1019 firstlinekey = '__firstline'
1020 1020
1021 1021 def __init__(self, vfs, path, keys=None):
1022 1022 self.vfs = vfs
1023 1023 self.path = path
1024 1024
1025 1025 def read(self, firstlinenonkeyval=False):
1026 1026 """Read the contents of a simple key-value file
1027 1027
1028 1028 'firstlinenonkeyval' indicates whether the first line of file should
1029 1029 be treated as a key-value pair or reuturned fully under the
1030 1030 __firstline key."""
1031 1031 lines = self.vfs.readlines(self.path)
1032 1032 d = {}
1033 1033 if firstlinenonkeyval:
1034 1034 if not lines:
1035 1035 e = _("empty simplekeyvalue file")
1036 1036 raise error.CorruptedState(e)
1037 1037 # we don't want to include '\n' in the __firstline
1038 1038 d[self.firstlinekey] = lines[0][:-1]
1039 1039 del lines[0]
1040 1040
1041 1041 try:
1042 1042 # the 'if line.strip()' part prevents us from failing on empty
1043 1043 # lines which only contain '\n' therefore are not skipped
1044 1044 # by 'if line'
1045 1045 updatedict = dict(line[:-1].split('=', 1) for line in lines
1046 1046 if line.strip())
1047 1047 if self.firstlinekey in updatedict:
1048 1048 e = _("%r can't be used as a key")
1049 1049 raise error.CorruptedState(e % self.firstlinekey)
1050 1050 d.update(updatedict)
1051 1051 except ValueError as e:
1052 1052 raise error.CorruptedState(str(e))
1053 1053 return d
1054 1054
1055 1055 def write(self, data, firstline=None):
1056 1056 """Write key=>value mapping to a file
1057 1057 data is a dict. Keys must be alphanumerical and start with a letter.
1058 1058 Values must not contain newline characters.
1059 1059
1060 1060 If 'firstline' is not None, it is written to file before
1061 1061 everything else, as it is, not in a key=value form"""
1062 1062 lines = []
1063 1063 if firstline is not None:
1064 1064 lines.append('%s\n' % firstline)
1065 1065
1066 1066 for k, v in data.items():
1067 1067 if k == self.firstlinekey:
1068 1068 e = "key name '%s' is reserved" % self.firstlinekey
1069 1069 raise error.ProgrammingError(e)
1070 1070 if not k[0].isalpha():
1071 1071 e = "keys must start with a letter in a key-value file"
1072 1072 raise error.ProgrammingError(e)
1073 1073 if not k.isalnum():
1074 1074 e = "invalid key name in a simple key-value file"
1075 1075 raise error.ProgrammingError(e)
1076 1076 if '\n' in v:
1077 1077 e = "invalid value in a simple key-value file"
1078 1078 raise error.ProgrammingError(e)
1079 1079 lines.append("%s=%s\n" % (k, v))
1080 1080 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1081 1081 fp.write(''.join(lines))
1082 1082
1083 1083 _reportobsoletedsource = [
1084 1084 'debugobsolete',
1085 1085 'pull',
1086 1086 'push',
1087 1087 'serve',
1088 1088 'unbundle',
1089 1089 ]
1090 1090
1091 1091 def registersummarycallback(repo, otr, txnname=''):
1092 1092 """register a callback to issue a summary after the transaction is closed
1093 1093 """
1094 1094 for source in _reportobsoletedsource:
1095 1095 if txnname.startswith(source):
1096 1096 reporef = weakref.ref(repo)
1097 1097 def reportsummary(tr):
1098 1098 """the actual callback reporting the summary"""
1099 1099 repo = reporef()
1100 1100 obsoleted = obsutil.getobsoleted(repo, tr)
1101 1101 if obsoleted:
1102 1102 repo.ui.status(_('obsoleted %i changesets\n')
1103 1103 % len(obsoleted))
1104 1104 otr.addpostclose('00-txnreport', reportsummary)
1105 1105 break
@@ -1,251 +1,251
1 1 $ hg init a
2 2 $ cd a
3 3 $ echo a > a
4 4 $ hg add -n
5 5 adding a
6 6 $ hg st
7 7 ? a
8 8 $ hg add
9 9 adding a
10 10 $ hg st
11 11 A a
12 12 $ hg forget a
13 13 $ hg add
14 14 adding a
15 15 $ hg st
16 16 A a
17 17 $ mkdir dir
18 18 $ cd dir
19 19 $ hg add ../a
20 20 ../a already tracked!
21 21 $ cd ..
22 22
23 23 $ echo b > b
24 24 $ hg add -n b
25 25 $ hg st
26 26 A a
27 27 ? b
28 28 $ hg add b
29 29 $ hg st
30 30 A a
31 31 A b
32 32
33 33 should fail
34 34
35 35 $ hg add b
36 36 b already tracked!
37 37 $ hg st
38 38 A a
39 39 A b
40 40
41 41 #if no-windows
42 42 $ echo foo > con.xml
43 43 $ hg --config ui.portablefilenames=jump add con.xml
44 44 abort: ui.portablefilenames value is invalid ('jump')
45 45 [255]
46 46 $ hg --config ui.portablefilenames=abort add con.xml
47 abort: filename contains 'con', which is reserved on Windows: 'con.xml'
47 abort: filename contains 'con', which is reserved on Windows: con.xml
48 48 [255]
49 49 $ hg st
50 50 A a
51 51 A b
52 52 ? con.xml
53 53 $ hg add con.xml
54 warning: filename contains 'con', which is reserved on Windows: 'con.xml'
54 warning: filename contains 'con', which is reserved on Windows: con.xml
55 55 $ hg st
56 56 A a
57 57 A b
58 58 A con.xml
59 59 $ hg forget con.xml
60 60 $ rm con.xml
61 61 #endif
62 62
63 63 #if eol-in-paths
64 64 $ echo bla > 'hello:world'
65 65 $ hg --config ui.portablefilenames=abort add
66 66 adding hello:world
67 67 abort: filename contains ':', which is reserved on Windows: 'hello:world'
68 68 [255]
69 69 $ hg st
70 70 A a
71 71 A b
72 72 ? hello:world
73 73 $ hg --config ui.portablefilenames=ignore add
74 74 adding hello:world
75 75 $ hg st
76 76 A a
77 77 A b
78 78 A hello:world
79 79 #endif
80 80
81 81 $ hg ci -m 0 --traceback
82 82
83 83 $ hg log -r "heads(. or wdir() & file('**'))"
84 84 changeset: 0:* (glob)
85 85 tag: tip
86 86 user: test
87 87 date: Thu Jan 01 00:00:00 1970 +0000
88 88 summary: 0
89 89
90 90 should fail
91 91
92 92 $ hg add a
93 93 a already tracked!
94 94
95 95 $ echo aa > a
96 96 $ hg ci -m 1
97 97 $ hg up 0
98 98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 99 $ echo aaa > a
100 100 $ hg ci -m 2
101 101 created new head
102 102
103 103 $ hg merge
104 104 merging a
105 105 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
106 106 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
107 107 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
108 108 [1]
109 109 $ hg st
110 110 M a
111 111 ? a.orig
112 112
113 113 wdir doesn't cause a crash, and can be dynamically selected if dirty
114 114
115 115 $ hg log -r "heads(. or wdir() & file('**'))"
116 116 changeset: 2147483647:ffffffffffff
117 117 parent: 2:* (glob)
118 118 parent: 1:* (glob)
119 119 user: test
120 120 date: * (glob)
121 121
122 122 should fail
123 123
124 124 $ hg add a
125 125 a already tracked!
126 126 $ hg st
127 127 M a
128 128 ? a.orig
129 129 $ hg resolve -m a
130 130 (no more unresolved files)
131 131 $ hg ci -m merge
132 132
133 133 Issue683: peculiarity with hg revert of an removed then added file
134 134
135 135 $ hg forget a
136 136 $ hg add a
137 137 $ hg st
138 138 ? a.orig
139 139 $ hg rm a
140 140 $ hg st
141 141 R a
142 142 ? a.orig
143 143 $ echo a > a
144 144 $ hg add a
145 145 $ hg st
146 146 M a
147 147 ? a.orig
148 148
149 149 Forgotten file can be added back (as either clean or modified)
150 150
151 151 $ hg forget b
152 152 $ hg add b
153 153 $ hg st -A b
154 154 C b
155 155 $ hg forget b
156 156 $ echo modified > b
157 157 $ hg add b
158 158 $ hg st -A b
159 159 M b
160 160 $ hg revert -qC b
161 161
162 162 $ hg add c && echo "unexpected addition of missing file"
163 163 c: * (glob)
164 164 [1]
165 165 $ echo c > c
166 166 $ hg add d c && echo "unexpected addition of missing file"
167 167 d: * (glob)
168 168 [1]
169 169 $ hg st
170 170 M a
171 171 A c
172 172 ? a.orig
173 173 $ hg up -C
174 174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 175
176 176 forget and get should have the right order: added but missing dir should be
177 177 forgotten before file with same name is added
178 178
179 179 $ echo file d > d
180 180 $ hg add d
181 181 $ hg ci -md
182 182 $ hg rm d
183 183 $ mkdir d
184 184 $ echo a > d/a
185 185 $ hg add d/a
186 186 $ rm -r d
187 187 $ hg up -C
188 188 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 189 $ cat d
190 190 file d
191 191
192 192 Test that adding a directory doesn't require case matching (issue4578)
193 193 #if icasefs
194 194 $ mkdir -p CapsDir1/CapsDir
195 195 $ echo abc > CapsDir1/CapsDir/AbC.txt
196 196 $ mkdir CapsDir1/CapsDir/SubDir
197 197 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
198 198
199 199 $ hg add capsdir1/capsdir
200 200 adding CapsDir1/CapsDir/AbC.txt (glob)
201 201 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
202 202
203 203 $ hg forget capsdir1/capsdir/abc.txt
204 204
205 205 $ hg forget capsdir1/capsdir
206 206 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
207 207
208 208 $ hg add capsdir1
209 209 adding CapsDir1/CapsDir/AbC.txt (glob)
210 210 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
211 211
212 212 $ hg ci -m "AbCDef" capsdir1/capsdir
213 213
214 214 $ hg status -A capsdir1/capsdir
215 215 C CapsDir1/CapsDir/AbC.txt
216 216 C CapsDir1/CapsDir/SubDir/Def.txt
217 217
218 218 $ hg files capsdir1/capsdir
219 219 CapsDir1/CapsDir/AbC.txt (glob)
220 220 CapsDir1/CapsDir/SubDir/Def.txt (glob)
221 221
222 222 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
223 223 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
224 224
225 225 $ hg revert -r '.^' capsdir1/capsdir
226 226 reverting CapsDir1/CapsDir/SubDir/Def.txt (glob)
227 227
228 228 The conditional tests above mean the hash on the diff line differs on Windows
229 229 and OS X
230 230 $ hg diff capsdir1/capsdir
231 231 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
232 232 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
233 233 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * (glob)
234 234 @@ -1,1 +1,1 @@
235 235 -xyz
236 236 +def
237 237
238 238 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
239 239 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
240 240
241 241 $ hg status -A capsdir1/capsdir
242 242 M CapsDir1/CapsDir/SubDir/Def.txt
243 243 C CapsDir1/CapsDir/ABC.txt
244 244
245 245 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
246 246 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
247 247 removing CapsDir1/CapsDir/ABC.txt (glob)
248 248 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
249 249 #endif
250 250
251 251 $ cd ..
@@ -1,250 +1,250
1 1 # enable bundle2 in advance
2 2
3 3 $ cat << EOF >> $HGRCPATH
4 4 > [format]
5 5 > usegeneraldelta=yes
6 6 > EOF
7 7
8 8 $ mkdir part1
9 9 $ cd part1
10 10
11 11 $ hg init
12 12 $ echo a > a
13 13 $ hg add a
14 14 $ hg commit -m "1"
15 15 $ hg status
16 16 $ hg copy a b
17 17 $ hg --config ui.portablefilenames=abort copy a con.xml
18 abort: filename contains 'con', which is reserved on Windows: 'con.xml'
18 abort: filename contains 'con', which is reserved on Windows: con.xml
19 19 [255]
20 20 $ hg status
21 21 A b
22 22 $ hg sum
23 23 parent: 0:c19d34741b0a tip
24 24 1
25 25 branch: default
26 26 commit: 1 copied
27 27 update: (current)
28 28 phases: 1 draft
29 29 $ hg --debug commit -m "2"
30 30 committing files:
31 31 b
32 32 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
33 33 committing manifest
34 34 committing changelog
35 35 updating the branch cache
36 36 committed changeset 1:93580a2c28a50a56f63526fb305067e6fbf739c4
37 37
38 38 we should see two history entries
39 39
40 40 $ hg history -v
41 41 changeset: 1:93580a2c28a5
42 42 tag: tip
43 43 user: test
44 44 date: Thu Jan 01 00:00:00 1970 +0000
45 45 files: b
46 46 description:
47 47 2
48 48
49 49
50 50 changeset: 0:c19d34741b0a
51 51 user: test
52 52 date: Thu Jan 01 00:00:00 1970 +0000
53 53 files: a
54 54 description:
55 55 1
56 56
57 57
58 58
59 59 we should see one log entry for a
60 60
61 61 $ hg log a
62 62 changeset: 0:c19d34741b0a
63 63 user: test
64 64 date: Thu Jan 01 00:00:00 1970 +0000
65 65 summary: 1
66 66
67 67
68 68 this should show a revision linked to changeset 0
69 69
70 70 $ hg debugindex a
71 71 rev offset length ..... linkrev nodeid p1 p2 (re)
72 72 0 0 3 ..... 0 b789fdd96dc2 000000000000 000000000000 (re)
73 73
74 74 we should see one log entry for b
75 75
76 76 $ hg log b
77 77 changeset: 1:93580a2c28a5
78 78 tag: tip
79 79 user: test
80 80 date: Thu Jan 01 00:00:00 1970 +0000
81 81 summary: 2
82 82
83 83
84 84 this should show a revision linked to changeset 1
85 85
86 86 $ hg debugindex b
87 87 rev offset length ..... linkrev nodeid p1 p2 (re)
88 88 0 0 65 ..... 1 37d9b5d994ea 000000000000 000000000000 (re)
89 89
90 90 this should show the rename information in the metadata
91 91
92 92 $ hg debugdata b 0 | head -3 | tail -2
93 93 copy: a
94 94 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
95 95
96 96 $ md5sum.py .hg/store/data/b.i
97 97 44913824c8f5890ae218f9829535922e .hg/store/data/b.i
98 98 $ hg cat b > bsum
99 99 $ md5sum.py bsum
100 100 60b725f10c9c85c70d97880dfe8191b3 bsum
101 101 $ hg cat a > asum
102 102 $ md5sum.py asum
103 103 60b725f10c9c85c70d97880dfe8191b3 asum
104 104 $ hg verify
105 105 checking changesets
106 106 checking manifests
107 107 crosschecking files in changesets and manifests
108 108 checking files
109 109 2 files, 2 changesets, 2 total revisions
110 110
111 111 $ cd ..
112 112
113 113
114 114 $ mkdir part2
115 115 $ cd part2
116 116
117 117 $ hg init
118 118 $ echo foo > foo
119 119 should fail - foo is not managed
120 120 $ hg mv foo bar
121 121 foo: not copying - file is not managed
122 122 abort: no files to copy
123 123 [255]
124 124 $ hg st -A
125 125 ? foo
126 126 $ hg add foo
127 127 dry-run; print a warning that this is not a real copy; foo is added
128 128 $ hg mv --dry-run foo bar
129 129 foo has not been committed yet, so no copy data will be stored for bar.
130 130 $ hg st -A
131 131 A foo
132 132 should print a warning that this is not a real copy; bar is added
133 133 $ hg mv foo bar
134 134 foo has not been committed yet, so no copy data will be stored for bar.
135 135 $ hg st -A
136 136 A bar
137 137 should print a warning that this is not a real copy; foo is added
138 138 $ hg cp bar foo
139 139 bar has not been committed yet, so no copy data will be stored for foo.
140 140 $ hg rm -f bar
141 141 $ rm bar
142 142 $ hg st -A
143 143 A foo
144 144 $ hg commit -m1
145 145
146 146 moving a missing file
147 147 $ rm foo
148 148 $ hg mv foo foo3
149 149 foo: deleted in working directory
150 150 foo3 does not exist!
151 151 $ hg up -qC .
152 152
153 153 copy --after to a nonexistent target filename
154 154 $ hg cp -A foo dummy
155 155 foo: not recording copy - dummy does not exist
156 156
157 157 dry-run; should show that foo is clean
158 158 $ hg copy --dry-run foo bar
159 159 $ hg st -A
160 160 C foo
161 161 should show copy
162 162 $ hg copy foo bar
163 163 $ hg st -C
164 164 A bar
165 165 foo
166 166
167 167 shouldn't show copy
168 168 $ hg commit -m2
169 169 $ hg st -C
170 170
171 171 should match
172 172 $ hg debugindex foo
173 173 rev offset length ..... linkrev nodeid p1 p2 (re)
174 174 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re)
175 175 $ hg debugrename bar
176 176 bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
177 177
178 178 $ echo bleah > foo
179 179 $ echo quux > bar
180 180 $ hg commit -m3
181 181
182 182 should not be renamed
183 183 $ hg debugrename bar
184 184 bar not renamed
185 185
186 186 $ hg copy -f foo bar
187 187 should show copy
188 188 $ hg st -C
189 189 M bar
190 190 foo
191 191
192 192 XXX: filtering lfilesrepo.status() in 3.3-rc causes the copy source to not be
193 193 displayed.
194 194 $ hg st -C --config extensions.largefiles=
195 195 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
196 196 M bar
197 197 foo
198 198
199 199 $ hg commit -m3
200 200
201 201 should show no parents for tip
202 202 $ hg debugindex bar
203 203 rev offset length ..... linkrev nodeid p1 p2 (re)
204 204 0 0 69 ..... 1 7711d36246cc 000000000000 000000000000 (re)
205 205 1 69 6 ..... 2 bdf70a2b8d03 7711d36246cc 000000000000 (re)
206 206 2 75 71 ..... 3 b2558327ea8d 000000000000 000000000000 (re)
207 207 should match
208 208 $ hg debugindex foo
209 209 rev offset length ..... linkrev nodeid p1 p2 (re)
210 210 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re)
211 211 1 5 7 ..... 2 dd12c926cf16 2ed2a3912a0b 000000000000 (re)
212 212 $ hg debugrename bar
213 213 bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17
214 214
215 215 should show no copies
216 216 $ hg st -C
217 217
218 218 copy --after on an added file
219 219 $ cp bar baz
220 220 $ hg add baz
221 221 $ hg cp -A bar baz
222 222 $ hg st -C
223 223 A baz
224 224 bar
225 225
226 226 foo was clean:
227 227 $ hg st -AC foo
228 228 C foo
229 229 Trying to copy on top of an existing file fails,
230 230 $ hg copy -A bar foo
231 231 foo: not overwriting - file already committed
232 232 (hg copy --after --force to replace the file by recording a copy)
233 233 same error without the --after, so the user doesn't have to go through
234 234 two hints:
235 235 $ hg copy bar foo
236 236 foo: not overwriting - file already committed
237 237 (hg copy --force to replace the file by recording a copy)
238 238 but it's considered modified after a copy --after --force
239 239 $ hg copy -Af bar foo
240 240 $ hg st -AC foo
241 241 M foo
242 242 bar
243 243 The hint for a file that exists but is not in file history doesn't
244 244 mention --force:
245 245 $ touch xyzzy
246 246 $ hg cp bar xyzzy
247 247 xyzzy: not overwriting - file exists
248 248 (hg copy --after to record the copy)
249 249
250 250 $ cd ..
@@ -1,659 +1,659
1 1 $ hg init
2 2 $ mkdir d1 d1/d11 d2
3 3 $ echo d1/a > d1/a
4 4 $ echo d1/ba > d1/ba
5 5 $ echo d1/a1 > d1/d11/a1
6 6 $ echo d1/b > d1/b
7 7 $ echo d2/b > d2/b
8 8 $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
9 9 $ hg commit -m "1"
10 10
11 11 rename a single file
12 12
13 13 $ hg rename d1/d11/a1 d2/c
14 14 $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
15 abort: filename contains 'con', which is reserved on Windows: 'd1/con.xml'
15 abort: filename contains 'con', which is reserved on Windows: d1/con.xml
16 16 [255]
17 17 $ hg sum
18 18 parent: 0:9b4b6e7b2c26 tip
19 19 1
20 20 branch: default
21 21 commit: 1 renamed
22 22 update: (current)
23 23 phases: 1 draft
24 24 $ hg status -C
25 25 A d2/c
26 26 d1/d11/a1
27 27 R d1/d11/a1
28 28 $ hg update -C
29 29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 30 $ rm d2/c
31 31
32 32 rename a single file using absolute paths
33 33
34 34 $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
35 35 $ hg status -C
36 36 A d2/c
37 37 d1/d11/a1
38 38 R d1/d11/a1
39 39 $ hg update -C
40 40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 41 $ rm d2/c
42 42
43 43 rename --after a single file
44 44
45 45 $ mv d1/d11/a1 d2/c
46 46 $ hg rename --after d1/d11/a1 d2/c
47 47 $ hg status -C
48 48 A d2/c
49 49 d1/d11/a1
50 50 R d1/d11/a1
51 51 $ hg update -C
52 52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 53 $ rm d2/c
54 54
55 55 rename --after a single file when src and tgt already tracked
56 56
57 57 $ mv d1/d11/a1 d2/c
58 58 $ hg addrem -s 0
59 59 removing d1/d11/a1
60 60 adding d2/c
61 61 $ hg rename --after d1/d11/a1 d2/c
62 62 $ hg status -C
63 63 A d2/c
64 64 d1/d11/a1
65 65 R d1/d11/a1
66 66 $ hg update -C
67 67 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 68 $ rm d2/c
69 69
70 70 rename --after a single file to a nonexistent target filename
71 71
72 72 $ hg rename --after d1/a dummy
73 73 d1/a: not recording move - dummy does not exist (glob)
74 74
75 75 move a single file to an existing directory
76 76
77 77 $ hg rename d1/d11/a1 d2
78 78 $ hg status -C
79 79 A d2/a1
80 80 d1/d11/a1
81 81 R d1/d11/a1
82 82 $ hg update -C
83 83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 84 $ rm d2/a1
85 85
86 86 move --after a single file to an existing directory
87 87
88 88 $ mv d1/d11/a1 d2
89 89 $ hg rename --after d1/d11/a1 d2
90 90 $ hg status -C
91 91 A d2/a1
92 92 d1/d11/a1
93 93 R d1/d11/a1
94 94 $ hg update -C
95 95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 96 $ rm d2/a1
97 97
98 98 rename a file using a relative path
99 99
100 100 $ (cd d1/d11; hg rename ../../d2/b e)
101 101 $ hg status -C
102 102 A d1/d11/e
103 103 d2/b
104 104 R d2/b
105 105 $ hg update -C
106 106 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 107 $ rm d1/d11/e
108 108
109 109 rename --after a file using a relative path
110 110
111 111 $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
112 112 $ hg status -C
113 113 A d1/d11/e
114 114 d2/b
115 115 R d2/b
116 116 $ hg update -C
117 117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 $ rm d1/d11/e
119 119
120 120 rename directory d1 as d3
121 121
122 122 $ hg rename d1/ d3
123 123 moving d1/a to d3/a (glob)
124 124 moving d1/b to d3/b (glob)
125 125 moving d1/ba to d3/ba (glob)
126 126 moving d1/d11/a1 to d3/d11/a1 (glob)
127 127 $ hg status -C
128 128 A d3/a
129 129 d1/a
130 130 A d3/b
131 131 d1/b
132 132 A d3/ba
133 133 d1/ba
134 134 A d3/d11/a1
135 135 d1/d11/a1
136 136 R d1/a
137 137 R d1/b
138 138 R d1/ba
139 139 R d1/d11/a1
140 140 $ hg update -C
141 141 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142 $ rm -rf d3
143 143
144 144 rename --after directory d1 as d3
145 145
146 146 $ mv d1 d3
147 147 $ hg rename --after d1 d3
148 148 moving d1/a to d3/a (glob)
149 149 moving d1/b to d3/b (glob)
150 150 moving d1/ba to d3/ba (glob)
151 151 moving d1/d11/a1 to d3/d11/a1 (glob)
152 152 $ hg status -C
153 153 A d3/a
154 154 d1/a
155 155 A d3/b
156 156 d1/b
157 157 A d3/ba
158 158 d1/ba
159 159 A d3/d11/a1
160 160 d1/d11/a1
161 161 R d1/a
162 162 R d1/b
163 163 R d1/ba
164 164 R d1/d11/a1
165 165 $ hg update -C
166 166 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 167 $ rm -rf d3
168 168
169 169 move a directory using a relative path
170 170
171 171 $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
172 172 moving ../d1/d11/a1 to d3/d11/a1 (glob)
173 173 $ hg status -C
174 174 A d2/d3/d11/a1
175 175 d1/d11/a1
176 176 R d1/d11/a1
177 177 $ hg update -C
178 178 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 179 $ rm -rf d2/d3
180 180
181 181 move --after a directory using a relative path
182 182
183 183 $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
184 184 moving ../d1/d11/a1 to d3/d11/a1 (glob)
185 185 $ hg status -C
186 186 A d2/d3/d11/a1
187 187 d1/d11/a1
188 188 R d1/d11/a1
189 189 $ hg update -C
190 190 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 191 $ rm -rf d2/d3
192 192
193 193 move directory d1/d11 to an existing directory d2 (removes empty d1)
194 194
195 195 $ hg rename d1/d11/ d2
196 196 moving d1/d11/a1 to d2/d11/a1 (glob)
197 197 $ hg status -C
198 198 A d2/d11/a1
199 199 d1/d11/a1
200 200 R d1/d11/a1
201 201 $ hg update -C
202 202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 203 $ rm -rf d2/d11
204 204
205 205 move directories d1 and d2 to a new directory d3
206 206
207 207 $ mkdir d3
208 208 $ hg rename d1 d2 d3
209 209 moving d1/a to d3/d1/a (glob)
210 210 moving d1/b to d3/d1/b (glob)
211 211 moving d1/ba to d3/d1/ba (glob)
212 212 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
213 213 moving d2/b to d3/d2/b (glob)
214 214 $ hg status -C
215 215 A d3/d1/a
216 216 d1/a
217 217 A d3/d1/b
218 218 d1/b
219 219 A d3/d1/ba
220 220 d1/ba
221 221 A d3/d1/d11/a1
222 222 d1/d11/a1
223 223 A d3/d2/b
224 224 d2/b
225 225 R d1/a
226 226 R d1/b
227 227 R d1/ba
228 228 R d1/d11/a1
229 229 R d2/b
230 230 $ hg update -C
231 231 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 232 $ rm -rf d3
233 233
234 234 move --after directories d1 and d2 to a new directory d3
235 235
236 236 $ mkdir d3
237 237 $ mv d1 d2 d3
238 238 $ hg rename --after d1 d2 d3
239 239 moving d1/a to d3/d1/a (glob)
240 240 moving d1/b to d3/d1/b (glob)
241 241 moving d1/ba to d3/d1/ba (glob)
242 242 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
243 243 moving d2/b to d3/d2/b (glob)
244 244 $ hg status -C
245 245 A d3/d1/a
246 246 d1/a
247 247 A d3/d1/b
248 248 d1/b
249 249 A d3/d1/ba
250 250 d1/ba
251 251 A d3/d1/d11/a1
252 252 d1/d11/a1
253 253 A d3/d2/b
254 254 d2/b
255 255 R d1/a
256 256 R d1/b
257 257 R d1/ba
258 258 R d1/d11/a1
259 259 R d2/b
260 260 $ hg update -C
261 261 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
262 262 $ rm -rf d3
263 263
264 264 move everything under directory d1 to existing directory d2, do not
265 265 overwrite existing files (d2/b)
266 266
267 267 $ hg rename d1/* d2
268 268 d2/b: not overwriting - file already committed
269 269 (hg rename --force to replace the file by recording a rename)
270 270 moving d1/d11/a1 to d2/d11/a1 (glob)
271 271 $ hg status -C
272 272 A d2/a
273 273 d1/a
274 274 A d2/ba
275 275 d1/ba
276 276 A d2/d11/a1
277 277 d1/d11/a1
278 278 R d1/a
279 279 R d1/ba
280 280 R d1/d11/a1
281 281 $ diff -u d1/b d2/b
282 282 --- d1/b * (glob)
283 283 +++ d2/b * (glob)
284 284 @@ * (glob)
285 285 -d1/b
286 286 +d2/b
287 287 [1]
288 288 $ hg update -C
289 289 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 290 $ rm d2/a d2/ba d2/d11/a1
291 291
292 292 attempt to move one file into a non-existent directory
293 293
294 294 $ hg rename d1/a dx/
295 295 abort: destination dx/ is not a directory
296 296 [255]
297 297 $ hg status -C
298 298 $ hg update -C
299 299 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 300
301 301 attempt to move potentially more than one file into a non-existent directory
302 302
303 303 $ hg rename 'glob:d1/**' dx
304 304 abort: with multiple sources, destination must be an existing directory
305 305 [255]
306 306
307 307 move every file under d1 to d2/d21 (glob)
308 308
309 309 $ mkdir d2/d21
310 310 $ hg rename 'glob:d1/**' d2/d21
311 311 moving d1/a to d2/d21/a (glob)
312 312 moving d1/b to d2/d21/b (glob)
313 313 moving d1/ba to d2/d21/ba (glob)
314 314 moving d1/d11/a1 to d2/d21/a1 (glob)
315 315 $ hg status -C
316 316 A d2/d21/a
317 317 d1/a
318 318 A d2/d21/a1
319 319 d1/d11/a1
320 320 A d2/d21/b
321 321 d1/b
322 322 A d2/d21/ba
323 323 d1/ba
324 324 R d1/a
325 325 R d1/b
326 326 R d1/ba
327 327 R d1/d11/a1
328 328 $ hg update -C
329 329 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 330 $ rm -rf d2/d21
331 331
332 332 move --after some files under d1 to d2/d21 (glob)
333 333
334 334 $ mkdir d2/d21
335 335 $ mv d1/a d1/d11/a1 d2/d21
336 336 $ hg rename --after 'glob:d1/**' d2/d21
337 337 moving d1/a to d2/d21/a (glob)
338 338 d1/b: not recording move - d2/d21/b does not exist (glob)
339 339 d1/ba: not recording move - d2/d21/ba does not exist (glob)
340 340 moving d1/d11/a1 to d2/d21/a1 (glob)
341 341 $ hg status -C
342 342 A d2/d21/a
343 343 d1/a
344 344 A d2/d21/a1
345 345 d1/d11/a1
346 346 R d1/a
347 347 R d1/d11/a1
348 348 $ hg update -C
349 349 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 350 $ rm -rf d2/d21
351 351
352 352 move every file under d1 starting with an 'a' to d2/d21 (regexp)
353 353
354 354 $ mkdir d2/d21
355 355 $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
356 356 moving d1/a to d2/d21/a (glob)
357 357 moving d1/d11/a1 to d2/d21/a1 (glob)
358 358 $ hg status -C
359 359 A d2/d21/a
360 360 d1/a
361 361 A d2/d21/a1
362 362 d1/d11/a1
363 363 R d1/a
364 364 R d1/d11/a1
365 365 $ hg update -C
366 366 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 367 $ rm -rf d2/d21
368 368
369 369 attempt to overwrite an existing file
370 370
371 371 $ echo "ca" > d1/ca
372 372 $ hg rename d1/ba d1/ca
373 373 d1/ca: not overwriting - file exists
374 374 (hg rename --after to record the rename)
375 375 $ hg status -C
376 376 ? d1/ca
377 377 $ hg update -C
378 378 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 379
380 380 forced overwrite of an existing file
381 381
382 382 $ echo "ca" > d1/ca
383 383 $ hg rename --force d1/ba d1/ca
384 384 $ hg status -C
385 385 A d1/ca
386 386 d1/ba
387 387 R d1/ba
388 388 $ hg update -C
389 389 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
390 390 $ rm d1/ca
391 391
392 392 attempt to overwrite an existing broken symlink
393 393
394 394 #if symlink
395 395 $ ln -s ba d1/ca
396 396 $ hg rename --traceback d1/ba d1/ca
397 397 d1/ca: not overwriting - file exists
398 398 (hg rename --after to record the rename)
399 399 $ hg status -C
400 400 ? d1/ca
401 401 $ hg update -C
402 402 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 403 $ rm d1/ca
404 404
405 405 replace a symlink with a file
406 406
407 407 $ ln -s ba d1/ca
408 408 $ hg rename --force d1/ba d1/ca
409 409 $ hg status -C
410 410 A d1/ca
411 411 d1/ba
412 412 R d1/ba
413 413 $ hg update -C
414 414 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
415 415 $ rm d1/ca
416 416 #endif
417 417
418 418 do not copy more than one source file to the same destination file
419 419
420 420 $ mkdir d3
421 421 $ hg rename d1/* d2/* d3
422 422 moving d1/d11/a1 to d3/d11/a1 (glob)
423 423 d3/b: not overwriting - d2/b collides with d1/b
424 424 $ hg status -C
425 425 A d3/a
426 426 d1/a
427 427 A d3/b
428 428 d1/b
429 429 A d3/ba
430 430 d1/ba
431 431 A d3/d11/a1
432 432 d1/d11/a1
433 433 R d1/a
434 434 R d1/b
435 435 R d1/ba
436 436 R d1/d11/a1
437 437 $ hg update -C
438 438 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
439 439 $ rm -rf d3
440 440
441 441 move a whole subtree with "hg rename ."
442 442
443 443 $ mkdir d3
444 444 $ (cd d1; hg rename . ../d3)
445 445 moving a to ../d3/d1/a
446 446 moving b to ../d3/d1/b
447 447 moving ba to ../d3/d1/ba
448 448 moving d11/a1 to ../d3/d1/d11/a1 (glob)
449 449 $ hg status -C
450 450 A d3/d1/a
451 451 d1/a
452 452 A d3/d1/b
453 453 d1/b
454 454 A d3/d1/ba
455 455 d1/ba
456 456 A d3/d1/d11/a1
457 457 d1/d11/a1
458 458 R d1/a
459 459 R d1/b
460 460 R d1/ba
461 461 R d1/d11/a1
462 462 $ hg update -C
463 463 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
464 464 $ rm -rf d3
465 465
466 466 move a whole subtree with "hg rename --after ."
467 467
468 468 $ mkdir d3
469 469 $ mv d1/* d3
470 470 $ (cd d1; hg rename --after . ../d3)
471 471 moving a to ../d3/a
472 472 moving b to ../d3/b
473 473 moving ba to ../d3/ba
474 474 moving d11/a1 to ../d3/d11/a1 (glob)
475 475 $ hg status -C
476 476 A d3/a
477 477 d1/a
478 478 A d3/b
479 479 d1/b
480 480 A d3/ba
481 481 d1/ba
482 482 A d3/d11/a1
483 483 d1/d11/a1
484 484 R d1/a
485 485 R d1/b
486 486 R d1/ba
487 487 R d1/d11/a1
488 488 $ hg update -C
489 489 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
490 490 $ rm -rf d3
491 491
492 492 move the parent tree with "hg rename .."
493 493
494 494 $ (cd d1/d11; hg rename .. ../../d3)
495 495 moving ../a to ../../d3/a (glob)
496 496 moving ../b to ../../d3/b (glob)
497 497 moving ../ba to ../../d3/ba (glob)
498 498 moving a1 to ../../d3/d11/a1
499 499 $ hg status -C
500 500 A d3/a
501 501 d1/a
502 502 A d3/b
503 503 d1/b
504 504 A d3/ba
505 505 d1/ba
506 506 A d3/d11/a1
507 507 d1/d11/a1
508 508 R d1/a
509 509 R d1/b
510 510 R d1/ba
511 511 R d1/d11/a1
512 512 $ hg update -C
513 513 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 514 $ rm -rf d3
515 515
516 516 skip removed files
517 517
518 518 $ hg remove d1/b
519 519 $ hg rename d1 d3
520 520 moving d1/a to d3/a (glob)
521 521 moving d1/ba to d3/ba (glob)
522 522 moving d1/d11/a1 to d3/d11/a1 (glob)
523 523 $ hg status -C
524 524 A d3/a
525 525 d1/a
526 526 A d3/ba
527 527 d1/ba
528 528 A d3/d11/a1
529 529 d1/d11/a1
530 530 R d1/a
531 531 R d1/b
532 532 R d1/ba
533 533 R d1/d11/a1
534 534 $ hg update -C
535 535 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
536 536 $ rm -rf d3
537 537
538 538 transitive rename
539 539
540 540 $ hg rename d1/b d1/bb
541 541 $ hg rename d1/bb d1/bc
542 542 $ hg status -C
543 543 A d1/bc
544 544 d1/b
545 545 R d1/b
546 546 $ hg update -C
547 547 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
548 548 $ rm d1/bc
549 549
550 550 transitive rename --after
551 551
552 552 $ hg rename d1/b d1/bb
553 553 $ mv d1/bb d1/bc
554 554 $ hg rename --after d1/bb d1/bc
555 555 $ hg status -C
556 556 A d1/bc
557 557 d1/b
558 558 R d1/b
559 559 $ hg update -C
560 560 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 561 $ rm d1/bc
562 562
563 563 $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
564 564 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
565 565 $ hg rename d1/b d1/bb
566 566 $ echo "some stuff added to d1/bb" >> d1/bb
567 567 $ hg rename d1/bb d1/b
568 568 $ hg status -C
569 569 M d1/b
570 570 $ hg update -C
571 571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 572
573 573 overwriting with renames (issue1959)
574 574
575 575 $ hg rename d1/a d1/c
576 576 $ hg rename d1/b d1/a
577 577 $ hg status -C
578 578 M d1/a
579 579 d1/b
580 580 A d1/c
581 581 d1/a
582 582 R d1/b
583 583 $ hg diff --git
584 584 diff --git a/d1/a b/d1/a
585 585 --- a/d1/a
586 586 +++ b/d1/a
587 587 @@ -1,1 +1,1 @@
588 588 -d1/a
589 589 +d1/b
590 590 diff --git a/d1/b b/d1/b
591 591 deleted file mode 100644
592 592 --- a/d1/b
593 593 +++ /dev/null
594 594 @@ -1,1 +0,0 @@
595 595 -d1/b
596 596 diff --git a/d1/a b/d1/c
597 597 copy from d1/a
598 598 copy to d1/c
599 599 $ hg update -C
600 600 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
601 601 $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
602 602
603 603 check illegal path components
604 604
605 605 $ hg rename d1/d11/a1 .hg/foo
606 606 abort: path contains illegal component: .hg/foo (glob)
607 607 [255]
608 608 $ hg status -C
609 609 $ hg rename d1/d11/a1 ../foo
610 610 abort: ../foo not under root '$TESTTMP'
611 611 [255]
612 612 $ hg status -C
613 613
614 614 $ mv d1/d11/a1 .hg/foo
615 615 $ hg rename --after d1/d11/a1 .hg/foo
616 616 abort: path contains illegal component: .hg/foo (glob)
617 617 [255]
618 618 $ hg status -C
619 619 ! d1/d11/a1
620 620 $ hg update -C
621 621 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
622 622 $ rm .hg/foo
623 623
624 624 $ hg rename d1/d11/a1 .hg
625 625 abort: path contains illegal component: .hg/a1 (glob)
626 626 [255]
627 627 $ hg --config extensions.largefiles= rename d1/d11/a1 .hg
628 628 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
629 629 abort: path contains illegal component: .hg/a1 (glob)
630 630 [255]
631 631 $ hg status -C
632 632 $ hg rename d1/d11/a1 ..
633 633 abort: ../a1 not under root '$TESTTMP' (glob)
634 634 [255]
635 635 $ hg --config extensions.largefiles= rename d1/d11/a1 ..
636 636 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
637 637 abort: ../a1 not under root '$TESTTMP' (glob)
638 638 [255]
639 639 $ hg status -C
640 640
641 641 $ mv d1/d11/a1 .hg
642 642 $ hg rename --after d1/d11/a1 .hg
643 643 abort: path contains illegal component: .hg/a1 (glob)
644 644 [255]
645 645 $ hg status -C
646 646 ! d1/d11/a1
647 647 $ hg update -C
648 648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
649 649 $ rm .hg/a1
650 650
651 651 $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
652 652 abort: path contains illegal component: .hg/foo (glob)
653 653 [255]
654 654 $ hg status -C
655 655 $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
656 656 abort: ../../../foo not under root '$TESTTMP'
657 657 [255]
658 658 $ hg status -C
659 659
General Comments 0
You need to be logged in to leave comments. Login now