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