##// END OF EJS Templates
shelve: new output format for shelve listings...
David Soria Parra -
r19855:a3b28588 default
parent child Browse files
Show More
@@ -1,607 +1,605
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 try:
24 try:
25 import cPickle as pickle
25 import cPickle as pickle
26 pickle.dump # import now
26 pickle.dump # import now
27 except ImportError:
27 except ImportError:
28 import pickle
28 import pickle
29 from mercurial.i18n import _
29 from mercurial.i18n import _
30 from mercurial.node import nullid
30 from mercurial.node import nullid
31 from mercurial import changegroup, cmdutil, scmutil, phases
31 from mercurial import changegroup, cmdutil, scmutil, phases
32 from mercurial import error, hg, mdiff, merge, patch, repair, util
32 from mercurial import error, hg, mdiff, merge, patch, repair, util
33 from mercurial import templatefilters
33 from mercurial import templatefilters
34 from mercurial import lock as lockmod
34 from mercurial import lock as lockmod
35 import errno
35 import errno
36
36
37 cmdtable = {}
37 cmdtable = {}
38 command = cmdutil.command(cmdtable)
38 command = cmdutil.command(cmdtable)
39 testedwith = 'internal'
39 testedwith = 'internal'
40
40
41 class shelvedfile(object):
41 class shelvedfile(object):
42 """Handles common functions on shelve files (.hg/.files/.patch) using
42 """Handles common functions on shelve files (.hg/.files/.patch) using
43 the vfs layer"""
43 the vfs layer"""
44 def __init__(self, repo, name, filetype=None):
44 def __init__(self, repo, name, filetype=None):
45 self.repo = repo
45 self.repo = repo
46 self.name = name
46 self.name = name
47 self.vfs = scmutil.vfs(repo.join('shelved'))
47 self.vfs = scmutil.vfs(repo.join('shelved'))
48 if filetype:
48 if filetype:
49 self.fname = name + '.' + filetype
49 self.fname = name + '.' + filetype
50 else:
50 else:
51 self.fname = name
51 self.fname = name
52
52
53 def exists(self):
53 def exists(self):
54 return self.vfs.exists(self.fname)
54 return self.vfs.exists(self.fname)
55
55
56 def filename(self):
56 def filename(self):
57 return self.vfs.join(self.fname)
57 return self.vfs.join(self.fname)
58
58
59 def unlink(self):
59 def unlink(self):
60 util.unlink(self.filename())
60 util.unlink(self.filename())
61
61
62 def stat(self):
62 def stat(self):
63 return self.vfs.stat(self.fname)
63 return self.vfs.stat(self.fname)
64
64
65 def opener(self, mode='rb'):
65 def opener(self, mode='rb'):
66 try:
66 try:
67 return self.vfs(self.fname, mode)
67 return self.vfs(self.fname, mode)
68 except IOError, err:
68 except IOError, err:
69 if err.errno != errno.ENOENT:
69 if err.errno != errno.ENOENT:
70 raise
70 raise
71 if mode[0] in 'wa':
71 if mode[0] in 'wa':
72 try:
72 try:
73 self.vfs.mkdir()
73 self.vfs.mkdir()
74 return self.vfs(self.fname, mode)
74 return self.vfs(self.fname, mode)
75 except IOError, err:
75 except IOError, err:
76 if err.errno != errno.EEXIST:
76 if err.errno != errno.EEXIST:
77 raise
77 raise
78 elif mode[0] == 'r':
78 elif mode[0] == 'r':
79 raise util.Abort(_("shelved change '%s' not found") %
79 raise util.Abort(_("shelved change '%s' not found") %
80 self.name)
80 self.name)
81
81
82 class shelvedstate(object):
82 class shelvedstate(object):
83 """Handles saving and restoring a shelved state. Ensures that different
83 """Handles saving and restoring a shelved state. Ensures that different
84 versions of a shelved state are possible and handles them appropriate"""
84 versions of a shelved state are possible and handles them appropriate"""
85 _version = 1
85 _version = 1
86 _filename = 'shelvedstate'
86 _filename = 'shelvedstate'
87
87
88 @classmethod
88 @classmethod
89 def load(cls, repo):
89 def load(cls, repo):
90 fp = repo.opener(cls._filename)
90 fp = repo.opener(cls._filename)
91 (version, name, parents, stripnodes) = pickle.load(fp)
91 (version, name, parents, stripnodes) = pickle.load(fp)
92
92
93 if version != cls._version:
93 if version != cls._version:
94 raise util.Abort(_('this version of shelve is incompatible '
94 raise util.Abort(_('this version of shelve is incompatible '
95 'with the version used in this repo'))
95 'with the version used in this repo'))
96
96
97 obj = cls()
97 obj = cls()
98 obj.name = name
98 obj.name = name
99 obj.parents = parents
99 obj.parents = parents
100 obj.stripnodes = stripnodes
100 obj.stripnodes = stripnodes
101
101
102 return obj
102 return obj
103
103
104 @classmethod
104 @classmethod
105 def save(cls, repo, name, stripnodes):
105 def save(cls, repo, name, stripnodes):
106 fp = repo.opener(cls._filename, 'wb')
106 fp = repo.opener(cls._filename, 'wb')
107 pickle.dump((cls._version, name,
107 pickle.dump((cls._version, name,
108 repo.dirstate.parents(),
108 repo.dirstate.parents(),
109 stripnodes), fp)
109 stripnodes), fp)
110 fp.close()
110 fp.close()
111
111
112 @staticmethod
112 @staticmethod
113 def clear(repo):
113 def clear(repo):
114 util.unlinkpath(repo.join('shelvedstate'), ignoremissing=True)
114 util.unlinkpath(repo.join('shelvedstate'), ignoremissing=True)
115
115
116 def createcmd(ui, repo, pats, opts):
116 def createcmd(ui, repo, pats, opts):
117 def publicancestors(ctx):
117 def publicancestors(ctx):
118 """Compute the heads of the public ancestors of a commit.
118 """Compute the heads of the public ancestors of a commit.
119
119
120 Much faster than the revset heads(ancestors(ctx) - draft())"""
120 Much faster than the revset heads(ancestors(ctx) - draft())"""
121 seen = set()
121 seen = set()
122 visit = util.deque()
122 visit = util.deque()
123 visit.append(ctx)
123 visit.append(ctx)
124 while visit:
124 while visit:
125 ctx = visit.popleft()
125 ctx = visit.popleft()
126 for parent in ctx.parents():
126 for parent in ctx.parents():
127 rev = parent.rev()
127 rev = parent.rev()
128 if rev not in seen:
128 if rev not in seen:
129 seen.add(rev)
129 seen.add(rev)
130 if parent.mutable():
130 if parent.mutable():
131 visit.append(parent)
131 visit.append(parent)
132 else:
132 else:
133 yield parent.node()
133 yield parent.node()
134
134
135 wctx = repo[None]
135 wctx = repo[None]
136 parents = wctx.parents()
136 parents = wctx.parents()
137 if len(parents) > 1:
137 if len(parents) > 1:
138 raise util.Abort(_('cannot shelve while merging'))
138 raise util.Abort(_('cannot shelve while merging'))
139 parent = parents[0]
139 parent = parents[0]
140
140
141 # we never need the user, so we use a generic user for all shelve operations
141 # we never need the user, so we use a generic user for all shelve operations
142 user = 'shelve@localhost'
142 user = 'shelve@localhost'
143 label = repo._bookmarkcurrent or parent.branch() or 'default'
143 label = repo._bookmarkcurrent or parent.branch() or 'default'
144
144
145 # slashes aren't allowed in filenames, therefore we rename it
145 # slashes aren't allowed in filenames, therefore we rename it
146 origlabel, label = label, label.replace('/', '_')
146 origlabel, label = label, label.replace('/', '_')
147
147
148 def gennames():
148 def gennames():
149 yield label
149 yield label
150 for i in xrange(1, 100):
150 for i in xrange(1, 100):
151 yield '%s-%02d' % (label, i)
151 yield '%s-%02d' % (label, i)
152
152
153 shelvedfiles = []
153 shelvedfiles = []
154
154
155 def commitfunc(ui, repo, message, match, opts):
155 def commitfunc(ui, repo, message, match, opts):
156 # check modified, added, removed, deleted only
156 # check modified, added, removed, deleted only
157 for flist in repo.status(match=match)[:4]:
157 for flist in repo.status(match=match)[:4]:
158 shelvedfiles.extend(flist)
158 shelvedfiles.extend(flist)
159 return repo.commit(message, user, opts.get('date'), match)
159 return repo.commit(message, user, opts.get('date'), match)
160
160
161 if parent.node() != nullid:
161 if parent.node() != nullid:
162 desc = parent.description().split('\n', 1)[0]
162 desc = parent.description().split('\n', 1)[0]
163 desc = _('shelved from %s (%s): %s') % (label, str(parent)[:8], desc)
164 else:
163 else:
165 desc = '(empty repository)'
164 desc = '(empty repository)'
166
165
167 if not opts['message']:
166 if not opts['message']:
168 opts['message'] = desc
167 opts['message'] = desc
169
168
170 name = opts['name']
169 name = opts['name']
171
170
172 wlock = lock = tr = None
171 wlock = lock = tr = None
173 try:
172 try:
174 wlock = repo.wlock()
173 wlock = repo.wlock()
175 lock = repo.lock()
174 lock = repo.lock()
176
175
177 # use an uncommited transaction to generate the bundle to avoid
176 # use an uncommited transaction to generate the bundle to avoid
178 # pull races. ensure we don't print the abort message to stderr.
177 # pull races. ensure we don't print the abort message to stderr.
179 tr = repo.transaction('commit', report=lambda x: None)
178 tr = repo.transaction('commit', report=lambda x: None)
180
179
181 if name:
180 if name:
182 if shelvedfile(repo, name, 'hg').exists():
181 if shelvedfile(repo, name, 'hg').exists():
183 raise util.Abort(_("a shelved change named '%s' already exists")
182 raise util.Abort(_("a shelved change named '%s' already exists")
184 % name)
183 % name)
185 else:
184 else:
186 for n in gennames():
185 for n in gennames():
187 if not shelvedfile(repo, n, 'hg').exists():
186 if not shelvedfile(repo, n, 'hg').exists():
188 name = n
187 name = n
189 break
188 break
190 else:
189 else:
191 raise util.Abort(_("too many shelved changes named '%s'") %
190 raise util.Abort(_("too many shelved changes named '%s'") %
192 label)
191 label)
193
192
194 # ensure we are not creating a subdirectory or a hidden file
193 # ensure we are not creating a subdirectory or a hidden file
195 if '/' in name or '\\' in name:
194 if '/' in name or '\\' in name:
196 raise util.Abort(_('shelved change names may not contain slashes'))
195 raise util.Abort(_('shelved change names may not contain slashes'))
197 if name.startswith('.'):
196 if name.startswith('.'):
198 raise util.Abort(_("shelved change names may not start with '.'"))
197 raise util.Abort(_("shelved change names may not start with '.'"))
199
198
200 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
199 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
201
200
202 if not node:
201 if not node:
203 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
202 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
204 if stat[3]:
203 if stat[3]:
205 ui.status(_("nothing changed (%d missing files, see "
204 ui.status(_("nothing changed (%d missing files, see "
206 "'hg status')\n") % len(stat[3]))
205 "'hg status')\n") % len(stat[3]))
207 else:
206 else:
208 ui.status(_("nothing changed\n"))
207 ui.status(_("nothing changed\n"))
209 return 1
208 return 1
210
209
211 phases.retractboundary(repo, phases.secret, [node])
210 phases.retractboundary(repo, phases.secret, [node])
212
211
213 fp = shelvedfile(repo, name, 'files').opener('wb')
212 fp = shelvedfile(repo, name, 'files').opener('wb')
214 fp.write('\0'.join(shelvedfiles))
213 fp.write('\0'.join(shelvedfiles))
215
214
216 bases = list(publicancestors(repo[node]))
215 bases = list(publicancestors(repo[node]))
217 cg = repo.changegroupsubset(bases, [node], 'shelve')
216 cg = repo.changegroupsubset(bases, [node], 'shelve')
218 changegroup.writebundle(cg, shelvedfile(repo, name, 'hg').filename(),
217 changegroup.writebundle(cg, shelvedfile(repo, name, 'hg').filename(),
219 'HG10UN')
218 'HG10UN')
220 cmdutil.export(repo, [node],
219 cmdutil.export(repo, [node],
221 fp=shelvedfile(repo, name, 'patch').opener('wb'),
220 fp=shelvedfile(repo, name, 'patch').opener('wb'),
222 opts=mdiff.diffopts(git=True))
221 opts=mdiff.diffopts(git=True))
223
222
224 if ui.formatted():
223 if ui.formatted():
225 desc = util.ellipsis(desc, ui.termwidth())
224 desc = util.ellipsis(desc, ui.termwidth())
226 ui.status(desc + '\n')
227 ui.status(_('shelved as %s\n') % name)
225 ui.status(_('shelved as %s\n') % name)
228 hg.update(repo, parent.node())
226 hg.update(repo, parent.node())
229 finally:
227 finally:
230 if tr:
228 if tr:
231 tr.abort()
229 tr.abort()
232 lockmod.release(lock, wlock)
230 lockmod.release(lock, wlock)
233
231
234 def cleanupcmd(ui, repo):
232 def cleanupcmd(ui, repo):
235 wlock = None
233 wlock = None
236 try:
234 try:
237 wlock = repo.wlock()
235 wlock = repo.wlock()
238 for (name, _) in repo.vfs.readdir('shelved'):
236 for (name, _) in repo.vfs.readdir('shelved'):
239 suffix = name.rsplit('.', 1)[-1]
237 suffix = name.rsplit('.', 1)[-1]
240 if suffix in ('hg', 'files', 'patch'):
238 if suffix in ('hg', 'files', 'patch'):
241 shelvedfile(repo, name).unlink()
239 shelvedfile(repo, name).unlink()
242 finally:
240 finally:
243 lockmod.release(wlock)
241 lockmod.release(wlock)
244
242
245 def deletecmd(ui, repo, pats):
243 def deletecmd(ui, repo, pats):
246 if not pats:
244 if not pats:
247 raise util.Abort(_('no shelved changes specified!'))
245 raise util.Abort(_('no shelved changes specified!'))
248 wlock = None
246 wlock = None
249 try:
247 try:
250 wlock = repo.wlock()
248 wlock = repo.wlock()
251 try:
249 try:
252 for name in pats:
250 for name in pats:
253 for suffix in 'hg files patch'.split():
251 for suffix in 'hg files patch'.split():
254 shelvedfile(repo, name, suffix).unlink()
252 shelvedfile(repo, name, suffix).unlink()
255 except OSError, err:
253 except OSError, err:
256 if err.errno != errno.ENOENT:
254 if err.errno != errno.ENOENT:
257 raise
255 raise
258 raise util.Abort(_("shelved change '%s' not found") % name)
256 raise util.Abort(_("shelved change '%s' not found") % name)
259 finally:
257 finally:
260 lockmod.release(wlock)
258 lockmod.release(wlock)
261
259
262 def listshelves(repo):
260 def listshelves(repo):
263 try:
261 try:
264 names = repo.vfs.readdir('shelved')
262 names = repo.vfs.readdir('shelved')
265 except OSError, err:
263 except OSError, err:
266 if err.errno != errno.ENOENT:
264 if err.errno != errno.ENOENT:
267 raise
265 raise
268 return []
266 return []
269 info = []
267 info = []
270 for (name, _) in names:
268 for (name, _) in names:
271 pfx, sfx = name.rsplit('.', 1)
269 pfx, sfx = name.rsplit('.', 1)
272 if not pfx or sfx != 'patch':
270 if not pfx or sfx != 'patch':
273 continue
271 continue
274 st = shelvedfile(repo, name).stat()
272 st = shelvedfile(repo, name).stat()
275 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
273 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
276 return sorted(info, reverse=True)
274 return sorted(info, reverse=True)
277
275
278 def listcmd(ui, repo, pats, opts):
276 def listcmd(ui, repo, pats, opts):
279 pats = set(pats)
277 pats = set(pats)
280 width = 80
278 width = 80
281 if not ui.plain():
279 if not ui.plain():
282 width = ui.termwidth()
280 width = ui.termwidth()
283 namelabel = 'shelve.newest'
281 namelabel = 'shelve.newest'
284 for mtime, name in listshelves(repo):
282 for mtime, name in listshelves(repo):
285 sname = util.split(name)[1]
283 sname = util.split(name)[1]
286 if pats and sname not in pats:
284 if pats and sname not in pats:
287 continue
285 continue
288 ui.write(sname, label=namelabel)
286 ui.write(sname, label=namelabel)
289 namelabel = 'shelve.name'
287 namelabel = 'shelve.name'
290 if ui.quiet:
288 if ui.quiet:
291 ui.write('\n')
289 ui.write('\n')
292 continue
290 continue
293 ui.write(' ' * (16 - len(sname)))
291 ui.write(' ' * (16 - len(sname)))
294 used = 16
292 used = 16
295 age = '[%s]' % templatefilters.age(util.makedate(mtime))
293 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
296 ui.write(age, label='shelve.age')
294 ui.write(age, label='shelve.age')
297 ui.write(' ' * (18 - len(age)))
295 ui.write(' ' * (12 - len(age)))
298 used += 18
296 used += 12
299 fp = open(name + '.patch', 'rb')
297 fp = open(name + '.patch', 'rb')
300 try:
298 try:
301 while True:
299 while True:
302 line = fp.readline()
300 line = fp.readline()
303 if not line:
301 if not line:
304 break
302 break
305 if not line.startswith('#'):
303 if not line.startswith('#'):
306 desc = line.rstrip()
304 desc = line.rstrip()
307 if ui.formatted():
305 if ui.formatted():
308 desc = util.ellipsis(desc, width - used)
306 desc = util.ellipsis(desc, width - used)
309 ui.write(desc)
307 ui.write(desc)
310 break
308 break
311 ui.write('\n')
309 ui.write('\n')
312 if not (opts['patch'] or opts['stat']):
310 if not (opts['patch'] or opts['stat']):
313 continue
311 continue
314 difflines = fp.readlines()
312 difflines = fp.readlines()
315 if opts['patch']:
313 if opts['patch']:
316 for chunk, label in patch.difflabel(iter, difflines):
314 for chunk, label in patch.difflabel(iter, difflines):
317 ui.write(chunk, label=label)
315 ui.write(chunk, label=label)
318 if opts['stat']:
316 if opts['stat']:
319 for chunk, label in patch.diffstatui(difflines, width=width,
317 for chunk, label in patch.diffstatui(difflines, width=width,
320 git=True):
318 git=True):
321 ui.write(chunk, label=label)
319 ui.write(chunk, label=label)
322 finally:
320 finally:
323 fp.close()
321 fp.close()
324
322
325 def readshelvedfiles(repo, basename):
323 def readshelvedfiles(repo, basename):
326 fp = shelvedfile(repo, basename, 'files').opener()
324 fp = shelvedfile(repo, basename, 'files').opener()
327 return fp.read().split('\0')
325 return fp.read().split('\0')
328
326
329 def checkparents(repo, state):
327 def checkparents(repo, state):
330 if state.parents != repo.dirstate.parents():
328 if state.parents != repo.dirstate.parents():
331 raise util.Abort(_('working directory parents do not match unshelve '
329 raise util.Abort(_('working directory parents do not match unshelve '
332 'state'))
330 'state'))
333
331
334 def unshelveabort(ui, repo, state, opts):
332 def unshelveabort(ui, repo, state, opts):
335 wlock = repo.wlock()
333 wlock = repo.wlock()
336 lock = None
334 lock = None
337 try:
335 try:
338 checkparents(repo, state)
336 checkparents(repo, state)
339 lock = repo.lock()
337 lock = repo.lock()
340 merge.mergestate(repo).reset()
338 merge.mergestate(repo).reset()
341 if opts['keep']:
339 if opts['keep']:
342 repo.setparents(repo.dirstate.parents()[0])
340 repo.setparents(repo.dirstate.parents()[0])
343 else:
341 else:
344 revertfiles = readshelvedfiles(repo, state.name)
342 revertfiles = readshelvedfiles(repo, state.name)
345 wctx = repo.parents()[0]
343 wctx = repo.parents()[0]
346 cmdutil.revert(ui, repo, wctx, [wctx.node(), nullid],
344 cmdutil.revert(ui, repo, wctx, [wctx.node(), nullid],
347 *revertfiles, no_backup=True)
345 *revertfiles, no_backup=True)
348 # fix up the weird dirstate states the merge left behind
346 # fix up the weird dirstate states the merge left behind
349 mf = wctx.manifest()
347 mf = wctx.manifest()
350 dirstate = repo.dirstate
348 dirstate = repo.dirstate
351 for f in revertfiles:
349 for f in revertfiles:
352 if f in mf:
350 if f in mf:
353 dirstate.normallookup(f)
351 dirstate.normallookup(f)
354 else:
352 else:
355 dirstate.drop(f)
353 dirstate.drop(f)
356 dirstate._pl = (wctx.node(), nullid)
354 dirstate._pl = (wctx.node(), nullid)
357 dirstate._dirty = True
355 dirstate._dirty = True
358 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
356 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
359 shelvedstate.clear(repo)
357 shelvedstate.clear(repo)
360 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
358 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
361 finally:
359 finally:
362 lockmod.release(lock, wlock)
360 lockmod.release(lock, wlock)
363
361
364 def unshelvecleanup(ui, repo, name, opts):
362 def unshelvecleanup(ui, repo, name, opts):
365 if not opts['keep']:
363 if not opts['keep']:
366 for filetype in 'hg files patch'.split():
364 for filetype in 'hg files patch'.split():
367 shelvedfile(repo, name, filetype).unlink()
365 shelvedfile(repo, name, filetype).unlink()
368
366
369 def finishmerge(ui, repo, ms, stripnodes, name, opts):
367 def finishmerge(ui, repo, ms, stripnodes, name, opts):
370 # Reset the working dir so it's no longer in a merge state.
368 # Reset the working dir so it's no longer in a merge state.
371 dirstate = repo.dirstate
369 dirstate = repo.dirstate
372 for f in ms:
370 for f in ms:
373 if dirstate[f] == 'm':
371 if dirstate[f] == 'm':
374 dirstate.normallookup(f)
372 dirstate.normallookup(f)
375 dirstate._pl = (dirstate._pl[0], nullid)
373 dirstate._pl = (dirstate._pl[0], nullid)
376 dirstate._dirty = dirstate._dirtypl = True
374 dirstate._dirty = dirstate._dirtypl = True
377 shelvedstate.clear(repo)
375 shelvedstate.clear(repo)
378
376
379 def unshelvecontinue(ui, repo, state, opts):
377 def unshelvecontinue(ui, repo, state, opts):
380 # We're finishing off a merge. First parent is our original
378 # We're finishing off a merge. First parent is our original
381 # parent, second is the temporary "fake" commit we're unshelving.
379 # parent, second is the temporary "fake" commit we're unshelving.
382 wlock = repo.wlock()
380 wlock = repo.wlock()
383 lock = None
381 lock = None
384 try:
382 try:
385 checkparents(repo, state)
383 checkparents(repo, state)
386 ms = merge.mergestate(repo)
384 ms = merge.mergestate(repo)
387 if [f for f in ms if ms[f] == 'u']:
385 if [f for f in ms if ms[f] == 'u']:
388 raise util.Abort(
386 raise util.Abort(
389 _("unresolved conflicts, can't continue"),
387 _("unresolved conflicts, can't continue"),
390 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
388 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
391 finishmerge(ui, repo, ms, state.stripnodes, state.name, opts)
389 finishmerge(ui, repo, ms, state.stripnodes, state.name, opts)
392 lock = repo.lock()
390 lock = repo.lock()
393 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
391 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
394 unshelvecleanup(ui, repo, state.name, opts)
392 unshelvecleanup(ui, repo, state.name, opts)
395 ui.status(_("unshelve of '%s' complete\n") % state.name)
393 ui.status(_("unshelve of '%s' complete\n") % state.name)
396 finally:
394 finally:
397 lockmod.release(lock, wlock)
395 lockmod.release(lock, wlock)
398
396
399 @command('unshelve',
397 @command('unshelve',
400 [('a', 'abort', None,
398 [('a', 'abort', None,
401 _('abort an incomplete unshelve operation')),
399 _('abort an incomplete unshelve operation')),
402 ('c', 'continue', None,
400 ('c', 'continue', None,
403 _('continue an incomplete unshelve operation')),
401 _('continue an incomplete unshelve operation')),
404 ('', 'keep', None,
402 ('', 'keep', None,
405 _('keep shelve after unshelving'))],
403 _('keep shelve after unshelving'))],
406 _('hg unshelve [SHELVED]'))
404 _('hg unshelve [SHELVED]'))
407 def unshelve(ui, repo, *shelved, **opts):
405 def unshelve(ui, repo, *shelved, **opts):
408 """restore a shelved change to the working directory
406 """restore a shelved change to the working directory
409
407
410 This command accepts an optional name of a shelved change to
408 This command accepts an optional name of a shelved change to
411 restore. If none is given, the most recent shelved change is used.
409 restore. If none is given, the most recent shelved change is used.
412
410
413 If a shelved change is applied successfully, the bundle that
411 If a shelved change is applied successfully, the bundle that
414 contains the shelved changes is deleted afterwards.
412 contains the shelved changes is deleted afterwards.
415
413
416 Since you can restore a shelved change on top of an arbitrary
414 Since you can restore a shelved change on top of an arbitrary
417 commit, it is possible that unshelving will result in a conflict
415 commit, it is possible that unshelving will result in a conflict
418 between your changes and the commits you are unshelving onto. If
416 between your changes and the commits you are unshelving onto. If
419 this occurs, you must resolve the conflict, then use
417 this occurs, you must resolve the conflict, then use
420 ``--continue`` to complete the unshelve operation. (The bundle
418 ``--continue`` to complete the unshelve operation. (The bundle
421 will not be deleted until you successfully complete the unshelve.)
419 will not be deleted until you successfully complete the unshelve.)
422
420
423 (Alternatively, you can use ``--abort`` to abandon an unshelve
421 (Alternatively, you can use ``--abort`` to abandon an unshelve
424 that causes a conflict. This reverts the unshelved changes, and
422 that causes a conflict. This reverts the unshelved changes, and
425 does not delete the bundle.)
423 does not delete the bundle.)
426 """
424 """
427 abortf = opts['abort']
425 abortf = opts['abort']
428 continuef = opts['continue']
426 continuef = opts['continue']
429 if not abortf and not continuef:
427 if not abortf and not continuef:
430 cmdutil.checkunfinished(repo)
428 cmdutil.checkunfinished(repo)
431
429
432 if abortf or continuef:
430 if abortf or continuef:
433 if abortf and continuef:
431 if abortf and continuef:
434 raise util.Abort(_('cannot use both abort and continue'))
432 raise util.Abort(_('cannot use both abort and continue'))
435 if shelved:
433 if shelved:
436 raise util.Abort(_('cannot combine abort/continue with '
434 raise util.Abort(_('cannot combine abort/continue with '
437 'naming a shelved change'))
435 'naming a shelved change'))
438
436
439 try:
437 try:
440 state = shelvedstate.load(repo)
438 state = shelvedstate.load(repo)
441 except IOError, err:
439 except IOError, err:
442 if err.errno != errno.ENOENT:
440 if err.errno != errno.ENOENT:
443 raise
441 raise
444 raise util.Abort(_('no unshelve operation underway'))
442 raise util.Abort(_('no unshelve operation underway'))
445
443
446 if abortf:
444 if abortf:
447 return unshelveabort(ui, repo, state, opts)
445 return unshelveabort(ui, repo, state, opts)
448 elif continuef:
446 elif continuef:
449 return unshelvecontinue(ui, repo, state, opts)
447 return unshelvecontinue(ui, repo, state, opts)
450 elif len(shelved) > 1:
448 elif len(shelved) > 1:
451 raise util.Abort(_('can only unshelve one change at a time'))
449 raise util.Abort(_('can only unshelve one change at a time'))
452 elif not shelved:
450 elif not shelved:
453 shelved = listshelves(repo)
451 shelved = listshelves(repo)
454 if not shelved:
452 if not shelved:
455 raise util.Abort(_('no shelved changes to apply!'))
453 raise util.Abort(_('no shelved changes to apply!'))
456 basename = util.split(shelved[0][1])[1]
454 basename = util.split(shelved[0][1])[1]
457 ui.status(_("unshelving change '%s'\n") % basename)
455 ui.status(_("unshelving change '%s'\n") % basename)
458 else:
456 else:
459 basename = shelved[0]
457 basename = shelved[0]
460
458
461 shelvedfiles = readshelvedfiles(repo, basename)
459 shelvedfiles = readshelvedfiles(repo, basename)
462
460
463 m, a, r, d = repo.status()[:4]
461 m, a, r, d = repo.status()[:4]
464 unsafe = set(m + a + r + d).intersection(shelvedfiles)
462 unsafe = set(m + a + r + d).intersection(shelvedfiles)
465 if unsafe:
463 if unsafe:
466 ui.warn(_('the following shelved files have been modified:\n'))
464 ui.warn(_('the following shelved files have been modified:\n'))
467 for f in sorted(unsafe):
465 for f in sorted(unsafe):
468 ui.warn(' %s\n' % f)
466 ui.warn(' %s\n' % f)
469 ui.warn(_('you must commit, revert, or shelve your changes before you '
467 ui.warn(_('you must commit, revert, or shelve your changes before you '
470 'can proceed\n'))
468 'can proceed\n'))
471 raise util.Abort(_('cannot unshelve due to local changes\n'))
469 raise util.Abort(_('cannot unshelve due to local changes\n'))
472
470
473 wlock = lock = tr = None
471 wlock = lock = tr = None
474 try:
472 try:
475 lock = repo.lock()
473 lock = repo.lock()
476
474
477 tr = repo.transaction('unshelve', report=lambda x: None)
475 tr = repo.transaction('unshelve', report=lambda x: None)
478 oldtiprev = len(repo)
476 oldtiprev = len(repo)
479 try:
477 try:
480 fp = shelvedfile(repo, basename, 'hg').opener()
478 fp = shelvedfile(repo, basename, 'hg').opener()
481 gen = changegroup.readbundle(fp, fp.name)
479 gen = changegroup.readbundle(fp, fp.name)
482 repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
480 repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
483 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
481 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
484 phases.retractboundary(repo, phases.secret, nodes)
482 phases.retractboundary(repo, phases.secret, nodes)
485 tr.close()
483 tr.close()
486 finally:
484 finally:
487 fp.close()
485 fp.close()
488
486
489 tip = repo['tip']
487 tip = repo['tip']
490 wctx = repo['.']
488 wctx = repo['.']
491 ancestor = tip.ancestor(wctx)
489 ancestor = tip.ancestor(wctx)
492
490
493 wlock = repo.wlock()
491 wlock = repo.wlock()
494
492
495 if ancestor.node() != wctx.node():
493 if ancestor.node() != wctx.node():
496 conflicts = hg.merge(repo, tip.node(), force=True, remind=False)
494 conflicts = hg.merge(repo, tip.node(), force=True, remind=False)
497 ms = merge.mergestate(repo)
495 ms = merge.mergestate(repo)
498 stripnodes = [repo.changelog.node(rev)
496 stripnodes = [repo.changelog.node(rev)
499 for rev in xrange(oldtiprev, len(repo))]
497 for rev in xrange(oldtiprev, len(repo))]
500 if conflicts:
498 if conflicts:
501 shelvedstate.save(repo, basename, stripnodes)
499 shelvedstate.save(repo, basename, stripnodes)
502 # Fix up the dirstate entries of files from the second
500 # Fix up the dirstate entries of files from the second
503 # parent as if we were not merging, except for those
501 # parent as if we were not merging, except for those
504 # with unresolved conflicts.
502 # with unresolved conflicts.
505 parents = repo.parents()
503 parents = repo.parents()
506 revertfiles = set(parents[1].files()).difference(ms)
504 revertfiles = set(parents[1].files()).difference(ms)
507 cmdutil.revert(ui, repo, parents[1],
505 cmdutil.revert(ui, repo, parents[1],
508 (parents[0].node(), nullid),
506 (parents[0].node(), nullid),
509 *revertfiles, no_backup=True)
507 *revertfiles, no_backup=True)
510 raise error.InterventionRequired(
508 raise error.InterventionRequired(
511 _("unresolved conflicts (see 'hg resolve', then "
509 _("unresolved conflicts (see 'hg resolve', then "
512 "'hg unshelve --continue')"))
510 "'hg unshelve --continue')"))
513 finishmerge(ui, repo, ms, stripnodes, basename, opts)
511 finishmerge(ui, repo, ms, stripnodes, basename, opts)
514 else:
512 else:
515 parent = tip.parents()[0]
513 parent = tip.parents()[0]
516 hg.update(repo, parent.node())
514 hg.update(repo, parent.node())
517 cmdutil.revert(ui, repo, tip, repo.dirstate.parents(), *tip.files(),
515 cmdutil.revert(ui, repo, tip, repo.dirstate.parents(), *tip.files(),
518 no_backup=True)
516 no_backup=True)
519
517
520 prevquiet = ui.quiet
518 prevquiet = ui.quiet
521 ui.quiet = True
519 ui.quiet = True
522 try:
520 try:
523 repo.rollback(force=True)
521 repo.rollback(force=True)
524 finally:
522 finally:
525 ui.quiet = prevquiet
523 ui.quiet = prevquiet
526
524
527 unshelvecleanup(ui, repo, basename, opts)
525 unshelvecleanup(ui, repo, basename, opts)
528 finally:
526 finally:
529 if tr:
527 if tr:
530 tr.release()
528 tr.release()
531 lockmod.release(lock, wlock)
529 lockmod.release(lock, wlock)
532
530
533 @command('shelve',
531 @command('shelve',
534 [('A', 'addremove', None,
532 [('A', 'addremove', None,
535 _('mark new/missing files as added/removed before shelving')),
533 _('mark new/missing files as added/removed before shelving')),
536 ('', 'cleanup', None,
534 ('', 'cleanup', None,
537 _('delete all shelved changes')),
535 _('delete all shelved changes')),
538 ('', 'date', '',
536 ('', 'date', '',
539 _('shelve with the specified commit date'), _('DATE')),
537 _('shelve with the specified commit date'), _('DATE')),
540 ('d', 'delete', None,
538 ('d', 'delete', None,
541 _('delete the named shelved change(s)')),
539 _('delete the named shelved change(s)')),
542 ('l', 'list', None,
540 ('l', 'list', None,
543 _('list current shelves')),
541 _('list current shelves')),
544 ('m', 'message', '',
542 ('m', 'message', '',
545 _('use text as shelve message'), _('TEXT')),
543 _('use text as shelve message'), _('TEXT')),
546 ('n', 'name', '',
544 ('n', 'name', '',
547 _('use the given name for the shelved commit'), _('NAME')),
545 _('use the given name for the shelved commit'), _('NAME')),
548 ('p', 'patch', None,
546 ('p', 'patch', None,
549 _('show patch')),
547 _('show patch')),
550 ('', 'stat', None,
548 ('', 'stat', None,
551 _('output diffstat-style summary of changes'))],
549 _('output diffstat-style summary of changes'))],
552 _('hg shelve'))
550 _('hg shelve'))
553 def shelvecmd(ui, repo, *pats, **opts):
551 def shelvecmd(ui, repo, *pats, **opts):
554 '''save and set aside changes from the working directory
552 '''save and set aside changes from the working directory
555
553
556 Shelving takes files that "hg status" reports as not clean, saves
554 Shelving takes files that "hg status" reports as not clean, saves
557 the modifications to a bundle (a shelved change), and reverts the
555 the modifications to a bundle (a shelved change), and reverts the
558 files so that their state in the working directory becomes clean.
556 files so that their state in the working directory becomes clean.
559
557
560 To restore these changes to the working directory, using "hg
558 To restore these changes to the working directory, using "hg
561 unshelve"; this will work even if you switch to a different
559 unshelve"; this will work even if you switch to a different
562 commit.
560 commit.
563
561
564 When no files are specified, "hg shelve" saves all not-clean
562 When no files are specified, "hg shelve" saves all not-clean
565 files. If specific files or directories are named, only changes to
563 files. If specific files or directories are named, only changes to
566 those files are shelved.
564 those files are shelved.
567
565
568 Each shelved change has a name that makes it easier to find later.
566 Each shelved change has a name that makes it easier to find later.
569 The name of a shelved change defaults to being based on the active
567 The name of a shelved change defaults to being based on the active
570 bookmark, or if there is no active bookmark, the current named
568 bookmark, or if there is no active bookmark, the current named
571 branch. To specify a different name, use ``--name``.
569 branch. To specify a different name, use ``--name``.
572
570
573 To see a list of existing shelved changes, use the ``--list``
571 To see a list of existing shelved changes, use the ``--list``
574 option. For each shelved change, this will print its name, age,
572 option. For each shelved change, this will print its name, age,
575 and description; use ``--patch`` or ``--stat`` for more details.
573 and description; use ``--patch`` or ``--stat`` for more details.
576
574
577 To delete specific shelved changes, use ``--delete``. To delete
575 To delete specific shelved changes, use ``--delete``. To delete
578 all shelved changes, use ``--cleanup``.
576 all shelved changes, use ``--cleanup``.
579 '''
577 '''
580 cmdutil.checkunfinished(repo)
578 cmdutil.checkunfinished(repo)
581
579
582 def checkopt(opt, incompatible):
580 def checkopt(opt, incompatible):
583 if opts[opt]:
581 if opts[opt]:
584 for i in incompatible.split():
582 for i in incompatible.split():
585 if opts[i]:
583 if opts[i]:
586 raise util.Abort(_("options '--%s' and '--%s' may not be "
584 raise util.Abort(_("options '--%s' and '--%s' may not be "
587 "used together") % (opt, i))
585 "used together") % (opt, i))
588 return True
586 return True
589 if checkopt('cleanup', 'addremove delete list message name patch stat'):
587 if checkopt('cleanup', 'addremove delete list message name patch stat'):
590 if pats:
588 if pats:
591 raise util.Abort(_("cannot specify names when using '--cleanup'"))
589 raise util.Abort(_("cannot specify names when using '--cleanup'"))
592 return cleanupcmd(ui, repo)
590 return cleanupcmd(ui, repo)
593 elif checkopt('delete', 'addremove cleanup list message name patch stat'):
591 elif checkopt('delete', 'addremove cleanup list message name patch stat'):
594 return deletecmd(ui, repo, pats)
592 return deletecmd(ui, repo, pats)
595 elif checkopt('list', 'addremove cleanup delete message name'):
593 elif checkopt('list', 'addremove cleanup delete message name'):
596 return listcmd(ui, repo, pats, opts)
594 return listcmd(ui, repo, pats, opts)
597 else:
595 else:
598 for i in ('patch', 'stat'):
596 for i in ('patch', 'stat'):
599 if opts[i]:
597 if opts[i]:
600 raise util.Abort(_("option '--%s' may not be "
598 raise util.Abort(_("option '--%s' may not be "
601 "used when shelving a change") % (i,))
599 "used when shelving a change") % (i,))
602 return createcmd(ui, repo, pats, opts)
600 return createcmd(ui, repo, pats, opts)
603
601
604 def extsetup(ui):
602 def extsetup(ui):
605 cmdutil.unfinishedstates.append(
603 cmdutil.unfinishedstates.append(
606 [shelvedstate._filename, False, True, _('unshelve already in progress'),
604 [shelvedstate._filename, False, True, _('unshelve already in progress'),
607 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
605 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
@@ -1,420 +1,416
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "shelve=" >> $HGRCPATH
2 $ echo "shelve=" >> $HGRCPATH
3 $ echo "[defaults]" >> $HGRCPATH
3 $ echo "[defaults]" >> $HGRCPATH
4 $ echo "diff = --nodates --git" >> $HGRCPATH
4 $ echo "diff = --nodates --git" >> $HGRCPATH
5
5
6 $ hg init repo
6 $ hg init repo
7 $ cd repo
7 $ cd repo
8 $ mkdir a b
8 $ mkdir a b
9 $ echo a > a/a
9 $ echo a > a/a
10 $ echo b > b/b
10 $ echo b > b/b
11 $ echo c > c
11 $ echo c > c
12 $ echo d > d
12 $ echo d > d
13 $ echo x > x
13 $ echo x > x
14 $ hg addremove -q
14 $ hg addremove -q
15
15
16 shelving in an empty repo should be possible
16 shelving in an empty repo should be possible
17
17
18 $ hg shelve
18 $ hg shelve
19 (empty repository)
20 shelved as default
19 shelved as default
21 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
20 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
22
21
23 $ hg unshelve
22 $ hg unshelve
24 unshelving change 'default'
23 unshelving change 'default'
25 adding changesets
24 adding changesets
26 adding manifests
25 adding manifests
27 adding file changes
26 adding file changes
28 added 1 changesets with 5 changes to 5 files
27 added 1 changesets with 5 changes to 5 files
29 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
30
29
31 $ hg commit -q -m 'initial commit'
30 $ hg commit -q -m 'initial commit'
32
31
33 $ hg shelve
32 $ hg shelve
34 nothing changed
33 nothing changed
35 [1]
34 [1]
36
35
37 create another commit
36 create another commit
38
37
39 $ echo n > n
38 $ echo n > n
40 $ hg add n
39 $ hg add n
41 $ hg commit n -m second
40 $ hg commit n -m second
42
41
43 shelve a change that we will delete later
42 shelve a change that we will delete later
44
43
45 $ echo a >> a/a
44 $ echo a >> a/a
46 $ hg shelve
45 $ hg shelve
47 shelved from default (bb4fec6d): second
48 shelved as default
46 shelved as default
49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50
48
51 set up some more complex changes to shelve
49 set up some more complex changes to shelve
52
50
53 $ echo a >> a/a
51 $ echo a >> a/a
54 $ hg mv b b.rename
52 $ hg mv b b.rename
55 moving b/b to b.rename/b (glob)
53 moving b/b to b.rename/b (glob)
56 $ hg cp c c.copy
54 $ hg cp c c.copy
57 $ hg status -C
55 $ hg status -C
58 M a/a
56 M a/a
59 A b.rename/b
57 A b.rename/b
60 b/b
58 b/b
61 A c.copy
59 A c.copy
62 c
60 c
63 R b/b
61 R b/b
64
62
65 prevent some foot-shooting
63 prevent some foot-shooting
66
64
67 $ hg shelve -n foo/bar
65 $ hg shelve -n foo/bar
68 abort: shelved change names may not contain slashes
66 abort: shelved change names may not contain slashes
69 [255]
67 [255]
70 $ hg shelve -n .baz
68 $ hg shelve -n .baz
71 abort: shelved change names may not start with '.'
69 abort: shelved change names may not start with '.'
72 [255]
70 [255]
73
71
74 the common case - no options or filenames
72 the common case - no options or filenames
75
73
76 $ hg shelve
74 $ hg shelve
77 shelved from default (bb4fec6d): second
78 shelved as default-01
75 shelved as default-01
79 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
76 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
80 $ hg status -C
77 $ hg status -C
81
78
82 ensure that our shelved changes exist
79 ensure that our shelved changes exist
83
80
84 $ hg shelve -l
81 $ hg shelve -l
85 default-01 [*] shelved from default (bb4fec6d): second (glob)
82 default-01 (*) second (glob)
86 default [*] shelved from default (bb4fec6d): second (glob)
83 default (*) second (glob)
87
84
88 $ hg shelve -l -p default
85 $ hg shelve -l -p default
89 default [*] shelved from default (bb4fec6d): second (glob)
86 default (*) second (glob)
90
87
91 diff --git a/a/a b/a/a
88 diff --git a/a/a b/a/a
92 --- a/a/a
89 --- a/a/a
93 +++ b/a/a
90 +++ b/a/a
94 @@ -1,1 +1,2 @@
91 @@ -1,1 +1,2 @@
95 a
92 a
96 +a
93 +a
97
94
98 delete our older shelved change
95 delete our older shelved change
99
96
100 $ hg shelve -d default
97 $ hg shelve -d default
101
98
102 local edits should prevent a shelved change from applying
99 local edits should prevent a shelved change from applying
103
100
104 $ echo e>>a/a
101 $ echo e>>a/a
105 $ hg unshelve
102 $ hg unshelve
106 unshelving change 'default-01'
103 unshelving change 'default-01'
107 the following shelved files have been modified:
104 the following shelved files have been modified:
108 a/a
105 a/a
109 you must commit, revert, or shelve your changes before you can proceed
106 you must commit, revert, or shelve your changes before you can proceed
110 abort: cannot unshelve due to local changes
107 abort: cannot unshelve due to local changes
111
108
112 [255]
109 [255]
113
110
114 $ hg revert -C a/a
111 $ hg revert -C a/a
115
112
116 apply it and make sure our state is as expected
113 apply it and make sure our state is as expected
117
114
118 $ hg unshelve
115 $ hg unshelve
119 unshelving change 'default-01'
116 unshelving change 'default-01'
120 adding changesets
117 adding changesets
121 adding manifests
118 adding manifests
122 adding file changes
119 adding file changes
123 added 1 changesets with 3 changes to 8 files
120 added 1 changesets with 3 changes to 8 files
124 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 $ hg status -C
122 $ hg status -C
126 M a/a
123 M a/a
127 A b.rename/b
124 A b.rename/b
128 b/b
125 b/b
129 A c.copy
126 A c.copy
130 c
127 c
131 R b/b
128 R b/b
132 $ hg shelve -l
129 $ hg shelve -l
133
130
134 $ hg unshelve
131 $ hg unshelve
135 abort: no shelved changes to apply!
132 abort: no shelved changes to apply!
136 [255]
133 [255]
137 $ hg unshelve foo
134 $ hg unshelve foo
138 abort: shelved change 'foo' not found
135 abort: shelved change 'foo' not found
139 [255]
136 [255]
140
137
141 named shelves, specific filenames, and "commit messages" should all work
138 named shelves, specific filenames, and "commit messages" should all work
142
139
143 $ hg status -C
140 $ hg status -C
144 M a/a
141 M a/a
145 A b.rename/b
142 A b.rename/b
146 b/b
143 b/b
147 A c.copy
144 A c.copy
148 c
145 c
149 R b/b
146 R b/b
150 $ hg shelve -q -n wibble -m wat a
147 $ hg shelve -q -n wibble -m wat a
151
148
152 expect "a" to no longer be present, but status otherwise unchanged
149 expect "a" to no longer be present, but status otherwise unchanged
153
150
154 $ hg status -C
151 $ hg status -C
155 A b.rename/b
152 A b.rename/b
156 b/b
153 b/b
157 A c.copy
154 A c.copy
158 c
155 c
159 R b/b
156 R b/b
160 $ hg shelve -l --stat
157 $ hg shelve -l --stat
161 wibble [*] wat (glob)
158 wibble (*) wat (glob)
162 a/a | 1 +
159 a/a | 1 +
163 1 files changed, 1 insertions(+), 0 deletions(-)
160 1 files changed, 1 insertions(+), 0 deletions(-)
164
161
165 and now "a/a" should reappear
162 and now "a/a" should reappear
166
163
167 $ hg unshelve -q wibble
164 $ hg unshelve -q wibble
168 $ hg status -C
165 $ hg status -C
169 M a/a
166 M a/a
170 A b.rename/b
167 A b.rename/b
171 b/b
168 b/b
172 A c.copy
169 A c.copy
173 c
170 c
174 R b/b
171 R b/b
175
172
176 cause unshelving to result in a merge with 'a' conflicting
173 cause unshelving to result in a merge with 'a' conflicting
177
174
178 $ hg shelve -q
175 $ hg shelve -q
179 $ echo c>>a/a
176 $ echo c>>a/a
180 $ hg commit -m second
177 $ hg commit -m second
181 $ hg tip --template '{files}\n'
178 $ hg tip --template '{files}\n'
182 a/a
179 a/a
183
180
184 add an unrelated change that should be preserved
181 add an unrelated change that should be preserved
185
182
186 $ mkdir foo
183 $ mkdir foo
187 $ echo foo > foo/foo
184 $ echo foo > foo/foo
188 $ hg add foo/foo
185 $ hg add foo/foo
189
186
190 force a conflicted merge to occur
187 force a conflicted merge to occur
191
188
192 $ hg unshelve
189 $ hg unshelve
193 unshelving change 'default'
190 unshelving change 'default'
194 adding changesets
191 adding changesets
195 adding manifests
192 adding manifests
196 adding file changes
193 adding file changes
197 added 1 changesets with 3 changes to 8 files (+1 heads)
194 added 1 changesets with 3 changes to 8 files (+1 heads)
198 merging a/a
195 merging a/a
199 warning: conflicts during merge.
196 warning: conflicts during merge.
200 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
197 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
201 2 files updated, 0 files merged, 1 files removed, 1 files unresolved
198 2 files updated, 0 files merged, 1 files removed, 1 files unresolved
202 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
199 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
203 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
200 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
204 [1]
201 [1]
205
202
206 ensure that we have a merge with unresolved conflicts
203 ensure that we have a merge with unresolved conflicts
207
204
208 $ hg heads -q
205 $ hg heads -q
209 3:7ec047b69dc0
206 3:6ea6529cfc65
210 2:ceefc37abe1e
207 2:ceefc37abe1e
211 $ hg parents -q
208 $ hg parents -q
212 2:ceefc37abe1e
209 2:ceefc37abe1e
213 3:7ec047b69dc0
210 3:6ea6529cfc65
214 $ hg status
211 $ hg status
215 M a/a
212 M a/a
216 M b.rename/b
213 M b.rename/b
217 M c.copy
214 M c.copy
218 A foo/foo
215 A foo/foo
219 R b/b
216 R b/b
220 ? a/a.orig
217 ? a/a.orig
221 $ hg diff
218 $ hg diff
222 diff --git a/a/a b/a/a
219 diff --git a/a/a b/a/a
223 --- a/a/a
220 --- a/a/a
224 +++ b/a/a
221 +++ b/a/a
225 @@ -1,2 +1,6 @@
222 @@ -1,2 +1,6 @@
226 a
223 a
227 +<<<<<<< local
224 +<<<<<<< local
228 c
225 c
229 +=======
226 +=======
230 +a
227 +a
231 +>>>>>>> other
228 +>>>>>>> other
232 diff --git a/b.rename/b b/b.rename/b
229 diff --git a/b.rename/b b/b.rename/b
233 --- /dev/null
230 --- /dev/null
234 +++ b/b.rename/b
231 +++ b/b.rename/b
235 @@ -0,0 +1,1 @@
232 @@ -0,0 +1,1 @@
236 +b
233 +b
237 diff --git a/b/b b/b/b
234 diff --git a/b/b b/b/b
238 deleted file mode 100644
235 deleted file mode 100644
239 --- a/b/b
236 --- a/b/b
240 +++ /dev/null
237 +++ /dev/null
241 @@ -1,1 +0,0 @@
238 @@ -1,1 +0,0 @@
242 -b
239 -b
243 diff --git a/c.copy b/c.copy
240 diff --git a/c.copy b/c.copy
244 --- /dev/null
241 --- /dev/null
245 +++ b/c.copy
242 +++ b/c.copy
246 @@ -0,0 +1,1 @@
243 @@ -0,0 +1,1 @@
247 +c
244 +c
248 diff --git a/foo/foo b/foo/foo
245 diff --git a/foo/foo b/foo/foo
249 new file mode 100644
246 new file mode 100644
250 --- /dev/null
247 --- /dev/null
251 +++ b/foo/foo
248 +++ b/foo/foo
252 @@ -0,0 +1,1 @@
249 @@ -0,0 +1,1 @@
253 +foo
250 +foo
254 $ hg resolve -l
251 $ hg resolve -l
255 U a/a
252 U a/a
256
253
257 $ hg shelve
254 $ hg shelve
258 abort: unshelve already in progress
255 abort: unshelve already in progress
259 (use 'hg unshelve --continue' or 'hg unshelve --abort')
256 (use 'hg unshelve --continue' or 'hg unshelve --abort')
260 [255]
257 [255]
261
258
262 abort the unshelve and be happy
259 abort the unshelve and be happy
263
260
264 $ hg status
261 $ hg status
265 M a/a
262 M a/a
266 M b.rename/b
263 M b.rename/b
267 M c.copy
264 M c.copy
268 A foo/foo
265 A foo/foo
269 R b/b
266 R b/b
270 ? a/a.orig
267 ? a/a.orig
271 $ hg unshelve -a
268 $ hg unshelve -a
272 unshelve of 'default' aborted
269 unshelve of 'default' aborted
273 $ hg heads -q
270 $ hg heads -q
274 2:ceefc37abe1e
271 2:ceefc37abe1e
275 $ hg parents
272 $ hg parents
276 changeset: 2:ceefc37abe1e
273 changeset: 2:ceefc37abe1e
277 tag: tip
274 tag: tip
278 user: test
275 user: test
279 date: Thu Jan 01 00:00:00 1970 +0000
276 date: Thu Jan 01 00:00:00 1970 +0000
280 summary: second
277 summary: second
281
278
282 $ hg resolve -l
279 $ hg resolve -l
283 $ hg status
280 $ hg status
284 A foo/foo
281 A foo/foo
285 ? a/a.orig
282 ? a/a.orig
286
283
287 try to continue with no unshelve underway
284 try to continue with no unshelve underway
288
285
289 $ hg unshelve -c
286 $ hg unshelve -c
290 abort: no unshelve operation underway
287 abort: no unshelve operation underway
291 [255]
288 [255]
292 $ hg status
289 $ hg status
293 A foo/foo
290 A foo/foo
294 ? a/a.orig
291 ? a/a.orig
295
292
296 redo the unshelve to get a conflict
293 redo the unshelve to get a conflict
297
294
298 $ hg unshelve -q
295 $ hg unshelve -q
299 warning: conflicts during merge.
296 warning: conflicts during merge.
300 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
297 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
301 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
298 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
302 [1]
299 [1]
303
300
304 attempt to continue
301 attempt to continue
305
302
306 $ hg unshelve -c
303 $ hg unshelve -c
307 abort: unresolved conflicts, can't continue
304 abort: unresolved conflicts, can't continue
308 (see 'hg resolve', then 'hg unshelve --continue')
305 (see 'hg resolve', then 'hg unshelve --continue')
309 [255]
306 [255]
310
307
311 $ hg revert -r . a/a
308 $ hg revert -r . a/a
312 $ hg resolve -m a/a
309 $ hg resolve -m a/a
313
310
314 $ hg unshelve -c
311 $ hg unshelve -c
315 unshelve of 'default' complete
312 unshelve of 'default' complete
316
313
317 ensure the repo is as we hope
314 ensure the repo is as we hope
318
315
319 $ hg parents
316 $ hg parents
320 changeset: 2:ceefc37abe1e
317 changeset: 2:ceefc37abe1e
321 tag: tip
318 tag: tip
322 user: test
319 user: test
323 date: Thu Jan 01 00:00:00 1970 +0000
320 date: Thu Jan 01 00:00:00 1970 +0000
324 summary: second
321 summary: second
325
322
326 $ hg heads -q
323 $ hg heads -q
327 2:ceefc37abe1e
324 2:ceefc37abe1e
328
325
329 $ hg status -C
326 $ hg status -C
330 M a/a
327 M a/a
331 M b.rename/b
328 M b.rename/b
332 b/b
329 b/b
333 M c.copy
330 M c.copy
334 c
331 c
335 A foo/foo
332 A foo/foo
336 R b/b
333 R b/b
337 ? a/a.orig
334 ? a/a.orig
338
335
339 there should be no shelves left
336 there should be no shelves left
340
337
341 $ hg shelve -l
338 $ hg shelve -l
342
339
343 $ hg commit -m whee a/a
340 $ hg commit -m whee a/a
344
341
345 #if execbit
342 #if execbit
346
343
347 ensure that metadata-only changes are shelved
344 ensure that metadata-only changes are shelved
348
345
349 $ chmod +x a/a
346 $ chmod +x a/a
350 $ hg shelve -q -n execbit a/a
347 $ hg shelve -q -n execbit a/a
351 $ hg status a/a
348 $ hg status a/a
352 $ hg unshelve -q execbit
349 $ hg unshelve -q execbit
353 $ hg status a/a
350 $ hg status a/a
354 M a/a
351 M a/a
355 $ hg revert a/a
352 $ hg revert a/a
356
353
357 #endif
354 #endif
358
355
359 #if symlink
356 #if symlink
360
357
361 $ rm a/a
358 $ rm a/a
362 $ ln -s foo a/a
359 $ ln -s foo a/a
363 $ hg shelve -q -n symlink a/a
360 $ hg shelve -q -n symlink a/a
364 $ hg status a/a
361 $ hg status a/a
365 $ hg unshelve -q symlink
362 $ hg unshelve -q symlink
366 $ hg status a/a
363 $ hg status a/a
367 M a/a
364 M a/a
368 $ hg revert a/a
365 $ hg revert a/a
369
366
370 #endif
367 #endif
371
368
372 set up another conflict between a commit and a shelved change
369 set up another conflict between a commit and a shelved change
373
370
374 $ hg revert -q -C -a
371 $ hg revert -q -C -a
375 $ echo a >> a/a
372 $ echo a >> a/a
376 $ hg shelve -q
373 $ hg shelve -q
377 $ echo x >> a/a
374 $ echo x >> a/a
378 $ hg ci -m 'create conflict'
375 $ hg ci -m 'create conflict'
379 $ hg add foo/foo
376 $ hg add foo/foo
380
377
381 if we resolve a conflict while unshelving, the unshelve should succeed
378 if we resolve a conflict while unshelving, the unshelve should succeed
382
379
383 $ HGMERGE=true hg unshelve
380 $ HGMERGE=true hg unshelve
384 unshelving change 'default'
381 unshelving change 'default'
385 adding changesets
382 adding changesets
386 adding manifests
383 adding manifests
387 adding file changes
384 adding file changes
388 added 1 changesets with 1 changes to 6 files (+1 heads)
385 added 1 changesets with 1 changes to 6 files (+1 heads)
389 merging a/a
386 merging a/a
390 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
387 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
391 $ hg parents -q
388 $ hg parents -q
392 4:be7e79683c99
389 4:be7e79683c99
393 $ hg shelve -l
390 $ hg shelve -l
394 $ hg status
391 $ hg status
395 M a/a
392 M a/a
396 A foo/foo
393 A foo/foo
397 $ cat a/a
394 $ cat a/a
398 a
395 a
399 c
396 c
400 x
397 x
401
398
402 test keep and cleanup
399 test keep and cleanup
403
400
404 $ hg shelve
401 $ hg shelve
405 shelved from default (be7e7968): create conflict
406 shelved as default
402 shelved as default
407 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
403 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
408 $ hg shelve --list
404 $ hg shelve --list
409 default [*] shelved from default (be7e7968): create conflict (glob)
405 default (*) create conflict (glob)
410 $ hg unshelve --keep
406 $ hg unshelve --keep
411 unshelving change 'default'
407 unshelving change 'default'
412 adding changesets
408 adding changesets
413 adding manifests
409 adding manifests
414 adding file changes
410 adding file changes
415 added 1 changesets with 1 changes to 7 files
411 added 1 changesets with 1 changes to 7 files
416 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
412 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
417 $ hg shelve --list
413 $ hg shelve --list
418 default [*] shelved from default (be7e7968): create conflict (glob)
414 default (*) create conflict (glob)
419 $ hg shelve --cleanup
415 $ hg shelve --cleanup
420 $ hg shelve --list
416 $ hg shelve --list
General Comments 0
You need to be logged in to leave comments. Login now