##// END OF EJS Templates
unshelve: store information about interactive mode in shelvedstate...
Navaneeth Suresh -
r42889:ee86ad6f default
parent child Browse files
Show More
@@ -1,1010 +1,1013 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 . import (
32 32 bookmarks,
33 33 bundle2,
34 34 bundlerepo,
35 35 changegroup,
36 36 cmdutil,
37 37 discovery,
38 38 error,
39 39 exchange,
40 40 hg,
41 41 lock as lockmod,
42 42 mdiff,
43 43 merge,
44 44 node as nodemod,
45 45 patch,
46 46 phases,
47 47 pycompat,
48 48 repair,
49 49 scmutil,
50 50 templatefilters,
51 51 util,
52 52 vfs as vfsmod,
53 53 )
54 54 from .utils import (
55 55 dateutil,
56 56 stringutil,
57 57 )
58 58
59 59 backupdir = 'shelve-backup'
60 60 shelvedir = 'shelved'
61 61 shelvefileextensions = ['hg', 'patch', 'shelve']
62 62 # universal extension is present in all types of shelves
63 63 patchextension = 'patch'
64 64
65 65 # we never need the user, so we use a
66 66 # generic user for all shelve operations
67 67 shelveuser = 'shelve@localhost'
68 68
69 69 class shelvedfile(object):
70 70 """Helper for the file storing a single shelve
71 71
72 72 Handles common functions on shelve files (.hg/.patch) using
73 73 the vfs layer"""
74 74 def __init__(self, repo, name, filetype=None):
75 75 self.repo = repo
76 76 self.name = name
77 77 self.vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
78 78 self.backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
79 79 self.ui = self.repo.ui
80 80 if filetype:
81 81 self.fname = name + '.' + filetype
82 82 else:
83 83 self.fname = name
84 84
85 85 def exists(self):
86 86 return self.vfs.exists(self.fname)
87 87
88 88 def filename(self):
89 89 return self.vfs.join(self.fname)
90 90
91 91 def backupfilename(self):
92 92 def gennames(base):
93 93 yield base
94 94 base, ext = base.rsplit('.', 1)
95 95 for i in itertools.count(1):
96 96 yield '%s-%d.%s' % (base, i, ext)
97 97
98 98 name = self.backupvfs.join(self.fname)
99 99 for n in gennames(name):
100 100 if not self.backupvfs.exists(n):
101 101 return n
102 102
103 103 def movetobackup(self):
104 104 if not self.backupvfs.isdir():
105 105 self.backupvfs.makedir()
106 106 util.rename(self.filename(), self.backupfilename())
107 107
108 108 def stat(self):
109 109 return self.vfs.stat(self.fname)
110 110
111 111 def opener(self, mode='rb'):
112 112 try:
113 113 return self.vfs(self.fname, mode)
114 114 except IOError as err:
115 115 if err.errno != errno.ENOENT:
116 116 raise
117 117 raise error.Abort(_("shelved change '%s' not found") % self.name)
118 118
119 119 def applybundle(self, tr):
120 120 fp = self.opener()
121 121 try:
122 122 targetphase = phases.internal
123 123 if not phases.supportinternal(self.repo):
124 124 targetphase = phases.secret
125 125 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
126 126 pretip = self.repo['tip']
127 127 bundle2.applybundle(self.repo, gen, tr,
128 128 source='unshelve',
129 129 url='bundle:' + self.vfs.join(self.fname),
130 130 targetphase=targetphase)
131 131 shelvectx = self.repo['tip']
132 132 if pretip == shelvectx:
133 133 shelverev = tr.changes['revduplicates'][-1]
134 134 shelvectx = self.repo[shelverev]
135 135 return shelvectx
136 136 finally:
137 137 fp.close()
138 138
139 139 def bundlerepo(self):
140 140 path = self.vfs.join(self.fname)
141 141 return bundlerepo.instance(self.repo.baseui,
142 142 'bundle://%s+%s' % (self.repo.root, path))
143 143
144 144 def writebundle(self, bases, node):
145 145 cgversion = changegroup.safeversion(self.repo)
146 146 if cgversion == '01':
147 147 btype = 'HG10BZ'
148 148 compression = None
149 149 else:
150 150 btype = 'HG20'
151 151 compression = 'BZ'
152 152
153 153 repo = self.repo.unfiltered()
154 154
155 155 outgoing = discovery.outgoing(repo, missingroots=bases,
156 156 missingheads=[node])
157 157 cg = changegroup.makechangegroup(repo, outgoing, cgversion, 'shelve')
158 158
159 159 bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs,
160 160 compression=compression)
161 161
162 162 def writeinfo(self, info):
163 163 scmutil.simplekeyvaluefile(self.vfs, self.fname).write(info)
164 164
165 165 def readinfo(self):
166 166 return scmutil.simplekeyvaluefile(self.vfs, self.fname).read()
167 167
168 168 class shelvedstate(object):
169 169 """Handle persistence during unshelving operations.
170 170
171 171 Handles saving and restoring a shelved state. Ensures that different
172 172 versions of a shelved state are possible and handles them appropriately.
173 173 """
174 174 _version = 2
175 175 _filename = 'shelvedstate'
176 176 _keep = 'keep'
177 177 _nokeep = 'nokeep'
178 178 # colon is essential to differentiate from a real bookmark name
179 179 _noactivebook = ':no-active-bookmark'
180 _interactive = 'interactive'
180 181
181 182 @classmethod
182 183 def _verifyandtransform(cls, d):
183 184 """Some basic shelvestate syntactic verification and transformation"""
184 185 try:
185 186 d['originalwctx'] = nodemod.bin(d['originalwctx'])
186 187 d['pendingctx'] = nodemod.bin(d['pendingctx'])
187 188 d['parents'] = [nodemod.bin(h)
188 189 for h in d['parents'].split(' ')]
189 190 d['nodestoremove'] = [nodemod.bin(h)
190 191 for h in d['nodestoremove'].split(' ')]
191 192 except (ValueError, TypeError, KeyError) as err:
192 193 raise error.CorruptedState(pycompat.bytestr(err))
193 194
194 195 @classmethod
195 196 def _getversion(cls, repo):
196 197 """Read version information from shelvestate file"""
197 198 fp = repo.vfs(cls._filename)
198 199 try:
199 200 version = int(fp.readline().strip())
200 201 except ValueError as err:
201 202 raise error.CorruptedState(pycompat.bytestr(err))
202 203 finally:
203 204 fp.close()
204 205 return version
205 206
206 207 @classmethod
207 208 def _readold(cls, repo):
208 209 """Read the old position-based version of a shelvestate file"""
209 210 # Order is important, because old shelvestate file uses it
210 211 # to detemine values of fields (i.g. name is on the second line,
211 212 # originalwctx is on the third and so forth). Please do not change.
212 213 keys = ['version', 'name', 'originalwctx', 'pendingctx', 'parents',
213 214 'nodestoremove', 'branchtorestore', 'keep', 'activebook']
214 215 # this is executed only seldomly, so it is not a big deal
215 216 # that we open this file twice
216 217 fp = repo.vfs(cls._filename)
217 218 d = {}
218 219 try:
219 220 for key in keys:
220 221 d[key] = fp.readline().strip()
221 222 finally:
222 223 fp.close()
223 224 return d
224 225
225 226 @classmethod
226 227 def load(cls, repo):
227 228 version = cls._getversion(repo)
228 229 if version < cls._version:
229 230 d = cls._readold(repo)
230 231 elif version == cls._version:
231 232 d = scmutil.simplekeyvaluefile(
232 233 repo.vfs, cls._filename).read(firstlinenonkeyval=True)
233 234 else:
234 235 raise error.Abort(_('this version of shelve is incompatible '
235 236 'with the version used in this repo'))
236 237
237 238 cls._verifyandtransform(d)
238 239 try:
239 240 obj = cls()
240 241 obj.name = d['name']
241 242 obj.wctx = repo[d['originalwctx']]
242 243 obj.pendingctx = repo[d['pendingctx']]
243 244 obj.parents = d['parents']
244 245 obj.nodestoremove = d['nodestoremove']
245 246 obj.branchtorestore = d.get('branchtorestore', '')
246 247 obj.keep = d.get('keep') == cls._keep
247 248 obj.activebookmark = ''
248 249 if d.get('activebook', '') != cls._noactivebook:
249 250 obj.activebookmark = d.get('activebook', '')
251 obj.interactive = d.get('interactive') == cls._interactive
250 252 except (error.RepoLookupError, KeyError) as err:
251 253 raise error.CorruptedState(pycompat.bytestr(err))
252 254
253 255 return obj
254 256
255 257 @classmethod
256 258 def save(cls, repo, name, originalwctx, pendingctx, nodestoremove,
257 branchtorestore, keep=False, activebook=''):
259 branchtorestore, keep=False, activebook='', interactive=False):
258 260 info = {
259 261 "name": name,
260 262 "originalwctx": nodemod.hex(originalwctx.node()),
261 263 "pendingctx": nodemod.hex(pendingctx.node()),
262 264 "parents": ' '.join([nodemod.hex(p)
263 265 for p in repo.dirstate.parents()]),
264 266 "nodestoremove": ' '.join([nodemod.hex(n)
265 267 for n in nodestoremove]),
266 268 "branchtorestore": branchtorestore,
267 269 "keep": cls._keep if keep else cls._nokeep,
268 270 "activebook": activebook or cls._noactivebook
269 271 }
272 if interactive:
273 info['interactive'] = cls._interactive
270 274 scmutil.simplekeyvaluefile(
271 275 repo.vfs, cls._filename).write(info,
272 276 firstline=("%d" % cls._version))
273 277
274 278 @classmethod
275 279 def clear(cls, repo):
276 280 repo.vfs.unlinkpath(cls._filename, ignoremissing=True)
277 281
278 282 def cleanupoldbackups(repo):
279 283 vfs = vfsmod.vfs(repo.vfs.join(backupdir))
280 284 maxbackups = repo.ui.configint('shelve', 'maxbackups')
281 285 hgfiles = [f for f in vfs.listdir()
282 286 if f.endswith('.' + patchextension)]
283 287 hgfiles = sorted([(vfs.stat(f)[stat.ST_MTIME], f) for f in hgfiles])
284 288 if maxbackups > 0 and maxbackups < len(hgfiles):
285 289 bordermtime = hgfiles[-maxbackups][0]
286 290 else:
287 291 bordermtime = None
288 292 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
289 293 if mtime == bordermtime:
290 294 # keep it, because timestamp can't decide exact order of backups
291 295 continue
292 296 base = f[:-(1 + len(patchextension))]
293 297 for ext in shelvefileextensions:
294 298 vfs.tryunlink(base + '.' + ext)
295 299
296 300 def _backupactivebookmark(repo):
297 301 activebookmark = repo._activebookmark
298 302 if activebookmark:
299 303 bookmarks.deactivate(repo)
300 304 return activebookmark
301 305
302 306 def _restoreactivebookmark(repo, mark):
303 307 if mark:
304 308 bookmarks.activate(repo, mark)
305 309
306 310 def _aborttransaction(repo, tr):
307 311 '''Abort current transaction for shelve/unshelve, but keep dirstate
308 312 '''
309 313 dirstatebackupname = 'dirstate.shelve'
310 314 repo.dirstate.savebackup(tr, dirstatebackupname)
311 315 tr.abort()
312 316 repo.dirstate.restorebackup(None, dirstatebackupname)
313 317
314 318 def getshelvename(repo, parent, opts):
315 319 """Decide on the name this shelve is going to have"""
316 320 def gennames():
317 321 yield label
318 322 for i in itertools.count(1):
319 323 yield '%s-%02d' % (label, i)
320 324 name = opts.get('name')
321 325 label = repo._activebookmark or parent.branch() or 'default'
322 326 # slashes aren't allowed in filenames, therefore we rename it
323 327 label = label.replace('/', '_')
324 328 label = label.replace('\\', '_')
325 329 # filenames must not start with '.' as it should not be hidden
326 330 if label.startswith('.'):
327 331 label = label.replace('.', '_', 1)
328 332
329 333 if name:
330 334 if shelvedfile(repo, name, patchextension).exists():
331 335 e = _("a shelved change named '%s' already exists") % name
332 336 raise error.Abort(e)
333 337
334 338 # ensure we are not creating a subdirectory or a hidden file
335 339 if '/' in name or '\\' in name:
336 340 raise error.Abort(_('shelved change names can not contain slashes'))
337 341 if name.startswith('.'):
338 342 raise error.Abort(_("shelved change names can not start with '.'"))
339 343
340 344 else:
341 345 for n in gennames():
342 346 if not shelvedfile(repo, n, patchextension).exists():
343 347 name = n
344 348 break
345 349
346 350 return name
347 351
348 352 def mutableancestors(ctx):
349 353 """return all mutable ancestors for ctx (included)
350 354
351 355 Much faster than the revset ancestors(ctx) & draft()"""
352 356 seen = {nodemod.nullrev}
353 357 visit = collections.deque()
354 358 visit.append(ctx)
355 359 while visit:
356 360 ctx = visit.popleft()
357 361 yield ctx.node()
358 362 for parent in ctx.parents():
359 363 rev = parent.rev()
360 364 if rev not in seen:
361 365 seen.add(rev)
362 366 if parent.mutable():
363 367 visit.append(parent)
364 368
365 369 def getcommitfunc(extra, interactive, editor=False):
366 370 def commitfunc(ui, repo, message, match, opts):
367 371 hasmq = util.safehasattr(repo, 'mq')
368 372 if hasmq:
369 373 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
370 374
371 375 targetphase = phases.internal
372 376 if not phases.supportinternal(repo):
373 377 targetphase = phases.secret
374 378 overrides = {('phases', 'new-commit'): targetphase}
375 379 try:
376 380 editor_ = False
377 381 if editor:
378 382 editor_ = cmdutil.getcommiteditor(editform='shelve.shelve',
379 383 **pycompat.strkwargs(opts))
380 384 with repo.ui.configoverride(overrides):
381 385 return repo.commit(message, shelveuser, opts.get('date'),
382 386 match, editor=editor_, extra=extra)
383 387 finally:
384 388 if hasmq:
385 389 repo.mq.checkapplied = saved
386 390
387 391 def interactivecommitfunc(ui, repo, *pats, **opts):
388 392 opts = pycompat.byteskwargs(opts)
389 393 match = scmutil.match(repo['.'], pats, {})
390 394 message = opts['message']
391 395 return commitfunc(ui, repo, message, match, opts)
392 396
393 397 return interactivecommitfunc if interactive else commitfunc
394 398
395 399 def _nothingtoshelvemessaging(ui, repo, pats, opts):
396 400 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
397 401 if stat.deleted:
398 402 ui.status(_("nothing changed (%d missing files, see "
399 403 "'hg status')\n") % len(stat.deleted))
400 404 else:
401 405 ui.status(_("nothing changed\n"))
402 406
403 407 def _shelvecreatedcommit(repo, node, name, match):
404 408 info = {'node': nodemod.hex(node)}
405 409 shelvedfile(repo, name, 'shelve').writeinfo(info)
406 410 bases = list(mutableancestors(repo[node]))
407 411 shelvedfile(repo, name, 'hg').writebundle(bases, node)
408 412 with shelvedfile(repo, name, patchextension).opener('wb') as fp:
409 413 cmdutil.exportfile(repo, [node], fp, opts=mdiff.diffopts(git=True),
410 414 match=match)
411 415
412 416 def _includeunknownfiles(repo, pats, opts, extra):
413 417 s = repo.status(match=scmutil.match(repo[None], pats, opts),
414 418 unknown=True)
415 419 if s.unknown:
416 420 extra['shelve_unknown'] = '\0'.join(s.unknown)
417 421 repo[None].add(s.unknown)
418 422
419 423 def _finishshelve(repo, tr):
420 424 if phases.supportinternal(repo):
421 425 tr.close()
422 426 else:
423 427 _aborttransaction(repo, tr)
424 428
425 429 def createcmd(ui, repo, pats, opts):
426 430 """subcommand that creates a new shelve"""
427 431 with repo.wlock():
428 432 cmdutil.checkunfinished(repo)
429 433 return _docreatecmd(ui, repo, pats, opts)
430 434
431 435 def _docreatecmd(ui, repo, pats, opts):
432 436 wctx = repo[None]
433 437 parents = wctx.parents()
434 438 parent = parents[0]
435 439 origbranch = wctx.branch()
436 440
437 441 if parent.node() != nodemod.nullid:
438 442 desc = "changes to: %s" % parent.description().split('\n', 1)[0]
439 443 else:
440 444 desc = '(changes in empty repository)'
441 445
442 446 if not opts.get('message'):
443 447 opts['message'] = desc
444 448
445 449 lock = tr = activebookmark = None
446 450 try:
447 451 lock = repo.lock()
448 452
449 453 # use an uncommitted transaction to generate the bundle to avoid
450 454 # pull races. ensure we don't print the abort message to stderr.
451 455 tr = repo.transaction('shelve', report=lambda x: None)
452 456
453 457 interactive = opts.get('interactive', False)
454 458 includeunknown = (opts.get('unknown', False) and
455 459 not opts.get('addremove', False))
456 460
457 461 name = getshelvename(repo, parent, opts)
458 462 activebookmark = _backupactivebookmark(repo)
459 463 extra = {'internal': 'shelve'}
460 464 if includeunknown:
461 465 _includeunknownfiles(repo, pats, opts, extra)
462 466
463 467 if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
464 468 # In non-bare shelve we don't store newly created branch
465 469 # at bundled commit
466 470 repo.dirstate.setbranch(repo['.'].branch())
467 471
468 472 commitfunc = getcommitfunc(extra, interactive, editor=True)
469 473 if not interactive:
470 474 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
471 475 else:
472 476 node = cmdutil.dorecord(ui, repo, commitfunc, None,
473 477 False, cmdutil.recordfilter, *pats,
474 478 **pycompat.strkwargs(opts))
475 479 if not node:
476 480 _nothingtoshelvemessaging(ui, repo, pats, opts)
477 481 return 1
478 482
479 483 # Create a matcher so that prefetch doesn't attempt to fetch
480 484 # the entire repository pointlessly, and as an optimisation
481 485 # for movedirstate, if needed.
482 486 match = scmutil.matchfiles(repo, repo[node].files())
483 487 _shelvecreatedcommit(repo, node, name, match)
484 488
485 489 if ui.formatted():
486 490 desc = stringutil.ellipsis(desc, ui.termwidth())
487 491 ui.status(_('shelved as %s\n') % name)
488 492 if opts['keep']:
489 493 with repo.dirstate.parentchange():
490 494 scmutil.movedirstate(repo, parent, match)
491 495 else:
492 496 hg.update(repo, parent.node())
493 497 if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
494 498 repo.dirstate.setbranch(origbranch)
495 499
496 500 _finishshelve(repo, tr)
497 501 finally:
498 502 _restoreactivebookmark(repo, activebookmark)
499 503 lockmod.release(tr, lock)
500 504
501 505 def _isbareshelve(pats, opts):
502 506 return (not pats
503 507 and not opts.get('interactive', False)
504 508 and not opts.get('include', False)
505 509 and not opts.get('exclude', False))
506 510
507 511 def _iswctxonnewbranch(repo):
508 512 return repo[None].branch() != repo['.'].branch()
509 513
510 514 def cleanupcmd(ui, repo):
511 515 """subcommand that deletes all shelves"""
512 516
513 517 with repo.wlock():
514 518 for (name, _type) in repo.vfs.readdir(shelvedir):
515 519 suffix = name.rsplit('.', 1)[-1]
516 520 if suffix in shelvefileextensions:
517 521 shelvedfile(repo, name).movetobackup()
518 522 cleanupoldbackups(repo)
519 523
520 524 def deletecmd(ui, repo, pats):
521 525 """subcommand that deletes a specific shelve"""
522 526 if not pats:
523 527 raise error.Abort(_('no shelved changes specified!'))
524 528 with repo.wlock():
525 529 try:
526 530 for name in pats:
527 531 for suffix in shelvefileextensions:
528 532 shfile = shelvedfile(repo, name, suffix)
529 533 # patch file is necessary, as it should
530 534 # be present for any kind of shelve,
531 535 # but the .hg file is optional as in future we
532 536 # will add obsolete shelve with does not create a
533 537 # bundle
534 538 if shfile.exists() or suffix == patchextension:
535 539 shfile.movetobackup()
536 540 cleanupoldbackups(repo)
537 541 except OSError as err:
538 542 if err.errno != errno.ENOENT:
539 543 raise
540 544 raise error.Abort(_("shelved change '%s' not found") % name)
541 545
542 546 def listshelves(repo):
543 547 """return all shelves in repo as list of (time, filename)"""
544 548 try:
545 549 names = repo.vfs.readdir(shelvedir)
546 550 except OSError as err:
547 551 if err.errno != errno.ENOENT:
548 552 raise
549 553 return []
550 554 info = []
551 555 for (name, _type) in names:
552 556 pfx, sfx = name.rsplit('.', 1)
553 557 if not pfx or sfx != patchextension:
554 558 continue
555 559 st = shelvedfile(repo, name).stat()
556 560 info.append((st[stat.ST_MTIME], shelvedfile(repo, pfx).filename()))
557 561 return sorted(info, reverse=True)
558 562
559 563 def listcmd(ui, repo, pats, opts):
560 564 """subcommand that displays the list of shelves"""
561 565 pats = set(pats)
562 566 width = 80
563 567 if not ui.plain():
564 568 width = ui.termwidth()
565 569 namelabel = 'shelve.newest'
566 570 ui.pager('shelve')
567 571 for mtime, name in listshelves(repo):
568 572 sname = util.split(name)[1]
569 573 if pats and sname not in pats:
570 574 continue
571 575 ui.write(sname, label=namelabel)
572 576 namelabel = 'shelve.name'
573 577 if ui.quiet:
574 578 ui.write('\n')
575 579 continue
576 580 ui.write(' ' * (16 - len(sname)))
577 581 used = 16
578 582 date = dateutil.makedate(mtime)
579 583 age = '(%s)' % templatefilters.age(date, abbrev=True)
580 584 ui.write(age, label='shelve.age')
581 585 ui.write(' ' * (12 - len(age)))
582 586 used += 12
583 587 with open(name + '.' + patchextension, 'rb') as fp:
584 588 while True:
585 589 line = fp.readline()
586 590 if not line:
587 591 break
588 592 if not line.startswith('#'):
589 593 desc = line.rstrip()
590 594 if ui.formatted():
591 595 desc = stringutil.ellipsis(desc, width - used)
592 596 ui.write(desc)
593 597 break
594 598 ui.write('\n')
595 599 if not (opts['patch'] or opts['stat']):
596 600 continue
597 601 difflines = fp.readlines()
598 602 if opts['patch']:
599 603 for chunk, label in patch.difflabel(iter, difflines):
600 604 ui.write(chunk, label=label)
601 605 if opts['stat']:
602 606 for chunk, label in patch.diffstatui(difflines, width=width):
603 607 ui.write(chunk, label=label)
604 608
605 609 def patchcmds(ui, repo, pats, opts):
606 610 """subcommand that displays shelves"""
607 611 if len(pats) == 0:
608 612 shelves = listshelves(repo)
609 613 if not shelves:
610 614 raise error.Abort(_("there are no shelves to show"))
611 615 mtime, name = shelves[0]
612 616 sname = util.split(name)[1]
613 617 pats = [sname]
614 618
615 619 for shelfname in pats:
616 620 if not shelvedfile(repo, shelfname, patchextension).exists():
617 621 raise error.Abort(_("cannot find shelf %s") % shelfname)
618 622
619 623 listcmd(ui, repo, pats, opts)
620 624
621 625 def checkparents(repo, state):
622 626 """check parent while resuming an unshelve"""
623 627 if state.parents != repo.dirstate.parents():
624 628 raise error.Abort(_('working directory parents do not match unshelve '
625 629 'state'))
626 630
627 631 def _loadshelvedstate(ui, repo, opts):
628 632 try:
629 633 state = shelvedstate.load(repo)
630 634 if opts.get('keep') is None:
631 635 opts['keep'] = state.keep
632 636 except IOError as err:
633 637 if err.errno != errno.ENOENT:
634 638 raise
635 639 cmdutil.wrongtooltocontinue(repo, _('unshelve'))
636 640 except error.CorruptedState as err:
637 641 ui.debug(pycompat.bytestr(err) + '\n')
638 642 if opts.get('continue'):
639 643 msg = _('corrupted shelved state file')
640 644 hint = _('please run hg unshelve --abort to abort unshelve '
641 645 'operation')
642 646 raise error.Abort(msg, hint=hint)
643 647 elif opts.get('abort'):
644 648 shelvedstate.clear(repo)
645 649 raise error.Abort(_('could not read shelved state file, your '
646 650 'working copy may be in an unexpected state\n'
647 651 'please update to some commit\n'))
648 652 return state
649 653
650 654 def unshelveabort(ui, repo, state):
651 655 """subcommand that abort an in-progress unshelve"""
652 656 with repo.lock():
653 657 try:
654 658 checkparents(repo, state)
655 659
656 660 merge.update(repo, state.pendingctx, branchmerge=False, force=True)
657 661 if (state.activebookmark
658 662 and state.activebookmark in repo._bookmarks):
659 663 bookmarks.activate(repo, state.activebookmark)
660 664 mergefiles(ui, repo, state.wctx, state.pendingctx)
661 665 if not phases.supportinternal(repo):
662 666 repair.strip(ui, repo, state.nodestoremove, backup=False,
663 667 topic='shelve')
664 668 finally:
665 669 shelvedstate.clear(repo)
666 670 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
667 671
668 672 def hgabortunshelve(ui, repo):
669 673 """logic to abort unshelve using 'hg abort"""
670 674 with repo.wlock():
671 675 state = _loadshelvedstate(ui, repo, {'abort' : True})
672 676 return unshelveabort(ui, repo, state)
673 677
674 678 def mergefiles(ui, repo, wctx, shelvectx):
675 679 """updates to wctx and merges the changes from shelvectx into the
676 680 dirstate."""
677 681 with ui.configoverride({('ui', 'quiet'): True}):
678 682 hg.update(repo, wctx.node())
679 683 ui.pushbuffer(True)
680 684 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents())
681 685 ui.popbuffer()
682 686
683 687 def restorebranch(ui, repo, branchtorestore):
684 688 if branchtorestore and branchtorestore != repo.dirstate.branch():
685 689 repo.dirstate.setbranch(branchtorestore)
686 690 ui.status(_('marked working directory as branch %s\n')
687 691 % branchtorestore)
688 692
689 693 def unshelvecleanup(ui, repo, name, opts):
690 694 """remove related files after an unshelve"""
691 695 if not opts.get('keep'):
692 696 for filetype in shelvefileextensions:
693 697 shfile = shelvedfile(repo, name, filetype)
694 698 if shfile.exists():
695 699 shfile.movetobackup()
696 700 cleanupoldbackups(repo)
697 def unshelvecontinue(ui, repo, state, opts, basename=None):
701 def unshelvecontinue(ui, repo, state, opts):
698 702 """subcommand to continue an in-progress unshelve"""
699 703 # We're finishing off a merge. First parent is our original
700 704 # parent, second is the temporary "fake" commit we're unshelving.
701 interactive = opts.get('interactive')
705 interactive = state.interactive
706 basename = state.name
702 707 with repo.lock():
703 708 checkparents(repo, state)
704 709 ms = merge.mergestate.read(repo)
705 710 if list(ms.unresolved()):
706 711 raise error.Abort(
707 712 _("unresolved conflicts, can't continue"),
708 713 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
709 714
710 715 shelvectx = repo[state.parents[1]]
711 716 pendingctx = state.pendingctx
712 717
713 718 with repo.dirstate.parentchange():
714 719 repo.setparents(state.pendingctx.node(), nodemod.nullid)
715 720 repo.dirstate.write(repo.currenttransaction())
716 721
717 722 targetphase = phases.internal
718 723 if not phases.supportinternal(repo):
719 724 targetphase = phases.secret
720 725 overrides = {('phases', 'new-commit'): targetphase}
721 726 with repo.ui.configoverride(overrides, 'unshelve'):
722 727 with repo.dirstate.parentchange():
723 728 repo.setparents(state.parents[0], nodemod.nullid)
724 729 newnode, ispartialunshelve = _createunshelvectx(ui,
725 730 repo, shelvectx, basename, interactive, opts)
726 731
727 732 if newnode is None:
728 733 # If it ended up being a no-op commit, then the normal
729 734 # merge state clean-up path doesn't happen, so do it
730 735 # here. Fix issue5494
731 736 merge.mergestate.clean(repo)
732 737 shelvectx = state.pendingctx
733 738 msg = _('note: unshelved changes already existed '
734 739 'in the working copy\n')
735 740 ui.status(msg)
736 741 else:
737 742 # only strip the shelvectx if we produced one
738 743 state.nodestoremove.append(newnode)
739 744 shelvectx = repo[newnode]
740 745
741 746 hg.updaterepo(repo, pendingctx.node(), overwrite=False)
742 747 mergefiles(ui, repo, state.wctx, shelvectx)
743 748 restorebranch(ui, repo, state.branchtorestore)
744 749
745 750 if not ispartialunshelve:
746 751 if not phases.supportinternal(repo):
747 752 repair.strip(ui, repo, state.nodestoremove, backup=False,
748 753 topic='shelve')
749 754 shelvedstate.clear(repo)
750 755 unshelvecleanup(ui, repo, state.name, opts)
751 756 _restoreactivebookmark(repo, state.activebookmark)
752 757 ui.status(_("unshelve of '%s' complete\n") % state.name)
753 758
754 759 def hgcontinueunshelve(ui, repo):
755 760 """logic to resume unshelve using 'hg continue'"""
756 761 with repo.wlock():
757 762 state = _loadshelvedstate(ui, repo, {'continue' : True})
758 763 return unshelvecontinue(ui, repo, state, {'keep' : state.keep})
759 764
760 765 def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
761 766 """Temporarily commit working copy changes before moving unshelve commit"""
762 767 # Store pending changes in a commit and remember added in case a shelve
763 768 # contains unknown files that are part of the pending change
764 769 s = repo.status()
765 770 addedbefore = frozenset(s.added)
766 771 if not (s.modified or s.added or s.removed):
767 772 return tmpwctx, addedbefore
768 773 ui.status(_("temporarily committing pending changes "
769 774 "(restore with 'hg unshelve --abort')\n"))
770 775 extra = {'internal': 'shelve'}
771 776 commitfunc = getcommitfunc(extra=extra, interactive=False,
772 777 editor=False)
773 778 tempopts = {}
774 779 tempopts['message'] = "pending changes temporary commit"
775 780 tempopts['date'] = opts.get('date')
776 781 with ui.configoverride({('ui', 'quiet'): True}):
777 782 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
778 783 tmpwctx = repo[node]
779 784 return tmpwctx, addedbefore
780 785
781 786 def _unshelverestorecommit(ui, repo, tr, basename):
782 787 """Recreate commit in the repository during the unshelve"""
783 788 repo = repo.unfiltered()
784 789 node = None
785 790 if shelvedfile(repo, basename, 'shelve').exists():
786 791 node = shelvedfile(repo, basename, 'shelve').readinfo()['node']
787 792 if node is None or node not in repo:
788 793 with ui.configoverride({('ui', 'quiet'): True}):
789 794 shelvectx = shelvedfile(repo, basename, 'hg').applybundle(tr)
790 795 # We might not strip the unbundled changeset, so we should keep track of
791 796 # the unshelve node in case we need to reuse it (eg: unshelve --keep)
792 797 if node is None:
793 798 info = {'node': nodemod.hex(shelvectx.node())}
794 799 shelvedfile(repo, basename, 'shelve').writeinfo(info)
795 800 else:
796 801 shelvectx = repo[node]
797 802
798 803 return repo, shelvectx
799 804
800 805 def _createunshelvectx(ui, repo, shelvectx, basename, interactive, opts):
801 806 """Handles the creation of unshelve commit and updates the shelve if it
802 807 was partially unshelved.
803 808
804 809 If interactive is:
805 810
806 811 * False: Commits all the changes in the working directory.
807 812 * True: Prompts the user to select changes to unshelve and commit them.
808 813 Update the shelve with remaining changes.
809 814
810 815 Returns the node of the new commit formed and a bool indicating whether
811 816 the shelve was partially unshelved.Creates a commit ctx to unshelve
812 817 interactively or non-interactively.
813 818
814 819 The user might want to unshelve certain changes only from the stored
815 820 shelve in interactive. So, we would create two commits. One with requested
816 821 changes to unshelve at that time and the latter is shelved for future.
817 822
818 823 Here, we return both the newnode which is created interactively and a
819 824 bool to know whether the shelve is partly done or completely done.
820 825 """
821 826 opts['message'] = shelvectx.description()
822 827 opts['interactive-unshelve'] = True
823 828 pats = []
824 829 if not interactive:
825 830 newnode = repo.commit(text=shelvectx.description(),
826 831 extra=shelvectx.extra(),
827 832 user=shelvectx.user(),
828 833 date=shelvectx.date())
829 834 return newnode, False
830 835
831 836 commitfunc = getcommitfunc(shelvectx.extra(), interactive=True,
832 837 editor=True)
833 838 newnode = cmdutil.dorecord(ui, repo, commitfunc, None, False,
834 839 cmdutil.recordfilter, *pats,
835 840 **pycompat.strkwargs(opts))
836 841 snode = repo.commit(text=shelvectx.description(),
837 842 extra=shelvectx.extra(),
838 843 user=shelvectx.user())
839 844 if snode:
840 845 m = scmutil.matchfiles(repo, repo[snode].files())
841 846 _shelvecreatedcommit(repo, snode, basename, m)
842 847
843 848 return newnode, bool(snode)
844 849
845 850 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
846 851 tmpwctx, shelvectx, branchtorestore,
847 852 activebookmark):
848 853 """Rebase restored commit from its original location to a destination"""
849 854 # If the shelve is not immediately on top of the commit
850 855 # we'll be merging with, rebase it to be on top.
851 856 interactive = opts.get('interactive')
852 857 if tmpwctx.node() == shelvectx.p1().node() and not interactive:
853 858 # We won't skip on interactive mode because, the user might want to
854 859 # unshelve certain changes only.
855 860 return shelvectx, False
856 861
857 862 overrides = {
858 863 ('ui', 'forcemerge'): opts.get('tool', ''),
859 864 ('phases', 'new-commit'): phases.secret,
860 865 }
861 866 with repo.ui.configoverride(overrides, 'unshelve'):
862 867 ui.status(_('rebasing shelved changes\n'))
863 868 stats = merge.graft(repo, shelvectx, shelvectx.p1(),
864 869 labels=['shelve', 'working-copy'],
865 870 keepconflictparent=True)
866 871 if stats.unresolvedcount:
867 872 tr.close()
868 873
869 874 nodestoremove = [repo.changelog.node(rev)
870 875 for rev in pycompat.xrange(oldtiprev, len(repo))]
871 876 shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoremove,
872 branchtorestore, opts.get('keep'), activebookmark)
877 branchtorestore, opts.get('keep'), activebookmark,
878 interactive)
873 879 raise error.InterventionRequired(
874 880 _("unresolved conflicts (see 'hg resolve', then "
875 881 "'hg unshelve --continue')"))
876 882
877 883 with repo.dirstate.parentchange():
878 884 repo.setparents(tmpwctx.node(), nodemod.nullid)
879 885 newnode, ispartialunshelve = _createunshelvectx(ui, repo,
880 886 shelvectx, basename, interactive, opts)
881 887
882 888 if newnode is None:
883 889 # If it ended up being a no-op commit, then the normal
884 890 # merge state clean-up path doesn't happen, so do it
885 891 # here. Fix issue5494
886 892 merge.mergestate.clean(repo)
887 893 shelvectx = tmpwctx
888 894 msg = _('note: unshelved changes already existed '
889 895 'in the working copy\n')
890 896 ui.status(msg)
891 897 else:
892 898 shelvectx = repo[newnode]
893 899 hg.updaterepo(repo, tmpwctx.node(), False)
894 900
895 901 return shelvectx, ispartialunshelve
896 902
897 903 def _forgetunknownfiles(repo, shelvectx, addedbefore):
898 904 # Forget any files that were unknown before the shelve, unknown before
899 905 # unshelve started, but are now added.
900 906 shelveunknown = shelvectx.extra().get('shelve_unknown')
901 907 if not shelveunknown:
902 908 return
903 909 shelveunknown = frozenset(shelveunknown.split('\0'))
904 910 addedafter = frozenset(repo.status().added)
905 911 toforget = (addedafter & shelveunknown) - addedbefore
906 912 repo[None].forget(toforget)
907 913
908 914 def _finishunshelve(repo, oldtiprev, tr, activebookmark):
909 915 _restoreactivebookmark(repo, activebookmark)
910 916 # The transaction aborting will strip all the commits for us,
911 917 # but it doesn't update the inmemory structures, so addchangegroup
912 918 # hooks still fire and try to operate on the missing commits.
913 919 # Clean up manually to prevent this.
914 920 repo.unfiltered().changelog.strip(oldtiprev, tr)
915 921 _aborttransaction(repo, tr)
916 922
917 923 def _checkunshelveuntrackedproblems(ui, repo, shelvectx):
918 924 """Check potential problems which may result from working
919 925 copy having untracked changes."""
920 926 wcdeleted = set(repo.status().deleted)
921 927 shelvetouched = set(shelvectx.files())
922 928 intersection = wcdeleted.intersection(shelvetouched)
923 929 if intersection:
924 930 m = _("shelved change touches missing files")
925 931 hint = _("run hg status to see which files are missing")
926 932 raise error.Abort(m, hint=hint)
927 933
928 934 def dounshelve(ui, repo, *shelved, **opts):
929 935 opts = pycompat.byteskwargs(opts)
930 936 abortf = opts.get('abort')
931 937 continuef = opts.get('continue')
932 938 interactive = opts.get('interactive')
933 939 if not abortf and not continuef:
934 940 cmdutil.checkunfinished(repo)
935 941 shelved = list(shelved)
936 942 if opts.get("name"):
937 943 shelved.append(opts["name"])
938 944
939 if abortf or continuef and not interactive:
945 if abortf or continuef:
940 946 if abortf and continuef:
941 947 raise error.Abort(_('cannot use both abort and continue'))
942 948 if shelved:
943 949 raise error.Abort(_('cannot combine abort/continue with '
944 950 'naming a shelved change'))
945 951 if abortf and opts.get('tool', False):
946 952 ui.warn(_('tool option will be ignored\n'))
947 953
948 954 state = _loadshelvedstate(ui, repo, opts)
949 955 if abortf:
950 956 return unshelveabort(ui, repo, state)
951 957 elif continuef:
952 958 return unshelvecontinue(ui, repo, state, opts)
953 959 elif len(shelved) > 1:
954 960 raise error.Abort(_('can only unshelve one change at a time'))
955 961 elif not shelved:
956 962 shelved = listshelves(repo)
957 963 if not shelved:
958 964 raise error.Abort(_('no shelved changes to apply!'))
959 965 basename = util.split(shelved[0][1])[1]
960 966 ui.status(_("unshelving change '%s'\n") % basename)
961 elif shelved:
967 else:
962 968 basename = shelved[0]
963 if continuef and interactive:
964 state = _loadshelvedstate(ui, repo, opts)
965 return unshelvecontinue(ui, repo, state, opts, basename)
966 969
967 970 if not shelvedfile(repo, basename, patchextension).exists():
968 971 raise error.Abort(_("shelved change '%s' not found") % basename)
969 972
970 973 repo = repo.unfiltered()
971 974 lock = tr = None
972 975 try:
973 976 lock = repo.lock()
974 977 tr = repo.transaction('unshelve', report=lambda x: None)
975 978 oldtiprev = len(repo)
976 979
977 980 pctx = repo['.']
978 981 tmpwctx = pctx
979 982 # The goal is to have a commit structure like so:
980 983 # ...-> pctx -> tmpwctx -> shelvectx
981 984 # where tmpwctx is an optional commit with the user's pending changes
982 985 # and shelvectx is the unshelved changes. Then we merge it all down
983 986 # to the original pctx.
984 987
985 988 activebookmark = _backupactivebookmark(repo)
986 989 tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
987 990 tmpwctx)
988 991 repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename)
989 992 _checkunshelveuntrackedproblems(ui, repo, shelvectx)
990 993 branchtorestore = ''
991 994 if shelvectx.branch() != shelvectx.p1().branch():
992 995 branchtorestore = shelvectx.branch()
993 996
994 997 shelvectx, ispartialunshelve = _rebaserestoredcommit(ui, repo, opts,
995 998 tr, oldtiprev, basename, pctx, tmpwctx, shelvectx,
996 999 branchtorestore, activebookmark)
997 1000 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
998 1001 with ui.configoverride(overrides, 'unshelve'):
999 1002 mergefiles(ui, repo, pctx, shelvectx)
1000 1003 restorebranch(ui, repo, branchtorestore)
1001 1004 if not ispartialunshelve:
1002 1005 _forgetunknownfiles(repo, shelvectx, addedbefore)
1003 1006
1004 1007 shelvedstate.clear(repo)
1005 1008 _finishunshelve(repo, oldtiprev, tr, activebookmark)
1006 1009 unshelvecleanup(ui, repo, basename, opts)
1007 1010 finally:
1008 1011 if tr:
1009 1012 tr.release()
1010 1013 lockmod.release(lock)
@@ -1,1385 +1,1384 b''
1 1 #testcases stripbased phasebased
2 2
3 3 $ cat <<EOF >> $HGRCPATH
4 4 > [extensions]
5 5 > mq =
6 6 > [defaults]
7 7 > diff = --nodates --git
8 8 > qnew = --date '0 0'
9 9 > [shelve]
10 10 > maxbackups = 2
11 11 > EOF
12 12
13 13 #if phasebased
14 14
15 15 $ cat <<EOF >> $HGRCPATH
16 16 > [format]
17 17 > internal-phase = yes
18 18 > EOF
19 19
20 20 #endif
21 21
22 22 $ hg init repo
23 23 $ cd repo
24 24 $ mkdir a b
25 25 $ echo a > a/a
26 26 $ echo b > b/b
27 27 $ echo c > c
28 28 $ echo d > d
29 29 $ echo x > x
30 30 $ hg addremove -q
31 31
32 32 shelve has a help message
33 33 $ hg shelve -h
34 34 hg shelve [OPTION]... [FILE]...
35 35
36 36 save and set aside changes from the working directory
37 37
38 38 Shelving takes files that "hg status" reports as not clean, saves the
39 39 modifications to a bundle (a shelved change), and reverts the files so
40 40 that their state in the working directory becomes clean.
41 41
42 42 To restore these changes to the working directory, using "hg unshelve";
43 43 this will work even if you switch to a different commit.
44 44
45 45 When no files are specified, "hg shelve" saves all not-clean files. If
46 46 specific files or directories are named, only changes to those files are
47 47 shelved.
48 48
49 49 In bare shelve (when no files are specified, without interactive, include
50 50 and exclude option), shelving remembers information if the working
51 51 directory was on newly created branch, in other words working directory
52 52 was on different branch than its first parent. In this situation
53 53 unshelving restores branch information to the working directory.
54 54
55 55 Each shelved change has a name that makes it easier to find later. The
56 56 name of a shelved change defaults to being based on the active bookmark,
57 57 or if there is no active bookmark, the current named branch. To specify a
58 58 different name, use "--name".
59 59
60 60 To see a list of existing shelved changes, use the "--list" option. For
61 61 each shelved change, this will print its name, age, and description; use "
62 62 --patch" or "--stat" for more details.
63 63
64 64 To delete specific shelved changes, use "--delete". To delete all shelved
65 65 changes, use "--cleanup".
66 66
67 67 options ([+] can be repeated):
68 68
69 69 -A --addremove mark new/missing files as added/removed before
70 70 shelving
71 71 -u --unknown store unknown files in the shelve
72 72 --cleanup delete all shelved changes
73 73 --date DATE shelve with the specified commit date
74 74 -d --delete delete the named shelved change(s)
75 75 -e --edit invoke editor on commit messages
76 76 -k --keep shelve, but keep changes in the working directory
77 77 -l --list list current shelves
78 78 -m --message TEXT use text as shelve message
79 79 -n --name NAME use the given name for the shelved commit
80 80 -p --patch output patches for changes (provide the names of the
81 81 shelved changes as positional arguments)
82 82 -i --interactive interactive mode
83 83 --stat output diffstat-style summary of changes (provide
84 84 the names of the shelved changes as positional
85 85 arguments)
86 86 -I --include PATTERN [+] include names matching the given patterns
87 87 -X --exclude PATTERN [+] exclude names matching the given patterns
88 88 --mq operate on patch repository
89 89
90 90 (some details hidden, use --verbose to show complete help)
91 91
92 92 shelving in an empty repo should be possible
93 93 (this tests also that editor is not invoked, if '--edit' is not
94 94 specified)
95 95
96 96 $ HGEDITOR=cat hg shelve
97 97 shelved as default
98 98 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
99 99
100 100 $ hg unshelve
101 101 unshelving change 'default'
102 102
103 103 $ hg commit -q -m 'initial commit'
104 104
105 105 $ hg shelve
106 106 nothing changed
107 107 [1]
108 108
109 109 make sure shelve files were backed up
110 110
111 111 $ ls .hg/shelve-backup
112 112 default.hg
113 113 default.patch
114 114 default.shelve
115 115
116 116 checks to make sure we dont create a directory or
117 117 hidden file while choosing a new shelve name
118 118
119 119 when we are given a name
120 120
121 121 $ hg shelve -n foo/bar
122 122 abort: shelved change names can not contain slashes
123 123 [255]
124 124 $ hg shelve -n .baz
125 125 abort: shelved change names can not start with '.'
126 126 [255]
127 127 $ hg shelve -n foo\\bar
128 128 abort: shelved change names can not contain slashes
129 129 [255]
130 130
131 131 when shelve has to choose itself
132 132
133 133 $ hg branch x/y -q
134 134 $ hg commit -q -m "Branch commit 0"
135 135 $ hg shelve
136 136 nothing changed
137 137 [1]
138 138 $ hg branch .x -q
139 139 $ hg commit -q -m "Branch commit 1"
140 140 $ hg shelve
141 141 nothing changed
142 142 [1]
143 143 $ hg branch x\\y -q
144 144 $ hg commit -q -m "Branch commit 2"
145 145 $ hg shelve
146 146 nothing changed
147 147 [1]
148 148
149 149 cleaning the branches made for name checking tests
150 150
151 151 $ hg up default -q
152 152 $ hg strip e9177275307e+6a6d231f43d+882bae7c62c2 -q
153 153
154 154 create an mq patch - shelving should work fine with a patch applied
155 155
156 156 $ echo n > n
157 157 $ hg add n
158 158 $ hg commit n -m second
159 159 $ hg qnew second.patch
160 160
161 161 shelve a change that we will delete later
162 162
163 163 $ echo a >> a/a
164 164 $ hg shelve
165 165 shelved as default
166 166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 167
168 168 set up some more complex changes to shelve
169 169
170 170 $ echo a >> a/a
171 171 $ hg mv b b.rename
172 172 moving b/b to b.rename/b
173 173 $ hg cp c c.copy
174 174 $ hg status -C
175 175 M a/a
176 176 A b.rename/b
177 177 b/b
178 178 A c.copy
179 179 c
180 180 R b/b
181 181
182 182 the common case - no options or filenames
183 183
184 184 $ hg shelve
185 185 shelved as default-01
186 186 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
187 187 $ hg status -C
188 188
189 189 ensure that our shelved changes exist
190 190
191 191 $ hg shelve -l
192 192 default-01 (*)* changes to: [mq]: second.patch (glob)
193 193 default (*)* changes to: [mq]: second.patch (glob)
194 194
195 195 $ hg shelve -l -p default
196 196 default (*)* changes to: [mq]: second.patch (glob)
197 197
198 198 diff --git a/a/a b/a/a
199 199 --- a/a/a
200 200 +++ b/a/a
201 201 @@ -1,1 +1,2 @@
202 202 a
203 203 +a
204 204
205 205 $ hg shelve --list --addremove
206 206 abort: options '--list' and '--addremove' may not be used together
207 207 [255]
208 208
209 209 delete our older shelved change
210 210
211 211 $ hg shelve -d default
212 212 $ hg qfinish -a -q
213 213
214 214 ensure shelve backups aren't overwritten
215 215
216 216 $ ls .hg/shelve-backup/
217 217 default-1.hg
218 218 default-1.patch
219 219 default-1.shelve
220 220 default.hg
221 221 default.patch
222 222 default.shelve
223 223
224 224 local edits should not prevent a shelved change from applying
225 225
226 226 $ printf "z\na\n" > a/a
227 227 $ hg unshelve --keep
228 228 unshelving change 'default-01'
229 229 temporarily committing pending changes (restore with 'hg unshelve --abort')
230 230 rebasing shelved changes
231 231 merging a/a
232 232
233 233 $ hg revert --all -q
234 234 $ rm a/a.orig b.rename/b c.copy
235 235
236 236 apply it and make sure our state is as expected
237 237
238 238 (this also tests that same timestamp prevents backups from being
239 239 removed, even though there are more than 'maxbackups' backups)
240 240
241 241 $ f -t .hg/shelve-backup/default.patch
242 242 .hg/shelve-backup/default.patch: file
243 243 $ touch -t 200001010000 .hg/shelve-backup/default.patch
244 244 $ f -t .hg/shelve-backup/default-1.patch
245 245 .hg/shelve-backup/default-1.patch: file
246 246 $ touch -t 200001010000 .hg/shelve-backup/default-1.patch
247 247
248 248 $ hg unshelve
249 249 unshelving change 'default-01'
250 250 $ hg status -C
251 251 M a/a
252 252 A b.rename/b
253 253 b/b
254 254 A c.copy
255 255 c
256 256 R b/b
257 257 $ hg shelve -l
258 258
259 259 (both of default.hg and default-1.hg should be still kept, because it
260 260 is difficult to decide actual order of them from same timestamp)
261 261
262 262 $ ls .hg/shelve-backup/
263 263 default-01.hg
264 264 default-01.patch
265 265 default-01.shelve
266 266 default-1.hg
267 267 default-1.patch
268 268 default-1.shelve
269 269 default.hg
270 270 default.patch
271 271 default.shelve
272 272
273 273 $ hg unshelve
274 274 abort: no shelved changes to apply!
275 275 [255]
276 276 $ hg unshelve foo
277 277 abort: shelved change 'foo' not found
278 278 [255]
279 279
280 280 named shelves, specific filenames, and "commit messages" should all work
281 281 (this tests also that editor is invoked, if '--edit' is specified)
282 282
283 283 $ hg status -C
284 284 M a/a
285 285 A b.rename/b
286 286 b/b
287 287 A c.copy
288 288 c
289 289 R b/b
290 290 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
291 291 wat
292 292
293 293
294 294 HG: Enter commit message. Lines beginning with 'HG:' are removed.
295 295 HG: Leave message empty to abort commit.
296 296 HG: --
297 297 HG: user: shelve@localhost
298 298 HG: branch 'default'
299 299 HG: changed a/a
300 300
301 301 expect "a" to no longer be present, but status otherwise unchanged
302 302
303 303 $ hg status -C
304 304 A b.rename/b
305 305 b/b
306 306 A c.copy
307 307 c
308 308 R b/b
309 309 $ hg shelve -l --stat
310 310 wibble (*) wat (glob)
311 311 a/a | 1 +
312 312 1 files changed, 1 insertions(+), 0 deletions(-)
313 313
314 314 and now "a/a" should reappear
315 315
316 316 $ cd a
317 317 $ hg unshelve -q wibble
318 318 $ cd ..
319 319 $ hg status -C
320 320 M a/a
321 321 A b.rename/b
322 322 b/b
323 323 A c.copy
324 324 c
325 325 R b/b
326 326
327 327 ensure old shelve backups are being deleted automatically
328 328
329 329 $ ls .hg/shelve-backup/
330 330 default-01.hg
331 331 default-01.patch
332 332 default-01.shelve
333 333 wibble.hg
334 334 wibble.patch
335 335 wibble.shelve
336 336
337 337 cause unshelving to result in a merge with 'a' conflicting
338 338
339 339 $ hg shelve -q
340 340 $ echo c>>a/a
341 341 $ hg commit -m second
342 342 $ hg tip --template '{files}\n'
343 343 a/a
344 344
345 345 add an unrelated change that should be preserved
346 346
347 347 $ mkdir foo
348 348 $ echo foo > foo/foo
349 349 $ hg add foo/foo
350 350
351 351 force a conflicted merge to occur
352 352
353 353 $ hg unshelve
354 354 unshelving change 'default'
355 355 temporarily committing pending changes (restore with 'hg unshelve --abort')
356 356 rebasing shelved changes
357 357 merging a/a
358 358 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
359 359 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
360 360 [1]
361 361 $ hg status -v
362 362 M a/a
363 363 M b.rename/b
364 364 M c.copy
365 365 R b/b
366 366 ? a/a.orig
367 367 # The repository is in an unfinished *unshelve* state.
368 368
369 369 # Unresolved merge conflicts:
370 370 #
371 371 # a/a
372 372 #
373 373 # To mark files as resolved: hg resolve --mark FILE
374 374
375 375 # To continue: hg unshelve --continue
376 376 # To abort: hg unshelve --abort
377 377
378 378
379 379 ensure that we have a merge with unresolved conflicts
380 380
381 381 #if phasebased
382 382 $ hg heads -q --template '{rev}\n'
383 383 8
384 384 5
385 385 $ hg parents -q --template '{rev}\n'
386 386 8
387 387 5
388 388 #endif
389 389
390 390 #if stripbased
391 391 $ hg heads -q --template '{rev}\n'
392 392 5
393 393 4
394 394 $ hg parents -q --template '{rev}\n'
395 395 4
396 396 5
397 397 #endif
398 398
399 399 $ hg status
400 400 M a/a
401 401 M b.rename/b
402 402 M c.copy
403 403 R b/b
404 404 ? a/a.orig
405 405 $ hg diff
406 406 diff --git a/a/a b/a/a
407 407 --- a/a/a
408 408 +++ b/a/a
409 409 @@ -1,2 +1,6 @@
410 410 a
411 411 +<<<<<<< shelve: 2377350b6337 - shelve: pending changes temporary commit
412 412 c
413 413 +=======
414 414 +a
415 415 +>>>>>>> working-copy: a68ec3400638 - shelve: changes to: [mq]: second.patch
416 416 diff --git a/b/b b/b.rename/b
417 417 rename from b/b
418 418 rename to b.rename/b
419 419 diff --git a/c b/c.copy
420 420 copy from c
421 421 copy to c.copy
422 422 $ hg resolve -l
423 423 U a/a
424 424
425 425 $ hg shelve
426 426 abort: unshelve already in progress
427 427 (use 'hg unshelve --continue' or 'hg unshelve --abort')
428 428 [255]
429 429
430 430 abort the unshelve and be happy
431 431
432 432 $ hg status
433 433 M a/a
434 434 M b.rename/b
435 435 M c.copy
436 436 R b/b
437 437 ? a/a.orig
438 438 $ hg unshelve -a
439 439 unshelve of 'default' aborted
440 440 $ hg heads -q
441 441 [37]:2e69b451d1ea (re)
442 442 $ hg parents
443 443 changeset: [37]:2e69b451d1ea (re)
444 444 tag: tip
445 445 parent: 3:509104101065 (?)
446 446 user: test
447 447 date: Thu Jan 01 00:00:00 1970 +0000
448 448 summary: second
449 449
450 450 $ hg resolve -l
451 451 $ hg status
452 452 A foo/foo
453 453 ? a/a.orig
454 454
455 455 try to continue with no unshelve underway
456 456
457 457 $ hg unshelve -c
458 458 abort: no unshelve in progress
459 459 [255]
460 460 $ hg status
461 461 A foo/foo
462 462 ? a/a.orig
463 463
464 464 redo the unshelve to get a conflict
465 465
466 466 $ hg unshelve -q
467 467 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
468 468 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
469 469 [1]
470 470
471 471 attempt to continue
472 472
473 473 $ hg unshelve -c
474 474 abort: unresolved conflicts, can't continue
475 475 (see 'hg resolve', then 'hg unshelve --continue')
476 476 [255]
477 477
478 478 $ hg revert -r . a/a
479 479 $ hg resolve -m a/a
480 480 (no more unresolved files)
481 481 continue: hg unshelve --continue
482 482
483 483 $ hg commit -m 'commit while unshelve in progress'
484 484 abort: unshelve already in progress
485 485 (use 'hg unshelve --continue' or 'hg unshelve --abort')
486 486 [255]
487 487
488 488 $ hg graft --continue
489 489 abort: no graft in progress
490 490 (continue: hg unshelve --continue)
491 491 [255]
492 492 $ hg unshelve -c
493 493 unshelve of 'default' complete
494 494
495 495 ensure the repo is as we hope
496 496
497 497 $ hg parents
498 498 changeset: [37]:2e69b451d1ea (re)
499 499 tag: tip
500 500 parent: 3:509104101065 (?)
501 501 user: test
502 502 date: Thu Jan 01 00:00:00 1970 +0000
503 503 summary: second
504 504
505 505 $ hg heads -q
506 506 [37]:2e69b451d1ea (re)
507 507
508 508 $ hg status -C
509 509 A b.rename/b
510 510 b/b
511 511 A c.copy
512 512 c
513 513 A foo/foo
514 514 R b/b
515 515 ? a/a.orig
516 516
517 517 there should be no shelves left
518 518
519 519 $ hg shelve -l
520 520
521 521 #if execbit
522 522
523 523 ensure that metadata-only changes are shelved
524 524
525 525 $ chmod +x a/a
526 526 $ hg shelve -q -n execbit a/a
527 527 $ hg status a/a
528 528 $ hg unshelve -q execbit
529 529 $ hg status a/a
530 530 M a/a
531 531 $ hg revert a/a
532 532
533 533 #else
534 534
535 535 Dummy shelve op, to keep rev numbers aligned
536 536
537 537 $ echo foo > a/a
538 538 $ hg shelve -q -n dummy a/a
539 539 $ hg unshelve -q dummy
540 540 $ hg revert a/a
541 541
542 542 #endif
543 543
544 544 #if symlink
545 545
546 546 $ rm a/a
547 547 $ ln -s foo a/a
548 548 $ hg shelve -q -n symlink a/a
549 549 $ hg status a/a
550 550 $ hg unshelve -q -n symlink
551 551 $ hg status a/a
552 552 M a/a
553 553 $ hg revert a/a
554 554
555 555 #else
556 556
557 557 Dummy shelve op, to keep rev numbers aligned
558 558
559 559 $ echo bar > a/a
560 560 $ hg shelve -q -n dummy a/a
561 561 $ hg unshelve -q dummy
562 562 $ hg revert a/a
563 563
564 564 #endif
565 565
566 566 set up another conflict between a commit and a shelved change
567 567
568 568 $ hg revert -q -C -a
569 569 $ rm a/a.orig b.rename/b c.copy
570 570 $ echo a >> a/a
571 571 $ hg shelve -q
572 572 $ echo x >> a/a
573 573 $ hg ci -m 'create conflict'
574 574 $ hg add foo/foo
575 575
576 576 if we resolve a conflict while unshelving, the unshelve should succeed
577 577
578 578 $ hg unshelve --tool :merge-other --keep
579 579 unshelving change 'default'
580 580 temporarily committing pending changes (restore with 'hg unshelve --abort')
581 581 rebasing shelved changes
582 582 merging a/a
583 583 $ hg parents -q
584 584 (4|13):33f7f61e6c5e (re)
585 585 $ hg shelve -l
586 586 default (*)* changes to: second (glob)
587 587 $ hg status
588 588 M a/a
589 589 A foo/foo
590 590 $ cat a/a
591 591 a
592 592 c
593 593 a
594 594 $ cat > a/a << EOF
595 595 > a
596 596 > c
597 597 > x
598 598 > EOF
599 599
600 600 $ HGMERGE=true hg unshelve
601 601 unshelving change 'default'
602 602 temporarily committing pending changes (restore with 'hg unshelve --abort')
603 603 rebasing shelved changes
604 604 merging a/a
605 605 note: unshelved changes already existed in the working copy
606 606 $ hg parents -q
607 607 (4|13):33f7f61e6c5e (re)
608 608 $ hg shelve -l
609 609 $ hg status
610 610 A foo/foo
611 611 $ cat a/a
612 612 a
613 613 c
614 614 x
615 615
616 616 test keep and cleanup
617 617
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 shelve --list
622 622 default (*)* changes to: create conflict (glob)
623 623 $ hg unshelve -k
624 624 unshelving change 'default'
625 625 $ hg shelve --list
626 626 default (*)* changes to: create conflict (glob)
627 627 $ hg shelve --cleanup
628 628 $ hg shelve --list
629 629
630 630 $ hg shelve --cleanup --delete
631 631 abort: options '--cleanup' and '--delete' may not be used together
632 632 [255]
633 633 $ hg shelve --cleanup --patch
634 634 abort: options '--cleanup' and '--patch' may not be used together
635 635 [255]
636 636 $ hg shelve --cleanup --message MESSAGE
637 637 abort: options '--cleanup' and '--message' may not be used together
638 638 [255]
639 639
640 640 test bookmarks
641 641
642 642 $ hg bookmark test
643 643 $ hg bookmark
644 644 \* test (4|13):33f7f61e6c5e (re)
645 645 $ hg shelve
646 646 shelved as test
647 647 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
648 648 $ hg bookmark
649 649 \* test (4|13):33f7f61e6c5e (re)
650 650 $ hg unshelve
651 651 unshelving change 'test'
652 652 $ hg bookmark
653 653 \* test (4|13):33f7f61e6c5e (re)
654 654
655 655 shelve should still work even if mq is disabled
656 656
657 657 $ hg --config extensions.mq=! shelve
658 658 shelved as test
659 659 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
660 660 $ hg --config extensions.mq=! shelve --list
661 661 test (*)* changes to: create conflict (glob)
662 662 $ hg bookmark
663 663 \* test (4|13):33f7f61e6c5e (re)
664 664 $ hg --config extensions.mq=! unshelve
665 665 unshelving change 'test'
666 666 $ hg bookmark
667 667 \* test (4|13):33f7f61e6c5e (re)
668 668
669 669 Recreate some conflict again
670 670
671 671 $ hg up -C -r 2e69b451d1ea
672 672 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
673 673 (leaving bookmark test)
674 674 $ echo y >> a/a
675 675 $ hg shelve
676 676 shelved as default
677 677 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
678 678 $ hg up test
679 679 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
680 680 (activating bookmark test)
681 681 $ hg bookmark
682 682 \* test (4|13):33f7f61e6c5e (re)
683 683 $ hg unshelve
684 684 unshelving change 'default'
685 685 rebasing shelved changes
686 686 merging a/a
687 687 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
688 688 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
689 689 [1]
690 690 $ hg bookmark
691 691 test (4|13):33f7f61e6c5e (re)
692 692
693 693 Test that resolving all conflicts in one direction (so that the rebase
694 694 is a no-op), works (issue4398)
695 695
696 696 $ hg revert -a -r .
697 697 reverting a/a
698 698 $ hg resolve -m a/a
699 699 (no more unresolved files)
700 700 continue: hg unshelve --continue
701 701 $ hg unshelve -c
702 702 note: unshelved changes already existed in the working copy
703 703 unshelve of 'default' complete
704 704 $ hg bookmark
705 705 \* test (4|13):33f7f61e6c5e (re)
706 706 $ hg diff
707 707 $ hg status
708 708 ? a/a.orig
709 709 ? foo/foo
710 710 $ hg summary
711 711 parent: (4|13):33f7f61e6c5e tip (re)
712 712 create conflict
713 713 branch: default
714 714 bookmarks: *test
715 715 commit: 2 unknown (clean)
716 716 update: (current)
717 717 phases: 5 draft
718 718
719 719 $ hg shelve --delete --stat
720 720 abort: options '--delete' and '--stat' may not be used together
721 721 [255]
722 722 $ hg shelve --delete --name NAME
723 723 abort: options '--delete' and '--name' may not be used together
724 724 [255]
725 725
726 726 Test interactive shelve
727 727 $ cat <<EOF >> $HGRCPATH
728 728 > [ui]
729 729 > interactive = true
730 730 > EOF
731 731 $ echo 'a' >> a/b
732 732 $ cat a/a >> a/b
733 733 $ echo 'x' >> a/b
734 734 $ mv a/b a/a
735 735 $ echo 'a' >> foo/foo
736 736 $ hg st
737 737 M a/a
738 738 ? a/a.orig
739 739 ? foo/foo
740 740 $ cat a/a
741 741 a
742 742 a
743 743 c
744 744 x
745 745 x
746 746 $ cat foo/foo
747 747 foo
748 748 a
749 749 $ hg shelve --interactive --config ui.interactive=false
750 750 abort: running non-interactively
751 751 [255]
752 752 $ hg shelve --interactive << EOF
753 753 > y
754 754 > y
755 755 > n
756 756 > EOF
757 757 diff --git a/a/a b/a/a
758 758 2 hunks, 2 lines changed
759 759 examine changes to 'a/a'?
760 760 (enter ? for help) [Ynesfdaq?] y
761 761
762 762 @@ -1,3 +1,4 @@
763 763 +a
764 764 a
765 765 c
766 766 x
767 767 record change 1/2 to 'a/a'?
768 768 (enter ? for help) [Ynesfdaq?] y
769 769
770 770 @@ -1,3 +2,4 @@
771 771 a
772 772 c
773 773 x
774 774 +x
775 775 record change 2/2 to 'a/a'?
776 776 (enter ? for help) [Ynesfdaq?] n
777 777
778 778 shelved as test
779 779 merging a/a
780 780 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
781 781 $ cat a/a
782 782 a
783 783 c
784 784 x
785 785 x
786 786 $ cat foo/foo
787 787 foo
788 788 a
789 789 $ hg st
790 790 M a/a
791 791 ? foo/foo
792 792 $ hg bookmark
793 793 \* test (4|13):33f7f61e6c5e (re)
794 794 $ hg unshelve
795 795 unshelving change 'test'
796 796 temporarily committing pending changes (restore with 'hg unshelve --abort')
797 797 rebasing shelved changes
798 798 merging a/a
799 799 $ hg bookmark
800 800 \* test (4|13):33f7f61e6c5e (re)
801 801 $ cat a/a
802 802 a
803 803 a
804 804 c
805 805 x
806 806 x
807 807
808 808 shelve --patch and shelve --stat should work with valid shelfnames
809 809
810 810 $ hg up --clean .
811 811 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
812 812 (leaving bookmark test)
813 813 $ hg shelve --list
814 814 $ echo 'patch a' > shelf-patch-a
815 815 $ hg add shelf-patch-a
816 816 $ hg shelve
817 817 shelved as default
818 818 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
819 819 $ echo 'patch b' > shelf-patch-b
820 820 $ hg add shelf-patch-b
821 821 $ hg shelve
822 822 shelved as default-01
823 823 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
824 824 $ hg shelve --patch default default-01
825 825 default-01 (*)* changes to: create conflict (glob)
826 826
827 827 diff --git a/shelf-patch-b b/shelf-patch-b
828 828 new file mode 100644
829 829 --- /dev/null
830 830 +++ b/shelf-patch-b
831 831 @@ -0,0 +1,1 @@
832 832 +patch b
833 833 default (*)* changes to: create conflict (glob)
834 834
835 835 diff --git a/shelf-patch-a b/shelf-patch-a
836 836 new file mode 100644
837 837 --- /dev/null
838 838 +++ b/shelf-patch-a
839 839 @@ -0,0 +1,1 @@
840 840 +patch a
841 841 $ hg shelve --stat default default-01
842 842 default-01 (*)* changes to: create conflict (glob)
843 843 shelf-patch-b | 1 +
844 844 1 files changed, 1 insertions(+), 0 deletions(-)
845 845 default (*)* changes to: create conflict (glob)
846 846 shelf-patch-a | 1 +
847 847 1 files changed, 1 insertions(+), 0 deletions(-)
848 848 $ hg shelve --patch default
849 849 default (*)* changes to: create conflict (glob)
850 850
851 851 diff --git a/shelf-patch-a b/shelf-patch-a
852 852 new file mode 100644
853 853 --- /dev/null
854 854 +++ b/shelf-patch-a
855 855 @@ -0,0 +1,1 @@
856 856 +patch a
857 857 $ hg shelve --stat default
858 858 default (*)* changes to: create conflict (glob)
859 859 shelf-patch-a | 1 +
860 860 1 files changed, 1 insertions(+), 0 deletions(-)
861 861 $ hg shelve --patch nonexistentshelf
862 862 abort: cannot find shelf nonexistentshelf
863 863 [255]
864 864 $ hg shelve --stat nonexistentshelf
865 865 abort: cannot find shelf nonexistentshelf
866 866 [255]
867 867 $ hg shelve --patch default nonexistentshelf
868 868 abort: cannot find shelf nonexistentshelf
869 869 [255]
870 870
871 871 when the user asks for a patch, we assume they want the most recent shelve if
872 872 they don't provide a shelve name
873 873
874 874 $ hg shelve --patch
875 875 default-01 (*)* changes to: create conflict (glob)
876 876
877 877 diff --git a/shelf-patch-b b/shelf-patch-b
878 878 new file mode 100644
879 879 --- /dev/null
880 880 +++ b/shelf-patch-b
881 881 @@ -0,0 +1,1 @@
882 882 +patch b
883 883
884 884 $ cd ..
885 885
886 886 Shelve from general delta repo uses bundle2 on disk
887 887 --------------------------------------------------
888 888
889 889 no general delta
890 890
891 891 $ hg clone --pull repo bundle1 --config format.usegeneraldelta=0
892 892 requesting all changes
893 893 adding changesets
894 894 adding manifests
895 895 adding file changes
896 896 added 5 changesets with 8 changes to 6 files
897 897 new changesets cc01e2b0c59f:33f7f61e6c5e
898 898 updating to branch default
899 899 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
900 900 $ cd bundle1
901 901 $ echo babar > jungle
902 902 $ hg add jungle
903 903 $ hg shelve
904 904 shelved as default
905 905 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
906 906 $ hg debugbundle .hg/shelved/*.hg
907 907 330882a04d2ce8487636b1fb292e5beea77fa1e3
908 908 $ cd ..
909 909
910 910 with general delta
911 911
912 912 $ hg clone --pull repo bundle2 --config format.usegeneraldelta=1
913 913 requesting all changes
914 914 adding changesets
915 915 adding manifests
916 916 adding file changes
917 917 added 5 changesets with 8 changes to 6 files
918 918 new changesets cc01e2b0c59f:33f7f61e6c5e
919 919 updating to branch default
920 920 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
921 921 $ cd bundle2
922 922 $ echo babar > jungle
923 923 $ hg add jungle
924 924 $ hg shelve
925 925 shelved as default
926 926 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
927 927 $ hg debugbundle .hg/shelved/*.hg
928 928 Stream params: {Compression: BZ}
929 929 changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
930 930 330882a04d2ce8487636b1fb292e5beea77fa1e3
931 931
932 932 Test shelve --keep
933 933
934 934 $ hg unshelve
935 935 unshelving change 'default'
936 936 $ hg shelve --keep --list
937 937 abort: options '--list' and '--keep' may not be used together
938 938 [255]
939 939 $ hg shelve --keep --patch
940 940 abort: options '--patch' and '--keep' may not be used together
941 941 [255]
942 942 $ hg shelve --keep --delete
943 943 abort: options '--delete' and '--keep' may not be used together
944 944 [255]
945 945 $ hg shelve --keep
946 946 shelved as default
947 947 $ hg diff
948 948 diff --git a/jungle b/jungle
949 949 new file mode 100644
950 950 --- /dev/null
951 951 +++ b/jungle
952 952 @@ -0,0 +1,1 @@
953 953 +babar
954 954 $ cd ..
955 955
956 956 Test visibility of in-memory changes inside transaction to external hook
957 957 ------------------------------------------------------------------------
958 958
959 959 $ cd repo
960 960
961 961 $ echo xxxx >> x
962 962 $ hg commit -m "#5: changes to invoke rebase"
963 963
964 964 $ cat > $TESTTMP/checkvisibility.sh <<EOF
965 965 > echo "==== \$1:"
966 966 > hg parents --template "VISIBLE {rev}:{node|short}\n"
967 967 > # test that pending changes are hidden
968 968 > unset HG_PENDING
969 969 > hg parents --template "ACTUAL {rev}:{node|short}\n"
970 970 > echo "===="
971 971 > EOF
972 972
973 973 $ cat >> .hg/hgrc <<EOF
974 974 > [defaults]
975 975 > # to fix hash id of temporary revisions
976 976 > unshelve = --date '0 0'
977 977 > EOF
978 978
979 979 "hg unshelve" at REV5 implies steps below:
980 980
981 981 (1) commit changes in the working directory (REV6)
982 982 (2) unbundle shelved revision (REV7)
983 983 (3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
984 984 (4) rebase: commit merged revision (REV8)
985 985 (5) rebase: update to REV6 (REV8 => REV6)
986 986 (6) update to REV5 (REV6 => REV5)
987 987 (7) abort transaction
988 988
989 989 == test visibility to external preupdate hook
990 990
991 991 $ cat >> .hg/hgrc <<EOF
992 992 > [hooks]
993 993 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
994 994 > EOF
995 995
996 996 $ echo nnnn >> n
997 997
998 998 $ sh $TESTTMP/checkvisibility.sh before-unshelving
999 999 ==== before-unshelving:
1000 1000 VISIBLE (5|19):703117a2acfb (re)
1001 1001 ACTUAL (5|19):703117a2acfb (re)
1002 1002 ====
1003 1003
1004 1004 $ hg unshelve --keep default
1005 1005 temporarily committing pending changes (restore with 'hg unshelve --abort')
1006 1006 rebasing shelved changes
1007 1007 ==== preupdate:
1008 1008 VISIBLE (6|20):54c00d20fb3f (re)
1009 1009 ACTUAL (5|19):703117a2acfb (re)
1010 1010 ====
1011 1011 ==== preupdate:
1012 1012 VISIBLE (8|21):8efe6f7537dc (re)
1013 1013 ACTUAL (5|19):703117a2acfb (re)
1014 1014 ====
1015 1015 ==== preupdate:
1016 1016 VISIBLE (6|20):54c00d20fb3f (re)
1017 1017 ACTUAL (5|19):703117a2acfb (re)
1018 1018 ====
1019 1019
1020 1020 $ cat >> .hg/hgrc <<EOF
1021 1021 > [hooks]
1022 1022 > preupdate.visibility =
1023 1023 > EOF
1024 1024
1025 1025 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1026 1026 ==== after-unshelving:
1027 1027 VISIBLE (5|19):703117a2acfb (re)
1028 1028 ACTUAL (5|19):703117a2acfb (re)
1029 1029 ====
1030 1030
1031 1031 == test visibility to external update hook
1032 1032
1033 1033 $ hg update -q -C 703117a2acfb
1034 1034
1035 1035 $ cat >> .hg/hgrc <<EOF
1036 1036 > [hooks]
1037 1037 > update.visibility = sh $TESTTMP/checkvisibility.sh update
1038 1038 > EOF
1039 1039
1040 1040 $ echo nnnn >> n
1041 1041
1042 1042 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1043 1043 ==== before-unshelving:
1044 1044 VISIBLE (5|19):703117a2acfb (re)
1045 1045 ACTUAL (5|19):703117a2acfb (re)
1046 1046 ====
1047 1047
1048 1048 $ hg unshelve --keep default
1049 1049 temporarily committing pending changes (restore with 'hg unshelve --abort')
1050 1050 rebasing shelved changes
1051 1051 ==== update:
1052 1052 VISIBLE (6|20):54c00d20fb3f (re)
1053 1053 VISIBLE 1?7:492ed9d705e5 (re)
1054 1054 ACTUAL (5|19):703117a2acfb (re)
1055 1055 ====
1056 1056 ==== update:
1057 1057 VISIBLE (6|20):54c00d20fb3f (re)
1058 1058 ACTUAL (5|19):703117a2acfb (re)
1059 1059 ====
1060 1060 ==== update:
1061 1061 VISIBLE (5|19):703117a2acfb (re)
1062 1062 ACTUAL (5|19):703117a2acfb (re)
1063 1063 ====
1064 1064
1065 1065 $ cat >> .hg/hgrc <<EOF
1066 1066 > [hooks]
1067 1067 > update.visibility =
1068 1068 > EOF
1069 1069
1070 1070 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1071 1071 ==== after-unshelving:
1072 1072 VISIBLE (5|19):703117a2acfb (re)
1073 1073 ACTUAL (5|19):703117a2acfb (re)
1074 1074 ====
1075 1075
1076 1076 $ cd ..
1077 1077
1078 1078 Keep active bookmark while (un)shelving even on shared repo (issue4940)
1079 1079 -----------------------------------------------------------------------
1080 1080
1081 1081 $ cat <<EOF >> $HGRCPATH
1082 1082 > [extensions]
1083 1083 > share =
1084 1084 > EOF
1085 1085
1086 1086 $ hg bookmarks -R repo
1087 1087 test (4|13):33f7f61e6c5e (re)
1088 1088 $ hg share -B repo share
1089 1089 updating working directory
1090 1090 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1091 1091 $ cd share
1092 1092
1093 1093 $ hg bookmarks
1094 1094 test (4|13):33f7f61e6c5e (re)
1095 1095 $ hg bookmarks foo
1096 1096 $ hg bookmarks
1097 1097 \* foo (5|19):703117a2acfb (re)
1098 1098 test (4|13):33f7f61e6c5e (re)
1099 1099 $ echo x >> x
1100 1100 $ hg shelve
1101 1101 shelved as foo
1102 1102 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1103 1103 $ hg bookmarks
1104 1104 \* foo (5|19):703117a2acfb (re)
1105 1105 test (4|13):33f7f61e6c5e (re)
1106 1106
1107 1107 $ hg unshelve
1108 1108 unshelving change 'foo'
1109 1109 $ hg bookmarks
1110 1110 \* foo (5|19):703117a2acfb (re)
1111 1111 test (4|13):33f7f61e6c5e (re)
1112 1112
1113 1113 $ cd ..
1114 1114
1115 1115 Abort unshelve while merging (issue5123)
1116 1116 ----------------------------------------
1117 1117
1118 1118 $ hg init issue5123
1119 1119 $ cd issue5123
1120 1120 $ echo > a
1121 1121 $ hg ci -Am a
1122 1122 adding a
1123 1123 $ hg co null
1124 1124 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1125 1125 $ echo > b
1126 1126 $ hg ci -Am b
1127 1127 adding b
1128 1128 created new head
1129 1129 $ echo > c
1130 1130 $ hg add c
1131 1131 $ hg shelve
1132 1132 shelved as default
1133 1133 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1134 1134 $ hg co 1
1135 1135 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1136 1136 $ hg merge 0
1137 1137 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1138 1138 (branch merge, don't forget to commit)
1139 1139 -- successful merge with two parents
1140 1140 $ hg log -G
1141 1141 @ changeset: 1:406bf70c274f
1142 1142 tag: tip
1143 1143 parent: -1:000000000000
1144 1144 user: test
1145 1145 date: Thu Jan 01 00:00:00 1970 +0000
1146 1146 summary: b
1147 1147
1148 1148 @ changeset: 0:ada8c9eb8252
1149 1149 user: test
1150 1150 date: Thu Jan 01 00:00:00 1970 +0000
1151 1151 summary: a
1152 1152
1153 1153 -- trying to pull in the shelve bits
1154 1154 -- unshelve should abort otherwise, it'll eat my second parent.
1155 1155 $ hg unshelve
1156 1156 abort: outstanding uncommitted merge
1157 1157 (use 'hg commit' or 'hg merge --abort')
1158 1158 [255]
1159 1159
1160 1160 $ cd ..
1161 1161
1162 1162 -- test for interactive mode on unshelve
1163 1163
1164 1164 $ hg init a
1165 1165 $ cd a
1166 1166 $ echo > b
1167 1167 $ hg ci -Am b
1168 1168 adding b
1169 1169 $ echo > c
1170 1170 $ echo > d
1171 1171 $ hg add .
1172 1172 adding c
1173 1173 adding d
1174 1174 $ hg shelve
1175 1175 shelved as default
1176 1176 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1177 1177 $ echo > e
1178 1178 $ hg add e
1179 1179 $ hg ci -m e
1180 1180 $ hg shelve --patch
1181 1181 default (*s ago) changes to: b (glob)
1182 1182
1183 1183 diff --git a/c b/c
1184 1184 new file mode 100644
1185 1185 --- /dev/null
1186 1186 +++ b/c
1187 1187 @@ -0,0 +1,1 @@
1188 1188 +
1189 1189 diff --git a/d b/d
1190 1190 new file mode 100644
1191 1191 --- /dev/null
1192 1192 +++ b/d
1193 1193 @@ -0,0 +1,1 @@
1194 1194 +
1195 1195 $ hg unshelve -i <<EOF
1196 1196 > y
1197 1197 > y
1198 1198 > y
1199 1199 > n
1200 1200 > EOF
1201 1201 unshelving change 'default'
1202 1202 rebasing shelved changes
1203 1203 diff --git a/c b/c
1204 1204 new file mode 100644
1205 1205 examine changes to 'c'?
1206 1206 (enter ? for help) [Ynesfdaq?] y
1207 1207
1208 1208 @@ -0,0 +1,1 @@
1209 1209 +
1210 1210 record change 1/2 to 'c'?
1211 1211 (enter ? for help) [Ynesfdaq?] y
1212 1212
1213 1213 diff --git a/d b/d
1214 1214 new file mode 100644
1215 1215 examine changes to 'd'?
1216 1216 (enter ? for help) [Ynesfdaq?] y
1217 1217
1218 1218 @@ -0,0 +1,1 @@
1219 1219 +
1220 1220 record change 2/2 to 'd'?
1221 1221 (enter ? for help) [Ynesfdaq?] n
1222 1222
1223 1223 $ ls
1224 1224 b
1225 1225 c
1226 1226 e
1227 1227 -- shelve should not contain `c` now
1228 1228 $ hg shelve --patch
1229 1229 default (*s ago) changes to: b (glob)
1230 1230
1231 1231 diff --git a/d b/d
1232 1232 new file mode 100644
1233 1233 --- /dev/null
1234 1234 +++ b/d
1235 1235 @@ -0,0 +1,1 @@
1236 1236 +
1237 1237 $ hg unshelve -i <<EOF
1238 1238 > y
1239 1239 > y
1240 1240 > EOF
1241 1241 unshelving change 'default'
1242 1242 rebasing shelved changes
1243 1243 diff --git a/d b/d
1244 1244 new file mode 100644
1245 1245 examine changes to 'd'?
1246 1246 (enter ? for help) [Ynesfdaq?] y
1247 1247
1248 1248 @@ -0,0 +1,1 @@
1249 1249 +
1250 1250 record this change to 'd'?
1251 1251 (enter ? for help) [Ynesfdaq?] y
1252 1252
1253 1253 $ ls
1254 1254 b
1255 1255 c
1256 1256 d
1257 1257 e
1258 1258 $ hg shelve --list
1259 1259
1260 1260 -- now, unshelve selected changes from a file
1261 1261
1262 1262 $ echo B > foo
1263 1263 $ hg add foo
1264 1264 $ hg ci -m 'add B to foo'
1265 1265 $ cat > foo <<EOF
1266 1266 > A
1267 1267 > B
1268 1268 > C
1269 1269 > EOF
1270 1270 $ hg shelve
1271 1271 shelved as default
1272 1272 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1273 1273 $ cat foo
1274 1274 B
1275 1275 $ hg unshelve -i <<EOF
1276 1276 > y
1277 1277 > y
1278 1278 > n
1279 1279 > EOF
1280 1280 unshelving change 'default'
1281 1281 rebasing shelved changes
1282 1282 diff --git a/foo b/foo
1283 1283 2 hunks, 2 lines changed
1284 1284 examine changes to 'foo'?
1285 1285 (enter ? for help) [Ynesfdaq?] y
1286 1286
1287 1287 @@ -1,1 +1,2 @@
1288 1288 +A
1289 1289 B
1290 1290 record change 1/2 to 'foo'?
1291 1291 (enter ? for help) [Ynesfdaq?] y
1292 1292
1293 1293 @@ -1,1 +2,2 @@
1294 1294 B
1295 1295 +C
1296 1296 record change 2/2 to 'foo'?
1297 1297 (enter ? for help) [Ynesfdaq?] n
1298 1298
1299 1299 $ cat foo
1300 1300 A
1301 1301 B
1302 1302 $ hg shelve --patch
1303 1303 default (*s ago) changes to: add B to foo (glob)
1304 1304
1305 1305 diff --git a/foo b/foo
1306 1306 --- a/foo
1307 1307 +++ b/foo
1308 1308 @@ -1,2 +1,3 @@
1309 1309 A
1310 1310 B
1311 1311 +C
1312 1312
1313 1313 -- unshelve interactive on conflicts
1314 1314
1315 1315 $ echo A >> bar1
1316 1316 $ echo A >> bar2
1317 1317 $ hg add bar1 bar2
1318 1318 $ hg ci -m 'add A to bars'
1319 1319 $ echo B >> bar1
1320 1320 $ echo B >> bar2
1321 1321 $ hg shelve
1322 1322 shelved as default-01
1323 1323 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1324 1324 $ echo C >> bar1
1325 1325 $ echo C >> bar2
1326 1326 $ hg ci -m 'add C to bars'
1327 1327 $ hg unshelve -i
1328 1328 unshelving change 'default-01'
1329 1329 rebasing shelved changes
1330 1330 merging bar1
1331 1331 merging bar2
1332 1332 warning: conflicts while merging bar1! (edit, then use 'hg resolve --mark')
1333 1333 warning: conflicts while merging bar2! (edit, then use 'hg resolve --mark')
1334 1334 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1335 1335 [1]
1336 1336
1337 1337 $ cat > bar1 <<EOF
1338 1338 > A
1339 1339 > B
1340 1340 > C
1341 1341 > EOF
1342 1342 $ cat > bar2 <<EOF
1343 1343 > A
1344 1344 > B
1345 1345 > C
1346 1346 > EOF
1347 1347 $ hg resolve -m bar1 bar2
1348 1348 (no more unresolved files)
1349 1349 continue: hg unshelve --continue
1350 1350 $ cat bar1
1351 1351 A
1352 1352 B
1353 1353 C
1354 $ hg unshelve --continue -i <<EOF
1354 $ hg unshelve --continue <<EOF
1355 1355 > y
1356 1356 > y
1357 1357 > y
1358 1358 > y
1359 1359 > EOF
1360 unshelving change 'default-01'
1361 1360 diff --git a/bar1 b/bar1
1362 1361 1 hunks, 1 lines changed
1363 1362 examine changes to 'bar1'?
1364 1363 (enter ? for help) [Ynesfdaq?] y
1365 1364
1366 1365 @@ -1,2 +1,3 @@
1367 1366 A
1368 1367 +B
1369 1368 C
1370 1369 record change 1/2 to 'bar1'?
1371 1370 (enter ? for help) [Ynesfdaq?] y
1372 1371
1373 1372 diff --git a/bar2 b/bar2
1374 1373 1 hunks, 1 lines changed
1375 1374 examine changes to 'bar2'?
1376 1375 (enter ? for help) [Ynesfdaq?] y
1377 1376
1378 1377 @@ -1,2 +1,3 @@
1379 1378 A
1380 1379 +B
1381 1380 C
1382 1381 record change 2/2 to 'bar2'?
1383 1382 (enter ? for help) [Ynesfdaq?] y
1384 1383
1385 1384 unshelve of 'default-01' complete
General Comments 0
You need to be logged in to leave comments. Login now