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