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