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