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