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