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