##// END OF EJS Templates
shelve: do not update when keeping changes, just move the dirstate...
Jordi Gutiérrez Hermoso -
r42180:50d5e64e default
parent child Browse files
Show More
@@ -1,1150 +1,1155 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 # the entire repository pointlessly
502 # the entire repository pointlessly, and as an optimisation
503 # for movedirstate, if needed.
503 504 match = scmutil.matchfiles(repo, repo[node].files())
504 505 _shelvecreatedcommit(repo, node, name, match)
505 506
506 507 if ui.formatted():
507 508 desc = stringutil.ellipsis(desc, ui.termwidth())
508 509 ui.status(_('shelved as %s\n') % name)
509 hg.update(repo, parent.node())
510 if opts['keep']:
511 with repo.dirstate.parentchange():
512 scmutil.movedirstate(repo, parent, match)
513 else:
514 hg.update(repo, parent.node())
510 515 if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
511 516 repo.dirstate.setbranch(origbranch)
512 517
513 518 _finishshelve(repo, tr)
514 519 finally:
515 520 _restoreactivebookmark(repo, activebookmark)
516 521 lockmod.release(tr, lock)
517 522
518 523 def _isbareshelve(pats, opts):
519 524 return (not pats
520 525 and not opts.get('interactive', False)
521 526 and not opts.get('include', False)
522 527 and not opts.get('exclude', False))
523 528
524 529 def _iswctxonnewbranch(repo):
525 530 return repo[None].branch() != repo['.'].branch()
526 531
527 532 def cleanupcmd(ui, repo):
528 533 """subcommand that deletes all shelves"""
529 534
530 535 with repo.wlock():
531 536 for (name, _type) in repo.vfs.readdir(shelvedir):
532 537 suffix = name.rsplit('.', 1)[-1]
533 538 if suffix in shelvefileextensions:
534 539 shelvedfile(repo, name).movetobackup()
535 540 cleanupoldbackups(repo)
536 541
537 542 def deletecmd(ui, repo, pats):
538 543 """subcommand that deletes a specific shelve"""
539 544 if not pats:
540 545 raise error.Abort(_('no shelved changes specified!'))
541 546 with repo.wlock():
542 547 try:
543 548 for name in pats:
544 549 for suffix in shelvefileextensions:
545 550 shfile = shelvedfile(repo, name, suffix)
546 551 # patch file is necessary, as it should
547 552 # be present for any kind of shelve,
548 553 # but the .hg file is optional as in future we
549 554 # will add obsolete shelve with does not create a
550 555 # bundle
551 556 if shfile.exists() or suffix == patchextension:
552 557 shfile.movetobackup()
553 558 cleanupoldbackups(repo)
554 559 except OSError as err:
555 560 if err.errno != errno.ENOENT:
556 561 raise
557 562 raise error.Abort(_("shelved change '%s' not found") % name)
558 563
559 564 def listshelves(repo):
560 565 """return all shelves in repo as list of (time, filename)"""
561 566 try:
562 567 names = repo.vfs.readdir(shelvedir)
563 568 except OSError as err:
564 569 if err.errno != errno.ENOENT:
565 570 raise
566 571 return []
567 572 info = []
568 573 for (name, _type) in names:
569 574 pfx, sfx = name.rsplit('.', 1)
570 575 if not pfx or sfx != patchextension:
571 576 continue
572 577 st = shelvedfile(repo, name).stat()
573 578 info.append((st[stat.ST_MTIME], shelvedfile(repo, pfx).filename()))
574 579 return sorted(info, reverse=True)
575 580
576 581 def listcmd(ui, repo, pats, opts):
577 582 """subcommand that displays the list of shelves"""
578 583 pats = set(pats)
579 584 width = 80
580 585 if not ui.plain():
581 586 width = ui.termwidth()
582 587 namelabel = 'shelve.newest'
583 588 ui.pager('shelve')
584 589 for mtime, name in listshelves(repo):
585 590 sname = util.split(name)[1]
586 591 if pats and sname not in pats:
587 592 continue
588 593 ui.write(sname, label=namelabel)
589 594 namelabel = 'shelve.name'
590 595 if ui.quiet:
591 596 ui.write('\n')
592 597 continue
593 598 ui.write(' ' * (16 - len(sname)))
594 599 used = 16
595 600 date = dateutil.makedate(mtime)
596 601 age = '(%s)' % templatefilters.age(date, abbrev=True)
597 602 ui.write(age, label='shelve.age')
598 603 ui.write(' ' * (12 - len(age)))
599 604 used += 12
600 605 with open(name + '.' + patchextension, 'rb') as fp:
601 606 while True:
602 607 line = fp.readline()
603 608 if not line:
604 609 break
605 610 if not line.startswith('#'):
606 611 desc = line.rstrip()
607 612 if ui.formatted():
608 613 desc = stringutil.ellipsis(desc, width - used)
609 614 ui.write(desc)
610 615 break
611 616 ui.write('\n')
612 617 if not (opts['patch'] or opts['stat']):
613 618 continue
614 619 difflines = fp.readlines()
615 620 if opts['patch']:
616 621 for chunk, label in patch.difflabel(iter, difflines):
617 622 ui.write(chunk, label=label)
618 623 if opts['stat']:
619 624 for chunk, label in patch.diffstatui(difflines, width=width):
620 625 ui.write(chunk, label=label)
621 626
622 627 def patchcmds(ui, repo, pats, opts):
623 628 """subcommand that displays shelves"""
624 629 if len(pats) == 0:
625 630 shelves = listshelves(repo)
626 631 if not shelves:
627 632 raise error.Abort(_("there are no shelves to show"))
628 633 mtime, name = shelves[0]
629 634 sname = util.split(name)[1]
630 635 pats = [sname]
631 636
632 637 for shelfname in pats:
633 638 if not shelvedfile(repo, shelfname, patchextension).exists():
634 639 raise error.Abort(_("cannot find shelf %s") % shelfname)
635 640
636 641 listcmd(ui, repo, pats, opts)
637 642
638 643 def checkparents(repo, state):
639 644 """check parent while resuming an unshelve"""
640 645 if state.parents != repo.dirstate.parents():
641 646 raise error.Abort(_('working directory parents do not match unshelve '
642 647 'state'))
643 648
644 649 def pathtofiles(repo, files):
645 650 cwd = repo.getcwd()
646 651 return [repo.pathto(f, cwd) for f in files]
647 652
648 653 def unshelveabort(ui, repo, state, opts):
649 654 """subcommand that abort an in-progress unshelve"""
650 655 with repo.lock():
651 656 try:
652 657 checkparents(repo, state)
653 658
654 659 merge.update(repo, state.pendingctx, branchmerge=False, force=True)
655 660 if (state.activebookmark
656 661 and state.activebookmark in repo._bookmarks):
657 662 bookmarks.activate(repo, state.activebookmark)
658 663
659 664 if repo.vfs.exists('unshelverebasestate'):
660 665 repo.vfs.rename('unshelverebasestate', 'rebasestate')
661 666 rebase.clearstatus(repo)
662 667
663 668 mergefiles(ui, repo, state.wctx, state.pendingctx)
664 669 if not phases.supportinternal(repo):
665 670 repair.strip(ui, repo, state.nodestoremove, backup=False,
666 671 topic='shelve')
667 672 finally:
668 673 shelvedstate.clear(repo)
669 674 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
670 675
671 676 def mergefiles(ui, repo, wctx, shelvectx):
672 677 """updates to wctx and merges the changes from shelvectx into the
673 678 dirstate."""
674 679 with ui.configoverride({('ui', 'quiet'): True}):
675 680 hg.update(repo, wctx.node())
676 681 files = []
677 682 files.extend(shelvectx.files())
678 683 files.extend(shelvectx.p1().files())
679 684
680 685 # revert will overwrite unknown files, so move them out of the way
681 686 for file in repo.status(unknown=True).unknown:
682 687 if file in files:
683 688 util.rename(repo.wjoin(file),
684 689 scmutil.backuppath(ui, repo, file))
685 690 ui.pushbuffer(True)
686 691 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
687 692 *pathtofiles(repo, files),
688 693 **{r'no_backup': True})
689 694 ui.popbuffer()
690 695
691 696 def restorebranch(ui, repo, branchtorestore):
692 697 if branchtorestore and branchtorestore != repo.dirstate.branch():
693 698 repo.dirstate.setbranch(branchtorestore)
694 699 ui.status(_('marked working directory as branch %s\n')
695 700 % branchtorestore)
696 701
697 702 def unshelvecleanup(ui, repo, name, opts):
698 703 """remove related files after an unshelve"""
699 704 if not opts.get('keep'):
700 705 for filetype in shelvefileextensions:
701 706 shfile = shelvedfile(repo, name, filetype)
702 707 if shfile.exists():
703 708 shfile.movetobackup()
704 709 cleanupoldbackups(repo)
705 710
706 711 def unshelvecontinue(ui, repo, state, opts):
707 712 """subcommand to continue an in-progress unshelve"""
708 713 # We're finishing off a merge. First parent is our original
709 714 # parent, second is the temporary "fake" commit we're unshelving.
710 715 with repo.lock():
711 716 checkparents(repo, state)
712 717 ms = merge.mergestate.read(repo)
713 718 if list(ms.unresolved()):
714 719 raise error.Abort(
715 720 _("unresolved conflicts, can't continue"),
716 721 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
717 722
718 723 shelvectx = repo[state.parents[1]]
719 724 pendingctx = state.pendingctx
720 725
721 726 with repo.dirstate.parentchange():
722 727 repo.setparents(state.pendingctx.node(), nodemod.nullid)
723 728 repo.dirstate.write(repo.currenttransaction())
724 729
725 730 targetphase = phases.internal
726 731 if not phases.supportinternal(repo):
727 732 targetphase = phases.secret
728 733 overrides = {('phases', 'new-commit'): targetphase}
729 734 with repo.ui.configoverride(overrides, 'unshelve'):
730 735 with repo.dirstate.parentchange():
731 736 repo.setparents(state.parents[0], nodemod.nullid)
732 737 newnode = repo.commit(text=shelvectx.description(),
733 738 extra=shelvectx.extra(),
734 739 user=shelvectx.user(),
735 740 date=shelvectx.date())
736 741
737 742 if newnode is None:
738 743 # If it ended up being a no-op commit, then the normal
739 744 # merge state clean-up path doesn't happen, so do it
740 745 # here. Fix issue5494
741 746 merge.mergestate.clean(repo)
742 747 shelvectx = state.pendingctx
743 748 msg = _('note: unshelved changes already existed '
744 749 'in the working copy\n')
745 750 ui.status(msg)
746 751 else:
747 752 # only strip the shelvectx if we produced one
748 753 state.nodestoremove.append(newnode)
749 754 shelvectx = repo[newnode]
750 755
751 756 hg.updaterepo(repo, pendingctx.node(), overwrite=False)
752 757
753 758 if repo.vfs.exists('unshelverebasestate'):
754 759 repo.vfs.rename('unshelverebasestate', 'rebasestate')
755 760 rebase.clearstatus(repo)
756 761
757 762 mergefiles(ui, repo, state.wctx, shelvectx)
758 763 restorebranch(ui, repo, state.branchtorestore)
759 764
760 765 if not phases.supportinternal(repo):
761 766 repair.strip(ui, repo, state.nodestoremove, backup=False,
762 767 topic='shelve')
763 768 _restoreactivebookmark(repo, state.activebookmark)
764 769 shelvedstate.clear(repo)
765 770 unshelvecleanup(ui, repo, state.name, opts)
766 771 ui.status(_("unshelve of '%s' complete\n") % state.name)
767 772
768 773 def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
769 774 """Temporarily commit working copy changes before moving unshelve commit"""
770 775 # Store pending changes in a commit and remember added in case a shelve
771 776 # contains unknown files that are part of the pending change
772 777 s = repo.status()
773 778 addedbefore = frozenset(s.added)
774 779 if not (s.modified or s.added or s.removed):
775 780 return tmpwctx, addedbefore
776 781 ui.status(_("temporarily committing pending changes "
777 782 "(restore with 'hg unshelve --abort')\n"))
778 783 extra = {'internal': 'shelve'}
779 784 commitfunc = getcommitfunc(extra=extra, interactive=False,
780 785 editor=False)
781 786 tempopts = {}
782 787 tempopts['message'] = "pending changes temporary commit"
783 788 tempopts['date'] = opts.get('date')
784 789 with ui.configoverride({('ui', 'quiet'): True}):
785 790 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
786 791 tmpwctx = repo[node]
787 792 return tmpwctx, addedbefore
788 793
789 794 def _unshelverestorecommit(ui, repo, tr, basename):
790 795 """Recreate commit in the repository during the unshelve"""
791 796 repo = repo.unfiltered()
792 797 node = None
793 798 if shelvedfile(repo, basename, 'shelve').exists():
794 799 node = shelvedfile(repo, basename, 'shelve').readinfo()['node']
795 800 if node is None or node not in repo:
796 801 with ui.configoverride({('ui', 'quiet'): True}):
797 802 shelvectx = shelvedfile(repo, basename, 'hg').applybundle(tr)
798 803 # We might not strip the unbundled changeset, so we should keep track of
799 804 # the unshelve node in case we need to reuse it (eg: unshelve --keep)
800 805 if node is None:
801 806 info = {'node': nodemod.hex(shelvectx.node())}
802 807 shelvedfile(repo, basename, 'shelve').writeinfo(info)
803 808 else:
804 809 shelvectx = repo[node]
805 810
806 811 return repo, shelvectx
807 812
808 813 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
809 814 tmpwctx, shelvectx, branchtorestore,
810 815 activebookmark):
811 816 """Rebase restored commit from its original location to a destination"""
812 817 # If the shelve is not immediately on top of the commit
813 818 # we'll be merging with, rebase it to be on top.
814 819 if tmpwctx.node() == shelvectx.p1().node():
815 820 return shelvectx
816 821
817 822 overrides = {
818 823 ('ui', 'forcemerge'): opts.get('tool', ''),
819 824 ('phases', 'new-commit'): phases.secret,
820 825 }
821 826 with repo.ui.configoverride(overrides, 'unshelve'):
822 827 ui.status(_('rebasing shelved changes\n'))
823 828 stats = merge.graft(repo, shelvectx, shelvectx.p1(),
824 829 labels=['shelve', 'working-copy'],
825 830 keepconflictparent=True)
826 831 if stats.unresolvedcount:
827 832 tr.close()
828 833
829 834 nodestoremove = [repo.changelog.node(rev)
830 835 for rev in pycompat.xrange(oldtiprev, len(repo))]
831 836 shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoremove,
832 837 branchtorestore, opts.get('keep'), activebookmark)
833 838 raise error.InterventionRequired(
834 839 _("unresolved conflicts (see 'hg resolve', then "
835 840 "'hg unshelve --continue')"))
836 841
837 842 with repo.dirstate.parentchange():
838 843 repo.setparents(tmpwctx.node(), nodemod.nullid)
839 844 newnode = repo.commit(text=shelvectx.description(),
840 845 extra=shelvectx.extra(),
841 846 user=shelvectx.user(),
842 847 date=shelvectx.date())
843 848
844 849 if newnode is None:
845 850 # If it ended up being a no-op commit, then the normal
846 851 # merge state clean-up path doesn't happen, so do it
847 852 # here. Fix issue5494
848 853 merge.mergestate.clean(repo)
849 854 shelvectx = tmpwctx
850 855 msg = _('note: unshelved changes already existed '
851 856 'in the working copy\n')
852 857 ui.status(msg)
853 858 else:
854 859 shelvectx = repo[newnode]
855 860 hg.updaterepo(repo, tmpwctx.node(), False)
856 861
857 862 return shelvectx
858 863
859 864 def _forgetunknownfiles(repo, shelvectx, addedbefore):
860 865 # Forget any files that were unknown before the shelve, unknown before
861 866 # unshelve started, but are now added.
862 867 shelveunknown = shelvectx.extra().get('shelve_unknown')
863 868 if not shelveunknown:
864 869 return
865 870 shelveunknown = frozenset(shelveunknown.split('\0'))
866 871 addedafter = frozenset(repo.status().added)
867 872 toforget = (addedafter & shelveunknown) - addedbefore
868 873 repo[None].forget(toforget)
869 874
870 875 def _finishunshelve(repo, oldtiprev, tr, activebookmark):
871 876 _restoreactivebookmark(repo, activebookmark)
872 877 # The transaction aborting will strip all the commits for us,
873 878 # but it doesn't update the inmemory structures, so addchangegroup
874 879 # hooks still fire and try to operate on the missing commits.
875 880 # Clean up manually to prevent this.
876 881 repo.unfiltered().changelog.strip(oldtiprev, tr)
877 882 _aborttransaction(repo, tr)
878 883
879 884 def _checkunshelveuntrackedproblems(ui, repo, shelvectx):
880 885 """Check potential problems which may result from working
881 886 copy having untracked changes."""
882 887 wcdeleted = set(repo.status().deleted)
883 888 shelvetouched = set(shelvectx.files())
884 889 intersection = wcdeleted.intersection(shelvetouched)
885 890 if intersection:
886 891 m = _("shelved change touches missing files")
887 892 hint = _("run hg status to see which files are missing")
888 893 raise error.Abort(m, hint=hint)
889 894
890 895 @command('unshelve',
891 896 [('a', 'abort', None,
892 897 _('abort an incomplete unshelve operation')),
893 898 ('c', 'continue', None,
894 899 _('continue an incomplete unshelve operation')),
895 900 ('k', 'keep', None,
896 901 _('keep shelve after unshelving')),
897 902 ('n', 'name', '',
898 903 _('restore shelved change with given name'), _('NAME')),
899 904 ('t', 'tool', '', _('specify merge tool')),
900 905 ('', 'date', '',
901 906 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
902 907 _('hg unshelve [[-n] SHELVED]'),
903 908 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
904 909 def unshelve(ui, repo, *shelved, **opts):
905 910 """restore a shelved change to the working directory
906 911
907 912 This command accepts an optional name of a shelved change to
908 913 restore. If none is given, the most recent shelved change is used.
909 914
910 915 If a shelved change is applied successfully, the bundle that
911 916 contains the shelved changes is moved to a backup location
912 917 (.hg/shelve-backup).
913 918
914 919 Since you can restore a shelved change on top of an arbitrary
915 920 commit, it is possible that unshelving will result in a conflict
916 921 between your changes and the commits you are unshelving onto. If
917 922 this occurs, you must resolve the conflict, then use
918 923 ``--continue`` to complete the unshelve operation. (The bundle
919 924 will not be moved until you successfully complete the unshelve.)
920 925
921 926 (Alternatively, you can use ``--abort`` to abandon an unshelve
922 927 that causes a conflict. This reverts the unshelved changes, and
923 928 leaves the bundle in place.)
924 929
925 930 If bare shelved change(when no files are specified, without interactive,
926 931 include and exclude option) was done on newly created branch it would
927 932 restore branch information to the working directory.
928 933
929 934 After a successful unshelve, the shelved changes are stored in a
930 935 backup directory. Only the N most recent backups are kept. N
931 936 defaults to 10 but can be overridden using the ``shelve.maxbackups``
932 937 configuration option.
933 938
934 939 .. container:: verbose
935 940
936 941 Timestamp in seconds is used to decide order of backups. More
937 942 than ``maxbackups`` backups are kept, if same timestamp
938 943 prevents from deciding exact order of them, for safety.
939 944 """
940 945 with repo.wlock():
941 946 return _dounshelve(ui, repo, *shelved, **opts)
942 947
943 948 def _dounshelve(ui, repo, *shelved, **opts):
944 949 opts = pycompat.byteskwargs(opts)
945 950 abortf = opts.get('abort')
946 951 continuef = opts.get('continue')
947 952 if not abortf and not continuef:
948 953 cmdutil.checkunfinished(repo)
949 954 shelved = list(shelved)
950 955 if opts.get("name"):
951 956 shelved.append(opts["name"])
952 957
953 958 if abortf or continuef:
954 959 if abortf and continuef:
955 960 raise error.Abort(_('cannot use both abort and continue'))
956 961 if shelved:
957 962 raise error.Abort(_('cannot combine abort/continue with '
958 963 'naming a shelved change'))
959 964 if abortf and opts.get('tool', False):
960 965 ui.warn(_('tool option will be ignored\n'))
961 966
962 967 try:
963 968 state = shelvedstate.load(repo)
964 969 if opts.get('keep') is None:
965 970 opts['keep'] = state.keep
966 971 except IOError as err:
967 972 if err.errno != errno.ENOENT:
968 973 raise
969 974 cmdutil.wrongtooltocontinue(repo, _('unshelve'))
970 975 except error.CorruptedState as err:
971 976 ui.debug(pycompat.bytestr(err) + '\n')
972 977 if continuef:
973 978 msg = _('corrupted shelved state file')
974 979 hint = _('please run hg unshelve --abort to abort unshelve '
975 980 'operation')
976 981 raise error.Abort(msg, hint=hint)
977 982 elif abortf:
978 983 msg = _('could not read shelved state file, your working copy '
979 984 'may be in an unexpected state\nplease update to some '
980 985 'commit\n')
981 986 ui.warn(msg)
982 987 shelvedstate.clear(repo)
983 988 return
984 989
985 990 if abortf:
986 991 return unshelveabort(ui, repo, state, opts)
987 992 elif continuef:
988 993 return unshelvecontinue(ui, repo, state, opts)
989 994 elif len(shelved) > 1:
990 995 raise error.Abort(_('can only unshelve one change at a time'))
991 996 elif not shelved:
992 997 shelved = listshelves(repo)
993 998 if not shelved:
994 999 raise error.Abort(_('no shelved changes to apply!'))
995 1000 basename = util.split(shelved[0][1])[1]
996 1001 ui.status(_("unshelving change '%s'\n") % basename)
997 1002 else:
998 1003 basename = shelved[0]
999 1004
1000 1005 if not shelvedfile(repo, basename, patchextension).exists():
1001 1006 raise error.Abort(_("shelved change '%s' not found") % basename)
1002 1007
1003 1008 repo = repo.unfiltered()
1004 1009 lock = tr = None
1005 1010 try:
1006 1011 lock = repo.lock()
1007 1012 tr = repo.transaction('unshelve', report=lambda x: None)
1008 1013 oldtiprev = len(repo)
1009 1014
1010 1015 pctx = repo['.']
1011 1016 tmpwctx = pctx
1012 1017 # The goal is to have a commit structure like so:
1013 1018 # ...-> pctx -> tmpwctx -> shelvectx
1014 1019 # where tmpwctx is an optional commit with the user's pending changes
1015 1020 # and shelvectx is the unshelved changes. Then we merge it all down
1016 1021 # to the original pctx.
1017 1022
1018 1023 activebookmark = _backupactivebookmark(repo)
1019 1024 tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
1020 1025 tmpwctx)
1021 1026 repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename)
1022 1027 _checkunshelveuntrackedproblems(ui, repo, shelvectx)
1023 1028 branchtorestore = ''
1024 1029 if shelvectx.branch() != shelvectx.p1().branch():
1025 1030 branchtorestore = shelvectx.branch()
1026 1031
1027 1032 shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
1028 1033 basename, pctx, tmpwctx,
1029 1034 shelvectx, branchtorestore,
1030 1035 activebookmark)
1031 1036 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
1032 1037 with ui.configoverride(overrides, 'unshelve'):
1033 1038 mergefiles(ui, repo, pctx, shelvectx)
1034 1039 restorebranch(ui, repo, branchtorestore)
1035 1040 _forgetunknownfiles(repo, shelvectx, addedbefore)
1036 1041
1037 1042 shelvedstate.clear(repo)
1038 1043 _finishunshelve(repo, oldtiprev, tr, activebookmark)
1039 1044 unshelvecleanup(ui, repo, basename, opts)
1040 1045 finally:
1041 1046 if tr:
1042 1047 tr.release()
1043 1048 lockmod.release(lock)
1044 1049
1045 1050 @command('shelve',
1046 1051 [('A', 'addremove', None,
1047 1052 _('mark new/missing files as added/removed before shelving')),
1048 1053 ('u', 'unknown', None,
1049 1054 _('store unknown files in the shelve')),
1050 1055 ('', 'cleanup', None,
1051 1056 _('delete all shelved changes')),
1052 1057 ('', 'date', '',
1053 1058 _('shelve with the specified commit date'), _('DATE')),
1054 1059 ('d', 'delete', None,
1055 1060 _('delete the named shelved change(s)')),
1056 1061 ('e', 'edit', False,
1057 1062 _('invoke editor on commit messages')),
1058 1063 ('k', 'keep', False,
1059 1064 _('shelve, but keep changes in the working directory')),
1060 1065 ('l', 'list', None,
1061 1066 _('list current shelves')),
1062 1067 ('m', 'message', '',
1063 1068 _('use text as shelve message'), _('TEXT')),
1064 1069 ('n', 'name', '',
1065 1070 _('use the given name for the shelved commit'), _('NAME')),
1066 1071 ('p', 'patch', None,
1067 1072 _('output patches for changes (provide the names of the shelved '
1068 1073 'changes as positional arguments)')),
1069 1074 ('i', 'interactive', None,
1070 1075 _('interactive mode, only works while creating a shelve')),
1071 1076 ('', 'stat', None,
1072 1077 _('output diffstat-style summary of changes (provide the names of '
1073 1078 'the shelved changes as positional arguments)')
1074 1079 )] + cmdutil.walkopts,
1075 1080 _('hg shelve [OPTION]... [FILE]...'),
1076 1081 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
1077 1082 def shelvecmd(ui, repo, *pats, **opts):
1078 1083 '''save and set aside changes from the working directory
1079 1084
1080 1085 Shelving takes files that "hg status" reports as not clean, saves
1081 1086 the modifications to a bundle (a shelved change), and reverts the
1082 1087 files so that their state in the working directory becomes clean.
1083 1088
1084 1089 To restore these changes to the working directory, using "hg
1085 1090 unshelve"; this will work even if you switch to a different
1086 1091 commit.
1087 1092
1088 1093 When no files are specified, "hg shelve" saves all not-clean
1089 1094 files. If specific files or directories are named, only changes to
1090 1095 those files are shelved.
1091 1096
1092 1097 In bare shelve (when no files are specified, without interactive,
1093 1098 include and exclude option), shelving remembers information if the
1094 1099 working directory was on newly created branch, in other words working
1095 1100 directory was on different branch than its first parent. In this
1096 1101 situation unshelving restores branch information to the working directory.
1097 1102
1098 1103 Each shelved change has a name that makes it easier to find later.
1099 1104 The name of a shelved change defaults to being based on the active
1100 1105 bookmark, or if there is no active bookmark, the current named
1101 1106 branch. To specify a different name, use ``--name``.
1102 1107
1103 1108 To see a list of existing shelved changes, use the ``--list``
1104 1109 option. For each shelved change, this will print its name, age,
1105 1110 and description; use ``--patch`` or ``--stat`` for more details.
1106 1111
1107 1112 To delete specific shelved changes, use ``--delete``. To delete
1108 1113 all shelved changes, use ``--cleanup``.
1109 1114 '''
1110 1115 opts = pycompat.byteskwargs(opts)
1111 1116 allowables = [
1112 1117 ('addremove', {'create'}), # 'create' is pseudo action
1113 1118 ('unknown', {'create'}),
1114 1119 ('cleanup', {'cleanup'}),
1115 1120 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
1116 1121 ('delete', {'delete'}),
1117 1122 ('edit', {'create'}),
1118 1123 ('list', {'list'}),
1119 1124 ('message', {'create'}),
1120 1125 ('name', {'create'}),
1121 1126 ('patch', {'patch', 'list'}),
1122 1127 ('stat', {'stat', 'list'}),
1123 1128 ]
1124 1129 def checkopt(opt):
1125 1130 if opts.get(opt):
1126 1131 for i, allowable in allowables:
1127 1132 if opts[i] and opt not in allowable:
1128 1133 raise error.Abort(_("options '--%s' and '--%s' may not be "
1129 1134 "used together") % (opt, i))
1130 1135 return True
1131 1136 if checkopt('cleanup'):
1132 1137 if pats:
1133 1138 raise error.Abort(_("cannot specify names when using '--cleanup'"))
1134 1139 return cleanupcmd(ui, repo)
1135 1140 elif checkopt('delete'):
1136 1141 return deletecmd(ui, repo, pats)
1137 1142 elif checkopt('list'):
1138 1143 return listcmd(ui, repo, pats, opts)
1139 1144 elif checkopt('patch') or checkopt('stat'):
1140 1145 return patchcmds(ui, repo, pats, opts)
1141 1146 else:
1142 1147 return createcmd(ui, repo, pats, opts)
1143 1148
1144 1149 def extsetup(ui):
1145 1150 cmdutil.unfinishedstates.append(
1146 1151 [shelvedstate._filename, False, False,
1147 1152 _('unshelve already in progress'),
1148 1153 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
1149 1154 cmdutil.afterresolvedstates.append(
1150 1155 [shelvedstate._filename, _('hg unshelve --continue')])
@@ -1,1090 +1,1104 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
932 Test shelve --keep
933
934 $ hg unshelve
935 unshelving change 'default'
936 $ hg shelve --keep
937 shelved as default
938 $ hg diff
939 diff --git a/jungle b/jungle
940 new file mode 100644
941 --- /dev/null
942 +++ b/jungle
943 @@ -0,0 +1,1 @@
944 +babar
931 945 $ cd ..
932 946
933 947 Test visibility of in-memory changes inside transaction to external hook
934 948 ------------------------------------------------------------------------
935 949
936 950 $ cd repo
937 951
938 952 $ echo xxxx >> x
939 953 $ hg commit -m "#5: changes to invoke rebase"
940 954
941 955 $ cat > $TESTTMP/checkvisibility.sh <<EOF
942 956 > echo "==== \$1:"
943 957 > hg parents --template "VISIBLE {rev}:{node|short}\n"
944 958 > # test that pending changes are hidden
945 959 > unset HG_PENDING
946 960 > hg parents --template "ACTUAL {rev}:{node|short}\n"
947 961 > echo "===="
948 962 > EOF
949 963
950 964 $ cat >> .hg/hgrc <<EOF
951 965 > [defaults]
952 966 > # to fix hash id of temporary revisions
953 967 > unshelve = --date '0 0'
954 968 > EOF
955 969
956 970 "hg unshelve" at REV5 implies steps below:
957 971
958 972 (1) commit changes in the working directory (REV6)
959 973 (2) unbundle shelved revision (REV7)
960 974 (3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
961 975 (4) rebase: commit merged revision (REV8)
962 976 (5) rebase: update to REV6 (REV8 => REV6)
963 977 (6) update to REV5 (REV6 => REV5)
964 978 (7) abort transaction
965 979
966 980 == test visibility to external preupdate hook
967 981
968 982 $ cat >> .hg/hgrc <<EOF
969 983 > [hooks]
970 984 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
971 985 > EOF
972 986
973 987 $ echo nnnn >> n
974 988
975 989 $ sh $TESTTMP/checkvisibility.sh before-unshelving
976 990 ==== before-unshelving:
977 991 VISIBLE (5|19):703117a2acfb (re)
978 992 ACTUAL (5|19):703117a2acfb (re)
979 993 ====
980 994
981 995 $ hg unshelve --keep default
982 996 temporarily committing pending changes (restore with 'hg unshelve --abort')
983 997 rebasing shelved changes
984 998 ==== preupdate:
985 999 VISIBLE (6|20):54c00d20fb3f (re)
986 1000 ACTUAL (5|19):703117a2acfb (re)
987 1001 ====
988 1002 ==== preupdate:
989 1003 VISIBLE (8|21):8efe6f7537dc (re)
990 1004 ACTUAL (5|19):703117a2acfb (re)
991 1005 ====
992 1006 ==== preupdate:
993 1007 VISIBLE (6|20):54c00d20fb3f (re)
994 1008 ACTUAL (5|19):703117a2acfb (re)
995 1009 ====
996 1010
997 1011 $ cat >> .hg/hgrc <<EOF
998 1012 > [hooks]
999 1013 > preupdate.visibility =
1000 1014 > EOF
1001 1015
1002 1016 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1003 1017 ==== after-unshelving:
1004 1018 VISIBLE (5|19):703117a2acfb (re)
1005 1019 ACTUAL (5|19):703117a2acfb (re)
1006 1020 ====
1007 1021
1008 1022 == test visibility to external update hook
1009 1023
1010 1024 $ hg update -q -C 703117a2acfb
1011 1025
1012 1026 $ cat >> .hg/hgrc <<EOF
1013 1027 > [hooks]
1014 1028 > update.visibility = sh $TESTTMP/checkvisibility.sh update
1015 1029 > EOF
1016 1030
1017 1031 $ echo nnnn >> n
1018 1032
1019 1033 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1020 1034 ==== before-unshelving:
1021 1035 VISIBLE (5|19):703117a2acfb (re)
1022 1036 ACTUAL (5|19):703117a2acfb (re)
1023 1037 ====
1024 1038
1025 1039 $ hg unshelve --keep default
1026 1040 temporarily committing pending changes (restore with 'hg unshelve --abort')
1027 1041 rebasing shelved changes
1028 1042 ==== update:
1029 1043 VISIBLE (6|20):54c00d20fb3f (re)
1030 1044 VISIBLE 1?7:492ed9d705e5 (re)
1031 1045 ACTUAL (5|19):703117a2acfb (re)
1032 1046 ====
1033 1047 ==== update:
1034 1048 VISIBLE (6|20):54c00d20fb3f (re)
1035 1049 ACTUAL (5|19):703117a2acfb (re)
1036 1050 ====
1037 1051 ==== update:
1038 1052 VISIBLE (5|19):703117a2acfb (re)
1039 1053 ACTUAL (5|19):703117a2acfb (re)
1040 1054 ====
1041 1055
1042 1056 $ cat >> .hg/hgrc <<EOF
1043 1057 > [hooks]
1044 1058 > update.visibility =
1045 1059 > EOF
1046 1060
1047 1061 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1048 1062 ==== after-unshelving:
1049 1063 VISIBLE (5|19):703117a2acfb (re)
1050 1064 ACTUAL (5|19):703117a2acfb (re)
1051 1065 ====
1052 1066
1053 1067 $ cd ..
1054 1068
1055 1069 Keep active bookmark while (un)shelving even on shared repo (issue4940)
1056 1070 -----------------------------------------------------------------------
1057 1071
1058 1072 $ cat <<EOF >> $HGRCPATH
1059 1073 > [extensions]
1060 1074 > share =
1061 1075 > EOF
1062 1076
1063 1077 $ hg bookmarks -R repo
1064 1078 test (4|13):33f7f61e6c5e (re)
1065 1079 $ hg share -B repo share
1066 1080 updating working directory
1067 1081 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1068 1082 $ cd share
1069 1083
1070 1084 $ hg bookmarks
1071 1085 test (4|13):33f7f61e6c5e (re)
1072 1086 $ hg bookmarks foo
1073 1087 $ hg bookmarks
1074 1088 \* foo (5|19):703117a2acfb (re)
1075 1089 test (4|13):33f7f61e6c5e (re)
1076 1090 $ echo x >> x
1077 1091 $ hg shelve
1078 1092 shelved as foo
1079 1093 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1080 1094 $ hg bookmarks
1081 1095 \* foo (5|19):703117a2acfb (re)
1082 1096 test (4|13):33f7f61e6c5e (re)
1083 1097
1084 1098 $ hg unshelve
1085 1099 unshelving change 'foo'
1086 1100 $ hg bookmarks
1087 1101 \* foo (5|19):703117a2acfb (re)
1088 1102 test (4|13):33f7f61e6c5e (re)
1089 1103
1090 1104 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now