##// END OF EJS Templates
shelve: trust caller of shelvedfile.opener() to check that the file exists...
Martin von Zweigbergk -
r46989:b2a8ff73 default
parent child Browse files
Show More
@@ -1,1163 +1,1158 b''
1 1 # shelve.py - save/restore working directory state
2 2 #
3 3 # Copyright 2013 Facebook, Inc.
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 """save and restore changes to the working directory
9 9
10 10 The "hg shelve" command saves changes made to the working directory
11 11 and reverts those changes, resetting the working directory to a clean
12 12 state.
13 13
14 14 Later on, the "hg unshelve" command restores the changes saved by "hg
15 15 shelve". Changes can be restored even after updating to a different
16 16 parent, in which case Mercurial's merge machinery will resolve any
17 17 conflicts if necessary.
18 18
19 19 You can have more than one shelved change outstanding at a time; each
20 20 shelved change has a distinct name. For details, see the help for "hg
21 21 shelve".
22 22 """
23 23 from __future__ import absolute_import
24 24
25 25 import collections
26 26 import errno
27 27 import itertools
28 28 import stat
29 29
30 30 from .i18n import _
31 31 from .pycompat import open
32 32 from .node import (
33 33 bin,
34 34 hex,
35 35 nullid,
36 36 nullrev,
37 37 )
38 38 from . import (
39 39 bookmarks,
40 40 bundle2,
41 41 changegroup,
42 42 cmdutil,
43 43 discovery,
44 44 error,
45 45 exchange,
46 46 hg,
47 47 lock as lockmod,
48 48 mdiff,
49 49 merge,
50 50 mergestate as mergestatemod,
51 51 patch,
52 52 phases,
53 53 pycompat,
54 54 repair,
55 55 scmutil,
56 56 templatefilters,
57 57 util,
58 58 vfs as vfsmod,
59 59 )
60 60 from .utils import (
61 61 dateutil,
62 62 stringutil,
63 63 )
64 64
65 65 backupdir = b'shelve-backup'
66 66 shelvedir = b'shelved'
67 67 shelvefileextensions = [b'hg', b'patch', b'shelve']
68 68 # universal extension is present in all types of shelves
69 69 patchextension = b'patch'
70 70
71 71 # we never need the user, so we use a
72 72 # generic user for all shelve operations
73 73 shelveuser = b'shelve@localhost'
74 74
75 75
76 76 class shelvedfile(object):
77 77 """Helper for the file storing a single shelve
78 78
79 79 Handles common functions on shelve files (.hg/.patch) using
80 80 the vfs layer"""
81 81
82 82 def __init__(self, repo, name, filetype=None):
83 83 self.repo = repo
84 84 self.name = name
85 85 self.vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
86 86 self.backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
87 87 self.ui = self.repo.ui
88 88 if filetype:
89 89 self.fname = name + b'.' + filetype
90 90 else:
91 91 self.fname = name
92 92
93 93 def exists(self):
94 94 return self.vfs.exists(self.fname)
95 95
96 96 def filename(self):
97 97 return self.vfs.join(self.fname)
98 98
99 99 def backupfilename(self):
100 100 def gennames(base):
101 101 yield base
102 102 base, ext = base.rsplit(b'.', 1)
103 103 for i in itertools.count(1):
104 104 yield b'%s-%d.%s' % (base, i, ext)
105 105
106 106 name = self.backupvfs.join(self.fname)
107 107 for n in gennames(name):
108 108 if not self.backupvfs.exists(n):
109 109 return n
110 110
111 111 def movetobackup(self):
112 112 if not self.backupvfs.isdir():
113 113 self.backupvfs.makedir()
114 114 util.rename(self.filename(), self.backupfilename())
115 115
116 116 def stat(self):
117 117 return self.vfs.stat(self.fname)
118 118
119 119 def opener(self, mode=b'rb'):
120 try:
121 return self.vfs(self.fname, mode)
122 except IOError as err:
123 if err.errno != errno.ENOENT:
124 raise
125 raise error.Abort(_(b"shelved change '%s' not found") % self.name)
120 return self.vfs(self.fname, mode)
126 121
127 122 def applybundle(self, tr):
128 123 fp = self.opener()
129 124 try:
130 125 targetphase = phases.internal
131 126 if not phases.supportinternal(self.repo):
132 127 targetphase = phases.secret
133 128 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
134 129 pretip = self.repo[b'tip']
135 130 bundle2.applybundle(
136 131 self.repo,
137 132 gen,
138 133 tr,
139 134 source=b'unshelve',
140 135 url=b'bundle:' + self.vfs.join(self.fname),
141 136 targetphase=targetphase,
142 137 )
143 138 shelvectx = self.repo[b'tip']
144 139 if pretip == shelvectx:
145 140 shelverev = tr.changes[b'revduplicates'][-1]
146 141 shelvectx = self.repo[shelverev]
147 142 return shelvectx
148 143 finally:
149 144 fp.close()
150 145
151 146 def writebundle(self, bases, node):
152 147 cgversion = changegroup.safeversion(self.repo)
153 148 if cgversion == b'01':
154 149 btype = b'HG10BZ'
155 150 compression = None
156 151 else:
157 152 btype = b'HG20'
158 153 compression = b'BZ'
159 154
160 155 repo = self.repo.unfiltered()
161 156
162 157 outgoing = discovery.outgoing(
163 158 repo, missingroots=bases, ancestorsof=[node]
164 159 )
165 160 cg = changegroup.makechangegroup(repo, outgoing, cgversion, b'shelve')
166 161
167 162 bundle2.writebundle(
168 163 self.ui, cg, self.fname, btype, self.vfs, compression=compression
169 164 )
170 165
171 166 def writeinfo(self, info):
172 167 scmutil.simplekeyvaluefile(self.vfs, self.fname).write(info)
173 168
174 169 def readinfo(self):
175 170 return scmutil.simplekeyvaluefile(self.vfs, self.fname).read()
176 171
177 172
178 173 class shelvedstate(object):
179 174 """Handle persistence during unshelving operations.
180 175
181 176 Handles saving and restoring a shelved state. Ensures that different
182 177 versions of a shelved state are possible and handles them appropriately.
183 178 """
184 179
185 180 _version = 2
186 181 _filename = b'shelvedstate'
187 182 _keep = b'keep'
188 183 _nokeep = b'nokeep'
189 184 # colon is essential to differentiate from a real bookmark name
190 185 _noactivebook = b':no-active-bookmark'
191 186 _interactive = b'interactive'
192 187
193 188 @classmethod
194 189 def _verifyandtransform(cls, d):
195 190 """Some basic shelvestate syntactic verification and transformation"""
196 191 try:
197 192 d[b'originalwctx'] = bin(d[b'originalwctx'])
198 193 d[b'pendingctx'] = bin(d[b'pendingctx'])
199 194 d[b'parents'] = [bin(h) for h in d[b'parents'].split(b' ')]
200 195 d[b'nodestoremove'] = [
201 196 bin(h) for h in d[b'nodestoremove'].split(b' ')
202 197 ]
203 198 except (ValueError, TypeError, KeyError) as err:
204 199 raise error.CorruptedState(pycompat.bytestr(err))
205 200
206 201 @classmethod
207 202 def _getversion(cls, repo):
208 203 """Read version information from shelvestate file"""
209 204 fp = repo.vfs(cls._filename)
210 205 try:
211 206 version = int(fp.readline().strip())
212 207 except ValueError as err:
213 208 raise error.CorruptedState(pycompat.bytestr(err))
214 209 finally:
215 210 fp.close()
216 211 return version
217 212
218 213 @classmethod
219 214 def _readold(cls, repo):
220 215 """Read the old position-based version of a shelvestate file"""
221 216 # Order is important, because old shelvestate file uses it
222 217 # to detemine values of fields (i.g. name is on the second line,
223 218 # originalwctx is on the third and so forth). Please do not change.
224 219 keys = [
225 220 b'version',
226 221 b'name',
227 222 b'originalwctx',
228 223 b'pendingctx',
229 224 b'parents',
230 225 b'nodestoremove',
231 226 b'branchtorestore',
232 227 b'keep',
233 228 b'activebook',
234 229 ]
235 230 # this is executed only seldomly, so it is not a big deal
236 231 # that we open this file twice
237 232 fp = repo.vfs(cls._filename)
238 233 d = {}
239 234 try:
240 235 for key in keys:
241 236 d[key] = fp.readline().strip()
242 237 finally:
243 238 fp.close()
244 239 return d
245 240
246 241 @classmethod
247 242 def load(cls, repo):
248 243 version = cls._getversion(repo)
249 244 if version < cls._version:
250 245 d = cls._readold(repo)
251 246 elif version == cls._version:
252 247 d = scmutil.simplekeyvaluefile(repo.vfs, cls._filename).read(
253 248 firstlinenonkeyval=True
254 249 )
255 250 else:
256 251 raise error.Abort(
257 252 _(
258 253 b'this version of shelve is incompatible '
259 254 b'with the version used in this repo'
260 255 )
261 256 )
262 257
263 258 cls._verifyandtransform(d)
264 259 try:
265 260 obj = cls()
266 261 obj.name = d[b'name']
267 262 obj.wctx = repo[d[b'originalwctx']]
268 263 obj.pendingctx = repo[d[b'pendingctx']]
269 264 obj.parents = d[b'parents']
270 265 obj.nodestoremove = d[b'nodestoremove']
271 266 obj.branchtorestore = d.get(b'branchtorestore', b'')
272 267 obj.keep = d.get(b'keep') == cls._keep
273 268 obj.activebookmark = b''
274 269 if d.get(b'activebook', b'') != cls._noactivebook:
275 270 obj.activebookmark = d.get(b'activebook', b'')
276 271 obj.interactive = d.get(b'interactive') == cls._interactive
277 272 except (error.RepoLookupError, KeyError) as err:
278 273 raise error.CorruptedState(pycompat.bytestr(err))
279 274
280 275 return obj
281 276
282 277 @classmethod
283 278 def save(
284 279 cls,
285 280 repo,
286 281 name,
287 282 originalwctx,
288 283 pendingctx,
289 284 nodestoremove,
290 285 branchtorestore,
291 286 keep=False,
292 287 activebook=b'',
293 288 interactive=False,
294 289 ):
295 290 info = {
296 291 b"name": name,
297 292 b"originalwctx": hex(originalwctx.node()),
298 293 b"pendingctx": hex(pendingctx.node()),
299 294 b"parents": b' '.join([hex(p) for p in repo.dirstate.parents()]),
300 295 b"nodestoremove": b' '.join([hex(n) for n in nodestoremove]),
301 296 b"branchtorestore": branchtorestore,
302 297 b"keep": cls._keep if keep else cls._nokeep,
303 298 b"activebook": activebook or cls._noactivebook,
304 299 }
305 300 if interactive:
306 301 info[b'interactive'] = cls._interactive
307 302 scmutil.simplekeyvaluefile(repo.vfs, cls._filename).write(
308 303 info, firstline=(b"%d" % cls._version)
309 304 )
310 305
311 306 @classmethod
312 307 def clear(cls, repo):
313 308 repo.vfs.unlinkpath(cls._filename, ignoremissing=True)
314 309
315 310
316 311 def cleanupoldbackups(repo):
317 312 vfs = vfsmod.vfs(repo.vfs.join(backupdir))
318 313 maxbackups = repo.ui.configint(b'shelve', b'maxbackups')
319 314 hgfiles = [f for f in vfs.listdir() if f.endswith(b'.' + patchextension)]
320 315 hgfiles = sorted([(vfs.stat(f)[stat.ST_MTIME], f) for f in hgfiles])
321 316 if maxbackups > 0 and maxbackups < len(hgfiles):
322 317 bordermtime = hgfiles[-maxbackups][0]
323 318 else:
324 319 bordermtime = None
325 320 for mtime, f in hgfiles[: len(hgfiles) - maxbackups]:
326 321 if mtime == bordermtime:
327 322 # keep it, because timestamp can't decide exact order of backups
328 323 continue
329 324 base = f[: -(1 + len(patchextension))]
330 325 for ext in shelvefileextensions:
331 326 vfs.tryunlink(base + b'.' + ext)
332 327
333 328
334 329 def _backupactivebookmark(repo):
335 330 activebookmark = repo._activebookmark
336 331 if activebookmark:
337 332 bookmarks.deactivate(repo)
338 333 return activebookmark
339 334
340 335
341 336 def _restoreactivebookmark(repo, mark):
342 337 if mark:
343 338 bookmarks.activate(repo, mark)
344 339
345 340
346 341 def _aborttransaction(repo, tr):
347 342 """Abort current transaction for shelve/unshelve, but keep dirstate"""
348 343 dirstatebackupname = b'dirstate.shelve'
349 344 repo.dirstate.savebackup(tr, dirstatebackupname)
350 345 tr.abort()
351 346 repo.dirstate.restorebackup(None, dirstatebackupname)
352 347
353 348
354 349 def getshelvename(repo, parent, opts):
355 350 """Decide on the name this shelve is going to have"""
356 351
357 352 def gennames():
358 353 yield label
359 354 for i in itertools.count(1):
360 355 yield b'%s-%02d' % (label, i)
361 356
362 357 name = opts.get(b'name')
363 358 label = repo._activebookmark or parent.branch() or b'default'
364 359 # slashes aren't allowed in filenames, therefore we rename it
365 360 label = label.replace(b'/', b'_')
366 361 label = label.replace(b'\\', b'_')
367 362 # filenames must not start with '.' as it should not be hidden
368 363 if label.startswith(b'.'):
369 364 label = label.replace(b'.', b'_', 1)
370 365
371 366 if name:
372 367 if shelvedfile(repo, name, patchextension).exists():
373 368 e = _(b"a shelved change named '%s' already exists") % name
374 369 raise error.Abort(e)
375 370
376 371 # ensure we are not creating a subdirectory or a hidden file
377 372 if b'/' in name or b'\\' in name:
378 373 raise error.Abort(
379 374 _(b'shelved change names can not contain slashes')
380 375 )
381 376 if name.startswith(b'.'):
382 377 raise error.Abort(_(b"shelved change names can not start with '.'"))
383 378
384 379 else:
385 380 for n in gennames():
386 381 if not shelvedfile(repo, n, patchextension).exists():
387 382 name = n
388 383 break
389 384
390 385 return name
391 386
392 387
393 388 def mutableancestors(ctx):
394 389 """return all mutable ancestors for ctx (included)
395 390
396 391 Much faster than the revset ancestors(ctx) & draft()"""
397 392 seen = {nullrev}
398 393 visit = collections.deque()
399 394 visit.append(ctx)
400 395 while visit:
401 396 ctx = visit.popleft()
402 397 yield ctx.node()
403 398 for parent in ctx.parents():
404 399 rev = parent.rev()
405 400 if rev not in seen:
406 401 seen.add(rev)
407 402 if parent.mutable():
408 403 visit.append(parent)
409 404
410 405
411 406 def getcommitfunc(extra, interactive, editor=False):
412 407 def commitfunc(ui, repo, message, match, opts):
413 408 hasmq = util.safehasattr(repo, b'mq')
414 409 if hasmq:
415 410 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
416 411
417 412 targetphase = phases.internal
418 413 if not phases.supportinternal(repo):
419 414 targetphase = phases.secret
420 415 overrides = {(b'phases', b'new-commit'): targetphase}
421 416 try:
422 417 editor_ = False
423 418 if editor:
424 419 editor_ = cmdutil.getcommiteditor(
425 420 editform=b'shelve.shelve', **pycompat.strkwargs(opts)
426 421 )
427 422 with repo.ui.configoverride(overrides):
428 423 return repo.commit(
429 424 message,
430 425 shelveuser,
431 426 opts.get(b'date'),
432 427 match,
433 428 editor=editor_,
434 429 extra=extra,
435 430 )
436 431 finally:
437 432 if hasmq:
438 433 repo.mq.checkapplied = saved
439 434
440 435 def interactivecommitfunc(ui, repo, *pats, **opts):
441 436 opts = pycompat.byteskwargs(opts)
442 437 match = scmutil.match(repo[b'.'], pats, {})
443 438 message = opts[b'message']
444 439 return commitfunc(ui, repo, message, match, opts)
445 440
446 441 return interactivecommitfunc if interactive else commitfunc
447 442
448 443
449 444 def _nothingtoshelvemessaging(ui, repo, pats, opts):
450 445 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
451 446 if stat.deleted:
452 447 ui.status(
453 448 _(b"nothing changed (%d missing files, see 'hg status')\n")
454 449 % len(stat.deleted)
455 450 )
456 451 else:
457 452 ui.status(_(b"nothing changed\n"))
458 453
459 454
460 455 def _shelvecreatedcommit(repo, node, name, match):
461 456 info = {b'node': hex(node)}
462 457 shelvedfile(repo, name, b'shelve').writeinfo(info)
463 458 bases = list(mutableancestors(repo[node]))
464 459 shelvedfile(repo, name, b'hg').writebundle(bases, node)
465 460 with shelvedfile(repo, name, patchextension).opener(b'wb') as fp:
466 461 cmdutil.exportfile(
467 462 repo, [node], fp, opts=mdiff.diffopts(git=True), match=match
468 463 )
469 464
470 465
471 466 def _includeunknownfiles(repo, pats, opts, extra):
472 467 s = repo.status(match=scmutil.match(repo[None], pats, opts), unknown=True)
473 468 if s.unknown:
474 469 extra[b'shelve_unknown'] = b'\0'.join(s.unknown)
475 470 repo[None].add(s.unknown)
476 471
477 472
478 473 def _finishshelve(repo, tr):
479 474 if phases.supportinternal(repo):
480 475 tr.close()
481 476 else:
482 477 _aborttransaction(repo, tr)
483 478
484 479
485 480 def createcmd(ui, repo, pats, opts):
486 481 """subcommand that creates a new shelve"""
487 482 with repo.wlock():
488 483 cmdutil.checkunfinished(repo)
489 484 return _docreatecmd(ui, repo, pats, opts)
490 485
491 486
492 487 def _docreatecmd(ui, repo, pats, opts):
493 488 wctx = repo[None]
494 489 parents = wctx.parents()
495 490 parent = parents[0]
496 491 origbranch = wctx.branch()
497 492
498 493 if parent.node() != nullid:
499 494 desc = b"changes to: %s" % parent.description().split(b'\n', 1)[0]
500 495 else:
501 496 desc = b'(changes in empty repository)'
502 497
503 498 if not opts.get(b'message'):
504 499 opts[b'message'] = desc
505 500
506 501 lock = tr = activebookmark = None
507 502 try:
508 503 lock = repo.lock()
509 504
510 505 # use an uncommitted transaction to generate the bundle to avoid
511 506 # pull races. ensure we don't print the abort message to stderr.
512 507 tr = repo.transaction(b'shelve', report=lambda x: None)
513 508
514 509 interactive = opts.get(b'interactive', False)
515 510 includeunknown = opts.get(b'unknown', False) and not opts.get(
516 511 b'addremove', False
517 512 )
518 513
519 514 name = getshelvename(repo, parent, opts)
520 515 activebookmark = _backupactivebookmark(repo)
521 516 extra = {b'internal': b'shelve'}
522 517 if includeunknown:
523 518 _includeunknownfiles(repo, pats, opts, extra)
524 519
525 520 if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
526 521 # In non-bare shelve we don't store newly created branch
527 522 # at bundled commit
528 523 repo.dirstate.setbranch(repo[b'.'].branch())
529 524
530 525 commitfunc = getcommitfunc(extra, interactive, editor=True)
531 526 if not interactive:
532 527 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
533 528 else:
534 529 node = cmdutil.dorecord(
535 530 ui,
536 531 repo,
537 532 commitfunc,
538 533 None,
539 534 False,
540 535 cmdutil.recordfilter,
541 536 *pats,
542 537 **pycompat.strkwargs(opts)
543 538 )
544 539 if not node:
545 540 _nothingtoshelvemessaging(ui, repo, pats, opts)
546 541 return 1
547 542
548 543 # Create a matcher so that prefetch doesn't attempt to fetch
549 544 # the entire repository pointlessly, and as an optimisation
550 545 # for movedirstate, if needed.
551 546 match = scmutil.matchfiles(repo, repo[node].files())
552 547 _shelvecreatedcommit(repo, node, name, match)
553 548
554 549 ui.status(_(b'shelved as %s\n') % name)
555 550 if opts[b'keep']:
556 551 with repo.dirstate.parentchange():
557 552 scmutil.movedirstate(repo, parent, match)
558 553 else:
559 554 hg.update(repo, parent.node())
560 555 ms = mergestatemod.mergestate.read(repo)
561 556 if not ms.unresolvedcount():
562 557 ms.reset()
563 558
564 559 if origbranch != repo[b'.'].branch() and not _isbareshelve(pats, opts):
565 560 repo.dirstate.setbranch(origbranch)
566 561
567 562 _finishshelve(repo, tr)
568 563 finally:
569 564 _restoreactivebookmark(repo, activebookmark)
570 565 lockmod.release(tr, lock)
571 566
572 567
573 568 def _isbareshelve(pats, opts):
574 569 return (
575 570 not pats
576 571 and not opts.get(b'interactive', False)
577 572 and not opts.get(b'include', False)
578 573 and not opts.get(b'exclude', False)
579 574 )
580 575
581 576
582 577 def _iswctxonnewbranch(repo):
583 578 return repo[None].branch() != repo[b'.'].branch()
584 579
585 580
586 581 def cleanupcmd(ui, repo):
587 582 """subcommand that deletes all shelves"""
588 583
589 584 with repo.wlock():
590 585 for (name, _type) in repo.vfs.readdir(shelvedir):
591 586 suffix = name.rsplit(b'.', 1)[-1]
592 587 if suffix in shelvefileextensions:
593 588 shelvedfile(repo, name).movetobackup()
594 589 cleanupoldbackups(repo)
595 590
596 591
597 592 def deletecmd(ui, repo, pats):
598 593 """subcommand that deletes a specific shelve"""
599 594 if not pats:
600 595 raise error.Abort(_(b'no shelved changes specified!'))
601 596 with repo.wlock():
602 597 for name in pats:
603 598 if not shelvedfile(repo, name, patchextension).exists():
604 599 raise error.Abort(_(b"shelved change '%s' not found") % name)
605 600 for suffix in shelvefileextensions:
606 601 shfile = shelvedfile(repo, name, suffix)
607 602 if shfile.exists():
608 603 shfile.movetobackup()
609 604 cleanupoldbackups(repo)
610 605
611 606
612 607 def listshelves(repo):
613 608 """return all shelves in repo as list of (time, filename)"""
614 609 try:
615 610 names = repo.vfs.readdir(shelvedir)
616 611 except OSError as err:
617 612 if err.errno != errno.ENOENT:
618 613 raise
619 614 return []
620 615 info = []
621 616 for (name, _type) in names:
622 617 pfx, sfx = name.rsplit(b'.', 1)
623 618 if not pfx or sfx != patchextension:
624 619 continue
625 620 st = shelvedfile(repo, name).stat()
626 621 info.append((st[stat.ST_MTIME], shelvedfile(repo, pfx).filename()))
627 622 return sorted(info, reverse=True)
628 623
629 624
630 625 def listcmd(ui, repo, pats, opts):
631 626 """subcommand that displays the list of shelves"""
632 627 pats = set(pats)
633 628 width = 80
634 629 if not ui.plain():
635 630 width = ui.termwidth()
636 631 namelabel = b'shelve.newest'
637 632 ui.pager(b'shelve')
638 633 for mtime, name in listshelves(repo):
639 634 sname = util.split(name)[1]
640 635 if pats and sname not in pats:
641 636 continue
642 637 ui.write(sname, label=namelabel)
643 638 namelabel = b'shelve.name'
644 639 if ui.quiet:
645 640 ui.write(b'\n')
646 641 continue
647 642 ui.write(b' ' * (16 - len(sname)))
648 643 used = 16
649 644 date = dateutil.makedate(mtime)
650 645 age = b'(%s)' % templatefilters.age(date, abbrev=True)
651 646 ui.write(age, label=b'shelve.age')
652 647 ui.write(b' ' * (12 - len(age)))
653 648 used += 12
654 649 with open(name + b'.' + patchextension, b'rb') as fp:
655 650 while True:
656 651 line = fp.readline()
657 652 if not line:
658 653 break
659 654 if not line.startswith(b'#'):
660 655 desc = line.rstrip()
661 656 if ui.formatted():
662 657 desc = stringutil.ellipsis(desc, width - used)
663 658 ui.write(desc)
664 659 break
665 660 ui.write(b'\n')
666 661 if not (opts[b'patch'] or opts[b'stat']):
667 662 continue
668 663 difflines = fp.readlines()
669 664 if opts[b'patch']:
670 665 for chunk, label in patch.difflabel(iter, difflines):
671 666 ui.write(chunk, label=label)
672 667 if opts[b'stat']:
673 668 for chunk, label in patch.diffstatui(difflines, width=width):
674 669 ui.write(chunk, label=label)
675 670
676 671
677 672 def patchcmds(ui, repo, pats, opts):
678 673 """subcommand that displays shelves"""
679 674 if len(pats) == 0:
680 675 shelves = listshelves(repo)
681 676 if not shelves:
682 677 raise error.Abort(_(b"there are no shelves to show"))
683 678 mtime, name = shelves[0]
684 679 sname = util.split(name)[1]
685 680 pats = [sname]
686 681
687 682 for shelfname in pats:
688 683 if not shelvedfile(repo, shelfname, patchextension).exists():
689 684 raise error.Abort(_(b"cannot find shelf %s") % shelfname)
690 685
691 686 listcmd(ui, repo, pats, opts)
692 687
693 688
694 689 def checkparents(repo, state):
695 690 """check parent while resuming an unshelve"""
696 691 if state.parents != repo.dirstate.parents():
697 692 raise error.Abort(
698 693 _(b'working directory parents do not match unshelve state')
699 694 )
700 695
701 696
702 697 def _loadshelvedstate(ui, repo, opts):
703 698 try:
704 699 state = shelvedstate.load(repo)
705 700 if opts.get(b'keep') is None:
706 701 opts[b'keep'] = state.keep
707 702 except IOError as err:
708 703 if err.errno != errno.ENOENT:
709 704 raise
710 705 cmdutil.wrongtooltocontinue(repo, _(b'unshelve'))
711 706 except error.CorruptedState as err:
712 707 ui.debug(pycompat.bytestr(err) + b'\n')
713 708 if opts.get(b'continue'):
714 709 msg = _(b'corrupted shelved state file')
715 710 hint = _(
716 711 b'please run hg unshelve --abort to abort unshelve '
717 712 b'operation'
718 713 )
719 714 raise error.Abort(msg, hint=hint)
720 715 elif opts.get(b'abort'):
721 716 shelvedstate.clear(repo)
722 717 raise error.Abort(
723 718 _(
724 719 b'could not read shelved state file, your '
725 720 b'working copy may be in an unexpected state\n'
726 721 b'please update to some commit\n'
727 722 )
728 723 )
729 724 return state
730 725
731 726
732 727 def unshelveabort(ui, repo, state):
733 728 """subcommand that abort an in-progress unshelve"""
734 729 with repo.lock():
735 730 try:
736 731 checkparents(repo, state)
737 732
738 733 merge.clean_update(state.pendingctx)
739 734 if state.activebookmark and state.activebookmark in repo._bookmarks:
740 735 bookmarks.activate(repo, state.activebookmark)
741 736 mergefiles(ui, repo, state.wctx, state.pendingctx)
742 737 if not phases.supportinternal(repo):
743 738 repair.strip(
744 739 ui, repo, state.nodestoremove, backup=False, topic=b'shelve'
745 740 )
746 741 finally:
747 742 shelvedstate.clear(repo)
748 743 ui.warn(_(b"unshelve of '%s' aborted\n") % state.name)
749 744
750 745
751 746 def hgabortunshelve(ui, repo):
752 747 """logic to abort unshelve using 'hg abort"""
753 748 with repo.wlock():
754 749 state = _loadshelvedstate(ui, repo, {b'abort': True})
755 750 return unshelveabort(ui, repo, state)
756 751
757 752
758 753 def mergefiles(ui, repo, wctx, shelvectx):
759 754 """updates to wctx and merges the changes from shelvectx into the
760 755 dirstate."""
761 756 with ui.configoverride({(b'ui', b'quiet'): True}):
762 757 hg.update(repo, wctx.node())
763 758 ui.pushbuffer(True)
764 759 cmdutil.revert(ui, repo, shelvectx)
765 760 ui.popbuffer()
766 761
767 762
768 763 def restorebranch(ui, repo, branchtorestore):
769 764 if branchtorestore and branchtorestore != repo.dirstate.branch():
770 765 repo.dirstate.setbranch(branchtorestore)
771 766 ui.status(
772 767 _(b'marked working directory as branch %s\n') % branchtorestore
773 768 )
774 769
775 770
776 771 def unshelvecleanup(ui, repo, name, opts):
777 772 """remove related files after an unshelve"""
778 773 if not opts.get(b'keep'):
779 774 for filetype in shelvefileextensions:
780 775 shfile = shelvedfile(repo, name, filetype)
781 776 if shfile.exists():
782 777 shfile.movetobackup()
783 778 cleanupoldbackups(repo)
784 779
785 780
786 781 def unshelvecontinue(ui, repo, state, opts):
787 782 """subcommand to continue an in-progress unshelve"""
788 783 # We're finishing off a merge. First parent is our original
789 784 # parent, second is the temporary "fake" commit we're unshelving.
790 785 interactive = state.interactive
791 786 basename = state.name
792 787 with repo.lock():
793 788 checkparents(repo, state)
794 789 ms = mergestatemod.mergestate.read(repo)
795 790 if list(ms.unresolved()):
796 791 raise error.Abort(
797 792 _(b"unresolved conflicts, can't continue"),
798 793 hint=_(b"see 'hg resolve', then 'hg unshelve --continue'"),
799 794 )
800 795
801 796 shelvectx = repo[state.parents[1]]
802 797 pendingctx = state.pendingctx
803 798
804 799 with repo.dirstate.parentchange():
805 800 repo.setparents(state.pendingctx.node(), nullid)
806 801 repo.dirstate.write(repo.currenttransaction())
807 802
808 803 targetphase = phases.internal
809 804 if not phases.supportinternal(repo):
810 805 targetphase = phases.secret
811 806 overrides = {(b'phases', b'new-commit'): targetphase}
812 807 with repo.ui.configoverride(overrides, b'unshelve'):
813 808 with repo.dirstate.parentchange():
814 809 repo.setparents(state.parents[0], nullid)
815 810 newnode, ispartialunshelve = _createunshelvectx(
816 811 ui, repo, shelvectx, basename, interactive, opts
817 812 )
818 813
819 814 if newnode is None:
820 815 shelvectx = state.pendingctx
821 816 msg = _(
822 817 b'note: unshelved changes already existed '
823 818 b'in the working copy\n'
824 819 )
825 820 ui.status(msg)
826 821 else:
827 822 # only strip the shelvectx if we produced one
828 823 state.nodestoremove.append(newnode)
829 824 shelvectx = repo[newnode]
830 825
831 826 merge.update(pendingctx)
832 827 mergefiles(ui, repo, state.wctx, shelvectx)
833 828 restorebranch(ui, repo, state.branchtorestore)
834 829
835 830 if not phases.supportinternal(repo):
836 831 repair.strip(
837 832 ui, repo, state.nodestoremove, backup=False, topic=b'shelve'
838 833 )
839 834 shelvedstate.clear(repo)
840 835 if not ispartialunshelve:
841 836 unshelvecleanup(ui, repo, state.name, opts)
842 837 _restoreactivebookmark(repo, state.activebookmark)
843 838 ui.status(_(b"unshelve of '%s' complete\n") % state.name)
844 839
845 840
846 841 def hgcontinueunshelve(ui, repo):
847 842 """logic to resume unshelve using 'hg continue'"""
848 843 with repo.wlock():
849 844 state = _loadshelvedstate(ui, repo, {b'continue': True})
850 845 return unshelvecontinue(ui, repo, state, {b'keep': state.keep})
851 846
852 847
853 848 def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
854 849 """Temporarily commit working copy changes before moving unshelve commit"""
855 850 # Store pending changes in a commit and remember added in case a shelve
856 851 # contains unknown files that are part of the pending change
857 852 s = repo.status()
858 853 addedbefore = frozenset(s.added)
859 854 if not (s.modified or s.added or s.removed):
860 855 return tmpwctx, addedbefore
861 856 ui.status(
862 857 _(
863 858 b"temporarily committing pending changes "
864 859 b"(restore with 'hg unshelve --abort')\n"
865 860 )
866 861 )
867 862 extra = {b'internal': b'shelve'}
868 863 commitfunc = getcommitfunc(extra=extra, interactive=False, editor=False)
869 864 tempopts = {}
870 865 tempopts[b'message'] = b"pending changes temporary commit"
871 866 tempopts[b'date'] = opts.get(b'date')
872 867 with ui.configoverride({(b'ui', b'quiet'): True}):
873 868 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
874 869 tmpwctx = repo[node]
875 870 return tmpwctx, addedbefore
876 871
877 872
878 873 def _unshelverestorecommit(ui, repo, tr, basename):
879 874 """Recreate commit in the repository during the unshelve"""
880 875 repo = repo.unfiltered()
881 876 node = None
882 877 if shelvedfile(repo, basename, b'shelve').exists():
883 878 node = shelvedfile(repo, basename, b'shelve').readinfo()[b'node']
884 879 if node is None or node not in repo:
885 880 with ui.configoverride({(b'ui', b'quiet'): True}):
886 881 shelvectx = shelvedfile(repo, basename, b'hg').applybundle(tr)
887 882 # We might not strip the unbundled changeset, so we should keep track of
888 883 # the unshelve node in case we need to reuse it (eg: unshelve --keep)
889 884 if node is None:
890 885 info = {b'node': hex(shelvectx.node())}
891 886 shelvedfile(repo, basename, b'shelve').writeinfo(info)
892 887 else:
893 888 shelvectx = repo[node]
894 889
895 890 return repo, shelvectx
896 891
897 892
898 893 def _createunshelvectx(ui, repo, shelvectx, basename, interactive, opts):
899 894 """Handles the creation of unshelve commit and updates the shelve if it
900 895 was partially unshelved.
901 896
902 897 If interactive is:
903 898
904 899 * False: Commits all the changes in the working directory.
905 900 * True: Prompts the user to select changes to unshelve and commit them.
906 901 Update the shelve with remaining changes.
907 902
908 903 Returns the node of the new commit formed and a bool indicating whether
909 904 the shelve was partially unshelved.Creates a commit ctx to unshelve
910 905 interactively or non-interactively.
911 906
912 907 The user might want to unshelve certain changes only from the stored
913 908 shelve in interactive. So, we would create two commits. One with requested
914 909 changes to unshelve at that time and the latter is shelved for future.
915 910
916 911 Here, we return both the newnode which is created interactively and a
917 912 bool to know whether the shelve is partly done or completely done.
918 913 """
919 914 opts[b'message'] = shelvectx.description()
920 915 opts[b'interactive-unshelve'] = True
921 916 pats = []
922 917 if not interactive:
923 918 newnode = repo.commit(
924 919 text=shelvectx.description(),
925 920 extra=shelvectx.extra(),
926 921 user=shelvectx.user(),
927 922 date=shelvectx.date(),
928 923 )
929 924 return newnode, False
930 925
931 926 commitfunc = getcommitfunc(shelvectx.extra(), interactive=True, editor=True)
932 927 newnode = cmdutil.dorecord(
933 928 ui,
934 929 repo,
935 930 commitfunc,
936 931 None,
937 932 False,
938 933 cmdutil.recordfilter,
939 934 *pats,
940 935 **pycompat.strkwargs(opts)
941 936 )
942 937 snode = repo.commit(
943 938 text=shelvectx.description(),
944 939 extra=shelvectx.extra(),
945 940 user=shelvectx.user(),
946 941 )
947 942 if snode:
948 943 m = scmutil.matchfiles(repo, repo[snode].files())
949 944 _shelvecreatedcommit(repo, snode, basename, m)
950 945
951 946 return newnode, bool(snode)
952 947
953 948
954 949 def _rebaserestoredcommit(
955 950 ui,
956 951 repo,
957 952 opts,
958 953 tr,
959 954 oldtiprev,
960 955 basename,
961 956 pctx,
962 957 tmpwctx,
963 958 shelvectx,
964 959 branchtorestore,
965 960 activebookmark,
966 961 ):
967 962 """Rebase restored commit from its original location to a destination"""
968 963 # If the shelve is not immediately on top of the commit
969 964 # we'll be merging with, rebase it to be on top.
970 965 interactive = opts.get(b'interactive')
971 966 if tmpwctx.node() == shelvectx.p1().node() and not interactive:
972 967 # We won't skip on interactive mode because, the user might want to
973 968 # unshelve certain changes only.
974 969 return shelvectx, False
975 970
976 971 overrides = {
977 972 (b'ui', b'forcemerge'): opts.get(b'tool', b''),
978 973 (b'phases', b'new-commit'): phases.secret,
979 974 }
980 975 with repo.ui.configoverride(overrides, b'unshelve'):
981 976 ui.status(_(b'rebasing shelved changes\n'))
982 977 stats = merge.graft(
983 978 repo,
984 979 shelvectx,
985 980 labels=[b'working-copy', b'shelve'],
986 981 keepconflictparent=True,
987 982 )
988 983 if stats.unresolvedcount:
989 984 tr.close()
990 985
991 986 nodestoremove = [
992 987 repo.changelog.node(rev)
993 988 for rev in pycompat.xrange(oldtiprev, len(repo))
994 989 ]
995 990 shelvedstate.save(
996 991 repo,
997 992 basename,
998 993 pctx,
999 994 tmpwctx,
1000 995 nodestoremove,
1001 996 branchtorestore,
1002 997 opts.get(b'keep'),
1003 998 activebookmark,
1004 999 interactive,
1005 1000 )
1006 1001 raise error.ConflictResolutionRequired(b'unshelve')
1007 1002
1008 1003 with repo.dirstate.parentchange():
1009 1004 repo.setparents(tmpwctx.node(), nullid)
1010 1005 newnode, ispartialunshelve = _createunshelvectx(
1011 1006 ui, repo, shelvectx, basename, interactive, opts
1012 1007 )
1013 1008
1014 1009 if newnode is None:
1015 1010 shelvectx = tmpwctx
1016 1011 msg = _(
1017 1012 b'note: unshelved changes already existed '
1018 1013 b'in the working copy\n'
1019 1014 )
1020 1015 ui.status(msg)
1021 1016 else:
1022 1017 shelvectx = repo[newnode]
1023 1018 merge.update(tmpwctx)
1024 1019
1025 1020 return shelvectx, ispartialunshelve
1026 1021
1027 1022
1028 1023 def _forgetunknownfiles(repo, shelvectx, addedbefore):
1029 1024 # Forget any files that were unknown before the shelve, unknown before
1030 1025 # unshelve started, but are now added.
1031 1026 shelveunknown = shelvectx.extra().get(b'shelve_unknown')
1032 1027 if not shelveunknown:
1033 1028 return
1034 1029 shelveunknown = frozenset(shelveunknown.split(b'\0'))
1035 1030 addedafter = frozenset(repo.status().added)
1036 1031 toforget = (addedafter & shelveunknown) - addedbefore
1037 1032 repo[None].forget(toforget)
1038 1033
1039 1034
1040 1035 def _finishunshelve(repo, oldtiprev, tr, activebookmark):
1041 1036 _restoreactivebookmark(repo, activebookmark)
1042 1037 # The transaction aborting will strip all the commits for us,
1043 1038 # but it doesn't update the inmemory structures, so addchangegroup
1044 1039 # hooks still fire and try to operate on the missing commits.
1045 1040 # Clean up manually to prevent this.
1046 1041 repo.unfiltered().changelog.strip(oldtiprev, tr)
1047 1042 _aborttransaction(repo, tr)
1048 1043
1049 1044
1050 1045 def _checkunshelveuntrackedproblems(ui, repo, shelvectx):
1051 1046 """Check potential problems which may result from working
1052 1047 copy having untracked changes."""
1053 1048 wcdeleted = set(repo.status().deleted)
1054 1049 shelvetouched = set(shelvectx.files())
1055 1050 intersection = wcdeleted.intersection(shelvetouched)
1056 1051 if intersection:
1057 1052 m = _(b"shelved change touches missing files")
1058 1053 hint = _(b"run hg status to see which files are missing")
1059 1054 raise error.Abort(m, hint=hint)
1060 1055
1061 1056
1062 1057 def unshelvecmd(ui, repo, *shelved, **opts):
1063 1058 opts = pycompat.byteskwargs(opts)
1064 1059 abortf = opts.get(b'abort')
1065 1060 continuef = opts.get(b'continue')
1066 1061 interactive = opts.get(b'interactive')
1067 1062 if not abortf and not continuef:
1068 1063 cmdutil.checkunfinished(repo)
1069 1064 shelved = list(shelved)
1070 1065 if opts.get(b"name"):
1071 1066 shelved.append(opts[b"name"])
1072 1067
1073 1068 if interactive and opts.get(b'keep'):
1074 1069 raise error.Abort(_(b'--keep on --interactive is not yet supported'))
1075 1070 if abortf or continuef:
1076 1071 if abortf and continuef:
1077 1072 raise error.Abort(_(b'cannot use both abort and continue'))
1078 1073 if shelved:
1079 1074 raise error.Abort(
1080 1075 _(
1081 1076 b'cannot combine abort/continue with '
1082 1077 b'naming a shelved change'
1083 1078 )
1084 1079 )
1085 1080 if abortf and opts.get(b'tool', False):
1086 1081 ui.warn(_(b'tool option will be ignored\n'))
1087 1082
1088 1083 state = _loadshelvedstate(ui, repo, opts)
1089 1084 if abortf:
1090 1085 return unshelveabort(ui, repo, state)
1091 1086 elif continuef and interactive:
1092 1087 raise error.Abort(_(b'cannot use both continue and interactive'))
1093 1088 elif continuef:
1094 1089 return unshelvecontinue(ui, repo, state, opts)
1095 1090 elif len(shelved) > 1:
1096 1091 raise error.Abort(_(b'can only unshelve one change at a time'))
1097 1092 elif not shelved:
1098 1093 shelved = listshelves(repo)
1099 1094 if not shelved:
1100 1095 raise error.Abort(_(b'no shelved changes to apply!'))
1101 1096 basename = util.split(shelved[0][1])[1]
1102 1097 ui.status(_(b"unshelving change '%s'\n") % basename)
1103 1098 else:
1104 1099 basename = shelved[0]
1105 1100
1106 1101 if not shelvedfile(repo, basename, patchextension).exists():
1107 1102 raise error.Abort(_(b"shelved change '%s' not found") % basename)
1108 1103
1109 1104 return _dounshelve(ui, repo, basename, opts)
1110 1105
1111 1106
1112 1107 def _dounshelve(ui, repo, basename, opts):
1113 1108 repo = repo.unfiltered()
1114 1109 lock = tr = None
1115 1110 try:
1116 1111 lock = repo.lock()
1117 1112 tr = repo.transaction(b'unshelve', report=lambda x: None)
1118 1113 oldtiprev = len(repo)
1119 1114
1120 1115 pctx = repo[b'.']
1121 1116 tmpwctx = pctx
1122 1117 # The goal is to have a commit structure like so:
1123 1118 # ...-> pctx -> tmpwctx -> shelvectx
1124 1119 # where tmpwctx is an optional commit with the user's pending changes
1125 1120 # and shelvectx is the unshelved changes. Then we merge it all down
1126 1121 # to the original pctx.
1127 1122
1128 1123 activebookmark = _backupactivebookmark(repo)
1129 1124 tmpwctx, addedbefore = _commitworkingcopychanges(
1130 1125 ui, repo, opts, tmpwctx
1131 1126 )
1132 1127 repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename)
1133 1128 _checkunshelveuntrackedproblems(ui, repo, shelvectx)
1134 1129 branchtorestore = b''
1135 1130 if shelvectx.branch() != shelvectx.p1().branch():
1136 1131 branchtorestore = shelvectx.branch()
1137 1132
1138 1133 shelvectx, ispartialunshelve = _rebaserestoredcommit(
1139 1134 ui,
1140 1135 repo,
1141 1136 opts,
1142 1137 tr,
1143 1138 oldtiprev,
1144 1139 basename,
1145 1140 pctx,
1146 1141 tmpwctx,
1147 1142 shelvectx,
1148 1143 branchtorestore,
1149 1144 activebookmark,
1150 1145 )
1151 1146 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
1152 1147 with ui.configoverride(overrides, b'unshelve'):
1153 1148 mergefiles(ui, repo, pctx, shelvectx)
1154 1149 restorebranch(ui, repo, branchtorestore)
1155 1150 shelvedstate.clear(repo)
1156 1151 _finishunshelve(repo, oldtiprev, tr, activebookmark)
1157 1152 _forgetunknownfiles(repo, shelvectx, addedbefore)
1158 1153 if not ispartialunshelve:
1159 1154 unshelvecleanup(ui, repo, basename, opts)
1160 1155 finally:
1161 1156 if tr:
1162 1157 tr.release()
1163 1158 lockmod.release(lock)
@@ -1,997 +1,997 b''
1 1 #testcases stripbased phasebased
2 2 #testcases abortflag abortcommand
3 3 #testcases continueflag continuecommand
4 4
5 5 $ cat <<EOF >> $HGRCPATH
6 6 > [extensions]
7 7 > mq =
8 8 > [defaults]
9 9 > diff = --nodates --git
10 10 > qnew = --date '0 0'
11 11 > [shelve]
12 12 > maxbackups = 2
13 13 > EOF
14 14
15 15 #if phasebased
16 16
17 17 $ cat <<EOF >> $HGRCPATH
18 18 > [format]
19 19 > internal-phase = yes
20 20 > EOF
21 21
22 22 #endif
23 23
24 24 #if abortflag
25 25 $ cat >> $HGRCPATH <<EOF
26 26 > [alias]
27 27 > abort = unshelve --abort
28 28 > EOF
29 29 #endif
30 30
31 31 #if continueflag
32 32 $ cat >> $HGRCPATH <<EOF
33 33 > [alias]
34 34 > continue = unshelve --continue
35 35 > EOF
36 36 #endif
37 37
38 38 shelve should leave dirstate clean (issue4055)
39 39
40 40 $ hg init shelverebase
41 41 $ cd shelverebase
42 42 $ printf 'x\ny\n' > x
43 43 $ echo z > z
44 44 $ hg commit -Aqm xy
45 45 $ echo z >> x
46 46 $ hg commit -Aqm z
47 47 $ hg up 5c4c67fb7dce
48 48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 49 $ printf 'a\nx\ny\nz\n' > x
50 50 $ hg commit -Aqm xyz
51 51 $ echo c >> z
52 52 $ hg shelve
53 53 shelved as default
54 54 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 55
56 56 $ hg rebase -d 6c103be8f4e4 --config extensions.rebase=
57 57 rebasing 2:323bfa07f744( tip)? "xyz" (re)
58 58 merging x
59 59 saved backup bundle to \$TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-(78114325|7ae538ef)-rebase.hg (re)
60 60 $ hg unshelve
61 61 unshelving change 'default'
62 62 rebasing shelved changes
63 63 $ hg status
64 64 M z
65 65
66 66 $ cd ..
67 67
68 68 shelve should only unshelve pending changes (issue4068)
69 69
70 70 $ hg init onlypendingchanges
71 71 $ cd onlypendingchanges
72 72 $ touch a
73 73 $ hg ci -Aqm a
74 74 $ touch b
75 75 $ hg ci -Aqm b
76 76 $ hg up -q 3903775176ed
77 77 $ touch c
78 78 $ hg ci -Aqm c
79 79
80 80 $ touch d
81 81 $ hg add d
82 82 $ hg shelve
83 83 shelved as default
84 84 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
85 85 $ hg up -q 0e067c57feba
86 86 $ hg unshelve
87 87 unshelving change 'default'
88 88 rebasing shelved changes
89 89 $ hg status
90 90 A d
91 91
92 92 unshelve should work on an ancestor of the original commit
93 93
94 94 $ hg shelve
95 95 shelved as default
96 96 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
97 97 $ hg up 3903775176ed
98 98 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
99 99 $ hg unshelve
100 100 unshelving change 'default'
101 101 rebasing shelved changes
102 102 $ hg status
103 103 A d
104 104
105 105 test bug 4073 we need to enable obsolete markers for it
106 106
107 107 $ cat >> $HGRCPATH << EOF
108 108 > [experimental]
109 109 > evolution.createmarkers=True
110 110 > EOF
111 111 $ hg shelve
112 112 shelved as default
113 113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 114 $ hg debugobsolete `hg log -r 0e067c57feba -T '{node}'`
115 115 1 new obsolescence markers
116 116 obsoleted 1 changesets
117 117 $ hg unshelve
118 118 unshelving change 'default'
119 119
120 120 unshelve should leave unknown files alone (issue4113)
121 121
122 122 $ echo e > e
123 123 $ hg shelve
124 124 shelved as default
125 125 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
126 126 $ hg status
127 127 ? e
128 128 $ hg unshelve
129 129 unshelving change 'default'
130 130 $ hg status
131 131 A d
132 132 ? e
133 133 $ cat e
134 134 e
135 135
136 136 unshelve should keep a copy of unknown files
137 137
138 138 $ hg add e
139 139 $ hg shelve
140 140 shelved as default
141 141 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
142 142 $ echo z > e
143 143 $ hg unshelve
144 144 unshelving change 'default'
145 145 $ cat e
146 146 e
147 147 $ cat e.orig
148 148 z
149 149 $ rm e.orig
150 150
151 151 restores backup of unknown file to right directory
152 152
153 153 $ hg shelve
154 154 shelved as default
155 155 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
156 156 $ echo z > e
157 157 $ mkdir dir
158 158 $ hg unshelve --cwd dir
159 159 unshelving change 'default'
160 160 $ rmdir dir
161 161 $ cat e
162 162 e
163 163 $ cat e.orig
164 164 z
165 165
166 166 unshelve and conflicts with tracked and untracked files
167 167
168 168 preparing:
169 169
170 170 $ rm -f *.orig
171 171 $ hg ci -qm 'commit stuff'
172 172 $ hg phase -p null:
173 173
174 174 no other changes - no merge:
175 175
176 176 $ echo f > f
177 177 $ hg add f
178 178 $ hg shelve
179 179 shelved as default
180 180 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 181 $ echo g > f
182 182 $ hg unshelve
183 183 unshelving change 'default'
184 184 $ hg st
185 185 A f
186 186 ? f.orig
187 187 $ cat f
188 188 f
189 189 $ cat f.orig
190 190 g
191 191
192 192 other uncommitted changes - merge:
193 193
194 194 $ hg st
195 195 A f
196 196 ? f.orig
197 197 $ hg shelve
198 198 shelved as default
199 199 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
200 200 #if repobundlerepo
201 201 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()' --hidden
202 202 o [48] changes to: commit stuff shelve@localhost (re)
203 203 |
204 204 ~
205 205 #endif
206 206 $ hg log -G --template '{rev} {desc|firstline} {author}'
207 207 @ [37] commit stuff test (re)
208 208 |
209 209 | o 2 c test
210 210 |/
211 211 o 0 a test
212 212
213 213 $ mv f.orig f
214 214 $ echo 1 > a
215 215 $ hg unshelve --date '1073741824 0'
216 216 unshelving change 'default'
217 217 temporarily committing pending changes (restore with 'hg unshelve --abort')
218 218 rebasing shelved changes
219 219 merging f
220 220 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
221 221 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
222 222 [240]
223 223
224 224 #if phasebased
225 225 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
226 226 @ 9 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
227 227 |
228 228 | @ 8 changes to: commit stuff shelve@localhost 1970-01-01 00:00 +0000
229 229 |/
230 230 o 7 commit stuff test 1970-01-01 00:00 +0000
231 231 |
232 232 | o 2 c test 1970-01-01 00:00 +0000
233 233 |/
234 234 o 0 a test 1970-01-01 00:00 +0000
235 235
236 236 #endif
237 237
238 238 #if stripbased
239 239 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
240 240 @ 5 changes to: commit stuff shelve@localhost 1970-01-01 00:00 +0000
241 241 |
242 242 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
243 243 |/
244 244 o 3 commit stuff test 1970-01-01 00:00 +0000
245 245 |
246 246 | o 2 c test 1970-01-01 00:00 +0000
247 247 |/
248 248 o 0 a test 1970-01-01 00:00 +0000
249 249
250 250 #endif
251 251
252 252 $ hg st
253 253 M f
254 254 ? f.orig
255 255 $ cat f
256 256 <<<<<<< working-copy: d44eae5c3d33 - shelve: pending changes temporary commit
257 257 g
258 258 =======
259 259 f
260 260 >>>>>>> shelve: aef214a5229c - shelve: changes to: commit stuff
261 261 $ cat f.orig
262 262 g
263 263 $ hg unshelve --abort -t false
264 264 tool option will be ignored
265 265 unshelve of 'default' aborted
266 266 $ hg st
267 267 M a
268 268 ? f.orig
269 269 $ cat f.orig
270 270 g
271 271 $ hg unshelve
272 272 unshelving change 'default'
273 273 temporarily committing pending changes (restore with 'hg unshelve --abort')
274 274 rebasing shelved changes
275 275 $ hg st
276 276 M a
277 277 A f
278 278 ? f.orig
279 279
280 280 other committed changes - merge:
281 281
282 282 $ hg shelve f
283 283 shelved as default
284 284 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
285 285 $ hg ci a -m 'intermediate other change'
286 286 $ mv f.orig f
287 287 $ hg unshelve
288 288 unshelving change 'default'
289 289 rebasing shelved changes
290 290 merging f
291 291 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
292 292 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
293 293 [240]
294 294 $ hg st
295 295 M f
296 296 ? f.orig
297 297 $ cat f
298 298 <<<<<<< working-copy: 6b563750f973 - test: intermediate other change
299 299 g
300 300 =======
301 301 f
302 302 >>>>>>> shelve: aef214a5229c - shelve: changes to: commit stuff
303 303 $ cat f.orig
304 304 g
305 305
306 306 #if abortcommand
307 307 when in dry-run mode
308 308 $ hg abort --dry-run
309 309 unshelve in progress, will be aborted
310 310 #endif
311 311
312 312 $ hg abort
313 313 unshelve of 'default' aborted
314 314 $ hg st
315 315 ? f.orig
316 316 $ cat f.orig
317 317 g
318 318 $ hg shelve --delete default
319 319 $ cd ..
320 320
321 321 you shouldn't be able to ask for the patch/stats of the most recent shelve if
322 322 there are no shelves
323 323
324 324 $ hg init noshelves
325 325 $ cd noshelves
326 326
327 327 $ hg shelve --patch
328 328 abort: there are no shelves to show
329 329 [255]
330 330 $ hg shelve --stat
331 331 abort: there are no shelves to show
332 332 [255]
333 333
334 334 $ cd ..
335 335
336 336 test .orig files go where the user wants them to
337 337 ---------------------------------------------------------------
338 338 $ hg init salvage
339 339 $ cd salvage
340 340 $ echo 'content' > root
341 341 $ hg commit -A -m 'root' -q
342 342 $ echo '' > root
343 343 $ hg shelve -q
344 344 $ echo 'contADDent' > root
345 345 $ hg unshelve -q --config 'ui.origbackuppath=.hg/origbackups'
346 346 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
347 347 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
348 348 [240]
349 349 $ ls .hg/origbackups
350 350 root
351 351 $ rm -rf .hg/origbackups
352 352
353 353 test Abort unshelve always gets user out of the unshelved state
354 354 ---------------------------------------------------------------
355 355
356 356 with a corrupted shelve state file
357 357 $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate
358 358 $ mv ../corrupt-shelvedstate .hg/shelvestate
359 359 $ hg unshelve --abort 2>&1 | grep 'aborted'
360 360 unshelve of 'default' aborted
361 361 $ hg summary
362 362 parent: 0:ae8c668541e8 tip
363 363 root
364 364 branch: default
365 365 commit: 1 modified
366 366 update: (current)
367 367 phases: 1 draft
368 368 $ hg up -C .
369 369 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 370
371 371 $ cd ..
372 372
373 373 Shelve and unshelve unknown files. For the purposes of unshelve, a shelved
374 374 unknown file is the same as a shelved added file, except that it will be in
375 375 unknown state after unshelve if and only if it was either absent or unknown
376 376 before the unshelve operation.
377 377
378 378 $ hg init unknowns
379 379 $ cd unknowns
380 380
381 381 The simplest case is if I simply have an unknown file that I shelve and unshelve
382 382
383 383 $ echo unknown > unknown
384 384 $ hg status
385 385 ? unknown
386 386 $ hg shelve --unknown
387 387 shelved as default
388 388 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
389 389 $ hg status
390 390 $ hg unshelve
391 391 unshelving change 'default'
392 392 $ hg status
393 393 ? unknown
394 394 $ rm unknown
395 395
396 396 If I shelve, add the file, and unshelve, does it stay added?
397 397
398 398 $ echo unknown > unknown
399 399 $ hg shelve -u
400 400 shelved as default
401 401 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
402 402 $ hg status
403 403 $ touch unknown
404 404 $ hg add unknown
405 405 $ hg status
406 406 A unknown
407 407 $ hg unshelve
408 408 unshelving change 'default'
409 409 temporarily committing pending changes (restore with 'hg unshelve --abort')
410 410 rebasing shelved changes
411 411 merging unknown
412 412 $ hg status
413 413 A unknown
414 414 $ hg forget unknown
415 415 $ rm unknown
416 416
417 417 And if I shelve, commit, then unshelve, does it become modified?
418 418
419 419 $ echo unknown > unknown
420 420 $ hg shelve -u
421 421 shelved as default
422 422 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
423 423 $ hg status
424 424 $ touch unknown
425 425 $ hg add unknown
426 426 $ hg commit -qm "Add unknown"
427 427 $ hg status
428 428 $ hg unshelve
429 429 unshelving change 'default'
430 430 rebasing shelved changes
431 431 merging unknown
432 432 $ hg status
433 433 M unknown
434 434 $ hg remove --force unknown
435 435 $ hg commit -qm "Remove unknown"
436 436
437 437 $ cd ..
438 438
439 439 We expects that non-bare shelve keeps newly created branch in
440 440 working directory.
441 441
442 442 $ hg init shelve-preserve-new-branch
443 443 $ cd shelve-preserve-new-branch
444 444 $ echo "a" >> a
445 445 $ hg add a
446 446 $ echo "b" >> b
447 447 $ hg add b
448 448 $ hg commit -m "ab"
449 449 $ echo "aa" >> a
450 450 $ echo "bb" >> b
451 451 $ hg branch new-branch
452 452 marked working directory as branch new-branch
453 453 (branches are permanent and global, did you want a bookmark?)
454 454 $ hg status
455 455 M a
456 456 M b
457 457 $ hg branch
458 458 new-branch
459 459 $ hg shelve a
460 460 shelved as default
461 461 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 462 $ hg branch
463 463 new-branch
464 464 $ hg status
465 465 M b
466 466 $ touch "c" >> c
467 467 $ hg add c
468 468 $ hg status
469 469 M b
470 470 A c
471 471 $ hg shelve --exclude c
472 472 shelved as default-01
473 473 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
474 474 $ hg branch
475 475 new-branch
476 476 $ hg status
477 477 A c
478 478 $ hg shelve --include c
479 479 shelved as default-02
480 480 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
481 481 $ hg branch
482 482 new-branch
483 483 $ hg status
484 484 $ echo "d" >> d
485 485 $ hg add d
486 486 $ hg status
487 487 A d
488 488
489 489 We expect that bare-shelve will not keep branch in current working directory.
490 490
491 491 $ hg shelve
492 492 shelved as default-03
493 493 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
494 494 $ hg branch
495 495 default
496 496 $ cd ..
497 497
498 498 When i shelve commit on newly created branch i expect
499 499 that after unshelve newly created branch will be preserved.
500 500
501 501 $ hg init shelve_on_new_branch_simple
502 502 $ cd shelve_on_new_branch_simple
503 503 $ echo "aaa" >> a
504 504 $ hg commit -A -m "a"
505 505 adding a
506 506 $ hg branch
507 507 default
508 508 $ hg branch test
509 509 marked working directory as branch test
510 510 (branches are permanent and global, did you want a bookmark?)
511 511 $ echo "bbb" >> a
512 512 $ hg status
513 513 M a
514 514 $ hg shelve
515 515 shelved as default
516 516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 517 $ hg branch
518 518 default
519 519 $ echo "bbb" >> b
520 520 $ hg status
521 521 ? b
522 522 $ hg unshelve
523 523 unshelving change 'default'
524 524 marked working directory as branch test
525 525 $ hg status
526 526 M a
527 527 ? b
528 528 $ hg branch
529 529 test
530 530 $ cd ..
531 531
532 532 When i shelve commit on newly created branch, make
533 533 some changes, unshelve it and running into merge
534 534 conflicts i expect that after fixing them and
535 535 running unshelve --continue newly created branch
536 536 will be preserved.
537 537
538 538 $ hg init shelve_on_new_branch_conflict
539 539 $ cd shelve_on_new_branch_conflict
540 540 $ echo "aaa" >> a
541 541 $ hg commit -A -m "a"
542 542 adding a
543 543 $ hg branch
544 544 default
545 545 $ hg branch test
546 546 marked working directory as branch test
547 547 (branches are permanent and global, did you want a bookmark?)
548 548 $ echo "bbb" >> a
549 549 $ hg status
550 550 M a
551 551 $ hg shelve
552 552 shelved as default
553 553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 554 $ hg branch
555 555 default
556 556 $ echo "ccc" >> a
557 557 $ hg status
558 558 M a
559 559 $ hg unshelve
560 560 unshelving change 'default'
561 561 temporarily committing pending changes (restore with 'hg unshelve --abort')
562 562 rebasing shelved changes
563 563 merging a
564 564 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
565 565 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
566 566 [240]
567 567 $ echo "aaabbbccc" > a
568 568 $ rm a.orig
569 569 $ hg resolve --mark a
570 570 (no more unresolved files)
571 571 continue: hg unshelve --continue
572 572 $ hg continue
573 573 marked working directory as branch test
574 574 unshelve of 'default' complete
575 575 $ cat a
576 576 aaabbbccc
577 577 $ hg status
578 578 M a
579 579 $ hg branch
580 580 test
581 581 $ hg commit -m "test-commit"
582 582
583 583 When i shelve on test branch, update to default branch
584 584 and unshelve i expect that it will not preserve previous
585 585 test branch.
586 586
587 587 $ echo "xxx" > b
588 588 $ hg add b
589 589 $ hg shelve
590 590 shelved as test
591 591 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
592 592 $ hg update -r 7049e48789d7
593 593 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
594 594 $ hg unshelve
595 595 unshelving change 'test'
596 596 rebasing shelved changes
597 597 $ hg status
598 598 A b
599 599 $ hg branch
600 600 default
601 601 $ cd ..
602 602
603 603 When i unshelve resulting in merge conflicts and makes saved
604 604 file shelvedstate looks like in previous versions in
605 605 mercurial(without restore branch information in 7th line) i
606 606 expect that after resolving conflicts and successfully
607 607 running 'shelve --continue' the branch information won't be
608 608 restored and branch will be unchanged.
609 609
610 610 shelve on new branch, conflict with previous shelvedstate
611 611
612 612 $ hg init conflict
613 613 $ cd conflict
614 614 $ echo "aaa" >> a
615 615 $ hg commit -A -m "a"
616 616 adding a
617 617 $ hg branch
618 618 default
619 619 $ hg branch test
620 620 marked working directory as branch test
621 621 (branches are permanent and global, did you want a bookmark?)
622 622 $ echo "bbb" >> a
623 623 $ hg status
624 624 M a
625 625 $ hg shelve
626 626 shelved as default
627 627 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
628 628 $ hg branch
629 629 default
630 630 $ echo "ccc" >> a
631 631 $ hg status
632 632 M a
633 633 $ hg unshelve
634 634 unshelving change 'default'
635 635 temporarily committing pending changes (restore with 'hg unshelve --abort')
636 636 rebasing shelved changes
637 637 merging a
638 638 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
639 639 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
640 640 [240]
641 641
642 642 Removing restore branch information from shelvedstate file(making it looks like
643 643 in previous versions) and running unshelve --continue
644 644
645 645 $ cp .hg/shelvedstate .hg/shelvedstate_old
646 646 $ cat .hg/shelvedstate_old | grep -v 'branchtorestore' > .hg/shelvedstate
647 647
648 648 $ echo "aaabbbccc" > a
649 649 $ rm a.orig
650 650 $ hg resolve --mark a
651 651 (no more unresolved files)
652 652 continue: hg unshelve --continue
653 653
654 654 #if continuecommand
655 655 $ hg continue --dry-run
656 656 unshelve in progress, will be resumed
657 657 #endif
658 658
659 659 $ hg continue
660 660 unshelve of 'default' complete
661 661 $ cat a
662 662 aaabbbccc
663 663 $ hg status
664 664 M a
665 665 $ hg branch
666 666 default
667 667 $ cd ..
668 668
669 669 On non bare shelve the branch information shouldn't be restored
670 670
671 671 $ hg init bare_shelve_on_new_branch
672 672 $ cd bare_shelve_on_new_branch
673 673 $ echo "aaa" >> a
674 674 $ hg commit -A -m "a"
675 675 adding a
676 676 $ hg branch
677 677 default
678 678 $ hg branch test
679 679 marked working directory as branch test
680 680 (branches are permanent and global, did you want a bookmark?)
681 681 $ echo "bbb" >> a
682 682 $ hg status
683 683 M a
684 684 $ hg shelve a
685 685 shelved as default
686 686 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
687 687 $ hg branch
688 688 test
689 689 $ hg branch default
690 690 marked working directory as branch default
691 691 (branches are permanent and global, did you want a bookmark?)
692 692 $ echo "bbb" >> b
693 693 $ hg status
694 694 ? b
695 695 $ hg unshelve
696 696 unshelving change 'default'
697 697 $ hg status
698 698 M a
699 699 ? b
700 700 $ hg branch
701 701 default
702 702 $ cd ..
703 703
704 704 Prepare unshelve with a corrupted shelvedstate
705 705 $ hg init r1 && cd r1
706 706 $ echo text1 > file && hg add file
707 707 $ hg shelve
708 708 shelved as default
709 709 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
710 710 $ echo text2 > file && hg ci -Am text1
711 711 adding file
712 712 $ hg unshelve
713 713 unshelving change 'default'
714 714 rebasing shelved changes
715 715 merging file
716 716 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
717 717 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
718 718 [240]
719 719 $ echo somethingsomething > .hg/shelvedstate
720 720
721 721 Unshelve --continue fails with appropriate message if shelvedstate is corrupted
722 722 $ hg continue
723 723 abort: corrupted shelved state file
724 724 (please run hg unshelve --abort to abort unshelve operation)
725 725 [255]
726 726
727 727 Unshelve --abort works with a corrupted shelvedstate
728 728 $ hg abort
729 729 abort: could not read shelved state file, your working copy may be in an unexpected state
730 730 please update to some commit
731 731
732 732 [255]
733 733
734 734 Unshelve --abort fails with appropriate message if there's no unshelve in
735 735 progress
736 736
737 737 #if abortflag
738 738 $ hg unshelve --abort
739 739 abort: no unshelve in progress
740 740 [20]
741 741 #else
742 742 $ hg abort
743 743 aborting the merge, updating back to 9451eaa6eee3
744 744 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
745 745 #endif
746 746 $ cd ..
747 747
748 748 Test corrupt shelves (in .hg/shelved/, not .hg/shelvestate)
749 749 $ hg init corrupt-shelves
750 750 $ cd corrupt-shelves
751 751 $ mkdir .hg/shelved
752 752
753 753 # A (corrupt) .patch file without a .hg file
754 754 $ touch .hg/shelved/junk1.patch
755 755 $ hg shelve -l
756 756 junk1 (* ago) (glob)
757 757 $ hg unshelve
758 758 unshelving change 'junk1'
759 abort: shelved change 'junk1' not found
759 abort: $ENOENT$: '$TESTTMP/corrupt-shelves/.hg/shelved/junk1.hg'
760 760 [255]
761 761 $ hg shelve -d junk1
762 762 $ find .hg/shelve* | sort
763 763 .hg/shelve-backup
764 764 .hg/shelve-backup/junk1.patch
765 765 .hg/shelved
766 766
767 767 # A .hg file without a .patch file
768 768 $ touch .hg/shelved/junk2.hg
769 769 $ hg shelve -l
770 770 $ hg unshelve
771 771 abort: no shelved changes to apply!
772 772 [255]
773 773 $ hg shelve -d junk2
774 774 abort: shelved change 'junk2' not found
775 775 [255]
776 776 $ find .hg/shelve* | sort
777 777 .hg/shelve-backup
778 778 .hg/shelve-backup/junk1.patch
779 779 .hg/shelved
780 780 .hg/shelved/junk2.hg
781 781
782 782 # A file with an unexpected extension
783 783 $ touch .hg/shelved/junk3
784 784 $ hg shelve -l 2>&1 | grep ValueError
785 785 ValueError: * (glob)
786 786 $ hg unshelve 2>&1 | grep ValueError
787 787 ValueError: * (glob)
788 788 $ hg shelve -d junk3
789 789 abort: shelved change 'junk3' not found
790 790 [255]
791 791 $ find .hg/shelve* | sort
792 792 .hg/shelve-backup
793 793 .hg/shelve-backup/junk1.patch
794 794 .hg/shelved
795 795 .hg/shelved/junk2.hg
796 796 .hg/shelved/junk3
797 797
798 798 $ cd ..
799 799
800 800 Unshelve respects --keep even if user intervention is needed
801 801 $ hg init unshelvekeep && cd unshelvekeep
802 802 $ echo 1 > file && hg ci -Am 1
803 803 adding file
804 804 $ echo 2 >> file
805 805 $ hg shelve
806 806 shelved as default
807 807 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
808 808 $ echo 3 >> file && hg ci -Am 13
809 809 $ hg shelve --list
810 810 default (*s ago) * changes to: 1 (glob)
811 811 $ hg unshelve --keep
812 812 unshelving change 'default'
813 813 rebasing shelved changes
814 814 merging file
815 815 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
816 816 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
817 817 [240]
818 818 $ hg resolve --mark file
819 819 (no more unresolved files)
820 820 continue: hg unshelve --continue
821 821 $ hg continue
822 822 unshelve of 'default' complete
823 823 $ hg shelve --list
824 824 default (*s ago) * changes to: 1 (glob)
825 825 $ cd ..
826 826
827 827 Unshelving when there are deleted files does not crash (issue4176)
828 828 $ hg init unshelve-deleted-file && cd unshelve-deleted-file
829 829 $ echo a > a && echo b > b && hg ci -Am ab
830 830 adding a
831 831 adding b
832 832 $ echo aa > a && hg shelve
833 833 shelved as default
834 834 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
835 835 $ rm b
836 836 $ hg st
837 837 ! b
838 838 $ hg unshelve
839 839 unshelving change 'default'
840 840 $ hg shelve
841 841 shelved as default
842 842 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
843 843 $ rm a && echo b > b
844 844 $ hg st
845 845 ! a
846 846 $ hg unshelve
847 847 unshelving change 'default'
848 848 abort: shelved change touches missing files
849 849 (run hg status to see which files are missing)
850 850 [255]
851 851 $ hg st
852 852 ! a
853 853 $ cd ..
854 854
855 855 New versions of Mercurial know how to read onld shelvedstate files
856 856 $ hg init oldshelvedstate
857 857 $ cd oldshelvedstate
858 858 $ echo root > root && hg ci -Am root
859 859 adding root
860 860 $ echo 1 > a
861 861 $ hg add a
862 862 $ hg shelve --name ashelve
863 863 shelved as ashelve
864 864 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
865 865 $ echo 2 > a
866 866 $ hg ci -Am a
867 867 adding a
868 868 $ hg unshelve
869 869 unshelving change 'ashelve'
870 870 rebasing shelved changes
871 871 merging a
872 872 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
873 873 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
874 874 [240]
875 875 putting v1 shelvedstate file in place of a created v2
876 876 $ cat << EOF > .hg/shelvedstate
877 877 > 1
878 878 > ashelve
879 879 > 8b058dae057a5a78f393f4535d9e363dd5efac9d
880 880 > 8b058dae057a5a78f393f4535d9e363dd5efac9d
881 881 > 8b058dae057a5a78f393f4535d9e363dd5efac9d f543b27db2cdb41737e2e0008dc524c471da1446
882 882 > f543b27db2cdb41737e2e0008dc524c471da1446
883 883 >
884 884 > nokeep
885 885 > :no-active-bookmark
886 886 > EOF
887 887 $ echo 1 > a
888 888 $ hg resolve --mark a
889 889 (no more unresolved files)
890 890 continue: hg unshelve --continue
891 891 mercurial does not crash
892 892 $ hg continue
893 893 unshelve of 'ashelve' complete
894 894
895 895 #if phasebased
896 896
897 897 Unshelve with some metadata file missing
898 898 ----------------------------------------
899 899
900 900 $ hg shelve
901 901 shelved as default
902 902 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
903 903 $ echo 3 > a
904 904
905 905 Test with the `.shelve` missing, but the changeset still in the repo (non-natural case)
906 906
907 907 $ rm .hg/shelved/default.shelve
908 908 $ hg unshelve
909 909 unshelving change 'default'
910 910 temporarily committing pending changes (restore with 'hg unshelve --abort')
911 911 rebasing shelved changes
912 912 merging a
913 913 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
914 914 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
915 915 [240]
916 916 $ hg abort
917 917 unshelve of 'default' aborted
918 918
919 919 Unshelve without .shelve metadata (can happen when upgrading a repository with old shelve)
920 920
921 921 $ cat .hg/shelved/default.shelve
922 922 node=82e0cb9893247d12667017593ce1e5655860f1ac
923 923 $ hg strip --hidden --rev 82e0cb989324 --no-backup
924 924 $ rm .hg/shelved/default.shelve
925 925 $ hg unshelve
926 926 unshelving change 'default'
927 927 temporarily committing pending changes (restore with 'hg unshelve --abort')
928 928 rebasing shelved changes
929 929 merging a
930 930 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
931 931 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
932 932 [240]
933 933 $ cat .hg/shelved/default.shelve
934 934 node=82e0cb9893247d12667017593ce1e5655860f1ac
935 935 $ hg abort
936 936 unshelve of 'default' aborted
937 937
938 938 #endif
939 939
940 940 $ cd ..
941 941
942 942 Block merge abort when unshelve in progress(issue6160)
943 943 ------------------------------------------------------
944 944
945 945 $ hg init a
946 946 $ cd a
947 947 $ echo foo > a ; hg commit -qAm "initial commit"
948 948 $ echo bar > a
949 949 $ hg shelve
950 950 shelved as default
951 951 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
952 952 $ echo foobar > a
953 953 $ hg unshelve
954 954 unshelving change 'default'
955 955 temporarily committing pending changes (restore with 'hg unshelve --abort')
956 956 rebasing shelved changes
957 957 merging a
958 958 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
959 959 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
960 960 [240]
961 961
962 962 $ hg log --template '{desc|firstline} {author} {date|isodate} \n' -r .
963 963 pending changes temporary commit shelve@localhost 1970-01-01 00:00 +0000
964 964 $ hg merge --abort
965 965 abort: cannot abort merge with unshelve in progress
966 966 (use 'hg unshelve --continue' or 'hg unshelve --abort')
967 967 [20]
968 968
969 969 $ hg unshelve --abort
970 970 unshelve of 'default' aborted
971 971
972 972 $ hg log -G --template '{desc|firstline} {author} {date|isodate} \n' -r .
973 973 @ initial commit test 1970-01-01 00:00 +0000
974 974
975 975 $ cd ..
976 976
977 977 Demonstrate that the labels are correct in the merge conflict
978 978 -------------------------------------------------------------
979 979 $ hg init labels
980 980 $ cd labels
981 981 $ echo r0 > foo
982 982 $ hg ci -qAm r0
983 983 $ echo "this will be shelved" >> foo
984 984 $ hg shelve -q
985 985 $ echo "this is in wdir, conflicts with shelve" >> foo
986 986 $ hg unshelve -q
987 987 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
988 988 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
989 989 [240]
990 990 $ cat foo
991 991 r0
992 992 <<<<<<< working-copy: 0b2fcf2a90e9 - shelve: pending changes temporary commit
993 993 this is in wdir, conflicts with shelve
994 994 =======
995 995 this will be shelved
996 996 >>>>>>> shelve: 9c072a2163db - shelve: changes to: r0
997 997 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now