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