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