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