##// END OF EJS Templates
shelve: mention FILE options in help...
Mads Kiilerich -
r20410:fc535464 default
parent child Browse files
Show More
@@ -1,691 +1,691 b''
1 1 # shelve.py - save/restore working directory state
2 2 #
3 3 # Copyright 2013 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """save and restore changes to the working directory
9 9
10 10 The "hg shelve" command saves changes made to the working directory
11 11 and reverts those changes, resetting the working directory to a clean
12 12 state.
13 13
14 14 Later on, the "hg unshelve" command restores the changes saved by "hg
15 15 shelve". Changes can be restored even after updating to a different
16 16 parent, in which case Mercurial's merge machinery will resolve any
17 17 conflicts if necessary.
18 18
19 19 You can have more than one shelved change outstanding at a time; each
20 20 shelved change has a distinct name. For details, see the help for "hg
21 21 shelve".
22 22 """
23 23
24 24 from mercurial.i18n import _
25 25 from mercurial.node import nullid, nullrev, bin, hex
26 26 from mercurial import changegroup, cmdutil, scmutil, phases, commands
27 27 from mercurial import error, hg, mdiff, merge, patch, repair, util
28 28 from mercurial import templatefilters
29 29 from mercurial import lock as lockmod
30 30 from hgext import rebase
31 31 import errno
32 32
33 33 cmdtable = {}
34 34 command = cmdutil.command(cmdtable)
35 35 testedwith = 'internal'
36 36
37 37 class shelvedfile(object):
38 38 """Helper for the file storing a single shelve
39 39
40 40 Handles common functions on shelve files (.hg/.files/.patch) using
41 41 the vfs layer"""
42 42 def __init__(self, repo, name, filetype=None):
43 43 self.repo = repo
44 44 self.name = name
45 45 self.vfs = scmutil.vfs(repo.join('shelved'))
46 46 if filetype:
47 47 self.fname = name + '.' + filetype
48 48 else:
49 49 self.fname = name
50 50
51 51 def exists(self):
52 52 return self.vfs.exists(self.fname)
53 53
54 54 def filename(self):
55 55 return self.vfs.join(self.fname)
56 56
57 57 def unlink(self):
58 58 util.unlink(self.filename())
59 59
60 60 def stat(self):
61 61 return self.vfs.stat(self.fname)
62 62
63 63 def opener(self, mode='rb'):
64 64 try:
65 65 return self.vfs(self.fname, mode)
66 66 except IOError, err:
67 67 if err.errno != errno.ENOENT:
68 68 raise
69 69 raise util.Abort(_("shelved change '%s' not found") % self.name)
70 70
71 71 class shelvedstate(object):
72 72 """Handle persistence during unshelving operations.
73 73
74 74 Handles saving and restoring a shelved state. Ensures that different
75 75 versions of a shelved state are possible and handles them appropriately.
76 76 """
77 77 _version = 1
78 78 _filename = 'shelvedstate'
79 79
80 80 @classmethod
81 81 def load(cls, repo):
82 82 fp = repo.opener(cls._filename)
83 83 try:
84 84 version = int(fp.readline().strip())
85 85
86 86 if version != cls._version:
87 87 raise util.Abort(_('this version of shelve is incompatible '
88 88 'with the version used in this repo'))
89 89 name = fp.readline().strip()
90 90 wctx = fp.readline().strip()
91 91 pendingctx = fp.readline().strip()
92 92 parents = [bin(h) for h in fp.readline().split()]
93 93 stripnodes = [bin(h) for h in fp.readline().split()]
94 94 finally:
95 95 fp.close()
96 96
97 97 obj = cls()
98 98 obj.name = name
99 99 obj.wctx = repo[bin(wctx)]
100 100 obj.pendingctx = repo[bin(pendingctx)]
101 101 obj.parents = parents
102 102 obj.stripnodes = stripnodes
103 103
104 104 return obj
105 105
106 106 @classmethod
107 107 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
108 108 fp = repo.opener(cls._filename, 'wb')
109 109 fp.write('%i\n' % cls._version)
110 110 fp.write('%s\n' % name)
111 111 fp.write('%s\n' % hex(originalwctx.node()))
112 112 fp.write('%s\n' % hex(pendingctx.node()))
113 113 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
114 114 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
115 115 fp.close()
116 116
117 117 @classmethod
118 118 def clear(cls, repo):
119 119 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
120 120
121 121 def createcmd(ui, repo, pats, opts):
122 122 """subcommand that creates a new shelve"""
123 123
124 124 def publicancestors(ctx):
125 125 """Compute the public ancestors of a commit.
126 126
127 127 Much faster than the revset ancestors(ctx) & draft()"""
128 128 seen = set([nullrev])
129 129 visit = util.deque()
130 130 visit.append(ctx)
131 131 while visit:
132 132 ctx = visit.popleft()
133 133 yield ctx.node()
134 134 for parent in ctx.parents():
135 135 rev = parent.rev()
136 136 if rev not in seen:
137 137 seen.add(rev)
138 138 if parent.mutable():
139 139 visit.append(parent)
140 140
141 141 wctx = repo[None]
142 142 parents = wctx.parents()
143 143 if len(parents) > 1:
144 144 raise util.Abort(_('cannot shelve while merging'))
145 145 parent = parents[0]
146 146
147 147 # we never need the user, so we use a generic user for all shelve operations
148 148 user = 'shelve@localhost'
149 149 label = repo._bookmarkcurrent or parent.branch() or 'default'
150 150
151 151 # slashes aren't allowed in filenames, therefore we rename it
152 152 origlabel, label = label, label.replace('/', '_')
153 153
154 154 def gennames():
155 155 yield label
156 156 for i in xrange(1, 100):
157 157 yield '%s-%02d' % (label, i)
158 158
159 159 shelvedfiles = []
160 160
161 161 def commitfunc(ui, repo, message, match, opts):
162 162 # check modified, added, removed, deleted only
163 163 for flist in repo.status(match=match)[:4]:
164 164 shelvedfiles.extend(flist)
165 165 hasmq = util.safehasattr(repo, 'mq')
166 166 if hasmq:
167 167 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
168 168 try:
169 169 return repo.commit(message, user, opts.get('date'), match)
170 170 finally:
171 171 if hasmq:
172 172 repo.mq.checkapplied = saved
173 173
174 174 if parent.node() != nullid:
175 175 desc = parent.description().split('\n', 1)[0]
176 176 else:
177 177 desc = '(empty repository)'
178 178
179 179 if not opts['message']:
180 180 opts['message'] = desc
181 181
182 182 name = opts['name']
183 183
184 184 wlock = lock = tr = bms = None
185 185 try:
186 186 wlock = repo.wlock()
187 187 lock = repo.lock()
188 188
189 189 bms = repo._bookmarks.copy()
190 190 # use an uncommitted transaction to generate the bundle to avoid
191 191 # pull races. ensure we don't print the abort message to stderr.
192 192 tr = repo.transaction('commit', report=lambda x: None)
193 193
194 194 if name:
195 195 if shelvedfile(repo, name, 'hg').exists():
196 196 raise util.Abort(_("a shelved change named '%s' already exists")
197 197 % name)
198 198 else:
199 199 for n in gennames():
200 200 if not shelvedfile(repo, n, 'hg').exists():
201 201 name = n
202 202 break
203 203 else:
204 204 raise util.Abort(_("too many shelved changes named '%s'") %
205 205 label)
206 206
207 207 # ensure we are not creating a subdirectory or a hidden file
208 208 if '/' in name or '\\' in name:
209 209 raise util.Abort(_('shelved change names may not contain slashes'))
210 210 if name.startswith('.'):
211 211 raise util.Abort(_("shelved change names may not start with '.'"))
212 212
213 213 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
214 214
215 215 if not node:
216 216 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
217 217 if stat[3]:
218 218 ui.status(_("nothing changed (%d missing files, see "
219 219 "'hg status')\n") % len(stat[3]))
220 220 else:
221 221 ui.status(_("nothing changed\n"))
222 222 return 1
223 223
224 224 phases.retractboundary(repo, phases.secret, [node])
225 225
226 226 fp = shelvedfile(repo, name, 'files').opener('wb')
227 227 fp.write('\0'.join(shelvedfiles))
228 228
229 229 bases = list(publicancestors(repo[node]))
230 230 cg = repo.changegroupsubset(bases, [node], 'shelve')
231 231 changegroup.writebundle(cg, shelvedfile(repo, name, 'hg').filename(),
232 232 'HG10UN')
233 233 cmdutil.export(repo, [node],
234 234 fp=shelvedfile(repo, name, 'patch').opener('wb'),
235 235 opts=mdiff.diffopts(git=True))
236 236
237 237
238 238 if ui.formatted():
239 239 desc = util.ellipsis(desc, ui.termwidth())
240 240 ui.status(_('shelved as %s\n') % name)
241 241 hg.update(repo, parent.node())
242 242 finally:
243 243 if bms:
244 244 # restore old bookmarks
245 245 repo._bookmarks.update(bms)
246 246 repo._bookmarks.write()
247 247 if tr:
248 248 tr.abort()
249 249 lockmod.release(lock, wlock)
250 250
251 251 def cleanupcmd(ui, repo):
252 252 """subcommand that deletes all shelves"""
253 253
254 254 wlock = None
255 255 try:
256 256 wlock = repo.wlock()
257 257 for (name, _) in repo.vfs.readdir('shelved'):
258 258 suffix = name.rsplit('.', 1)[-1]
259 259 if suffix in ('hg', 'files', 'patch'):
260 260 shelvedfile(repo, name).unlink()
261 261 finally:
262 262 lockmod.release(wlock)
263 263
264 264 def deletecmd(ui, repo, pats):
265 265 """subcommand that deletes a specific shelve"""
266 266 if not pats:
267 267 raise util.Abort(_('no shelved changes specified!'))
268 268 wlock = None
269 269 try:
270 270 wlock = repo.wlock()
271 271 try:
272 272 for name in pats:
273 273 for suffix in 'hg files patch'.split():
274 274 shelvedfile(repo, name, suffix).unlink()
275 275 except OSError, err:
276 276 if err.errno != errno.ENOENT:
277 277 raise
278 278 raise util.Abort(_("shelved change '%s' not found") % name)
279 279 finally:
280 280 lockmod.release(wlock)
281 281
282 282 def listshelves(repo):
283 283 """return all shelves in repo as list of (time, filename)"""
284 284 try:
285 285 names = repo.vfs.readdir('shelved')
286 286 except OSError, err:
287 287 if err.errno != errno.ENOENT:
288 288 raise
289 289 return []
290 290 info = []
291 291 for (name, _) in names:
292 292 pfx, sfx = name.rsplit('.', 1)
293 293 if not pfx or sfx != 'patch':
294 294 continue
295 295 st = shelvedfile(repo, name).stat()
296 296 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
297 297 return sorted(info, reverse=True)
298 298
299 299 def listcmd(ui, repo, pats, opts):
300 300 """subcommand that displays the list of shelves"""
301 301 pats = set(pats)
302 302 width = 80
303 303 if not ui.plain():
304 304 width = ui.termwidth()
305 305 namelabel = 'shelve.newest'
306 306 for mtime, name in listshelves(repo):
307 307 sname = util.split(name)[1]
308 308 if pats and sname not in pats:
309 309 continue
310 310 ui.write(sname, label=namelabel)
311 311 namelabel = 'shelve.name'
312 312 if ui.quiet:
313 313 ui.write('\n')
314 314 continue
315 315 ui.write(' ' * (16 - len(sname)))
316 316 used = 16
317 317 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
318 318 ui.write(age, label='shelve.age')
319 319 ui.write(' ' * (12 - len(age)))
320 320 used += 12
321 321 fp = open(name + '.patch', 'rb')
322 322 try:
323 323 while True:
324 324 line = fp.readline()
325 325 if not line:
326 326 break
327 327 if not line.startswith('#'):
328 328 desc = line.rstrip()
329 329 if ui.formatted():
330 330 desc = util.ellipsis(desc, width - used)
331 331 ui.write(desc)
332 332 break
333 333 ui.write('\n')
334 334 if not (opts['patch'] or opts['stat']):
335 335 continue
336 336 difflines = fp.readlines()
337 337 if opts['patch']:
338 338 for chunk, label in patch.difflabel(iter, difflines):
339 339 ui.write(chunk, label=label)
340 340 if opts['stat']:
341 341 for chunk, label in patch.diffstatui(difflines, width=width,
342 342 git=True):
343 343 ui.write(chunk, label=label)
344 344 finally:
345 345 fp.close()
346 346
347 347 def checkparents(repo, state):
348 348 """check parent while resuming an unshelve"""
349 349 if state.parents != repo.dirstate.parents():
350 350 raise util.Abort(_('working directory parents do not match unshelve '
351 351 'state'))
352 352
353 353 def pathtofiles(repo, files):
354 354 cwd = repo.getcwd()
355 355 return [repo.pathto(f, cwd) for f in files]
356 356
357 357 def unshelveabort(ui, repo, state, opts):
358 358 """subcommand that abort an in-progress unshelve"""
359 359 wlock = repo.wlock()
360 360 lock = None
361 361 try:
362 362 checkparents(repo, state)
363 363
364 364 util.rename(repo.join('unshelverebasestate'),
365 365 repo.join('rebasestate'))
366 366 try:
367 367 rebase.rebase(ui, repo, **{
368 368 'abort' : True
369 369 })
370 370 except Exception:
371 371 util.rename(repo.join('rebasestate'),
372 372 repo.join('unshelverebasestate'))
373 373 raise
374 374
375 375 lock = repo.lock()
376 376
377 377 mergefiles(ui, repo, state.wctx, state.pendingctx)
378 378
379 379 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
380 380 shelvedstate.clear(repo)
381 381 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
382 382 finally:
383 383 lockmod.release(lock, wlock)
384 384
385 385 def mergefiles(ui, repo, wctx, shelvectx):
386 386 """updates to wctx and merges the changes from shelvectx into the
387 387 dirstate."""
388 388 oldquiet = ui.quiet
389 389 try:
390 390 ui.quiet = True
391 391 hg.update(repo, wctx.node())
392 392 files = []
393 393 files.extend(shelvectx.files())
394 394 files.extend(shelvectx.parents()[0].files())
395 395
396 396 # revert will overwrite unknown files, so move them out of the way
397 397 m, a, r, d, u = repo.status(unknown=True)[:5]
398 398 for file in u:
399 399 if file in files:
400 400 util.rename(file, file + ".orig")
401 401 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
402 402 *pathtofiles(repo, files),
403 403 **{'no_backup': True})
404 404 finally:
405 405 ui.quiet = oldquiet
406 406
407 407 def unshelvecleanup(ui, repo, name, opts):
408 408 """remove related files after an unshelve"""
409 409 if not opts['keep']:
410 410 for filetype in 'hg files patch'.split():
411 411 shelvedfile(repo, name, filetype).unlink()
412 412
413 413 def unshelvecontinue(ui, repo, state, opts):
414 414 """subcommand to continue an in-progress unshelve"""
415 415 # We're finishing off a merge. First parent is our original
416 416 # parent, second is the temporary "fake" commit we're unshelving.
417 417 wlock = repo.wlock()
418 418 lock = None
419 419 try:
420 420 checkparents(repo, state)
421 421 ms = merge.mergestate(repo)
422 422 if [f for f in ms if ms[f] == 'u']:
423 423 raise util.Abort(
424 424 _("unresolved conflicts, can't continue"),
425 425 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
426 426
427 427 lock = repo.lock()
428 428
429 429 util.rename(repo.join('unshelverebasestate'),
430 430 repo.join('rebasestate'))
431 431 try:
432 432 rebase.rebase(ui, repo, **{
433 433 'continue' : True
434 434 })
435 435 except Exception:
436 436 util.rename(repo.join('rebasestate'),
437 437 repo.join('unshelverebasestate'))
438 438 raise
439 439
440 440 shelvectx = repo['tip']
441 441 if not shelvectx in state.pendingctx.children():
442 442 # rebase was a no-op, so it produced no child commit
443 443 shelvectx = state.pendingctx
444 444
445 445 mergefiles(ui, repo, state.wctx, shelvectx)
446 446
447 447 state.stripnodes.append(shelvectx.node())
448 448 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
449 449 shelvedstate.clear(repo)
450 450 unshelvecleanup(ui, repo, state.name, opts)
451 451 ui.status(_("unshelve of '%s' complete\n") % state.name)
452 452 finally:
453 453 lockmod.release(lock, wlock)
454 454
455 455 @command('unshelve',
456 456 [('a', 'abort', None,
457 457 _('abort an incomplete unshelve operation')),
458 458 ('c', 'continue', None,
459 459 _('continue an incomplete unshelve operation')),
460 460 ('', 'keep', None,
461 461 _('keep shelve after unshelving'))],
462 462 _('hg unshelve [SHELVED]'))
463 463 def unshelve(ui, repo, *shelved, **opts):
464 464 """restore a shelved change to the working directory
465 465
466 466 This command accepts an optional name of a shelved change to
467 467 restore. If none is given, the most recent shelved change is used.
468 468
469 469 If a shelved change is applied successfully, the bundle that
470 470 contains the shelved changes is deleted afterwards.
471 471
472 472 Since you can restore a shelved change on top of an arbitrary
473 473 commit, it is possible that unshelving will result in a conflict
474 474 between your changes and the commits you are unshelving onto. If
475 475 this occurs, you must resolve the conflict, then use
476 476 ``--continue`` to complete the unshelve operation. (The bundle
477 477 will not be deleted until you successfully complete the unshelve.)
478 478
479 479 (Alternatively, you can use ``--abort`` to abandon an unshelve
480 480 that causes a conflict. This reverts the unshelved changes, and
481 481 does not delete the bundle.)
482 482 """
483 483 abortf = opts['abort']
484 484 continuef = opts['continue']
485 485 if not abortf and not continuef:
486 486 cmdutil.checkunfinished(repo)
487 487
488 488 if abortf or continuef:
489 489 if abortf and continuef:
490 490 raise util.Abort(_('cannot use both abort and continue'))
491 491 if shelved:
492 492 raise util.Abort(_('cannot combine abort/continue with '
493 493 'naming a shelved change'))
494 494
495 495 try:
496 496 state = shelvedstate.load(repo)
497 497 except IOError, err:
498 498 if err.errno != errno.ENOENT:
499 499 raise
500 500 raise util.Abort(_('no unshelve operation underway'))
501 501
502 502 if abortf:
503 503 return unshelveabort(ui, repo, state, opts)
504 504 elif continuef:
505 505 return unshelvecontinue(ui, repo, state, opts)
506 506 elif len(shelved) > 1:
507 507 raise util.Abort(_('can only unshelve one change at a time'))
508 508 elif not shelved:
509 509 shelved = listshelves(repo)
510 510 if not shelved:
511 511 raise util.Abort(_('no shelved changes to apply!'))
512 512 basename = util.split(shelved[0][1])[1]
513 513 ui.status(_("unshelving change '%s'\n") % basename)
514 514 else:
515 515 basename = shelved[0]
516 516
517 517 if not shelvedfile(repo, basename, 'files').exists():
518 518 raise util.Abort(_("shelved change '%s' not found") % basename)
519 519
520 520 wlock = lock = tr = None
521 521 try:
522 522 lock = repo.lock()
523 523 wlock = repo.wlock()
524 524
525 525 tr = repo.transaction('unshelve', report=lambda x: None)
526 526 oldtiprev = len(repo)
527 527
528 528 wctx = repo['.']
529 529 tmpwctx = wctx
530 530 # The goal is to have a commit structure like so:
531 531 # ...-> wctx -> tmpwctx -> shelvectx
532 532 # where tmpwctx is an optional commit with the user's pending changes
533 533 # and shelvectx is the unshelved changes. Then we merge it all down
534 534 # to the original wctx.
535 535
536 536 # Store pending changes in a commit
537 537 m, a, r, d = repo.status()[:4]
538 538 if m or a or r or d:
539 539 def commitfunc(ui, repo, message, match, opts):
540 540 hasmq = util.safehasattr(repo, 'mq')
541 541 if hasmq:
542 542 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
543 543
544 544 try:
545 545 return repo.commit(message, 'shelve@localhost',
546 546 opts.get('date'), match)
547 547 finally:
548 548 if hasmq:
549 549 repo.mq.checkapplied = saved
550 550
551 551 tempopts = {}
552 552 tempopts['message'] = "pending changes temporary commit"
553 553 oldquiet = ui.quiet
554 554 try:
555 555 ui.quiet = True
556 556 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
557 557 finally:
558 558 ui.quiet = oldquiet
559 559 tmpwctx = repo[node]
560 560
561 561 try:
562 562 fp = shelvedfile(repo, basename, 'hg').opener()
563 563 gen = changegroup.readbundle(fp, fp.name)
564 564 repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
565 565 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
566 566 phases.retractboundary(repo, phases.secret, nodes)
567 567 finally:
568 568 fp.close()
569 569
570 570 shelvectx = repo['tip']
571 571
572 572 # If the shelve is not immediately on top of the commit
573 573 # we'll be merging with, rebase it to be on top.
574 574 if tmpwctx.node() != shelvectx.parents()[0].node():
575 575 try:
576 576 rebase.rebase(ui, repo, **{
577 577 'rev' : [shelvectx.rev()],
578 578 'dest' : str(tmpwctx.rev()),
579 579 'keep' : True,
580 580 })
581 581 except error.InterventionRequired:
582 582 tr.close()
583 583
584 584 stripnodes = [repo.changelog.node(rev)
585 585 for rev in xrange(oldtiprev, len(repo))]
586 586 shelvedstate.save(repo, basename, wctx, tmpwctx, stripnodes)
587 587
588 588 util.rename(repo.join('rebasestate'),
589 589 repo.join('unshelverebasestate'))
590 590 raise error.InterventionRequired(
591 591 _("unresolved conflicts (see 'hg resolve', then "
592 592 "'hg unshelve --continue')"))
593 593
594 594 # refresh ctx after rebase completes
595 595 shelvectx = repo['tip']
596 596
597 597 if not shelvectx in tmpwctx.children():
598 598 # rebase was a no-op, so it produced no child commit
599 599 shelvectx = tmpwctx
600 600
601 601 mergefiles(ui, repo, wctx, shelvectx)
602 602 shelvedstate.clear(repo)
603 603
604 604 # The transaction aborting will strip all the commits for us,
605 605 # but it doesn't update the inmemory structures, so addchangegroup
606 606 # hooks still fire and try to operate on the missing commits.
607 607 # Clean up manually to prevent this.
608 608 repo.unfiltered().changelog.strip(oldtiprev, tr)
609 609
610 610 unshelvecleanup(ui, repo, basename, opts)
611 611 finally:
612 612 if tr:
613 613 tr.release()
614 614 lockmod.release(lock, wlock)
615 615
616 616 @command('shelve',
617 617 [('A', 'addremove', None,
618 618 _('mark new/missing files as added/removed before shelving')),
619 619 ('', 'cleanup', None,
620 620 _('delete all shelved changes')),
621 621 ('', 'date', '',
622 622 _('shelve with the specified commit date'), _('DATE')),
623 623 ('d', 'delete', None,
624 624 _('delete the named shelved change(s)')),
625 625 ('l', 'list', None,
626 626 _('list current shelves')),
627 627 ('m', 'message', '',
628 628 _('use text as shelve message'), _('TEXT')),
629 629 ('n', 'name', '',
630 630 _('use the given name for the shelved commit'), _('NAME')),
631 631 ('p', 'patch', None,
632 632 _('show patch')),
633 633 ('', 'stat', None,
634 634 _('output diffstat-style summary of changes'))] + commands.walkopts,
635 _('hg shelve [OPTION]...'))
635 _('hg shelve [OPTION]... [FILE]...'))
636 636 def shelvecmd(ui, repo, *pats, **opts):
637 637 '''save and set aside changes from the working directory
638 638
639 639 Shelving takes files that "hg status" reports as not clean, saves
640 640 the modifications to a bundle (a shelved change), and reverts the
641 641 files so that their state in the working directory becomes clean.
642 642
643 643 To restore these changes to the working directory, using "hg
644 644 unshelve"; this will work even if you switch to a different
645 645 commit.
646 646
647 647 When no files are specified, "hg shelve" saves all not-clean
648 648 files. If specific files or directories are named, only changes to
649 649 those files are shelved.
650 650
651 651 Each shelved change has a name that makes it easier to find later.
652 652 The name of a shelved change defaults to being based on the active
653 653 bookmark, or if there is no active bookmark, the current named
654 654 branch. To specify a different name, use ``--name``.
655 655
656 656 To see a list of existing shelved changes, use the ``--list``
657 657 option. For each shelved change, this will print its name, age,
658 658 and description; use ``--patch`` or ``--stat`` for more details.
659 659
660 660 To delete specific shelved changes, use ``--delete``. To delete
661 661 all shelved changes, use ``--cleanup``.
662 662 '''
663 663 cmdutil.checkunfinished(repo)
664 664
665 665 def checkopt(opt, incompatible):
666 666 if opts[opt]:
667 667 for i in incompatible.split():
668 668 if opts[i]:
669 669 raise util.Abort(_("options '--%s' and '--%s' may not be "
670 670 "used together") % (opt, i))
671 671 return True
672 672 if checkopt('cleanup', 'addremove delete list message name patch stat'):
673 673 if pats:
674 674 raise util.Abort(_("cannot specify names when using '--cleanup'"))
675 675 return cleanupcmd(ui, repo)
676 676 elif checkopt('delete', 'addremove cleanup list message name patch stat'):
677 677 return deletecmd(ui, repo, pats)
678 678 elif checkopt('list', 'addremove cleanup delete message name'):
679 679 return listcmd(ui, repo, pats, opts)
680 680 else:
681 681 for i in ('patch', 'stat'):
682 682 if opts[i]:
683 683 raise util.Abort(_("option '--%s' may not be "
684 684 "used when shelving a change") % (i,))
685 685 return createcmd(ui, repo, pats, opts)
686 686
687 687 def extsetup(ui):
688 688 cmdutil.unfinishedstates.append(
689 689 [shelvedstate._filename, False, False,
690 690 _('unshelve already in progress'),
691 691 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
General Comments 0
You need to be logged in to leave comments. Login now