##// END OF EJS Templates
unshelve: changes how date is set on interactive mode...
Navaneeth Suresh -
r42887:aaad4fe5 default
parent child Browse files
Show More
@@ -1,1011 +1,1010 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 from __future__ import absolute_import
23 from __future__ import absolute_import
24
24
25 import collections
25 import collections
26 import errno
26 import errno
27 import itertools
27 import itertools
28 import stat
28 import stat
29
29
30 from .i18n import _
30 from .i18n import _
31 from . import (
31 from . import (
32 bookmarks,
32 bookmarks,
33 bundle2,
33 bundle2,
34 bundlerepo,
34 bundlerepo,
35 changegroup,
35 changegroup,
36 cmdutil,
36 cmdutil,
37 discovery,
37 discovery,
38 error,
38 error,
39 exchange,
39 exchange,
40 hg,
40 hg,
41 lock as lockmod,
41 lock as lockmod,
42 mdiff,
42 mdiff,
43 merge,
43 merge,
44 node as nodemod,
44 node as nodemod,
45 patch,
45 patch,
46 phases,
46 phases,
47 pycompat,
47 pycompat,
48 repair,
48 repair,
49 scmutil,
49 scmutil,
50 templatefilters,
50 templatefilters,
51 util,
51 util,
52 vfs as vfsmod,
52 vfs as vfsmod,
53 )
53 )
54 from .utils import (
54 from .utils import (
55 dateutil,
55 dateutil,
56 stringutil,
56 stringutil,
57 )
57 )
58
58
59 backupdir = 'shelve-backup'
59 backupdir = 'shelve-backup'
60 shelvedir = 'shelved'
60 shelvedir = 'shelved'
61 shelvefileextensions = ['hg', 'patch', 'shelve']
61 shelvefileextensions = ['hg', 'patch', 'shelve']
62 # universal extension is present in all types of shelves
62 # universal extension is present in all types of shelves
63 patchextension = 'patch'
63 patchextension = 'patch'
64
64
65 # we never need the user, so we use a
65 # we never need the user, so we use a
66 # generic user for all shelve operations
66 # generic user for all shelve operations
67 shelveuser = 'shelve@localhost'
67 shelveuser = 'shelve@localhost'
68
68
69 class shelvedfile(object):
69 class shelvedfile(object):
70 """Helper for the file storing a single shelve
70 """Helper for the file storing a single shelve
71
71
72 Handles common functions on shelve files (.hg/.patch) using
72 Handles common functions on shelve files (.hg/.patch) using
73 the vfs layer"""
73 the vfs layer"""
74 def __init__(self, repo, name, filetype=None):
74 def __init__(self, repo, name, filetype=None):
75 self.repo = repo
75 self.repo = repo
76 self.name = name
76 self.name = name
77 self.vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
77 self.vfs = vfsmod.vfs(repo.vfs.join(shelvedir))
78 self.backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
78 self.backupvfs = vfsmod.vfs(repo.vfs.join(backupdir))
79 self.ui = self.repo.ui
79 self.ui = self.repo.ui
80 if filetype:
80 if filetype:
81 self.fname = name + '.' + filetype
81 self.fname = name + '.' + filetype
82 else:
82 else:
83 self.fname = name
83 self.fname = name
84
84
85 def exists(self):
85 def exists(self):
86 return self.vfs.exists(self.fname)
86 return self.vfs.exists(self.fname)
87
87
88 def filename(self):
88 def filename(self):
89 return self.vfs.join(self.fname)
89 return self.vfs.join(self.fname)
90
90
91 def backupfilename(self):
91 def backupfilename(self):
92 def gennames(base):
92 def gennames(base):
93 yield base
93 yield base
94 base, ext = base.rsplit('.', 1)
94 base, ext = base.rsplit('.', 1)
95 for i in itertools.count(1):
95 for i in itertools.count(1):
96 yield '%s-%d.%s' % (base, i, ext)
96 yield '%s-%d.%s' % (base, i, ext)
97
97
98 name = self.backupvfs.join(self.fname)
98 name = self.backupvfs.join(self.fname)
99 for n in gennames(name):
99 for n in gennames(name):
100 if not self.backupvfs.exists(n):
100 if not self.backupvfs.exists(n):
101 return n
101 return n
102
102
103 def movetobackup(self):
103 def movetobackup(self):
104 if not self.backupvfs.isdir():
104 if not self.backupvfs.isdir():
105 self.backupvfs.makedir()
105 self.backupvfs.makedir()
106 util.rename(self.filename(), self.backupfilename())
106 util.rename(self.filename(), self.backupfilename())
107
107
108 def stat(self):
108 def stat(self):
109 return self.vfs.stat(self.fname)
109 return self.vfs.stat(self.fname)
110
110
111 def opener(self, mode='rb'):
111 def opener(self, mode='rb'):
112 try:
112 try:
113 return self.vfs(self.fname, mode)
113 return self.vfs(self.fname, mode)
114 except IOError as err:
114 except IOError as err:
115 if err.errno != errno.ENOENT:
115 if err.errno != errno.ENOENT:
116 raise
116 raise
117 raise error.Abort(_("shelved change '%s' not found") % self.name)
117 raise error.Abort(_("shelved change '%s' not found") % self.name)
118
118
119 def applybundle(self, tr):
119 def applybundle(self, tr):
120 fp = self.opener()
120 fp = self.opener()
121 try:
121 try:
122 targetphase = phases.internal
122 targetphase = phases.internal
123 if not phases.supportinternal(self.repo):
123 if not phases.supportinternal(self.repo):
124 targetphase = phases.secret
124 targetphase = phases.secret
125 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
125 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
126 pretip = self.repo['tip']
126 pretip = self.repo['tip']
127 bundle2.applybundle(self.repo, gen, tr,
127 bundle2.applybundle(self.repo, gen, tr,
128 source='unshelve',
128 source='unshelve',
129 url='bundle:' + self.vfs.join(self.fname),
129 url='bundle:' + self.vfs.join(self.fname),
130 targetphase=targetphase)
130 targetphase=targetphase)
131 shelvectx = self.repo['tip']
131 shelvectx = self.repo['tip']
132 if pretip == shelvectx:
132 if pretip == shelvectx:
133 shelverev = tr.changes['revduplicates'][-1]
133 shelverev = tr.changes['revduplicates'][-1]
134 shelvectx = self.repo[shelverev]
134 shelvectx = self.repo[shelverev]
135 return shelvectx
135 return shelvectx
136 finally:
136 finally:
137 fp.close()
137 fp.close()
138
138
139 def bundlerepo(self):
139 def bundlerepo(self):
140 path = self.vfs.join(self.fname)
140 path = self.vfs.join(self.fname)
141 return bundlerepo.instance(self.repo.baseui,
141 return bundlerepo.instance(self.repo.baseui,
142 'bundle://%s+%s' % (self.repo.root, path))
142 'bundle://%s+%s' % (self.repo.root, path))
143
143
144 def writebundle(self, bases, node):
144 def writebundle(self, bases, node):
145 cgversion = changegroup.safeversion(self.repo)
145 cgversion = changegroup.safeversion(self.repo)
146 if cgversion == '01':
146 if cgversion == '01':
147 btype = 'HG10BZ'
147 btype = 'HG10BZ'
148 compression = None
148 compression = None
149 else:
149 else:
150 btype = 'HG20'
150 btype = 'HG20'
151 compression = 'BZ'
151 compression = 'BZ'
152
152
153 repo = self.repo.unfiltered()
153 repo = self.repo.unfiltered()
154
154
155 outgoing = discovery.outgoing(repo, missingroots=bases,
155 outgoing = discovery.outgoing(repo, missingroots=bases,
156 missingheads=[node])
156 missingheads=[node])
157 cg = changegroup.makechangegroup(repo, outgoing, cgversion, 'shelve')
157 cg = changegroup.makechangegroup(repo, outgoing, cgversion, 'shelve')
158
158
159 bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs,
159 bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs,
160 compression=compression)
160 compression=compression)
161
161
162 def writeinfo(self, info):
162 def writeinfo(self, info):
163 scmutil.simplekeyvaluefile(self.vfs, self.fname).write(info)
163 scmutil.simplekeyvaluefile(self.vfs, self.fname).write(info)
164
164
165 def readinfo(self):
165 def readinfo(self):
166 return scmutil.simplekeyvaluefile(self.vfs, self.fname).read()
166 return scmutil.simplekeyvaluefile(self.vfs, self.fname).read()
167
167
168 class shelvedstate(object):
168 class shelvedstate(object):
169 """Handle persistence during unshelving operations.
169 """Handle persistence during unshelving operations.
170
170
171 Handles saving and restoring a shelved state. Ensures that different
171 Handles saving and restoring a shelved state. Ensures that different
172 versions of a shelved state are possible and handles them appropriately.
172 versions of a shelved state are possible and handles them appropriately.
173 """
173 """
174 _version = 2
174 _version = 2
175 _filename = 'shelvedstate'
175 _filename = 'shelvedstate'
176 _keep = 'keep'
176 _keep = 'keep'
177 _nokeep = 'nokeep'
177 _nokeep = 'nokeep'
178 # colon is essential to differentiate from a real bookmark name
178 # colon is essential to differentiate from a real bookmark name
179 _noactivebook = ':no-active-bookmark'
179 _noactivebook = ':no-active-bookmark'
180
180
181 @classmethod
181 @classmethod
182 def _verifyandtransform(cls, d):
182 def _verifyandtransform(cls, d):
183 """Some basic shelvestate syntactic verification and transformation"""
183 """Some basic shelvestate syntactic verification and transformation"""
184 try:
184 try:
185 d['originalwctx'] = nodemod.bin(d['originalwctx'])
185 d['originalwctx'] = nodemod.bin(d['originalwctx'])
186 d['pendingctx'] = nodemod.bin(d['pendingctx'])
186 d['pendingctx'] = nodemod.bin(d['pendingctx'])
187 d['parents'] = [nodemod.bin(h)
187 d['parents'] = [nodemod.bin(h)
188 for h in d['parents'].split(' ')]
188 for h in d['parents'].split(' ')]
189 d['nodestoremove'] = [nodemod.bin(h)
189 d['nodestoremove'] = [nodemod.bin(h)
190 for h in d['nodestoremove'].split(' ')]
190 for h in d['nodestoremove'].split(' ')]
191 except (ValueError, TypeError, KeyError) as err:
191 except (ValueError, TypeError, KeyError) as err:
192 raise error.CorruptedState(pycompat.bytestr(err))
192 raise error.CorruptedState(pycompat.bytestr(err))
193
193
194 @classmethod
194 @classmethod
195 def _getversion(cls, repo):
195 def _getversion(cls, repo):
196 """Read version information from shelvestate file"""
196 """Read version information from shelvestate file"""
197 fp = repo.vfs(cls._filename)
197 fp = repo.vfs(cls._filename)
198 try:
198 try:
199 version = int(fp.readline().strip())
199 version = int(fp.readline().strip())
200 except ValueError as err:
200 except ValueError as err:
201 raise error.CorruptedState(pycompat.bytestr(err))
201 raise error.CorruptedState(pycompat.bytestr(err))
202 finally:
202 finally:
203 fp.close()
203 fp.close()
204 return version
204 return version
205
205
206 @classmethod
206 @classmethod
207 def _readold(cls, repo):
207 def _readold(cls, repo):
208 """Read the old position-based version of a shelvestate file"""
208 """Read the old position-based version of a shelvestate file"""
209 # Order is important, because old shelvestate file uses it
209 # Order is important, because old shelvestate file uses it
210 # to detemine values of fields (i.g. name is on the second line,
210 # to detemine values of fields (i.g. name is on the second line,
211 # originalwctx is on the third and so forth). Please do not change.
211 # originalwctx is on the third and so forth). Please do not change.
212 keys = ['version', 'name', 'originalwctx', 'pendingctx', 'parents',
212 keys = ['version', 'name', 'originalwctx', 'pendingctx', 'parents',
213 'nodestoremove', 'branchtorestore', 'keep', 'activebook']
213 'nodestoremove', 'branchtorestore', 'keep', 'activebook']
214 # this is executed only seldomly, so it is not a big deal
214 # this is executed only seldomly, so it is not a big deal
215 # that we open this file twice
215 # that we open this file twice
216 fp = repo.vfs(cls._filename)
216 fp = repo.vfs(cls._filename)
217 d = {}
217 d = {}
218 try:
218 try:
219 for key in keys:
219 for key in keys:
220 d[key] = fp.readline().strip()
220 d[key] = fp.readline().strip()
221 finally:
221 finally:
222 fp.close()
222 fp.close()
223 return d
223 return d
224
224
225 @classmethod
225 @classmethod
226 def load(cls, repo):
226 def load(cls, repo):
227 version = cls._getversion(repo)
227 version = cls._getversion(repo)
228 if version < cls._version:
228 if version < cls._version:
229 d = cls._readold(repo)
229 d = cls._readold(repo)
230 elif version == cls._version:
230 elif version == cls._version:
231 d = scmutil.simplekeyvaluefile(
231 d = scmutil.simplekeyvaluefile(
232 repo.vfs, cls._filename).read(firstlinenonkeyval=True)
232 repo.vfs, cls._filename).read(firstlinenonkeyval=True)
233 else:
233 else:
234 raise error.Abort(_('this version of shelve is incompatible '
234 raise error.Abort(_('this version of shelve is incompatible '
235 'with the version used in this repo'))
235 'with the version used in this repo'))
236
236
237 cls._verifyandtransform(d)
237 cls._verifyandtransform(d)
238 try:
238 try:
239 obj = cls()
239 obj = cls()
240 obj.name = d['name']
240 obj.name = d['name']
241 obj.wctx = repo[d['originalwctx']]
241 obj.wctx = repo[d['originalwctx']]
242 obj.pendingctx = repo[d['pendingctx']]
242 obj.pendingctx = repo[d['pendingctx']]
243 obj.parents = d['parents']
243 obj.parents = d['parents']
244 obj.nodestoremove = d['nodestoremove']
244 obj.nodestoremove = d['nodestoremove']
245 obj.branchtorestore = d.get('branchtorestore', '')
245 obj.branchtorestore = d.get('branchtorestore', '')
246 obj.keep = d.get('keep') == cls._keep
246 obj.keep = d.get('keep') == cls._keep
247 obj.activebookmark = ''
247 obj.activebookmark = ''
248 if d.get('activebook', '') != cls._noactivebook:
248 if d.get('activebook', '') != cls._noactivebook:
249 obj.activebookmark = d.get('activebook', '')
249 obj.activebookmark = d.get('activebook', '')
250 except (error.RepoLookupError, KeyError) as err:
250 except (error.RepoLookupError, KeyError) as err:
251 raise error.CorruptedState(pycompat.bytestr(err))
251 raise error.CorruptedState(pycompat.bytestr(err))
252
252
253 return obj
253 return obj
254
254
255 @classmethod
255 @classmethod
256 def save(cls, repo, name, originalwctx, pendingctx, nodestoremove,
256 def save(cls, repo, name, originalwctx, pendingctx, nodestoremove,
257 branchtorestore, keep=False, activebook=''):
257 branchtorestore, keep=False, activebook=''):
258 info = {
258 info = {
259 "name": name,
259 "name": name,
260 "originalwctx": nodemod.hex(originalwctx.node()),
260 "originalwctx": nodemod.hex(originalwctx.node()),
261 "pendingctx": nodemod.hex(pendingctx.node()),
261 "pendingctx": nodemod.hex(pendingctx.node()),
262 "parents": ' '.join([nodemod.hex(p)
262 "parents": ' '.join([nodemod.hex(p)
263 for p in repo.dirstate.parents()]),
263 for p in repo.dirstate.parents()]),
264 "nodestoremove": ' '.join([nodemod.hex(n)
264 "nodestoremove": ' '.join([nodemod.hex(n)
265 for n in nodestoremove]),
265 for n in nodestoremove]),
266 "branchtorestore": branchtorestore,
266 "branchtorestore": branchtorestore,
267 "keep": cls._keep if keep else cls._nokeep,
267 "keep": cls._keep if keep else cls._nokeep,
268 "activebook": activebook or cls._noactivebook
268 "activebook": activebook or cls._noactivebook
269 }
269 }
270 scmutil.simplekeyvaluefile(
270 scmutil.simplekeyvaluefile(
271 repo.vfs, cls._filename).write(info,
271 repo.vfs, cls._filename).write(info,
272 firstline=("%d" % cls._version))
272 firstline=("%d" % cls._version))
273
273
274 @classmethod
274 @classmethod
275 def clear(cls, repo):
275 def clear(cls, repo):
276 repo.vfs.unlinkpath(cls._filename, ignoremissing=True)
276 repo.vfs.unlinkpath(cls._filename, ignoremissing=True)
277
277
278 def cleanupoldbackups(repo):
278 def cleanupoldbackups(repo):
279 vfs = vfsmod.vfs(repo.vfs.join(backupdir))
279 vfs = vfsmod.vfs(repo.vfs.join(backupdir))
280 maxbackups = repo.ui.configint('shelve', 'maxbackups')
280 maxbackups = repo.ui.configint('shelve', 'maxbackups')
281 hgfiles = [f for f in vfs.listdir()
281 hgfiles = [f for f in vfs.listdir()
282 if f.endswith('.' + patchextension)]
282 if f.endswith('.' + patchextension)]
283 hgfiles = sorted([(vfs.stat(f)[stat.ST_MTIME], f) for f in hgfiles])
283 hgfiles = sorted([(vfs.stat(f)[stat.ST_MTIME], f) for f in hgfiles])
284 if maxbackups > 0 and maxbackups < len(hgfiles):
284 if maxbackups > 0 and maxbackups < len(hgfiles):
285 bordermtime = hgfiles[-maxbackups][0]
285 bordermtime = hgfiles[-maxbackups][0]
286 else:
286 else:
287 bordermtime = None
287 bordermtime = None
288 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
288 for mtime, f in hgfiles[:len(hgfiles) - maxbackups]:
289 if mtime == bordermtime:
289 if mtime == bordermtime:
290 # keep it, because timestamp can't decide exact order of backups
290 # keep it, because timestamp can't decide exact order of backups
291 continue
291 continue
292 base = f[:-(1 + len(patchextension))]
292 base = f[:-(1 + len(patchextension))]
293 for ext in shelvefileextensions:
293 for ext in shelvefileextensions:
294 vfs.tryunlink(base + '.' + ext)
294 vfs.tryunlink(base + '.' + ext)
295
295
296 def _backupactivebookmark(repo):
296 def _backupactivebookmark(repo):
297 activebookmark = repo._activebookmark
297 activebookmark = repo._activebookmark
298 if activebookmark:
298 if activebookmark:
299 bookmarks.deactivate(repo)
299 bookmarks.deactivate(repo)
300 return activebookmark
300 return activebookmark
301
301
302 def _restoreactivebookmark(repo, mark):
302 def _restoreactivebookmark(repo, mark):
303 if mark:
303 if mark:
304 bookmarks.activate(repo, mark)
304 bookmarks.activate(repo, mark)
305
305
306 def _aborttransaction(repo, tr):
306 def _aborttransaction(repo, tr):
307 '''Abort current transaction for shelve/unshelve, but keep dirstate
307 '''Abort current transaction for shelve/unshelve, but keep dirstate
308 '''
308 '''
309 dirstatebackupname = 'dirstate.shelve'
309 dirstatebackupname = 'dirstate.shelve'
310 repo.dirstate.savebackup(tr, dirstatebackupname)
310 repo.dirstate.savebackup(tr, dirstatebackupname)
311 tr.abort()
311 tr.abort()
312 repo.dirstate.restorebackup(None, dirstatebackupname)
312 repo.dirstate.restorebackup(None, dirstatebackupname)
313
313
314 def getshelvename(repo, parent, opts):
314 def getshelvename(repo, parent, opts):
315 """Decide on the name this shelve is going to have"""
315 """Decide on the name this shelve is going to have"""
316 def gennames():
316 def gennames():
317 yield label
317 yield label
318 for i in itertools.count(1):
318 for i in itertools.count(1):
319 yield '%s-%02d' % (label, i)
319 yield '%s-%02d' % (label, i)
320 name = opts.get('name')
320 name = opts.get('name')
321 label = repo._activebookmark or parent.branch() or 'default'
321 label = repo._activebookmark or parent.branch() or 'default'
322 # slashes aren't allowed in filenames, therefore we rename it
322 # slashes aren't allowed in filenames, therefore we rename it
323 label = label.replace('/', '_')
323 label = label.replace('/', '_')
324 label = label.replace('\\', '_')
324 label = label.replace('\\', '_')
325 # filenames must not start with '.' as it should not be hidden
325 # filenames must not start with '.' as it should not be hidden
326 if label.startswith('.'):
326 if label.startswith('.'):
327 label = label.replace('.', '_', 1)
327 label = label.replace('.', '_', 1)
328
328
329 if name:
329 if name:
330 if shelvedfile(repo, name, patchextension).exists():
330 if shelvedfile(repo, name, patchextension).exists():
331 e = _("a shelved change named '%s' already exists") % name
331 e = _("a shelved change named '%s' already exists") % name
332 raise error.Abort(e)
332 raise error.Abort(e)
333
333
334 # ensure we are not creating a subdirectory or a hidden file
334 # ensure we are not creating a subdirectory or a hidden file
335 if '/' in name or '\\' in name:
335 if '/' in name or '\\' in name:
336 raise error.Abort(_('shelved change names can not contain slashes'))
336 raise error.Abort(_('shelved change names can not contain slashes'))
337 if name.startswith('.'):
337 if name.startswith('.'):
338 raise error.Abort(_("shelved change names can not start with '.'"))
338 raise error.Abort(_("shelved change names can not start with '.'"))
339
339
340 else:
340 else:
341 for n in gennames():
341 for n in gennames():
342 if not shelvedfile(repo, n, patchextension).exists():
342 if not shelvedfile(repo, n, patchextension).exists():
343 name = n
343 name = n
344 break
344 break
345
345
346 return name
346 return name
347
347
348 def mutableancestors(ctx):
348 def mutableancestors(ctx):
349 """return all mutable ancestors for ctx (included)
349 """return all mutable ancestors for ctx (included)
350
350
351 Much faster than the revset ancestors(ctx) & draft()"""
351 Much faster than the revset ancestors(ctx) & draft()"""
352 seen = {nodemod.nullrev}
352 seen = {nodemod.nullrev}
353 visit = collections.deque()
353 visit = collections.deque()
354 visit.append(ctx)
354 visit.append(ctx)
355 while visit:
355 while visit:
356 ctx = visit.popleft()
356 ctx = visit.popleft()
357 yield ctx.node()
357 yield ctx.node()
358 for parent in ctx.parents():
358 for parent in ctx.parents():
359 rev = parent.rev()
359 rev = parent.rev()
360 if rev not in seen:
360 if rev not in seen:
361 seen.add(rev)
361 seen.add(rev)
362 if parent.mutable():
362 if parent.mutable():
363 visit.append(parent)
363 visit.append(parent)
364
364
365 def getcommitfunc(extra, interactive, editor=False):
365 def getcommitfunc(extra, interactive, editor=False):
366 def commitfunc(ui, repo, message, match, opts):
366 def commitfunc(ui, repo, message, match, opts):
367 hasmq = util.safehasattr(repo, 'mq')
367 hasmq = util.safehasattr(repo, 'mq')
368 if hasmq:
368 if hasmq:
369 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
369 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
370
370
371 targetphase = phases.internal
371 targetphase = phases.internal
372 if not phases.supportinternal(repo):
372 if not phases.supportinternal(repo):
373 targetphase = phases.secret
373 targetphase = phases.secret
374 overrides = {('phases', 'new-commit'): targetphase}
374 overrides = {('phases', 'new-commit'): targetphase}
375 try:
375 try:
376 editor_ = False
376 editor_ = False
377 if editor:
377 if editor:
378 editor_ = cmdutil.getcommiteditor(editform='shelve.shelve',
378 editor_ = cmdutil.getcommiteditor(editform='shelve.shelve',
379 **pycompat.strkwargs(opts))
379 **pycompat.strkwargs(opts))
380 with repo.ui.configoverride(overrides):
380 with repo.ui.configoverride(overrides):
381 return repo.commit(message, shelveuser, opts.get('date'),
381 return repo.commit(message, shelveuser, opts.get('date'),
382 match, editor=editor_, extra=extra)
382 match, editor=editor_, extra=extra)
383 finally:
383 finally:
384 if hasmq:
384 if hasmq:
385 repo.mq.checkapplied = saved
385 repo.mq.checkapplied = saved
386
386
387 def interactivecommitfunc(ui, repo, *pats, **opts):
387 def interactivecommitfunc(ui, repo, *pats, **opts):
388 opts = pycompat.byteskwargs(opts)
388 opts = pycompat.byteskwargs(opts)
389 match = scmutil.match(repo['.'], pats, {})
389 match = scmutil.match(repo['.'], pats, {})
390 message = opts['message']
390 message = opts['message']
391 return commitfunc(ui, repo, message, match, opts)
391 return commitfunc(ui, repo, message, match, opts)
392
392
393 return interactivecommitfunc if interactive else commitfunc
393 return interactivecommitfunc if interactive else commitfunc
394
394
395 def _nothingtoshelvemessaging(ui, repo, pats, opts):
395 def _nothingtoshelvemessaging(ui, repo, pats, opts):
396 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
396 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
397 if stat.deleted:
397 if stat.deleted:
398 ui.status(_("nothing changed (%d missing files, see "
398 ui.status(_("nothing changed (%d missing files, see "
399 "'hg status')\n") % len(stat.deleted))
399 "'hg status')\n") % len(stat.deleted))
400 else:
400 else:
401 ui.status(_("nothing changed\n"))
401 ui.status(_("nothing changed\n"))
402
402
403 def _shelvecreatedcommit(repo, node, name, match):
403 def _shelvecreatedcommit(repo, node, name, match):
404 info = {'node': nodemod.hex(node)}
404 info = {'node': nodemod.hex(node)}
405 shelvedfile(repo, name, 'shelve').writeinfo(info)
405 shelvedfile(repo, name, 'shelve').writeinfo(info)
406 bases = list(mutableancestors(repo[node]))
406 bases = list(mutableancestors(repo[node]))
407 shelvedfile(repo, name, 'hg').writebundle(bases, node)
407 shelvedfile(repo, name, 'hg').writebundle(bases, node)
408 with shelvedfile(repo, name, patchextension).opener('wb') as fp:
408 with shelvedfile(repo, name, patchextension).opener('wb') as fp:
409 cmdutil.exportfile(repo, [node], fp, opts=mdiff.diffopts(git=True),
409 cmdutil.exportfile(repo, [node], fp, opts=mdiff.diffopts(git=True),
410 match=match)
410 match=match)
411
411
412 def _includeunknownfiles(repo, pats, opts, extra):
412 def _includeunknownfiles(repo, pats, opts, extra):
413 s = repo.status(match=scmutil.match(repo[None], pats, opts),
413 s = repo.status(match=scmutil.match(repo[None], pats, opts),
414 unknown=True)
414 unknown=True)
415 if s.unknown:
415 if s.unknown:
416 extra['shelve_unknown'] = '\0'.join(s.unknown)
416 extra['shelve_unknown'] = '\0'.join(s.unknown)
417 repo[None].add(s.unknown)
417 repo[None].add(s.unknown)
418
418
419 def _finishshelve(repo, tr):
419 def _finishshelve(repo, tr):
420 if phases.supportinternal(repo):
420 if phases.supportinternal(repo):
421 tr.close()
421 tr.close()
422 else:
422 else:
423 _aborttransaction(repo, tr)
423 _aborttransaction(repo, tr)
424
424
425 def createcmd(ui, repo, pats, opts):
425 def createcmd(ui, repo, pats, opts):
426 """subcommand that creates a new shelve"""
426 """subcommand that creates a new shelve"""
427 with repo.wlock():
427 with repo.wlock():
428 cmdutil.checkunfinished(repo)
428 cmdutil.checkunfinished(repo)
429 return _docreatecmd(ui, repo, pats, opts)
429 return _docreatecmd(ui, repo, pats, opts)
430
430
431 def _docreatecmd(ui, repo, pats, opts):
431 def _docreatecmd(ui, repo, pats, opts):
432 wctx = repo[None]
432 wctx = repo[None]
433 parents = wctx.parents()
433 parents = wctx.parents()
434 parent = parents[0]
434 parent = parents[0]
435 origbranch = wctx.branch()
435 origbranch = wctx.branch()
436
436
437 if parent.node() != nodemod.nullid:
437 if parent.node() != nodemod.nullid:
438 desc = "changes to: %s" % parent.description().split('\n', 1)[0]
438 desc = "changes to: %s" % parent.description().split('\n', 1)[0]
439 else:
439 else:
440 desc = '(changes in empty repository)'
440 desc = '(changes in empty repository)'
441
441
442 if not opts.get('message'):
442 if not opts.get('message'):
443 opts['message'] = desc
443 opts['message'] = desc
444
444
445 lock = tr = activebookmark = None
445 lock = tr = activebookmark = None
446 try:
446 try:
447 lock = repo.lock()
447 lock = repo.lock()
448
448
449 # use an uncommitted transaction to generate the bundle to avoid
449 # use an uncommitted transaction to generate the bundle to avoid
450 # pull races. ensure we don't print the abort message to stderr.
450 # pull races. ensure we don't print the abort message to stderr.
451 tr = repo.transaction('shelve', report=lambda x: None)
451 tr = repo.transaction('shelve', report=lambda x: None)
452
452
453 interactive = opts.get('interactive', False)
453 interactive = opts.get('interactive', False)
454 includeunknown = (opts.get('unknown', False) and
454 includeunknown = (opts.get('unknown', False) and
455 not opts.get('addremove', False))
455 not opts.get('addremove', False))
456
456
457 name = getshelvename(repo, parent, opts)
457 name = getshelvename(repo, parent, opts)
458 activebookmark = _backupactivebookmark(repo)
458 activebookmark = _backupactivebookmark(repo)
459 extra = {'internal': 'shelve'}
459 extra = {'internal': 'shelve'}
460 if includeunknown:
460 if includeunknown:
461 _includeunknownfiles(repo, pats, opts, extra)
461 _includeunknownfiles(repo, pats, opts, extra)
462
462
463 if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
463 if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
464 # In non-bare shelve we don't store newly created branch
464 # In non-bare shelve we don't store newly created branch
465 # at bundled commit
465 # at bundled commit
466 repo.dirstate.setbranch(repo['.'].branch())
466 repo.dirstate.setbranch(repo['.'].branch())
467
467
468 commitfunc = getcommitfunc(extra, interactive, editor=True)
468 commitfunc = getcommitfunc(extra, interactive, editor=True)
469 if not interactive:
469 if not interactive:
470 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
470 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
471 else:
471 else:
472 node = cmdutil.dorecord(ui, repo, commitfunc, None,
472 node = cmdutil.dorecord(ui, repo, commitfunc, None,
473 False, cmdutil.recordfilter, *pats,
473 False, cmdutil.recordfilter, *pats,
474 **pycompat.strkwargs(opts))
474 **pycompat.strkwargs(opts))
475 if not node:
475 if not node:
476 _nothingtoshelvemessaging(ui, repo, pats, opts)
476 _nothingtoshelvemessaging(ui, repo, pats, opts)
477 return 1
477 return 1
478
478
479 # Create a matcher so that prefetch doesn't attempt to fetch
479 # Create a matcher so that prefetch doesn't attempt to fetch
480 # the entire repository pointlessly, and as an optimisation
480 # the entire repository pointlessly, and as an optimisation
481 # for movedirstate, if needed.
481 # for movedirstate, if needed.
482 match = scmutil.matchfiles(repo, repo[node].files())
482 match = scmutil.matchfiles(repo, repo[node].files())
483 _shelvecreatedcommit(repo, node, name, match)
483 _shelvecreatedcommit(repo, node, name, match)
484
484
485 if ui.formatted():
485 if ui.formatted():
486 desc = stringutil.ellipsis(desc, ui.termwidth())
486 desc = stringutil.ellipsis(desc, ui.termwidth())
487 ui.status(_('shelved as %s\n') % name)
487 ui.status(_('shelved as %s\n') % name)
488 if opts['keep']:
488 if opts['keep']:
489 with repo.dirstate.parentchange():
489 with repo.dirstate.parentchange():
490 scmutil.movedirstate(repo, parent, match)
490 scmutil.movedirstate(repo, parent, match)
491 else:
491 else:
492 hg.update(repo, parent.node())
492 hg.update(repo, parent.node())
493 if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
493 if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
494 repo.dirstate.setbranch(origbranch)
494 repo.dirstate.setbranch(origbranch)
495
495
496 _finishshelve(repo, tr)
496 _finishshelve(repo, tr)
497 finally:
497 finally:
498 _restoreactivebookmark(repo, activebookmark)
498 _restoreactivebookmark(repo, activebookmark)
499 lockmod.release(tr, lock)
499 lockmod.release(tr, lock)
500
500
501 def _isbareshelve(pats, opts):
501 def _isbareshelve(pats, opts):
502 return (not pats
502 return (not pats
503 and not opts.get('interactive', False)
503 and not opts.get('interactive', False)
504 and not opts.get('include', False)
504 and not opts.get('include', False)
505 and not opts.get('exclude', False))
505 and not opts.get('exclude', False))
506
506
507 def _iswctxonnewbranch(repo):
507 def _iswctxonnewbranch(repo):
508 return repo[None].branch() != repo['.'].branch()
508 return repo[None].branch() != repo['.'].branch()
509
509
510 def cleanupcmd(ui, repo):
510 def cleanupcmd(ui, repo):
511 """subcommand that deletes all shelves"""
511 """subcommand that deletes all shelves"""
512
512
513 with repo.wlock():
513 with repo.wlock():
514 for (name, _type) in repo.vfs.readdir(shelvedir):
514 for (name, _type) in repo.vfs.readdir(shelvedir):
515 suffix = name.rsplit('.', 1)[-1]
515 suffix = name.rsplit('.', 1)[-1]
516 if suffix in shelvefileextensions:
516 if suffix in shelvefileextensions:
517 shelvedfile(repo, name).movetobackup()
517 shelvedfile(repo, name).movetobackup()
518 cleanupoldbackups(repo)
518 cleanupoldbackups(repo)
519
519
520 def deletecmd(ui, repo, pats):
520 def deletecmd(ui, repo, pats):
521 """subcommand that deletes a specific shelve"""
521 """subcommand that deletes a specific shelve"""
522 if not pats:
522 if not pats:
523 raise error.Abort(_('no shelved changes specified!'))
523 raise error.Abort(_('no shelved changes specified!'))
524 with repo.wlock():
524 with repo.wlock():
525 try:
525 try:
526 for name in pats:
526 for name in pats:
527 for suffix in shelvefileextensions:
527 for suffix in shelvefileextensions:
528 shfile = shelvedfile(repo, name, suffix)
528 shfile = shelvedfile(repo, name, suffix)
529 # patch file is necessary, as it should
529 # patch file is necessary, as it should
530 # be present for any kind of shelve,
530 # be present for any kind of shelve,
531 # but the .hg file is optional as in future we
531 # but the .hg file is optional as in future we
532 # will add obsolete shelve with does not create a
532 # will add obsolete shelve with does not create a
533 # bundle
533 # bundle
534 if shfile.exists() or suffix == patchextension:
534 if shfile.exists() or suffix == patchextension:
535 shfile.movetobackup()
535 shfile.movetobackup()
536 cleanupoldbackups(repo)
536 cleanupoldbackups(repo)
537 except OSError as err:
537 except OSError as err:
538 if err.errno != errno.ENOENT:
538 if err.errno != errno.ENOENT:
539 raise
539 raise
540 raise error.Abort(_("shelved change '%s' not found") % name)
540 raise error.Abort(_("shelved change '%s' not found") % name)
541
541
542 def listshelves(repo):
542 def listshelves(repo):
543 """return all shelves in repo as list of (time, filename)"""
543 """return all shelves in repo as list of (time, filename)"""
544 try:
544 try:
545 names = repo.vfs.readdir(shelvedir)
545 names = repo.vfs.readdir(shelvedir)
546 except OSError as err:
546 except OSError as err:
547 if err.errno != errno.ENOENT:
547 if err.errno != errno.ENOENT:
548 raise
548 raise
549 return []
549 return []
550 info = []
550 info = []
551 for (name, _type) in names:
551 for (name, _type) in names:
552 pfx, sfx = name.rsplit('.', 1)
552 pfx, sfx = name.rsplit('.', 1)
553 if not pfx or sfx != patchextension:
553 if not pfx or sfx != patchextension:
554 continue
554 continue
555 st = shelvedfile(repo, name).stat()
555 st = shelvedfile(repo, name).stat()
556 info.append((st[stat.ST_MTIME], shelvedfile(repo, pfx).filename()))
556 info.append((st[stat.ST_MTIME], shelvedfile(repo, pfx).filename()))
557 return sorted(info, reverse=True)
557 return sorted(info, reverse=True)
558
558
559 def listcmd(ui, repo, pats, opts):
559 def listcmd(ui, repo, pats, opts):
560 """subcommand that displays the list of shelves"""
560 """subcommand that displays the list of shelves"""
561 pats = set(pats)
561 pats = set(pats)
562 width = 80
562 width = 80
563 if not ui.plain():
563 if not ui.plain():
564 width = ui.termwidth()
564 width = ui.termwidth()
565 namelabel = 'shelve.newest'
565 namelabel = 'shelve.newest'
566 ui.pager('shelve')
566 ui.pager('shelve')
567 for mtime, name in listshelves(repo):
567 for mtime, name in listshelves(repo):
568 sname = util.split(name)[1]
568 sname = util.split(name)[1]
569 if pats and sname not in pats:
569 if pats and sname not in pats:
570 continue
570 continue
571 ui.write(sname, label=namelabel)
571 ui.write(sname, label=namelabel)
572 namelabel = 'shelve.name'
572 namelabel = 'shelve.name'
573 if ui.quiet:
573 if ui.quiet:
574 ui.write('\n')
574 ui.write('\n')
575 continue
575 continue
576 ui.write(' ' * (16 - len(sname)))
576 ui.write(' ' * (16 - len(sname)))
577 used = 16
577 used = 16
578 date = dateutil.makedate(mtime)
578 date = dateutil.makedate(mtime)
579 age = '(%s)' % templatefilters.age(date, abbrev=True)
579 age = '(%s)' % templatefilters.age(date, abbrev=True)
580 ui.write(age, label='shelve.age')
580 ui.write(age, label='shelve.age')
581 ui.write(' ' * (12 - len(age)))
581 ui.write(' ' * (12 - len(age)))
582 used += 12
582 used += 12
583 with open(name + '.' + patchextension, 'rb') as fp:
583 with open(name + '.' + patchextension, 'rb') as fp:
584 while True:
584 while True:
585 line = fp.readline()
585 line = fp.readline()
586 if not line:
586 if not line:
587 break
587 break
588 if not line.startswith('#'):
588 if not line.startswith('#'):
589 desc = line.rstrip()
589 desc = line.rstrip()
590 if ui.formatted():
590 if ui.formatted():
591 desc = stringutil.ellipsis(desc, width - used)
591 desc = stringutil.ellipsis(desc, width - used)
592 ui.write(desc)
592 ui.write(desc)
593 break
593 break
594 ui.write('\n')
594 ui.write('\n')
595 if not (opts['patch'] or opts['stat']):
595 if not (opts['patch'] or opts['stat']):
596 continue
596 continue
597 difflines = fp.readlines()
597 difflines = fp.readlines()
598 if opts['patch']:
598 if opts['patch']:
599 for chunk, label in patch.difflabel(iter, difflines):
599 for chunk, label in patch.difflabel(iter, difflines):
600 ui.write(chunk, label=label)
600 ui.write(chunk, label=label)
601 if opts['stat']:
601 if opts['stat']:
602 for chunk, label in patch.diffstatui(difflines, width=width):
602 for chunk, label in patch.diffstatui(difflines, width=width):
603 ui.write(chunk, label=label)
603 ui.write(chunk, label=label)
604
604
605 def patchcmds(ui, repo, pats, opts):
605 def patchcmds(ui, repo, pats, opts):
606 """subcommand that displays shelves"""
606 """subcommand that displays shelves"""
607 if len(pats) == 0:
607 if len(pats) == 0:
608 shelves = listshelves(repo)
608 shelves = listshelves(repo)
609 if not shelves:
609 if not shelves:
610 raise error.Abort(_("there are no shelves to show"))
610 raise error.Abort(_("there are no shelves to show"))
611 mtime, name = shelves[0]
611 mtime, name = shelves[0]
612 sname = util.split(name)[1]
612 sname = util.split(name)[1]
613 pats = [sname]
613 pats = [sname]
614
614
615 for shelfname in pats:
615 for shelfname in pats:
616 if not shelvedfile(repo, shelfname, patchextension).exists():
616 if not shelvedfile(repo, shelfname, patchextension).exists():
617 raise error.Abort(_("cannot find shelf %s") % shelfname)
617 raise error.Abort(_("cannot find shelf %s") % shelfname)
618
618
619 listcmd(ui, repo, pats, opts)
619 listcmd(ui, repo, pats, opts)
620
620
621 def checkparents(repo, state):
621 def checkparents(repo, state):
622 """check parent while resuming an unshelve"""
622 """check parent while resuming an unshelve"""
623 if state.parents != repo.dirstate.parents():
623 if state.parents != repo.dirstate.parents():
624 raise error.Abort(_('working directory parents do not match unshelve '
624 raise error.Abort(_('working directory parents do not match unshelve '
625 'state'))
625 'state'))
626
626
627 def _loadshelvedstate(ui, repo, opts):
627 def _loadshelvedstate(ui, repo, opts):
628 try:
628 try:
629 state = shelvedstate.load(repo)
629 state = shelvedstate.load(repo)
630 if opts.get('keep') is None:
630 if opts.get('keep') is None:
631 opts['keep'] = state.keep
631 opts['keep'] = state.keep
632 except IOError as err:
632 except IOError as err:
633 if err.errno != errno.ENOENT:
633 if err.errno != errno.ENOENT:
634 raise
634 raise
635 cmdutil.wrongtooltocontinue(repo, _('unshelve'))
635 cmdutil.wrongtooltocontinue(repo, _('unshelve'))
636 except error.CorruptedState as err:
636 except error.CorruptedState as err:
637 ui.debug(pycompat.bytestr(err) + '\n')
637 ui.debug(pycompat.bytestr(err) + '\n')
638 if opts.get('continue'):
638 if opts.get('continue'):
639 msg = _('corrupted shelved state file')
639 msg = _('corrupted shelved state file')
640 hint = _('please run hg unshelve --abort to abort unshelve '
640 hint = _('please run hg unshelve --abort to abort unshelve '
641 'operation')
641 'operation')
642 raise error.Abort(msg, hint=hint)
642 raise error.Abort(msg, hint=hint)
643 elif opts.get('abort'):
643 elif opts.get('abort'):
644 shelvedstate.clear(repo)
644 shelvedstate.clear(repo)
645 raise error.Abort(_('could not read shelved state file, your '
645 raise error.Abort(_('could not read shelved state file, your '
646 'working copy may be in an unexpected state\n'
646 'working copy may be in an unexpected state\n'
647 'please update to some commit\n'))
647 'please update to some commit\n'))
648 return state
648 return state
649
649
650 def unshelveabort(ui, repo, state):
650 def unshelveabort(ui, repo, state):
651 """subcommand that abort an in-progress unshelve"""
651 """subcommand that abort an in-progress unshelve"""
652 with repo.lock():
652 with repo.lock():
653 try:
653 try:
654 checkparents(repo, state)
654 checkparents(repo, state)
655
655
656 merge.update(repo, state.pendingctx, branchmerge=False, force=True)
656 merge.update(repo, state.pendingctx, branchmerge=False, force=True)
657 if (state.activebookmark
657 if (state.activebookmark
658 and state.activebookmark in repo._bookmarks):
658 and state.activebookmark in repo._bookmarks):
659 bookmarks.activate(repo, state.activebookmark)
659 bookmarks.activate(repo, state.activebookmark)
660 mergefiles(ui, repo, state.wctx, state.pendingctx)
660 mergefiles(ui, repo, state.wctx, state.pendingctx)
661 if not phases.supportinternal(repo):
661 if not phases.supportinternal(repo):
662 repair.strip(ui, repo, state.nodestoremove, backup=False,
662 repair.strip(ui, repo, state.nodestoremove, backup=False,
663 topic='shelve')
663 topic='shelve')
664 finally:
664 finally:
665 shelvedstate.clear(repo)
665 shelvedstate.clear(repo)
666 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
666 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
667
667
668 def hgabortunshelve(ui, repo):
668 def hgabortunshelve(ui, repo):
669 """logic to abort unshelve using 'hg abort"""
669 """logic to abort unshelve using 'hg abort"""
670 with repo.wlock():
670 with repo.wlock():
671 state = _loadshelvedstate(ui, repo, {'abort' : True})
671 state = _loadshelvedstate(ui, repo, {'abort' : True})
672 return unshelveabort(ui, repo, state)
672 return unshelveabort(ui, repo, state)
673
673
674 def mergefiles(ui, repo, wctx, shelvectx):
674 def mergefiles(ui, repo, wctx, shelvectx):
675 """updates to wctx and merges the changes from shelvectx into the
675 """updates to wctx and merges the changes from shelvectx into the
676 dirstate."""
676 dirstate."""
677 with ui.configoverride({('ui', 'quiet'): True}):
677 with ui.configoverride({('ui', 'quiet'): True}):
678 hg.update(repo, wctx.node())
678 hg.update(repo, wctx.node())
679 ui.pushbuffer(True)
679 ui.pushbuffer(True)
680 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents())
680 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents())
681 ui.popbuffer()
681 ui.popbuffer()
682
682
683 def restorebranch(ui, repo, branchtorestore):
683 def restorebranch(ui, repo, branchtorestore):
684 if branchtorestore and branchtorestore != repo.dirstate.branch():
684 if branchtorestore and branchtorestore != repo.dirstate.branch():
685 repo.dirstate.setbranch(branchtorestore)
685 repo.dirstate.setbranch(branchtorestore)
686 ui.status(_('marked working directory as branch %s\n')
686 ui.status(_('marked working directory as branch %s\n')
687 % branchtorestore)
687 % branchtorestore)
688
688
689 def unshelvecleanup(ui, repo, name, opts):
689 def unshelvecleanup(ui, repo, name, opts):
690 """remove related files after an unshelve"""
690 """remove related files after an unshelve"""
691 if not opts.get('keep'):
691 if not opts.get('keep'):
692 for filetype in shelvefileextensions:
692 for filetype in shelvefileextensions:
693 shfile = shelvedfile(repo, name, filetype)
693 shfile = shelvedfile(repo, name, filetype)
694 if shfile.exists():
694 if shfile.exists():
695 shfile.movetobackup()
695 shfile.movetobackup()
696 cleanupoldbackups(repo)
696 cleanupoldbackups(repo)
697 def unshelvecontinue(ui, repo, state, opts, basename=None):
697 def unshelvecontinue(ui, repo, state, opts, basename=None):
698 """subcommand to continue an in-progress unshelve"""
698 """subcommand to continue an in-progress unshelve"""
699 # We're finishing off a merge. First parent is our original
699 # We're finishing off a merge. First parent is our original
700 # parent, second is the temporary "fake" commit we're unshelving.
700 # parent, second is the temporary "fake" commit we're unshelving.
701 interactive = opts.get('interactive')
701 interactive = opts.get('interactive')
702 with repo.lock():
702 with repo.lock():
703 checkparents(repo, state)
703 checkparents(repo, state)
704 ms = merge.mergestate.read(repo)
704 ms = merge.mergestate.read(repo)
705 if list(ms.unresolved()):
705 if list(ms.unresolved()):
706 raise error.Abort(
706 raise error.Abort(
707 _("unresolved conflicts, can't continue"),
707 _("unresolved conflicts, can't continue"),
708 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
708 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
709
709
710 shelvectx = repo[state.parents[1]]
710 shelvectx = repo[state.parents[1]]
711 pendingctx = state.pendingctx
711 pendingctx = state.pendingctx
712
712
713 with repo.dirstate.parentchange():
713 with repo.dirstate.parentchange():
714 repo.setparents(state.pendingctx.node(), nodemod.nullid)
714 repo.setparents(state.pendingctx.node(), nodemod.nullid)
715 repo.dirstate.write(repo.currenttransaction())
715 repo.dirstate.write(repo.currenttransaction())
716
716
717 targetphase = phases.internal
717 targetphase = phases.internal
718 if not phases.supportinternal(repo):
718 if not phases.supportinternal(repo):
719 targetphase = phases.secret
719 targetphase = phases.secret
720 overrides = {('phases', 'new-commit'): targetphase}
720 overrides = {('phases', 'new-commit'): targetphase}
721 with repo.ui.configoverride(overrides, 'unshelve'):
721 with repo.ui.configoverride(overrides, 'unshelve'):
722 with repo.dirstate.parentchange():
722 with repo.dirstate.parentchange():
723 repo.setparents(state.parents[0], nodemod.nullid)
723 repo.setparents(state.parents[0], nodemod.nullid)
724 newnode, ispartialunshelve = _createunshelvectx(ui,
724 newnode, ispartialunshelve = _createunshelvectx(ui,
725 repo, shelvectx, basename, interactive, opts)
725 repo, shelvectx, basename, interactive, opts)
726
726
727 if newnode is None:
727 if newnode is None:
728 # If it ended up being a no-op commit, then the normal
728 # If it ended up being a no-op commit, then the normal
729 # merge state clean-up path doesn't happen, so do it
729 # merge state clean-up path doesn't happen, so do it
730 # here. Fix issue5494
730 # here. Fix issue5494
731 merge.mergestate.clean(repo)
731 merge.mergestate.clean(repo)
732 shelvectx = state.pendingctx
732 shelvectx = state.pendingctx
733 msg = _('note: unshelved changes already existed '
733 msg = _('note: unshelved changes already existed '
734 'in the working copy\n')
734 'in the working copy\n')
735 ui.status(msg)
735 ui.status(msg)
736 else:
736 else:
737 # only strip the shelvectx if we produced one
737 # only strip the shelvectx if we produced one
738 state.nodestoremove.append(newnode)
738 state.nodestoremove.append(newnode)
739 shelvectx = repo[newnode]
739 shelvectx = repo[newnode]
740
740
741 hg.updaterepo(repo, pendingctx.node(), overwrite=False)
741 hg.updaterepo(repo, pendingctx.node(), overwrite=False)
742 mergefiles(ui, repo, state.wctx, shelvectx)
742 mergefiles(ui, repo, state.wctx, shelvectx)
743 restorebranch(ui, repo, state.branchtorestore)
743 restorebranch(ui, repo, state.branchtorestore)
744
744
745 if not ispartialunshelve:
745 if not ispartialunshelve:
746 if not phases.supportinternal(repo):
746 if not phases.supportinternal(repo):
747 repair.strip(ui, repo, state.nodestoremove, backup=False,
747 repair.strip(ui, repo, state.nodestoremove, backup=False,
748 topic='shelve')
748 topic='shelve')
749 shelvedstate.clear(repo)
749 shelvedstate.clear(repo)
750 unshelvecleanup(ui, repo, state.name, opts)
750 unshelvecleanup(ui, repo, state.name, opts)
751 _restoreactivebookmark(repo, state.activebookmark)
751 _restoreactivebookmark(repo, state.activebookmark)
752 ui.status(_("unshelve of '%s' complete\n") % state.name)
752 ui.status(_("unshelve of '%s' complete\n") % state.name)
753
753
754 def hgcontinueunshelve(ui, repo):
754 def hgcontinueunshelve(ui, repo):
755 """logic to resume unshelve using 'hg continue'"""
755 """logic to resume unshelve using 'hg continue'"""
756 with repo.wlock():
756 with repo.wlock():
757 state = _loadshelvedstate(ui, repo, {'continue' : True})
757 state = _loadshelvedstate(ui, repo, {'continue' : True})
758 return unshelvecontinue(ui, repo, state, {'keep' : state.keep})
758 return unshelvecontinue(ui, repo, state, {'keep' : state.keep})
759
759
760 def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
760 def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
761 """Temporarily commit working copy changes before moving unshelve commit"""
761 """Temporarily commit working copy changes before moving unshelve commit"""
762 # Store pending changes in a commit and remember added in case a shelve
762 # Store pending changes in a commit and remember added in case a shelve
763 # contains unknown files that are part of the pending change
763 # contains unknown files that are part of the pending change
764 s = repo.status()
764 s = repo.status()
765 addedbefore = frozenset(s.added)
765 addedbefore = frozenset(s.added)
766 if not (s.modified or s.added or s.removed):
766 if not (s.modified or s.added or s.removed):
767 return tmpwctx, addedbefore
767 return tmpwctx, addedbefore
768 ui.status(_("temporarily committing pending changes "
768 ui.status(_("temporarily committing pending changes "
769 "(restore with 'hg unshelve --abort')\n"))
769 "(restore with 'hg unshelve --abort')\n"))
770 extra = {'internal': 'shelve'}
770 extra = {'internal': 'shelve'}
771 commitfunc = getcommitfunc(extra=extra, interactive=False,
771 commitfunc = getcommitfunc(extra=extra, interactive=False,
772 editor=False)
772 editor=False)
773 tempopts = {}
773 tempopts = {}
774 tempopts['message'] = "pending changes temporary commit"
774 tempopts['message'] = "pending changes temporary commit"
775 tempopts['date'] = opts.get('date')
775 tempopts['date'] = opts.get('date')
776 with ui.configoverride({('ui', 'quiet'): True}):
776 with ui.configoverride({('ui', 'quiet'): True}):
777 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
777 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
778 tmpwctx = repo[node]
778 tmpwctx = repo[node]
779 return tmpwctx, addedbefore
779 return tmpwctx, addedbefore
780
780
781 def _unshelverestorecommit(ui, repo, tr, basename):
781 def _unshelverestorecommit(ui, repo, tr, basename):
782 """Recreate commit in the repository during the unshelve"""
782 """Recreate commit in the repository during the unshelve"""
783 repo = repo.unfiltered()
783 repo = repo.unfiltered()
784 node = None
784 node = None
785 if shelvedfile(repo, basename, 'shelve').exists():
785 if shelvedfile(repo, basename, 'shelve').exists():
786 node = shelvedfile(repo, basename, 'shelve').readinfo()['node']
786 node = shelvedfile(repo, basename, 'shelve').readinfo()['node']
787 if node is None or node not in repo:
787 if node is None or node not in repo:
788 with ui.configoverride({('ui', 'quiet'): True}):
788 with ui.configoverride({('ui', 'quiet'): True}):
789 shelvectx = shelvedfile(repo, basename, 'hg').applybundle(tr)
789 shelvectx = shelvedfile(repo, basename, 'hg').applybundle(tr)
790 # We might not strip the unbundled changeset, so we should keep track of
790 # We might not strip the unbundled changeset, so we should keep track of
791 # the unshelve node in case we need to reuse it (eg: unshelve --keep)
791 # the unshelve node in case we need to reuse it (eg: unshelve --keep)
792 if node is None:
792 if node is None:
793 info = {'node': nodemod.hex(shelvectx.node())}
793 info = {'node': nodemod.hex(shelvectx.node())}
794 shelvedfile(repo, basename, 'shelve').writeinfo(info)
794 shelvedfile(repo, basename, 'shelve').writeinfo(info)
795 else:
795 else:
796 shelvectx = repo[node]
796 shelvectx = repo[node]
797
797
798 return repo, shelvectx
798 return repo, shelvectx
799
799
800 def _createunshelvectx(ui, repo, shelvectx, basename, interactive, opts):
800 def _createunshelvectx(ui, repo, shelvectx, basename, interactive, opts):
801 """Handles the creation of unshelve commit and updates the shelve if it
801 """Handles the creation of unshelve commit and updates the shelve if it
802 was partially unshelved.
802 was partially unshelved.
803
803
804 If interactive is:
804 If interactive is:
805
805
806 * False: Commits all the changes in the working directory.
806 * False: Commits all the changes in the working directory.
807 * True: Prompts the user to select changes to unshelve and commit them.
807 * True: Prompts the user to select changes to unshelve and commit them.
808 Update the shelve with remaining changes.
808 Update the shelve with remaining changes.
809
809
810 Returns the node of the new commit formed and a bool indicating whether
810 Returns the node of the new commit formed and a bool indicating whether
811 the shelve was partially unshelved.Creates a commit ctx to unshelve
811 the shelve was partially unshelved.Creates a commit ctx to unshelve
812 interactively or non-interactively.
812 interactively or non-interactively.
813
813
814 The user might want to unshelve certain changes only from the stored
814 The user might want to unshelve certain changes only from the stored
815 shelve in interactive. So, we would create two commits. One with requested
815 shelve in interactive. So, we would create two commits. One with requested
816 changes to unshelve at that time and the latter is shelved for future.
816 changes to unshelve at that time and the latter is shelved for future.
817
817
818 Here, we return both the newnode which is created interactively and a
818 Here, we return both the newnode which is created interactively and a
819 bool to know whether the shelve is partly done or completely done.
819 bool to know whether the shelve is partly done or completely done.
820 """
820 """
821 opts['message'] = shelvectx.description()
821 opts['message'] = shelvectx.description()
822 opts['interactive-unshelve'] = True
822 opts['interactive-unshelve'] = True
823 pats = []
823 pats = []
824 if not interactive:
824 if not interactive:
825 newnode = repo.commit(text=shelvectx.description(),
825 newnode = repo.commit(text=shelvectx.description(),
826 extra=shelvectx.extra(),
826 extra=shelvectx.extra(),
827 user=shelvectx.user(),
827 user=shelvectx.user(),
828 date=shelvectx.date())
828 date=shelvectx.date())
829 return newnode, False
829 return newnode, False
830
830
831 commitfunc = getcommitfunc(shelvectx.extra(), interactive=True,
831 commitfunc = getcommitfunc(shelvectx.extra(), interactive=True,
832 editor=True)
832 editor=True)
833 newnode = cmdutil.dorecord(ui, repo, commitfunc, None, False,
833 newnode = cmdutil.dorecord(ui, repo, commitfunc, None, False,
834 cmdutil.recordfilter, *pats,
834 cmdutil.recordfilter, *pats,
835 **pycompat.strkwargs(opts))
835 **pycompat.strkwargs(opts))
836 snode = repo.commit(text=shelvectx.description(),
836 snode = repo.commit(text=shelvectx.description(),
837 extra=shelvectx.extra(),
837 extra=shelvectx.extra(),
838 user=shelvectx.user(),
838 user=shelvectx.user())
839 date=shelvectx.date())
840 m = scmutil.matchfiles(repo, repo[snode].files())
839 m = scmutil.matchfiles(repo, repo[snode].files())
841 if snode:
840 if snode:
842 _shelvecreatedcommit(repo, snode, basename, m)
841 _shelvecreatedcommit(repo, snode, basename, m)
843
842
844 return newnode, bool(snode)
843 return newnode, bool(snode)
845
844
846 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
845 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
847 tmpwctx, shelvectx, branchtorestore,
846 tmpwctx, shelvectx, branchtorestore,
848 activebookmark):
847 activebookmark):
849 """Rebase restored commit from its original location to a destination"""
848 """Rebase restored commit from its original location to a destination"""
850 # If the shelve is not immediately on top of the commit
849 # If the shelve is not immediately on top of the commit
851 # we'll be merging with, rebase it to be on top.
850 # we'll be merging with, rebase it to be on top.
852 interactive = opts.get('interactive')
851 interactive = opts.get('interactive')
853 if tmpwctx.node() == shelvectx.p1().node() and not interactive:
852 if tmpwctx.node() == shelvectx.p1().node() and not interactive:
854 # We won't skip on interactive mode because, the user might want to
853 # We won't skip on interactive mode because, the user might want to
855 # unshelve certain changes only.
854 # unshelve certain changes only.
856 return shelvectx, False
855 return shelvectx, False
857
856
858 overrides = {
857 overrides = {
859 ('ui', 'forcemerge'): opts.get('tool', ''),
858 ('ui', 'forcemerge'): opts.get('tool', ''),
860 ('phases', 'new-commit'): phases.secret,
859 ('phases', 'new-commit'): phases.secret,
861 }
860 }
862 with repo.ui.configoverride(overrides, 'unshelve'):
861 with repo.ui.configoverride(overrides, 'unshelve'):
863 ui.status(_('rebasing shelved changes\n'))
862 ui.status(_('rebasing shelved changes\n'))
864 stats = merge.graft(repo, shelvectx, shelvectx.p1(),
863 stats = merge.graft(repo, shelvectx, shelvectx.p1(),
865 labels=['shelve', 'working-copy'],
864 labels=['shelve', 'working-copy'],
866 keepconflictparent=True)
865 keepconflictparent=True)
867 if stats.unresolvedcount:
866 if stats.unresolvedcount:
868 tr.close()
867 tr.close()
869
868
870 nodestoremove = [repo.changelog.node(rev)
869 nodestoremove = [repo.changelog.node(rev)
871 for rev in pycompat.xrange(oldtiprev, len(repo))]
870 for rev in pycompat.xrange(oldtiprev, len(repo))]
872 shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoremove,
871 shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoremove,
873 branchtorestore, opts.get('keep'), activebookmark)
872 branchtorestore, opts.get('keep'), activebookmark)
874 raise error.InterventionRequired(
873 raise error.InterventionRequired(
875 _("unresolved conflicts (see 'hg resolve', then "
874 _("unresolved conflicts (see 'hg resolve', then "
876 "'hg unshelve --continue')"))
875 "'hg unshelve --continue')"))
877
876
878 with repo.dirstate.parentchange():
877 with repo.dirstate.parentchange():
879 repo.setparents(tmpwctx.node(), nodemod.nullid)
878 repo.setparents(tmpwctx.node(), nodemod.nullid)
880 newnode, ispartialunshelve = _createunshelvectx(ui, repo,
879 newnode, ispartialunshelve = _createunshelvectx(ui, repo,
881 shelvectx, basename, interactive, opts)
880 shelvectx, basename, interactive, opts)
882
881
883 if newnode is None:
882 if newnode is None:
884 # If it ended up being a no-op commit, then the normal
883 # If it ended up being a no-op commit, then the normal
885 # merge state clean-up path doesn't happen, so do it
884 # merge state clean-up path doesn't happen, so do it
886 # here. Fix issue5494
885 # here. Fix issue5494
887 merge.mergestate.clean(repo)
886 merge.mergestate.clean(repo)
888 shelvectx = tmpwctx
887 shelvectx = tmpwctx
889 msg = _('note: unshelved changes already existed '
888 msg = _('note: unshelved changes already existed '
890 'in the working copy\n')
889 'in the working copy\n')
891 ui.status(msg)
890 ui.status(msg)
892 else:
891 else:
893 shelvectx = repo[newnode]
892 shelvectx = repo[newnode]
894 hg.updaterepo(repo, tmpwctx.node(), False)
893 hg.updaterepo(repo, tmpwctx.node(), False)
895
894
896 return shelvectx, ispartialunshelve
895 return shelvectx, ispartialunshelve
897
896
898 def _forgetunknownfiles(repo, shelvectx, addedbefore):
897 def _forgetunknownfiles(repo, shelvectx, addedbefore):
899 # Forget any files that were unknown before the shelve, unknown before
898 # Forget any files that were unknown before the shelve, unknown before
900 # unshelve started, but are now added.
899 # unshelve started, but are now added.
901 shelveunknown = shelvectx.extra().get('shelve_unknown')
900 shelveunknown = shelvectx.extra().get('shelve_unknown')
902 if not shelveunknown:
901 if not shelveunknown:
903 return
902 return
904 shelveunknown = frozenset(shelveunknown.split('\0'))
903 shelveunknown = frozenset(shelveunknown.split('\0'))
905 addedafter = frozenset(repo.status().added)
904 addedafter = frozenset(repo.status().added)
906 toforget = (addedafter & shelveunknown) - addedbefore
905 toforget = (addedafter & shelveunknown) - addedbefore
907 repo[None].forget(toforget)
906 repo[None].forget(toforget)
908
907
909 def _finishunshelve(repo, oldtiprev, tr, activebookmark):
908 def _finishunshelve(repo, oldtiprev, tr, activebookmark):
910 _restoreactivebookmark(repo, activebookmark)
909 _restoreactivebookmark(repo, activebookmark)
911 # The transaction aborting will strip all the commits for us,
910 # The transaction aborting will strip all the commits for us,
912 # but it doesn't update the inmemory structures, so addchangegroup
911 # but it doesn't update the inmemory structures, so addchangegroup
913 # hooks still fire and try to operate on the missing commits.
912 # hooks still fire and try to operate on the missing commits.
914 # Clean up manually to prevent this.
913 # Clean up manually to prevent this.
915 repo.unfiltered().changelog.strip(oldtiprev, tr)
914 repo.unfiltered().changelog.strip(oldtiprev, tr)
916 _aborttransaction(repo, tr)
915 _aborttransaction(repo, tr)
917
916
918 def _checkunshelveuntrackedproblems(ui, repo, shelvectx):
917 def _checkunshelveuntrackedproblems(ui, repo, shelvectx):
919 """Check potential problems which may result from working
918 """Check potential problems which may result from working
920 copy having untracked changes."""
919 copy having untracked changes."""
921 wcdeleted = set(repo.status().deleted)
920 wcdeleted = set(repo.status().deleted)
922 shelvetouched = set(shelvectx.files())
921 shelvetouched = set(shelvectx.files())
923 intersection = wcdeleted.intersection(shelvetouched)
922 intersection = wcdeleted.intersection(shelvetouched)
924 if intersection:
923 if intersection:
925 m = _("shelved change touches missing files")
924 m = _("shelved change touches missing files")
926 hint = _("run hg status to see which files are missing")
925 hint = _("run hg status to see which files are missing")
927 raise error.Abort(m, hint=hint)
926 raise error.Abort(m, hint=hint)
928
927
929 def dounshelve(ui, repo, *shelved, **opts):
928 def dounshelve(ui, repo, *shelved, **opts):
930 opts = pycompat.byteskwargs(opts)
929 opts = pycompat.byteskwargs(opts)
931 abortf = opts.get('abort')
930 abortf = opts.get('abort')
932 continuef = opts.get('continue')
931 continuef = opts.get('continue')
933 interactive = opts.get('interactive')
932 interactive = opts.get('interactive')
934 if not abortf and not continuef:
933 if not abortf and not continuef:
935 cmdutil.checkunfinished(repo)
934 cmdutil.checkunfinished(repo)
936 shelved = list(shelved)
935 shelved = list(shelved)
937 if opts.get("name"):
936 if opts.get("name"):
938 shelved.append(opts["name"])
937 shelved.append(opts["name"])
939
938
940 if abortf or continuef and not interactive:
939 if abortf or continuef and not interactive:
941 if abortf and continuef:
940 if abortf and continuef:
942 raise error.Abort(_('cannot use both abort and continue'))
941 raise error.Abort(_('cannot use both abort and continue'))
943 if shelved:
942 if shelved:
944 raise error.Abort(_('cannot combine abort/continue with '
943 raise error.Abort(_('cannot combine abort/continue with '
945 'naming a shelved change'))
944 'naming a shelved change'))
946 if abortf and opts.get('tool', False):
945 if abortf and opts.get('tool', False):
947 ui.warn(_('tool option will be ignored\n'))
946 ui.warn(_('tool option will be ignored\n'))
948
947
949 state = _loadshelvedstate(ui, repo, opts)
948 state = _loadshelvedstate(ui, repo, opts)
950 if abortf:
949 if abortf:
951 return unshelveabort(ui, repo, state)
950 return unshelveabort(ui, repo, state)
952 elif continuef:
951 elif continuef:
953 return unshelvecontinue(ui, repo, state, opts)
952 return unshelvecontinue(ui, repo, state, opts)
954 elif len(shelved) > 1:
953 elif len(shelved) > 1:
955 raise error.Abort(_('can only unshelve one change at a time'))
954 raise error.Abort(_('can only unshelve one change at a time'))
956 elif not shelved:
955 elif not shelved:
957 shelved = listshelves(repo)
956 shelved = listshelves(repo)
958 if not shelved:
957 if not shelved:
959 raise error.Abort(_('no shelved changes to apply!'))
958 raise error.Abort(_('no shelved changes to apply!'))
960 basename = util.split(shelved[0][1])[1]
959 basename = util.split(shelved[0][1])[1]
961 ui.status(_("unshelving change '%s'\n") % basename)
960 ui.status(_("unshelving change '%s'\n") % basename)
962 elif shelved:
961 elif shelved:
963 basename = shelved[0]
962 basename = shelved[0]
964 if continuef and interactive:
963 if continuef and interactive:
965 state = _loadshelvedstate(ui, repo, opts)
964 state = _loadshelvedstate(ui, repo, opts)
966 return unshelvecontinue(ui, repo, state, opts, basename)
965 return unshelvecontinue(ui, repo, state, opts, basename)
967
966
968 if not shelvedfile(repo, basename, patchextension).exists():
967 if not shelvedfile(repo, basename, patchextension).exists():
969 raise error.Abort(_("shelved change '%s' not found") % basename)
968 raise error.Abort(_("shelved change '%s' not found") % basename)
970
969
971 repo = repo.unfiltered()
970 repo = repo.unfiltered()
972 lock = tr = None
971 lock = tr = None
973 try:
972 try:
974 lock = repo.lock()
973 lock = repo.lock()
975 tr = repo.transaction('unshelve', report=lambda x: None)
974 tr = repo.transaction('unshelve', report=lambda x: None)
976 oldtiprev = len(repo)
975 oldtiprev = len(repo)
977
976
978 pctx = repo['.']
977 pctx = repo['.']
979 tmpwctx = pctx
978 tmpwctx = pctx
980 # The goal is to have a commit structure like so:
979 # The goal is to have a commit structure like so:
981 # ...-> pctx -> tmpwctx -> shelvectx
980 # ...-> pctx -> tmpwctx -> shelvectx
982 # where tmpwctx is an optional commit with the user's pending changes
981 # where tmpwctx is an optional commit with the user's pending changes
983 # and shelvectx is the unshelved changes. Then we merge it all down
982 # and shelvectx is the unshelved changes. Then we merge it all down
984 # to the original pctx.
983 # to the original pctx.
985
984
986 activebookmark = _backupactivebookmark(repo)
985 activebookmark = _backupactivebookmark(repo)
987 tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
986 tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
988 tmpwctx)
987 tmpwctx)
989 repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename)
988 repo, shelvectx = _unshelverestorecommit(ui, repo, tr, basename)
990 _checkunshelveuntrackedproblems(ui, repo, shelvectx)
989 _checkunshelveuntrackedproblems(ui, repo, shelvectx)
991 branchtorestore = ''
990 branchtorestore = ''
992 if shelvectx.branch() != shelvectx.p1().branch():
991 if shelvectx.branch() != shelvectx.p1().branch():
993 branchtorestore = shelvectx.branch()
992 branchtorestore = shelvectx.branch()
994
993
995 shelvectx, ispartialunshelve = _rebaserestoredcommit(ui, repo, opts,
994 shelvectx, ispartialunshelve = _rebaserestoredcommit(ui, repo, opts,
996 tr, oldtiprev, basename, pctx, tmpwctx, shelvectx,
995 tr, oldtiprev, basename, pctx, tmpwctx, shelvectx,
997 branchtorestore, activebookmark)
996 branchtorestore, activebookmark)
998 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
997 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
999 with ui.configoverride(overrides, 'unshelve'):
998 with ui.configoverride(overrides, 'unshelve'):
1000 mergefiles(ui, repo, pctx, shelvectx)
999 mergefiles(ui, repo, pctx, shelvectx)
1001 restorebranch(ui, repo, branchtorestore)
1000 restorebranch(ui, repo, branchtorestore)
1002 if not ispartialunshelve:
1001 if not ispartialunshelve:
1003 _forgetunknownfiles(repo, shelvectx, addedbefore)
1002 _forgetunknownfiles(repo, shelvectx, addedbefore)
1004
1003
1005 shelvedstate.clear(repo)
1004 shelvedstate.clear(repo)
1006 _finishunshelve(repo, oldtiprev, tr, activebookmark)
1005 _finishunshelve(repo, oldtiprev, tr, activebookmark)
1007 unshelvecleanup(ui, repo, basename, opts)
1006 unshelvecleanup(ui, repo, basename, opts)
1008 finally:
1007 finally:
1009 if tr:
1008 if tr:
1010 tr.release()
1009 tr.release()
1011 lockmod.release(lock)
1010 lockmod.release(lock)
General Comments 0
You need to be logged in to leave comments. Login now