##// END OF EJS Templates
shelve: use absolute_import
timeless -
r28378:96a7368a default
parent child Browse files
Show More
@@ -1,859 +1,880 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 from __future__ import absolute_import
23 24
24 25 import collections
26 import errno
25 27 import itertools
28 from mercurial import (
29 bundle2,
30 bundlerepo,
31 changegroup,
32 cmdutil,
33 commands,
34 error,
35 exchange,
36 hg,
37 lock as lockmod,
38 mdiff,
39 merge,
40 node as nodemod,
41 patch,
42 phases,
43 repair,
44 scmutil,
45 templatefilters,
46 util,
47 )
26 48 from mercurial.i18n import _
27 from mercurial.node import nullid, nullrev, bin, hex
28 from mercurial import changegroup, cmdutil, scmutil, phases, commands
29 from mercurial import error, hg, mdiff, merge, patch, repair, util
30 from mercurial import templatefilters, exchange, bundlerepo, bundle2
31 from mercurial import lock as lockmod
32 from hgext import rebase
33 import errno
49
50 from . import (
51 rebase,
52 )
34 53
35 54 cmdtable = {}
36 55 command = cmdutil.command(cmdtable)
37 56 # Note for extension authors: ONLY specify testedwith = 'internal' for
38 57 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
39 58 # be specifying the version(s) of Mercurial they are tested with, or
40 59 # leave the attribute unspecified.
41 60 testedwith = 'internal'
42 61
43 62 backupdir = 'shelve-backup'
44 63
45 64 class shelvedfile(object):
46 65 """Helper for the file storing a single shelve
47 66
48 67 Handles common functions on shelve files (.hg/.patch) using
49 68 the vfs layer"""
50 69 def __init__(self, repo, name, filetype=None):
51 70 self.repo = repo
52 71 self.name = name
53 72 self.vfs = scmutil.vfs(repo.join('shelved'))
54 73 self.backupvfs = scmutil.vfs(repo.join(backupdir))
55 74 self.ui = self.repo.ui
56 75 if filetype:
57 76 self.fname = name + '.' + filetype
58 77 else:
59 78 self.fname = name
60 79
61 80 def exists(self):
62 81 return self.vfs.exists(self.fname)
63 82
64 83 def filename(self):
65 84 return self.vfs.join(self.fname)
66 85
67 86 def backupfilename(self):
68 87 def gennames(base):
69 88 yield base
70 89 base, ext = base.rsplit('.', 1)
71 90 for i in itertools.count(1):
72 91 yield '%s-%d.%s' % (base, i, ext)
73 92
74 93 name = self.backupvfs.join(self.fname)
75 94 for n in gennames(name):
76 95 if not self.backupvfs.exists(n):
77 96 return n
78 97
79 98 def movetobackup(self):
80 99 if not self.backupvfs.isdir():
81 100 self.backupvfs.makedir()
82 101 util.rename(self.filename(), self.backupfilename())
83 102
84 103 def stat(self):
85 104 return self.vfs.stat(self.fname)
86 105
87 106 def opener(self, mode='rb'):
88 107 try:
89 108 return self.vfs(self.fname, mode)
90 109 except IOError as err:
91 110 if err.errno != errno.ENOENT:
92 111 raise
93 112 raise error.Abort(_("shelved change '%s' not found") % self.name)
94 113
95 114 def applybundle(self):
96 115 fp = self.opener()
97 116 try:
98 117 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
99 118 if not isinstance(gen, bundle2.unbundle20):
100 119 gen.apply(self.repo, 'unshelve',
101 120 'bundle:' + self.vfs.join(self.fname),
102 121 targetphase=phases.secret)
103 122 if isinstance(gen, bundle2.unbundle20):
104 123 bundle2.applybundle(self.repo, gen,
105 124 self.repo.currenttransaction(),
106 125 source='unshelve',
107 126 url='bundle:' + self.vfs.join(self.fname))
108 127 finally:
109 128 fp.close()
110 129
111 130 def bundlerepo(self):
112 131 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
113 132 self.vfs.join(self.fname))
114 133 def writebundle(self, bases, node):
115 134 cgversion = changegroup.safeversion(self.repo)
116 135 if cgversion == '01':
117 136 btype = 'HG10BZ'
118 137 compression = None
119 138 else:
120 139 btype = 'HG20'
121 140 compression = 'BZ'
122 141
123 142 cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
124 143 version=cgversion)
125 144 changegroup.writebundle(self.ui, cg, self.fname, btype, self.vfs,
126 145 compression=compression)
127 146
128 147 class shelvedstate(object):
129 148 """Handle persistence during unshelving operations.
130 149
131 150 Handles saving and restoring a shelved state. Ensures that different
132 151 versions of a shelved state are possible and handles them appropriately.
133 152 """
134 153 _version = 1
135 154 _filename = 'shelvedstate'
136 155
137 156 @classmethod
138 157 def load(cls, repo):
139 158 fp = repo.vfs(cls._filename)
140 159 try:
141 160 version = int(fp.readline().strip())
142 161
143 162 if version != cls._version:
144 163 raise error.Abort(_('this version of shelve is incompatible '
145 164 'with the version used in this repo'))
146 165 name = fp.readline().strip()
147 166 wctx = fp.readline().strip()
148 167 pendingctx = fp.readline().strip()
149 parents = [bin(h) for h in fp.readline().split()]
150 stripnodes = [bin(h) for h in fp.readline().split()]
168 parents = [nodemod.bin(h) for h in fp.readline().split()]
169 stripnodes = [nodemod.bin(h) for h in fp.readline().split()]
151 170 finally:
152 171 fp.close()
153 172
154 173 obj = cls()
155 174 obj.name = name
156 obj.wctx = repo[bin(wctx)]
157 obj.pendingctx = repo[bin(pendingctx)]
175 obj.wctx = repo[nodemod.bin(wctx)]
176 obj.pendingctx = repo[nodemod.bin(pendingctx)]
158 177 obj.parents = parents
159 178 obj.stripnodes = stripnodes
160 179
161 180 return obj
162 181
163 182 @classmethod
164 183 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
165 184 fp = repo.vfs(cls._filename, 'wb')
166 185 fp.write('%i\n' % cls._version)
167 186 fp.write('%s\n' % name)
168 fp.write('%s\n' % hex(originalwctx.node()))
169 fp.write('%s\n' % hex(pendingctx.node()))
170 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
171 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
187 fp.write('%s\n' % nodemod.hex(originalwctx.node()))
188 fp.write('%s\n' % nodemod.hex(pendingctx.node()))
189 fp.write('%s\n' %
190 ' '.join([nodemod.hex(p) for p in repo.dirstate.parents()]))
191 fp.write('%s\n' %
192 ' '.join([nodemod.hex(n) for n in stripnodes]))
172 193 fp.close()
173 194
174 195 @classmethod
175 196 def clear(cls, repo):
176 197 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
177 198
178 199 def cleanupoldbackups(repo):
179 200 vfs = scmutil.vfs(repo.join(backupdir))
180 201 maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
181 202 hgfiles = [f for f in vfs.listdir() if f.endswith('.hg')]
182 203 hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
183 204 if 0 < maxbackups and maxbackups < len(hgfiles):
184 205 bordermtime = hgfiles[-maxbackups][0]
185 206 else:
186 207 bordermtime = None
187 208 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
188 209 if mtime == bordermtime:
189 210 # keep it, because timestamp can't decide exact order of backups
190 211 continue
191 212 base = f[:-3]
192 213 for ext in 'hg patch'.split():
193 214 try:
194 215 vfs.unlink(base + '.' + ext)
195 216 except OSError as err:
196 217 if err.errno != errno.ENOENT:
197 218 raise
198 219
199 220 def _aborttransaction(repo):
200 221 '''Abort current transaction for shelve/unshelve, but keep dirstate
201 222 '''
202 223 backupname = 'dirstate.shelve'
203 224 dirstatebackup = None
204 225 try:
205 226 # create backup of (un)shelved dirstate, because aborting transaction
206 227 # should restore dirstate to one at the beginning of the
207 228 # transaction, which doesn't include the result of (un)shelving
208 229 fp = repo.vfs.open(backupname, "w")
209 230 dirstatebackup = backupname
210 231 # clearing _dirty/_dirtypl of dirstate by _writedirstate below
211 232 # is unintentional. but it doesn't cause problem in this case,
212 233 # because no code path refers them until transaction is aborted.
213 234 repo.dirstate._writedirstate(fp) # write in-memory changes forcibly
214 235
215 236 tr = repo.currenttransaction()
216 237 tr.abort()
217 238
218 239 # restore to backuped dirstate
219 240 repo.vfs.rename(dirstatebackup, 'dirstate')
220 241 dirstatebackup = None
221 242 finally:
222 243 if dirstatebackup:
223 244 repo.vfs.unlink(dirstatebackup)
224 245
225 246 def createcmd(ui, repo, pats, opts):
226 247 """subcommand that creates a new shelve"""
227 248 with repo.wlock():
228 249 cmdutil.checkunfinished(repo)
229 250 return _docreatecmd(ui, repo, pats, opts)
230 251
231 252 def _docreatecmd(ui, repo, pats, opts):
232 253 def mutableancestors(ctx):
233 254 """return all mutable ancestors for ctx (included)
234 255
235 256 Much faster than the revset ancestors(ctx) & draft()"""
236 seen = set([nullrev])
257 seen = set([nodemod.nullrev])
237 258 visit = collections.deque()
238 259 visit.append(ctx)
239 260 while visit:
240 261 ctx = visit.popleft()
241 262 yield ctx.node()
242 263 for parent in ctx.parents():
243 264 rev = parent.rev()
244 265 if rev not in seen:
245 266 seen.add(rev)
246 267 if parent.mutable():
247 268 visit.append(parent)
248 269
249 270 wctx = repo[None]
250 271 parents = wctx.parents()
251 272 if len(parents) > 1:
252 273 raise error.Abort(_('cannot shelve while merging'))
253 274 parent = parents[0]
254 275
255 276 # we never need the user, so we use a generic user for all shelve operations
256 277 user = 'shelve@localhost'
257 278 label = repo._activebookmark or parent.branch() or 'default'
258 279
259 280 # slashes aren't allowed in filenames, therefore we rename it
260 281 label = label.replace('/', '_')
261 282
262 283 def gennames():
263 284 yield label
264 285 for i in xrange(1, 100):
265 286 yield '%s-%02d' % (label, i)
266 287
267 if parent.node() != nullid:
288 if parent.node() != nodemod.nullid:
268 289 desc = "changes to: %s" % parent.description().split('\n', 1)[0]
269 290 else:
270 291 desc = '(changes in empty repository)'
271 292
272 293 if not opts['message']:
273 294 opts['message'] = desc
274 295
275 296 name = opts['name']
276 297
277 298 lock = tr = None
278 299 try:
279 300 lock = repo.lock()
280 301
281 302 # use an uncommitted transaction to generate the bundle to avoid
282 303 # pull races. ensure we don't print the abort message to stderr.
283 304 tr = repo.transaction('commit', report=lambda x: None)
284 305
285 306 if name:
286 307 if shelvedfile(repo, name, 'hg').exists():
287 308 raise error.Abort(_("a shelved change named '%s' already exists"
288 309 ) % name)
289 310 else:
290 311 for n in gennames():
291 312 if not shelvedfile(repo, n, 'hg').exists():
292 313 name = n
293 314 break
294 315 else:
295 316 raise error.Abort(_("too many shelved changes named '%s'") %
296 317 label)
297 318
298 319 # ensure we are not creating a subdirectory or a hidden file
299 320 if '/' in name or '\\' in name:
300 321 raise error.Abort(_('shelved change names may not contain slashes'))
301 322 if name.startswith('.'):
302 323 raise error.Abort(_("shelved change names may not start with '.'"))
303 324 interactive = opts.get('interactive', False)
304 325 includeunknown = (opts.get('unknown', False) and
305 326 not opts.get('addremove', False))
306 327
307 328 extra={}
308 329 if includeunknown:
309 330 s = repo.status(match=scmutil.match(repo[None], pats, opts),
310 331 unknown=True)
311 332 if s.unknown:
312 333 extra['shelve_unknown'] = '\0'.join(s.unknown)
313 334 repo[None].add(s.unknown)
314 335
315 336 def commitfunc(ui, repo, message, match, opts):
316 337 hasmq = util.safehasattr(repo, 'mq')
317 338 if hasmq:
318 339 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
319 340 backup = repo.ui.backupconfig('phases', 'new-commit')
320 341 try:
321 342 repo.ui. setconfig('phases', 'new-commit', phases.secret)
322 343 editor = cmdutil.getcommiteditor(editform='shelve.shelve',
323 344 **opts)
324 345 return repo.commit(message, user, opts.get('date'), match,
325 346 editor=editor, extra=extra)
326 347 finally:
327 348 repo.ui.restoreconfig(backup)
328 349 if hasmq:
329 350 repo.mq.checkapplied = saved
330 351
331 352 def interactivecommitfunc(ui, repo, *pats, **opts):
332 353 match = scmutil.match(repo['.'], pats, {})
333 354 message = opts['message']
334 355 return commitfunc(ui, repo, message, match, opts)
335 356 if not interactive:
336 357 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
337 358 else:
338 359 node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
339 360 False, cmdutil.recordfilter, *pats, **opts)
340 361 if not node:
341 362 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
342 363 if stat.deleted:
343 364 ui.status(_("nothing changed (%d missing files, see "
344 365 "'hg status')\n") % len(stat.deleted))
345 366 else:
346 367 ui.status(_("nothing changed\n"))
347 368 return 1
348 369
349 370 bases = list(mutableancestors(repo[node]))
350 371 shelvedfile(repo, name, 'hg').writebundle(bases, node)
351 372 cmdutil.export(repo, [node],
352 373 fp=shelvedfile(repo, name, 'patch').opener('wb'),
353 374 opts=mdiff.diffopts(git=True))
354 375
355 376
356 377 if ui.formatted():
357 378 desc = util.ellipsis(desc, ui.termwidth())
358 379 ui.status(_('shelved as %s\n') % name)
359 380 hg.update(repo, parent.node())
360 381
361 382 _aborttransaction(repo)
362 383 finally:
363 384 lockmod.release(tr, lock)
364 385
365 386 def cleanupcmd(ui, repo):
366 387 """subcommand that deletes all shelves"""
367 388
368 389 with repo.wlock():
369 390 for (name, _type) in repo.vfs.readdir('shelved'):
370 391 suffix = name.rsplit('.', 1)[-1]
371 392 if suffix in ('hg', 'patch'):
372 393 shelvedfile(repo, name).movetobackup()
373 394 cleanupoldbackups(repo)
374 395
375 396 def deletecmd(ui, repo, pats):
376 397 """subcommand that deletes a specific shelve"""
377 398 if not pats:
378 399 raise error.Abort(_('no shelved changes specified!'))
379 400 with repo.wlock():
380 401 try:
381 402 for name in pats:
382 403 for suffix in 'hg patch'.split():
383 404 shelvedfile(repo, name, suffix).movetobackup()
384 405 cleanupoldbackups(repo)
385 406 except OSError as err:
386 407 if err.errno != errno.ENOENT:
387 408 raise
388 409 raise error.Abort(_("shelved change '%s' not found") % name)
389 410
390 411 def listshelves(repo):
391 412 """return all shelves in repo as list of (time, filename)"""
392 413 try:
393 414 names = repo.vfs.readdir('shelved')
394 415 except OSError as err:
395 416 if err.errno != errno.ENOENT:
396 417 raise
397 418 return []
398 419 info = []
399 420 for (name, _type) in names:
400 421 pfx, sfx = name.rsplit('.', 1)
401 422 if not pfx or sfx != 'patch':
402 423 continue
403 424 st = shelvedfile(repo, name).stat()
404 425 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
405 426 return sorted(info, reverse=True)
406 427
407 428 def listcmd(ui, repo, pats, opts):
408 429 """subcommand that displays the list of shelves"""
409 430 pats = set(pats)
410 431 width = 80
411 432 if not ui.plain():
412 433 width = ui.termwidth()
413 434 namelabel = 'shelve.newest'
414 435 for mtime, name in listshelves(repo):
415 436 sname = util.split(name)[1]
416 437 if pats and sname not in pats:
417 438 continue
418 439 ui.write(sname, label=namelabel)
419 440 namelabel = 'shelve.name'
420 441 if ui.quiet:
421 442 ui.write('\n')
422 443 continue
423 444 ui.write(' ' * (16 - len(sname)))
424 445 used = 16
425 446 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
426 447 ui.write(age, label='shelve.age')
427 448 ui.write(' ' * (12 - len(age)))
428 449 used += 12
429 450 with open(name + '.patch', 'rb') as fp:
430 451 while True:
431 452 line = fp.readline()
432 453 if not line:
433 454 break
434 455 if not line.startswith('#'):
435 456 desc = line.rstrip()
436 457 if ui.formatted():
437 458 desc = util.ellipsis(desc, width - used)
438 459 ui.write(desc)
439 460 break
440 461 ui.write('\n')
441 462 if not (opts['patch'] or opts['stat']):
442 463 continue
443 464 difflines = fp.readlines()
444 465 if opts['patch']:
445 466 for chunk, label in patch.difflabel(iter, difflines):
446 467 ui.write(chunk, label=label)
447 468 if opts['stat']:
448 469 for chunk, label in patch.diffstatui(difflines, width=width,
449 470 git=True):
450 471 ui.write(chunk, label=label)
451 472
452 473 def singlepatchcmds(ui, repo, pats, opts, subcommand):
453 474 """subcommand that displays a single shelf"""
454 475 if len(pats) != 1:
455 476 raise error.Abort(_("--%s expects a single shelf") % subcommand)
456 477 shelfname = pats[0]
457 478
458 479 if not shelvedfile(repo, shelfname, 'patch').exists():
459 480 raise error.Abort(_("cannot find shelf %s") % shelfname)
460 481
461 482 listcmd(ui, repo, pats, opts)
462 483
463 484 def checkparents(repo, state):
464 485 """check parent while resuming an unshelve"""
465 486 if state.parents != repo.dirstate.parents():
466 487 raise error.Abort(_('working directory parents do not match unshelve '
467 488 'state'))
468 489
469 490 def pathtofiles(repo, files):
470 491 cwd = repo.getcwd()
471 492 return [repo.pathto(f, cwd) for f in files]
472 493
473 494 def unshelveabort(ui, repo, state, opts):
474 495 """subcommand that abort an in-progress unshelve"""
475 496 with repo.lock():
476 497 try:
477 498 checkparents(repo, state)
478 499
479 500 util.rename(repo.join('unshelverebasestate'),
480 501 repo.join('rebasestate'))
481 502 try:
482 503 rebase.rebase(ui, repo, **{
483 504 'abort' : True
484 505 })
485 506 except Exception:
486 507 util.rename(repo.join('rebasestate'),
487 508 repo.join('unshelverebasestate'))
488 509 raise
489 510
490 511 mergefiles(ui, repo, state.wctx, state.pendingctx)
491 512 repair.strip(ui, repo, state.stripnodes, backup=False,
492 513 topic='shelve')
493 514 finally:
494 515 shelvedstate.clear(repo)
495 516 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
496 517
497 518 def mergefiles(ui, repo, wctx, shelvectx):
498 519 """updates to wctx and merges the changes from shelvectx into the
499 520 dirstate."""
500 521 oldquiet = ui.quiet
501 522 try:
502 523 ui.quiet = True
503 524 hg.update(repo, wctx.node())
504 525 files = []
505 526 files.extend(shelvectx.files())
506 527 files.extend(shelvectx.parents()[0].files())
507 528
508 529 # revert will overwrite unknown files, so move them out of the way
509 530 for file in repo.status(unknown=True).unknown:
510 531 if file in files:
511 532 util.rename(file, scmutil.origpath(ui, repo, file))
512 533 ui.pushbuffer(True)
513 534 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
514 535 *pathtofiles(repo, files),
515 536 **{'no_backup': True})
516 537 ui.popbuffer()
517 538 finally:
518 539 ui.quiet = oldquiet
519 540
520 541 def unshelvecleanup(ui, repo, name, opts):
521 542 """remove related files after an unshelve"""
522 543 if not opts['keep']:
523 544 for filetype in 'hg patch'.split():
524 545 shelvedfile(repo, name, filetype).movetobackup()
525 546 cleanupoldbackups(repo)
526 547
527 548 def unshelvecontinue(ui, repo, state, opts):
528 549 """subcommand to continue an in-progress unshelve"""
529 550 # We're finishing off a merge. First parent is our original
530 551 # parent, second is the temporary "fake" commit we're unshelving.
531 552 with repo.lock():
532 553 checkparents(repo, state)
533 554 ms = merge.mergestate.read(repo)
534 555 if [f for f in ms if ms[f] == 'u']:
535 556 raise error.Abort(
536 557 _("unresolved conflicts, can't continue"),
537 558 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
538 559
539 560 util.rename(repo.join('unshelverebasestate'),
540 561 repo.join('rebasestate'))
541 562 try:
542 563 rebase.rebase(ui, repo, **{
543 564 'continue' : True
544 565 })
545 566 except Exception:
546 567 util.rename(repo.join('rebasestate'),
547 568 repo.join('unshelverebasestate'))
548 569 raise
549 570
550 571 shelvectx = repo['tip']
551 572 if not shelvectx in state.pendingctx.children():
552 573 # rebase was a no-op, so it produced no child commit
553 574 shelvectx = state.pendingctx
554 575 else:
555 576 # only strip the shelvectx if the rebase produced it
556 577 state.stripnodes.append(shelvectx.node())
557 578
558 579 mergefiles(ui, repo, state.wctx, shelvectx)
559 580
560 581 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
561 582 shelvedstate.clear(repo)
562 583 unshelvecleanup(ui, repo, state.name, opts)
563 584 ui.status(_("unshelve of '%s' complete\n") % state.name)
564 585
565 586 @command('unshelve',
566 587 [('a', 'abort', None,
567 588 _('abort an incomplete unshelve operation')),
568 589 ('c', 'continue', None,
569 590 _('continue an incomplete unshelve operation')),
570 591 ('k', 'keep', None,
571 592 _('keep shelve after unshelving')),
572 593 ('t', 'tool', '', _('specify merge tool')),
573 594 ('', 'date', '',
574 595 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
575 596 _('hg unshelve [SHELVED]'))
576 597 def unshelve(ui, repo, *shelved, **opts):
577 598 """restore a shelved change to the working directory
578 599
579 600 This command accepts an optional name of a shelved change to
580 601 restore. If none is given, the most recent shelved change is used.
581 602
582 603 If a shelved change is applied successfully, the bundle that
583 604 contains the shelved changes is moved to a backup location
584 605 (.hg/shelve-backup).
585 606
586 607 Since you can restore a shelved change on top of an arbitrary
587 608 commit, it is possible that unshelving will result in a conflict
588 609 between your changes and the commits you are unshelving onto. If
589 610 this occurs, you must resolve the conflict, then use
590 611 ``--continue`` to complete the unshelve operation. (The bundle
591 612 will not be moved until you successfully complete the unshelve.)
592 613
593 614 (Alternatively, you can use ``--abort`` to abandon an unshelve
594 615 that causes a conflict. This reverts the unshelved changes, and
595 616 leaves the bundle in place.)
596 617
597 618 After a successful unshelve, the shelved changes are stored in a
598 619 backup directory. Only the N most recent backups are kept. N
599 620 defaults to 10 but can be overridden using the ``shelve.maxbackups``
600 621 configuration option.
601 622
602 623 .. container:: verbose
603 624
604 625 Timestamp in seconds is used to decide order of backups. More
605 626 than ``maxbackups`` backups are kept, if same timestamp
606 627 prevents from deciding exact order of them, for safety.
607 628 """
608 629 with repo.wlock():
609 630 return _dounshelve(ui, repo, *shelved, **opts)
610 631
611 632 def _dounshelve(ui, repo, *shelved, **opts):
612 633 abortf = opts['abort']
613 634 continuef = opts['continue']
614 635 if not abortf and not continuef:
615 636 cmdutil.checkunfinished(repo)
616 637
617 638 if abortf or continuef:
618 639 if abortf and continuef:
619 640 raise error.Abort(_('cannot use both abort and continue'))
620 641 if shelved:
621 642 raise error.Abort(_('cannot combine abort/continue with '
622 643 'naming a shelved change'))
623 644 if abortf and opts.get('tool', False):
624 645 ui.warn(_('tool option will be ignored\n'))
625 646
626 647 try:
627 648 state = shelvedstate.load(repo)
628 649 except IOError as err:
629 650 if err.errno != errno.ENOENT:
630 651 raise
631 652 cmdutil.wrongtooltocontinue(repo, _('unshelve'))
632 653
633 654 if abortf:
634 655 return unshelveabort(ui, repo, state, opts)
635 656 elif continuef:
636 657 return unshelvecontinue(ui, repo, state, opts)
637 658 elif len(shelved) > 1:
638 659 raise error.Abort(_('can only unshelve one change at a time'))
639 660 elif not shelved:
640 661 shelved = listshelves(repo)
641 662 if not shelved:
642 663 raise error.Abort(_('no shelved changes to apply!'))
643 664 basename = util.split(shelved[0][1])[1]
644 665 ui.status(_("unshelving change '%s'\n") % basename)
645 666 else:
646 667 basename = shelved[0]
647 668
648 669 if not shelvedfile(repo, basename, 'patch').exists():
649 670 raise error.Abort(_("shelved change '%s' not found") % basename)
650 671
651 672 oldquiet = ui.quiet
652 673 lock = tr = None
653 674 forcemerge = ui.backupconfig('ui', 'forcemerge')
654 675 try:
655 676 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'unshelve')
656 677 lock = repo.lock()
657 678
658 679 tr = repo.transaction('unshelve', report=lambda x: None)
659 680 oldtiprev = len(repo)
660 681
661 682 pctx = repo['.']
662 683 tmpwctx = pctx
663 684 # The goal is to have a commit structure like so:
664 685 # ...-> pctx -> tmpwctx -> shelvectx
665 686 # where tmpwctx is an optional commit with the user's pending changes
666 687 # and shelvectx is the unshelved changes. Then we merge it all down
667 688 # to the original pctx.
668 689
669 690 # Store pending changes in a commit and remember added in case a shelve
670 691 # contains unknown files that are part of the pending change
671 692 s = repo.status()
672 693 addedbefore = frozenset(s.added)
673 694 if s.modified or s.added or s.removed or s.deleted:
674 695 ui.status(_("temporarily committing pending changes "
675 696 "(restore with 'hg unshelve --abort')\n"))
676 697 def commitfunc(ui, repo, message, match, opts):
677 698 hasmq = util.safehasattr(repo, 'mq')
678 699 if hasmq:
679 700 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
680 701
681 702 backup = repo.ui.backupconfig('phases', 'new-commit')
682 703 try:
683 704 repo.ui.setconfig('phases', 'new-commit', phases.secret)
684 705 return repo.commit(message, 'shelve@localhost',
685 706 opts.get('date'), match)
686 707 finally:
687 708 repo.ui.restoreconfig(backup)
688 709 if hasmq:
689 710 repo.mq.checkapplied = saved
690 711
691 712 tempopts = {}
692 713 tempopts['message'] = "pending changes temporary commit"
693 714 tempopts['date'] = opts.get('date')
694 715 ui.quiet = True
695 716 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
696 717 tmpwctx = repo[node]
697 718
698 719 ui.quiet = True
699 720 shelvedfile(repo, basename, 'hg').applybundle()
700 721
701 722 ui.quiet = oldquiet
702 723
703 724 shelvectx = repo['tip']
704 725
705 726 # If the shelve is not immediately on top of the commit
706 727 # we'll be merging with, rebase it to be on top.
707 728 if tmpwctx.node() != shelvectx.parents()[0].node():
708 729 ui.status(_('rebasing shelved changes\n'))
709 730 try:
710 731 rebase.rebase(ui, repo, **{
711 732 'rev' : [shelvectx.rev()],
712 733 'dest' : str(tmpwctx.rev()),
713 734 'keep' : True,
714 735 'tool' : opts.get('tool', ''),
715 736 })
716 737 except error.InterventionRequired:
717 738 tr.close()
718 739
719 740 stripnodes = [repo.changelog.node(rev)
720 741 for rev in xrange(oldtiprev, len(repo))]
721 742 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
722 743
723 744 util.rename(repo.join('rebasestate'),
724 745 repo.join('unshelverebasestate'))
725 746 raise error.InterventionRequired(
726 747 _("unresolved conflicts (see 'hg resolve', then "
727 748 "'hg unshelve --continue')"))
728 749
729 750 # refresh ctx after rebase completes
730 751 shelvectx = repo['tip']
731 752
732 753 if not shelvectx in tmpwctx.children():
733 754 # rebase was a no-op, so it produced no child commit
734 755 shelvectx = tmpwctx
735 756
736 757 mergefiles(ui, repo, pctx, shelvectx)
737 758
738 759 # Forget any files that were unknown before the shelve, unknown before
739 760 # unshelve started, but are now added.
740 761 shelveunknown = shelvectx.extra().get('shelve_unknown')
741 762 if shelveunknown:
742 763 shelveunknown = frozenset(shelveunknown.split('\0'))
743 764 addedafter = frozenset(repo.status().added)
744 765 toforget = (addedafter & shelveunknown) - addedbefore
745 766 repo[None].forget(toforget)
746 767
747 768 shelvedstate.clear(repo)
748 769
749 770 # The transaction aborting will strip all the commits for us,
750 771 # but it doesn't update the inmemory structures, so addchangegroup
751 772 # hooks still fire and try to operate on the missing commits.
752 773 # Clean up manually to prevent this.
753 774 repo.unfiltered().changelog.strip(oldtiprev, tr)
754 775
755 776 unshelvecleanup(ui, repo, basename, opts)
756 777
757 778 _aborttransaction(repo)
758 779 finally:
759 780 ui.quiet = oldquiet
760 781 if tr:
761 782 tr.release()
762 783 lockmod.release(lock)
763 784 ui.restoreconfig(forcemerge)
764 785
765 786 @command('shelve',
766 787 [('A', 'addremove', None,
767 788 _('mark new/missing files as added/removed before shelving')),
768 789 ('u', 'unknown', None,
769 790 _('store unknown files in the shelve')),
770 791 ('', 'cleanup', None,
771 792 _('delete all shelved changes')),
772 793 ('', 'date', '',
773 794 _('shelve with the specified commit date'), _('DATE')),
774 795 ('d', 'delete', None,
775 796 _('delete the named shelved change(s)')),
776 797 ('e', 'edit', False,
777 798 _('invoke editor on commit messages')),
778 799 ('l', 'list', None,
779 800 _('list current shelves')),
780 801 ('m', 'message', '',
781 802 _('use text as shelve message'), _('TEXT')),
782 803 ('n', 'name', '',
783 804 _('use the given name for the shelved commit'), _('NAME')),
784 805 ('p', 'patch', None,
785 806 _('show patch')),
786 807 ('i', 'interactive', None,
787 808 _('interactive mode, only works while creating a shelve')),
788 809 ('', 'stat', None,
789 810 _('output diffstat-style summary of changes'))] + commands.walkopts,
790 811 _('hg shelve [OPTION]... [FILE]...'))
791 812 def shelvecmd(ui, repo, *pats, **opts):
792 813 '''save and set aside changes from the working directory
793 814
794 815 Shelving takes files that "hg status" reports as not clean, saves
795 816 the modifications to a bundle (a shelved change), and reverts the
796 817 files so that their state in the working directory becomes clean.
797 818
798 819 To restore these changes to the working directory, using "hg
799 820 unshelve"; this will work even if you switch to a different
800 821 commit.
801 822
802 823 When no files are specified, "hg shelve" saves all not-clean
803 824 files. If specific files or directories are named, only changes to
804 825 those files are shelved.
805 826
806 827 Each shelved change has a name that makes it easier to find later.
807 828 The name of a shelved change defaults to being based on the active
808 829 bookmark, or if there is no active bookmark, the current named
809 830 branch. To specify a different name, use ``--name``.
810 831
811 832 To see a list of existing shelved changes, use the ``--list``
812 833 option. For each shelved change, this will print its name, age,
813 834 and description; use ``--patch`` or ``--stat`` for more details.
814 835
815 836 To delete specific shelved changes, use ``--delete``. To delete
816 837 all shelved changes, use ``--cleanup``.
817 838 '''
818 839 allowables = [
819 840 ('addremove', set(['create'])), # 'create' is pseudo action
820 841 ('unknown', set(['create'])),
821 842 ('cleanup', set(['cleanup'])),
822 843 # ('date', set(['create'])), # ignored for passing '--date "0 0"' in tests
823 844 ('delete', set(['delete'])),
824 845 ('edit', set(['create'])),
825 846 ('list', set(['list'])),
826 847 ('message', set(['create'])),
827 848 ('name', set(['create'])),
828 849 ('patch', set(['patch', 'list'])),
829 850 ('stat', set(['stat', 'list'])),
830 851 ]
831 852 def checkopt(opt):
832 853 if opts[opt]:
833 854 for i, allowable in allowables:
834 855 if opts[i] and opt not in allowable:
835 856 raise error.Abort(_("options '--%s' and '--%s' may not be "
836 857 "used together") % (opt, i))
837 858 return True
838 859 if checkopt('cleanup'):
839 860 if pats:
840 861 raise error.Abort(_("cannot specify names when using '--cleanup'"))
841 862 return cleanupcmd(ui, repo)
842 863 elif checkopt('delete'):
843 864 return deletecmd(ui, repo, pats)
844 865 elif checkopt('list'):
845 866 return listcmd(ui, repo, pats, opts)
846 867 elif checkopt('patch'):
847 868 return singlepatchcmds(ui, repo, pats, opts, subcommand='patch')
848 869 elif checkopt('stat'):
849 870 return singlepatchcmds(ui, repo, pats, opts, subcommand='stat')
850 871 else:
851 872 return createcmd(ui, repo, pats, opts)
852 873
853 874 def extsetup(ui):
854 875 cmdutil.unfinishedstates.append(
855 876 [shelvedstate._filename, False, False,
856 877 _('unshelve already in progress'),
857 878 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
858 879 cmdutil.afterresolvedstates.append(
859 880 [shelvedstate._filename, _('hg unshelve --continue')])
@@ -1,149 +1,148 b''
1 1 #require test-repo
2 2
3 3 $ cd "$TESTDIR"/..
4 4
5 5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 6 contrib/check-code.py not using absolute_import
7 7 contrib/check-code.py requires print_function
8 8 contrib/debugshell.py not using absolute_import
9 9 contrib/hgfixes/fix_bytes.py not using absolute_import
10 10 contrib/hgfixes/fix_bytesmod.py not using absolute_import
11 11 contrib/hgfixes/fix_leftover_imports.py not using absolute_import
12 12 contrib/import-checker.py not using absolute_import
13 13 contrib/import-checker.py requires print_function
14 14 contrib/memory.py not using absolute_import
15 15 contrib/perf.py not using absolute_import
16 16 contrib/python-hook-examples.py not using absolute_import
17 17 contrib/revsetbenchmarks.py not using absolute_import
18 18 contrib/revsetbenchmarks.py requires print_function
19 19 contrib/showstack.py not using absolute_import
20 20 contrib/synthrepo.py not using absolute_import
21 21 contrib/win32/hgwebdir_wsgi.py not using absolute_import
22 22 doc/check-seclevel.py not using absolute_import
23 23 doc/gendoc.py not using absolute_import
24 24 doc/hgmanpage.py not using absolute_import
25 25 hgext/__init__.py not using absolute_import
26 26 hgext/color.py not using absolute_import
27 27 hgext/convert/__init__.py not using absolute_import
28 28 hgext/convert/bzr.py not using absolute_import
29 29 hgext/convert/common.py not using absolute_import
30 30 hgext/convert/convcmd.py not using absolute_import
31 31 hgext/convert/cvs.py not using absolute_import
32 32 hgext/convert/subversion.py not using absolute_import
33 33 hgext/convert/transport.py not using absolute_import
34 34 hgext/eol.py not using absolute_import
35 35 hgext/extdiff.py not using absolute_import
36 36 hgext/factotum.py not using absolute_import
37 37 hgext/fetch.py not using absolute_import
38 38 hgext/gpg.py not using absolute_import
39 39 hgext/graphlog.py not using absolute_import
40 40 hgext/hgcia.py not using absolute_import
41 41 hgext/hgk.py not using absolute_import
42 42 hgext/highlight/__init__.py not using absolute_import
43 43 hgext/highlight/highlight.py not using absolute_import
44 44 hgext/histedit.py not using absolute_import
45 45 hgext/largefiles/__init__.py not using absolute_import
46 46 hgext/largefiles/basestore.py not using absolute_import
47 47 hgext/largefiles/lfcommands.py not using absolute_import
48 48 hgext/largefiles/lfutil.py not using absolute_import
49 49 hgext/largefiles/localstore.py not using absolute_import
50 50 hgext/largefiles/overrides.py not using absolute_import
51 51 hgext/largefiles/proto.py not using absolute_import
52 52 hgext/largefiles/remotestore.py not using absolute_import
53 53 hgext/largefiles/reposetup.py not using absolute_import
54 54 hgext/largefiles/uisetup.py not using absolute_import
55 55 hgext/largefiles/wirestore.py not using absolute_import
56 56 hgext/mq.py not using absolute_import
57 57 hgext/notify.py not using absolute_import
58 58 hgext/patchbomb.py not using absolute_import
59 59 hgext/purge.py not using absolute_import
60 60 hgext/rebase.py not using absolute_import
61 61 hgext/record.py not using absolute_import
62 62 hgext/relink.py not using absolute_import
63 63 hgext/schemes.py not using absolute_import
64 64 hgext/share.py not using absolute_import
65 hgext/shelve.py not using absolute_import
66 65 hgext/transplant.py not using absolute_import
67 66 hgext/win32mbcs.py not using absolute_import
68 67 hgext/win32text.py not using absolute_import
69 68 i18n/check-translation.py not using absolute_import
70 69 i18n/polib.py not using absolute_import
71 70 setup.py not using absolute_import
72 71 tests/filterpyflakes.py requires print_function
73 72 tests/generate-working-copy-states.py requires print_function
74 73 tests/get-with-headers.py requires print_function
75 74 tests/heredoctest.py requires print_function
76 75 tests/hypothesishelpers.py not using absolute_import
77 76 tests/hypothesishelpers.py requires print_function
78 77 tests/killdaemons.py not using absolute_import
79 78 tests/md5sum.py not using absolute_import
80 79 tests/mockblackbox.py not using absolute_import
81 80 tests/printenv.py not using absolute_import
82 81 tests/readlink.py not using absolute_import
83 82 tests/readlink.py requires print_function
84 83 tests/revlog-formatv0.py not using absolute_import
85 84 tests/run-tests.py not using absolute_import
86 85 tests/seq.py not using absolute_import
87 86 tests/seq.py requires print_function
88 87 tests/silenttestrunner.py not using absolute_import
89 88 tests/silenttestrunner.py requires print_function
90 89 tests/sitecustomize.py not using absolute_import
91 90 tests/svn-safe-append.py not using absolute_import
92 91 tests/svnxml.py not using absolute_import
93 92 tests/test-ancestor.py requires print_function
94 93 tests/test-atomictempfile.py not using absolute_import
95 94 tests/test-batching.py not using absolute_import
96 95 tests/test-batching.py requires print_function
97 96 tests/test-bdiff.py not using absolute_import
98 97 tests/test-bdiff.py requires print_function
99 98 tests/test-context.py not using absolute_import
100 99 tests/test-context.py requires print_function
101 100 tests/test-demandimport.py not using absolute_import
102 101 tests/test-demandimport.py requires print_function
103 102 tests/test-dispatch.py not using absolute_import
104 103 tests/test-dispatch.py requires print_function
105 104 tests/test-doctest.py not using absolute_import
106 105 tests/test-duplicateoptions.py not using absolute_import
107 106 tests/test-duplicateoptions.py requires print_function
108 107 tests/test-filecache.py not using absolute_import
109 108 tests/test-filecache.py requires print_function
110 109 tests/test-filelog.py not using absolute_import
111 110 tests/test-filelog.py requires print_function
112 111 tests/test-hg-parseurl.py not using absolute_import
113 112 tests/test-hg-parseurl.py requires print_function
114 113 tests/test-hgweb-auth.py not using absolute_import
115 114 tests/test-hgweb-auth.py requires print_function
116 115 tests/test-hgwebdir-paths.py not using absolute_import
117 116 tests/test-hybridencode.py not using absolute_import
118 117 tests/test-hybridencode.py requires print_function
119 118 tests/test-lrucachedict.py not using absolute_import
120 119 tests/test-lrucachedict.py requires print_function
121 120 tests/test-manifest.py not using absolute_import
122 121 tests/test-minirst.py not using absolute_import
123 122 tests/test-minirst.py requires print_function
124 123 tests/test-parseindex2.py not using absolute_import
125 124 tests/test-parseindex2.py requires print_function
126 125 tests/test-pathencode.py not using absolute_import
127 126 tests/test-pathencode.py requires print_function
128 127 tests/test-propertycache.py not using absolute_import
129 128 tests/test-propertycache.py requires print_function
130 129 tests/test-revlog-ancestry.py not using absolute_import
131 130 tests/test-revlog-ancestry.py requires print_function
132 131 tests/test-run-tests.py not using absolute_import
133 132 tests/test-simplemerge.py not using absolute_import
134 133 tests/test-status-inprocess.py not using absolute_import
135 134 tests/test-status-inprocess.py requires print_function
136 135 tests/test-symlink-os-yes-fs-no.py not using absolute_import
137 136 tests/test-trusted.py not using absolute_import
138 137 tests/test-trusted.py requires print_function
139 138 tests/test-ui-color.py not using absolute_import
140 139 tests/test-ui-color.py requires print_function
141 140 tests/test-ui-config.py not using absolute_import
142 141 tests/test-ui-config.py requires print_function
143 142 tests/test-ui-verbosity.py not using absolute_import
144 143 tests/test-ui-verbosity.py requires print_function
145 144 tests/test-url.py not using absolute_import
146 145 tests/test-url.py requires print_function
147 146 tests/test-walkrepo.py requires print_function
148 147 tests/test-wireproto.py requires print_function
149 148 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now