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