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