##// END OF EJS Templates
shelve: move ui.quiet manipulations to configoverride
Kostia Balytskyi -
r31757:473f2fcc default
parent child Browse files
Show More
@@ -1,1022 +1,1012
1 # shelve.py - save/restore working directory state
1 # shelve.py - save/restore working directory state
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """save and restore changes to the working directory
8 """save and restore changes to the working directory
9
9
10 The "hg shelve" command saves changes made to the working directory
10 The "hg shelve" command saves changes made to the working directory
11 and reverts those changes, resetting the working directory to a clean
11 and reverts those changes, resetting the working directory to a clean
12 state.
12 state.
13
13
14 Later on, the "hg unshelve" command restores the changes saved by "hg
14 Later on, the "hg unshelve" command restores the changes saved by "hg
15 shelve". Changes can be restored even after updating to a different
15 shelve". Changes can be restored even after updating to a different
16 parent, in which case Mercurial's merge machinery will resolve any
16 parent, in which case Mercurial's merge machinery will resolve any
17 conflicts if necessary.
17 conflicts if necessary.
18
18
19 You can have more than one shelved change outstanding at a time; each
19 You can have more than one shelved change outstanding at a time; each
20 shelved change has a distinct name. For details, see the help for "hg
20 shelved change has a distinct name. For details, see the help for "hg
21 shelve".
21 shelve".
22 """
22 """
23 from __future__ import absolute_import
23 from __future__ import absolute_import
24
24
25 import collections
25 import collections
26 import errno
26 import errno
27 import itertools
27 import itertools
28
28
29 from mercurial.i18n import _
29 from mercurial.i18n import _
30 from mercurial import (
30 from mercurial import (
31 bookmarks,
31 bookmarks,
32 bundle2,
32 bundle2,
33 bundlerepo,
33 bundlerepo,
34 changegroup,
34 changegroup,
35 cmdutil,
35 cmdutil,
36 commands,
36 commands,
37 error,
37 error,
38 exchange,
38 exchange,
39 hg,
39 hg,
40 lock as lockmod,
40 lock as lockmod,
41 mdiff,
41 mdiff,
42 merge,
42 merge,
43 node as nodemod,
43 node as nodemod,
44 patch,
44 patch,
45 phases,
45 phases,
46 repair,
46 repair,
47 scmutil,
47 scmutil,
48 templatefilters,
48 templatefilters,
49 util,
49 util,
50 vfs as vfsmod,
50 vfs as vfsmod,
51 )
51 )
52
52
53 from . import (
53 from . import (
54 rebase,
54 rebase,
55 )
55 )
56
56
57 cmdtable = {}
57 cmdtable = {}
58 command = cmdutil.command(cmdtable)
58 command = cmdutil.command(cmdtable)
59 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
59 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
60 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
60 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
61 # be specifying the version(s) of Mercurial they are tested with, or
61 # be specifying the version(s) of Mercurial they are tested with, or
62 # leave the attribute unspecified.
62 # leave the attribute unspecified.
63 testedwith = 'ships-with-hg-core'
63 testedwith = 'ships-with-hg-core'
64
64
65 backupdir = 'shelve-backup'
65 backupdir = 'shelve-backup'
66 shelvedir = 'shelved'
66 shelvedir = 'shelved'
67 shelvefileextensions = ['hg', 'patch', 'oshelve']
67 shelvefileextensions = ['hg', 'patch', 'oshelve']
68 # universal extension is present in all types of shelves
68 # universal extension is present in all types of shelves
69 patchextension = 'patch'
69 patchextension = 'patch'
70
70
71 # we never need the user, so we use a
71 # we never need the user, so we use a
72 # generic user for all shelve operations
72 # generic user for all shelve operations
73 shelveuser = 'shelve@localhost'
73 shelveuser = 'shelve@localhost'
74
74
75 class shelvedfile(object):
75 class shelvedfile(object):
76 """Helper for the file storing a single shelve
76 """Helper for the file storing a single shelve
77
77
78 Handles common functions on shelve files (.hg/.patch) using
78 Handles common functions on shelve files (.hg/.patch) using
79 the vfs layer"""
79 the vfs layer"""
80 def __init__(self, repo, name, filetype=None):
80 def __init__(self, repo, name, filetype=None):
81 self.repo = repo
81 self.repo = repo
82 self.name = name
82 self.name = name
83 self.vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
83 self.vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
84 self.backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
84 self.backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
85 self.ui = self.repo.ui
85 self.ui = self.repo.ui
86 if filetype:
86 if filetype:
87 self.fname = name + '.' + filetype
87 self.fname = name + '.' + filetype
88 else:
88 else:
89 self.fname = name
89 self.fname = name
90
90
91 def exists(self):
91 def exists(self):
92 return self.vfs.exists(self.fname)
92 return self.vfs.exists(self.fname)
93
93
94 def filename(self):
94 def filename(self):
95 return self.vfs.join(self.fname)
95 return self.vfs.join(self.fname)
96
96
97 def backupfilename(self):
97 def backupfilename(self):
98 def gennames(base):
98 def gennames(base):
99 yield base
99 yield base
100 base, ext = base.rsplit('.', 1)
100 base, ext = base.rsplit('.', 1)
101 for i in itertools.count(1):
101 for i in itertools.count(1):
102 yield '%s-%d.%s' % (base, i, ext)
102 yield '%s-%d.%s' % (base, i, ext)
103
103
104 name = self.backupvfs.join(self.fname)
104 name = self.backupvfs.join(self.fname)
105 for n in gennames(name):
105 for n in gennames(name):
106 if not self.backupvfs.exists(n):
106 if not self.backupvfs.exists(n):
107 return n
107 return n
108
108
109 def movetobackup(self):
109 def movetobackup(self):
110 if not self.backupvfs.isdir():
110 if not self.backupvfs.isdir():
111 self.backupvfs.makedir()
111 self.backupvfs.makedir()
112 util.rename(self.filename(), self.backupfilename())
112 util.rename(self.filename(), self.backupfilename())
113
113
114 def stat(self):
114 def stat(self):
115 return self.vfs.stat(self.fname)
115 return self.vfs.stat(self.fname)
116
116
117 def opener(self, mode='rb'):
117 def opener(self, mode='rb'):
118 try:
118 try:
119 return self.vfs(self.fname, mode)
119 return self.vfs(self.fname, mode)
120 except IOError as err:
120 except IOError as err:
121 if err.errno != errno.ENOENT:
121 if err.errno != errno.ENOENT:
122 raise
122 raise
123 raise error.Abort(_("shelved change '%s' not found") % self.name)
123 raise error.Abort(_("shelved change '%s' not found") % self.name)
124
124
125 def applybundle(self):
125 def applybundle(self):
126 fp = self.opener()
126 fp = self.opener()
127 try:
127 try:
128 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
128 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
129 if not isinstance(gen, bundle2.unbundle20):
129 if not isinstance(gen, bundle2.unbundle20):
130 gen.apply(self.repo, 'unshelve',
130 gen.apply(self.repo, 'unshelve',
131 'bundle:' + self.vfs.join(self.fname),
131 'bundle:' + self.vfs.join(self.fname),
132 targetphase=phases.secret)
132 targetphase=phases.secret)
133 if isinstance(gen, bundle2.unbundle20):
133 if isinstance(gen, bundle2.unbundle20):
134 bundle2.applybundle(self.repo, gen,
134 bundle2.applybundle(self.repo, gen,
135 self.repo.currenttransaction(),
135 self.repo.currenttransaction(),
136 source='unshelve',
136 source='unshelve',
137 url='bundle:' + self.vfs.join(self.fname))
137 url='bundle:' + self.vfs.join(self.fname))
138 finally:
138 finally:
139 fp.close()
139 fp.close()
140
140
141 def bundlerepo(self):
141 def bundlerepo(self):
142 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
142 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
143 self.vfs.join(self.fname))
143 self.vfs.join(self.fname))
144 def writebundle(self, bases, node):
144 def writebundle(self, bases, node):
145 cgversion = changegroup.safeversion(self.repo)
145 cgversion = changegroup.safeversion(self.repo)
146 if cgversion == '01':
146 if cgversion == '01':
147 btype = 'HG10BZ'
147 btype = 'HG10BZ'
148 compression = None
148 compression = None
149 else:
149 else:
150 btype = 'HG20'
150 btype = 'HG20'
151 compression = 'BZ'
151 compression = 'BZ'
152
152
153 cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
153 cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
154 version=cgversion)
154 version=cgversion)
155 bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs,
155 bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs,
156 compression=compression)
156 compression=compression)
157
157
158 def writeobsshelveinfo(self, info):
158 def writeobsshelveinfo(self, info):
159 scmutil.simplekeyvaluefile(self.vfs, self.fname).write(info)
159 scmutil.simplekeyvaluefile(self.vfs, self.fname).write(info)
160
160
161 def readobsshelveinfo(self):
161 def readobsshelveinfo(self):
162 return scmutil.simplekeyvaluefile(self.vfs, self.fname).read()
162 return scmutil.simplekeyvaluefile(self.vfs, self.fname).read()
163
163
164 class shelvedstate(object):
164 class shelvedstate(object):
165 """Handle persistence during unshelving operations.
165 """Handle persistence during unshelving operations.
166
166
167 Handles saving and restoring a shelved state. Ensures that different
167 Handles saving and restoring a shelved state. Ensures that different
168 versions of a shelved state are possible and handles them appropriately.
168 versions of a shelved state are possible and handles them appropriately.
169 """
169 """
170 _version = 1
170 _version = 1
171 _filename = 'shelvedstate'
171 _filename = 'shelvedstate'
172 _keep = 'keep'
172 _keep = 'keep'
173 _nokeep = 'nokeep'
173 _nokeep = 'nokeep'
174 # colon is essential to differentiate from a real bookmark name
174 # colon is essential to differentiate from a real bookmark name
175 _noactivebook = ':no-active-bookmark'
175 _noactivebook = ':no-active-bookmark'
176
176
177 @classmethod
177 @classmethod
178 def load(cls, repo):
178 def load(cls, repo):
179 fp = repo.vfs(cls._filename)
179 fp = repo.vfs(cls._filename)
180 try:
180 try:
181 version = int(fp.readline().strip())
181 version = int(fp.readline().strip())
182
182
183 if version != cls._version:
183 if version != cls._version:
184 raise error.Abort(_('this version of shelve is incompatible '
184 raise error.Abort(_('this version of shelve is incompatible '
185 'with the version used in this repo'))
185 'with the version used in this repo'))
186 name = fp.readline().strip()
186 name = fp.readline().strip()
187 wctx = nodemod.bin(fp.readline().strip())
187 wctx = nodemod.bin(fp.readline().strip())
188 pendingctx = nodemod.bin(fp.readline().strip())
188 pendingctx = nodemod.bin(fp.readline().strip())
189 parents = [nodemod.bin(h) for h in fp.readline().split()]
189 parents = [nodemod.bin(h) for h in fp.readline().split()]
190 nodestoprune = [nodemod.bin(h) for h in fp.readline().split()]
190 nodestoprune = [nodemod.bin(h) for h in fp.readline().split()]
191 branchtorestore = fp.readline().strip()
191 branchtorestore = fp.readline().strip()
192 keep = fp.readline().strip() == cls._keep
192 keep = fp.readline().strip() == cls._keep
193 activebook = fp.readline().strip()
193 activebook = fp.readline().strip()
194 except (ValueError, TypeError) as err:
194 except (ValueError, TypeError) as err:
195 raise error.CorruptedState(str(err))
195 raise error.CorruptedState(str(err))
196 finally:
196 finally:
197 fp.close()
197 fp.close()
198
198
199 try:
199 try:
200 obj = cls()
200 obj = cls()
201 obj.name = name
201 obj.name = name
202 obj.wctx = repo[wctx]
202 obj.wctx = repo[wctx]
203 obj.pendingctx = repo[pendingctx]
203 obj.pendingctx = repo[pendingctx]
204 obj.parents = parents
204 obj.parents = parents
205 obj.nodestoprune = nodestoprune
205 obj.nodestoprune = nodestoprune
206 obj.branchtorestore = branchtorestore
206 obj.branchtorestore = branchtorestore
207 obj.keep = keep
207 obj.keep = keep
208 obj.activebookmark = ''
208 obj.activebookmark = ''
209 if activebook != cls._noactivebook:
209 if activebook != cls._noactivebook:
210 obj.activebookmark = activebook
210 obj.activebookmark = activebook
211 except error.RepoLookupError as err:
211 except error.RepoLookupError as err:
212 raise error.CorruptedState(str(err))
212 raise error.CorruptedState(str(err))
213
213
214 return obj
214 return obj
215
215
216 @classmethod
216 @classmethod
217 def save(cls, repo, name, originalwctx, pendingctx, nodestoprune,
217 def save(cls, repo, name, originalwctx, pendingctx, nodestoprune,
218 branchtorestore, keep=False, activebook=''):
218 branchtorestore, keep=False, activebook=''):
219 fp = repo.vfs(cls._filename, 'wb')
219 fp = repo.vfs(cls._filename, 'wb')
220 fp.write('%i\n' % cls._version)
220 fp.write('%i\n' % cls._version)
221 fp.write('%s\n' % name)
221 fp.write('%s\n' % name)
222 fp.write('%s\n' % nodemod.hex(originalwctx.node()))
222 fp.write('%s\n' % nodemod.hex(originalwctx.node()))
223 fp.write('%s\n' % nodemod.hex(pendingctx.node()))
223 fp.write('%s\n' % nodemod.hex(pendingctx.node()))
224 fp.write('%s\n' %
224 fp.write('%s\n' %
225 ' '.join([nodemod.hex(p) for p in repo.dirstate.parents()]))
225 ' '.join([nodemod.hex(p) for p in repo.dirstate.parents()]))
226 fp.write('%s\n' %
226 fp.write('%s\n' %
227 ' '.join([nodemod.hex(n) for n in nodestoprune]))
227 ' '.join([nodemod.hex(n) for n in nodestoprune]))
228 fp.write('%s\n' % branchtorestore)
228 fp.write('%s\n' % branchtorestore)
229 fp.write('%s\n' % (cls._keep if keep else cls._nokeep))
229 fp.write('%s\n' % (cls._keep if keep else cls._nokeep))
230 fp.write('%s\n' % (activebook or cls._noactivebook))
230 fp.write('%s\n' % (activebook or cls._noactivebook))
231 fp.close()
231 fp.close()
232
232
233 @classmethod
233 @classmethod
234 def clear(cls, repo):
234 def clear(cls, repo):
235 repo.vfs.unlinkpath(cls._filename, ignoremissing=True)
235 repo.vfs.unlinkpath(cls._filename, ignoremissing=True)
236
236
237 def cleanupoldbackups(repo):
237 def cleanupoldbackups(repo):
238 vfs = vfsmod.vfs(repo.vfs.join(backupdir))
238 vfs = vfsmod.vfs(repo.vfs.join(backupdir))
239 maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
239 maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
240 hgfiles = [f for f in vfs.listdir()
240 hgfiles = [f for f in vfs.listdir()
241 if f.endswith('.' + patchextension)]
241 if f.endswith('.' + patchextension)]
242 hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
242 hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
243 if 0 < maxbackups and maxbackups < len(hgfiles):
243 if 0 < maxbackups and maxbackups < len(hgfiles):
244 bordermtime = hgfiles[-maxbackups][0]
244 bordermtime = hgfiles[-maxbackups][0]
245 else:
245 else:
246 bordermtime = None
246 bordermtime = None
247 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
247 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
248 if mtime == bordermtime:
248 if mtime == bordermtime:
249 # keep it, because timestamp can't decide exact order of backups
249 # keep it, because timestamp can't decide exact order of backups
250 continue
250 continue
251 base = f[:-(1 + len(patchextension))]
251 base = f[:-(1 + len(patchextension))]
252 for ext in shelvefileextensions:
252 for ext in shelvefileextensions:
253 vfs.tryunlink(base + '.' + ext)
253 vfs.tryunlink(base + '.' + ext)
254
254
255 def _backupactivebookmark(repo):
255 def _backupactivebookmark(repo):
256 activebookmark = repo._activebookmark
256 activebookmark = repo._activebookmark
257 if activebookmark:
257 if activebookmark:
258 bookmarks.deactivate(repo)
258 bookmarks.deactivate(repo)
259 return activebookmark
259 return activebookmark
260
260
261 def _restoreactivebookmark(repo, mark):
261 def _restoreactivebookmark(repo, mark):
262 if mark:
262 if mark:
263 bookmarks.activate(repo, mark)
263 bookmarks.activate(repo, mark)
264
264
265 def _aborttransaction(repo):
265 def _aborttransaction(repo):
266 '''Abort current transaction for shelve/unshelve, but keep dirstate
266 '''Abort current transaction for shelve/unshelve, but keep dirstate
267 '''
267 '''
268 tr = repo.currenttransaction()
268 tr = repo.currenttransaction()
269 repo.dirstate.savebackup(tr, suffix='.shelve')
269 repo.dirstate.savebackup(tr, suffix='.shelve')
270 tr.abort()
270 tr.abort()
271 repo.dirstate.restorebackup(None, suffix='.shelve')
271 repo.dirstate.restorebackup(None, suffix='.shelve')
272
272
273 def createcmd(ui, repo, pats, opts):
273 def createcmd(ui, repo, pats, opts):
274 """subcommand that creates a new shelve"""
274 """subcommand that creates a new shelve"""
275 with repo.wlock():
275 with repo.wlock():
276 cmdutil.checkunfinished(repo)
276 cmdutil.checkunfinished(repo)
277 return _docreatecmd(ui, repo, pats, opts)
277 return _docreatecmd(ui, repo, pats, opts)
278
278
279 def getshelvename(repo, parent, opts):
279 def getshelvename(repo, parent, opts):
280 """Decide on the name this shelve is going to have"""
280 """Decide on the name this shelve is going to have"""
281 def gennames():
281 def gennames():
282 yield label
282 yield label
283 for i in xrange(1, 100):
283 for i in xrange(1, 100):
284 yield '%s-%02d' % (label, i)
284 yield '%s-%02d' % (label, i)
285 name = opts.get('name')
285 name = opts.get('name')
286 label = repo._activebookmark or parent.branch() or 'default'
286 label = repo._activebookmark or parent.branch() or 'default'
287 # slashes aren't allowed in filenames, therefore we rename it
287 # slashes aren't allowed in filenames, therefore we rename it
288 label = label.replace('/', '_')
288 label = label.replace('/', '_')
289 label = label.replace('\\', '_')
289 label = label.replace('\\', '_')
290 # filenames must not start with '.' as it should not be hidden
290 # filenames must not start with '.' as it should not be hidden
291 if label.startswith('.'):
291 if label.startswith('.'):
292 label = label.replace('.', '_', 1)
292 label = label.replace('.', '_', 1)
293
293
294 if name:
294 if name:
295 if shelvedfile(repo, name, patchextension).exists():
295 if shelvedfile(repo, name, patchextension).exists():
296 e = _("a shelved change named '%s' already exists") % name
296 e = _("a shelved change named '%s' already exists") % name
297 raise error.Abort(e)
297 raise error.Abort(e)
298
298
299 # ensure we are not creating a subdirectory or a hidden file
299 # ensure we are not creating a subdirectory or a hidden file
300 if '/' in name or '\\' in name:
300 if '/' in name or '\\' in name:
301 raise error.Abort(_('shelved change names can not contain slashes'))
301 raise error.Abort(_('shelved change names can not contain slashes'))
302 if name.startswith('.'):
302 if name.startswith('.'):
303 raise error.Abort(_("shelved change names can not start with '.'"))
303 raise error.Abort(_("shelved change names can not start with '.'"))
304
304
305 else:
305 else:
306 for n in gennames():
306 for n in gennames():
307 if not shelvedfile(repo, n, patchextension).exists():
307 if not shelvedfile(repo, n, patchextension).exists():
308 name = n
308 name = n
309 break
309 break
310 else:
310 else:
311 raise error.Abort(_("too many shelved changes named '%s'") % label)
311 raise error.Abort(_("too many shelved changes named '%s'") % label)
312
312
313 return name
313 return name
314
314
315 def mutableancestors(ctx):
315 def mutableancestors(ctx):
316 """return all mutable ancestors for ctx (included)
316 """return all mutable ancestors for ctx (included)
317
317
318 Much faster than the revset ancestors(ctx) & draft()"""
318 Much faster than the revset ancestors(ctx) & draft()"""
319 seen = set([nodemod.nullrev])
319 seen = set([nodemod.nullrev])
320 visit = collections.deque()
320 visit = collections.deque()
321 visit.append(ctx)
321 visit.append(ctx)
322 while visit:
322 while visit:
323 ctx = visit.popleft()
323 ctx = visit.popleft()
324 yield ctx.node()
324 yield ctx.node()
325 for parent in ctx.parents():
325 for parent in ctx.parents():
326 rev = parent.rev()
326 rev = parent.rev()
327 if rev not in seen:
327 if rev not in seen:
328 seen.add(rev)
328 seen.add(rev)
329 if parent.mutable():
329 if parent.mutable():
330 visit.append(parent)
330 visit.append(parent)
331
331
332 def getcommitfunc(extra, interactive, editor=False):
332 def getcommitfunc(extra, interactive, editor=False):
333 def commitfunc(ui, repo, message, match, opts):
333 def commitfunc(ui, repo, message, match, opts):
334 hasmq = util.safehasattr(repo, 'mq')
334 hasmq = util.safehasattr(repo, 'mq')
335 if hasmq:
335 if hasmq:
336 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
336 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
337 overrides = {('phases', 'new-commit'): phases.secret}
337 overrides = {('phases', 'new-commit'): phases.secret}
338 try:
338 try:
339 editor_ = False
339 editor_ = False
340 if editor:
340 if editor:
341 editor_ = cmdutil.getcommiteditor(editform='shelve.shelve',
341 editor_ = cmdutil.getcommiteditor(editform='shelve.shelve',
342 **opts)
342 **opts)
343 with repo.ui.configoverride(overrides):
343 with repo.ui.configoverride(overrides):
344 return repo.commit(message, shelveuser, opts.get('date'),
344 return repo.commit(message, shelveuser, opts.get('date'),
345 match, editor=editor_, extra=extra)
345 match, editor=editor_, extra=extra)
346 finally:
346 finally:
347 if hasmq:
347 if hasmq:
348 repo.mq.checkapplied = saved
348 repo.mq.checkapplied = saved
349
349
350 def interactivecommitfunc(ui, repo, *pats, **opts):
350 def interactivecommitfunc(ui, repo, *pats, **opts):
351 match = scmutil.match(repo['.'], pats, {})
351 match = scmutil.match(repo['.'], pats, {})
352 message = opts['message']
352 message = opts['message']
353 return commitfunc(ui, repo, message, match, opts)
353 return commitfunc(ui, repo, message, match, opts)
354
354
355 return interactivecommitfunc if interactive else commitfunc
355 return interactivecommitfunc if interactive else commitfunc
356
356
357 def _nothingtoshelvemessaging(ui, repo, pats, opts):
357 def _nothingtoshelvemessaging(ui, repo, pats, opts):
358 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
358 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
359 if stat.deleted:
359 if stat.deleted:
360 ui.status(_("nothing changed (%d missing files, see "
360 ui.status(_("nothing changed (%d missing files, see "
361 "'hg status')\n") % len(stat.deleted))
361 "'hg status')\n") % len(stat.deleted))
362 else:
362 else:
363 ui.status(_("nothing changed\n"))
363 ui.status(_("nothing changed\n"))
364
364
365 def _shelvecreatedcommit(repo, node, name):
365 def _shelvecreatedcommit(repo, node, name):
366 bases = list(mutableancestors(repo[node]))
366 bases = list(mutableancestors(repo[node]))
367 shelvedfile(repo, name, 'hg').writebundle(bases, node)
367 shelvedfile(repo, name, 'hg').writebundle(bases, node)
368 cmdutil.export(repo, [node],
368 cmdutil.export(repo, [node],
369 fp=shelvedfile(repo, name, patchextension).opener('wb'),
369 fp=shelvedfile(repo, name, patchextension).opener('wb'),
370 opts=mdiff.diffopts(git=True))
370 opts=mdiff.diffopts(git=True))
371
371
372 def _includeunknownfiles(repo, pats, opts, extra):
372 def _includeunknownfiles(repo, pats, opts, extra):
373 s = repo.status(match=scmutil.match(repo[None], pats, opts),
373 s = repo.status(match=scmutil.match(repo[None], pats, opts),
374 unknown=True)
374 unknown=True)
375 if s.unknown:
375 if s.unknown:
376 extra['shelve_unknown'] = '\0'.join(s.unknown)
376 extra['shelve_unknown'] = '\0'.join(s.unknown)
377 repo[None].add(s.unknown)
377 repo[None].add(s.unknown)
378
378
379 def _finishshelve(repo):
379 def _finishshelve(repo):
380 _aborttransaction(repo)
380 _aborttransaction(repo)
381
381
382 def _docreatecmd(ui, repo, pats, opts):
382 def _docreatecmd(ui, repo, pats, opts):
383 wctx = repo[None]
383 wctx = repo[None]
384 parents = wctx.parents()
384 parents = wctx.parents()
385 if len(parents) > 1:
385 if len(parents) > 1:
386 raise error.Abort(_('cannot shelve while merging'))
386 raise error.Abort(_('cannot shelve while merging'))
387 parent = parents[0]
387 parent = parents[0]
388 origbranch = wctx.branch()
388 origbranch = wctx.branch()
389
389
390 if parent.node() != nodemod.nullid:
390 if parent.node() != nodemod.nullid:
391 desc = "changes to: %s" % parent.description().split('\n', 1)[0]
391 desc = "changes to: %s" % parent.description().split('\n', 1)[0]
392 else:
392 else:
393 desc = '(changes in empty repository)'
393 desc = '(changes in empty repository)'
394
394
395 if not opts.get('message'):
395 if not opts.get('message'):
396 opts['message'] = desc
396 opts['message'] = desc
397
397
398 lock = tr = activebookmark = None
398 lock = tr = activebookmark = None
399 try:
399 try:
400 lock = repo.lock()
400 lock = repo.lock()
401
401
402 # use an uncommitted transaction to generate the bundle to avoid
402 # use an uncommitted transaction to generate the bundle to avoid
403 # pull races. ensure we don't print the abort message to stderr.
403 # pull races. ensure we don't print the abort message to stderr.
404 tr = repo.transaction('commit', report=lambda x: None)
404 tr = repo.transaction('commit', report=lambda x: None)
405
405
406 interactive = opts.get('interactive', False)
406 interactive = opts.get('interactive', False)
407 includeunknown = (opts.get('unknown', False) and
407 includeunknown = (opts.get('unknown', False) and
408 not opts.get('addremove', False))
408 not opts.get('addremove', False))
409
409
410 name = getshelvename(repo, parent, opts)
410 name = getshelvename(repo, parent, opts)
411 activebookmark = _backupactivebookmark(repo)
411 activebookmark = _backupactivebookmark(repo)
412 extra = {}
412 extra = {}
413 if includeunknown:
413 if includeunknown:
414 _includeunknownfiles(repo, pats, opts, extra)
414 _includeunknownfiles(repo, pats, opts, extra)
415
415
416 if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
416 if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
417 # In non-bare shelve we don't store newly created branch
417 # In non-bare shelve we don't store newly created branch
418 # at bundled commit
418 # at bundled commit
419 repo.dirstate.setbranch(repo['.'].branch())
419 repo.dirstate.setbranch(repo['.'].branch())
420
420
421 commitfunc = getcommitfunc(extra, interactive, editor=True)
421 commitfunc = getcommitfunc(extra, interactive, editor=True)
422 if not interactive:
422 if not interactive:
423 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
423 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
424 else:
424 else:
425 node = cmdutil.dorecord(ui, repo, commitfunc, None,
425 node = cmdutil.dorecord(ui, repo, commitfunc, None,
426 False, cmdutil.recordfilter, *pats,
426 False, cmdutil.recordfilter, *pats,
427 **opts)
427 **opts)
428 if not node:
428 if not node:
429 _nothingtoshelvemessaging(ui, repo, pats, opts)
429 _nothingtoshelvemessaging(ui, repo, pats, opts)
430 return 1
430 return 1
431
431
432 _shelvecreatedcommit(repo, node, name)
432 _shelvecreatedcommit(repo, node, name)
433
433
434 if ui.formatted():
434 if ui.formatted():
435 desc = util.ellipsis(desc, ui.termwidth())
435 desc = util.ellipsis(desc, ui.termwidth())
436 ui.status(_('shelved as %s\n') % name)
436 ui.status(_('shelved as %s\n') % name)
437 hg.update(repo, parent.node())
437 hg.update(repo, parent.node())
438 if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
438 if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
439 repo.dirstate.setbranch(origbranch)
439 repo.dirstate.setbranch(origbranch)
440
440
441 _finishshelve(repo)
441 _finishshelve(repo)
442 finally:
442 finally:
443 _restoreactivebookmark(repo, activebookmark)
443 _restoreactivebookmark(repo, activebookmark)
444 lockmod.release(tr, lock)
444 lockmod.release(tr, lock)
445
445
446 def _isbareshelve(pats, opts):
446 def _isbareshelve(pats, opts):
447 return (not pats
447 return (not pats
448 and not opts.get('interactive', False)
448 and not opts.get('interactive', False)
449 and not opts.get('include', False)
449 and not opts.get('include', False)
450 and not opts.get('exclude', False))
450 and not opts.get('exclude', False))
451
451
452 def _iswctxonnewbranch(repo):
452 def _iswctxonnewbranch(repo):
453 return repo[None].branch() != repo['.'].branch()
453 return repo[None].branch() != repo['.'].branch()
454
454
455 def cleanupcmd(ui, repo):
455 def cleanupcmd(ui, repo):
456 """subcommand that deletes all shelves"""
456 """subcommand that deletes all shelves"""
457
457
458 with repo.wlock():
458 with repo.wlock():
459 for (name, _type) in repo.vfs.readdir(shelvedir):
459 for (name, _type) in repo.vfs.readdir(shelvedir):
460 suffix = name.rsplit('.', 1)[-1]
460 suffix = name.rsplit('.', 1)[-1]
461 if suffix in shelvefileextensions:
461 if suffix in shelvefileextensions:
462 shelvedfile(repo, name).movetobackup()
462 shelvedfile(repo, name).movetobackup()
463 cleanupoldbackups(repo)
463 cleanupoldbackups(repo)
464
464
465 def deletecmd(ui, repo, pats):
465 def deletecmd(ui, repo, pats):
466 """subcommand that deletes a specific shelve"""
466 """subcommand that deletes a specific shelve"""
467 if not pats:
467 if not pats:
468 raise error.Abort(_('no shelved changes specified!'))
468 raise error.Abort(_('no shelved changes specified!'))
469 with repo.wlock():
469 with repo.wlock():
470 try:
470 try:
471 for name in pats:
471 for name in pats:
472 for suffix in shelvefileextensions:
472 for suffix in shelvefileextensions:
473 shfile = shelvedfile(repo, name, suffix)
473 shfile = shelvedfile(repo, name, suffix)
474 # patch file is necessary, as it should
474 # patch file is necessary, as it should
475 # be present for any kind of shelve,
475 # be present for any kind of shelve,
476 # but the .hg file is optional as in future we
476 # but the .hg file is optional as in future we
477 # will add obsolete shelve with does not create a
477 # will add obsolete shelve with does not create a
478 # bundle
478 # bundle
479 if shfile.exists() or suffix == patchextension:
479 if shfile.exists() or suffix == patchextension:
480 shfile.movetobackup()
480 shfile.movetobackup()
481 cleanupoldbackups(repo)
481 cleanupoldbackups(repo)
482 except OSError as err:
482 except OSError as err:
483 if err.errno != errno.ENOENT:
483 if err.errno != errno.ENOENT:
484 raise
484 raise
485 raise error.Abort(_("shelved change '%s' not found") % name)
485 raise error.Abort(_("shelved change '%s' not found") % name)
486
486
487 def listshelves(repo):
487 def listshelves(repo):
488 """return all shelves in repo as list of (time, filename)"""
488 """return all shelves in repo as list of (time, filename)"""
489 try:
489 try:
490 names = repo.vfs.readdir(shelvedir)
490 names = repo.vfs.readdir(shelvedir)
491 except OSError as err:
491 except OSError as err:
492 if err.errno != errno.ENOENT:
492 if err.errno != errno.ENOENT:
493 raise
493 raise
494 return []
494 return []
495 info = []
495 info = []
496 for (name, _type) in names:
496 for (name, _type) in names:
497 pfx, sfx = name.rsplit('.', 1)
497 pfx, sfx = name.rsplit('.', 1)
498 if not pfx or sfx != patchextension:
498 if not pfx or sfx != patchextension:
499 continue
499 continue
500 st = shelvedfile(repo, name).stat()
500 st = shelvedfile(repo, name).stat()
501 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
501 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
502 return sorted(info, reverse=True)
502 return sorted(info, reverse=True)
503
503
504 def listcmd(ui, repo, pats, opts):
504 def listcmd(ui, repo, pats, opts):
505 """subcommand that displays the list of shelves"""
505 """subcommand that displays the list of shelves"""
506 pats = set(pats)
506 pats = set(pats)
507 width = 80
507 width = 80
508 if not ui.plain():
508 if not ui.plain():
509 width = ui.termwidth()
509 width = ui.termwidth()
510 namelabel = 'shelve.newest'
510 namelabel = 'shelve.newest'
511 ui.pager('shelve')
511 ui.pager('shelve')
512 for mtime, name in listshelves(repo):
512 for mtime, name in listshelves(repo):
513 sname = util.split(name)[1]
513 sname = util.split(name)[1]
514 if pats and sname not in pats:
514 if pats and sname not in pats:
515 continue
515 continue
516 ui.write(sname, label=namelabel)
516 ui.write(sname, label=namelabel)
517 namelabel = 'shelve.name'
517 namelabel = 'shelve.name'
518 if ui.quiet:
518 if ui.quiet:
519 ui.write('\n')
519 ui.write('\n')
520 continue
520 continue
521 ui.write(' ' * (16 - len(sname)))
521 ui.write(' ' * (16 - len(sname)))
522 used = 16
522 used = 16
523 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
523 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
524 ui.write(age, label='shelve.age')
524 ui.write(age, label='shelve.age')
525 ui.write(' ' * (12 - len(age)))
525 ui.write(' ' * (12 - len(age)))
526 used += 12
526 used += 12
527 with open(name + '.' + patchextension, 'rb') as fp:
527 with open(name + '.' + patchextension, 'rb') as fp:
528 while True:
528 while True:
529 line = fp.readline()
529 line = fp.readline()
530 if not line:
530 if not line:
531 break
531 break
532 if not line.startswith('#'):
532 if not line.startswith('#'):
533 desc = line.rstrip()
533 desc = line.rstrip()
534 if ui.formatted():
534 if ui.formatted():
535 desc = util.ellipsis(desc, width - used)
535 desc = util.ellipsis(desc, width - used)
536 ui.write(desc)
536 ui.write(desc)
537 break
537 break
538 ui.write('\n')
538 ui.write('\n')
539 if not (opts['patch'] or opts['stat']):
539 if not (opts['patch'] or opts['stat']):
540 continue
540 continue
541 difflines = fp.readlines()
541 difflines = fp.readlines()
542 if opts['patch']:
542 if opts['patch']:
543 for chunk, label in patch.difflabel(iter, difflines):
543 for chunk, label in patch.difflabel(iter, difflines):
544 ui.write(chunk, label=label)
544 ui.write(chunk, label=label)
545 if opts['stat']:
545 if opts['stat']:
546 for chunk, label in patch.diffstatui(difflines, width=width):
546 for chunk, label in patch.diffstatui(difflines, width=width):
547 ui.write(chunk, label=label)
547 ui.write(chunk, label=label)
548
548
549 def patchcmds(ui, repo, pats, opts, subcommand):
549 def patchcmds(ui, repo, pats, opts, subcommand):
550 """subcommand that displays shelves"""
550 """subcommand that displays shelves"""
551 if len(pats) == 0:
551 if len(pats) == 0:
552 raise error.Abort(_("--%s expects at least one shelf") % subcommand)
552 raise error.Abort(_("--%s expects at least one shelf") % subcommand)
553
553
554 for shelfname in pats:
554 for shelfname in pats:
555 if not shelvedfile(repo, shelfname, patchextension).exists():
555 if not shelvedfile(repo, shelfname, patchextension).exists():
556 raise error.Abort(_("cannot find shelf %s") % shelfname)
556 raise error.Abort(_("cannot find shelf %s") % shelfname)
557
557
558 listcmd(ui, repo, pats, opts)
558 listcmd(ui, repo, pats, opts)
559
559
560 def checkparents(repo, state):
560 def checkparents(repo, state):
561 """check parent while resuming an unshelve"""
561 """check parent while resuming an unshelve"""
562 if state.parents != repo.dirstate.parents():
562 if state.parents != repo.dirstate.parents():
563 raise error.Abort(_('working directory parents do not match unshelve '
563 raise error.Abort(_('working directory parents do not match unshelve '
564 'state'))
564 'state'))
565
565
566 def pathtofiles(repo, files):
566 def pathtofiles(repo, files):
567 cwd = repo.getcwd()
567 cwd = repo.getcwd()
568 return [repo.pathto(f, cwd) for f in files]
568 return [repo.pathto(f, cwd) for f in files]
569
569
570 def unshelveabort(ui, repo, state, opts):
570 def unshelveabort(ui, repo, state, opts):
571 """subcommand that abort an in-progress unshelve"""
571 """subcommand that abort an in-progress unshelve"""
572 with repo.lock():
572 with repo.lock():
573 try:
573 try:
574 checkparents(repo, state)
574 checkparents(repo, state)
575
575
576 repo.vfs.rename('unshelverebasestate', 'rebasestate')
576 repo.vfs.rename('unshelverebasestate', 'rebasestate')
577 try:
577 try:
578 rebase.rebase(ui, repo, **{
578 rebase.rebase(ui, repo, **{
579 'abort' : True
579 'abort' : True
580 })
580 })
581 except Exception:
581 except Exception:
582 repo.vfs.rename('rebasestate', 'unshelverebasestate')
582 repo.vfs.rename('rebasestate', 'unshelverebasestate')
583 raise
583 raise
584
584
585 mergefiles(ui, repo, state.wctx, state.pendingctx)
585 mergefiles(ui, repo, state.wctx, state.pendingctx)
586 repair.strip(ui, repo, state.nodestoprune, backup=False,
586 repair.strip(ui, repo, state.nodestoprune, backup=False,
587 topic='shelve')
587 topic='shelve')
588 finally:
588 finally:
589 shelvedstate.clear(repo)
589 shelvedstate.clear(repo)
590 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
590 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
591
591
592 def mergefiles(ui, repo, wctx, shelvectx):
592 def mergefiles(ui, repo, wctx, shelvectx):
593 """updates to wctx and merges the changes from shelvectx into the
593 """updates to wctx and merges the changes from shelvectx into the
594 dirstate."""
594 dirstate."""
595 oldquiet = ui.quiet
595 with ui.configoverride({('ui', 'quiet'): True}):
596 try:
597 ui.quiet = True
598 hg.update(repo, wctx.node())
596 hg.update(repo, wctx.node())
599 files = []
597 files = []
600 files.extend(shelvectx.files())
598 files.extend(shelvectx.files())
601 files.extend(shelvectx.parents()[0].files())
599 files.extend(shelvectx.parents()[0].files())
602
600
603 # revert will overwrite unknown files, so move them out of the way
601 # revert will overwrite unknown files, so move them out of the way
604 for file in repo.status(unknown=True).unknown:
602 for file in repo.status(unknown=True).unknown:
605 if file in files:
603 if file in files:
606 util.rename(file, scmutil.origpath(ui, repo, file))
604 util.rename(file, scmutil.origpath(ui, repo, file))
607 ui.pushbuffer(True)
605 ui.pushbuffer(True)
608 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
606 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
609 *pathtofiles(repo, files),
607 *pathtofiles(repo, files),
610 **{'no_backup': True})
608 **{'no_backup': True})
611 ui.popbuffer()
609 ui.popbuffer()
612 finally:
613 ui.quiet = oldquiet
614
610
615 def restorebranch(ui, repo, branchtorestore):
611 def restorebranch(ui, repo, branchtorestore):
616 if branchtorestore and branchtorestore != repo.dirstate.branch():
612 if branchtorestore and branchtorestore != repo.dirstate.branch():
617 repo.dirstate.setbranch(branchtorestore)
613 repo.dirstate.setbranch(branchtorestore)
618 ui.status(_('marked working directory as branch %s\n')
614 ui.status(_('marked working directory as branch %s\n')
619 % branchtorestore)
615 % branchtorestore)
620
616
621 def unshelvecleanup(ui, repo, name, opts):
617 def unshelvecleanup(ui, repo, name, opts):
622 """remove related files after an unshelve"""
618 """remove related files after an unshelve"""
623 if not opts.get('keep'):
619 if not opts.get('keep'):
624 for filetype in shelvefileextensions:
620 for filetype in shelvefileextensions:
625 shfile = shelvedfile(repo, name, filetype)
621 shfile = shelvedfile(repo, name, filetype)
626 if shfile.exists():
622 if shfile.exists():
627 shfile.movetobackup()
623 shfile.movetobackup()
628 cleanupoldbackups(repo)
624 cleanupoldbackups(repo)
629
625
630 def unshelvecontinue(ui, repo, state, opts):
626 def unshelvecontinue(ui, repo, state, opts):
631 """subcommand to continue an in-progress unshelve"""
627 """subcommand to continue an in-progress unshelve"""
632 # We're finishing off a merge. First parent is our original
628 # We're finishing off a merge. First parent is our original
633 # parent, second is the temporary "fake" commit we're unshelving.
629 # parent, second is the temporary "fake" commit we're unshelving.
634 with repo.lock():
630 with repo.lock():
635 checkparents(repo, state)
631 checkparents(repo, state)
636 ms = merge.mergestate.read(repo)
632 ms = merge.mergestate.read(repo)
637 if [f for f in ms if ms[f] == 'u']:
633 if [f for f in ms if ms[f] == 'u']:
638 raise error.Abort(
634 raise error.Abort(
639 _("unresolved conflicts, can't continue"),
635 _("unresolved conflicts, can't continue"),
640 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
636 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
641
637
642 repo.vfs.rename('unshelverebasestate', 'rebasestate')
638 repo.vfs.rename('unshelverebasestate', 'rebasestate')
643 try:
639 try:
644 rebase.rebase(ui, repo, **{
640 rebase.rebase(ui, repo, **{
645 'continue' : True
641 'continue' : True
646 })
642 })
647 except Exception:
643 except Exception:
648 repo.vfs.rename('rebasestate', 'unshelverebasestate')
644 repo.vfs.rename('rebasestate', 'unshelverebasestate')
649 raise
645 raise
650
646
651 shelvectx = repo['tip']
647 shelvectx = repo['tip']
652 if not shelvectx in state.pendingctx.children():
648 if not shelvectx in state.pendingctx.children():
653 # rebase was a no-op, so it produced no child commit
649 # rebase was a no-op, so it produced no child commit
654 shelvectx = state.pendingctx
650 shelvectx = state.pendingctx
655 else:
651 else:
656 # only strip the shelvectx if the rebase produced it
652 # only strip the shelvectx if the rebase produced it
657 state.nodestoprune.append(shelvectx.node())
653 state.nodestoprune.append(shelvectx.node())
658
654
659 mergefiles(ui, repo, state.wctx, shelvectx)
655 mergefiles(ui, repo, state.wctx, shelvectx)
660 restorebranch(ui, repo, state.branchtorestore)
656 restorebranch(ui, repo, state.branchtorestore)
661
657
662 repair.strip(ui, repo, state.nodestoprune, backup=False, topic='shelve')
658 repair.strip(ui, repo, state.nodestoprune, backup=False, topic='shelve')
663 _restoreactivebookmark(repo, state.activebookmark)
659 _restoreactivebookmark(repo, state.activebookmark)
664 shelvedstate.clear(repo)
660 shelvedstate.clear(repo)
665 unshelvecleanup(ui, repo, state.name, opts)
661 unshelvecleanup(ui, repo, state.name, opts)
666 ui.status(_("unshelve of '%s' complete\n") % state.name)
662 ui.status(_("unshelve of '%s' complete\n") % state.name)
667
663
668 def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
664 def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
669 """Temporarily commit working copy changes before moving unshelve commit"""
665 """Temporarily commit working copy changes before moving unshelve commit"""
670 # Store pending changes in a commit and remember added in case a shelve
666 # Store pending changes in a commit and remember added in case a shelve
671 # contains unknown files that are part of the pending change
667 # contains unknown files that are part of the pending change
672 s = repo.status()
668 s = repo.status()
673 addedbefore = frozenset(s.added)
669 addedbefore = frozenset(s.added)
674 if not (s.modified or s.added or s.removed):
670 if not (s.modified or s.added or s.removed):
675 return tmpwctx, addedbefore
671 return tmpwctx, addedbefore
676 ui.status(_("temporarily committing pending changes "
672 ui.status(_("temporarily committing pending changes "
677 "(restore with 'hg unshelve --abort')\n"))
673 "(restore with 'hg unshelve --abort')\n"))
678 commitfunc = getcommitfunc(extra=None, interactive=False,
674 commitfunc = getcommitfunc(extra=None, interactive=False,
679 editor=False)
675 editor=False)
680 tempopts = {}
676 tempopts = {}
681 tempopts['message'] = "pending changes temporary commit"
677 tempopts['message'] = "pending changes temporary commit"
682 tempopts['date'] = opts.get('date')
678 tempopts['date'] = opts.get('date')
683 ui.quiet = True
679 with ui.configoverride({('ui', 'quiet'): True}):
684 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
680 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
685 tmpwctx = repo[node]
681 tmpwctx = repo[node]
686 return tmpwctx, addedbefore
682 return tmpwctx, addedbefore
687
683
688 def _unshelverestorecommit(ui, repo, basename, oldquiet):
684 def _unshelverestorecommit(ui, repo, basename):
689 """Recreate commit in the repository during the unshelve"""
685 """Recreate commit in the repository during the unshelve"""
690 ui.quiet = True
686 with ui.configoverride({('ui', 'quiet'): True}):
691 shelvedfile(repo, basename, 'hg').applybundle()
687 shelvedfile(repo, basename, 'hg').applybundle()
692 shelvectx = repo['tip']
688 shelvectx = repo['tip']
693 ui.quiet = oldquiet
694 return repo, shelvectx
689 return repo, shelvectx
695
690
696 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
691 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
697 tmpwctx, shelvectx, branchtorestore,
692 tmpwctx, shelvectx, branchtorestore,
698 activebookmark):
693 activebookmark):
699 """Rebase restored commit from its original location to a destination"""
694 """Rebase restored commit from its original location to a destination"""
700 # If the shelve is not immediately on top of the commit
695 # If the shelve is not immediately on top of the commit
701 # we'll be merging with, rebase it to be on top.
696 # we'll be merging with, rebase it to be on top.
702 if tmpwctx.node() == shelvectx.parents()[0].node():
697 if tmpwctx.node() == shelvectx.parents()[0].node():
703 return shelvectx
698 return shelvectx
704
699
705 ui.status(_('rebasing shelved changes\n'))
700 ui.status(_('rebasing shelved changes\n'))
706 try:
701 try:
707 rebase.rebase(ui, repo, **{
702 rebase.rebase(ui, repo, **{
708 'rev': [shelvectx.rev()],
703 'rev': [shelvectx.rev()],
709 'dest': str(tmpwctx.rev()),
704 'dest': str(tmpwctx.rev()),
710 'keep': True,
705 'keep': True,
711 'tool': opts.get('tool', ''),
706 'tool': opts.get('tool', ''),
712 })
707 })
713 except error.InterventionRequired:
708 except error.InterventionRequired:
714 tr.close()
709 tr.close()
715
710
716 nodestoprune = [repo.changelog.node(rev)
711 nodestoprune = [repo.changelog.node(rev)
717 for rev in xrange(oldtiprev, len(repo))]
712 for rev in xrange(oldtiprev, len(repo))]
718 shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoprune,
713 shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoprune,
719 branchtorestore, opts.get('keep'), activebookmark)
714 branchtorestore, opts.get('keep'), activebookmark)
720
715
721 repo.vfs.rename('rebasestate', 'unshelverebasestate')
716 repo.vfs.rename('rebasestate', 'unshelverebasestate')
722 raise error.InterventionRequired(
717 raise error.InterventionRequired(
723 _("unresolved conflicts (see 'hg resolve', then "
718 _("unresolved conflicts (see 'hg resolve', then "
724 "'hg unshelve --continue')"))
719 "'hg unshelve --continue')"))
725
720
726 # refresh ctx after rebase completes
721 # refresh ctx after rebase completes
727 shelvectx = repo['tip']
722 shelvectx = repo['tip']
728
723
729 if not shelvectx in tmpwctx.children():
724 if not shelvectx in tmpwctx.children():
730 # rebase was a no-op, so it produced no child commit
725 # rebase was a no-op, so it produced no child commit
731 shelvectx = tmpwctx
726 shelvectx = tmpwctx
732 return shelvectx
727 return shelvectx
733
728
734 def _forgetunknownfiles(repo, shelvectx, addedbefore):
729 def _forgetunknownfiles(repo, shelvectx, addedbefore):
735 # Forget any files that were unknown before the shelve, unknown before
730 # Forget any files that were unknown before the shelve, unknown before
736 # unshelve started, but are now added.
731 # unshelve started, but are now added.
737 shelveunknown = shelvectx.extra().get('shelve_unknown')
732 shelveunknown = shelvectx.extra().get('shelve_unknown')
738 if not shelveunknown:
733 if not shelveunknown:
739 return
734 return
740 shelveunknown = frozenset(shelveunknown.split('\0'))
735 shelveunknown = frozenset(shelveunknown.split('\0'))
741 addedafter = frozenset(repo.status().added)
736 addedafter = frozenset(repo.status().added)
742 toforget = (addedafter & shelveunknown) - addedbefore
737 toforget = (addedafter & shelveunknown) - addedbefore
743 repo[None].forget(toforget)
738 repo[None].forget(toforget)
744
739
745 def _finishunshelve(repo, oldtiprev, tr, activebookmark):
740 def _finishunshelve(repo, oldtiprev, tr, activebookmark):
746 _restoreactivebookmark(repo, activebookmark)
741 _restoreactivebookmark(repo, activebookmark)
747 # The transaction aborting will strip all the commits for us,
742 # The transaction aborting will strip all the commits for us,
748 # but it doesn't update the inmemory structures, so addchangegroup
743 # but it doesn't update the inmemory structures, so addchangegroup
749 # hooks still fire and try to operate on the missing commits.
744 # hooks still fire and try to operate on the missing commits.
750 # Clean up manually to prevent this.
745 # Clean up manually to prevent this.
751 repo.unfiltered().changelog.strip(oldtiprev, tr)
746 repo.unfiltered().changelog.strip(oldtiprev, tr)
752 _aborttransaction(repo)
747 _aborttransaction(repo)
753
748
754 def _checkunshelveuntrackedproblems(ui, repo, shelvectx):
749 def _checkunshelveuntrackedproblems(ui, repo, shelvectx):
755 """Check potential problems which may result from working
750 """Check potential problems which may result from working
756 copy having untracked changes."""
751 copy having untracked changes."""
757 wcdeleted = set(repo.status().deleted)
752 wcdeleted = set(repo.status().deleted)
758 shelvetouched = set(shelvectx.files())
753 shelvetouched = set(shelvectx.files())
759 intersection = wcdeleted.intersection(shelvetouched)
754 intersection = wcdeleted.intersection(shelvetouched)
760 if intersection:
755 if intersection:
761 m = _("shelved change touches missing files")
756 m = _("shelved change touches missing files")
762 hint = _("run hg status to see which files are missing")
757 hint = _("run hg status to see which files are missing")
763 raise error.Abort(m, hint=hint)
758 raise error.Abort(m, hint=hint)
764
759
765 @command('unshelve',
760 @command('unshelve',
766 [('a', 'abort', None,
761 [('a', 'abort', None,
767 _('abort an incomplete unshelve operation')),
762 _('abort an incomplete unshelve operation')),
768 ('c', 'continue', None,
763 ('c', 'continue', None,
769 _('continue an incomplete unshelve operation')),
764 _('continue an incomplete unshelve operation')),
770 ('k', 'keep', None,
765 ('k', 'keep', None,
771 _('keep shelve after unshelving')),
766 _('keep shelve after unshelving')),
772 ('n', 'name', '',
767 ('n', 'name', '',
773 _('restore shelved change with given name'), _('NAME')),
768 _('restore shelved change with given name'), _('NAME')),
774 ('t', 'tool', '', _('specify merge tool')),
769 ('t', 'tool', '', _('specify merge tool')),
775 ('', 'date', '',
770 ('', 'date', '',
776 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
771 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
777 _('hg unshelve [[-n] SHELVED]'))
772 _('hg unshelve [[-n] SHELVED]'))
778 def unshelve(ui, repo, *shelved, **opts):
773 def unshelve(ui, repo, *shelved, **opts):
779 """restore a shelved change to the working directory
774 """restore a shelved change to the working directory
780
775
781 This command accepts an optional name of a shelved change to
776 This command accepts an optional name of a shelved change to
782 restore. If none is given, the most recent shelved change is used.
777 restore. If none is given, the most recent shelved change is used.
783
778
784 If a shelved change is applied successfully, the bundle that
779 If a shelved change is applied successfully, the bundle that
785 contains the shelved changes is moved to a backup location
780 contains the shelved changes is moved to a backup location
786 (.hg/shelve-backup).
781 (.hg/shelve-backup).
787
782
788 Since you can restore a shelved change on top of an arbitrary
783 Since you can restore a shelved change on top of an arbitrary
789 commit, it is possible that unshelving will result in a conflict
784 commit, it is possible that unshelving will result in a conflict
790 between your changes and the commits you are unshelving onto. If
785 between your changes and the commits you are unshelving onto. If
791 this occurs, you must resolve the conflict, then use
786 this occurs, you must resolve the conflict, then use
792 ``--continue`` to complete the unshelve operation. (The bundle
787 ``--continue`` to complete the unshelve operation. (The bundle
793 will not be moved until you successfully complete the unshelve.)
788 will not be moved until you successfully complete the unshelve.)
794
789
795 (Alternatively, you can use ``--abort`` to abandon an unshelve
790 (Alternatively, you can use ``--abort`` to abandon an unshelve
796 that causes a conflict. This reverts the unshelved changes, and
791 that causes a conflict. This reverts the unshelved changes, and
797 leaves the bundle in place.)
792 leaves the bundle in place.)
798
793
799 If bare shelved change(when no files are specified, without interactive,
794 If bare shelved change(when no files are specified, without interactive,
800 include and exclude option) was done on newly created branch it would
795 include and exclude option) was done on newly created branch it would
801 restore branch information to the working directory.
796 restore branch information to the working directory.
802
797
803 After a successful unshelve, the shelved changes are stored in a
798 After a successful unshelve, the shelved changes are stored in a
804 backup directory. Only the N most recent backups are kept. N
799 backup directory. Only the N most recent backups are kept. N
805 defaults to 10 but can be overridden using the ``shelve.maxbackups``
800 defaults to 10 but can be overridden using the ``shelve.maxbackups``
806 configuration option.
801 configuration option.
807
802
808 .. container:: verbose
803 .. container:: verbose
809
804
810 Timestamp in seconds is used to decide order of backups. More
805 Timestamp in seconds is used to decide order of backups. More
811 than ``maxbackups`` backups are kept, if same timestamp
806 than ``maxbackups`` backups are kept, if same timestamp
812 prevents from deciding exact order of them, for safety.
807 prevents from deciding exact order of them, for safety.
813 """
808 """
814 with repo.wlock():
809 with repo.wlock():
815 return _dounshelve(ui, repo, *shelved, **opts)
810 return _dounshelve(ui, repo, *shelved, **opts)
816
811
817 def _dounshelve(ui, repo, *shelved, **opts):
812 def _dounshelve(ui, repo, *shelved, **opts):
818 abortf = opts.get('abort')
813 abortf = opts.get('abort')
819 continuef = opts.get('continue')
814 continuef = opts.get('continue')
820 if not abortf and not continuef:
815 if not abortf and not continuef:
821 cmdutil.checkunfinished(repo)
816 cmdutil.checkunfinished(repo)
822 shelved = list(shelved)
817 shelved = list(shelved)
823 if opts.get("name"):
818 if opts.get("name"):
824 shelved.append(opts["name"])
819 shelved.append(opts["name"])
825
820
826 if abortf or continuef:
821 if abortf or continuef:
827 if abortf and continuef:
822 if abortf and continuef:
828 raise error.Abort(_('cannot use both abort and continue'))
823 raise error.Abort(_('cannot use both abort and continue'))
829 if shelved:
824 if shelved:
830 raise error.Abort(_('cannot combine abort/continue with '
825 raise error.Abort(_('cannot combine abort/continue with '
831 'naming a shelved change'))
826 'naming a shelved change'))
832 if abortf and opts.get('tool', False):
827 if abortf and opts.get('tool', False):
833 ui.warn(_('tool option will be ignored\n'))
828 ui.warn(_('tool option will be ignored\n'))
834
829
835 try:
830 try:
836 state = shelvedstate.load(repo)
831 state = shelvedstate.load(repo)
837 if opts.get('keep') is None:
832 if opts.get('keep') is None:
838 opts['keep'] = state.keep
833 opts['keep'] = state.keep
839 except IOError as err:
834 except IOError as err:
840 if err.errno != errno.ENOENT:
835 if err.errno != errno.ENOENT:
841 raise
836 raise
842 cmdutil.wrongtooltocontinue(repo, _('unshelve'))
837 cmdutil.wrongtooltocontinue(repo, _('unshelve'))
843 except error.CorruptedState as err:
838 except error.CorruptedState as err:
844 ui.debug(str(err) + '\n')
839 ui.debug(str(err) + '\n')
845 if continuef:
840 if continuef:
846 msg = _('corrupted shelved state file')
841 msg = _('corrupted shelved state file')
847 hint = _('please run hg unshelve --abort to abort unshelve '
842 hint = _('please run hg unshelve --abort to abort unshelve '
848 'operation')
843 'operation')
849 raise error.Abort(msg, hint=hint)
844 raise error.Abort(msg, hint=hint)
850 elif abortf:
845 elif abortf:
851 msg = _('could not read shelved state file, your working copy '
846 msg = _('could not read shelved state file, your working copy '
852 'may be in an unexpected state\nplease update to some '
847 'may be in an unexpected state\nplease update to some '
853 'commit\n')
848 'commit\n')
854 ui.warn(msg)
849 ui.warn(msg)
855 shelvedstate.clear(repo)
850 shelvedstate.clear(repo)
856 return
851 return
857
852
858 if abortf:
853 if abortf:
859 return unshelveabort(ui, repo, state, opts)
854 return unshelveabort(ui, repo, state, opts)
860 elif continuef:
855 elif continuef:
861 return unshelvecontinue(ui, repo, state, opts)
856 return unshelvecontinue(ui, repo, state, opts)
862 elif len(shelved) > 1:
857 elif len(shelved) > 1:
863 raise error.Abort(_('can only unshelve one change at a time'))
858 raise error.Abort(_('can only unshelve one change at a time'))
864 elif not shelved:
859 elif not shelved:
865 shelved = listshelves(repo)
860 shelved = listshelves(repo)
866 if not shelved:
861 if not shelved:
867 raise error.Abort(_('no shelved changes to apply!'))
862 raise error.Abort(_('no shelved changes to apply!'))
868 basename = util.split(shelved[0][1])[1]
863 basename = util.split(shelved[0][1])[1]
869 ui.status(_("unshelving change '%s'\n") % basename)
864 ui.status(_("unshelving change '%s'\n") % basename)
870 else:
865 else:
871 basename = shelved[0]
866 basename = shelved[0]
872
867
873 if not shelvedfile(repo, basename, patchextension).exists():
868 if not shelvedfile(repo, basename, patchextension).exists():
874 raise error.Abort(_("shelved change '%s' not found") % basename)
869 raise error.Abort(_("shelved change '%s' not found") % basename)
875
870
876 oldquiet = ui.quiet
877 lock = tr = None
871 lock = tr = None
878 try:
872 try:
879 lock = repo.lock()
873 lock = repo.lock()
880
881 tr = repo.transaction('unshelve', report=lambda x: None)
874 tr = repo.transaction('unshelve', report=lambda x: None)
882 oldtiprev = len(repo)
875 oldtiprev = len(repo)
883
876
884 pctx = repo['.']
877 pctx = repo['.']
885 tmpwctx = pctx
878 tmpwctx = pctx
886 # The goal is to have a commit structure like so:
879 # The goal is to have a commit structure like so:
887 # ...-> pctx -> tmpwctx -> shelvectx
880 # ...-> pctx -> tmpwctx -> shelvectx
888 # where tmpwctx is an optional commit with the user's pending changes
881 # where tmpwctx is an optional commit with the user's pending changes
889 # and shelvectx is the unshelved changes. Then we merge it all down
882 # and shelvectx is the unshelved changes. Then we merge it all down
890 # to the original pctx.
883 # to the original pctx.
891
884
892 activebookmark = _backupactivebookmark(repo)
885 activebookmark = _backupactivebookmark(repo)
893 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
886 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
894 with ui.configoverride(overrides, 'unshelve'):
887 with ui.configoverride(overrides, 'unshelve'):
895 tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
888 tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
896 tmpwctx)
889 tmpwctx)
897
890 repo, shelvectx = _unshelverestorecommit(ui, repo, basename)
898 repo, shelvectx = _unshelverestorecommit(ui, repo, basename,
899 oldquiet)
900 _checkunshelveuntrackedproblems(ui, repo, shelvectx)
891 _checkunshelveuntrackedproblems(ui, repo, shelvectx)
901 branchtorestore = ''
892 branchtorestore = ''
902 if shelvectx.branch() != shelvectx.p1().branch():
893 if shelvectx.branch() != shelvectx.p1().branch():
903 branchtorestore = shelvectx.branch()
894 branchtorestore = shelvectx.branch()
904
895
905 shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
896 shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
906 basename, pctx, tmpwctx,
897 basename, pctx, tmpwctx,
907 shelvectx, branchtorestore,
898 shelvectx, branchtorestore,
908 activebookmark)
899 activebookmark)
909 mergefiles(ui, repo, pctx, shelvectx)
900 mergefiles(ui, repo, pctx, shelvectx)
910 restorebranch(ui, repo, branchtorestore)
901 restorebranch(ui, repo, branchtorestore)
911 _forgetunknownfiles(repo, shelvectx, addedbefore)
902 _forgetunknownfiles(repo, shelvectx, addedbefore)
912
903
913 shelvedstate.clear(repo)
904 shelvedstate.clear(repo)
914 _finishunshelve(repo, oldtiprev, tr, activebookmark)
905 _finishunshelve(repo, oldtiprev, tr, activebookmark)
915 unshelvecleanup(ui, repo, basename, opts)
906 unshelvecleanup(ui, repo, basename, opts)
916 finally:
907 finally:
917 ui.quiet = oldquiet
918 if tr:
908 if tr:
919 tr.release()
909 tr.release()
920 lockmod.release(lock)
910 lockmod.release(lock)
921
911
922 @command('shelve',
912 @command('shelve',
923 [('A', 'addremove', None,
913 [('A', 'addremove', None,
924 _('mark new/missing files as added/removed before shelving')),
914 _('mark new/missing files as added/removed before shelving')),
925 ('u', 'unknown', None,
915 ('u', 'unknown', None,
926 _('store unknown files in the shelve')),
916 _('store unknown files in the shelve')),
927 ('', 'cleanup', None,
917 ('', 'cleanup', None,
928 _('delete all shelved changes')),
918 _('delete all shelved changes')),
929 ('', 'date', '',
919 ('', 'date', '',
930 _('shelve with the specified commit date'), _('DATE')),
920 _('shelve with the specified commit date'), _('DATE')),
931 ('d', 'delete', None,
921 ('d', 'delete', None,
932 _('delete the named shelved change(s)')),
922 _('delete the named shelved change(s)')),
933 ('e', 'edit', False,
923 ('e', 'edit', False,
934 _('invoke editor on commit messages')),
924 _('invoke editor on commit messages')),
935 ('l', 'list', None,
925 ('l', 'list', None,
936 _('list current shelves')),
926 _('list current shelves')),
937 ('m', 'message', '',
927 ('m', 'message', '',
938 _('use text as shelve message'), _('TEXT')),
928 _('use text as shelve message'), _('TEXT')),
939 ('n', 'name', '',
929 ('n', 'name', '',
940 _('use the given name for the shelved commit'), _('NAME')),
930 _('use the given name for the shelved commit'), _('NAME')),
941 ('p', 'patch', None,
931 ('p', 'patch', None,
942 _('show patch')),
932 _('show patch')),
943 ('i', 'interactive', None,
933 ('i', 'interactive', None,
944 _('interactive mode, only works while creating a shelve')),
934 _('interactive mode, only works while creating a shelve')),
945 ('', 'stat', None,
935 ('', 'stat', None,
946 _('output diffstat-style summary of changes'))] + commands.walkopts,
936 _('output diffstat-style summary of changes'))] + commands.walkopts,
947 _('hg shelve [OPTION]... [FILE]...'))
937 _('hg shelve [OPTION]... [FILE]...'))
948 def shelvecmd(ui, repo, *pats, **opts):
938 def shelvecmd(ui, repo, *pats, **opts):
949 '''save and set aside changes from the working directory
939 '''save and set aside changes from the working directory
950
940
951 Shelving takes files that "hg status" reports as not clean, saves
941 Shelving takes files that "hg status" reports as not clean, saves
952 the modifications to a bundle (a shelved change), and reverts the
942 the modifications to a bundle (a shelved change), and reverts the
953 files so that their state in the working directory becomes clean.
943 files so that their state in the working directory becomes clean.
954
944
955 To restore these changes to the working directory, using "hg
945 To restore these changes to the working directory, using "hg
956 unshelve"; this will work even if you switch to a different
946 unshelve"; this will work even if you switch to a different
957 commit.
947 commit.
958
948
959 When no files are specified, "hg shelve" saves all not-clean
949 When no files are specified, "hg shelve" saves all not-clean
960 files. If specific files or directories are named, only changes to
950 files. If specific files or directories are named, only changes to
961 those files are shelved.
951 those files are shelved.
962
952
963 In bare shelve (when no files are specified, without interactive,
953 In bare shelve (when no files are specified, without interactive,
964 include and exclude option), shelving remembers information if the
954 include and exclude option), shelving remembers information if the
965 working directory was on newly created branch, in other words working
955 working directory was on newly created branch, in other words working
966 directory was on different branch than its first parent. In this
956 directory was on different branch than its first parent. In this
967 situation unshelving restores branch information to the working directory.
957 situation unshelving restores branch information to the working directory.
968
958
969 Each shelved change has a name that makes it easier to find later.
959 Each shelved change has a name that makes it easier to find later.
970 The name of a shelved change defaults to being based on the active
960 The name of a shelved change defaults to being based on the active
971 bookmark, or if there is no active bookmark, the current named
961 bookmark, or if there is no active bookmark, the current named
972 branch. To specify a different name, use ``--name``.
962 branch. To specify a different name, use ``--name``.
973
963
974 To see a list of existing shelved changes, use the ``--list``
964 To see a list of existing shelved changes, use the ``--list``
975 option. For each shelved change, this will print its name, age,
965 option. For each shelved change, this will print its name, age,
976 and description; use ``--patch`` or ``--stat`` for more details.
966 and description; use ``--patch`` or ``--stat`` for more details.
977
967
978 To delete specific shelved changes, use ``--delete``. To delete
968 To delete specific shelved changes, use ``--delete``. To delete
979 all shelved changes, use ``--cleanup``.
969 all shelved changes, use ``--cleanup``.
980 '''
970 '''
981 allowables = [
971 allowables = [
982 ('addremove', set(['create'])), # 'create' is pseudo action
972 ('addremove', set(['create'])), # 'create' is pseudo action
983 ('unknown', set(['create'])),
973 ('unknown', set(['create'])),
984 ('cleanup', set(['cleanup'])),
974 ('cleanup', set(['cleanup'])),
985 # ('date', set(['create'])), # ignored for passing '--date "0 0"' in tests
975 # ('date', set(['create'])), # ignored for passing '--date "0 0"' in tests
986 ('delete', set(['delete'])),
976 ('delete', set(['delete'])),
987 ('edit', set(['create'])),
977 ('edit', set(['create'])),
988 ('list', set(['list'])),
978 ('list', set(['list'])),
989 ('message', set(['create'])),
979 ('message', set(['create'])),
990 ('name', set(['create'])),
980 ('name', set(['create'])),
991 ('patch', set(['patch', 'list'])),
981 ('patch', set(['patch', 'list'])),
992 ('stat', set(['stat', 'list'])),
982 ('stat', set(['stat', 'list'])),
993 ]
983 ]
994 def checkopt(opt):
984 def checkopt(opt):
995 if opts.get(opt):
985 if opts.get(opt):
996 for i, allowable in allowables:
986 for i, allowable in allowables:
997 if opts[i] and opt not in allowable:
987 if opts[i] and opt not in allowable:
998 raise error.Abort(_("options '--%s' and '--%s' may not be "
988 raise error.Abort(_("options '--%s' and '--%s' may not be "
999 "used together") % (opt, i))
989 "used together") % (opt, i))
1000 return True
990 return True
1001 if checkopt('cleanup'):
991 if checkopt('cleanup'):
1002 if pats:
992 if pats:
1003 raise error.Abort(_("cannot specify names when using '--cleanup'"))
993 raise error.Abort(_("cannot specify names when using '--cleanup'"))
1004 return cleanupcmd(ui, repo)
994 return cleanupcmd(ui, repo)
1005 elif checkopt('delete'):
995 elif checkopt('delete'):
1006 return deletecmd(ui, repo, pats)
996 return deletecmd(ui, repo, pats)
1007 elif checkopt('list'):
997 elif checkopt('list'):
1008 return listcmd(ui, repo, pats, opts)
998 return listcmd(ui, repo, pats, opts)
1009 elif checkopt('patch'):
999 elif checkopt('patch'):
1010 return patchcmds(ui, repo, pats, opts, subcommand='patch')
1000 return patchcmds(ui, repo, pats, opts, subcommand='patch')
1011 elif checkopt('stat'):
1001 elif checkopt('stat'):
1012 return patchcmds(ui, repo, pats, opts, subcommand='stat')
1002 return patchcmds(ui, repo, pats, opts, subcommand='stat')
1013 else:
1003 else:
1014 return createcmd(ui, repo, pats, opts)
1004 return createcmd(ui, repo, pats, opts)
1015
1005
1016 def extsetup(ui):
1006 def extsetup(ui):
1017 cmdutil.unfinishedstates.append(
1007 cmdutil.unfinishedstates.append(
1018 [shelvedstate._filename, False, False,
1008 [shelvedstate._filename, False, False,
1019 _('unshelve already in progress'),
1009 _('unshelve already in progress'),
1020 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
1010 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
1021 cmdutil.afterresolvedstates.append(
1011 cmdutil.afterresolvedstates.append(
1022 [shelvedstate._filename, _('hg unshelve --continue')])
1012 [shelvedstate._filename, _('hg unshelve --continue')])
General Comments 0
You need to be logged in to leave comments. Login now