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