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