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