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