##// END OF EJS Templates
shelve: pass 'editform' argument to 'cmdutil.getcommiteditor'...
FUJIWARA Katsunori -
r22005:dabf8fb8 default
parent child Browse files
Show More
@@ -1,719 +1,720
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
23
24 from mercurial.i18n import _
24 from mercurial.i18n import _
25 from mercurial.node import nullid, nullrev, bin, hex
25 from mercurial.node import nullid, nullrev, bin, hex
26 from mercurial import changegroup, cmdutil, scmutil, phases, commands
26 from mercurial import changegroup, cmdutil, scmutil, phases, commands
27 from mercurial import error, hg, mdiff, merge, patch, repair, util
27 from mercurial import error, hg, mdiff, merge, patch, repair, util
28 from mercurial import templatefilters, changegroup, exchange
28 from mercurial import templatefilters, changegroup, exchange
29 from mercurial import lock as lockmod
29 from mercurial import lock as lockmod
30 from hgext import rebase
30 from hgext import rebase
31 import errno
31 import errno
32
32
33 cmdtable = {}
33 cmdtable = {}
34 command = cmdutil.command(cmdtable)
34 command = cmdutil.command(cmdtable)
35 testedwith = 'internal'
35 testedwith = 'internal'
36
36
37 class shelvedfile(object):
37 class shelvedfile(object):
38 """Helper for the file storing a single shelve
38 """Helper for the file storing a single shelve
39
39
40 Handles common functions on shelve files (.hg/.files/.patch) using
40 Handles common functions on shelve files (.hg/.files/.patch) using
41 the vfs layer"""
41 the vfs layer"""
42 def __init__(self, repo, name, filetype=None):
42 def __init__(self, repo, name, filetype=None):
43 self.repo = repo
43 self.repo = repo
44 self.name = name
44 self.name = name
45 self.vfs = scmutil.vfs(repo.join('shelved'))
45 self.vfs = scmutil.vfs(repo.join('shelved'))
46 if filetype:
46 if filetype:
47 self.fname = name + '.' + filetype
47 self.fname = name + '.' + filetype
48 else:
48 else:
49 self.fname = name
49 self.fname = name
50
50
51 def exists(self):
51 def exists(self):
52 return self.vfs.exists(self.fname)
52 return self.vfs.exists(self.fname)
53
53
54 def filename(self):
54 def filename(self):
55 return self.vfs.join(self.fname)
55 return self.vfs.join(self.fname)
56
56
57 def unlink(self):
57 def unlink(self):
58 util.unlink(self.filename())
58 util.unlink(self.filename())
59
59
60 def stat(self):
60 def stat(self):
61 return self.vfs.stat(self.fname)
61 return self.vfs.stat(self.fname)
62
62
63 def opener(self, mode='rb'):
63 def opener(self, mode='rb'):
64 try:
64 try:
65 return self.vfs(self.fname, mode)
65 return self.vfs(self.fname, mode)
66 except IOError, err:
66 except IOError, err:
67 if err.errno != errno.ENOENT:
67 if err.errno != errno.ENOENT:
68 raise
68 raise
69 raise util.Abort(_("shelved change '%s' not found") % self.name)
69 raise util.Abort(_("shelved change '%s' not found") % self.name)
70
70
71 def applybundle(self):
71 def applybundle(self):
72 fp = self.opener()
72 fp = self.opener()
73 try:
73 try:
74 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
74 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
75 changegroup.addchangegroup(self.repo, gen, 'unshelve',
75 changegroup.addchangegroup(self.repo, gen, 'unshelve',
76 'bundle:' + self.vfs.join(self.fname))
76 'bundle:' + self.vfs.join(self.fname))
77 finally:
77 finally:
78 fp.close()
78 fp.close()
79
79
80 def writebundle(self, cg):
80 def writebundle(self, cg):
81 changegroup.writebundle(cg, self.fname, 'HG10UN', self.vfs)
81 changegroup.writebundle(cg, self.fname, 'HG10UN', self.vfs)
82
82
83 class shelvedstate(object):
83 class shelvedstate(object):
84 """Handle persistence during unshelving operations.
84 """Handle persistence during unshelving operations.
85
85
86 Handles saving and restoring a shelved state. Ensures that different
86 Handles saving and restoring a shelved state. Ensures that different
87 versions of a shelved state are possible and handles them appropriately.
87 versions of a shelved state are possible and handles them appropriately.
88 """
88 """
89 _version = 1
89 _version = 1
90 _filename = 'shelvedstate'
90 _filename = 'shelvedstate'
91
91
92 @classmethod
92 @classmethod
93 def load(cls, repo):
93 def load(cls, repo):
94 fp = repo.opener(cls._filename)
94 fp = repo.opener(cls._filename)
95 try:
95 try:
96 version = int(fp.readline().strip())
96 version = int(fp.readline().strip())
97
97
98 if version != cls._version:
98 if version != cls._version:
99 raise util.Abort(_('this version of shelve is incompatible '
99 raise util.Abort(_('this version of shelve is incompatible '
100 'with the version used in this repo'))
100 'with the version used in this repo'))
101 name = fp.readline().strip()
101 name = fp.readline().strip()
102 wctx = fp.readline().strip()
102 wctx = fp.readline().strip()
103 pendingctx = fp.readline().strip()
103 pendingctx = fp.readline().strip()
104 parents = [bin(h) for h in fp.readline().split()]
104 parents = [bin(h) for h in fp.readline().split()]
105 stripnodes = [bin(h) for h in fp.readline().split()]
105 stripnodes = [bin(h) for h in fp.readline().split()]
106 finally:
106 finally:
107 fp.close()
107 fp.close()
108
108
109 obj = cls()
109 obj = cls()
110 obj.name = name
110 obj.name = name
111 obj.wctx = repo[bin(wctx)]
111 obj.wctx = repo[bin(wctx)]
112 obj.pendingctx = repo[bin(pendingctx)]
112 obj.pendingctx = repo[bin(pendingctx)]
113 obj.parents = parents
113 obj.parents = parents
114 obj.stripnodes = stripnodes
114 obj.stripnodes = stripnodes
115
115
116 return obj
116 return obj
117
117
118 @classmethod
118 @classmethod
119 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
119 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
120 fp = repo.opener(cls._filename, 'wb')
120 fp = repo.opener(cls._filename, 'wb')
121 fp.write('%i\n' % cls._version)
121 fp.write('%i\n' % cls._version)
122 fp.write('%s\n' % name)
122 fp.write('%s\n' % name)
123 fp.write('%s\n' % hex(originalwctx.node()))
123 fp.write('%s\n' % hex(originalwctx.node()))
124 fp.write('%s\n' % hex(pendingctx.node()))
124 fp.write('%s\n' % hex(pendingctx.node()))
125 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
125 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
126 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
126 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
127 fp.close()
127 fp.close()
128
128
129 @classmethod
129 @classmethod
130 def clear(cls, repo):
130 def clear(cls, repo):
131 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
131 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
132
132
133 def createcmd(ui, repo, pats, opts):
133 def createcmd(ui, repo, pats, opts):
134 """subcommand that creates a new shelve"""
134 """subcommand that creates a new shelve"""
135
135
136 def publicancestors(ctx):
136 def publicancestors(ctx):
137 """Compute the public ancestors of a commit.
137 """Compute the public ancestors of a commit.
138
138
139 Much faster than the revset ancestors(ctx) & draft()"""
139 Much faster than the revset ancestors(ctx) & draft()"""
140 seen = set([nullrev])
140 seen = set([nullrev])
141 visit = util.deque()
141 visit = util.deque()
142 visit.append(ctx)
142 visit.append(ctx)
143 while visit:
143 while visit:
144 ctx = visit.popleft()
144 ctx = visit.popleft()
145 yield ctx.node()
145 yield ctx.node()
146 for parent in ctx.parents():
146 for parent in ctx.parents():
147 rev = parent.rev()
147 rev = parent.rev()
148 if rev not in seen:
148 if rev not in seen:
149 seen.add(rev)
149 seen.add(rev)
150 if parent.mutable():
150 if parent.mutable():
151 visit.append(parent)
151 visit.append(parent)
152
152
153 wctx = repo[None]
153 wctx = repo[None]
154 parents = wctx.parents()
154 parents = wctx.parents()
155 if len(parents) > 1:
155 if len(parents) > 1:
156 raise util.Abort(_('cannot shelve while merging'))
156 raise util.Abort(_('cannot shelve while merging'))
157 parent = parents[0]
157 parent = parents[0]
158
158
159 # we never need the user, so we use a generic user for all shelve operations
159 # we never need the user, so we use a generic user for all shelve operations
160 user = 'shelve@localhost'
160 user = 'shelve@localhost'
161 label = repo._bookmarkcurrent or parent.branch() or 'default'
161 label = repo._bookmarkcurrent or parent.branch() or 'default'
162
162
163 # slashes aren't allowed in filenames, therefore we rename it
163 # slashes aren't allowed in filenames, therefore we rename it
164 label = label.replace('/', '_')
164 label = label.replace('/', '_')
165
165
166 def gennames():
166 def gennames():
167 yield label
167 yield label
168 for i in xrange(1, 100):
168 for i in xrange(1, 100):
169 yield '%s-%02d' % (label, i)
169 yield '%s-%02d' % (label, i)
170
170
171 shelvedfiles = []
171 shelvedfiles = []
172
172
173 def commitfunc(ui, repo, message, match, opts):
173 def commitfunc(ui, repo, message, match, opts):
174 # check modified, added, removed, deleted only
174 # check modified, added, removed, deleted only
175 for flist in repo.status(match=match)[:4]:
175 for flist in repo.status(match=match)[:4]:
176 shelvedfiles.extend(flist)
176 shelvedfiles.extend(flist)
177 hasmq = util.safehasattr(repo, 'mq')
177 hasmq = util.safehasattr(repo, 'mq')
178 if hasmq:
178 if hasmq:
179 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
179 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
180 try:
180 try:
181 editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
181 return repo.commit(message, user, opts.get('date'), match,
182 return repo.commit(message, user, opts.get('date'), match,
182 editor=cmdutil.getcommiteditor(**opts))
183 editor=editor)
183 finally:
184 finally:
184 if hasmq:
185 if hasmq:
185 repo.mq.checkapplied = saved
186 repo.mq.checkapplied = saved
186
187
187 if parent.node() != nullid:
188 if parent.node() != nullid:
188 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
189 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
189 else:
190 else:
190 desc = '(changes in empty repository)'
191 desc = '(changes in empty repository)'
191
192
192 if not opts['message']:
193 if not opts['message']:
193 opts['message'] = desc
194 opts['message'] = desc
194
195
195 name = opts['name']
196 name = opts['name']
196
197
197 wlock = lock = tr = bms = None
198 wlock = lock = tr = bms = None
198 try:
199 try:
199 wlock = repo.wlock()
200 wlock = repo.wlock()
200 lock = repo.lock()
201 lock = repo.lock()
201
202
202 bms = repo._bookmarks.copy()
203 bms = repo._bookmarks.copy()
203 # use an uncommitted transaction to generate the bundle to avoid
204 # use an uncommitted transaction to generate the bundle to avoid
204 # pull races. ensure we don't print the abort message to stderr.
205 # pull races. ensure we don't print the abort message to stderr.
205 tr = repo.transaction('commit', report=lambda x: None)
206 tr = repo.transaction('commit', report=lambda x: None)
206
207
207 if name:
208 if name:
208 if shelvedfile(repo, name, 'hg').exists():
209 if shelvedfile(repo, name, 'hg').exists():
209 raise util.Abort(_("a shelved change named '%s' already exists")
210 raise util.Abort(_("a shelved change named '%s' already exists")
210 % name)
211 % name)
211 else:
212 else:
212 for n in gennames():
213 for n in gennames():
213 if not shelvedfile(repo, n, 'hg').exists():
214 if not shelvedfile(repo, n, 'hg').exists():
214 name = n
215 name = n
215 break
216 break
216 else:
217 else:
217 raise util.Abort(_("too many shelved changes named '%s'") %
218 raise util.Abort(_("too many shelved changes named '%s'") %
218 label)
219 label)
219
220
220 # ensure we are not creating a subdirectory or a hidden file
221 # ensure we are not creating a subdirectory or a hidden file
221 if '/' in name or '\\' in name:
222 if '/' in name or '\\' in name:
222 raise util.Abort(_('shelved change names may not contain slashes'))
223 raise util.Abort(_('shelved change names may not contain slashes'))
223 if name.startswith('.'):
224 if name.startswith('.'):
224 raise util.Abort(_("shelved change names may not start with '.'"))
225 raise util.Abort(_("shelved change names may not start with '.'"))
225
226
226 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
227 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
227
228
228 if not node:
229 if not node:
229 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
230 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
230 if stat[3]:
231 if stat[3]:
231 ui.status(_("nothing changed (%d missing files, see "
232 ui.status(_("nothing changed (%d missing files, see "
232 "'hg status')\n") % len(stat[3]))
233 "'hg status')\n") % len(stat[3]))
233 else:
234 else:
234 ui.status(_("nothing changed\n"))
235 ui.status(_("nothing changed\n"))
235 return 1
236 return 1
236
237
237 phases.retractboundary(repo, phases.secret, [node])
238 phases.retractboundary(repo, phases.secret, [node])
238
239
239 fp = shelvedfile(repo, name, 'files').opener('wb')
240 fp = shelvedfile(repo, name, 'files').opener('wb')
240 fp.write('\0'.join(shelvedfiles))
241 fp.write('\0'.join(shelvedfiles))
241
242
242 bases = list(publicancestors(repo[node]))
243 bases = list(publicancestors(repo[node]))
243 cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
244 cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
244 shelvedfile(repo, name, 'hg').writebundle(cg)
245 shelvedfile(repo, name, 'hg').writebundle(cg)
245 cmdutil.export(repo, [node],
246 cmdutil.export(repo, [node],
246 fp=shelvedfile(repo, name, 'patch').opener('wb'),
247 fp=shelvedfile(repo, name, 'patch').opener('wb'),
247 opts=mdiff.diffopts(git=True))
248 opts=mdiff.diffopts(git=True))
248
249
249
250
250 if ui.formatted():
251 if ui.formatted():
251 desc = util.ellipsis(desc, ui.termwidth())
252 desc = util.ellipsis(desc, ui.termwidth())
252 ui.status(_('shelved as %s\n') % name)
253 ui.status(_('shelved as %s\n') % name)
253 hg.update(repo, parent.node())
254 hg.update(repo, parent.node())
254 finally:
255 finally:
255 if bms:
256 if bms:
256 # restore old bookmarks
257 # restore old bookmarks
257 repo._bookmarks.update(bms)
258 repo._bookmarks.update(bms)
258 repo._bookmarks.write()
259 repo._bookmarks.write()
259 if tr:
260 if tr:
260 tr.abort()
261 tr.abort()
261 lockmod.release(lock, wlock)
262 lockmod.release(lock, wlock)
262
263
263 def cleanupcmd(ui, repo):
264 def cleanupcmd(ui, repo):
264 """subcommand that deletes all shelves"""
265 """subcommand that deletes all shelves"""
265
266
266 wlock = None
267 wlock = None
267 try:
268 try:
268 wlock = repo.wlock()
269 wlock = repo.wlock()
269 for (name, _) in repo.vfs.readdir('shelved'):
270 for (name, _) in repo.vfs.readdir('shelved'):
270 suffix = name.rsplit('.', 1)[-1]
271 suffix = name.rsplit('.', 1)[-1]
271 if suffix in ('hg', 'files', 'patch'):
272 if suffix in ('hg', 'files', 'patch'):
272 shelvedfile(repo, name).unlink()
273 shelvedfile(repo, name).unlink()
273 finally:
274 finally:
274 lockmod.release(wlock)
275 lockmod.release(wlock)
275
276
276 def deletecmd(ui, repo, pats):
277 def deletecmd(ui, repo, pats):
277 """subcommand that deletes a specific shelve"""
278 """subcommand that deletes a specific shelve"""
278 if not pats:
279 if not pats:
279 raise util.Abort(_('no shelved changes specified!'))
280 raise util.Abort(_('no shelved changes specified!'))
280 wlock = None
281 wlock = None
281 try:
282 try:
282 wlock = repo.wlock()
283 wlock = repo.wlock()
283 try:
284 try:
284 for name in pats:
285 for name in pats:
285 for suffix in 'hg files patch'.split():
286 for suffix in 'hg files patch'.split():
286 shelvedfile(repo, name, suffix).unlink()
287 shelvedfile(repo, name, suffix).unlink()
287 except OSError, err:
288 except OSError, err:
288 if err.errno != errno.ENOENT:
289 if err.errno != errno.ENOENT:
289 raise
290 raise
290 raise util.Abort(_("shelved change '%s' not found") % name)
291 raise util.Abort(_("shelved change '%s' not found") % name)
291 finally:
292 finally:
292 lockmod.release(wlock)
293 lockmod.release(wlock)
293
294
294 def listshelves(repo):
295 def listshelves(repo):
295 """return all shelves in repo as list of (time, filename)"""
296 """return all shelves in repo as list of (time, filename)"""
296 try:
297 try:
297 names = repo.vfs.readdir('shelved')
298 names = repo.vfs.readdir('shelved')
298 except OSError, err:
299 except OSError, err:
299 if err.errno != errno.ENOENT:
300 if err.errno != errno.ENOENT:
300 raise
301 raise
301 return []
302 return []
302 info = []
303 info = []
303 for (name, _) in names:
304 for (name, _) in names:
304 pfx, sfx = name.rsplit('.', 1)
305 pfx, sfx = name.rsplit('.', 1)
305 if not pfx or sfx != 'patch':
306 if not pfx or sfx != 'patch':
306 continue
307 continue
307 st = shelvedfile(repo, name).stat()
308 st = shelvedfile(repo, name).stat()
308 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
309 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
309 return sorted(info, reverse=True)
310 return sorted(info, reverse=True)
310
311
311 def listcmd(ui, repo, pats, opts):
312 def listcmd(ui, repo, pats, opts):
312 """subcommand that displays the list of shelves"""
313 """subcommand that displays the list of shelves"""
313 pats = set(pats)
314 pats = set(pats)
314 width = 80
315 width = 80
315 if not ui.plain():
316 if not ui.plain():
316 width = ui.termwidth()
317 width = ui.termwidth()
317 namelabel = 'shelve.newest'
318 namelabel = 'shelve.newest'
318 for mtime, name in listshelves(repo):
319 for mtime, name in listshelves(repo):
319 sname = util.split(name)[1]
320 sname = util.split(name)[1]
320 if pats and sname not in pats:
321 if pats and sname not in pats:
321 continue
322 continue
322 ui.write(sname, label=namelabel)
323 ui.write(sname, label=namelabel)
323 namelabel = 'shelve.name'
324 namelabel = 'shelve.name'
324 if ui.quiet:
325 if ui.quiet:
325 ui.write('\n')
326 ui.write('\n')
326 continue
327 continue
327 ui.write(' ' * (16 - len(sname)))
328 ui.write(' ' * (16 - len(sname)))
328 used = 16
329 used = 16
329 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
330 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
330 ui.write(age, label='shelve.age')
331 ui.write(age, label='shelve.age')
331 ui.write(' ' * (12 - len(age)))
332 ui.write(' ' * (12 - len(age)))
332 used += 12
333 used += 12
333 fp = open(name + '.patch', 'rb')
334 fp = open(name + '.patch', 'rb')
334 try:
335 try:
335 while True:
336 while True:
336 line = fp.readline()
337 line = fp.readline()
337 if not line:
338 if not line:
338 break
339 break
339 if not line.startswith('#'):
340 if not line.startswith('#'):
340 desc = line.rstrip()
341 desc = line.rstrip()
341 if ui.formatted():
342 if ui.formatted():
342 desc = util.ellipsis(desc, width - used)
343 desc = util.ellipsis(desc, width - used)
343 ui.write(desc)
344 ui.write(desc)
344 break
345 break
345 ui.write('\n')
346 ui.write('\n')
346 if not (opts['patch'] or opts['stat']):
347 if not (opts['patch'] or opts['stat']):
347 continue
348 continue
348 difflines = fp.readlines()
349 difflines = fp.readlines()
349 if opts['patch']:
350 if opts['patch']:
350 for chunk, label in patch.difflabel(iter, difflines):
351 for chunk, label in patch.difflabel(iter, difflines):
351 ui.write(chunk, label=label)
352 ui.write(chunk, label=label)
352 if opts['stat']:
353 if opts['stat']:
353 for chunk, label in patch.diffstatui(difflines, width=width,
354 for chunk, label in patch.diffstatui(difflines, width=width,
354 git=True):
355 git=True):
355 ui.write(chunk, label=label)
356 ui.write(chunk, label=label)
356 finally:
357 finally:
357 fp.close()
358 fp.close()
358
359
359 def checkparents(repo, state):
360 def checkparents(repo, state):
360 """check parent while resuming an unshelve"""
361 """check parent while resuming an unshelve"""
361 if state.parents != repo.dirstate.parents():
362 if state.parents != repo.dirstate.parents():
362 raise util.Abort(_('working directory parents do not match unshelve '
363 raise util.Abort(_('working directory parents do not match unshelve '
363 'state'))
364 'state'))
364
365
365 def pathtofiles(repo, files):
366 def pathtofiles(repo, files):
366 cwd = repo.getcwd()
367 cwd = repo.getcwd()
367 return [repo.pathto(f, cwd) for f in files]
368 return [repo.pathto(f, cwd) for f in files]
368
369
369 def unshelveabort(ui, repo, state, opts):
370 def unshelveabort(ui, repo, state, opts):
370 """subcommand that abort an in-progress unshelve"""
371 """subcommand that abort an in-progress unshelve"""
371 wlock = repo.wlock()
372 wlock = repo.wlock()
372 lock = None
373 lock = None
373 try:
374 try:
374 checkparents(repo, state)
375 checkparents(repo, state)
375
376
376 util.rename(repo.join('unshelverebasestate'),
377 util.rename(repo.join('unshelverebasestate'),
377 repo.join('rebasestate'))
378 repo.join('rebasestate'))
378 try:
379 try:
379 rebase.rebase(ui, repo, **{
380 rebase.rebase(ui, repo, **{
380 'abort' : True
381 'abort' : True
381 })
382 })
382 except Exception:
383 except Exception:
383 util.rename(repo.join('rebasestate'),
384 util.rename(repo.join('rebasestate'),
384 repo.join('unshelverebasestate'))
385 repo.join('unshelverebasestate'))
385 raise
386 raise
386
387
387 lock = repo.lock()
388 lock = repo.lock()
388
389
389 mergefiles(ui, repo, state.wctx, state.pendingctx)
390 mergefiles(ui, repo, state.wctx, state.pendingctx)
390
391
391 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
392 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
392 shelvedstate.clear(repo)
393 shelvedstate.clear(repo)
393 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
394 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
394 finally:
395 finally:
395 lockmod.release(lock, wlock)
396 lockmod.release(lock, wlock)
396
397
397 def mergefiles(ui, repo, wctx, shelvectx):
398 def mergefiles(ui, repo, wctx, shelvectx):
398 """updates to wctx and merges the changes from shelvectx into the
399 """updates to wctx and merges the changes from shelvectx into the
399 dirstate."""
400 dirstate."""
400 oldquiet = ui.quiet
401 oldquiet = ui.quiet
401 try:
402 try:
402 ui.quiet = True
403 ui.quiet = True
403 hg.update(repo, wctx.node())
404 hg.update(repo, wctx.node())
404 files = []
405 files = []
405 files.extend(shelvectx.files())
406 files.extend(shelvectx.files())
406 files.extend(shelvectx.parents()[0].files())
407 files.extend(shelvectx.parents()[0].files())
407
408
408 # revert will overwrite unknown files, so move them out of the way
409 # revert will overwrite unknown files, so move them out of the way
409 m, a, r, d, u = repo.status(unknown=True)[:5]
410 m, a, r, d, u = repo.status(unknown=True)[:5]
410 for file in u:
411 for file in u:
411 if file in files:
412 if file in files:
412 util.rename(file, file + ".orig")
413 util.rename(file, file + ".orig")
413 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
414 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
414 *pathtofiles(repo, files),
415 *pathtofiles(repo, files),
415 **{'no_backup': True})
416 **{'no_backup': True})
416 finally:
417 finally:
417 ui.quiet = oldquiet
418 ui.quiet = oldquiet
418
419
419 def unshelvecleanup(ui, repo, name, opts):
420 def unshelvecleanup(ui, repo, name, opts):
420 """remove related files after an unshelve"""
421 """remove related files after an unshelve"""
421 if not opts['keep']:
422 if not opts['keep']:
422 for filetype in 'hg files patch'.split():
423 for filetype in 'hg files patch'.split():
423 shelvedfile(repo, name, filetype).unlink()
424 shelvedfile(repo, name, filetype).unlink()
424
425
425 def unshelvecontinue(ui, repo, state, opts):
426 def unshelvecontinue(ui, repo, state, opts):
426 """subcommand to continue an in-progress unshelve"""
427 """subcommand to continue an in-progress unshelve"""
427 # We're finishing off a merge. First parent is our original
428 # We're finishing off a merge. First parent is our original
428 # parent, second is the temporary "fake" commit we're unshelving.
429 # parent, second is the temporary "fake" commit we're unshelving.
429 wlock = repo.wlock()
430 wlock = repo.wlock()
430 lock = None
431 lock = None
431 try:
432 try:
432 checkparents(repo, state)
433 checkparents(repo, state)
433 ms = merge.mergestate(repo)
434 ms = merge.mergestate(repo)
434 if [f for f in ms if ms[f] == 'u']:
435 if [f for f in ms if ms[f] == 'u']:
435 raise util.Abort(
436 raise util.Abort(
436 _("unresolved conflicts, can't continue"),
437 _("unresolved conflicts, can't continue"),
437 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
438 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
438
439
439 lock = repo.lock()
440 lock = repo.lock()
440
441
441 util.rename(repo.join('unshelverebasestate'),
442 util.rename(repo.join('unshelverebasestate'),
442 repo.join('rebasestate'))
443 repo.join('rebasestate'))
443 try:
444 try:
444 rebase.rebase(ui, repo, **{
445 rebase.rebase(ui, repo, **{
445 'continue' : True
446 'continue' : True
446 })
447 })
447 except Exception:
448 except Exception:
448 util.rename(repo.join('rebasestate'),
449 util.rename(repo.join('rebasestate'),
449 repo.join('unshelverebasestate'))
450 repo.join('unshelverebasestate'))
450 raise
451 raise
451
452
452 shelvectx = repo['tip']
453 shelvectx = repo['tip']
453 if not shelvectx in state.pendingctx.children():
454 if not shelvectx in state.pendingctx.children():
454 # rebase was a no-op, so it produced no child commit
455 # rebase was a no-op, so it produced no child commit
455 shelvectx = state.pendingctx
456 shelvectx = state.pendingctx
456
457
457 mergefiles(ui, repo, state.wctx, shelvectx)
458 mergefiles(ui, repo, state.wctx, shelvectx)
458
459
459 state.stripnodes.append(shelvectx.node())
460 state.stripnodes.append(shelvectx.node())
460 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
461 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
461 shelvedstate.clear(repo)
462 shelvedstate.clear(repo)
462 unshelvecleanup(ui, repo, state.name, opts)
463 unshelvecleanup(ui, repo, state.name, opts)
463 ui.status(_("unshelve of '%s' complete\n") % state.name)
464 ui.status(_("unshelve of '%s' complete\n") % state.name)
464 finally:
465 finally:
465 lockmod.release(lock, wlock)
466 lockmod.release(lock, wlock)
466
467
467 @command('unshelve',
468 @command('unshelve',
468 [('a', 'abort', None,
469 [('a', 'abort', None,
469 _('abort an incomplete unshelve operation')),
470 _('abort an incomplete unshelve operation')),
470 ('c', 'continue', None,
471 ('c', 'continue', None,
471 _('continue an incomplete unshelve operation')),
472 _('continue an incomplete unshelve operation')),
472 ('', 'keep', None,
473 ('', 'keep', None,
473 _('keep shelve after unshelving')),
474 _('keep shelve after unshelving')),
474 ('', 'date', '',
475 ('', 'date', '',
475 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
476 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
476 _('hg unshelve [SHELVED]'))
477 _('hg unshelve [SHELVED]'))
477 def unshelve(ui, repo, *shelved, **opts):
478 def unshelve(ui, repo, *shelved, **opts):
478 """restore a shelved change to the working directory
479 """restore a shelved change to the working directory
479
480
480 This command accepts an optional name of a shelved change to
481 This command accepts an optional name of a shelved change to
481 restore. If none is given, the most recent shelved change is used.
482 restore. If none is given, the most recent shelved change is used.
482
483
483 If a shelved change is applied successfully, the bundle that
484 If a shelved change is applied successfully, the bundle that
484 contains the shelved changes is deleted afterwards.
485 contains the shelved changes is deleted afterwards.
485
486
486 Since you can restore a shelved change on top of an arbitrary
487 Since you can restore a shelved change on top of an arbitrary
487 commit, it is possible that unshelving will result in a conflict
488 commit, it is possible that unshelving will result in a conflict
488 between your changes and the commits you are unshelving onto. If
489 between your changes and the commits you are unshelving onto. If
489 this occurs, you must resolve the conflict, then use
490 this occurs, you must resolve the conflict, then use
490 ``--continue`` to complete the unshelve operation. (The bundle
491 ``--continue`` to complete the unshelve operation. (The bundle
491 will not be deleted until you successfully complete the unshelve.)
492 will not be deleted until you successfully complete the unshelve.)
492
493
493 (Alternatively, you can use ``--abort`` to abandon an unshelve
494 (Alternatively, you can use ``--abort`` to abandon an unshelve
494 that causes a conflict. This reverts the unshelved changes, and
495 that causes a conflict. This reverts the unshelved changes, and
495 does not delete the bundle.)
496 does not delete the bundle.)
496 """
497 """
497 abortf = opts['abort']
498 abortf = opts['abort']
498 continuef = opts['continue']
499 continuef = opts['continue']
499 if not abortf and not continuef:
500 if not abortf and not continuef:
500 cmdutil.checkunfinished(repo)
501 cmdutil.checkunfinished(repo)
501
502
502 if abortf or continuef:
503 if abortf or continuef:
503 if abortf and continuef:
504 if abortf and continuef:
504 raise util.Abort(_('cannot use both abort and continue'))
505 raise util.Abort(_('cannot use both abort and continue'))
505 if shelved:
506 if shelved:
506 raise util.Abort(_('cannot combine abort/continue with '
507 raise util.Abort(_('cannot combine abort/continue with '
507 'naming a shelved change'))
508 'naming a shelved change'))
508
509
509 try:
510 try:
510 state = shelvedstate.load(repo)
511 state = shelvedstate.load(repo)
511 except IOError, err:
512 except IOError, err:
512 if err.errno != errno.ENOENT:
513 if err.errno != errno.ENOENT:
513 raise
514 raise
514 raise util.Abort(_('no unshelve operation underway'))
515 raise util.Abort(_('no unshelve operation underway'))
515
516
516 if abortf:
517 if abortf:
517 return unshelveabort(ui, repo, state, opts)
518 return unshelveabort(ui, repo, state, opts)
518 elif continuef:
519 elif continuef:
519 return unshelvecontinue(ui, repo, state, opts)
520 return unshelvecontinue(ui, repo, state, opts)
520 elif len(shelved) > 1:
521 elif len(shelved) > 1:
521 raise util.Abort(_('can only unshelve one change at a time'))
522 raise util.Abort(_('can only unshelve one change at a time'))
522 elif not shelved:
523 elif not shelved:
523 shelved = listshelves(repo)
524 shelved = listshelves(repo)
524 if not shelved:
525 if not shelved:
525 raise util.Abort(_('no shelved changes to apply!'))
526 raise util.Abort(_('no shelved changes to apply!'))
526 basename = util.split(shelved[0][1])[1]
527 basename = util.split(shelved[0][1])[1]
527 ui.status(_("unshelving change '%s'\n") % basename)
528 ui.status(_("unshelving change '%s'\n") % basename)
528 else:
529 else:
529 basename = shelved[0]
530 basename = shelved[0]
530
531
531 if not shelvedfile(repo, basename, 'files').exists():
532 if not shelvedfile(repo, basename, 'files').exists():
532 raise util.Abort(_("shelved change '%s' not found") % basename)
533 raise util.Abort(_("shelved change '%s' not found") % basename)
533
534
534 oldquiet = ui.quiet
535 oldquiet = ui.quiet
535 wlock = lock = tr = None
536 wlock = lock = tr = None
536 try:
537 try:
537 lock = repo.lock()
538 lock = repo.lock()
538 wlock = repo.wlock()
539 wlock = repo.wlock()
539
540
540 tr = repo.transaction('unshelve', report=lambda x: None)
541 tr = repo.transaction('unshelve', report=lambda x: None)
541 oldtiprev = len(repo)
542 oldtiprev = len(repo)
542
543
543 pctx = repo['.']
544 pctx = repo['.']
544 tmpwctx = pctx
545 tmpwctx = pctx
545 # The goal is to have a commit structure like so:
546 # The goal is to have a commit structure like so:
546 # ...-> pctx -> tmpwctx -> shelvectx
547 # ...-> pctx -> tmpwctx -> shelvectx
547 # where tmpwctx is an optional commit with the user's pending changes
548 # where tmpwctx is an optional commit with the user's pending changes
548 # and shelvectx is the unshelved changes. Then we merge it all down
549 # and shelvectx is the unshelved changes. Then we merge it all down
549 # to the original pctx.
550 # to the original pctx.
550
551
551 # Store pending changes in a commit
552 # Store pending changes in a commit
552 m, a, r, d = repo.status()[:4]
553 m, a, r, d = repo.status()[:4]
553 if m or a or r or d:
554 if m or a or r or d:
554 ui.status(_("temporarily committing pending changes "
555 ui.status(_("temporarily committing pending changes "
555 "(restore with 'hg unshelve --abort')\n"))
556 "(restore with 'hg unshelve --abort')\n"))
556 def commitfunc(ui, repo, message, match, opts):
557 def commitfunc(ui, repo, message, match, opts):
557 hasmq = util.safehasattr(repo, 'mq')
558 hasmq = util.safehasattr(repo, 'mq')
558 if hasmq:
559 if hasmq:
559 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
560 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
560
561
561 try:
562 try:
562 return repo.commit(message, 'shelve@localhost',
563 return repo.commit(message, 'shelve@localhost',
563 opts.get('date'), match)
564 opts.get('date'), match)
564 finally:
565 finally:
565 if hasmq:
566 if hasmq:
566 repo.mq.checkapplied = saved
567 repo.mq.checkapplied = saved
567
568
568 tempopts = {}
569 tempopts = {}
569 tempopts['message'] = "pending changes temporary commit"
570 tempopts['message'] = "pending changes temporary commit"
570 tempopts['date'] = opts.get('date')
571 tempopts['date'] = opts.get('date')
571 ui.quiet = True
572 ui.quiet = True
572 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
573 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
573 tmpwctx = repo[node]
574 tmpwctx = repo[node]
574
575
575 ui.quiet = True
576 ui.quiet = True
576 shelvedfile(repo, basename, 'hg').applybundle()
577 shelvedfile(repo, basename, 'hg').applybundle()
577 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
578 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
578 phases.retractboundary(repo, phases.secret, nodes)
579 phases.retractboundary(repo, phases.secret, nodes)
579
580
580 ui.quiet = oldquiet
581 ui.quiet = oldquiet
581
582
582 shelvectx = repo['tip']
583 shelvectx = repo['tip']
583
584
584 # If the shelve is not immediately on top of the commit
585 # If the shelve is not immediately on top of the commit
585 # we'll be merging with, rebase it to be on top.
586 # we'll be merging with, rebase it to be on top.
586 if tmpwctx.node() != shelvectx.parents()[0].node():
587 if tmpwctx.node() != shelvectx.parents()[0].node():
587 ui.status(_('rebasing shelved changes\n'))
588 ui.status(_('rebasing shelved changes\n'))
588 try:
589 try:
589 rebase.rebase(ui, repo, **{
590 rebase.rebase(ui, repo, **{
590 'rev' : [shelvectx.rev()],
591 'rev' : [shelvectx.rev()],
591 'dest' : str(tmpwctx.rev()),
592 'dest' : str(tmpwctx.rev()),
592 'keep' : True,
593 'keep' : True,
593 })
594 })
594 except error.InterventionRequired:
595 except error.InterventionRequired:
595 tr.close()
596 tr.close()
596
597
597 stripnodes = [repo.changelog.node(rev)
598 stripnodes = [repo.changelog.node(rev)
598 for rev in xrange(oldtiprev, len(repo))]
599 for rev in xrange(oldtiprev, len(repo))]
599 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
600 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
600
601
601 util.rename(repo.join('rebasestate'),
602 util.rename(repo.join('rebasestate'),
602 repo.join('unshelverebasestate'))
603 repo.join('unshelverebasestate'))
603 raise error.InterventionRequired(
604 raise error.InterventionRequired(
604 _("unresolved conflicts (see 'hg resolve', then "
605 _("unresolved conflicts (see 'hg resolve', then "
605 "'hg unshelve --continue')"))
606 "'hg unshelve --continue')"))
606
607
607 # refresh ctx after rebase completes
608 # refresh ctx after rebase completes
608 shelvectx = repo['tip']
609 shelvectx = repo['tip']
609
610
610 if not shelvectx in tmpwctx.children():
611 if not shelvectx in tmpwctx.children():
611 # rebase was a no-op, so it produced no child commit
612 # rebase was a no-op, so it produced no child commit
612 shelvectx = tmpwctx
613 shelvectx = tmpwctx
613
614
614 mergefiles(ui, repo, pctx, shelvectx)
615 mergefiles(ui, repo, pctx, shelvectx)
615 shelvedstate.clear(repo)
616 shelvedstate.clear(repo)
616
617
617 # The transaction aborting will strip all the commits for us,
618 # The transaction aborting will strip all the commits for us,
618 # but it doesn't update the inmemory structures, so addchangegroup
619 # but it doesn't update the inmemory structures, so addchangegroup
619 # hooks still fire and try to operate on the missing commits.
620 # hooks still fire and try to operate on the missing commits.
620 # Clean up manually to prevent this.
621 # Clean up manually to prevent this.
621 repo.unfiltered().changelog.strip(oldtiprev, tr)
622 repo.unfiltered().changelog.strip(oldtiprev, tr)
622
623
623 unshelvecleanup(ui, repo, basename, opts)
624 unshelvecleanup(ui, repo, basename, opts)
624 finally:
625 finally:
625 ui.quiet = oldquiet
626 ui.quiet = oldquiet
626 if tr:
627 if tr:
627 tr.release()
628 tr.release()
628 lockmod.release(lock, wlock)
629 lockmod.release(lock, wlock)
629
630
630 @command('shelve',
631 @command('shelve',
631 [('A', 'addremove', None,
632 [('A', 'addremove', None,
632 _('mark new/missing files as added/removed before shelving')),
633 _('mark new/missing files as added/removed before shelving')),
633 ('', 'cleanup', None,
634 ('', 'cleanup', None,
634 _('delete all shelved changes')),
635 _('delete all shelved changes')),
635 ('', 'date', '',
636 ('', 'date', '',
636 _('shelve with the specified commit date'), _('DATE')),
637 _('shelve with the specified commit date'), _('DATE')),
637 ('d', 'delete', None,
638 ('d', 'delete', None,
638 _('delete the named shelved change(s)')),
639 _('delete the named shelved change(s)')),
639 ('e', 'edit', False,
640 ('e', 'edit', False,
640 _('invoke editor on commit messages')),
641 _('invoke editor on commit messages')),
641 ('l', 'list', None,
642 ('l', 'list', None,
642 _('list current shelves')),
643 _('list current shelves')),
643 ('m', 'message', '',
644 ('m', 'message', '',
644 _('use text as shelve message'), _('TEXT')),
645 _('use text as shelve message'), _('TEXT')),
645 ('n', 'name', '',
646 ('n', 'name', '',
646 _('use the given name for the shelved commit'), _('NAME')),
647 _('use the given name for the shelved commit'), _('NAME')),
647 ('p', 'patch', None,
648 ('p', 'patch', None,
648 _('show patch')),
649 _('show patch')),
649 ('', 'stat', None,
650 ('', 'stat', None,
650 _('output diffstat-style summary of changes'))] + commands.walkopts,
651 _('output diffstat-style summary of changes'))] + commands.walkopts,
651 _('hg shelve [OPTION]... [FILE]...'))
652 _('hg shelve [OPTION]... [FILE]...'))
652 def shelvecmd(ui, repo, *pats, **opts):
653 def shelvecmd(ui, repo, *pats, **opts):
653 '''save and set aside changes from the working directory
654 '''save and set aside changes from the working directory
654
655
655 Shelving takes files that "hg status" reports as not clean, saves
656 Shelving takes files that "hg status" reports as not clean, saves
656 the modifications to a bundle (a shelved change), and reverts the
657 the modifications to a bundle (a shelved change), and reverts the
657 files so that their state in the working directory becomes clean.
658 files so that their state in the working directory becomes clean.
658
659
659 To restore these changes to the working directory, using "hg
660 To restore these changes to the working directory, using "hg
660 unshelve"; this will work even if you switch to a different
661 unshelve"; this will work even if you switch to a different
661 commit.
662 commit.
662
663
663 When no files are specified, "hg shelve" saves all not-clean
664 When no files are specified, "hg shelve" saves all not-clean
664 files. If specific files or directories are named, only changes to
665 files. If specific files or directories are named, only changes to
665 those files are shelved.
666 those files are shelved.
666
667
667 Each shelved change has a name that makes it easier to find later.
668 Each shelved change has a name that makes it easier to find later.
668 The name of a shelved change defaults to being based on the active
669 The name of a shelved change defaults to being based on the active
669 bookmark, or if there is no active bookmark, the current named
670 bookmark, or if there is no active bookmark, the current named
670 branch. To specify a different name, use ``--name``.
671 branch. To specify a different name, use ``--name``.
671
672
672 To see a list of existing shelved changes, use the ``--list``
673 To see a list of existing shelved changes, use the ``--list``
673 option. For each shelved change, this will print its name, age,
674 option. For each shelved change, this will print its name, age,
674 and description; use ``--patch`` or ``--stat`` for more details.
675 and description; use ``--patch`` or ``--stat`` for more details.
675
676
676 To delete specific shelved changes, use ``--delete``. To delete
677 To delete specific shelved changes, use ``--delete``. To delete
677 all shelved changes, use ``--cleanup``.
678 all shelved changes, use ``--cleanup``.
678 '''
679 '''
679 cmdutil.checkunfinished(repo)
680 cmdutil.checkunfinished(repo)
680
681
681 allowables = [
682 allowables = [
682 ('addremove', 'create'), # 'create' is pseudo action
683 ('addremove', 'create'), # 'create' is pseudo action
683 ('cleanup', 'cleanup'),
684 ('cleanup', 'cleanup'),
684 # ('date', 'create'), # ignored for passing '--date "0 0"' in tests
685 # ('date', 'create'), # ignored for passing '--date "0 0"' in tests
685 ('delete', 'delete'),
686 ('delete', 'delete'),
686 ('edit', 'create'),
687 ('edit', 'create'),
687 ('list', 'list'),
688 ('list', 'list'),
688 ('message', 'create'),
689 ('message', 'create'),
689 ('name', 'create'),
690 ('name', 'create'),
690 ('patch', 'list'),
691 ('patch', 'list'),
691 ('stat', 'list'),
692 ('stat', 'list'),
692 ]
693 ]
693 def checkopt(opt):
694 def checkopt(opt):
694 if opts[opt]:
695 if opts[opt]:
695 for i, allowable in allowables:
696 for i, allowable in allowables:
696 if opts[i] and opt != allowable:
697 if opts[i] and opt != allowable:
697 raise util.Abort(_("options '--%s' and '--%s' may not be "
698 raise util.Abort(_("options '--%s' and '--%s' may not be "
698 "used together") % (opt, i))
699 "used together") % (opt, i))
699 return True
700 return True
700 if checkopt('cleanup'):
701 if checkopt('cleanup'):
701 if pats:
702 if pats:
702 raise util.Abort(_("cannot specify names when using '--cleanup'"))
703 raise util.Abort(_("cannot specify names when using '--cleanup'"))
703 return cleanupcmd(ui, repo)
704 return cleanupcmd(ui, repo)
704 elif checkopt('delete'):
705 elif checkopt('delete'):
705 return deletecmd(ui, repo, pats)
706 return deletecmd(ui, repo, pats)
706 elif checkopt('list'):
707 elif checkopt('list'):
707 return listcmd(ui, repo, pats, opts)
708 return listcmd(ui, repo, pats, opts)
708 else:
709 else:
709 for i in ('patch', 'stat'):
710 for i in ('patch', 'stat'):
710 if opts[i]:
711 if opts[i]:
711 raise util.Abort(_("option '--%s' may not be "
712 raise util.Abort(_("option '--%s' may not be "
712 "used when shelving a change") % (i,))
713 "used when shelving a change") % (i,))
713 return createcmd(ui, repo, pats, opts)
714 return createcmd(ui, repo, pats, opts)
714
715
715 def extsetup(ui):
716 def extsetup(ui):
716 cmdutil.unfinishedstates.append(
717 cmdutil.unfinishedstates.append(
717 [shelvedstate._filename, False, False,
718 [shelvedstate._filename, False, False,
718 _('unshelve already in progress'),
719 _('unshelve already in progress'),
719 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
720 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
General Comments 0
You need to be logged in to leave comments. Login now