##// END OF EJS Templates
shelve: delete shelve statefile on any exception during abort...
Christian Delahousse -
r26681:ca8170b5 default
parent child Browse files
Show More
@@ -1,833 +1,833 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
24 24 import collections
25 25 import itertools
26 26 from mercurial.i18n import _
27 27 from mercurial.node import nullid, nullrev, bin, hex
28 28 from mercurial import changegroup, cmdutil, scmutil, phases, commands
29 29 from mercurial import error, hg, mdiff, merge, patch, repair, util
30 30 from mercurial import templatefilters, exchange, bundlerepo
31 31 from mercurial import lock as lockmod
32 32 from hgext import rebase
33 33 import errno
34 34
35 35 cmdtable = {}
36 36 command = cmdutil.command(cmdtable)
37 37 # Note for extension authors: ONLY specify testedwith = 'internal' for
38 38 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
39 39 # be specifying the version(s) of Mercurial they are tested with, or
40 40 # leave the attribute unspecified.
41 41 testedwith = 'internal'
42 42
43 43 backupdir = 'shelve-backup'
44 44
45 45 class shelvedfile(object):
46 46 """Helper for the file storing a single shelve
47 47
48 48 Handles common functions on shelve files (.hg/.patch) using
49 49 the vfs layer"""
50 50 def __init__(self, repo, name, filetype=None):
51 51 self.repo = repo
52 52 self.name = name
53 53 self.vfs = scmutil.vfs(repo.join('shelved'))
54 54 self.backupvfs = scmutil.vfs(repo.join(backupdir))
55 55 self.ui = self.repo.ui
56 56 if filetype:
57 57 self.fname = name + '.' + filetype
58 58 else:
59 59 self.fname = name
60 60
61 61 def exists(self):
62 62 return self.vfs.exists(self.fname)
63 63
64 64 def filename(self):
65 65 return self.vfs.join(self.fname)
66 66
67 67 def backupfilename(self):
68 68 def gennames(base):
69 69 yield base
70 70 base, ext = base.rsplit('.', 1)
71 71 for i in itertools.count(1):
72 72 yield '%s-%d.%s' % (base, i, ext)
73 73
74 74 name = self.backupvfs.join(self.fname)
75 75 for n in gennames(name):
76 76 if not self.backupvfs.exists(n):
77 77 return n
78 78
79 79 def movetobackup(self):
80 80 if not self.backupvfs.isdir():
81 81 self.backupvfs.makedir()
82 82 util.rename(self.filename(), self.backupfilename())
83 83
84 84 def stat(self):
85 85 return self.vfs.stat(self.fname)
86 86
87 87 def opener(self, mode='rb'):
88 88 try:
89 89 return self.vfs(self.fname, mode)
90 90 except IOError as err:
91 91 if err.errno != errno.ENOENT:
92 92 raise
93 93 raise error.Abort(_("shelved change '%s' not found") % self.name)
94 94
95 95 def applybundle(self):
96 96 fp = self.opener()
97 97 try:
98 98 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
99 99 changegroup.addchangegroup(self.repo, gen, 'unshelve',
100 100 'bundle:' + self.vfs.join(self.fname),
101 101 targetphase=phases.secret)
102 102 finally:
103 103 fp.close()
104 104
105 105 def bundlerepo(self):
106 106 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
107 107 self.vfs.join(self.fname))
108 108 def writebundle(self, bases, node):
109 109 btype = 'HG10BZ'
110 110 cgversion = '01'
111 111 compression = None
112 112 if 'generaldelta' in self.repo.requirements:
113 113 btype = 'HG20'
114 114 cgversion = '02'
115 115 compression = 'BZ'
116 116
117 117 cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
118 118 version=cgversion)
119 119 changegroup.writebundle(self.ui, cg, self.fname, btype, self.vfs,
120 120 compression=compression)
121 121
122 122 class shelvedstate(object):
123 123 """Handle persistence during unshelving operations.
124 124
125 125 Handles saving and restoring a shelved state. Ensures that different
126 126 versions of a shelved state are possible and handles them appropriately.
127 127 """
128 128 _version = 1
129 129 _filename = 'shelvedstate'
130 130
131 131 @classmethod
132 132 def load(cls, repo):
133 133 fp = repo.vfs(cls._filename)
134 134 try:
135 135 version = int(fp.readline().strip())
136 136
137 137 if version != cls._version:
138 138 raise error.Abort(_('this version of shelve is incompatible '
139 139 'with the version used in this repo'))
140 140 name = fp.readline().strip()
141 141 wctx = fp.readline().strip()
142 142 pendingctx = fp.readline().strip()
143 143 parents = [bin(h) for h in fp.readline().split()]
144 144 stripnodes = [bin(h) for h in fp.readline().split()]
145 145 finally:
146 146 fp.close()
147 147
148 148 obj = cls()
149 149 obj.name = name
150 150 obj.wctx = repo[bin(wctx)]
151 151 obj.pendingctx = repo[bin(pendingctx)]
152 152 obj.parents = parents
153 153 obj.stripnodes = stripnodes
154 154
155 155 return obj
156 156
157 157 @classmethod
158 158 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
159 159 fp = repo.vfs(cls._filename, 'wb')
160 160 fp.write('%i\n' % cls._version)
161 161 fp.write('%s\n' % name)
162 162 fp.write('%s\n' % hex(originalwctx.node()))
163 163 fp.write('%s\n' % hex(pendingctx.node()))
164 164 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
165 165 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
166 166 fp.close()
167 167
168 168 @classmethod
169 169 def clear(cls, repo):
170 170 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
171 171
172 172 def cleanupoldbackups(repo):
173 173 vfs = scmutil.vfs(repo.join(backupdir))
174 174 maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
175 175 hgfiles = [f for f in vfs.listdir() if f.endswith('.hg')]
176 176 hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
177 177 if 0 < maxbackups and maxbackups < len(hgfiles):
178 178 bordermtime = hgfiles[-maxbackups][0]
179 179 else:
180 180 bordermtime = None
181 181 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
182 182 if mtime == bordermtime:
183 183 # keep it, because timestamp can't decide exact order of backups
184 184 continue
185 185 base = f[:-3]
186 186 for ext in 'hg patch'.split():
187 187 try:
188 188 vfs.unlink(base + '.' + ext)
189 189 except OSError as err:
190 190 if err.errno != errno.ENOENT:
191 191 raise
192 192
193 193 def _aborttransaction(repo):
194 194 '''Abort current transaction for shelve/unshelve, but keep dirstate
195 195 '''
196 196 backupname = 'dirstate.shelve'
197 197 dirstatebackup = None
198 198 try:
199 199 # create backup of (un)shelved dirstate, because aborting transaction
200 200 # should restore dirstate to one at the beginning of the
201 201 # transaction, which doesn't include the result of (un)shelving
202 202 fp = repo.vfs.open(backupname, "w")
203 203 dirstatebackup = backupname
204 204 # clearing _dirty/_dirtypl of dirstate by _writedirstate below
205 205 # is unintentional. but it doesn't cause problem in this case,
206 206 # because no code path refers them until transaction is aborted.
207 207 repo.dirstate._writedirstate(fp) # write in-memory changes forcibly
208 208
209 209 tr = repo.currenttransaction()
210 210 tr.abort()
211 211
212 212 # restore to backuped dirstate
213 213 repo.vfs.rename(dirstatebackup, 'dirstate')
214 214 dirstatebackup = None
215 215 finally:
216 216 if dirstatebackup:
217 217 repo.vfs.unlink(dirstatebackup)
218 218
219 219 def createcmd(ui, repo, pats, opts):
220 220 """subcommand that creates a new shelve"""
221 221
222 222 def mutableancestors(ctx):
223 223 """return all mutable ancestors for ctx (included)
224 224
225 225 Much faster than the revset ancestors(ctx) & draft()"""
226 226 seen = set([nullrev])
227 227 visit = collections.deque()
228 228 visit.append(ctx)
229 229 while visit:
230 230 ctx = visit.popleft()
231 231 yield ctx.node()
232 232 for parent in ctx.parents():
233 233 rev = parent.rev()
234 234 if rev not in seen:
235 235 seen.add(rev)
236 236 if parent.mutable():
237 237 visit.append(parent)
238 238
239 239 wctx = repo[None]
240 240 parents = wctx.parents()
241 241 if len(parents) > 1:
242 242 raise error.Abort(_('cannot shelve while merging'))
243 243 parent = parents[0]
244 244
245 245 # we never need the user, so we use a generic user for all shelve operations
246 246 user = 'shelve@localhost'
247 247 label = repo._activebookmark or parent.branch() or 'default'
248 248
249 249 # slashes aren't allowed in filenames, therefore we rename it
250 250 label = label.replace('/', '_')
251 251
252 252 def gennames():
253 253 yield label
254 254 for i in xrange(1, 100):
255 255 yield '%s-%02d' % (label, i)
256 256
257 257 def commitfunc(ui, repo, message, match, opts):
258 258 hasmq = util.safehasattr(repo, 'mq')
259 259 if hasmq:
260 260 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
261 261 backup = repo.ui.backupconfig('phases', 'new-commit')
262 262 try:
263 263 repo.ui. setconfig('phases', 'new-commit', phases.secret)
264 264 editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
265 265 return repo.commit(message, user, opts.get('date'), match,
266 266 editor=editor)
267 267 finally:
268 268 repo.ui.restoreconfig(backup)
269 269 if hasmq:
270 270 repo.mq.checkapplied = saved
271 271
272 272 if parent.node() != nullid:
273 273 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
274 274 else:
275 275 desc = '(changes in empty repository)'
276 276
277 277 if not opts['message']:
278 278 opts['message'] = desc
279 279
280 280 name = opts['name']
281 281
282 282 wlock = lock = tr = None
283 283 try:
284 284 wlock = repo.wlock()
285 285 lock = repo.lock()
286 286
287 287 # use an uncommitted transaction to generate the bundle to avoid
288 288 # pull races. ensure we don't print the abort message to stderr.
289 289 tr = repo.transaction('commit', report=lambda x: None)
290 290
291 291 if name:
292 292 if shelvedfile(repo, name, 'hg').exists():
293 293 raise error.Abort(_("a shelved change named '%s' already exists"
294 294 ) % name)
295 295 else:
296 296 for n in gennames():
297 297 if not shelvedfile(repo, n, 'hg').exists():
298 298 name = n
299 299 break
300 300 else:
301 301 raise error.Abort(_("too many shelved changes named '%s'") %
302 302 label)
303 303
304 304 # ensure we are not creating a subdirectory or a hidden file
305 305 if '/' in name or '\\' in name:
306 306 raise error.Abort(_('shelved change names may not contain slashes'))
307 307 if name.startswith('.'):
308 308 raise error.Abort(_("shelved change names may not start with '.'"))
309 309 interactive = opts.get('interactive', False)
310 310
311 311 def interactivecommitfunc(ui, repo, *pats, **opts):
312 312 match = scmutil.match(repo['.'], pats, {})
313 313 message = opts['message']
314 314 return commitfunc(ui, repo, message, match, opts)
315 315 if not interactive:
316 316 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
317 317 else:
318 318 node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
319 319 False, cmdutil.recordfilter, *pats, **opts)
320 320 if not node:
321 321 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
322 322 if stat.deleted:
323 323 ui.status(_("nothing changed (%d missing files, see "
324 324 "'hg status')\n") % len(stat.deleted))
325 325 else:
326 326 ui.status(_("nothing changed\n"))
327 327 return 1
328 328
329 329 bases = list(mutableancestors(repo[node]))
330 330 shelvedfile(repo, name, 'hg').writebundle(bases, node)
331 331 cmdutil.export(repo, [node],
332 332 fp=shelvedfile(repo, name, 'patch').opener('wb'),
333 333 opts=mdiff.diffopts(git=True))
334 334
335 335
336 336 if ui.formatted():
337 337 desc = util.ellipsis(desc, ui.termwidth())
338 338 ui.status(_('shelved as %s\n') % name)
339 339 hg.update(repo, parent.node())
340 340
341 341 _aborttransaction(repo)
342 342 finally:
343 343 lockmod.release(tr, lock, wlock)
344 344
345 345 def cleanupcmd(ui, repo):
346 346 """subcommand that deletes all shelves"""
347 347
348 348 wlock = None
349 349 try:
350 350 wlock = repo.wlock()
351 351 for (name, _type) in repo.vfs.readdir('shelved'):
352 352 suffix = name.rsplit('.', 1)[-1]
353 353 if suffix in ('hg', 'patch'):
354 354 shelvedfile(repo, name).movetobackup()
355 355 cleanupoldbackups(repo)
356 356 finally:
357 357 lockmod.release(wlock)
358 358
359 359 def deletecmd(ui, repo, pats):
360 360 """subcommand that deletes a specific shelve"""
361 361 if not pats:
362 362 raise error.Abort(_('no shelved changes specified!'))
363 363 wlock = repo.wlock()
364 364 try:
365 365 for name in pats:
366 366 for suffix in 'hg patch'.split():
367 367 shelvedfile(repo, name, suffix).movetobackup()
368 368 cleanupoldbackups(repo)
369 369 except OSError as err:
370 370 if err.errno != errno.ENOENT:
371 371 raise
372 372 raise error.Abort(_("shelved change '%s' not found") % name)
373 373 finally:
374 374 lockmod.release(wlock)
375 375
376 376 def listshelves(repo):
377 377 """return all shelves in repo as list of (time, filename)"""
378 378 try:
379 379 names = repo.vfs.readdir('shelved')
380 380 except OSError as err:
381 381 if err.errno != errno.ENOENT:
382 382 raise
383 383 return []
384 384 info = []
385 385 for (name, _type) in names:
386 386 pfx, sfx = name.rsplit('.', 1)
387 387 if not pfx or sfx != 'patch':
388 388 continue
389 389 st = shelvedfile(repo, name).stat()
390 390 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
391 391 return sorted(info, reverse=True)
392 392
393 393 def listcmd(ui, repo, pats, opts):
394 394 """subcommand that displays the list of shelves"""
395 395 pats = set(pats)
396 396 width = 80
397 397 if not ui.plain():
398 398 width = ui.termwidth()
399 399 namelabel = 'shelve.newest'
400 400 for mtime, name in listshelves(repo):
401 401 sname = util.split(name)[1]
402 402 if pats and sname not in pats:
403 403 continue
404 404 ui.write(sname, label=namelabel)
405 405 namelabel = 'shelve.name'
406 406 if ui.quiet:
407 407 ui.write('\n')
408 408 continue
409 409 ui.write(' ' * (16 - len(sname)))
410 410 used = 16
411 411 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
412 412 ui.write(age, label='shelve.age')
413 413 ui.write(' ' * (12 - len(age)))
414 414 used += 12
415 415 fp = open(name + '.patch', 'rb')
416 416 try:
417 417 while True:
418 418 line = fp.readline()
419 419 if not line:
420 420 break
421 421 if not line.startswith('#'):
422 422 desc = line.rstrip()
423 423 if ui.formatted():
424 424 desc = util.ellipsis(desc, width - used)
425 425 ui.write(desc)
426 426 break
427 427 ui.write('\n')
428 428 if not (opts['patch'] or opts['stat']):
429 429 continue
430 430 difflines = fp.readlines()
431 431 if opts['patch']:
432 432 for chunk, label in patch.difflabel(iter, difflines):
433 433 ui.write(chunk, label=label)
434 434 if opts['stat']:
435 435 for chunk, label in patch.diffstatui(difflines, width=width,
436 436 git=True):
437 437 ui.write(chunk, label=label)
438 438 finally:
439 439 fp.close()
440 440
441 441 def singlepatchcmds(ui, repo, pats, opts, subcommand):
442 442 """subcommand that displays a single shelf"""
443 443 if len(pats) != 1:
444 444 raise error.Abort(_("--%s expects a single shelf") % subcommand)
445 445 shelfname = pats[0]
446 446
447 447 if not shelvedfile(repo, shelfname, 'patch').exists():
448 448 raise error.Abort(_("cannot find shelf %s") % shelfname)
449 449
450 450 listcmd(ui, repo, pats, opts)
451 451
452 452 def checkparents(repo, state):
453 453 """check parent while resuming an unshelve"""
454 454 if state.parents != repo.dirstate.parents():
455 455 raise error.Abort(_('working directory parents do not match unshelve '
456 456 'state'))
457 457
458 458 def pathtofiles(repo, files):
459 459 cwd = repo.getcwd()
460 460 return [repo.pathto(f, cwd) for f in files]
461 461
462 462 def unshelveabort(ui, repo, state, opts):
463 463 """subcommand that abort an in-progress unshelve"""
464 464 wlock = repo.wlock()
465 465 lock = None
466 466 try:
467 467 checkparents(repo, state)
468 468
469 469 util.rename(repo.join('unshelverebasestate'),
470 470 repo.join('rebasestate'))
471 471 try:
472 472 rebase.rebase(ui, repo, **{
473 473 'abort' : True
474 474 })
475 475 except Exception:
476 476 util.rename(repo.join('rebasestate'),
477 477 repo.join('unshelverebasestate'))
478 478 raise
479 479
480 480 lock = repo.lock()
481 481
482 482 mergefiles(ui, repo, state.wctx, state.pendingctx)
483 483
484 484 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
485 finally:
485 486 shelvedstate.clear(repo)
486 487 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
487 finally:
488 488 lockmod.release(lock, wlock)
489 489
490 490 def mergefiles(ui, repo, wctx, shelvectx):
491 491 """updates to wctx and merges the changes from shelvectx into the
492 492 dirstate."""
493 493 oldquiet = ui.quiet
494 494 try:
495 495 ui.quiet = True
496 496 hg.update(repo, wctx.node())
497 497 files = []
498 498 files.extend(shelvectx.files())
499 499 files.extend(shelvectx.parents()[0].files())
500 500
501 501 # revert will overwrite unknown files, so move them out of the way
502 502 for file in repo.status(unknown=True).unknown:
503 503 if file in files:
504 504 util.rename(file, file + ".orig")
505 505 ui.pushbuffer(True)
506 506 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
507 507 *pathtofiles(repo, files),
508 508 **{'no_backup': True})
509 509 ui.popbuffer()
510 510 finally:
511 511 ui.quiet = oldquiet
512 512
513 513 def unshelvecleanup(ui, repo, name, opts):
514 514 """remove related files after an unshelve"""
515 515 if not opts['keep']:
516 516 for filetype in 'hg patch'.split():
517 517 shelvedfile(repo, name, filetype).movetobackup()
518 518 cleanupoldbackups(repo)
519 519
520 520 def unshelvecontinue(ui, repo, state, opts):
521 521 """subcommand to continue an in-progress unshelve"""
522 522 # We're finishing off a merge. First parent is our original
523 523 # parent, second is the temporary "fake" commit we're unshelving.
524 524 wlock = repo.wlock()
525 525 lock = None
526 526 try:
527 527 checkparents(repo, state)
528 528 ms = merge.mergestate(repo)
529 529 if [f for f in ms if ms[f] == 'u']:
530 530 raise error.Abort(
531 531 _("unresolved conflicts, can't continue"),
532 532 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
533 533
534 534 lock = repo.lock()
535 535
536 536 util.rename(repo.join('unshelverebasestate'),
537 537 repo.join('rebasestate'))
538 538 try:
539 539 rebase.rebase(ui, repo, **{
540 540 'continue' : True
541 541 })
542 542 except Exception:
543 543 util.rename(repo.join('rebasestate'),
544 544 repo.join('unshelverebasestate'))
545 545 raise
546 546
547 547 shelvectx = repo['tip']
548 548 if not shelvectx in state.pendingctx.children():
549 549 # rebase was a no-op, so it produced no child commit
550 550 shelvectx = state.pendingctx
551 551 else:
552 552 # only strip the shelvectx if the rebase produced it
553 553 state.stripnodes.append(shelvectx.node())
554 554
555 555 mergefiles(ui, repo, state.wctx, shelvectx)
556 556
557 557 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
558 558 shelvedstate.clear(repo)
559 559 unshelvecleanup(ui, repo, state.name, opts)
560 560 ui.status(_("unshelve of '%s' complete\n") % state.name)
561 561 finally:
562 562 lockmod.release(lock, wlock)
563 563
564 564 @command('unshelve',
565 565 [('a', 'abort', None,
566 566 _('abort an incomplete unshelve operation')),
567 567 ('c', 'continue', None,
568 568 _('continue an incomplete unshelve operation')),
569 569 ('', 'keep', None,
570 570 _('keep shelve after unshelving')),
571 571 ('', 'date', '',
572 572 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
573 573 _('hg unshelve [SHELVED]'))
574 574 def unshelve(ui, repo, *shelved, **opts):
575 575 """restore a shelved change to the working directory
576 576
577 577 This command accepts an optional name of a shelved change to
578 578 restore. If none is given, the most recent shelved change is used.
579 579
580 580 If a shelved change is applied successfully, the bundle that
581 581 contains the shelved changes is moved to a backup location
582 582 (.hg/shelve-backup).
583 583
584 584 Since you can restore a shelved change on top of an arbitrary
585 585 commit, it is possible that unshelving will result in a conflict
586 586 between your changes and the commits you are unshelving onto. If
587 587 this occurs, you must resolve the conflict, then use
588 588 ``--continue`` to complete the unshelve operation. (The bundle
589 589 will not be moved until you successfully complete the unshelve.)
590 590
591 591 (Alternatively, you can use ``--abort`` to abandon an unshelve
592 592 that causes a conflict. This reverts the unshelved changes, and
593 593 leaves the bundle in place.)
594 594
595 595 After a successful unshelve, the shelved changes are stored in a
596 596 backup directory. Only the N most recent backups are kept. N
597 597 defaults to 10 but can be overridden using the ``shelve.maxbackups``
598 598 configuration option.
599 599
600 600 .. container:: verbose
601 601
602 602 Timestamp in seconds is used to decide order of backups. More
603 603 than ``maxbackups`` backups are kept, if same timestamp
604 604 prevents from deciding exact order of them, for safety.
605 605 """
606 606 abortf = opts['abort']
607 607 continuef = opts['continue']
608 608 if not abortf and not continuef:
609 609 cmdutil.checkunfinished(repo)
610 610
611 611 if abortf or continuef:
612 612 if abortf and continuef:
613 613 raise error.Abort(_('cannot use both abort and continue'))
614 614 if shelved:
615 615 raise error.Abort(_('cannot combine abort/continue with '
616 616 'naming a shelved change'))
617 617
618 618 try:
619 619 state = shelvedstate.load(repo)
620 620 except IOError as err:
621 621 if err.errno != errno.ENOENT:
622 622 raise
623 623 raise error.Abort(_('no unshelve operation underway'))
624 624
625 625 if abortf:
626 626 return unshelveabort(ui, repo, state, opts)
627 627 elif continuef:
628 628 return unshelvecontinue(ui, repo, state, opts)
629 629 elif len(shelved) > 1:
630 630 raise error.Abort(_('can only unshelve one change at a time'))
631 631 elif not shelved:
632 632 shelved = listshelves(repo)
633 633 if not shelved:
634 634 raise error.Abort(_('no shelved changes to apply!'))
635 635 basename = util.split(shelved[0][1])[1]
636 636 ui.status(_("unshelving change '%s'\n") % basename)
637 637 else:
638 638 basename = shelved[0]
639 639
640 640 if not shelvedfile(repo, basename, 'patch').exists():
641 641 raise error.Abort(_("shelved change '%s' not found") % basename)
642 642
643 643 oldquiet = ui.quiet
644 644 wlock = lock = tr = None
645 645 try:
646 646 wlock = repo.wlock()
647 647 lock = repo.lock()
648 648
649 649 tr = repo.transaction('unshelve', report=lambda x: None)
650 650 oldtiprev = len(repo)
651 651
652 652 pctx = repo['.']
653 653 tmpwctx = pctx
654 654 # The goal is to have a commit structure like so:
655 655 # ...-> pctx -> tmpwctx -> shelvectx
656 656 # where tmpwctx is an optional commit with the user's pending changes
657 657 # and shelvectx is the unshelved changes. Then we merge it all down
658 658 # to the original pctx.
659 659
660 660 # Store pending changes in a commit
661 661 s = repo.status()
662 662 if s.modified or s.added or s.removed or s.deleted:
663 663 ui.status(_("temporarily committing pending changes "
664 664 "(restore with 'hg unshelve --abort')\n"))
665 665 def commitfunc(ui, repo, message, match, opts):
666 666 hasmq = util.safehasattr(repo, 'mq')
667 667 if hasmq:
668 668 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
669 669
670 670 backup = repo.ui.backupconfig('phases', 'new-commit')
671 671 try:
672 672 repo.ui. setconfig('phases', 'new-commit', phases.secret)
673 673 return repo.commit(message, 'shelve@localhost',
674 674 opts.get('date'), match)
675 675 finally:
676 676 repo.ui.restoreconfig(backup)
677 677 if hasmq:
678 678 repo.mq.checkapplied = saved
679 679
680 680 tempopts = {}
681 681 tempopts['message'] = "pending changes temporary commit"
682 682 tempopts['date'] = opts.get('date')
683 683 ui.quiet = True
684 684 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
685 685 tmpwctx = repo[node]
686 686
687 687 ui.quiet = True
688 688 shelvedfile(repo, basename, 'hg').applybundle()
689 689
690 690 ui.quiet = oldquiet
691 691
692 692 shelvectx = repo['tip']
693 693
694 694 # If the shelve is not immediately on top of the commit
695 695 # we'll be merging with, rebase it to be on top.
696 696 if tmpwctx.node() != shelvectx.parents()[0].node():
697 697 ui.status(_('rebasing shelved changes\n'))
698 698 try:
699 699 rebase.rebase(ui, repo, **{
700 700 'rev' : [shelvectx.rev()],
701 701 'dest' : str(tmpwctx.rev()),
702 702 'keep' : True,
703 703 })
704 704 except error.InterventionRequired:
705 705 tr.close()
706 706
707 707 stripnodes = [repo.changelog.node(rev)
708 708 for rev in xrange(oldtiprev, len(repo))]
709 709 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
710 710
711 711 util.rename(repo.join('rebasestate'),
712 712 repo.join('unshelverebasestate'))
713 713 raise error.InterventionRequired(
714 714 _("unresolved conflicts (see 'hg resolve', then "
715 715 "'hg unshelve --continue')"))
716 716
717 717 # refresh ctx after rebase completes
718 718 shelvectx = repo['tip']
719 719
720 720 if not shelvectx in tmpwctx.children():
721 721 # rebase was a no-op, so it produced no child commit
722 722 shelvectx = tmpwctx
723 723
724 724 mergefiles(ui, repo, pctx, shelvectx)
725 725 shelvedstate.clear(repo)
726 726
727 727 # The transaction aborting will strip all the commits for us,
728 728 # but it doesn't update the inmemory structures, so addchangegroup
729 729 # hooks still fire and try to operate on the missing commits.
730 730 # Clean up manually to prevent this.
731 731 repo.unfiltered().changelog.strip(oldtiprev, tr)
732 732
733 733 unshelvecleanup(ui, repo, basename, opts)
734 734
735 735 _aborttransaction(repo)
736 736 finally:
737 737 ui.quiet = oldquiet
738 738 if tr:
739 739 tr.release()
740 740 lockmod.release(lock, wlock)
741 741
742 742 @command('shelve',
743 743 [('A', 'addremove', None,
744 744 _('mark new/missing files as added/removed before shelving')),
745 745 ('', 'cleanup', None,
746 746 _('delete all shelved changes')),
747 747 ('', 'date', '',
748 748 _('shelve with the specified commit date'), _('DATE')),
749 749 ('d', 'delete', None,
750 750 _('delete the named shelved change(s)')),
751 751 ('e', 'edit', False,
752 752 _('invoke editor on commit messages')),
753 753 ('l', 'list', None,
754 754 _('list current shelves')),
755 755 ('m', 'message', '',
756 756 _('use text as shelve message'), _('TEXT')),
757 757 ('n', 'name', '',
758 758 _('use the given name for the shelved commit'), _('NAME')),
759 759 ('p', 'patch', None,
760 760 _('show patch')),
761 761 ('i', 'interactive', None,
762 762 _('interactive mode, only works while creating a shelve')),
763 763 ('', 'stat', None,
764 764 _('output diffstat-style summary of changes'))] + commands.walkopts,
765 765 _('hg shelve [OPTION]... [FILE]...'))
766 766 def shelvecmd(ui, repo, *pats, **opts):
767 767 '''save and set aside changes from the working directory
768 768
769 769 Shelving takes files that "hg status" reports as not clean, saves
770 770 the modifications to a bundle (a shelved change), and reverts the
771 771 files so that their state in the working directory becomes clean.
772 772
773 773 To restore these changes to the working directory, using "hg
774 774 unshelve"; this will work even if you switch to a different
775 775 commit.
776 776
777 777 When no files are specified, "hg shelve" saves all not-clean
778 778 files. If specific files or directories are named, only changes to
779 779 those files are shelved.
780 780
781 781 Each shelved change has a name that makes it easier to find later.
782 782 The name of a shelved change defaults to being based on the active
783 783 bookmark, or if there is no active bookmark, the current named
784 784 branch. To specify a different name, use ``--name``.
785 785
786 786 To see a list of existing shelved changes, use the ``--list``
787 787 option. For each shelved change, this will print its name, age,
788 788 and description; use ``--patch`` or ``--stat`` for more details.
789 789
790 790 To delete specific shelved changes, use ``--delete``. To delete
791 791 all shelved changes, use ``--cleanup``.
792 792 '''
793 793 cmdutil.checkunfinished(repo)
794 794
795 795 allowables = [
796 796 ('addremove', set(['create'])), # 'create' is pseudo action
797 797 ('cleanup', set(['cleanup'])),
798 798 # ('date', set(['create'])), # ignored for passing '--date "0 0"' in tests
799 799 ('delete', set(['delete'])),
800 800 ('edit', set(['create'])),
801 801 ('list', set(['list'])),
802 802 ('message', set(['create'])),
803 803 ('name', set(['create'])),
804 804 ('patch', set(['patch', 'list'])),
805 805 ('stat', set(['stat', 'list'])),
806 806 ]
807 807 def checkopt(opt):
808 808 if opts[opt]:
809 809 for i, allowable in allowables:
810 810 if opts[i] and opt not in allowable:
811 811 raise error.Abort(_("options '--%s' and '--%s' may not be "
812 812 "used together") % (opt, i))
813 813 return True
814 814 if checkopt('cleanup'):
815 815 if pats:
816 816 raise error.Abort(_("cannot specify names when using '--cleanup'"))
817 817 return cleanupcmd(ui, repo)
818 818 elif checkopt('delete'):
819 819 return deletecmd(ui, repo, pats)
820 820 elif checkopt('list'):
821 821 return listcmd(ui, repo, pats, opts)
822 822 elif checkopt('patch'):
823 823 return singlepatchcmds(ui, repo, pats, opts, subcommand='patch')
824 824 elif checkopt('stat'):
825 825 return singlepatchcmds(ui, repo, pats, opts, subcommand='stat')
826 826 else:
827 827 return createcmd(ui, repo, pats, opts)
828 828
829 829 def extsetup(ui):
830 830 cmdutil.unfinishedstates.append(
831 831 [shelvedstate._filename, False, False,
832 832 _('unshelve already in progress'),
833 833 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
@@ -1,1012 +1,1052 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [extensions]
3 3 > mq =
4 4 > shelve =
5 5 > [defaults]
6 6 > diff = --nodates --git
7 7 > qnew = --date '0 0'
8 8 > [shelve]
9 9 > maxbackups = 2
10 10 > EOF
11 11
12 12 $ hg init repo
13 13 $ cd repo
14 14 $ mkdir a b
15 15 $ echo a > a/a
16 16 $ echo b > b/b
17 17 $ echo c > c
18 18 $ echo d > d
19 19 $ echo x > x
20 20 $ hg addremove -q
21 21
22 22 shelve has a help message
23 23 $ hg shelve -h
24 24 hg shelve [OPTION]... [FILE]...
25 25
26 26 save and set aside changes from the working directory
27 27
28 28 Shelving takes files that "hg status" reports as not clean, saves the
29 29 modifications to a bundle (a shelved change), and reverts the files so
30 30 that their state in the working directory becomes clean.
31 31
32 32 To restore these changes to the working directory, using "hg unshelve";
33 33 this will work even if you switch to a different commit.
34 34
35 35 When no files are specified, "hg shelve" saves all not-clean files. If
36 36 specific files or directories are named, only changes to those files are
37 37 shelved.
38 38
39 39 Each shelved change has a name that makes it easier to find later. The
40 40 name of a shelved change defaults to being based on the active bookmark,
41 41 or if there is no active bookmark, the current named branch. To specify a
42 42 different name, use "--name".
43 43
44 44 To see a list of existing shelved changes, use the "--list" option. For
45 45 each shelved change, this will print its name, age, and description; use "
46 46 --patch" or "--stat" for more details.
47 47
48 48 To delete specific shelved changes, use "--delete". To delete all shelved
49 49 changes, use "--cleanup".
50 50
51 51 (use "hg help -e shelve" to show help for the shelve extension)
52 52
53 53 options ([+] can be repeated):
54 54
55 55 -A --addremove mark new/missing files as added/removed before
56 56 shelving
57 57 --cleanup delete all shelved changes
58 58 --date DATE shelve with the specified commit date
59 59 -d --delete delete the named shelved change(s)
60 60 -e --edit invoke editor on commit messages
61 61 -l --list list current shelves
62 62 -m --message TEXT use text as shelve message
63 63 -n --name NAME use the given name for the shelved commit
64 64 -p --patch show patch
65 65 -i --interactive interactive mode, only works while creating a shelve
66 66 --stat output diffstat-style summary of changes
67 67 -I --include PATTERN [+] include names matching the given patterns
68 68 -X --exclude PATTERN [+] exclude names matching the given patterns
69 69 --mq operate on patch repository
70 70
71 71 (some details hidden, use --verbose to show complete help)
72 72
73 73 shelving in an empty repo should be possible
74 74 (this tests also that editor is not invoked, if '--edit' is not
75 75 specified)
76 76
77 77 $ HGEDITOR=cat hg shelve
78 78 shelved as default
79 79 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
80 80
81 81 $ hg unshelve
82 82 unshelving change 'default'
83 83
84 84 $ hg commit -q -m 'initial commit'
85 85
86 86 $ hg shelve
87 87 nothing changed
88 88 [1]
89 89
90 90 make sure shelve files were backed up
91 91
92 92 $ ls .hg/shelve-backup
93 93 default.hg
94 94 default.patch
95 95
96 96 create an mq patch - shelving should work fine with a patch applied
97 97
98 98 $ echo n > n
99 99 $ hg add n
100 100 $ hg commit n -m second
101 101 $ hg qnew second.patch
102 102
103 103 shelve a change that we will delete later
104 104
105 105 $ echo a >> a/a
106 106 $ hg shelve
107 107 shelved as default
108 108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109
110 110 set up some more complex changes to shelve
111 111
112 112 $ echo a >> a/a
113 113 $ hg mv b b.rename
114 114 moving b/b to b.rename/b (glob)
115 115 $ hg cp c c.copy
116 116 $ hg status -C
117 117 M a/a
118 118 A b.rename/b
119 119 b/b
120 120 A c.copy
121 121 c
122 122 R b/b
123 123
124 124 prevent some foot-shooting
125 125
126 126 $ hg shelve -n foo/bar
127 127 abort: shelved change names may not contain slashes
128 128 [255]
129 129 $ hg shelve -n .baz
130 130 abort: shelved change names may not start with '.'
131 131 [255]
132 132
133 133 the common case - no options or filenames
134 134
135 135 $ hg shelve
136 136 shelved as default-01
137 137 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
138 138 $ hg status -C
139 139
140 140 ensure that our shelved changes exist
141 141
142 142 $ hg shelve -l
143 143 default-01 (*)* changes to '[mq]: second.patch' (glob)
144 144 default (*)* changes to '[mq]: second.patch' (glob)
145 145
146 146 $ hg shelve -l -p default
147 147 default (*)* changes to '[mq]: second.patch' (glob)
148 148
149 149 diff --git a/a/a b/a/a
150 150 --- a/a/a
151 151 +++ b/a/a
152 152 @@ -1,1 +1,2 @@
153 153 a
154 154 +a
155 155
156 156 $ hg shelve --list --addremove
157 157 abort: options '--list' and '--addremove' may not be used together
158 158 [255]
159 159
160 160 delete our older shelved change
161 161
162 162 $ hg shelve -d default
163 163 $ hg qfinish -a -q
164 164
165 165 ensure shelve backups aren't overwritten
166 166
167 167 $ ls .hg/shelve-backup/
168 168 default-1.hg
169 169 default-1.patch
170 170 default.hg
171 171 default.patch
172 172
173 173 local edits should not prevent a shelved change from applying
174 174
175 175 $ printf "z\na\n" > a/a
176 176 $ hg unshelve --keep
177 177 unshelving change 'default-01'
178 178 temporarily committing pending changes (restore with 'hg unshelve --abort')
179 179 rebasing shelved changes
180 180 rebasing 4:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
181 181 merging a/a
182 182
183 183 $ hg revert --all -q
184 184 $ rm a/a.orig b.rename/b c.copy
185 185
186 186 apply it and make sure our state is as expected
187 187
188 188 (this also tests that same timestamp prevents backups from being
189 189 removed, even though there are more than 'maxbackups' backups)
190 190
191 191 $ f -t .hg/shelve-backup/default.hg
192 192 .hg/shelve-backup/default.hg: file
193 193 $ touch -t 200001010000 .hg/shelve-backup/default.hg
194 194 $ f -t .hg/shelve-backup/default-1.hg
195 195 .hg/shelve-backup/default-1.hg: file
196 196 $ touch -t 200001010000 .hg/shelve-backup/default-1.hg
197 197
198 198 $ hg unshelve
199 199 unshelving change 'default-01'
200 200 $ hg status -C
201 201 M a/a
202 202 A b.rename/b
203 203 b/b
204 204 A c.copy
205 205 c
206 206 R b/b
207 207 $ hg shelve -l
208 208
209 209 (both of default.hg and default-1.hg should be still kept, because it
210 210 is difficult to decide actual order of them from same timestamp)
211 211
212 212 $ ls .hg/shelve-backup/
213 213 default-01.hg
214 214 default-01.patch
215 215 default-1.hg
216 216 default-1.patch
217 217 default.hg
218 218 default.patch
219 219
220 220 $ hg unshelve
221 221 abort: no shelved changes to apply!
222 222 [255]
223 223 $ hg unshelve foo
224 224 abort: shelved change 'foo' not found
225 225 [255]
226 226
227 227 named shelves, specific filenames, and "commit messages" should all work
228 228 (this tests also that editor is invoked, if '--edit' is specified)
229 229
230 230 $ hg status -C
231 231 M a/a
232 232 A b.rename/b
233 233 b/b
234 234 A c.copy
235 235 c
236 236 R b/b
237 237 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
238 238 wat
239 239
240 240
241 241 HG: Enter commit message. Lines beginning with 'HG:' are removed.
242 242 HG: Leave message empty to abort commit.
243 243 HG: --
244 244 HG: user: shelve@localhost
245 245 HG: branch 'default'
246 246 HG: changed a/a
247 247
248 248 expect "a" to no longer be present, but status otherwise unchanged
249 249
250 250 $ hg status -C
251 251 A b.rename/b
252 252 b/b
253 253 A c.copy
254 254 c
255 255 R b/b
256 256 $ hg shelve -l --stat
257 257 wibble (*) wat (glob)
258 258 a/a | 1 +
259 259 1 files changed, 1 insertions(+), 0 deletions(-)
260 260
261 261 and now "a/a" should reappear
262 262
263 263 $ cd a
264 264 $ hg unshelve -q wibble
265 265 $ cd ..
266 266 $ hg status -C
267 267 M a/a
268 268 A b.rename/b
269 269 b/b
270 270 A c.copy
271 271 c
272 272 R b/b
273 273
274 274 ensure old shelve backups are being deleted automatically
275 275
276 276 $ ls .hg/shelve-backup/
277 277 default-01.hg
278 278 default-01.patch
279 279 wibble.hg
280 280 wibble.patch
281 281
282 282 cause unshelving to result in a merge with 'a' conflicting
283 283
284 284 $ hg shelve -q
285 285 $ echo c>>a/a
286 286 $ hg commit -m second
287 287 $ hg tip --template '{files}\n'
288 288 a/a
289 289
290 290 add an unrelated change that should be preserved
291 291
292 292 $ mkdir foo
293 293 $ echo foo > foo/foo
294 294 $ hg add foo/foo
295 295
296 296 force a conflicted merge to occur
297 297
298 298 $ hg unshelve
299 299 unshelving change 'default'
300 300 temporarily committing pending changes (restore with 'hg unshelve --abort')
301 301 rebasing shelved changes
302 302 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
303 303 merging a/a
304 304 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
305 305 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
306 306 [1]
307 307
308 308 ensure that we have a merge with unresolved conflicts
309 309
310 310 $ hg heads -q --template '{rev}\n'
311 311 5
312 312 4
313 313 $ hg parents -q --template '{rev}\n'
314 314 4
315 315 5
316 316 $ hg status
317 317 M a/a
318 318 M b.rename/b
319 319 M c.copy
320 320 R b/b
321 321 ? a/a.orig
322 322 $ hg diff
323 323 diff --git a/a/a b/a/a
324 324 --- a/a/a
325 325 +++ b/a/a
326 326 @@ -1,2 +1,6 @@
327 327 a
328 328 +<<<<<<< dest: * - shelve: pending changes temporary commit (glob)
329 329 c
330 330 +=======
331 331 +a
332 332 +>>>>>>> source: 4702e8911fe0 - shelve: changes to '[mq]: second.patch'
333 333 diff --git a/b/b b/b.rename/b
334 334 rename from b/b
335 335 rename to b.rename/b
336 336 diff --git a/c b/c.copy
337 337 copy from c
338 338 copy to c.copy
339 339 $ hg resolve -l
340 340 U a/a
341 341
342 342 $ hg shelve
343 343 abort: unshelve already in progress
344 344 (use 'hg unshelve --continue' or 'hg unshelve --abort')
345 345 [255]
346 346
347 347 abort the unshelve and be happy
348 348
349 349 $ hg status
350 350 M a/a
351 351 M b.rename/b
352 352 M c.copy
353 353 R b/b
354 354 ? a/a.orig
355 355 $ hg unshelve -a
356 356 rebase aborted
357 357 unshelve of 'default' aborted
358 358 $ hg heads -q
359 359 3:2e69b451d1ea
360 360 $ hg parents
361 361 changeset: 3:2e69b451d1ea
362 362 tag: tip
363 363 user: test
364 364 date: Thu Jan 01 00:00:00 1970 +0000
365 365 summary: second
366 366
367 367 $ hg resolve -l
368 368 $ hg status
369 369 A foo/foo
370 370 ? a/a.orig
371 371
372 372 try to continue with no unshelve underway
373 373
374 374 $ hg unshelve -c
375 375 abort: no unshelve operation underway
376 376 [255]
377 377 $ hg status
378 378 A foo/foo
379 379 ? a/a.orig
380 380
381 381 redo the unshelve to get a conflict
382 382
383 383 $ hg unshelve -q
384 384 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
385 385 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
386 386 [1]
387 387
388 388 attempt to continue
389 389
390 390 $ hg unshelve -c
391 391 abort: unresolved conflicts, can't continue
392 392 (see 'hg resolve', then 'hg unshelve --continue')
393 393 [255]
394 394
395 395 $ hg revert -r . a/a
396 396 $ hg resolve -m a/a
397 397 (no more unresolved files)
398 398
399 399 $ hg commit -m 'commit while unshelve in progress'
400 400 abort: unshelve already in progress
401 401 (use 'hg unshelve --continue' or 'hg unshelve --abort')
402 402 [255]
403 403
404 404 $ hg unshelve -c
405 405 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
406 406 unshelve of 'default' complete
407 407
408 408 ensure the repo is as we hope
409 409
410 410 $ hg parents
411 411 changeset: 3:2e69b451d1ea
412 412 tag: tip
413 413 user: test
414 414 date: Thu Jan 01 00:00:00 1970 +0000
415 415 summary: second
416 416
417 417 $ hg heads -q
418 418 3:2e69b451d1ea
419 419
420 420 $ hg status -C
421 421 A b.rename/b
422 422 b/b
423 423 A c.copy
424 424 c
425 425 A foo/foo
426 426 R b/b
427 427 ? a/a.orig
428 428
429 429 there should be no shelves left
430 430
431 431 $ hg shelve -l
432 432
433 433 #if execbit
434 434
435 435 ensure that metadata-only changes are shelved
436 436
437 437 $ chmod +x a/a
438 438 $ hg shelve -q -n execbit a/a
439 439 $ hg status a/a
440 440 $ hg unshelve -q execbit
441 441 $ hg status a/a
442 442 M a/a
443 443 $ hg revert a/a
444 444
445 445 #endif
446 446
447 447 #if symlink
448 448
449 449 $ rm a/a
450 450 $ ln -s foo a/a
451 451 $ hg shelve -q -n symlink a/a
452 452 $ hg status a/a
453 453 $ hg unshelve -q symlink
454 454 $ hg status a/a
455 455 M a/a
456 456 $ hg revert a/a
457 457
458 458 #endif
459 459
460 460 set up another conflict between a commit and a shelved change
461 461
462 462 $ hg revert -q -C -a
463 463 $ rm a/a.orig b.rename/b c.copy
464 464 $ echo a >> a/a
465 465 $ hg shelve -q
466 466 $ echo x >> a/a
467 467 $ hg ci -m 'create conflict'
468 468 $ hg add foo/foo
469 469
470 470 if we resolve a conflict while unshelving, the unshelve should succeed
471 471
472 472 $ HGMERGE=true hg unshelve
473 473 unshelving change 'default'
474 474 temporarily committing pending changes (restore with 'hg unshelve --abort')
475 475 rebasing shelved changes
476 476 rebasing 6:c5e6910e7601 "changes to 'second'" (tip)
477 477 merging a/a
478 478 note: rebase of 6:c5e6910e7601 created no changes to commit
479 479 $ hg parents -q
480 480 4:33f7f61e6c5e
481 481 $ hg shelve -l
482 482 $ hg status
483 483 A foo/foo
484 484 $ cat a/a
485 485 a
486 486 c
487 487 x
488 488
489 489 test keep and cleanup
490 490
491 491 $ hg shelve
492 492 shelved as default
493 493 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
494 494 $ hg shelve --list
495 495 default (*) changes to 'create conflict' (glob)
496 496 $ hg unshelve --keep
497 497 unshelving change 'default'
498 498 $ hg shelve --list
499 499 default (*) changes to 'create conflict' (glob)
500 500 $ hg shelve --cleanup
501 501 $ hg shelve --list
502 502
503 503 $ hg shelve --cleanup --delete
504 504 abort: options '--cleanup' and '--delete' may not be used together
505 505 [255]
506 506 $ hg shelve --cleanup --patch
507 507 abort: options '--cleanup' and '--patch' may not be used together
508 508 [255]
509 509 $ hg shelve --cleanup --message MESSAGE
510 510 abort: options '--cleanup' and '--message' may not be used together
511 511 [255]
512 512
513 513 test bookmarks
514 514
515 515 $ hg bookmark test
516 516 $ hg bookmark
517 517 * test 4:33f7f61e6c5e
518 518 $ hg shelve
519 519 shelved as test
520 520 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
521 521 $ hg bookmark
522 522 * test 4:33f7f61e6c5e
523 523 $ hg unshelve
524 524 unshelving change 'test'
525 525 $ hg bookmark
526 526 * test 4:33f7f61e6c5e
527 527
528 528 shelve should still work even if mq is disabled
529 529
530 530 $ hg --config extensions.mq=! shelve
531 531 shelved as test
532 532 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
533 533 $ hg --config extensions.mq=! shelve --list
534 534 test (*) changes to 'create conflict' (glob)
535 535 $ hg bookmark
536 536 * test 4:33f7f61e6c5e
537 537 $ hg --config extensions.mq=! unshelve
538 538 unshelving change 'test'
539 539 $ hg bookmark
540 540 * test 4:33f7f61e6c5e
541 541
542 542 shelve should leave dirstate clean (issue4055)
543 543
544 544 $ cd ..
545 545 $ hg init shelverebase
546 546 $ cd shelverebase
547 547 $ printf 'x\ny\n' > x
548 548 $ echo z > z
549 549 $ hg commit -Aqm xy
550 550 $ echo z >> x
551 551 $ hg commit -Aqm z
552 552 $ hg up 0
553 553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 554 $ printf 'a\nx\ny\nz\n' > x
555 555 $ hg commit -Aqm xyz
556 556 $ echo c >> z
557 557 $ hg shelve
558 558 shelved as default
559 559 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
560 560 $ hg rebase -d 1 --config extensions.rebase=
561 561 rebasing 2:323bfa07f744 "xyz" (tip)
562 562 merging x
563 563 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-backup.hg (glob)
564 564 $ hg unshelve
565 565 unshelving change 'default'
566 566 rebasing shelved changes
567 567 rebasing 4:b8fefe789ed0 "changes to 'xyz'" (tip)
568 568 $ hg status
569 569 M z
570 570
571 571 $ cd ..
572 572
573 573 shelve should only unshelve pending changes (issue4068)
574 574
575 575 $ hg init onlypendingchanges
576 576 $ cd onlypendingchanges
577 577 $ touch a
578 578 $ hg ci -Aqm a
579 579 $ touch b
580 580 $ hg ci -Aqm b
581 581 $ hg up -q 0
582 582 $ touch c
583 583 $ hg ci -Aqm c
584 584
585 585 $ touch d
586 586 $ hg add d
587 587 $ hg shelve
588 588 shelved as default
589 589 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
590 590 $ hg up -q 1
591 591 $ hg unshelve
592 592 unshelving change 'default'
593 593 rebasing shelved changes
594 594 rebasing 3:0cae6656c016 "changes to 'c'" (tip)
595 595 $ hg status
596 596 A d
597 597
598 598 unshelve should work on an ancestor of the original commit
599 599
600 600 $ hg shelve
601 601 shelved as default
602 602 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
603 603 $ hg up 0
604 604 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
605 605 $ hg unshelve
606 606 unshelving change 'default'
607 607 rebasing shelved changes
608 608 rebasing 3:be58f65f55fb "changes to 'b'" (tip)
609 609 $ hg status
610 610 A d
611 611
612 612 test bug 4073 we need to enable obsolete markers for it
613 613
614 614 $ cat >> $HGRCPATH << EOF
615 615 > [experimental]
616 616 > evolution=createmarkers
617 617 > EOF
618 618 $ hg shelve
619 619 shelved as default
620 620 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
621 621 $ hg debugobsolete `hg --debug id -i -r 1`
622 622 $ hg unshelve
623 623 unshelving change 'default'
624 624
625 625 unshelve should leave unknown files alone (issue4113)
626 626
627 627 $ echo e > e
628 628 $ hg shelve
629 629 shelved as default
630 630 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
631 631 $ hg status
632 632 ? e
633 633 $ hg unshelve
634 634 unshelving change 'default'
635 635 $ hg status
636 636 A d
637 637 ? e
638 638 $ cat e
639 639 e
640 640
641 641 unshelve should keep a copy of unknown files
642 642
643 643 $ hg add e
644 644 $ hg shelve
645 645 shelved as default
646 646 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
647 647 $ echo z > e
648 648 $ hg unshelve
649 649 unshelving change 'default'
650 650 $ cat e
651 651 e
652 652 $ cat e.orig
653 653 z
654 654
655 655
656 656 unshelve and conflicts with tracked and untracked files
657 657
658 658 preparing:
659 659
660 660 $ rm *.orig
661 661 $ hg ci -qm 'commit stuff'
662 662 $ hg phase -p null:
663 663
664 664 no other changes - no merge:
665 665
666 666 $ echo f > f
667 667 $ hg add f
668 668 $ hg shelve
669 669 shelved as default
670 670 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
671 671 $ echo g > f
672 672 $ hg unshelve
673 673 unshelving change 'default'
674 674 $ hg st
675 675 A f
676 676 ? f.orig
677 677 $ cat f
678 678 f
679 679 $ cat f.orig
680 680 g
681 681
682 682 other uncommitted changes - merge:
683 683
684 684 $ hg st
685 685 A f
686 686 ? f.orig
687 687 $ hg shelve
688 688 shelved as default
689 689 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
690 690 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
691 691 o 4 changes to 'commit stuff' shelve@localhost
692 692 |
693 693 $ hg log -G --template '{rev} {desc|firstline} {author}'
694 694 @ 3 commit stuff test
695 695 |
696 696 | o 2 c test
697 697 |/
698 698 o 0 a test
699 699
700 700 $ mv f.orig f
701 701 $ echo 1 > a
702 702 $ hg unshelve --date '1073741824 0'
703 703 unshelving change 'default'
704 704 temporarily committing pending changes (restore with 'hg unshelve --abort')
705 705 rebasing shelved changes
706 706 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
707 707 merging f
708 708 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
709 709 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
710 710 [1]
711 711 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
712 712 @ 5 changes to 'commit stuff' shelve@localhost 1970-01-01 00:00 +0000
713 713 |
714 714 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
715 715 |/
716 716 o 3 commit stuff test 1970-01-01 00:00 +0000
717 717 |
718 718 | o 2 c test 1970-01-01 00:00 +0000
719 719 |/
720 720 o 0 a test 1970-01-01 00:00 +0000
721 721
722 722 $ hg st
723 723 M f
724 724 ? f.orig
725 725 $ cat f
726 726 <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit
727 727 g
728 728 =======
729 729 f
730 730 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
731 731 $ cat f.orig
732 732 g
733 733 $ hg unshelve --abort
734 734 rebase aborted
735 735 unshelve of 'default' aborted
736 736 $ hg st
737 737 M a
738 738 ? f.orig
739 739 $ cat f.orig
740 740 g
741 741 $ hg unshelve
742 742 unshelving change 'default'
743 743 temporarily committing pending changes (restore with 'hg unshelve --abort')
744 744 rebasing shelved changes
745 745 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
746 746 $ hg st
747 747 M a
748 748 A f
749 749 ? f.orig
750 750
751 751 other committed changes - merge:
752 752
753 753 $ hg shelve f
754 754 shelved as default
755 755 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
756 756 $ hg ci a -m 'intermediate other change'
757 757 $ mv f.orig f
758 758 $ hg unshelve
759 759 unshelving change 'default'
760 760 rebasing shelved changes
761 761 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
762 762 merging f
763 763 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
764 764 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
765 765 [1]
766 766 $ hg st
767 767 M f
768 768 ? f.orig
769 769 $ cat f
770 770 <<<<<<< dest: * - test: intermediate other change (glob)
771 771 g
772 772 =======
773 773 f
774 774 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
775 775 $ cat f.orig
776 776 g
777 777 $ hg unshelve --abort
778 778 rebase aborted
779 779 unshelve of 'default' aborted
780 780 $ hg st
781 781 ? f.orig
782 782 $ cat f.orig
783 783 g
784 784 $ hg shelve --delete default
785 785
786 786 Recreate some conflict again
787 787
788 788 $ cd ../repo
789 789 $ hg up -C -r 3
790 790 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
791 791 (leaving bookmark test)
792 792 $ echo y >> a/a
793 793 $ hg shelve
794 794 shelved as default
795 795 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
796 796 $ hg up test
797 797 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
798 798 (activating bookmark test)
799 799 $ hg bookmark
800 800 * test 4:33f7f61e6c5e
801 801 $ hg unshelve
802 802 unshelving change 'default'
803 803 rebasing shelved changes
804 804 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
805 805 merging a/a
806 806 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
807 807 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
808 808 [1]
809 809 $ hg bookmark
810 810 test 4:33f7f61e6c5e
811 811
812 812 Test that resolving all conflicts in one direction (so that the rebase
813 813 is a no-op), works (issue4398)
814 814
815 815 $ hg revert -a -r .
816 816 reverting a/a (glob)
817 817 $ hg resolve -m a/a
818 818 (no more unresolved files)
819 819 $ hg unshelve -c
820 820 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
821 821 note: rebase of 5:4b555fdb4e96 created no changes to commit
822 822 unshelve of 'default' complete
823 823 $ hg bookmark
824 824 * test 4:33f7f61e6c5e
825 825 $ hg diff
826 826 $ hg status
827 827 ? a/a.orig
828 828 ? foo/foo
829 829 $ hg summary
830 830 parent: 4:33f7f61e6c5e tip
831 831 create conflict
832 832 branch: default
833 833 bookmarks: *test
834 834 commit: 2 unknown (clean)
835 835 update: (current)
836 836 phases: 5 draft
837 837
838 838 $ hg shelve --delete --stat
839 839 abort: options '--delete' and '--stat' may not be used together
840 840 [255]
841 841 $ hg shelve --delete --name NAME
842 842 abort: options '--delete' and '--name' may not be used together
843 843 [255]
844 844
845 845 Test interactive shelve
846 846 $ cat <<EOF >> $HGRCPATH
847 847 > [ui]
848 848 > interactive = true
849 849 > EOF
850 850 $ echo 'a' >> a/b
851 851 $ cat a/a >> a/b
852 852 $ echo 'x' >> a/b
853 853 $ mv a/b a/a
854 854 $ echo 'a' >> foo/foo
855 855 $ hg st
856 856 M a/a
857 857 ? a/a.orig
858 858 ? foo/foo
859 859 $ cat a/a
860 860 a
861 861 a
862 862 c
863 863 x
864 864 x
865 865 $ cat foo/foo
866 866 foo
867 867 a
868 868 $ hg shelve --interactive --config ui.interactive=false
869 869 abort: running non-interactively
870 870 [255]
871 871 $ hg shelve --interactive << EOF
872 872 > y
873 873 > y
874 874 > n
875 875 > EOF
876 876 diff --git a/a/a b/a/a
877 877 2 hunks, 2 lines changed
878 878 examine changes to 'a/a'? [Ynesfdaq?] y
879 879
880 880 @@ -1,3 +1,4 @@
881 881 +a
882 882 a
883 883 c
884 884 x
885 885 record change 1/2 to 'a/a'? [Ynesfdaq?] y
886 886
887 887 @@ -1,3 +2,4 @@
888 888 a
889 889 c
890 890 x
891 891 +x
892 892 record change 2/2 to 'a/a'? [Ynesfdaq?] n
893 893
894 894 shelved as test
895 895 merging a/a
896 896 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
897 897 $ cat a/a
898 898 a
899 899 c
900 900 x
901 901 x
902 902 $ cat foo/foo
903 903 foo
904 904 a
905 905 $ hg st
906 906 M a/a
907 907 ? foo/foo
908 908 $ hg bookmark
909 909 * test 4:33f7f61e6c5e
910 910 $ hg unshelve
911 911 unshelving change 'test'
912 912 temporarily committing pending changes (restore with 'hg unshelve --abort')
913 913 rebasing shelved changes
914 914 rebasing 6:65b5d1c34c34 "changes to 'create conflict'" (tip)
915 915 merging a/a
916 916 $ hg bookmark
917 917 * test 4:33f7f61e6c5e
918 918 $ cat a/a
919 919 a
920 920 a
921 921 c
922 922 x
923 923 x
924 924
925 925 shelve --patch and shelve --stat should work with a single valid shelfname
926 926
927 927 $ hg up --clean .
928 928 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
929 929 (leaving bookmark test)
930 930 $ hg shelve --list
931 931 $ echo 'patch a' > shelf-patch-a
932 932 $ hg add shelf-patch-a
933 933 $ hg shelve
934 934 shelved as default
935 935 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
936 936 $ echo 'patch b' > shelf-patch-b
937 937 $ hg add shelf-patch-b
938 938 $ hg shelve
939 939 shelved as default-01
940 940 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
941 941 $ hg shelve --patch default default-01
942 942 abort: --patch expects a single shelf
943 943 [255]
944 944 $ hg shelve --stat default default-01
945 945 abort: --stat expects a single shelf
946 946 [255]
947 947 $ hg shelve --patch default
948 948 default (* ago) changes to 'create conflict' (glob)
949 949
950 950 diff --git a/shelf-patch-a b/shelf-patch-a
951 951 new file mode 100644
952 952 --- /dev/null
953 953 +++ b/shelf-patch-a
954 954 @@ -0,0 +1,1 @@
955 955 +patch a
956 956 $ hg shelve --stat default
957 957 default (* ago) changes to 'create conflict' (glob)
958 958 shelf-patch-a | 1 +
959 959 1 files changed, 1 insertions(+), 0 deletions(-)
960 960 $ hg shelve --patch nonexistentshelf
961 961 abort: cannot find shelf nonexistentshelf
962 962 [255]
963 963 $ hg shelve --stat nonexistentshelf
964 964 abort: cannot find shelf nonexistentshelf
965 965 [255]
966 966
967 967 $ cd ..
968 968
969 969 Shelve from general delta repo uses bundle2 on disk
970 970 --------------------------------------------------
971 971
972 972 no general delta
973 973
974 974 $ hg clone --pull repo bundle1 --config format.generaldelta=0
975 975 requesting all changes
976 976 adding changesets
977 977 adding manifests
978 978 adding file changes
979 979 added 5 changesets with 8 changes to 6 files
980 980 updating to branch default
981 981 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
982 982 $ cd bundle1
983 983 $ echo babar > jungle
984 984 $ hg add jungle
985 985 $ hg shelve
986 986 shelved as default
987 987 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
988 988 $ hg debugbundle .hg/shelved/*.hg
989 989 7e30d8ac6f23cfc84330fd7e698730374615d21a
990 990 $ cd ..
991 991
992 992 with general delta
993 993
994 994 $ hg clone --pull repo bundle2 --config format.generaldelta=1
995 995 requesting all changes
996 996 adding changesets
997 997 adding manifests
998 998 adding file changes
999 999 added 5 changesets with 8 changes to 6 files
1000 1000 updating to branch default
1001 1001 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1002 1002 $ cd bundle2
1003 1003 $ echo babar > jungle
1004 1004 $ hg add jungle
1005 1005 $ hg shelve
1006 1006 shelved as default
1007 1007 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1008 1008 $ hg debugbundle .hg/shelved/*.hg
1009 1009 Stream params: {'Compression': 'BZ'}
1010 1010 changegroup -- "{'version': '02'}"
1011 1011 7e30d8ac6f23cfc84330fd7e698730374615d21a
1012 1012 $ cd ..
1013
1014 test Abort unshelve always gets user out of the unshelved state
1015 ---------------------------------------------------------------
1016 $ hg init salvage
1017 $ cd salvage
1018 $ echo 'content' > root
1019 $ hg commit -A -m 'root' -q
1020 $ echo '' > root
1021 $ hg shelve -q
1022 $ echo 'contADDent' > root
1023 $ hg unshelve -q
1024 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
1025 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1026 [1]
1027 Wreak havoc on the unshelve process
1028 $ rm .hg/unshelverebasestate
1029 $ hg unshelve --abort
1030 unshelve of 'default' aborted
1031 abort: No such file or directory
1032 [255]
1033 Can the user leave the current state?
1034 $ hg up -C .
1035 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1036
1037 Try again but with a corrupted shelve state file
1038 $ hg strip -r 2 -r 1 -q
1039 $ hg up -r 0 -q
1040 $ echo '' > root
1041 $ hg shelve -q
1042 $ echo 'contADDent' > root
1043 $ hg unshelve -q
1044 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
1045 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1046 [1]
1047 $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate
1048 $ mv ../corrupt-shelvedstate .hg/histedit-state
1049 $ hg unshelve --abort |& grep 'rebase aborted'
1050 rebase aborted
1051 $ hg up -C .
1052 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now