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