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