##// END OF EJS Templates
bookmarks: use recordchange instead of writing if transaction is active...
FUJIWARA Katsunori -
r26520:46dec89f default
parent child Browse files
Show More
@@ -1,810 +1,805 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 import collections
24 import collections
25 import itertools
25 import itertools
26 from mercurial.i18n import _
26 from mercurial.i18n import _
27 from mercurial.node import nullid, nullrev, bin, hex
27 from mercurial.node import nullid, nullrev, bin, hex
28 from mercurial import changegroup, cmdutil, scmutil, phases, commands
28 from mercurial import changegroup, cmdutil, scmutil, phases, commands
29 from mercurial import error, hg, mdiff, merge, patch, repair, util
29 from mercurial import error, hg, mdiff, merge, patch, repair, util
30 from mercurial import templatefilters, exchange, bundlerepo
30 from mercurial import templatefilters, exchange, bundlerepo
31 from mercurial import lock as lockmod
31 from mercurial import lock as lockmod
32 from hgext import rebase
32 from hgext import rebase
33 import errno
33 import errno
34
34
35 cmdtable = {}
35 cmdtable = {}
36 command = cmdutil.command(cmdtable)
36 command = cmdutil.command(cmdtable)
37 # Note for extension authors: ONLY specify testedwith = 'internal' for
37 # Note for extension authors: ONLY specify testedwith = 'internal' for
38 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
38 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
39 # be specifying the version(s) of Mercurial they are tested with, or
39 # be specifying the version(s) of Mercurial they are tested with, or
40 # leave the attribute unspecified.
40 # leave the attribute unspecified.
41 testedwith = 'internal'
41 testedwith = 'internal'
42
42
43 backupdir = 'shelve-backup'
43 backupdir = 'shelve-backup'
44
44
45 class shelvedfile(object):
45 class shelvedfile(object):
46 """Helper for the file storing a single shelve
46 """Helper for the file storing a single shelve
47
47
48 Handles common functions on shelve files (.hg/.patch) using
48 Handles common functions on shelve files (.hg/.patch) using
49 the vfs layer"""
49 the vfs layer"""
50 def __init__(self, repo, name, filetype=None):
50 def __init__(self, repo, name, filetype=None):
51 self.repo = repo
51 self.repo = repo
52 self.name = name
52 self.name = name
53 self.vfs = scmutil.vfs(repo.join('shelved'))
53 self.vfs = scmutil.vfs(repo.join('shelved'))
54 self.backupvfs = scmutil.vfs(repo.join(backupdir))
54 self.backupvfs = scmutil.vfs(repo.join(backupdir))
55 self.ui = self.repo.ui
55 self.ui = self.repo.ui
56 if filetype:
56 if filetype:
57 self.fname = name + '.' + filetype
57 self.fname = name + '.' + filetype
58 else:
58 else:
59 self.fname = name
59 self.fname = name
60
60
61 def exists(self):
61 def exists(self):
62 return self.vfs.exists(self.fname)
62 return self.vfs.exists(self.fname)
63
63
64 def filename(self):
64 def filename(self):
65 return self.vfs.join(self.fname)
65 return self.vfs.join(self.fname)
66
66
67 def backupfilename(self):
67 def backupfilename(self):
68 def gennames(base):
68 def gennames(base):
69 yield base
69 yield base
70 base, ext = base.rsplit('.', 1)
70 base, ext = base.rsplit('.', 1)
71 for i in itertools.count(1):
71 for i in itertools.count(1):
72 yield '%s-%d.%s' % (base, i, ext)
72 yield '%s-%d.%s' % (base, i, ext)
73
73
74 name = self.backupvfs.join(self.fname)
74 name = self.backupvfs.join(self.fname)
75 for n in gennames(name):
75 for n in gennames(name):
76 if not self.backupvfs.exists(n):
76 if not self.backupvfs.exists(n):
77 return n
77 return n
78
78
79 def movetobackup(self):
79 def movetobackup(self):
80 if not self.backupvfs.isdir():
80 if not self.backupvfs.isdir():
81 self.backupvfs.makedir()
81 self.backupvfs.makedir()
82 util.rename(self.filename(), self.backupfilename())
82 util.rename(self.filename(), self.backupfilename())
83
83
84 def stat(self):
84 def stat(self):
85 return self.vfs.stat(self.fname)
85 return self.vfs.stat(self.fname)
86
86
87 def opener(self, mode='rb'):
87 def opener(self, mode='rb'):
88 try:
88 try:
89 return self.vfs(self.fname, mode)
89 return self.vfs(self.fname, mode)
90 except IOError as err:
90 except IOError as err:
91 if err.errno != errno.ENOENT:
91 if err.errno != errno.ENOENT:
92 raise
92 raise
93 raise util.Abort(_("shelved change '%s' not found") % self.name)
93 raise util.Abort(_("shelved change '%s' not found") % self.name)
94
94
95 def applybundle(self):
95 def applybundle(self):
96 fp = self.opener()
96 fp = self.opener()
97 try:
97 try:
98 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
98 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
99 changegroup.addchangegroup(self.repo, gen, 'unshelve',
99 changegroup.addchangegroup(self.repo, gen, 'unshelve',
100 'bundle:' + self.vfs.join(self.fname),
100 'bundle:' + self.vfs.join(self.fname),
101 targetphase=phases.secret)
101 targetphase=phases.secret)
102 finally:
102 finally:
103 fp.close()
103 fp.close()
104
104
105 def bundlerepo(self):
105 def bundlerepo(self):
106 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
106 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
107 self.vfs.join(self.fname))
107 self.vfs.join(self.fname))
108 def writebundle(self, bases, node):
108 def writebundle(self, bases, node):
109 btype = 'HG10BZ'
109 btype = 'HG10BZ'
110 cgversion = '01'
110 cgversion = '01'
111 compression = None
111 compression = None
112 if 'generaldelta' in self.repo.requirements:
112 if 'generaldelta' in self.repo.requirements:
113 btype = 'HG20'
113 btype = 'HG20'
114 cgversion = '02'
114 cgversion = '02'
115 compression = 'BZ'
115 compression = 'BZ'
116
116
117 cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
117 cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
118 version=cgversion)
118 version=cgversion)
119 changegroup.writebundle(self.ui, cg, self.fname, btype, self.vfs,
119 changegroup.writebundle(self.ui, cg, self.fname, btype, self.vfs,
120 compression=compression)
120 compression=compression)
121
121
122 class shelvedstate(object):
122 class shelvedstate(object):
123 """Handle persistence during unshelving operations.
123 """Handle persistence during unshelving operations.
124
124
125 Handles saving and restoring a shelved state. Ensures that different
125 Handles saving and restoring a shelved state. Ensures that different
126 versions of a shelved state are possible and handles them appropriately.
126 versions of a shelved state are possible and handles them appropriately.
127 """
127 """
128 _version = 1
128 _version = 1
129 _filename = 'shelvedstate'
129 _filename = 'shelvedstate'
130
130
131 @classmethod
131 @classmethod
132 def load(cls, repo):
132 def load(cls, repo):
133 fp = repo.vfs(cls._filename)
133 fp = repo.vfs(cls._filename)
134 try:
134 try:
135 version = int(fp.readline().strip())
135 version = int(fp.readline().strip())
136
136
137 if version != cls._version:
137 if version != cls._version:
138 raise util.Abort(_('this version of shelve is incompatible '
138 raise util.Abort(_('this version of shelve is incompatible '
139 'with the version used in this repo'))
139 'with the version used in this repo'))
140 name = fp.readline().strip()
140 name = fp.readline().strip()
141 wctx = fp.readline().strip()
141 wctx = fp.readline().strip()
142 pendingctx = fp.readline().strip()
142 pendingctx = fp.readline().strip()
143 parents = [bin(h) for h in fp.readline().split()]
143 parents = [bin(h) for h in fp.readline().split()]
144 stripnodes = [bin(h) for h in fp.readline().split()]
144 stripnodes = [bin(h) for h in fp.readline().split()]
145 finally:
145 finally:
146 fp.close()
146 fp.close()
147
147
148 obj = cls()
148 obj = cls()
149 obj.name = name
149 obj.name = name
150 obj.wctx = repo[bin(wctx)]
150 obj.wctx = repo[bin(wctx)]
151 obj.pendingctx = repo[bin(pendingctx)]
151 obj.pendingctx = repo[bin(pendingctx)]
152 obj.parents = parents
152 obj.parents = parents
153 obj.stripnodes = stripnodes
153 obj.stripnodes = stripnodes
154
154
155 return obj
155 return obj
156
156
157 @classmethod
157 @classmethod
158 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
158 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
159 fp = repo.vfs(cls._filename, 'wb')
159 fp = repo.vfs(cls._filename, 'wb')
160 fp.write('%i\n' % cls._version)
160 fp.write('%i\n' % cls._version)
161 fp.write('%s\n' % name)
161 fp.write('%s\n' % name)
162 fp.write('%s\n' % hex(originalwctx.node()))
162 fp.write('%s\n' % hex(originalwctx.node()))
163 fp.write('%s\n' % hex(pendingctx.node()))
163 fp.write('%s\n' % hex(pendingctx.node()))
164 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
164 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
165 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
165 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
166 fp.close()
166 fp.close()
167
167
168 @classmethod
168 @classmethod
169 def clear(cls, repo):
169 def clear(cls, repo):
170 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
170 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
171
171
172 def cleanupoldbackups(repo):
172 def cleanupoldbackups(repo):
173 vfs = scmutil.vfs(repo.join(backupdir))
173 vfs = scmutil.vfs(repo.join(backupdir))
174 maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
174 maxbackups = repo.ui.configint('shelve', 'maxbackups', 10)
175 hgfiles = [f for f in vfs.listdir() if f.endswith('.hg')]
175 hgfiles = [f for f in vfs.listdir() if f.endswith('.hg')]
176 hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
176 hgfiles = sorted([(vfs.stat(f).st_mtime, f) for f in hgfiles])
177 if 0 < maxbackups and maxbackups < len(hgfiles):
177 if 0 < maxbackups and maxbackups < len(hgfiles):
178 bordermtime = hgfiles[-maxbackups][0]
178 bordermtime = hgfiles[-maxbackups][0]
179 else:
179 else:
180 bordermtime = None
180 bordermtime = None
181 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
181 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
182 if mtime == bordermtime:
182 if mtime == bordermtime:
183 # keep it, because timestamp can't decide exact order of backups
183 # keep it, because timestamp can't decide exact order of backups
184 continue
184 continue
185 base = f[:-3]
185 base = f[:-3]
186 for ext in 'hg patch'.split():
186 for ext in 'hg patch'.split():
187 try:
187 try:
188 vfs.unlink(base + '.' + ext)
188 vfs.unlink(base + '.' + ext)
189 except OSError as err:
189 except OSError as err:
190 if err.errno != errno.ENOENT:
190 if err.errno != errno.ENOENT:
191 raise
191 raise
192
192
193 def createcmd(ui, repo, pats, opts):
193 def createcmd(ui, repo, pats, opts):
194 """subcommand that creates a new shelve"""
194 """subcommand that creates a new shelve"""
195
195
196 def publicancestors(ctx):
196 def publicancestors(ctx):
197 """Compute the public ancestors of a commit.
197 """Compute the public ancestors of a commit.
198
198
199 Much faster than the revset ancestors(ctx) & draft()"""
199 Much faster than the revset ancestors(ctx) & draft()"""
200 seen = set([nullrev])
200 seen = set([nullrev])
201 visit = collections.deque()
201 visit = collections.deque()
202 visit.append(ctx)
202 visit.append(ctx)
203 while visit:
203 while visit:
204 ctx = visit.popleft()
204 ctx = visit.popleft()
205 yield ctx.node()
205 yield ctx.node()
206 for parent in ctx.parents():
206 for parent in ctx.parents():
207 rev = parent.rev()
207 rev = parent.rev()
208 if rev not in seen:
208 if rev not in seen:
209 seen.add(rev)
209 seen.add(rev)
210 if parent.mutable():
210 if parent.mutable():
211 visit.append(parent)
211 visit.append(parent)
212
212
213 wctx = repo[None]
213 wctx = repo[None]
214 parents = wctx.parents()
214 parents = wctx.parents()
215 if len(parents) > 1:
215 if len(parents) > 1:
216 raise util.Abort(_('cannot shelve while merging'))
216 raise util.Abort(_('cannot shelve while merging'))
217 parent = parents[0]
217 parent = parents[0]
218
218
219 # we never need the user, so we use a generic user for all shelve operations
219 # we never need the user, so we use a generic user for all shelve operations
220 user = 'shelve@localhost'
220 user = 'shelve@localhost'
221 label = repo._activebookmark or parent.branch() or 'default'
221 label = repo._activebookmark or parent.branch() or 'default'
222
222
223 # slashes aren't allowed in filenames, therefore we rename it
223 # slashes aren't allowed in filenames, therefore we rename it
224 label = label.replace('/', '_')
224 label = label.replace('/', '_')
225
225
226 def gennames():
226 def gennames():
227 yield label
227 yield label
228 for i in xrange(1, 100):
228 for i in xrange(1, 100):
229 yield '%s-%02d' % (label, i)
229 yield '%s-%02d' % (label, i)
230
230
231 def commitfunc(ui, repo, message, match, opts):
231 def commitfunc(ui, repo, message, match, opts):
232 hasmq = util.safehasattr(repo, 'mq')
232 hasmq = util.safehasattr(repo, 'mq')
233 if hasmq:
233 if hasmq:
234 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
234 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
235 backup = repo.ui.backupconfig('phases', 'new-commit')
235 backup = repo.ui.backupconfig('phases', 'new-commit')
236 try:
236 try:
237 repo.ui. setconfig('phases', 'new-commit', phases.secret)
237 repo.ui. setconfig('phases', 'new-commit', phases.secret)
238 editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
238 editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
239 return repo.commit(message, user, opts.get('date'), match,
239 return repo.commit(message, user, opts.get('date'), match,
240 editor=editor)
240 editor=editor)
241 finally:
241 finally:
242 repo.ui.restoreconfig(backup)
242 repo.ui.restoreconfig(backup)
243 if hasmq:
243 if hasmq:
244 repo.mq.checkapplied = saved
244 repo.mq.checkapplied = saved
245
245
246 if parent.node() != nullid:
246 if parent.node() != nullid:
247 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
247 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
248 else:
248 else:
249 desc = '(changes in empty repository)'
249 desc = '(changes in empty repository)'
250
250
251 if not opts['message']:
251 if not opts['message']:
252 opts['message'] = desc
252 opts['message'] = desc
253
253
254 name = opts['name']
254 name = opts['name']
255
255
256 wlock = lock = tr = bms = None
256 wlock = lock = tr = None
257 try:
257 try:
258 wlock = repo.wlock()
258 wlock = repo.wlock()
259 lock = repo.lock()
259 lock = repo.lock()
260
260
261 bms = repo._bookmarks.copy()
262 # use an uncommitted transaction to generate the bundle to avoid
261 # use an uncommitted transaction to generate the bundle to avoid
263 # pull races. ensure we don't print the abort message to stderr.
262 # pull races. ensure we don't print the abort message to stderr.
264 tr = repo.transaction('commit', report=lambda x: None)
263 tr = repo.transaction('commit', report=lambda x: None)
265
264
266 if name:
265 if name:
267 if shelvedfile(repo, name, 'hg').exists():
266 if shelvedfile(repo, name, 'hg').exists():
268 raise util.Abort(_("a shelved change named '%s' already exists")
267 raise util.Abort(_("a shelved change named '%s' already exists")
269 % name)
268 % name)
270 else:
269 else:
271 for n in gennames():
270 for n in gennames():
272 if not shelvedfile(repo, n, 'hg').exists():
271 if not shelvedfile(repo, n, 'hg').exists():
273 name = n
272 name = n
274 break
273 break
275 else:
274 else:
276 raise util.Abort(_("too many shelved changes named '%s'") %
275 raise util.Abort(_("too many shelved changes named '%s'") %
277 label)
276 label)
278
277
279 # ensure we are not creating a subdirectory or a hidden file
278 # ensure we are not creating a subdirectory or a hidden file
280 if '/' in name or '\\' in name:
279 if '/' in name or '\\' in name:
281 raise util.Abort(_('shelved change names may not contain slashes'))
280 raise util.Abort(_('shelved change names may not contain slashes'))
282 if name.startswith('.'):
281 if name.startswith('.'):
283 raise util.Abort(_("shelved change names may not start with '.'"))
282 raise util.Abort(_("shelved change names may not start with '.'"))
284 interactive = opts.get('interactive', False)
283 interactive = opts.get('interactive', False)
285
284
286 def interactivecommitfunc(ui, repo, *pats, **opts):
285 def interactivecommitfunc(ui, repo, *pats, **opts):
287 match = scmutil.match(repo['.'], pats, {})
286 match = scmutil.match(repo['.'], pats, {})
288 message = opts['message']
287 message = opts['message']
289 return commitfunc(ui, repo, message, match, opts)
288 return commitfunc(ui, repo, message, match, opts)
290 if not interactive:
289 if not interactive:
291 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
290 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
292 else:
291 else:
293 node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
292 node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
294 False, cmdutil.recordfilter, *pats, **opts)
293 False, cmdutil.recordfilter, *pats, **opts)
295 if not node:
294 if not node:
296 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
295 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
297 if stat.deleted:
296 if stat.deleted:
298 ui.status(_("nothing changed (%d missing files, see "
297 ui.status(_("nothing changed (%d missing files, see "
299 "'hg status')\n") % len(stat.deleted))
298 "'hg status')\n") % len(stat.deleted))
300 else:
299 else:
301 ui.status(_("nothing changed\n"))
300 ui.status(_("nothing changed\n"))
302 return 1
301 return 1
303
302
304 bases = list(publicancestors(repo[node]))
303 bases = list(publicancestors(repo[node]))
305 shelvedfile(repo, name, 'hg').writebundle(bases, node)
304 shelvedfile(repo, name, 'hg').writebundle(bases, node)
306 cmdutil.export(repo, [node],
305 cmdutil.export(repo, [node],
307 fp=shelvedfile(repo, name, 'patch').opener('wb'),
306 fp=shelvedfile(repo, name, 'patch').opener('wb'),
308 opts=mdiff.diffopts(git=True))
307 opts=mdiff.diffopts(git=True))
309
308
310
309
311 if ui.formatted():
310 if ui.formatted():
312 desc = util.ellipsis(desc, ui.termwidth())
311 desc = util.ellipsis(desc, ui.termwidth())
313 ui.status(_('shelved as %s\n') % name)
312 ui.status(_('shelved as %s\n') % name)
314 hg.update(repo, parent.node())
313 hg.update(repo, parent.node())
315 finally:
314 finally:
316 if bms:
317 # restore old bookmarks
318 repo._bookmarks.update(bms)
319 repo._bookmarks.write()
320 if tr:
315 if tr:
321 tr.abort()
316 tr.abort()
322 lockmod.release(lock, wlock)
317 lockmod.release(lock, wlock)
323
318
324 def cleanupcmd(ui, repo):
319 def cleanupcmd(ui, repo):
325 """subcommand that deletes all shelves"""
320 """subcommand that deletes all shelves"""
326
321
327 wlock = None
322 wlock = None
328 try:
323 try:
329 wlock = repo.wlock()
324 wlock = repo.wlock()
330 for (name, _type) in repo.vfs.readdir('shelved'):
325 for (name, _type) in repo.vfs.readdir('shelved'):
331 suffix = name.rsplit('.', 1)[-1]
326 suffix = name.rsplit('.', 1)[-1]
332 if suffix in ('hg', 'patch'):
327 if suffix in ('hg', 'patch'):
333 shelvedfile(repo, name).movetobackup()
328 shelvedfile(repo, name).movetobackup()
334 cleanupoldbackups(repo)
329 cleanupoldbackups(repo)
335 finally:
330 finally:
336 lockmod.release(wlock)
331 lockmod.release(wlock)
337
332
338 def deletecmd(ui, repo, pats):
333 def deletecmd(ui, repo, pats):
339 """subcommand that deletes a specific shelve"""
334 """subcommand that deletes a specific shelve"""
340 if not pats:
335 if not pats:
341 raise util.Abort(_('no shelved changes specified!'))
336 raise util.Abort(_('no shelved changes specified!'))
342 wlock = repo.wlock()
337 wlock = repo.wlock()
343 try:
338 try:
344 for name in pats:
339 for name in pats:
345 for suffix in 'hg patch'.split():
340 for suffix in 'hg patch'.split():
346 shelvedfile(repo, name, suffix).movetobackup()
341 shelvedfile(repo, name, suffix).movetobackup()
347 cleanupoldbackups(repo)
342 cleanupoldbackups(repo)
348 except OSError as err:
343 except OSError as err:
349 if err.errno != errno.ENOENT:
344 if err.errno != errno.ENOENT:
350 raise
345 raise
351 raise util.Abort(_("shelved change '%s' not found") % name)
346 raise util.Abort(_("shelved change '%s' not found") % name)
352 finally:
347 finally:
353 lockmod.release(wlock)
348 lockmod.release(wlock)
354
349
355 def listshelves(repo):
350 def listshelves(repo):
356 """return all shelves in repo as list of (time, filename)"""
351 """return all shelves in repo as list of (time, filename)"""
357 try:
352 try:
358 names = repo.vfs.readdir('shelved')
353 names = repo.vfs.readdir('shelved')
359 except OSError as err:
354 except OSError as err:
360 if err.errno != errno.ENOENT:
355 if err.errno != errno.ENOENT:
361 raise
356 raise
362 return []
357 return []
363 info = []
358 info = []
364 for (name, _type) in names:
359 for (name, _type) in names:
365 pfx, sfx = name.rsplit('.', 1)
360 pfx, sfx = name.rsplit('.', 1)
366 if not pfx or sfx != 'patch':
361 if not pfx or sfx != 'patch':
367 continue
362 continue
368 st = shelvedfile(repo, name).stat()
363 st = shelvedfile(repo, name).stat()
369 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
364 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
370 return sorted(info, reverse=True)
365 return sorted(info, reverse=True)
371
366
372 def listcmd(ui, repo, pats, opts):
367 def listcmd(ui, repo, pats, opts):
373 """subcommand that displays the list of shelves"""
368 """subcommand that displays the list of shelves"""
374 pats = set(pats)
369 pats = set(pats)
375 width = 80
370 width = 80
376 if not ui.plain():
371 if not ui.plain():
377 width = ui.termwidth()
372 width = ui.termwidth()
378 namelabel = 'shelve.newest'
373 namelabel = 'shelve.newest'
379 for mtime, name in listshelves(repo):
374 for mtime, name in listshelves(repo):
380 sname = util.split(name)[1]
375 sname = util.split(name)[1]
381 if pats and sname not in pats:
376 if pats and sname not in pats:
382 continue
377 continue
383 ui.write(sname, label=namelabel)
378 ui.write(sname, label=namelabel)
384 namelabel = 'shelve.name'
379 namelabel = 'shelve.name'
385 if ui.quiet:
380 if ui.quiet:
386 ui.write('\n')
381 ui.write('\n')
387 continue
382 continue
388 ui.write(' ' * (16 - len(sname)))
383 ui.write(' ' * (16 - len(sname)))
389 used = 16
384 used = 16
390 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
385 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
391 ui.write(age, label='shelve.age')
386 ui.write(age, label='shelve.age')
392 ui.write(' ' * (12 - len(age)))
387 ui.write(' ' * (12 - len(age)))
393 used += 12
388 used += 12
394 fp = open(name + '.patch', 'rb')
389 fp = open(name + '.patch', 'rb')
395 try:
390 try:
396 while True:
391 while True:
397 line = fp.readline()
392 line = fp.readline()
398 if not line:
393 if not line:
399 break
394 break
400 if not line.startswith('#'):
395 if not line.startswith('#'):
401 desc = line.rstrip()
396 desc = line.rstrip()
402 if ui.formatted():
397 if ui.formatted():
403 desc = util.ellipsis(desc, width - used)
398 desc = util.ellipsis(desc, width - used)
404 ui.write(desc)
399 ui.write(desc)
405 break
400 break
406 ui.write('\n')
401 ui.write('\n')
407 if not (opts['patch'] or opts['stat']):
402 if not (opts['patch'] or opts['stat']):
408 continue
403 continue
409 difflines = fp.readlines()
404 difflines = fp.readlines()
410 if opts['patch']:
405 if opts['patch']:
411 for chunk, label in patch.difflabel(iter, difflines):
406 for chunk, label in patch.difflabel(iter, difflines):
412 ui.write(chunk, label=label)
407 ui.write(chunk, label=label)
413 if opts['stat']:
408 if opts['stat']:
414 for chunk, label in patch.diffstatui(difflines, width=width,
409 for chunk, label in patch.diffstatui(difflines, width=width,
415 git=True):
410 git=True):
416 ui.write(chunk, label=label)
411 ui.write(chunk, label=label)
417 finally:
412 finally:
418 fp.close()
413 fp.close()
419
414
420 def singlepatchcmds(ui, repo, pats, opts, subcommand):
415 def singlepatchcmds(ui, repo, pats, opts, subcommand):
421 """subcommand that displays a single shelf"""
416 """subcommand that displays a single shelf"""
422 if len(pats) != 1:
417 if len(pats) != 1:
423 raise util.Abort(_("--%s expects a single shelf") % subcommand)
418 raise util.Abort(_("--%s expects a single shelf") % subcommand)
424 shelfname = pats[0]
419 shelfname = pats[0]
425
420
426 if not shelvedfile(repo, shelfname, 'patch').exists():
421 if not shelvedfile(repo, shelfname, 'patch').exists():
427 raise util.Abort(_("cannot find shelf %s") % shelfname)
422 raise util.Abort(_("cannot find shelf %s") % shelfname)
428
423
429 listcmd(ui, repo, pats, opts)
424 listcmd(ui, repo, pats, opts)
430
425
431 def checkparents(repo, state):
426 def checkparents(repo, state):
432 """check parent while resuming an unshelve"""
427 """check parent while resuming an unshelve"""
433 if state.parents != repo.dirstate.parents():
428 if state.parents != repo.dirstate.parents():
434 raise util.Abort(_('working directory parents do not match unshelve '
429 raise util.Abort(_('working directory parents do not match unshelve '
435 'state'))
430 'state'))
436
431
437 def pathtofiles(repo, files):
432 def pathtofiles(repo, files):
438 cwd = repo.getcwd()
433 cwd = repo.getcwd()
439 return [repo.pathto(f, cwd) for f in files]
434 return [repo.pathto(f, cwd) for f in files]
440
435
441 def unshelveabort(ui, repo, state, opts):
436 def unshelveabort(ui, repo, state, opts):
442 """subcommand that abort an in-progress unshelve"""
437 """subcommand that abort an in-progress unshelve"""
443 wlock = repo.wlock()
438 wlock = repo.wlock()
444 lock = None
439 lock = None
445 try:
440 try:
446 checkparents(repo, state)
441 checkparents(repo, state)
447
442
448 util.rename(repo.join('unshelverebasestate'),
443 util.rename(repo.join('unshelverebasestate'),
449 repo.join('rebasestate'))
444 repo.join('rebasestate'))
450 try:
445 try:
451 rebase.rebase(ui, repo, **{
446 rebase.rebase(ui, repo, **{
452 'abort' : True
447 'abort' : True
453 })
448 })
454 except Exception:
449 except Exception:
455 util.rename(repo.join('rebasestate'),
450 util.rename(repo.join('rebasestate'),
456 repo.join('unshelverebasestate'))
451 repo.join('unshelverebasestate'))
457 raise
452 raise
458
453
459 lock = repo.lock()
454 lock = repo.lock()
460
455
461 mergefiles(ui, repo, state.wctx, state.pendingctx)
456 mergefiles(ui, repo, state.wctx, state.pendingctx)
462
457
463 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
458 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
464 shelvedstate.clear(repo)
459 shelvedstate.clear(repo)
465 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
460 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
466 finally:
461 finally:
467 lockmod.release(lock, wlock)
462 lockmod.release(lock, wlock)
468
463
469 def mergefiles(ui, repo, wctx, shelvectx):
464 def mergefiles(ui, repo, wctx, shelvectx):
470 """updates to wctx and merges the changes from shelvectx into the
465 """updates to wctx and merges the changes from shelvectx into the
471 dirstate."""
466 dirstate."""
472 oldquiet = ui.quiet
467 oldquiet = ui.quiet
473 try:
468 try:
474 ui.quiet = True
469 ui.quiet = True
475 hg.update(repo, wctx.node())
470 hg.update(repo, wctx.node())
476 files = []
471 files = []
477 files.extend(shelvectx.files())
472 files.extend(shelvectx.files())
478 files.extend(shelvectx.parents()[0].files())
473 files.extend(shelvectx.parents()[0].files())
479
474
480 # revert will overwrite unknown files, so move them out of the way
475 # revert will overwrite unknown files, so move them out of the way
481 for file in repo.status(unknown=True).unknown:
476 for file in repo.status(unknown=True).unknown:
482 if file in files:
477 if file in files:
483 util.rename(file, file + ".orig")
478 util.rename(file, file + ".orig")
484 ui.pushbuffer(True)
479 ui.pushbuffer(True)
485 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
480 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
486 *pathtofiles(repo, files),
481 *pathtofiles(repo, files),
487 **{'no_backup': True})
482 **{'no_backup': True})
488 ui.popbuffer()
483 ui.popbuffer()
489 finally:
484 finally:
490 ui.quiet = oldquiet
485 ui.quiet = oldquiet
491
486
492 def unshelvecleanup(ui, repo, name, opts):
487 def unshelvecleanup(ui, repo, name, opts):
493 """remove related files after an unshelve"""
488 """remove related files after an unshelve"""
494 if not opts['keep']:
489 if not opts['keep']:
495 for filetype in 'hg patch'.split():
490 for filetype in 'hg patch'.split():
496 shelvedfile(repo, name, filetype).movetobackup()
491 shelvedfile(repo, name, filetype).movetobackup()
497 cleanupoldbackups(repo)
492 cleanupoldbackups(repo)
498
493
499 def unshelvecontinue(ui, repo, state, opts):
494 def unshelvecontinue(ui, repo, state, opts):
500 """subcommand to continue an in-progress unshelve"""
495 """subcommand to continue an in-progress unshelve"""
501 # We're finishing off a merge. First parent is our original
496 # We're finishing off a merge. First parent is our original
502 # parent, second is the temporary "fake" commit we're unshelving.
497 # parent, second is the temporary "fake" commit we're unshelving.
503 wlock = repo.wlock()
498 wlock = repo.wlock()
504 lock = None
499 lock = None
505 try:
500 try:
506 checkparents(repo, state)
501 checkparents(repo, state)
507 ms = merge.mergestate(repo)
502 ms = merge.mergestate(repo)
508 if [f for f in ms if ms[f] == 'u']:
503 if [f for f in ms if ms[f] == 'u']:
509 raise util.Abort(
504 raise util.Abort(
510 _("unresolved conflicts, can't continue"),
505 _("unresolved conflicts, can't continue"),
511 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
506 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
512
507
513 lock = repo.lock()
508 lock = repo.lock()
514
509
515 util.rename(repo.join('unshelverebasestate'),
510 util.rename(repo.join('unshelverebasestate'),
516 repo.join('rebasestate'))
511 repo.join('rebasestate'))
517 try:
512 try:
518 rebase.rebase(ui, repo, **{
513 rebase.rebase(ui, repo, **{
519 'continue' : True
514 'continue' : True
520 })
515 })
521 except Exception:
516 except Exception:
522 util.rename(repo.join('rebasestate'),
517 util.rename(repo.join('rebasestate'),
523 repo.join('unshelverebasestate'))
518 repo.join('unshelverebasestate'))
524 raise
519 raise
525
520
526 shelvectx = repo['tip']
521 shelvectx = repo['tip']
527 if not shelvectx in state.pendingctx.children():
522 if not shelvectx in state.pendingctx.children():
528 # rebase was a no-op, so it produced no child commit
523 # rebase was a no-op, so it produced no child commit
529 shelvectx = state.pendingctx
524 shelvectx = state.pendingctx
530 else:
525 else:
531 # only strip the shelvectx if the rebase produced it
526 # only strip the shelvectx if the rebase produced it
532 state.stripnodes.append(shelvectx.node())
527 state.stripnodes.append(shelvectx.node())
533
528
534 mergefiles(ui, repo, state.wctx, shelvectx)
529 mergefiles(ui, repo, state.wctx, shelvectx)
535
530
536 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
531 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
537 shelvedstate.clear(repo)
532 shelvedstate.clear(repo)
538 unshelvecleanup(ui, repo, state.name, opts)
533 unshelvecleanup(ui, repo, state.name, opts)
539 ui.status(_("unshelve of '%s' complete\n") % state.name)
534 ui.status(_("unshelve of '%s' complete\n") % state.name)
540 finally:
535 finally:
541 lockmod.release(lock, wlock)
536 lockmod.release(lock, wlock)
542
537
543 @command('unshelve',
538 @command('unshelve',
544 [('a', 'abort', None,
539 [('a', 'abort', None,
545 _('abort an incomplete unshelve operation')),
540 _('abort an incomplete unshelve operation')),
546 ('c', 'continue', None,
541 ('c', 'continue', None,
547 _('continue an incomplete unshelve operation')),
542 _('continue an incomplete unshelve operation')),
548 ('', 'keep', None,
543 ('', 'keep', None,
549 _('keep shelve after unshelving')),
544 _('keep shelve after unshelving')),
550 ('', 'date', '',
545 ('', 'date', '',
551 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
546 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
552 _('hg unshelve [SHELVED]'))
547 _('hg unshelve [SHELVED]'))
553 def unshelve(ui, repo, *shelved, **opts):
548 def unshelve(ui, repo, *shelved, **opts):
554 """restore a shelved change to the working directory
549 """restore a shelved change to the working directory
555
550
556 This command accepts an optional name of a shelved change to
551 This command accepts an optional name of a shelved change to
557 restore. If none is given, the most recent shelved change is used.
552 restore. If none is given, the most recent shelved change is used.
558
553
559 If a shelved change is applied successfully, the bundle that
554 If a shelved change is applied successfully, the bundle that
560 contains the shelved changes is moved to a backup location
555 contains the shelved changes is moved to a backup location
561 (.hg/shelve-backup).
556 (.hg/shelve-backup).
562
557
563 Since you can restore a shelved change on top of an arbitrary
558 Since you can restore a shelved change on top of an arbitrary
564 commit, it is possible that unshelving will result in a conflict
559 commit, it is possible that unshelving will result in a conflict
565 between your changes and the commits you are unshelving onto. If
560 between your changes and the commits you are unshelving onto. If
566 this occurs, you must resolve the conflict, then use
561 this occurs, you must resolve the conflict, then use
567 ``--continue`` to complete the unshelve operation. (The bundle
562 ``--continue`` to complete the unshelve operation. (The bundle
568 will not be moved until you successfully complete the unshelve.)
563 will not be moved until you successfully complete the unshelve.)
569
564
570 (Alternatively, you can use ``--abort`` to abandon an unshelve
565 (Alternatively, you can use ``--abort`` to abandon an unshelve
571 that causes a conflict. This reverts the unshelved changes, and
566 that causes a conflict. This reverts the unshelved changes, and
572 leaves the bundle in place.)
567 leaves the bundle in place.)
573
568
574 After a successful unshelve, the shelved changes are stored in a
569 After a successful unshelve, the shelved changes are stored in a
575 backup directory. Only the N most recent backups are kept. N
570 backup directory. Only the N most recent backups are kept. N
576 defaults to 10 but can be overridden using the ``shelve.maxbackups``
571 defaults to 10 but can be overridden using the ``shelve.maxbackups``
577 configuration option.
572 configuration option.
578
573
579 .. container:: verbose
574 .. container:: verbose
580
575
581 Timestamp in seconds is used to decide order of backups. More
576 Timestamp in seconds is used to decide order of backups. More
582 than ``maxbackups`` backups are kept, if same timestamp
577 than ``maxbackups`` backups are kept, if same timestamp
583 prevents from deciding exact order of them, for safety.
578 prevents from deciding exact order of them, for safety.
584 """
579 """
585 abortf = opts['abort']
580 abortf = opts['abort']
586 continuef = opts['continue']
581 continuef = opts['continue']
587 if not abortf and not continuef:
582 if not abortf and not continuef:
588 cmdutil.checkunfinished(repo)
583 cmdutil.checkunfinished(repo)
589
584
590 if abortf or continuef:
585 if abortf or continuef:
591 if abortf and continuef:
586 if abortf and continuef:
592 raise util.Abort(_('cannot use both abort and continue'))
587 raise util.Abort(_('cannot use both abort and continue'))
593 if shelved:
588 if shelved:
594 raise util.Abort(_('cannot combine abort/continue with '
589 raise util.Abort(_('cannot combine abort/continue with '
595 'naming a shelved change'))
590 'naming a shelved change'))
596
591
597 try:
592 try:
598 state = shelvedstate.load(repo)
593 state = shelvedstate.load(repo)
599 except IOError as err:
594 except IOError as err:
600 if err.errno != errno.ENOENT:
595 if err.errno != errno.ENOENT:
601 raise
596 raise
602 raise util.Abort(_('no unshelve operation underway'))
597 raise util.Abort(_('no unshelve operation underway'))
603
598
604 if abortf:
599 if abortf:
605 return unshelveabort(ui, repo, state, opts)
600 return unshelveabort(ui, repo, state, opts)
606 elif continuef:
601 elif continuef:
607 return unshelvecontinue(ui, repo, state, opts)
602 return unshelvecontinue(ui, repo, state, opts)
608 elif len(shelved) > 1:
603 elif len(shelved) > 1:
609 raise util.Abort(_('can only unshelve one change at a time'))
604 raise util.Abort(_('can only unshelve one change at a time'))
610 elif not shelved:
605 elif not shelved:
611 shelved = listshelves(repo)
606 shelved = listshelves(repo)
612 if not shelved:
607 if not shelved:
613 raise util.Abort(_('no shelved changes to apply!'))
608 raise util.Abort(_('no shelved changes to apply!'))
614 basename = util.split(shelved[0][1])[1]
609 basename = util.split(shelved[0][1])[1]
615 ui.status(_("unshelving change '%s'\n") % basename)
610 ui.status(_("unshelving change '%s'\n") % basename)
616 else:
611 else:
617 basename = shelved[0]
612 basename = shelved[0]
618
613
619 if not shelvedfile(repo, basename, 'patch').exists():
614 if not shelvedfile(repo, basename, 'patch').exists():
620 raise util.Abort(_("shelved change '%s' not found") % basename)
615 raise util.Abort(_("shelved change '%s' not found") % basename)
621
616
622 oldquiet = ui.quiet
617 oldquiet = ui.quiet
623 wlock = lock = tr = None
618 wlock = lock = tr = None
624 try:
619 try:
625 wlock = repo.wlock()
620 wlock = repo.wlock()
626 lock = repo.lock()
621 lock = repo.lock()
627
622
628 tr = repo.transaction('unshelve', report=lambda x: None)
623 tr = repo.transaction('unshelve', report=lambda x: None)
629 oldtiprev = len(repo)
624 oldtiprev = len(repo)
630
625
631 pctx = repo['.']
626 pctx = repo['.']
632 tmpwctx = pctx
627 tmpwctx = pctx
633 # The goal is to have a commit structure like so:
628 # The goal is to have a commit structure like so:
634 # ...-> pctx -> tmpwctx -> shelvectx
629 # ...-> pctx -> tmpwctx -> shelvectx
635 # where tmpwctx is an optional commit with the user's pending changes
630 # where tmpwctx is an optional commit with the user's pending changes
636 # and shelvectx is the unshelved changes. Then we merge it all down
631 # and shelvectx is the unshelved changes. Then we merge it all down
637 # to the original pctx.
632 # to the original pctx.
638
633
639 # Store pending changes in a commit
634 # Store pending changes in a commit
640 s = repo.status()
635 s = repo.status()
641 if s.modified or s.added or s.removed or s.deleted:
636 if s.modified or s.added or s.removed or s.deleted:
642 ui.status(_("temporarily committing pending changes "
637 ui.status(_("temporarily committing pending changes "
643 "(restore with 'hg unshelve --abort')\n"))
638 "(restore with 'hg unshelve --abort')\n"))
644 def commitfunc(ui, repo, message, match, opts):
639 def commitfunc(ui, repo, message, match, opts):
645 hasmq = util.safehasattr(repo, 'mq')
640 hasmq = util.safehasattr(repo, 'mq')
646 if hasmq:
641 if hasmq:
647 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
642 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
648
643
649 backup = repo.ui.backupconfig('phases', 'new-commit')
644 backup = repo.ui.backupconfig('phases', 'new-commit')
650 try:
645 try:
651 repo.ui. setconfig('phases', 'new-commit', phases.secret)
646 repo.ui. setconfig('phases', 'new-commit', phases.secret)
652 return repo.commit(message, 'shelve@localhost',
647 return repo.commit(message, 'shelve@localhost',
653 opts.get('date'), match)
648 opts.get('date'), match)
654 finally:
649 finally:
655 repo.ui.restoreconfig(backup)
650 repo.ui.restoreconfig(backup)
656 if hasmq:
651 if hasmq:
657 repo.mq.checkapplied = saved
652 repo.mq.checkapplied = saved
658
653
659 tempopts = {}
654 tempopts = {}
660 tempopts['message'] = "pending changes temporary commit"
655 tempopts['message'] = "pending changes temporary commit"
661 tempopts['date'] = opts.get('date')
656 tempopts['date'] = opts.get('date')
662 ui.quiet = True
657 ui.quiet = True
663 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
658 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
664 tmpwctx = repo[node]
659 tmpwctx = repo[node]
665
660
666 ui.quiet = True
661 ui.quiet = True
667 shelvedfile(repo, basename, 'hg').applybundle()
662 shelvedfile(repo, basename, 'hg').applybundle()
668
663
669 ui.quiet = oldquiet
664 ui.quiet = oldquiet
670
665
671 shelvectx = repo['tip']
666 shelvectx = repo['tip']
672
667
673 # If the shelve is not immediately on top of the commit
668 # If the shelve is not immediately on top of the commit
674 # we'll be merging with, rebase it to be on top.
669 # we'll be merging with, rebase it to be on top.
675 if tmpwctx.node() != shelvectx.parents()[0].node():
670 if tmpwctx.node() != shelvectx.parents()[0].node():
676 ui.status(_('rebasing shelved changes\n'))
671 ui.status(_('rebasing shelved changes\n'))
677 try:
672 try:
678 rebase.rebase(ui, repo, **{
673 rebase.rebase(ui, repo, **{
679 'rev' : [shelvectx.rev()],
674 'rev' : [shelvectx.rev()],
680 'dest' : str(tmpwctx.rev()),
675 'dest' : str(tmpwctx.rev()),
681 'keep' : True,
676 'keep' : True,
682 })
677 })
683 except error.InterventionRequired:
678 except error.InterventionRequired:
684 tr.close()
679 tr.close()
685
680
686 stripnodes = [repo.changelog.node(rev)
681 stripnodes = [repo.changelog.node(rev)
687 for rev in xrange(oldtiprev, len(repo))]
682 for rev in xrange(oldtiprev, len(repo))]
688 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
683 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
689
684
690 util.rename(repo.join('rebasestate'),
685 util.rename(repo.join('rebasestate'),
691 repo.join('unshelverebasestate'))
686 repo.join('unshelverebasestate'))
692 raise error.InterventionRequired(
687 raise error.InterventionRequired(
693 _("unresolved conflicts (see 'hg resolve', then "
688 _("unresolved conflicts (see 'hg resolve', then "
694 "'hg unshelve --continue')"))
689 "'hg unshelve --continue')"))
695
690
696 # refresh ctx after rebase completes
691 # refresh ctx after rebase completes
697 shelvectx = repo['tip']
692 shelvectx = repo['tip']
698
693
699 if not shelvectx in tmpwctx.children():
694 if not shelvectx in tmpwctx.children():
700 # rebase was a no-op, so it produced no child commit
695 # rebase was a no-op, so it produced no child commit
701 shelvectx = tmpwctx
696 shelvectx = tmpwctx
702
697
703 mergefiles(ui, repo, pctx, shelvectx)
698 mergefiles(ui, repo, pctx, shelvectx)
704 shelvedstate.clear(repo)
699 shelvedstate.clear(repo)
705
700
706 # The transaction aborting will strip all the commits for us,
701 # The transaction aborting will strip all the commits for us,
707 # but it doesn't update the inmemory structures, so addchangegroup
702 # but it doesn't update the inmemory structures, so addchangegroup
708 # hooks still fire and try to operate on the missing commits.
703 # hooks still fire and try to operate on the missing commits.
709 # Clean up manually to prevent this.
704 # Clean up manually to prevent this.
710 repo.unfiltered().changelog.strip(oldtiprev, tr)
705 repo.unfiltered().changelog.strip(oldtiprev, tr)
711
706
712 unshelvecleanup(ui, repo, basename, opts)
707 unshelvecleanup(ui, repo, basename, opts)
713 finally:
708 finally:
714 ui.quiet = oldquiet
709 ui.quiet = oldquiet
715 if tr:
710 if tr:
716 tr.release()
711 tr.release()
717 lockmod.release(lock, wlock)
712 lockmod.release(lock, wlock)
718
713
719 @command('shelve',
714 @command('shelve',
720 [('A', 'addremove', None,
715 [('A', 'addremove', None,
721 _('mark new/missing files as added/removed before shelving')),
716 _('mark new/missing files as added/removed before shelving')),
722 ('', 'cleanup', None,
717 ('', 'cleanup', None,
723 _('delete all shelved changes')),
718 _('delete all shelved changes')),
724 ('', 'date', '',
719 ('', 'date', '',
725 _('shelve with the specified commit date'), _('DATE')),
720 _('shelve with the specified commit date'), _('DATE')),
726 ('d', 'delete', None,
721 ('d', 'delete', None,
727 _('delete the named shelved change(s)')),
722 _('delete the named shelved change(s)')),
728 ('e', 'edit', False,
723 ('e', 'edit', False,
729 _('invoke editor on commit messages')),
724 _('invoke editor on commit messages')),
730 ('l', 'list', None,
725 ('l', 'list', None,
731 _('list current shelves')),
726 _('list current shelves')),
732 ('m', 'message', '',
727 ('m', 'message', '',
733 _('use text as shelve message'), _('TEXT')),
728 _('use text as shelve message'), _('TEXT')),
734 ('n', 'name', '',
729 ('n', 'name', '',
735 _('use the given name for the shelved commit'), _('NAME')),
730 _('use the given name for the shelved commit'), _('NAME')),
736 ('p', 'patch', None,
731 ('p', 'patch', None,
737 _('show patch')),
732 _('show patch')),
738 ('i', 'interactive', None,
733 ('i', 'interactive', None,
739 _('interactive mode, only works while creating a shelve')),
734 _('interactive mode, only works while creating a shelve')),
740 ('', 'stat', None,
735 ('', 'stat', None,
741 _('output diffstat-style summary of changes'))] + commands.walkopts,
736 _('output diffstat-style summary of changes'))] + commands.walkopts,
742 _('hg shelve [OPTION]... [FILE]...'))
737 _('hg shelve [OPTION]... [FILE]...'))
743 def shelvecmd(ui, repo, *pats, **opts):
738 def shelvecmd(ui, repo, *pats, **opts):
744 '''save and set aside changes from the working directory
739 '''save and set aside changes from the working directory
745
740
746 Shelving takes files that "hg status" reports as not clean, saves
741 Shelving takes files that "hg status" reports as not clean, saves
747 the modifications to a bundle (a shelved change), and reverts the
742 the modifications to a bundle (a shelved change), and reverts the
748 files so that their state in the working directory becomes clean.
743 files so that their state in the working directory becomes clean.
749
744
750 To restore these changes to the working directory, using "hg
745 To restore these changes to the working directory, using "hg
751 unshelve"; this will work even if you switch to a different
746 unshelve"; this will work even if you switch to a different
752 commit.
747 commit.
753
748
754 When no files are specified, "hg shelve" saves all not-clean
749 When no files are specified, "hg shelve" saves all not-clean
755 files. If specific files or directories are named, only changes to
750 files. If specific files or directories are named, only changes to
756 those files are shelved.
751 those files are shelved.
757
752
758 Each shelved change has a name that makes it easier to find later.
753 Each shelved change has a name that makes it easier to find later.
759 The name of a shelved change defaults to being based on the active
754 The name of a shelved change defaults to being based on the active
760 bookmark, or if there is no active bookmark, the current named
755 bookmark, or if there is no active bookmark, the current named
761 branch. To specify a different name, use ``--name``.
756 branch. To specify a different name, use ``--name``.
762
757
763 To see a list of existing shelved changes, use the ``--list``
758 To see a list of existing shelved changes, use the ``--list``
764 option. For each shelved change, this will print its name, age,
759 option. For each shelved change, this will print its name, age,
765 and description; use ``--patch`` or ``--stat`` for more details.
760 and description; use ``--patch`` or ``--stat`` for more details.
766
761
767 To delete specific shelved changes, use ``--delete``. To delete
762 To delete specific shelved changes, use ``--delete``. To delete
768 all shelved changes, use ``--cleanup``.
763 all shelved changes, use ``--cleanup``.
769 '''
764 '''
770 cmdutil.checkunfinished(repo)
765 cmdutil.checkunfinished(repo)
771
766
772 allowables = [
767 allowables = [
773 ('addremove', set(['create'])), # 'create' is pseudo action
768 ('addremove', set(['create'])), # 'create' is pseudo action
774 ('cleanup', set(['cleanup'])),
769 ('cleanup', set(['cleanup'])),
775 # ('date', set(['create'])), # ignored for passing '--date "0 0"' in tests
770 # ('date', set(['create'])), # ignored for passing '--date "0 0"' in tests
776 ('delete', set(['delete'])),
771 ('delete', set(['delete'])),
777 ('edit', set(['create'])),
772 ('edit', set(['create'])),
778 ('list', set(['list'])),
773 ('list', set(['list'])),
779 ('message', set(['create'])),
774 ('message', set(['create'])),
780 ('name', set(['create'])),
775 ('name', set(['create'])),
781 ('patch', set(['patch', 'list'])),
776 ('patch', set(['patch', 'list'])),
782 ('stat', set(['stat', 'list'])),
777 ('stat', set(['stat', 'list'])),
783 ]
778 ]
784 def checkopt(opt):
779 def checkopt(opt):
785 if opts[opt]:
780 if opts[opt]:
786 for i, allowable in allowables:
781 for i, allowable in allowables:
787 if opts[i] and opt not in allowable:
782 if opts[i] and opt not in allowable:
788 raise util.Abort(_("options '--%s' and '--%s' may not be "
783 raise util.Abort(_("options '--%s' and '--%s' may not be "
789 "used together") % (opt, i))
784 "used together") % (opt, i))
790 return True
785 return True
791 if checkopt('cleanup'):
786 if checkopt('cleanup'):
792 if pats:
787 if pats:
793 raise util.Abort(_("cannot specify names when using '--cleanup'"))
788 raise util.Abort(_("cannot specify names when using '--cleanup'"))
794 return cleanupcmd(ui, repo)
789 return cleanupcmd(ui, repo)
795 elif checkopt('delete'):
790 elif checkopt('delete'):
796 return deletecmd(ui, repo, pats)
791 return deletecmd(ui, repo, pats)
797 elif checkopt('list'):
792 elif checkopt('list'):
798 return listcmd(ui, repo, pats, opts)
793 return listcmd(ui, repo, pats, opts)
799 elif checkopt('patch'):
794 elif checkopt('patch'):
800 return singlepatchcmds(ui, repo, pats, opts, subcommand='patch')
795 return singlepatchcmds(ui, repo, pats, opts, subcommand='patch')
801 elif checkopt('stat'):
796 elif checkopt('stat'):
802 return singlepatchcmds(ui, repo, pats, opts, subcommand='stat')
797 return singlepatchcmds(ui, repo, pats, opts, subcommand='stat')
803 else:
798 else:
804 return createcmd(ui, repo, pats, opts)
799 return createcmd(ui, repo, pats, opts)
805
800
806 def extsetup(ui):
801 def extsetup(ui):
807 cmdutil.unfinishedstates.append(
802 cmdutil.unfinishedstates.append(
808 [shelvedstate._filename, False, False,
803 [shelvedstate._filename, False, False,
809 _('unshelve already in progress'),
804 _('unshelve already in progress'),
810 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
805 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
@@ -1,570 +1,578 b''
1 # Mercurial bookmark support code
1 # Mercurial bookmark support code
2 #
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
3 # Copyright 2008 David Soria Parra <dsp@php.net>
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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12
12
13 from .i18n import _
13 from .i18n import _
14 from .node import (
14 from .node import (
15 bin,
15 bin,
16 hex,
16 hex,
17 )
17 )
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 lock as lockmod,
20 lock as lockmod,
21 obsolete,
21 obsolete,
22 util,
22 util,
23 )
23 )
24
24
25 class bmstore(dict):
25 class bmstore(dict):
26 """Storage for bookmarks.
26 """Storage for bookmarks.
27
27
28 This object should do all bookmark reads and writes, so that it's
28 This object should do all bookmark reads and writes, so that it's
29 fairly simple to replace the storage underlying bookmarks without
29 fairly simple to replace the storage underlying bookmarks without
30 having to clone the logic surrounding bookmarks.
30 having to clone the logic surrounding bookmarks.
31
31
32 This particular bmstore implementation stores bookmarks as
32 This particular bmstore implementation stores bookmarks as
33 {hash}\s{name}\n (the same format as localtags) in
33 {hash}\s{name}\n (the same format as localtags) in
34 .hg/bookmarks. The mapping is stored as {name: nodeid}.
34 .hg/bookmarks. The mapping is stored as {name: nodeid}.
35
35
36 This class does NOT handle the "active" bookmark state at this
36 This class does NOT handle the "active" bookmark state at this
37 time.
37 time.
38 """
38 """
39
39
40 def __init__(self, repo):
40 def __init__(self, repo):
41 dict.__init__(self)
41 dict.__init__(self)
42 self._repo = repo
42 self._repo = repo
43 try:
43 try:
44 bkfile = self.getbkfile(repo)
44 bkfile = self.getbkfile(repo)
45 for line in bkfile:
45 for line in bkfile:
46 line = line.strip()
46 line = line.strip()
47 if not line:
47 if not line:
48 continue
48 continue
49 if ' ' not in line:
49 if ' ' not in line:
50 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
50 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
51 % line)
51 % line)
52 continue
52 continue
53 sha, refspec = line.split(' ', 1)
53 sha, refspec = line.split(' ', 1)
54 refspec = encoding.tolocal(refspec)
54 refspec = encoding.tolocal(refspec)
55 try:
55 try:
56 self[refspec] = repo.changelog.lookup(sha)
56 self[refspec] = repo.changelog.lookup(sha)
57 except LookupError:
57 except LookupError:
58 pass
58 pass
59 except IOError as inst:
59 except IOError as inst:
60 if inst.errno != errno.ENOENT:
60 if inst.errno != errno.ENOENT:
61 raise
61 raise
62
62
63 def getbkfile(self, repo):
63 def getbkfile(self, repo):
64 bkfile = None
64 bkfile = None
65 if 'HG_PENDING' in os.environ:
65 if 'HG_PENDING' in os.environ:
66 try:
66 try:
67 bkfile = repo.vfs('bookmarks.pending')
67 bkfile = repo.vfs('bookmarks.pending')
68 except IOError as inst:
68 except IOError as inst:
69 if inst.errno != errno.ENOENT:
69 if inst.errno != errno.ENOENT:
70 raise
70 raise
71 if bkfile is None:
71 if bkfile is None:
72 bkfile = repo.vfs('bookmarks')
72 bkfile = repo.vfs('bookmarks')
73 return bkfile
73 return bkfile
74
74
75 def recordchange(self, tr):
75 def recordchange(self, tr):
76 """record that bookmarks have been changed in a transaction
76 """record that bookmarks have been changed in a transaction
77
77
78 The transaction is then responsible for updating the file content."""
78 The transaction is then responsible for updating the file content."""
79 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
79 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
80 location='plain')
80 location='plain')
81 tr.hookargs['bookmark_moved'] = '1'
81 tr.hookargs['bookmark_moved'] = '1'
82
82
83 def write(self):
83 def write(self):
84 '''Write bookmarks
84 '''Write bookmarks
85
85
86 Write the given bookmark => hash dictionary to the .hg/bookmarks file
86 Write the given bookmark => hash dictionary to the .hg/bookmarks file
87 in a format equal to those of localtags.
87 in a format equal to those of localtags.
88
88
89 We also store a backup of the previous state in undo.bookmarks that
89 We also store a backup of the previous state in undo.bookmarks that
90 can be copied back on rollback.
90 can be copied back on rollback.
91 '''
91 '''
92 repo = self._repo
92 repo = self._repo
93 if (repo.ui.configbool('devel', 'all-warnings')
93 if (repo.ui.configbool('devel', 'all-warnings')
94 or repo.ui.configbool('devel', 'check-locks')):
94 or repo.ui.configbool('devel', 'check-locks')):
95 l = repo._wlockref and repo._wlockref()
95 l = repo._wlockref and repo._wlockref()
96 if l is None or not l.held:
96 if l is None or not l.held:
97 repo.ui.develwarn('bookmarks write with no wlock')
97 repo.ui.develwarn('bookmarks write with no wlock')
98
99 tr = repo.currenttransaction()
100 if tr:
101 self.recordchange(tr)
102 # invalidatevolatilesets() is omitted because this doesn't
103 # write changes out actually
104 return
105
98 self._writerepo(repo)
106 self._writerepo(repo)
99 repo.invalidatevolatilesets()
107 repo.invalidatevolatilesets()
100
108
101 def _writerepo(self, repo):
109 def _writerepo(self, repo):
102 """Factored out for extensibility"""
110 """Factored out for extensibility"""
103 if repo._activebookmark not in self:
111 if repo._activebookmark not in self:
104 deactivate(repo)
112 deactivate(repo)
105
113
106 wlock = repo.wlock()
114 wlock = repo.wlock()
107 try:
115 try:
108
116
109 file = repo.vfs('bookmarks', 'w', atomictemp=True)
117 file = repo.vfs('bookmarks', 'w', atomictemp=True)
110 self._write(file)
118 self._write(file)
111 file.close()
119 file.close()
112
120
113 finally:
121 finally:
114 wlock.release()
122 wlock.release()
115
123
116 def _write(self, fp):
124 def _write(self, fp):
117 for name, node in self.iteritems():
125 for name, node in self.iteritems():
118 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
126 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
119
127
120 def readactive(repo):
128 def readactive(repo):
121 """
129 """
122 Get the active bookmark. We can have an active bookmark that updates
130 Get the active bookmark. We can have an active bookmark that updates
123 itself as we commit. This function returns the name of that bookmark.
131 itself as we commit. This function returns the name of that bookmark.
124 It is stored in .hg/bookmarks.current
132 It is stored in .hg/bookmarks.current
125 """
133 """
126 mark = None
134 mark = None
127 try:
135 try:
128 file = repo.vfs('bookmarks.current')
136 file = repo.vfs('bookmarks.current')
129 except IOError as inst:
137 except IOError as inst:
130 if inst.errno != errno.ENOENT:
138 if inst.errno != errno.ENOENT:
131 raise
139 raise
132 return None
140 return None
133 try:
141 try:
134 # No readline() in osutil.posixfile, reading everything is cheap
142 # No readline() in osutil.posixfile, reading everything is cheap
135 mark = encoding.tolocal((file.readlines() or [''])[0])
143 mark = encoding.tolocal((file.readlines() or [''])[0])
136 if mark == '' or mark not in repo._bookmarks:
144 if mark == '' or mark not in repo._bookmarks:
137 mark = None
145 mark = None
138 finally:
146 finally:
139 file.close()
147 file.close()
140 return mark
148 return mark
141
149
142 def activate(repo, mark):
150 def activate(repo, mark):
143 """
151 """
144 Set the given bookmark to be 'active', meaning that this bookmark will
152 Set the given bookmark to be 'active', meaning that this bookmark will
145 follow new commits that are made.
153 follow new commits that are made.
146 The name is recorded in .hg/bookmarks.current
154 The name is recorded in .hg/bookmarks.current
147 """
155 """
148 if mark not in repo._bookmarks:
156 if mark not in repo._bookmarks:
149 raise AssertionError('bookmark %s does not exist!' % mark)
157 raise AssertionError('bookmark %s does not exist!' % mark)
150
158
151 active = repo._activebookmark
159 active = repo._activebookmark
152 if active == mark:
160 if active == mark:
153 return
161 return
154
162
155 wlock = repo.wlock()
163 wlock = repo.wlock()
156 try:
164 try:
157 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
165 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
158 file.write(encoding.fromlocal(mark))
166 file.write(encoding.fromlocal(mark))
159 file.close()
167 file.close()
160 finally:
168 finally:
161 wlock.release()
169 wlock.release()
162 repo._activebookmark = mark
170 repo._activebookmark = mark
163
171
164 def deactivate(repo):
172 def deactivate(repo):
165 """
173 """
166 Unset the active bookmark in this reposiotry.
174 Unset the active bookmark in this reposiotry.
167 """
175 """
168 wlock = repo.wlock()
176 wlock = repo.wlock()
169 try:
177 try:
170 repo.vfs.unlink('bookmarks.current')
178 repo.vfs.unlink('bookmarks.current')
171 repo._activebookmark = None
179 repo._activebookmark = None
172 except OSError as inst:
180 except OSError as inst:
173 if inst.errno != errno.ENOENT:
181 if inst.errno != errno.ENOENT:
174 raise
182 raise
175 finally:
183 finally:
176 wlock.release()
184 wlock.release()
177
185
178 def isactivewdirparent(repo):
186 def isactivewdirparent(repo):
179 """
187 """
180 Tell whether the 'active' bookmark (the one that follows new commits)
188 Tell whether the 'active' bookmark (the one that follows new commits)
181 points to one of the parents of the current working directory (wdir).
189 points to one of the parents of the current working directory (wdir).
182
190
183 While this is normally the case, it can on occasion be false; for example,
191 While this is normally the case, it can on occasion be false; for example,
184 immediately after a pull, the active bookmark can be moved to point
192 immediately after a pull, the active bookmark can be moved to point
185 to a place different than the wdir. This is solved by running `hg update`.
193 to a place different than the wdir. This is solved by running `hg update`.
186 """
194 """
187 mark = repo._activebookmark
195 mark = repo._activebookmark
188 marks = repo._bookmarks
196 marks = repo._bookmarks
189 parents = [p.node() for p in repo[None].parents()]
197 parents = [p.node() for p in repo[None].parents()]
190 return (mark in marks and marks[mark] in parents)
198 return (mark in marks and marks[mark] in parents)
191
199
192 def deletedivergent(repo, deletefrom, bm):
200 def deletedivergent(repo, deletefrom, bm):
193 '''Delete divergent versions of bm on nodes in deletefrom.
201 '''Delete divergent versions of bm on nodes in deletefrom.
194
202
195 Return True if at least one bookmark was deleted, False otherwise.'''
203 Return True if at least one bookmark was deleted, False otherwise.'''
196 deleted = False
204 deleted = False
197 marks = repo._bookmarks
205 marks = repo._bookmarks
198 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
206 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
199 for mark in divergent:
207 for mark in divergent:
200 if mark == '@' or '@' not in mark:
208 if mark == '@' or '@' not in mark:
201 # can't be divergent by definition
209 # can't be divergent by definition
202 continue
210 continue
203 if mark and marks[mark] in deletefrom:
211 if mark and marks[mark] in deletefrom:
204 if mark != bm:
212 if mark != bm:
205 del marks[mark]
213 del marks[mark]
206 deleted = True
214 deleted = True
207 return deleted
215 return deleted
208
216
209 def calculateupdate(ui, repo, checkout):
217 def calculateupdate(ui, repo, checkout):
210 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
218 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
211 check out and where to move the active bookmark from, if needed.'''
219 check out and where to move the active bookmark from, if needed.'''
212 movemarkfrom = None
220 movemarkfrom = None
213 if checkout is None:
221 if checkout is None:
214 activemark = repo._activebookmark
222 activemark = repo._activebookmark
215 if isactivewdirparent(repo):
223 if isactivewdirparent(repo):
216 movemarkfrom = repo['.'].node()
224 movemarkfrom = repo['.'].node()
217 elif activemark:
225 elif activemark:
218 ui.status(_("updating to active bookmark %s\n") % activemark)
226 ui.status(_("updating to active bookmark %s\n") % activemark)
219 checkout = activemark
227 checkout = activemark
220 return (checkout, movemarkfrom)
228 return (checkout, movemarkfrom)
221
229
222 def update(repo, parents, node):
230 def update(repo, parents, node):
223 deletefrom = parents
231 deletefrom = parents
224 marks = repo._bookmarks
232 marks = repo._bookmarks
225 update = False
233 update = False
226 active = repo._activebookmark
234 active = repo._activebookmark
227 if not active:
235 if not active:
228 return False
236 return False
229
237
230 if marks[active] in parents:
238 if marks[active] in parents:
231 new = repo[node]
239 new = repo[node]
232 divs = [repo[b] for b in marks
240 divs = [repo[b] for b in marks
233 if b.split('@', 1)[0] == active.split('@', 1)[0]]
241 if b.split('@', 1)[0] == active.split('@', 1)[0]]
234 anc = repo.changelog.ancestors([new.rev()])
242 anc = repo.changelog.ancestors([new.rev()])
235 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
243 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
236 if validdest(repo, repo[marks[active]], new):
244 if validdest(repo, repo[marks[active]], new):
237 marks[active] = new.node()
245 marks[active] = new.node()
238 update = True
246 update = True
239
247
240 if deletedivergent(repo, deletefrom, active):
248 if deletedivergent(repo, deletefrom, active):
241 update = True
249 update = True
242
250
243 if update:
251 if update:
244 marks.write()
252 marks.write()
245 return update
253 return update
246
254
247 def listbookmarks(repo):
255 def listbookmarks(repo):
248 # We may try to list bookmarks on a repo type that does not
256 # We may try to list bookmarks on a repo type that does not
249 # support it (e.g., statichttprepository).
257 # support it (e.g., statichttprepository).
250 marks = getattr(repo, '_bookmarks', {})
258 marks = getattr(repo, '_bookmarks', {})
251
259
252 d = {}
260 d = {}
253 hasnode = repo.changelog.hasnode
261 hasnode = repo.changelog.hasnode
254 for k, v in marks.iteritems():
262 for k, v in marks.iteritems():
255 # don't expose local divergent bookmarks
263 # don't expose local divergent bookmarks
256 if hasnode(v) and ('@' not in k or k.endswith('@')):
264 if hasnode(v) and ('@' not in k or k.endswith('@')):
257 d[k] = hex(v)
265 d[k] = hex(v)
258 return d
266 return d
259
267
260 def pushbookmark(repo, key, old, new):
268 def pushbookmark(repo, key, old, new):
261 w = l = tr = None
269 w = l = tr = None
262 try:
270 try:
263 w = repo.wlock()
271 w = repo.wlock()
264 l = repo.lock()
272 l = repo.lock()
265 tr = repo.transaction('bookmarks')
273 tr = repo.transaction('bookmarks')
266 marks = repo._bookmarks
274 marks = repo._bookmarks
267 existing = hex(marks.get(key, ''))
275 existing = hex(marks.get(key, ''))
268 if existing != old and existing != new:
276 if existing != old and existing != new:
269 return False
277 return False
270 if new == '':
278 if new == '':
271 del marks[key]
279 del marks[key]
272 else:
280 else:
273 if new not in repo:
281 if new not in repo:
274 return False
282 return False
275 marks[key] = repo[new].node()
283 marks[key] = repo[new].node()
276 marks.recordchange(tr)
284 marks.recordchange(tr)
277 tr.close()
285 tr.close()
278 return True
286 return True
279 finally:
287 finally:
280 lockmod.release(tr, l, w)
288 lockmod.release(tr, l, w)
281
289
282 def compare(repo, srcmarks, dstmarks,
290 def compare(repo, srcmarks, dstmarks,
283 srchex=None, dsthex=None, targets=None):
291 srchex=None, dsthex=None, targets=None):
284 '''Compare bookmarks between srcmarks and dstmarks
292 '''Compare bookmarks between srcmarks and dstmarks
285
293
286 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
294 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
287 differ, invalid)", each are list of bookmarks below:
295 differ, invalid)", each are list of bookmarks below:
288
296
289 :addsrc: added on src side (removed on dst side, perhaps)
297 :addsrc: added on src side (removed on dst side, perhaps)
290 :adddst: added on dst side (removed on src side, perhaps)
298 :adddst: added on dst side (removed on src side, perhaps)
291 :advsrc: advanced on src side
299 :advsrc: advanced on src side
292 :advdst: advanced on dst side
300 :advdst: advanced on dst side
293 :diverge: diverge
301 :diverge: diverge
294 :differ: changed, but changeset referred on src is unknown on dst
302 :differ: changed, but changeset referred on src is unknown on dst
295 :invalid: unknown on both side
303 :invalid: unknown on both side
296 :same: same on both side
304 :same: same on both side
297
305
298 Each elements of lists in result tuple is tuple "(bookmark name,
306 Each elements of lists in result tuple is tuple "(bookmark name,
299 changeset ID on source side, changeset ID on destination
307 changeset ID on source side, changeset ID on destination
300 side)". Each changeset IDs are 40 hexadecimal digit string or
308 side)". Each changeset IDs are 40 hexadecimal digit string or
301 None.
309 None.
302
310
303 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
311 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
304 "invalid" list may be unknown for repo.
312 "invalid" list may be unknown for repo.
305
313
306 This function expects that "srcmarks" and "dstmarks" return
314 This function expects that "srcmarks" and "dstmarks" return
307 changeset ID in 40 hexadecimal digit string for specified
315 changeset ID in 40 hexadecimal digit string for specified
308 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
316 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
309 binary value), "srchex" or "dsthex" should be specified to convert
317 binary value), "srchex" or "dsthex" should be specified to convert
310 into such form.
318 into such form.
311
319
312 If "targets" is specified, only bookmarks listed in it are
320 If "targets" is specified, only bookmarks listed in it are
313 examined.
321 examined.
314 '''
322 '''
315 if not srchex:
323 if not srchex:
316 srchex = lambda x: x
324 srchex = lambda x: x
317 if not dsthex:
325 if not dsthex:
318 dsthex = lambda x: x
326 dsthex = lambda x: x
319
327
320 if targets:
328 if targets:
321 bset = set(targets)
329 bset = set(targets)
322 else:
330 else:
323 srcmarkset = set(srcmarks)
331 srcmarkset = set(srcmarks)
324 dstmarkset = set(dstmarks)
332 dstmarkset = set(dstmarks)
325 bset = srcmarkset | dstmarkset
333 bset = srcmarkset | dstmarkset
326
334
327 results = ([], [], [], [], [], [], [], [])
335 results = ([], [], [], [], [], [], [], [])
328 addsrc = results[0].append
336 addsrc = results[0].append
329 adddst = results[1].append
337 adddst = results[1].append
330 advsrc = results[2].append
338 advsrc = results[2].append
331 advdst = results[3].append
339 advdst = results[3].append
332 diverge = results[4].append
340 diverge = results[4].append
333 differ = results[5].append
341 differ = results[5].append
334 invalid = results[6].append
342 invalid = results[6].append
335 same = results[7].append
343 same = results[7].append
336
344
337 for b in sorted(bset):
345 for b in sorted(bset):
338 if b not in srcmarks:
346 if b not in srcmarks:
339 if b in dstmarks:
347 if b in dstmarks:
340 adddst((b, None, dsthex(dstmarks[b])))
348 adddst((b, None, dsthex(dstmarks[b])))
341 else:
349 else:
342 invalid((b, None, None))
350 invalid((b, None, None))
343 elif b not in dstmarks:
351 elif b not in dstmarks:
344 addsrc((b, srchex(srcmarks[b]), None))
352 addsrc((b, srchex(srcmarks[b]), None))
345 else:
353 else:
346 scid = srchex(srcmarks[b])
354 scid = srchex(srcmarks[b])
347 dcid = dsthex(dstmarks[b])
355 dcid = dsthex(dstmarks[b])
348 if scid == dcid:
356 if scid == dcid:
349 same((b, scid, dcid))
357 same((b, scid, dcid))
350 elif scid in repo and dcid in repo:
358 elif scid in repo and dcid in repo:
351 sctx = repo[scid]
359 sctx = repo[scid]
352 dctx = repo[dcid]
360 dctx = repo[dcid]
353 if sctx.rev() < dctx.rev():
361 if sctx.rev() < dctx.rev():
354 if validdest(repo, sctx, dctx):
362 if validdest(repo, sctx, dctx):
355 advdst((b, scid, dcid))
363 advdst((b, scid, dcid))
356 else:
364 else:
357 diverge((b, scid, dcid))
365 diverge((b, scid, dcid))
358 else:
366 else:
359 if validdest(repo, dctx, sctx):
367 if validdest(repo, dctx, sctx):
360 advsrc((b, scid, dcid))
368 advsrc((b, scid, dcid))
361 else:
369 else:
362 diverge((b, scid, dcid))
370 diverge((b, scid, dcid))
363 else:
371 else:
364 # it is too expensive to examine in detail, in this case
372 # it is too expensive to examine in detail, in this case
365 differ((b, scid, dcid))
373 differ((b, scid, dcid))
366
374
367 return results
375 return results
368
376
369 def _diverge(ui, b, path, localmarks, remotenode):
377 def _diverge(ui, b, path, localmarks, remotenode):
370 '''Return appropriate diverged bookmark for specified ``path``
378 '''Return appropriate diverged bookmark for specified ``path``
371
379
372 This returns None, if it is failed to assign any divergent
380 This returns None, if it is failed to assign any divergent
373 bookmark name.
381 bookmark name.
374
382
375 This reuses already existing one with "@number" suffix, if it
383 This reuses already existing one with "@number" suffix, if it
376 refers ``remotenode``.
384 refers ``remotenode``.
377 '''
385 '''
378 if b == '@':
386 if b == '@':
379 b = ''
387 b = ''
380 # try to use an @pathalias suffix
388 # try to use an @pathalias suffix
381 # if an @pathalias already exists, we overwrite (update) it
389 # if an @pathalias already exists, we overwrite (update) it
382 if path.startswith("file:"):
390 if path.startswith("file:"):
383 path = util.url(path).path
391 path = util.url(path).path
384 for p, u in ui.configitems("paths"):
392 for p, u in ui.configitems("paths"):
385 if u.startswith("file:"):
393 if u.startswith("file:"):
386 u = util.url(u).path
394 u = util.url(u).path
387 if path == u:
395 if path == u:
388 return '%s@%s' % (b, p)
396 return '%s@%s' % (b, p)
389
397
390 # assign a unique "@number" suffix newly
398 # assign a unique "@number" suffix newly
391 for x in range(1, 100):
399 for x in range(1, 100):
392 n = '%s@%d' % (b, x)
400 n = '%s@%d' % (b, x)
393 if n not in localmarks or localmarks[n] == remotenode:
401 if n not in localmarks or localmarks[n] == remotenode:
394 return n
402 return n
395
403
396 return None
404 return None
397
405
398 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
406 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
399 ui.debug("checking for updated bookmarks\n")
407 ui.debug("checking for updated bookmarks\n")
400 localmarks = repo._bookmarks
408 localmarks = repo._bookmarks
401 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
409 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
402 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
410 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
403
411
404 status = ui.status
412 status = ui.status
405 warn = ui.warn
413 warn = ui.warn
406 if ui.configbool('ui', 'quietbookmarkmove', False):
414 if ui.configbool('ui', 'quietbookmarkmove', False):
407 status = warn = ui.debug
415 status = warn = ui.debug
408
416
409 explicit = set(explicit)
417 explicit = set(explicit)
410 changed = []
418 changed = []
411 for b, scid, dcid in addsrc:
419 for b, scid, dcid in addsrc:
412 if scid in repo: # add remote bookmarks for changes we already have
420 if scid in repo: # add remote bookmarks for changes we already have
413 changed.append((b, bin(scid), status,
421 changed.append((b, bin(scid), status,
414 _("adding remote bookmark %s\n") % (b)))
422 _("adding remote bookmark %s\n") % (b)))
415 elif b in explicit:
423 elif b in explicit:
416 explicit.remove(b)
424 explicit.remove(b)
417 ui.warn(_("remote bookmark %s points to locally missing %s\n")
425 ui.warn(_("remote bookmark %s points to locally missing %s\n")
418 % (b, scid[:12]))
426 % (b, scid[:12]))
419
427
420 for b, scid, dcid in advsrc:
428 for b, scid, dcid in advsrc:
421 changed.append((b, bin(scid), status,
429 changed.append((b, bin(scid), status,
422 _("updating bookmark %s\n") % (b)))
430 _("updating bookmark %s\n") % (b)))
423 # remove normal movement from explicit set
431 # remove normal movement from explicit set
424 explicit.difference_update(d[0] for d in changed)
432 explicit.difference_update(d[0] for d in changed)
425
433
426 for b, scid, dcid in diverge:
434 for b, scid, dcid in diverge:
427 if b in explicit:
435 if b in explicit:
428 explicit.discard(b)
436 explicit.discard(b)
429 changed.append((b, bin(scid), status,
437 changed.append((b, bin(scid), status,
430 _("importing bookmark %s\n") % (b)))
438 _("importing bookmark %s\n") % (b)))
431 else:
439 else:
432 snode = bin(scid)
440 snode = bin(scid)
433 db = _diverge(ui, b, path, localmarks, snode)
441 db = _diverge(ui, b, path, localmarks, snode)
434 if db:
442 if db:
435 changed.append((db, snode, warn,
443 changed.append((db, snode, warn,
436 _("divergent bookmark %s stored as %s\n") %
444 _("divergent bookmark %s stored as %s\n") %
437 (b, db)))
445 (b, db)))
438 else:
446 else:
439 warn(_("warning: failed to assign numbered name "
447 warn(_("warning: failed to assign numbered name "
440 "to divergent bookmark %s\n") % (b))
448 "to divergent bookmark %s\n") % (b))
441 for b, scid, dcid in adddst + advdst:
449 for b, scid, dcid in adddst + advdst:
442 if b in explicit:
450 if b in explicit:
443 explicit.discard(b)
451 explicit.discard(b)
444 changed.append((b, bin(scid), status,
452 changed.append((b, bin(scid), status,
445 _("importing bookmark %s\n") % (b)))
453 _("importing bookmark %s\n") % (b)))
446 for b, scid, dcid in differ:
454 for b, scid, dcid in differ:
447 if b in explicit:
455 if b in explicit:
448 explicit.remove(b)
456 explicit.remove(b)
449 ui.warn(_("remote bookmark %s points to locally missing %s\n")
457 ui.warn(_("remote bookmark %s points to locally missing %s\n")
450 % (b, scid[:12]))
458 % (b, scid[:12]))
451
459
452 if changed:
460 if changed:
453 tr = trfunc()
461 tr = trfunc()
454 for b, node, writer, msg in sorted(changed):
462 for b, node, writer, msg in sorted(changed):
455 localmarks[b] = node
463 localmarks[b] = node
456 writer(msg)
464 writer(msg)
457 localmarks.recordchange(tr)
465 localmarks.recordchange(tr)
458
466
459 def incoming(ui, repo, other):
467 def incoming(ui, repo, other):
460 '''Show bookmarks incoming from other to repo
468 '''Show bookmarks incoming from other to repo
461 '''
469 '''
462 ui.status(_("searching for changed bookmarks\n"))
470 ui.status(_("searching for changed bookmarks\n"))
463
471
464 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
472 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
465 dsthex=hex)
473 dsthex=hex)
466 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
474 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
467
475
468 incomings = []
476 incomings = []
469 if ui.debugflag:
477 if ui.debugflag:
470 getid = lambda id: id
478 getid = lambda id: id
471 else:
479 else:
472 getid = lambda id: id[:12]
480 getid = lambda id: id[:12]
473 if ui.verbose:
481 if ui.verbose:
474 def add(b, id, st):
482 def add(b, id, st):
475 incomings.append(" %-25s %s %s\n" % (b, getid(id), st))
483 incomings.append(" %-25s %s %s\n" % (b, getid(id), st))
476 else:
484 else:
477 def add(b, id, st):
485 def add(b, id, st):
478 incomings.append(" %-25s %s\n" % (b, getid(id)))
486 incomings.append(" %-25s %s\n" % (b, getid(id)))
479 for b, scid, dcid in addsrc:
487 for b, scid, dcid in addsrc:
480 # i18n: "added" refers to a bookmark
488 # i18n: "added" refers to a bookmark
481 add(b, scid, _('added'))
489 add(b, scid, _('added'))
482 for b, scid, dcid in advsrc:
490 for b, scid, dcid in advsrc:
483 # i18n: "advanced" refers to a bookmark
491 # i18n: "advanced" refers to a bookmark
484 add(b, scid, _('advanced'))
492 add(b, scid, _('advanced'))
485 for b, scid, dcid in diverge:
493 for b, scid, dcid in diverge:
486 # i18n: "diverged" refers to a bookmark
494 # i18n: "diverged" refers to a bookmark
487 add(b, scid, _('diverged'))
495 add(b, scid, _('diverged'))
488 for b, scid, dcid in differ:
496 for b, scid, dcid in differ:
489 # i18n: "changed" refers to a bookmark
497 # i18n: "changed" refers to a bookmark
490 add(b, scid, _('changed'))
498 add(b, scid, _('changed'))
491
499
492 if not incomings:
500 if not incomings:
493 ui.status(_("no changed bookmarks found\n"))
501 ui.status(_("no changed bookmarks found\n"))
494 return 1
502 return 1
495
503
496 for s in sorted(incomings):
504 for s in sorted(incomings):
497 ui.write(s)
505 ui.write(s)
498
506
499 return 0
507 return 0
500
508
501 def outgoing(ui, repo, other):
509 def outgoing(ui, repo, other):
502 '''Show bookmarks outgoing from repo to other
510 '''Show bookmarks outgoing from repo to other
503 '''
511 '''
504 ui.status(_("searching for changed bookmarks\n"))
512 ui.status(_("searching for changed bookmarks\n"))
505
513
506 r = compare(repo, repo._bookmarks, other.listkeys('bookmarks'),
514 r = compare(repo, repo._bookmarks, other.listkeys('bookmarks'),
507 srchex=hex)
515 srchex=hex)
508 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
516 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
509
517
510 outgoings = []
518 outgoings = []
511 if ui.debugflag:
519 if ui.debugflag:
512 getid = lambda id: id
520 getid = lambda id: id
513 else:
521 else:
514 getid = lambda id: id[:12]
522 getid = lambda id: id[:12]
515 if ui.verbose:
523 if ui.verbose:
516 def add(b, id, st):
524 def add(b, id, st):
517 outgoings.append(" %-25s %s %s\n" % (b, getid(id), st))
525 outgoings.append(" %-25s %s %s\n" % (b, getid(id), st))
518 else:
526 else:
519 def add(b, id, st):
527 def add(b, id, st):
520 outgoings.append(" %-25s %s\n" % (b, getid(id)))
528 outgoings.append(" %-25s %s\n" % (b, getid(id)))
521 for b, scid, dcid in addsrc:
529 for b, scid, dcid in addsrc:
522 # i18n: "added refers to a bookmark
530 # i18n: "added refers to a bookmark
523 add(b, scid, _('added'))
531 add(b, scid, _('added'))
524 for b, scid, dcid in adddst:
532 for b, scid, dcid in adddst:
525 # i18n: "deleted" refers to a bookmark
533 # i18n: "deleted" refers to a bookmark
526 add(b, ' ' * 40, _('deleted'))
534 add(b, ' ' * 40, _('deleted'))
527 for b, scid, dcid in advsrc:
535 for b, scid, dcid in advsrc:
528 # i18n: "advanced" refers to a bookmark
536 # i18n: "advanced" refers to a bookmark
529 add(b, scid, _('advanced'))
537 add(b, scid, _('advanced'))
530 for b, scid, dcid in diverge:
538 for b, scid, dcid in diverge:
531 # i18n: "diverged" refers to a bookmark
539 # i18n: "diverged" refers to a bookmark
532 add(b, scid, _('diverged'))
540 add(b, scid, _('diverged'))
533 for b, scid, dcid in differ:
541 for b, scid, dcid in differ:
534 # i18n: "changed" refers to a bookmark
542 # i18n: "changed" refers to a bookmark
535 add(b, scid, _('changed'))
543 add(b, scid, _('changed'))
536
544
537 if not outgoings:
545 if not outgoings:
538 ui.status(_("no changed bookmarks found\n"))
546 ui.status(_("no changed bookmarks found\n"))
539 return 1
547 return 1
540
548
541 for s in sorted(outgoings):
549 for s in sorted(outgoings):
542 ui.write(s)
550 ui.write(s)
543
551
544 return 0
552 return 0
545
553
546 def summary(repo, other):
554 def summary(repo, other):
547 '''Compare bookmarks between repo and other for "hg summary" output
555 '''Compare bookmarks between repo and other for "hg summary" output
548
556
549 This returns "(# of incoming, # of outgoing)" tuple.
557 This returns "(# of incoming, # of outgoing)" tuple.
550 '''
558 '''
551 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
559 r = compare(repo, other.listkeys('bookmarks'), repo._bookmarks,
552 dsthex=hex)
560 dsthex=hex)
553 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
561 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = r
554 return (len(addsrc), len(adddst))
562 return (len(addsrc), len(adddst))
555
563
556 def validdest(repo, old, new):
564 def validdest(repo, old, new):
557 """Is the new bookmark destination a valid update from the old one"""
565 """Is the new bookmark destination a valid update from the old one"""
558 repo = repo.unfiltered()
566 repo = repo.unfiltered()
559 if old == new:
567 if old == new:
560 # Old == new -> nothing to update.
568 # Old == new -> nothing to update.
561 return False
569 return False
562 elif not old:
570 elif not old:
563 # old is nullrev, anything is valid.
571 # old is nullrev, anything is valid.
564 # (new != nullrev has been excluded by the previous check)
572 # (new != nullrev has been excluded by the previous check)
565 return True
573 return True
566 elif repo.obsstore:
574 elif repo.obsstore:
567 return new.node() in obsolete.foreground(repo, [old.node()])
575 return new.node() in obsolete.foreground(repo, [old.node()])
568 else:
576 else:
569 # still an independent clause as it is lazier (and therefore faster)
577 # still an independent clause as it is lazier (and therefore faster)
570 return old.descendant(new)
578 return old.descendant(new)
@@ -1,1002 +1,1017 b''
1 $ cat <<EOF >> $HGRCPATH
1 $ cat <<EOF >> $HGRCPATH
2 > [extensions]
2 > [extensions]
3 > mq =
3 > mq =
4 > shelve =
4 > shelve =
5 > [defaults]
5 > [defaults]
6 > diff = --nodates --git
6 > diff = --nodates --git
7 > qnew = --date '0 0'
7 > qnew = --date '0 0'
8 > [shelve]
8 > [shelve]
9 > maxbackups = 2
9 > maxbackups = 2
10 > EOF
10 > EOF
11
11
12 $ hg init repo
12 $ hg init repo
13 $ cd repo
13 $ cd repo
14 $ mkdir a b
14 $ mkdir a b
15 $ echo a > a/a
15 $ echo a > a/a
16 $ echo b > b/b
16 $ echo b > b/b
17 $ echo c > c
17 $ echo c > c
18 $ echo d > d
18 $ echo d > d
19 $ echo x > x
19 $ echo x > x
20 $ hg addremove -q
20 $ hg addremove -q
21
21
22 shelve has a help message
22 shelve has a help message
23 $ hg shelve -h
23 $ hg shelve -h
24 hg shelve [OPTION]... [FILE]...
24 hg shelve [OPTION]... [FILE]...
25
25
26 save and set aside changes from the working directory
26 save and set aside changes from the working directory
27
27
28 Shelving takes files that "hg status" reports as not clean, saves the
28 Shelving takes files that "hg status" reports as not clean, saves the
29 modifications to a bundle (a shelved change), and reverts the files so
29 modifications to a bundle (a shelved change), and reverts the files so
30 that their state in the working directory becomes clean.
30 that their state in the working directory becomes clean.
31
31
32 To restore these changes to the working directory, using "hg unshelve";
32 To restore these changes to the working directory, using "hg unshelve";
33 this will work even if you switch to a different commit.
33 this will work even if you switch to a different commit.
34
34
35 When no files are specified, "hg shelve" saves all not-clean files. If
35 When no files are specified, "hg shelve" saves all not-clean files. If
36 specific files or directories are named, only changes to those files are
36 specific files or directories are named, only changes to those files are
37 shelved.
37 shelved.
38
38
39 Each shelved change has a name that makes it easier to find later. The
39 Each shelved change has a name that makes it easier to find later. The
40 name of a shelved change defaults to being based on the active bookmark,
40 name of a shelved change defaults to being based on the active bookmark,
41 or if there is no active bookmark, the current named branch. To specify a
41 or if there is no active bookmark, the current named branch. To specify a
42 different name, use "--name".
42 different name, use "--name".
43
43
44 To see a list of existing shelved changes, use the "--list" option. For
44 To see a list of existing shelved changes, use the "--list" option. For
45 each shelved change, this will print its name, age, and description; use "
45 each shelved change, this will print its name, age, and description; use "
46 --patch" or "--stat" for more details.
46 --patch" or "--stat" for more details.
47
47
48 To delete specific shelved changes, use "--delete". To delete all shelved
48 To delete specific shelved changes, use "--delete". To delete all shelved
49 changes, use "--cleanup".
49 changes, use "--cleanup".
50
50
51 (use "hg help -e shelve" to show help for the shelve extension)
51 (use "hg help -e shelve" to show help for the shelve extension)
52
52
53 options ([+] can be repeated):
53 options ([+] can be repeated):
54
54
55 -A --addremove mark new/missing files as added/removed before
55 -A --addremove mark new/missing files as added/removed before
56 shelving
56 shelving
57 --cleanup delete all shelved changes
57 --cleanup delete all shelved changes
58 --date DATE shelve with the specified commit date
58 --date DATE shelve with the specified commit date
59 -d --delete delete the named shelved change(s)
59 -d --delete delete the named shelved change(s)
60 -e --edit invoke editor on commit messages
60 -e --edit invoke editor on commit messages
61 -l --list list current shelves
61 -l --list list current shelves
62 -m --message TEXT use text as shelve message
62 -m --message TEXT use text as shelve message
63 -n --name NAME use the given name for the shelved commit
63 -n --name NAME use the given name for the shelved commit
64 -p --patch show patch
64 -p --patch show patch
65 -i --interactive interactive mode, only works while creating a shelve
65 -i --interactive interactive mode, only works while creating a shelve
66 --stat output diffstat-style summary of changes
66 --stat output diffstat-style summary of changes
67 -I --include PATTERN [+] include names matching the given patterns
67 -I --include PATTERN [+] include names matching the given patterns
68 -X --exclude PATTERN [+] exclude names matching the given patterns
68 -X --exclude PATTERN [+] exclude names matching the given patterns
69 --mq operate on patch repository
69 --mq operate on patch repository
70
70
71 (some details hidden, use --verbose to show complete help)
71 (some details hidden, use --verbose to show complete help)
72
72
73 shelving in an empty repo should be possible
73 shelving in an empty repo should be possible
74 (this tests also that editor is not invoked, if '--edit' is not
74 (this tests also that editor is not invoked, if '--edit' is not
75 specified)
75 specified)
76
76
77 $ HGEDITOR=cat hg shelve
77 $ HGEDITOR=cat hg shelve
78 shelved as default
78 shelved as default
79 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
79 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
80
80
81 $ hg unshelve
81 $ hg unshelve
82 unshelving change 'default'
82 unshelving change 'default'
83
83
84 $ hg commit -q -m 'initial commit'
84 $ hg commit -q -m 'initial commit'
85
85
86 $ hg shelve
86 $ hg shelve
87 nothing changed
87 nothing changed
88 [1]
88 [1]
89
89
90 make sure shelve files were backed up
90 make sure shelve files were backed up
91
91
92 $ ls .hg/shelve-backup
92 $ ls .hg/shelve-backup
93 default.hg
93 default.hg
94 default.patch
94 default.patch
95
95
96 create an mq patch - shelving should work fine with a patch applied
96 create an mq patch - shelving should work fine with a patch applied
97
97
98 $ echo n > n
98 $ echo n > n
99 $ hg add n
99 $ hg add n
100 $ hg commit n -m second
100 $ hg commit n -m second
101 $ hg qnew second.patch
101 $ hg qnew second.patch
102
102
103 shelve a change that we will delete later
103 shelve a change that we will delete later
104
104
105 $ echo a >> a/a
105 $ echo a >> a/a
106 $ hg shelve
106 $ hg shelve
107 shelved as default
107 shelved as default
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109
109
110 set up some more complex changes to shelve
110 set up some more complex changes to shelve
111
111
112 $ echo a >> a/a
112 $ echo a >> a/a
113 $ hg mv b b.rename
113 $ hg mv b b.rename
114 moving b/b to b.rename/b (glob)
114 moving b/b to b.rename/b (glob)
115 $ hg cp c c.copy
115 $ hg cp c c.copy
116 $ hg status -C
116 $ hg status -C
117 M a/a
117 M a/a
118 A b.rename/b
118 A b.rename/b
119 b/b
119 b/b
120 A c.copy
120 A c.copy
121 c
121 c
122 R b/b
122 R b/b
123
123
124 prevent some foot-shooting
124 prevent some foot-shooting
125
125
126 $ hg shelve -n foo/bar
126 $ hg shelve -n foo/bar
127 abort: shelved change names may not contain slashes
127 abort: shelved change names may not contain slashes
128 [255]
128 [255]
129 $ hg shelve -n .baz
129 $ hg shelve -n .baz
130 abort: shelved change names may not start with '.'
130 abort: shelved change names may not start with '.'
131 [255]
131 [255]
132
132
133 the common case - no options or filenames
133 the common case - no options or filenames
134
134
135 $ hg shelve
135 $ hg shelve
136 shelved as default-01
136 shelved as default-01
137 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
137 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
138 $ hg status -C
138 $ hg status -C
139
139
140 ensure that our shelved changes exist
140 ensure that our shelved changes exist
141
141
142 $ hg shelve -l
142 $ hg shelve -l
143 default-01 (*)* changes to '[mq]: second.patch' (glob)
143 default-01 (*)* changes to '[mq]: second.patch' (glob)
144 default (*)* changes to '[mq]: second.patch' (glob)
144 default (*)* changes to '[mq]: second.patch' (glob)
145
145
146 $ hg shelve -l -p default
146 $ hg shelve -l -p default
147 default (*)* changes to '[mq]: second.patch' (glob)
147 default (*)* changes to '[mq]: second.patch' (glob)
148
148
149 diff --git a/a/a b/a/a
149 diff --git a/a/a b/a/a
150 --- a/a/a
150 --- a/a/a
151 +++ b/a/a
151 +++ b/a/a
152 @@ -1,1 +1,2 @@
152 @@ -1,1 +1,2 @@
153 a
153 a
154 +a
154 +a
155
155
156 $ hg shelve --list --addremove
156 $ hg shelve --list --addremove
157 abort: options '--list' and '--addremove' may not be used together
157 abort: options '--list' and '--addremove' may not be used together
158 [255]
158 [255]
159
159
160 delete our older shelved change
160 delete our older shelved change
161
161
162 $ hg shelve -d default
162 $ hg shelve -d default
163 $ hg qfinish -a -q
163 $ hg qfinish -a -q
164
164
165 ensure shelve backups aren't overwritten
165 ensure shelve backups aren't overwritten
166
166
167 $ ls .hg/shelve-backup/
167 $ ls .hg/shelve-backup/
168 default-1.hg
168 default-1.hg
169 default-1.patch
169 default-1.patch
170 default.hg
170 default.hg
171 default.patch
171 default.patch
172
172
173 local edits should not prevent a shelved change from applying
173 local edits should not prevent a shelved change from applying
174
174
175 $ printf "z\na\n" > a/a
175 $ printf "z\na\n" > a/a
176 $ hg unshelve --keep
176 $ hg unshelve --keep
177 unshelving change 'default-01'
177 unshelving change 'default-01'
178 temporarily committing pending changes (restore with 'hg unshelve --abort')
178 temporarily committing pending changes (restore with 'hg unshelve --abort')
179 rebasing shelved changes
179 rebasing shelved changes
180 rebasing 4:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
180 rebasing 4:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
181 merging a/a
181 merging a/a
182
182
183 $ hg revert --all -q
183 $ hg revert --all -q
184 $ rm a/a.orig b.rename/b c.copy
184 $ rm a/a.orig b.rename/b c.copy
185
185
186 apply it and make sure our state is as expected
186 apply it and make sure our state is as expected
187
187
188 (this also tests that same timestamp prevents backups from being
188 (this also tests that same timestamp prevents backups from being
189 removed, even though there are more than 'maxbackups' backups)
189 removed, even though there are more than 'maxbackups' backups)
190
190
191 $ f -t .hg/shelve-backup/default.hg
191 $ f -t .hg/shelve-backup/default.hg
192 .hg/shelve-backup/default.hg: file
192 .hg/shelve-backup/default.hg: file
193 $ touch -t 200001010000 .hg/shelve-backup/default.hg
193 $ touch -t 200001010000 .hg/shelve-backup/default.hg
194 $ f -t .hg/shelve-backup/default-1.hg
194 $ f -t .hg/shelve-backup/default-1.hg
195 .hg/shelve-backup/default-1.hg: file
195 .hg/shelve-backup/default-1.hg: file
196 $ touch -t 200001010000 .hg/shelve-backup/default-1.hg
196 $ touch -t 200001010000 .hg/shelve-backup/default-1.hg
197
197
198 $ hg unshelve
198 $ hg unshelve
199 unshelving change 'default-01'
199 unshelving change 'default-01'
200 $ hg status -C
200 $ hg status -C
201 M a/a
201 M a/a
202 A b.rename/b
202 A b.rename/b
203 b/b
203 b/b
204 A c.copy
204 A c.copy
205 c
205 c
206 R b/b
206 R b/b
207 $ hg shelve -l
207 $ hg shelve -l
208
208
209 (both of default.hg and default-1.hg should be still kept, because it
209 (both of default.hg and default-1.hg should be still kept, because it
210 is difficult to decide actual order of them from same timestamp)
210 is difficult to decide actual order of them from same timestamp)
211
211
212 $ ls .hg/shelve-backup/
212 $ ls .hg/shelve-backup/
213 default-01.hg
213 default-01.hg
214 default-01.patch
214 default-01.patch
215 default-1.hg
215 default-1.hg
216 default-1.patch
216 default-1.patch
217 default.hg
217 default.hg
218 default.patch
218 default.patch
219
219
220 $ hg unshelve
220 $ hg unshelve
221 abort: no shelved changes to apply!
221 abort: no shelved changes to apply!
222 [255]
222 [255]
223 $ hg unshelve foo
223 $ hg unshelve foo
224 abort: shelved change 'foo' not found
224 abort: shelved change 'foo' not found
225 [255]
225 [255]
226
226
227 named shelves, specific filenames, and "commit messages" should all work
227 named shelves, specific filenames, and "commit messages" should all work
228 (this tests also that editor is invoked, if '--edit' is specified)
228 (this tests also that editor is invoked, if '--edit' is specified)
229
229
230 $ hg status -C
230 $ hg status -C
231 M a/a
231 M a/a
232 A b.rename/b
232 A b.rename/b
233 b/b
233 b/b
234 A c.copy
234 A c.copy
235 c
235 c
236 R b/b
236 R b/b
237 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
237 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
238 wat
238 wat
239
239
240
240
241 HG: Enter commit message. Lines beginning with 'HG:' are removed.
241 HG: Enter commit message. Lines beginning with 'HG:' are removed.
242 HG: Leave message empty to abort commit.
242 HG: Leave message empty to abort commit.
243 HG: --
243 HG: --
244 HG: user: shelve@localhost
244 HG: user: shelve@localhost
245 HG: branch 'default'
245 HG: branch 'default'
246 HG: changed a/a
246 HG: changed a/a
247
247
248 expect "a" to no longer be present, but status otherwise unchanged
248 expect "a" to no longer be present, but status otherwise unchanged
249
249
250 $ hg status -C
250 $ hg status -C
251 A b.rename/b
251 A b.rename/b
252 b/b
252 b/b
253 A c.copy
253 A c.copy
254 c
254 c
255 R b/b
255 R b/b
256 $ hg shelve -l --stat
256 $ hg shelve -l --stat
257 wibble (*) wat (glob)
257 wibble (*) wat (glob)
258 a/a | 1 +
258 a/a | 1 +
259 1 files changed, 1 insertions(+), 0 deletions(-)
259 1 files changed, 1 insertions(+), 0 deletions(-)
260
260
261 and now "a/a" should reappear
261 and now "a/a" should reappear
262
262
263 $ cd a
263 $ cd a
264 $ hg unshelve -q wibble
264 $ hg unshelve -q wibble
265 $ cd ..
265 $ cd ..
266 $ hg status -C
266 $ hg status -C
267 M a/a
267 M a/a
268 A b.rename/b
268 A b.rename/b
269 b/b
269 b/b
270 A c.copy
270 A c.copy
271 c
271 c
272 R b/b
272 R b/b
273
273
274 ensure old shelve backups are being deleted automatically
274 ensure old shelve backups are being deleted automatically
275
275
276 $ ls .hg/shelve-backup/
276 $ ls .hg/shelve-backup/
277 default-01.hg
277 default-01.hg
278 default-01.patch
278 default-01.patch
279 wibble.hg
279 wibble.hg
280 wibble.patch
280 wibble.patch
281
281
282 cause unshelving to result in a merge with 'a' conflicting
282 cause unshelving to result in a merge with 'a' conflicting
283
283
284 $ hg shelve -q
284 $ hg shelve -q
285 $ echo c>>a/a
285 $ echo c>>a/a
286 $ hg commit -m second
286 $ hg commit -m second
287 $ hg tip --template '{files}\n'
287 $ hg tip --template '{files}\n'
288 a/a
288 a/a
289
289
290 add an unrelated change that should be preserved
290 add an unrelated change that should be preserved
291
291
292 $ mkdir foo
292 $ mkdir foo
293 $ echo foo > foo/foo
293 $ echo foo > foo/foo
294 $ hg add foo/foo
294 $ hg add foo/foo
295
295
296 force a conflicted merge to occur
296 force a conflicted merge to occur
297
297
298 $ hg unshelve
298 $ hg unshelve
299 unshelving change 'default'
299 unshelving change 'default'
300 temporarily committing pending changes (restore with 'hg unshelve --abort')
300 temporarily committing pending changes (restore with 'hg unshelve --abort')
301 rebasing shelved changes
301 rebasing shelved changes
302 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
302 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
303 merging a/a
303 merging a/a
304 warning: conflicts during merge.
304 warning: conflicts during merge.
305 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
305 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
306 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
306 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
307 [1]
307 [1]
308
308
309 ensure that we have a merge with unresolved conflicts
309 ensure that we have a merge with unresolved conflicts
310
310
311 $ hg heads -q --template '{rev}\n'
311 $ hg heads -q --template '{rev}\n'
312 5
312 5
313 4
313 4
314 $ hg parents -q --template '{rev}\n'
314 $ hg parents -q --template '{rev}\n'
315 4
315 4
316 5
316 5
317 $ hg status
317 $ hg status
318 M a/a
318 M a/a
319 M b.rename/b
319 M b.rename/b
320 M c.copy
320 M c.copy
321 R b/b
321 R b/b
322 ? a/a.orig
322 ? a/a.orig
323 $ hg diff
323 $ hg diff
324 diff --git a/a/a b/a/a
324 diff --git a/a/a b/a/a
325 --- a/a/a
325 --- a/a/a
326 +++ b/a/a
326 +++ b/a/a
327 @@ -1,2 +1,6 @@
327 @@ -1,2 +1,6 @@
328 a
328 a
329 +<<<<<<< dest: * - shelve: pending changes temporary commit (glob)
329 +<<<<<<< dest: * - shelve: pending changes temporary commit (glob)
330 c
330 c
331 +=======
331 +=======
332 +a
332 +a
333 +>>>>>>> source: 4702e8911fe0 - shelve: changes to '[mq]: second.patch'
333 +>>>>>>> source: 4702e8911fe0 - shelve: changes to '[mq]: second.patch'
334 diff --git a/b/b b/b.rename/b
334 diff --git a/b/b b/b.rename/b
335 rename from b/b
335 rename from b/b
336 rename to b.rename/b
336 rename to b.rename/b
337 diff --git a/c b/c.copy
337 diff --git a/c b/c.copy
338 copy from c
338 copy from c
339 copy to c.copy
339 copy to c.copy
340 $ hg resolve -l
340 $ hg resolve -l
341 U a/a
341 U a/a
342
342
343 $ hg shelve
343 $ hg shelve
344 abort: unshelve already in progress
344 abort: unshelve already in progress
345 (use 'hg unshelve --continue' or 'hg unshelve --abort')
345 (use 'hg unshelve --continue' or 'hg unshelve --abort')
346 [255]
346 [255]
347
347
348 abort the unshelve and be happy
348 abort the unshelve and be happy
349
349
350 $ hg status
350 $ hg status
351 M a/a
351 M a/a
352 M b.rename/b
352 M b.rename/b
353 M c.copy
353 M c.copy
354 R b/b
354 R b/b
355 ? a/a.orig
355 ? a/a.orig
356 $ hg unshelve -a
356 $ hg unshelve -a
357 rebase aborted
357 rebase aborted
358 unshelve of 'default' aborted
358 unshelve of 'default' aborted
359 $ hg heads -q
359 $ hg heads -q
360 3:2e69b451d1ea
360 3:2e69b451d1ea
361 $ hg parents
361 $ hg parents
362 changeset: 3:2e69b451d1ea
362 changeset: 3:2e69b451d1ea
363 tag: tip
363 tag: tip
364 user: test
364 user: test
365 date: Thu Jan 01 00:00:00 1970 +0000
365 date: Thu Jan 01 00:00:00 1970 +0000
366 summary: second
366 summary: second
367
367
368 $ hg resolve -l
368 $ hg resolve -l
369 $ hg status
369 $ hg status
370 A foo/foo
370 A foo/foo
371 ? a/a.orig
371 ? a/a.orig
372
372
373 try to continue with no unshelve underway
373 try to continue with no unshelve underway
374
374
375 $ hg unshelve -c
375 $ hg unshelve -c
376 abort: no unshelve operation underway
376 abort: no unshelve operation underway
377 [255]
377 [255]
378 $ hg status
378 $ hg status
379 A foo/foo
379 A foo/foo
380 ? a/a.orig
380 ? a/a.orig
381
381
382 redo the unshelve to get a conflict
382 redo the unshelve to get a conflict
383
383
384 $ hg unshelve -q
384 $ hg unshelve -q
385 warning: conflicts during merge.
385 warning: conflicts during merge.
386 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
386 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
387 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
387 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
388 [1]
388 [1]
389
389
390 attempt to continue
390 attempt to continue
391
391
392 $ hg unshelve -c
392 $ hg unshelve -c
393 abort: unresolved conflicts, can't continue
393 abort: unresolved conflicts, can't continue
394 (see 'hg resolve', then 'hg unshelve --continue')
394 (see 'hg resolve', then 'hg unshelve --continue')
395 [255]
395 [255]
396
396
397 $ hg revert -r . a/a
397 $ hg revert -r . a/a
398 $ hg resolve -m a/a
398 $ hg resolve -m a/a
399 (no more unresolved files)
399 (no more unresolved files)
400
400
401 $ hg commit -m 'commit while unshelve in progress'
401 $ hg commit -m 'commit while unshelve in progress'
402 abort: unshelve already in progress
402 abort: unshelve already in progress
403 (use 'hg unshelve --continue' or 'hg unshelve --abort')
403 (use 'hg unshelve --continue' or 'hg unshelve --abort')
404 [255]
404 [255]
405
405
406 $ hg unshelve -c
406 $ hg unshelve -c
407 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
407 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
408 unshelve of 'default' complete
408 unshelve of 'default' complete
409
409
410 ensure the repo is as we hope
410 ensure the repo is as we hope
411
411
412 $ hg parents
412 $ hg parents
413 changeset: 3:2e69b451d1ea
413 changeset: 3:2e69b451d1ea
414 tag: tip
414 tag: tip
415 user: test
415 user: test
416 date: Thu Jan 01 00:00:00 1970 +0000
416 date: Thu Jan 01 00:00:00 1970 +0000
417 summary: second
417 summary: second
418
418
419 $ hg heads -q
419 $ hg heads -q
420 3:2e69b451d1ea
420 3:2e69b451d1ea
421
421
422 $ hg status -C
422 $ hg status -C
423 A b.rename/b
423 A b.rename/b
424 b/b
424 b/b
425 A c.copy
425 A c.copy
426 c
426 c
427 A foo/foo
427 A foo/foo
428 R b/b
428 R b/b
429 ? a/a.orig
429 ? a/a.orig
430
430
431 there should be no shelves left
431 there should be no shelves left
432
432
433 $ hg shelve -l
433 $ hg shelve -l
434
434
435 #if execbit
435 #if execbit
436
436
437 ensure that metadata-only changes are shelved
437 ensure that metadata-only changes are shelved
438
438
439 $ chmod +x a/a
439 $ chmod +x a/a
440 $ hg shelve -q -n execbit a/a
440 $ hg shelve -q -n execbit a/a
441 $ hg status a/a
441 $ hg status a/a
442 $ hg unshelve -q execbit
442 $ hg unshelve -q execbit
443 $ hg status a/a
443 $ hg status a/a
444 M a/a
444 M a/a
445 $ hg revert a/a
445 $ hg revert a/a
446
446
447 #endif
447 #endif
448
448
449 #if symlink
449 #if symlink
450
450
451 $ rm a/a
451 $ rm a/a
452 $ ln -s foo a/a
452 $ ln -s foo a/a
453 $ hg shelve -q -n symlink a/a
453 $ hg shelve -q -n symlink a/a
454 $ hg status a/a
454 $ hg status a/a
455 $ hg unshelve -q symlink
455 $ hg unshelve -q symlink
456 $ hg status a/a
456 $ hg status a/a
457 M a/a
457 M a/a
458 $ hg revert a/a
458 $ hg revert a/a
459
459
460 #endif
460 #endif
461
461
462 set up another conflict between a commit and a shelved change
462 set up another conflict between a commit and a shelved change
463
463
464 $ hg revert -q -C -a
464 $ hg revert -q -C -a
465 $ rm a/a.orig b.rename/b c.copy
465 $ rm a/a.orig b.rename/b c.copy
466 $ echo a >> a/a
466 $ echo a >> a/a
467 $ hg shelve -q
467 $ hg shelve -q
468 $ echo x >> a/a
468 $ echo x >> a/a
469 $ hg ci -m 'create conflict'
469 $ hg ci -m 'create conflict'
470 $ hg add foo/foo
470 $ hg add foo/foo
471
471
472 if we resolve a conflict while unshelving, the unshelve should succeed
472 if we resolve a conflict while unshelving, the unshelve should succeed
473
473
474 $ HGMERGE=true hg unshelve
474 $ HGMERGE=true hg unshelve
475 unshelving change 'default'
475 unshelving change 'default'
476 temporarily committing pending changes (restore with 'hg unshelve --abort')
476 temporarily committing pending changes (restore with 'hg unshelve --abort')
477 rebasing shelved changes
477 rebasing shelved changes
478 rebasing 6:c5e6910e7601 "changes to 'second'" (tip)
478 rebasing 6:c5e6910e7601 "changes to 'second'" (tip)
479 merging a/a
479 merging a/a
480 note: rebase of 6:c5e6910e7601 created no changes to commit
480 note: rebase of 6:c5e6910e7601 created no changes to commit
481 $ hg parents -q
481 $ hg parents -q
482 4:33f7f61e6c5e
482 4:33f7f61e6c5e
483 $ hg shelve -l
483 $ hg shelve -l
484 $ hg status
484 $ hg status
485 A foo/foo
485 A foo/foo
486 $ cat a/a
486 $ cat a/a
487 a
487 a
488 c
488 c
489 x
489 x
490
490
491 test keep and cleanup
491 test keep and cleanup
492
492
493 $ hg shelve
493 $ hg shelve
494 shelved as default
494 shelved as default
495 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
495 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
496 $ hg shelve --list
496 $ hg shelve --list
497 default (*) changes to 'create conflict' (glob)
497 default (*) changes to 'create conflict' (glob)
498 $ hg unshelve --keep
498 $ hg unshelve --keep
499 unshelving change 'default'
499 unshelving change 'default'
500 $ hg shelve --list
500 $ hg shelve --list
501 default (*) changes to 'create conflict' (glob)
501 default (*) changes to 'create conflict' (glob)
502 $ hg shelve --cleanup
502 $ hg shelve --cleanup
503 $ hg shelve --list
503 $ hg shelve --list
504
504
505 $ hg shelve --cleanup --delete
505 $ hg shelve --cleanup --delete
506 abort: options '--cleanup' and '--delete' may not be used together
506 abort: options '--cleanup' and '--delete' may not be used together
507 [255]
507 [255]
508 $ hg shelve --cleanup --patch
508 $ hg shelve --cleanup --patch
509 abort: options '--cleanup' and '--patch' may not be used together
509 abort: options '--cleanup' and '--patch' may not be used together
510 [255]
510 [255]
511 $ hg shelve --cleanup --message MESSAGE
511 $ hg shelve --cleanup --message MESSAGE
512 abort: options '--cleanup' and '--message' may not be used together
512 abort: options '--cleanup' and '--message' may not be used together
513 [255]
513 [255]
514
514
515 test bookmarks
515 test bookmarks
516
516
517 $ hg bookmark test
517 $ hg bookmark test
518 $ hg bookmark
518 $ hg bookmark
519 * test 4:33f7f61e6c5e
519 * test 4:33f7f61e6c5e
520 $ hg shelve
520 $ hg shelve
521 shelved as test
521 shelved as test
522 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
522 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
523 $ hg bookmark
523 $ hg bookmark
524 * test 4:33f7f61e6c5e
524 * test 4:33f7f61e6c5e
525 $ hg unshelve
525 $ hg unshelve
526 unshelving change 'test'
526 unshelving change 'test'
527 $ hg bookmark
527 $ hg bookmark
528 * test 4:33f7f61e6c5e
528 * test 4:33f7f61e6c5e
529
529
530 shelve should still work even if mq is disabled
530 shelve should still work even if mq is disabled
531
531
532 $ hg --config extensions.mq=! shelve
532 $ hg --config extensions.mq=! shelve
533 shelved as test
533 shelved as test
534 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
534 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
535 $ hg --config extensions.mq=! shelve --list
535 $ hg --config extensions.mq=! shelve --list
536 test (*) changes to 'create conflict' (glob)
536 test (*) changes to 'create conflict' (glob)
537 $ hg bookmark
538 * test 4:33f7f61e6c5e
537 $ hg --config extensions.mq=! unshelve
539 $ hg --config extensions.mq=! unshelve
538 unshelving change 'test'
540 unshelving change 'test'
541 $ hg bookmark
542 * test 4:33f7f61e6c5e
539
543
540 shelve should leave dirstate clean (issue4055)
544 shelve should leave dirstate clean (issue4055)
541
545
542 $ cd ..
546 $ cd ..
543 $ hg init shelverebase
547 $ hg init shelverebase
544 $ cd shelverebase
548 $ cd shelverebase
545 $ printf 'x\ny\n' > x
549 $ printf 'x\ny\n' > x
546 $ echo z > z
550 $ echo z > z
547 $ hg commit -Aqm xy
551 $ hg commit -Aqm xy
548 $ echo z >> x
552 $ echo z >> x
549 $ hg commit -Aqm z
553 $ hg commit -Aqm z
550 $ hg up 0
554 $ hg up 0
551 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
555 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
552 $ printf 'a\nx\ny\nz\n' > x
556 $ printf 'a\nx\ny\nz\n' > x
553 $ hg commit -Aqm xyz
557 $ hg commit -Aqm xyz
554 $ echo c >> z
558 $ echo c >> z
555 $ hg shelve
559 $ hg shelve
556 shelved as default
560 shelved as default
557 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 $ hg rebase -d 1 --config extensions.rebase=
562 $ hg rebase -d 1 --config extensions.rebase=
559 rebasing 2:323bfa07f744 "xyz" (tip)
563 rebasing 2:323bfa07f744 "xyz" (tip)
560 merging x
564 merging x
561 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-backup.hg (glob)
565 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-backup.hg (glob)
562 $ hg unshelve
566 $ hg unshelve
563 unshelving change 'default'
567 unshelving change 'default'
564 rebasing shelved changes
568 rebasing shelved changes
565 rebasing 4:b8fefe789ed0 "changes to 'xyz'" (tip)
569 rebasing 4:b8fefe789ed0 "changes to 'xyz'" (tip)
566 $ hg status
570 $ hg status
567 M z
571 M z
568
572
569 $ cd ..
573 $ cd ..
570
574
571 shelve should only unshelve pending changes (issue4068)
575 shelve should only unshelve pending changes (issue4068)
572
576
573 $ hg init onlypendingchanges
577 $ hg init onlypendingchanges
574 $ cd onlypendingchanges
578 $ cd onlypendingchanges
575 $ touch a
579 $ touch a
576 $ hg ci -Aqm a
580 $ hg ci -Aqm a
577 $ touch b
581 $ touch b
578 $ hg ci -Aqm b
582 $ hg ci -Aqm b
579 $ hg up -q 0
583 $ hg up -q 0
580 $ touch c
584 $ touch c
581 $ hg ci -Aqm c
585 $ hg ci -Aqm c
582
586
583 $ touch d
587 $ touch d
584 $ hg add d
588 $ hg add d
585 $ hg shelve
589 $ hg shelve
586 shelved as default
590 shelved as default
587 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
591 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
588 $ hg up -q 1
592 $ hg up -q 1
589 $ hg unshelve
593 $ hg unshelve
590 unshelving change 'default'
594 unshelving change 'default'
591 rebasing shelved changes
595 rebasing shelved changes
592 rebasing 3:0cae6656c016 "changes to 'c'" (tip)
596 rebasing 3:0cae6656c016 "changes to 'c'" (tip)
593 $ hg status
597 $ hg status
594 A d
598 A d
595
599
596 unshelve should work on an ancestor of the original commit
600 unshelve should work on an ancestor of the original commit
597
601
598 $ hg shelve
602 $ hg shelve
599 shelved as default
603 shelved as default
600 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
604 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
601 $ hg up 0
605 $ hg up 0
602 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
606 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
603 $ hg unshelve
607 $ hg unshelve
604 unshelving change 'default'
608 unshelving change 'default'
605 rebasing shelved changes
609 rebasing shelved changes
606 rebasing 3:be58f65f55fb "changes to 'b'" (tip)
610 rebasing 3:be58f65f55fb "changes to 'b'" (tip)
607 $ hg status
611 $ hg status
608 A d
612 A d
609
613
610 test bug 4073 we need to enable obsolete markers for it
614 test bug 4073 we need to enable obsolete markers for it
611
615
612 $ cat >> $HGRCPATH << EOF
616 $ cat >> $HGRCPATH << EOF
613 > [experimental]
617 > [experimental]
614 > evolution=createmarkers
618 > evolution=createmarkers
615 > EOF
619 > EOF
616 $ hg shelve
620 $ hg shelve
617 shelved as default
621 shelved as default
618 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
622 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
619 $ hg debugobsolete `hg --debug id -i -r 1`
623 $ hg debugobsolete `hg --debug id -i -r 1`
620 $ hg unshelve
624 $ hg unshelve
621 unshelving change 'default'
625 unshelving change 'default'
622
626
623 unshelve should leave unknown files alone (issue4113)
627 unshelve should leave unknown files alone (issue4113)
624
628
625 $ echo e > e
629 $ echo e > e
626 $ hg shelve
630 $ hg shelve
627 shelved as default
631 shelved as default
628 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
632 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
629 $ hg status
633 $ hg status
630 ? e
634 ? e
631 $ hg unshelve
635 $ hg unshelve
632 unshelving change 'default'
636 unshelving change 'default'
633 $ hg status
637 $ hg status
634 A d
638 A d
635 ? e
639 ? e
636 $ cat e
640 $ cat e
637 e
641 e
638
642
639 unshelve should keep a copy of unknown files
643 unshelve should keep a copy of unknown files
640
644
641 $ hg add e
645 $ hg add e
642 $ hg shelve
646 $ hg shelve
643 shelved as default
647 shelved as default
644 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
648 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
645 $ echo z > e
649 $ echo z > e
646 $ hg unshelve
650 $ hg unshelve
647 unshelving change 'default'
651 unshelving change 'default'
648 $ cat e
652 $ cat e
649 e
653 e
650 $ cat e.orig
654 $ cat e.orig
651 z
655 z
652
656
653
657
654 unshelve and conflicts with tracked and untracked files
658 unshelve and conflicts with tracked and untracked files
655
659
656 preparing:
660 preparing:
657
661
658 $ rm *.orig
662 $ rm *.orig
659 $ hg ci -qm 'commit stuff'
663 $ hg ci -qm 'commit stuff'
660 $ hg phase -p null:
664 $ hg phase -p null:
661
665
662 no other changes - no merge:
666 no other changes - no merge:
663
667
664 $ echo f > f
668 $ echo f > f
665 $ hg add f
669 $ hg add f
666 $ hg shelve
670 $ hg shelve
667 shelved as default
671 shelved as default
668 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
672 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
669 $ echo g > f
673 $ echo g > f
670 $ hg unshelve
674 $ hg unshelve
671 unshelving change 'default'
675 unshelving change 'default'
672 $ hg st
676 $ hg st
673 A f
677 A f
674 ? f.orig
678 ? f.orig
675 $ cat f
679 $ cat f
676 f
680 f
677 $ cat f.orig
681 $ cat f.orig
678 g
682 g
679
683
680 other uncommitted changes - merge:
684 other uncommitted changes - merge:
681
685
682 $ hg st
686 $ hg st
683 A f
687 A f
684 ? f.orig
688 ? f.orig
685 $ hg shelve
689 $ hg shelve
686 shelved as default
690 shelved as default
687 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
691 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
688 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
692 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
689 o 4 changes to 'commit stuff' shelve@localhost
693 o 4 changes to 'commit stuff' shelve@localhost
690 |
694 |
691 $ hg log -G --template '{rev} {desc|firstline} {author}'
695 $ hg log -G --template '{rev} {desc|firstline} {author}'
692 @ 3 commit stuff test
696 @ 3 commit stuff test
693 |
697 |
694 | o 2 c test
698 | o 2 c test
695 |/
699 |/
696 o 0 a test
700 o 0 a test
697
701
698 $ mv f.orig f
702 $ mv f.orig f
699 $ echo 1 > a
703 $ echo 1 > a
700 $ hg unshelve --date '1073741824 0'
704 $ hg unshelve --date '1073741824 0'
701 unshelving change 'default'
705 unshelving change 'default'
702 temporarily committing pending changes (restore with 'hg unshelve --abort')
706 temporarily committing pending changes (restore with 'hg unshelve --abort')
703 rebasing shelved changes
707 rebasing shelved changes
704 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
708 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
705 merging f
709 merging f
706 warning: conflicts during merge.
710 warning: conflicts during merge.
707 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
711 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
708 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
712 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
709 [1]
713 [1]
710 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
714 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
711 @ 5 changes to 'commit stuff' shelve@localhost 1970-01-01 00:00 +0000
715 @ 5 changes to 'commit stuff' shelve@localhost 1970-01-01 00:00 +0000
712 |
716 |
713 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
717 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
714 |/
718 |/
715 o 3 commit stuff test 1970-01-01 00:00 +0000
719 o 3 commit stuff test 1970-01-01 00:00 +0000
716 |
720 |
717 | o 2 c test 1970-01-01 00:00 +0000
721 | o 2 c test 1970-01-01 00:00 +0000
718 |/
722 |/
719 o 0 a test 1970-01-01 00:00 +0000
723 o 0 a test 1970-01-01 00:00 +0000
720
724
721 $ hg st
725 $ hg st
722 M f
726 M f
723 ? f.orig
727 ? f.orig
724 $ cat f
728 $ cat f
725 <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit
729 <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit
726 g
730 g
727 =======
731 =======
728 f
732 f
729 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
733 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
730 $ cat f.orig
734 $ cat f.orig
731 g
735 g
732 $ hg unshelve --abort
736 $ hg unshelve --abort
733 rebase aborted
737 rebase aborted
734 unshelve of 'default' aborted
738 unshelve of 'default' aborted
735 $ hg st
739 $ hg st
736 M a
740 M a
737 ? f.orig
741 ? f.orig
738 $ cat f.orig
742 $ cat f.orig
739 g
743 g
740 $ hg unshelve
744 $ hg unshelve
741 unshelving change 'default'
745 unshelving change 'default'
742 temporarily committing pending changes (restore with 'hg unshelve --abort')
746 temporarily committing pending changes (restore with 'hg unshelve --abort')
743 rebasing shelved changes
747 rebasing shelved changes
744 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
748 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
745 $ hg st
749 $ hg st
746 M a
750 M a
747 A f
751 A f
748 ? f.orig
752 ? f.orig
749
753
750 other committed changes - merge:
754 other committed changes - merge:
751
755
752 $ hg shelve f
756 $ hg shelve f
753 shelved as default
757 shelved as default
754 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
758 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
755 $ hg ci a -m 'intermediate other change'
759 $ hg ci a -m 'intermediate other change'
756 $ mv f.orig f
760 $ mv f.orig f
757 $ hg unshelve
761 $ hg unshelve
758 unshelving change 'default'
762 unshelving change 'default'
759 rebasing shelved changes
763 rebasing shelved changes
760 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
764 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
761 merging f
765 merging f
762 warning: conflicts during merge.
766 warning: conflicts during merge.
763 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
767 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
764 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
768 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
765 [1]
769 [1]
766 $ hg st
770 $ hg st
767 M f
771 M f
768 ? f.orig
772 ? f.orig
769 $ cat f
773 $ cat f
770 <<<<<<< dest: * - test: intermediate other change (glob)
774 <<<<<<< dest: * - test: intermediate other change (glob)
771 g
775 g
772 =======
776 =======
773 f
777 f
774 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
778 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
775 $ cat f.orig
779 $ cat f.orig
776 g
780 g
777 $ hg unshelve --abort
781 $ hg unshelve --abort
778 rebase aborted
782 rebase aborted
779 unshelve of 'default' aborted
783 unshelve of 'default' aborted
780 $ hg st
784 $ hg st
781 ? f.orig
785 ? f.orig
782 $ cat f.orig
786 $ cat f.orig
783 g
787 g
784 $ hg shelve --delete default
788 $ hg shelve --delete default
785
789
786 Recreate some conflict again
790 Recreate some conflict again
787
791
788 $ cd ../repo
792 $ cd ../repo
789 $ hg up -C -r 3
793 $ hg up -C -r 3
790 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
794 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
791 (leaving bookmark test)
795 (leaving bookmark test)
792 $ echo y >> a/a
796 $ echo y >> a/a
793 $ hg shelve
797 $ hg shelve
794 shelved as default
798 shelved as default
795 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
799 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
796 $ hg up test
800 $ hg up test
797 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
801 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
798 (activating bookmark test)
802 (activating bookmark test)
803 $ hg bookmark
804 * test 4:33f7f61e6c5e
799 $ hg unshelve
805 $ hg unshelve
800 unshelving change 'default'
806 unshelving change 'default'
801 rebasing shelved changes
807 rebasing shelved changes
802 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
808 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
803 merging a/a
809 merging a/a
804 warning: conflicts during merge.
810 warning: conflicts during merge.
805 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
811 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
806 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
812 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
807 [1]
813 [1]
814 $ hg bookmark
815 test 4:33f7f61e6c5e
808
816
809 Test that resolving all conflicts in one direction (so that the rebase
817 Test that resolving all conflicts in one direction (so that the rebase
810 is a no-op), works (issue4398)
818 is a no-op), works (issue4398)
811
819
812 $ hg revert -a -r .
820 $ hg revert -a -r .
813 reverting a/a (glob)
821 reverting a/a (glob)
814 $ hg resolve -m a/a
822 $ hg resolve -m a/a
815 (no more unresolved files)
823 (no more unresolved files)
816 $ hg unshelve -c
824 $ hg unshelve -c
817 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
825 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
818 note: rebase of 5:4b555fdb4e96 created no changes to commit
826 note: rebase of 5:4b555fdb4e96 created no changes to commit
819 unshelve of 'default' complete
827 unshelve of 'default' complete
828 $ hg bookmark
829 * test 4:33f7f61e6c5e
820 $ hg diff
830 $ hg diff
821 $ hg status
831 $ hg status
822 ? a/a.orig
832 ? a/a.orig
823 ? foo/foo
833 ? foo/foo
824 $ hg summary
834 $ hg summary
825 parent: 4:33f7f61e6c5e tip
835 parent: 4:33f7f61e6c5e tip
826 create conflict
836 create conflict
827 branch: default
837 branch: default
828 bookmarks: *test
838 bookmarks: *test
829 commit: 2 unknown (clean)
839 commit: 2 unknown (clean)
830 update: (current)
840 update: (current)
831 phases: 5 draft
841 phases: 5 draft
832
842
833 $ hg shelve --delete --stat
843 $ hg shelve --delete --stat
834 abort: options '--delete' and '--stat' may not be used together
844 abort: options '--delete' and '--stat' may not be used together
835 [255]
845 [255]
836 $ hg shelve --delete --name NAME
846 $ hg shelve --delete --name NAME
837 abort: options '--delete' and '--name' may not be used together
847 abort: options '--delete' and '--name' may not be used together
838 [255]
848 [255]
839
849
840 Test interactive shelve
850 Test interactive shelve
841 $ cat <<EOF >> $HGRCPATH
851 $ cat <<EOF >> $HGRCPATH
842 > [ui]
852 > [ui]
843 > interactive = true
853 > interactive = true
844 > EOF
854 > EOF
845 $ echo 'a' >> a/b
855 $ echo 'a' >> a/b
846 $ cat a/a >> a/b
856 $ cat a/a >> a/b
847 $ echo 'x' >> a/b
857 $ echo 'x' >> a/b
848 $ mv a/b a/a
858 $ mv a/b a/a
849 $ echo 'a' >> foo/foo
859 $ echo 'a' >> foo/foo
850 $ hg st
860 $ hg st
851 M a/a
861 M a/a
852 ? a/a.orig
862 ? a/a.orig
853 ? foo/foo
863 ? foo/foo
854 $ cat a/a
864 $ cat a/a
855 a
865 a
856 a
866 a
857 c
867 c
858 x
868 x
859 x
869 x
860 $ cat foo/foo
870 $ cat foo/foo
861 foo
871 foo
862 a
872 a
863 $ hg shelve --interactive --config ui.interactive=false
873 $ hg shelve --interactive --config ui.interactive=false
864 abort: running non-interactively
874 abort: running non-interactively
865 [255]
875 [255]
866 $ hg shelve --interactive << EOF
876 $ hg shelve --interactive << EOF
867 > y
877 > y
868 > y
878 > y
869 > n
879 > n
870 > EOF
880 > EOF
871 diff --git a/a/a b/a/a
881 diff --git a/a/a b/a/a
872 2 hunks, 2 lines changed
882 2 hunks, 2 lines changed
873 examine changes to 'a/a'? [Ynesfdaq?] y
883 examine changes to 'a/a'? [Ynesfdaq?] y
874
884
875 @@ -1,3 +1,4 @@
885 @@ -1,3 +1,4 @@
876 +a
886 +a
877 a
887 a
878 c
888 c
879 x
889 x
880 record change 1/2 to 'a/a'? [Ynesfdaq?] y
890 record change 1/2 to 'a/a'? [Ynesfdaq?] y
881
891
882 @@ -1,3 +2,4 @@
892 @@ -1,3 +2,4 @@
883 a
893 a
884 c
894 c
885 x
895 x
886 +x
896 +x
887 record change 2/2 to 'a/a'? [Ynesfdaq?] n
897 record change 2/2 to 'a/a'? [Ynesfdaq?] n
888
898
889 shelved as test
899 shelved as test
890 merging a/a
900 merging a/a
891 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
901 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
892 $ cat a/a
902 $ cat a/a
893 a
903 a
894 c
904 c
895 x
905 x
896 x
906 x
897 $ cat foo/foo
907 $ cat foo/foo
898 foo
908 foo
899 a
909 a
900 $ hg st
910 $ hg st
901 M a/a
911 M a/a
902 ? foo/foo
912 ? foo/foo
913 $ hg bookmark
914 * test 4:33f7f61e6c5e
903 $ hg unshelve
915 $ hg unshelve
904 unshelving change 'test'
916 unshelving change 'test'
905 temporarily committing pending changes (restore with 'hg unshelve --abort')
917 temporarily committing pending changes (restore with 'hg unshelve --abort')
906 rebasing shelved changes
918 rebasing shelved changes
907 rebasing 6:65b5d1c34c34 "changes to 'create conflict'" (tip)
919 rebasing 6:65b5d1c34c34 "changes to 'create conflict'" (tip)
908 merging a/a
920 merging a/a
921 $ hg bookmark
922 * test 4:33f7f61e6c5e
909 $ cat a/a
923 $ cat a/a
910 a
924 a
911 a
925 a
912 c
926 c
913 x
927 x
914 x
928 x
915
929
916 shelve --patch and shelve --stat should work with a single valid shelfname
930 shelve --patch and shelve --stat should work with a single valid shelfname
917
931
918 $ hg up --clean .
932 $ hg up --clean .
919 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
933 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
934 (leaving bookmark test)
920 $ hg shelve --list
935 $ hg shelve --list
921 $ echo 'patch a' > shelf-patch-a
936 $ echo 'patch a' > shelf-patch-a
922 $ hg add shelf-patch-a
937 $ hg add shelf-patch-a
923 $ hg shelve
938 $ hg shelve
924 shelved as default
939 shelved as default
925 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
940 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
926 $ echo 'patch b' > shelf-patch-b
941 $ echo 'patch b' > shelf-patch-b
927 $ hg add shelf-patch-b
942 $ hg add shelf-patch-b
928 $ hg shelve
943 $ hg shelve
929 shelved as default-01
944 shelved as default-01
930 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
945 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
931 $ hg shelve --patch default default-01
946 $ hg shelve --patch default default-01
932 abort: --patch expects a single shelf
947 abort: --patch expects a single shelf
933 [255]
948 [255]
934 $ hg shelve --stat default default-01
949 $ hg shelve --stat default default-01
935 abort: --stat expects a single shelf
950 abort: --stat expects a single shelf
936 [255]
951 [255]
937 $ hg shelve --patch default
952 $ hg shelve --patch default
938 default (* ago) changes to 'create conflict' (glob)
953 default (* ago) changes to 'create conflict' (glob)
939
954
940 diff --git a/shelf-patch-a b/shelf-patch-a
955 diff --git a/shelf-patch-a b/shelf-patch-a
941 new file mode 100644
956 new file mode 100644
942 --- /dev/null
957 --- /dev/null
943 +++ b/shelf-patch-a
958 +++ b/shelf-patch-a
944 @@ -0,0 +1,1 @@
959 @@ -0,0 +1,1 @@
945 +patch a
960 +patch a
946 $ hg shelve --stat default
961 $ hg shelve --stat default
947 default (* ago) changes to 'create conflict' (glob)
962 default (* ago) changes to 'create conflict' (glob)
948 shelf-patch-a | 1 +
963 shelf-patch-a | 1 +
949 1 files changed, 1 insertions(+), 0 deletions(-)
964 1 files changed, 1 insertions(+), 0 deletions(-)
950 $ hg shelve --patch nonexistentshelf
965 $ hg shelve --patch nonexistentshelf
951 abort: cannot find shelf nonexistentshelf
966 abort: cannot find shelf nonexistentshelf
952 [255]
967 [255]
953 $ hg shelve --stat nonexistentshelf
968 $ hg shelve --stat nonexistentshelf
954 abort: cannot find shelf nonexistentshelf
969 abort: cannot find shelf nonexistentshelf
955 [255]
970 [255]
956
971
957 $ cd ..
972 $ cd ..
958
973
959 Shelve from general delta repo uses bundle2 on disk
974 Shelve from general delta repo uses bundle2 on disk
960 --------------------------------------------------
975 --------------------------------------------------
961
976
962 no general delta
977 no general delta
963
978
964 $ hg clone --pull repo bundle1 --config format.generaldelta=0
979 $ hg clone --pull repo bundle1 --config format.generaldelta=0
965 requesting all changes
980 requesting all changes
966 adding changesets
981 adding changesets
967 adding manifests
982 adding manifests
968 adding file changes
983 adding file changes
969 added 5 changesets with 8 changes to 6 files
984 added 5 changesets with 8 changes to 6 files
970 updating to branch default
985 updating to branch default
971 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
986 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
972 $ cd bundle1
987 $ cd bundle1
973 $ echo babar > jungle
988 $ echo babar > jungle
974 $ hg add jungle
989 $ hg add jungle
975 $ hg shelve
990 $ hg shelve
976 shelved as default
991 shelved as default
977 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
992 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
978 $ hg debugbundle .hg/shelved/*.hg
993 $ hg debugbundle .hg/shelved/*.hg
979 7e30d8ac6f23cfc84330fd7e698730374615d21a
994 7e30d8ac6f23cfc84330fd7e698730374615d21a
980 $ cd ..
995 $ cd ..
981
996
982 with general delta
997 with general delta
983
998
984 $ hg clone --pull repo bundle2 --config format.generaldelta=1
999 $ hg clone --pull repo bundle2 --config format.generaldelta=1
985 requesting all changes
1000 requesting all changes
986 adding changesets
1001 adding changesets
987 adding manifests
1002 adding manifests
988 adding file changes
1003 adding file changes
989 added 5 changesets with 8 changes to 6 files
1004 added 5 changesets with 8 changes to 6 files
990 updating to branch default
1005 updating to branch default
991 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1006 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
992 $ cd bundle2
1007 $ cd bundle2
993 $ echo babar > jungle
1008 $ echo babar > jungle
994 $ hg add jungle
1009 $ hg add jungle
995 $ hg shelve
1010 $ hg shelve
996 shelved as default
1011 shelved as default
997 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1012 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
998 $ hg debugbundle .hg/shelved/*.hg
1013 $ hg debugbundle .hg/shelved/*.hg
999 Stream params: {'Compression': 'BZ'}
1014 Stream params: {'Compression': 'BZ'}
1000 changegroup -- "{'version': '02'}"
1015 changegroup -- "{'version': '02'}"
1001 7e30d8ac6f23cfc84330fd7e698730374615d21a
1016 7e30d8ac6f23cfc84330fd7e698730374615d21a
1002 $ cd ..
1017 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now