##// END OF EJS Templates
shelve: allow shelving of a change with an mq patch applied...
David Soria Parra -
r19856:28b1b7b9 default
parent child Browse files
Show More
@@ -1,3448 +1,3449 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
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 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60
60
61 This extension used to provide a strip command. This command now lives
61 This extension used to provide a strip command. This command now lives
62 in the strip extension.
62 in the strip extension.
63 '''
63 '''
64
64
65 from mercurial.i18n import _
65 from mercurial.i18n import _
66 from mercurial.node import bin, hex, short, nullid, nullrev
66 from mercurial.node import bin, hex, short, nullid, nullrev
67 from mercurial.lock import release
67 from mercurial.lock import release
68 from mercurial import commands, cmdutil, hg, scmutil, util, revset
68 from mercurial import commands, cmdutil, hg, scmutil, util, revset
69 from mercurial import extensions, error, phases
69 from mercurial import extensions, error, phases
70 from mercurial import patch as patchmod
70 from mercurial import patch as patchmod
71 from mercurial import localrepo
71 from mercurial import localrepo
72 from mercurial import subrepo
72 from mercurial import subrepo
73 import os, re, errno, shutil
73 import os, re, errno, shutil
74
74
75 commands.norepo += " qclone"
75 commands.norepo += " qclone"
76
76
77 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
77 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
78
78
79 cmdtable = {}
79 cmdtable = {}
80 command = cmdutil.command(cmdtable)
80 command = cmdutil.command(cmdtable)
81 testedwith = 'internal'
81 testedwith = 'internal'
82
82
83 # force load strip extension formely included in mq and import some utility
83 # force load strip extension formely included in mq and import some utility
84 try:
84 try:
85 stripext = extensions.find('strip')
85 stripext = extensions.find('strip')
86 except KeyError:
86 except KeyError:
87 # note: load is lazy so we could avoid the try-except,
87 # note: load is lazy so we could avoid the try-except,
88 # but I (marmoute) prefer this explicite code.
88 # but I (marmoute) prefer this explicite code.
89 class dummyui(object):
89 class dummyui(object):
90 def debug(self, msg):
90 def debug(self, msg):
91 pass
91 pass
92 stripext = extensions.load(dummyui(), 'strip', '')
92 stripext = extensions.load(dummyui(), 'strip', '')
93
93
94 strip = stripext.strip
94 strip = stripext.strip
95 checksubstate = stripext.checksubstate
95 checksubstate = stripext.checksubstate
96 checklocalchanges = stripext.checklocalchanges
96 checklocalchanges = stripext.checklocalchanges
97
97
98
98
99 # Patch names looks like unix-file names.
99 # Patch names looks like unix-file names.
100 # They must be joinable with queue directory and result in the patch path.
100 # They must be joinable with queue directory and result in the patch path.
101 normname = util.normpath
101 normname = util.normpath
102
102
103 class statusentry(object):
103 class statusentry(object):
104 def __init__(self, node, name):
104 def __init__(self, node, name):
105 self.node, self.name = node, name
105 self.node, self.name = node, name
106 def __repr__(self):
106 def __repr__(self):
107 return hex(self.node) + ':' + self.name
107 return hex(self.node) + ':' + self.name
108
108
109 class patchheader(object):
109 class patchheader(object):
110 def __init__(self, pf, plainmode=False):
110 def __init__(self, pf, plainmode=False):
111 def eatdiff(lines):
111 def eatdiff(lines):
112 while lines:
112 while lines:
113 l = lines[-1]
113 l = lines[-1]
114 if (l.startswith("diff -") or
114 if (l.startswith("diff -") or
115 l.startswith("Index:") or
115 l.startswith("Index:") or
116 l.startswith("===========")):
116 l.startswith("===========")):
117 del lines[-1]
117 del lines[-1]
118 else:
118 else:
119 break
119 break
120 def eatempty(lines):
120 def eatempty(lines):
121 while lines:
121 while lines:
122 if not lines[-1].strip():
122 if not lines[-1].strip():
123 del lines[-1]
123 del lines[-1]
124 else:
124 else:
125 break
125 break
126
126
127 message = []
127 message = []
128 comments = []
128 comments = []
129 user = None
129 user = None
130 date = None
130 date = None
131 parent = None
131 parent = None
132 format = None
132 format = None
133 subject = None
133 subject = None
134 branch = None
134 branch = None
135 nodeid = None
135 nodeid = None
136 diffstart = 0
136 diffstart = 0
137
137
138 for line in file(pf):
138 for line in file(pf):
139 line = line.rstrip()
139 line = line.rstrip()
140 if (line.startswith('diff --git')
140 if (line.startswith('diff --git')
141 or (diffstart and line.startswith('+++ '))):
141 or (diffstart and line.startswith('+++ '))):
142 diffstart = 2
142 diffstart = 2
143 break
143 break
144 diffstart = 0 # reset
144 diffstart = 0 # reset
145 if line.startswith("--- "):
145 if line.startswith("--- "):
146 diffstart = 1
146 diffstart = 1
147 continue
147 continue
148 elif format == "hgpatch":
148 elif format == "hgpatch":
149 # parse values when importing the result of an hg export
149 # parse values when importing the result of an hg export
150 if line.startswith("# User "):
150 if line.startswith("# User "):
151 user = line[7:]
151 user = line[7:]
152 elif line.startswith("# Date "):
152 elif line.startswith("# Date "):
153 date = line[7:]
153 date = line[7:]
154 elif line.startswith("# Parent "):
154 elif line.startswith("# Parent "):
155 parent = line[9:].lstrip()
155 parent = line[9:].lstrip()
156 elif line.startswith("# Branch "):
156 elif line.startswith("# Branch "):
157 branch = line[9:]
157 branch = line[9:]
158 elif line.startswith("# Node ID "):
158 elif line.startswith("# Node ID "):
159 nodeid = line[10:]
159 nodeid = line[10:]
160 elif not line.startswith("# ") and line:
160 elif not line.startswith("# ") and line:
161 message.append(line)
161 message.append(line)
162 format = None
162 format = None
163 elif line == '# HG changeset patch':
163 elif line == '# HG changeset patch':
164 message = []
164 message = []
165 format = "hgpatch"
165 format = "hgpatch"
166 elif (format != "tagdone" and (line.startswith("Subject: ") or
166 elif (format != "tagdone" and (line.startswith("Subject: ") or
167 line.startswith("subject: "))):
167 line.startswith("subject: "))):
168 subject = line[9:]
168 subject = line[9:]
169 format = "tag"
169 format = "tag"
170 elif (format != "tagdone" and (line.startswith("From: ") or
170 elif (format != "tagdone" and (line.startswith("From: ") or
171 line.startswith("from: "))):
171 line.startswith("from: "))):
172 user = line[6:]
172 user = line[6:]
173 format = "tag"
173 format = "tag"
174 elif (format != "tagdone" and (line.startswith("Date: ") or
174 elif (format != "tagdone" and (line.startswith("Date: ") or
175 line.startswith("date: "))):
175 line.startswith("date: "))):
176 date = line[6:]
176 date = line[6:]
177 format = "tag"
177 format = "tag"
178 elif format == "tag" and line == "":
178 elif format == "tag" and line == "":
179 # when looking for tags (subject: from: etc) they
179 # when looking for tags (subject: from: etc) they
180 # end once you find a blank line in the source
180 # end once you find a blank line in the source
181 format = "tagdone"
181 format = "tagdone"
182 elif message or line:
182 elif message or line:
183 message.append(line)
183 message.append(line)
184 comments.append(line)
184 comments.append(line)
185
185
186 eatdiff(message)
186 eatdiff(message)
187 eatdiff(comments)
187 eatdiff(comments)
188 # Remember the exact starting line of the patch diffs before consuming
188 # Remember the exact starting line of the patch diffs before consuming
189 # empty lines, for external use by TortoiseHg and others
189 # empty lines, for external use by TortoiseHg and others
190 self.diffstartline = len(comments)
190 self.diffstartline = len(comments)
191 eatempty(message)
191 eatempty(message)
192 eatempty(comments)
192 eatempty(comments)
193
193
194 # make sure message isn't empty
194 # make sure message isn't empty
195 if format and format.startswith("tag") and subject:
195 if format and format.startswith("tag") and subject:
196 message.insert(0, "")
196 message.insert(0, "")
197 message.insert(0, subject)
197 message.insert(0, subject)
198
198
199 self.message = message
199 self.message = message
200 self.comments = comments
200 self.comments = comments
201 self.user = user
201 self.user = user
202 self.date = date
202 self.date = date
203 self.parent = parent
203 self.parent = parent
204 # nodeid and branch are for external use by TortoiseHg and others
204 # nodeid and branch are for external use by TortoiseHg and others
205 self.nodeid = nodeid
205 self.nodeid = nodeid
206 self.branch = branch
206 self.branch = branch
207 self.haspatch = diffstart > 1
207 self.haspatch = diffstart > 1
208 self.plainmode = plainmode
208 self.plainmode = plainmode
209
209
210 def setuser(self, user):
210 def setuser(self, user):
211 if not self.updateheader(['From: ', '# User '], user):
211 if not self.updateheader(['From: ', '# User '], user):
212 try:
212 try:
213 patchheaderat = self.comments.index('# HG changeset patch')
213 patchheaderat = self.comments.index('# HG changeset patch')
214 self.comments.insert(patchheaderat + 1, '# User ' + user)
214 self.comments.insert(patchheaderat + 1, '# User ' + user)
215 except ValueError:
215 except ValueError:
216 if self.plainmode or self._hasheader(['Date: ']):
216 if self.plainmode or self._hasheader(['Date: ']):
217 self.comments = ['From: ' + user] + self.comments
217 self.comments = ['From: ' + user] + self.comments
218 else:
218 else:
219 tmp = ['# HG changeset patch', '# User ' + user, '']
219 tmp = ['# HG changeset patch', '# User ' + user, '']
220 self.comments = tmp + self.comments
220 self.comments = tmp + self.comments
221 self.user = user
221 self.user = user
222
222
223 def setdate(self, date):
223 def setdate(self, date):
224 if not self.updateheader(['Date: ', '# Date '], date):
224 if not self.updateheader(['Date: ', '# Date '], date):
225 try:
225 try:
226 patchheaderat = self.comments.index('# HG changeset patch')
226 patchheaderat = self.comments.index('# HG changeset patch')
227 self.comments.insert(patchheaderat + 1, '# Date ' + date)
227 self.comments.insert(patchheaderat + 1, '# Date ' + date)
228 except ValueError:
228 except ValueError:
229 if self.plainmode or self._hasheader(['From: ']):
229 if self.plainmode or self._hasheader(['From: ']):
230 self.comments = ['Date: ' + date] + self.comments
230 self.comments = ['Date: ' + date] + self.comments
231 else:
231 else:
232 tmp = ['# HG changeset patch', '# Date ' + date, '']
232 tmp = ['# HG changeset patch', '# Date ' + date, '']
233 self.comments = tmp + self.comments
233 self.comments = tmp + self.comments
234 self.date = date
234 self.date = date
235
235
236 def setparent(self, parent):
236 def setparent(self, parent):
237 if not self.updateheader(['# Parent '], parent):
237 if not self.updateheader(['# Parent '], parent):
238 try:
238 try:
239 patchheaderat = self.comments.index('# HG changeset patch')
239 patchheaderat = self.comments.index('# HG changeset patch')
240 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
240 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
241 except ValueError:
241 except ValueError:
242 pass
242 pass
243 self.parent = parent
243 self.parent = parent
244
244
245 def setmessage(self, message):
245 def setmessage(self, message):
246 if self.comments:
246 if self.comments:
247 self._delmsg()
247 self._delmsg()
248 self.message = [message]
248 self.message = [message]
249 self.comments += self.message
249 self.comments += self.message
250
250
251 def updateheader(self, prefixes, new):
251 def updateheader(self, prefixes, new):
252 '''Update all references to a field in the patch header.
252 '''Update all references to a field in the patch header.
253 Return whether the field is present.'''
253 Return whether the field is present.'''
254 res = False
254 res = False
255 for prefix in prefixes:
255 for prefix in prefixes:
256 for i in xrange(len(self.comments)):
256 for i in xrange(len(self.comments)):
257 if self.comments[i].startswith(prefix):
257 if self.comments[i].startswith(prefix):
258 self.comments[i] = prefix + new
258 self.comments[i] = prefix + new
259 res = True
259 res = True
260 break
260 break
261 return res
261 return res
262
262
263 def _hasheader(self, prefixes):
263 def _hasheader(self, prefixes):
264 '''Check if a header starts with any of the given prefixes.'''
264 '''Check if a header starts with any of the given prefixes.'''
265 for prefix in prefixes:
265 for prefix in prefixes:
266 for comment in self.comments:
266 for comment in self.comments:
267 if comment.startswith(prefix):
267 if comment.startswith(prefix):
268 return True
268 return True
269 return False
269 return False
270
270
271 def __str__(self):
271 def __str__(self):
272 if not self.comments:
272 if not self.comments:
273 return ''
273 return ''
274 return '\n'.join(self.comments) + '\n\n'
274 return '\n'.join(self.comments) + '\n\n'
275
275
276 def _delmsg(self):
276 def _delmsg(self):
277 '''Remove existing message, keeping the rest of the comments fields.
277 '''Remove existing message, keeping the rest of the comments fields.
278 If comments contains 'subject: ', message will prepend
278 If comments contains 'subject: ', message will prepend
279 the field and a blank line.'''
279 the field and a blank line.'''
280 if self.message:
280 if self.message:
281 subj = 'subject: ' + self.message[0].lower()
281 subj = 'subject: ' + self.message[0].lower()
282 for i in xrange(len(self.comments)):
282 for i in xrange(len(self.comments)):
283 if subj == self.comments[i].lower():
283 if subj == self.comments[i].lower():
284 del self.comments[i]
284 del self.comments[i]
285 self.message = self.message[2:]
285 self.message = self.message[2:]
286 break
286 break
287 ci = 0
287 ci = 0
288 for mi in self.message:
288 for mi in self.message:
289 while mi != self.comments[ci]:
289 while mi != self.comments[ci]:
290 ci += 1
290 ci += 1
291 del self.comments[ci]
291 del self.comments[ci]
292
292
293 def newcommit(repo, phase, *args, **kwargs):
293 def newcommit(repo, phase, *args, **kwargs):
294 """helper dedicated to ensure a commit respect mq.secret setting
294 """helper dedicated to ensure a commit respect mq.secret setting
295
295
296 It should be used instead of repo.commit inside the mq source for operation
296 It should be used instead of repo.commit inside the mq source for operation
297 creating new changeset.
297 creating new changeset.
298 """
298 """
299 repo = repo.unfiltered()
299 repo = repo.unfiltered()
300 if phase is None:
300 if phase is None:
301 if repo.ui.configbool('mq', 'secret', False):
301 if repo.ui.configbool('mq', 'secret', False):
302 phase = phases.secret
302 phase = phases.secret
303 if phase is not None:
303 if phase is not None:
304 backup = repo.ui.backupconfig('phases', 'new-commit')
304 backup = repo.ui.backupconfig('phases', 'new-commit')
305 try:
305 try:
306 if phase is not None:
306 if phase is not None:
307 repo.ui.setconfig('phases', 'new-commit', phase)
307 repo.ui.setconfig('phases', 'new-commit', phase)
308 return repo.commit(*args, **kwargs)
308 return repo.commit(*args, **kwargs)
309 finally:
309 finally:
310 if phase is not None:
310 if phase is not None:
311 repo.ui.restoreconfig(backup)
311 repo.ui.restoreconfig(backup)
312
312
313 class AbortNoCleanup(error.Abort):
313 class AbortNoCleanup(error.Abort):
314 pass
314 pass
315
315
316 class queue(object):
316 class queue(object):
317 def __init__(self, ui, baseui, path, patchdir=None):
317 def __init__(self, ui, baseui, path, patchdir=None):
318 self.basepath = path
318 self.basepath = path
319 try:
319 try:
320 fh = open(os.path.join(path, 'patches.queue'))
320 fh = open(os.path.join(path, 'patches.queue'))
321 cur = fh.read().rstrip()
321 cur = fh.read().rstrip()
322 fh.close()
322 fh.close()
323 if not cur:
323 if not cur:
324 curpath = os.path.join(path, 'patches')
324 curpath = os.path.join(path, 'patches')
325 else:
325 else:
326 curpath = os.path.join(path, 'patches-' + cur)
326 curpath = os.path.join(path, 'patches-' + cur)
327 except IOError:
327 except IOError:
328 curpath = os.path.join(path, 'patches')
328 curpath = os.path.join(path, 'patches')
329 self.path = patchdir or curpath
329 self.path = patchdir or curpath
330 self.opener = scmutil.opener(self.path)
330 self.opener = scmutil.opener(self.path)
331 self.ui = ui
331 self.ui = ui
332 self.baseui = baseui
332 self.baseui = baseui
333 self.applieddirty = False
333 self.applieddirty = False
334 self.seriesdirty = False
334 self.seriesdirty = False
335 self.added = []
335 self.added = []
336 self.seriespath = "series"
336 self.seriespath = "series"
337 self.statuspath = "status"
337 self.statuspath = "status"
338 self.guardspath = "guards"
338 self.guardspath = "guards"
339 self.activeguards = None
339 self.activeguards = None
340 self.guardsdirty = False
340 self.guardsdirty = False
341 # Handle mq.git as a bool with extended values
341 # Handle mq.git as a bool with extended values
342 try:
342 try:
343 gitmode = ui.configbool('mq', 'git', None)
343 gitmode = ui.configbool('mq', 'git', None)
344 if gitmode is None:
344 if gitmode is None:
345 raise error.ConfigError
345 raise error.ConfigError
346 self.gitmode = gitmode and 'yes' or 'no'
346 self.gitmode = gitmode and 'yes' or 'no'
347 except error.ConfigError:
347 except error.ConfigError:
348 self.gitmode = ui.config('mq', 'git', 'auto').lower()
348 self.gitmode = ui.config('mq', 'git', 'auto').lower()
349 self.plainmode = ui.configbool('mq', 'plain', False)
349 self.plainmode = ui.configbool('mq', 'plain', False)
350 self.checkapplied = True
350
351
351 @util.propertycache
352 @util.propertycache
352 def applied(self):
353 def applied(self):
353 def parselines(lines):
354 def parselines(lines):
354 for l in lines:
355 for l in lines:
355 entry = l.split(':', 1)
356 entry = l.split(':', 1)
356 if len(entry) > 1:
357 if len(entry) > 1:
357 n, name = entry
358 n, name = entry
358 yield statusentry(bin(n), name)
359 yield statusentry(bin(n), name)
359 elif l.strip():
360 elif l.strip():
360 self.ui.warn(_('malformated mq status line: %s\n') % entry)
361 self.ui.warn(_('malformated mq status line: %s\n') % entry)
361 # else we ignore empty lines
362 # else we ignore empty lines
362 try:
363 try:
363 lines = self.opener.read(self.statuspath).splitlines()
364 lines = self.opener.read(self.statuspath).splitlines()
364 return list(parselines(lines))
365 return list(parselines(lines))
365 except IOError, e:
366 except IOError, e:
366 if e.errno == errno.ENOENT:
367 if e.errno == errno.ENOENT:
367 return []
368 return []
368 raise
369 raise
369
370
370 @util.propertycache
371 @util.propertycache
371 def fullseries(self):
372 def fullseries(self):
372 try:
373 try:
373 return self.opener.read(self.seriespath).splitlines()
374 return self.opener.read(self.seriespath).splitlines()
374 except IOError, e:
375 except IOError, e:
375 if e.errno == errno.ENOENT:
376 if e.errno == errno.ENOENT:
376 return []
377 return []
377 raise
378 raise
378
379
379 @util.propertycache
380 @util.propertycache
380 def series(self):
381 def series(self):
381 self.parseseries()
382 self.parseseries()
382 return self.series
383 return self.series
383
384
384 @util.propertycache
385 @util.propertycache
385 def seriesguards(self):
386 def seriesguards(self):
386 self.parseseries()
387 self.parseseries()
387 return self.seriesguards
388 return self.seriesguards
388
389
389 def invalidate(self):
390 def invalidate(self):
390 for a in 'applied fullseries series seriesguards'.split():
391 for a in 'applied fullseries series seriesguards'.split():
391 if a in self.__dict__:
392 if a in self.__dict__:
392 delattr(self, a)
393 delattr(self, a)
393 self.applieddirty = False
394 self.applieddirty = False
394 self.seriesdirty = False
395 self.seriesdirty = False
395 self.guardsdirty = False
396 self.guardsdirty = False
396 self.activeguards = None
397 self.activeguards = None
397
398
398 def diffopts(self, opts={}, patchfn=None):
399 def diffopts(self, opts={}, patchfn=None):
399 diffopts = patchmod.diffopts(self.ui, opts)
400 diffopts = patchmod.diffopts(self.ui, opts)
400 if self.gitmode == 'auto':
401 if self.gitmode == 'auto':
401 diffopts.upgrade = True
402 diffopts.upgrade = True
402 elif self.gitmode == 'keep':
403 elif self.gitmode == 'keep':
403 pass
404 pass
404 elif self.gitmode in ('yes', 'no'):
405 elif self.gitmode in ('yes', 'no'):
405 diffopts.git = self.gitmode == 'yes'
406 diffopts.git = self.gitmode == 'yes'
406 else:
407 else:
407 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
408 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
408 ' got %s') % self.gitmode)
409 ' got %s') % self.gitmode)
409 if patchfn:
410 if patchfn:
410 diffopts = self.patchopts(diffopts, patchfn)
411 diffopts = self.patchopts(diffopts, patchfn)
411 return diffopts
412 return diffopts
412
413
413 def patchopts(self, diffopts, *patches):
414 def patchopts(self, diffopts, *patches):
414 """Return a copy of input diff options with git set to true if
415 """Return a copy of input diff options with git set to true if
415 referenced patch is a git patch and should be preserved as such.
416 referenced patch is a git patch and should be preserved as such.
416 """
417 """
417 diffopts = diffopts.copy()
418 diffopts = diffopts.copy()
418 if not diffopts.git and self.gitmode == 'keep':
419 if not diffopts.git and self.gitmode == 'keep':
419 for patchfn in patches:
420 for patchfn in patches:
420 patchf = self.opener(patchfn, 'r')
421 patchf = self.opener(patchfn, 'r')
421 # if the patch was a git patch, refresh it as a git patch
422 # if the patch was a git patch, refresh it as a git patch
422 for line in patchf:
423 for line in patchf:
423 if line.startswith('diff --git'):
424 if line.startswith('diff --git'):
424 diffopts.git = True
425 diffopts.git = True
425 break
426 break
426 patchf.close()
427 patchf.close()
427 return diffopts
428 return diffopts
428
429
429 def join(self, *p):
430 def join(self, *p):
430 return os.path.join(self.path, *p)
431 return os.path.join(self.path, *p)
431
432
432 def findseries(self, patch):
433 def findseries(self, patch):
433 def matchpatch(l):
434 def matchpatch(l):
434 l = l.split('#', 1)[0]
435 l = l.split('#', 1)[0]
435 return l.strip() == patch
436 return l.strip() == patch
436 for index, l in enumerate(self.fullseries):
437 for index, l in enumerate(self.fullseries):
437 if matchpatch(l):
438 if matchpatch(l):
438 return index
439 return index
439 return None
440 return None
440
441
441 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
442 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
442
443
443 def parseseries(self):
444 def parseseries(self):
444 self.series = []
445 self.series = []
445 self.seriesguards = []
446 self.seriesguards = []
446 for l in self.fullseries:
447 for l in self.fullseries:
447 h = l.find('#')
448 h = l.find('#')
448 if h == -1:
449 if h == -1:
449 patch = l
450 patch = l
450 comment = ''
451 comment = ''
451 elif h == 0:
452 elif h == 0:
452 continue
453 continue
453 else:
454 else:
454 patch = l[:h]
455 patch = l[:h]
455 comment = l[h:]
456 comment = l[h:]
456 patch = patch.strip()
457 patch = patch.strip()
457 if patch:
458 if patch:
458 if patch in self.series:
459 if patch in self.series:
459 raise util.Abort(_('%s appears more than once in %s') %
460 raise util.Abort(_('%s appears more than once in %s') %
460 (patch, self.join(self.seriespath)))
461 (patch, self.join(self.seriespath)))
461 self.series.append(patch)
462 self.series.append(patch)
462 self.seriesguards.append(self.guard_re.findall(comment))
463 self.seriesguards.append(self.guard_re.findall(comment))
463
464
464 def checkguard(self, guard):
465 def checkguard(self, guard):
465 if not guard:
466 if not guard:
466 return _('guard cannot be an empty string')
467 return _('guard cannot be an empty string')
467 bad_chars = '# \t\r\n\f'
468 bad_chars = '# \t\r\n\f'
468 first = guard[0]
469 first = guard[0]
469 if first in '-+':
470 if first in '-+':
470 return (_('guard %r starts with invalid character: %r') %
471 return (_('guard %r starts with invalid character: %r') %
471 (guard, first))
472 (guard, first))
472 for c in bad_chars:
473 for c in bad_chars:
473 if c in guard:
474 if c in guard:
474 return _('invalid character in guard %r: %r') % (guard, c)
475 return _('invalid character in guard %r: %r') % (guard, c)
475
476
476 def setactive(self, guards):
477 def setactive(self, guards):
477 for guard in guards:
478 for guard in guards:
478 bad = self.checkguard(guard)
479 bad = self.checkguard(guard)
479 if bad:
480 if bad:
480 raise util.Abort(bad)
481 raise util.Abort(bad)
481 guards = sorted(set(guards))
482 guards = sorted(set(guards))
482 self.ui.debug('active guards: %s\n' % ' '.join(guards))
483 self.ui.debug('active guards: %s\n' % ' '.join(guards))
483 self.activeguards = guards
484 self.activeguards = guards
484 self.guardsdirty = True
485 self.guardsdirty = True
485
486
486 def active(self):
487 def active(self):
487 if self.activeguards is None:
488 if self.activeguards is None:
488 self.activeguards = []
489 self.activeguards = []
489 try:
490 try:
490 guards = self.opener.read(self.guardspath).split()
491 guards = self.opener.read(self.guardspath).split()
491 except IOError, err:
492 except IOError, err:
492 if err.errno != errno.ENOENT:
493 if err.errno != errno.ENOENT:
493 raise
494 raise
494 guards = []
495 guards = []
495 for i, guard in enumerate(guards):
496 for i, guard in enumerate(guards):
496 bad = self.checkguard(guard)
497 bad = self.checkguard(guard)
497 if bad:
498 if bad:
498 self.ui.warn('%s:%d: %s\n' %
499 self.ui.warn('%s:%d: %s\n' %
499 (self.join(self.guardspath), i + 1, bad))
500 (self.join(self.guardspath), i + 1, bad))
500 else:
501 else:
501 self.activeguards.append(guard)
502 self.activeguards.append(guard)
502 return self.activeguards
503 return self.activeguards
503
504
504 def setguards(self, idx, guards):
505 def setguards(self, idx, guards):
505 for g in guards:
506 for g in guards:
506 if len(g) < 2:
507 if len(g) < 2:
507 raise util.Abort(_('guard %r too short') % g)
508 raise util.Abort(_('guard %r too short') % g)
508 if g[0] not in '-+':
509 if g[0] not in '-+':
509 raise util.Abort(_('guard %r starts with invalid char') % g)
510 raise util.Abort(_('guard %r starts with invalid char') % g)
510 bad = self.checkguard(g[1:])
511 bad = self.checkguard(g[1:])
511 if bad:
512 if bad:
512 raise util.Abort(bad)
513 raise util.Abort(bad)
513 drop = self.guard_re.sub('', self.fullseries[idx])
514 drop = self.guard_re.sub('', self.fullseries[idx])
514 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
515 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
515 self.parseseries()
516 self.parseseries()
516 self.seriesdirty = True
517 self.seriesdirty = True
517
518
518 def pushable(self, idx):
519 def pushable(self, idx):
519 if isinstance(idx, str):
520 if isinstance(idx, str):
520 idx = self.series.index(idx)
521 idx = self.series.index(idx)
521 patchguards = self.seriesguards[idx]
522 patchguards = self.seriesguards[idx]
522 if not patchguards:
523 if not patchguards:
523 return True, None
524 return True, None
524 guards = self.active()
525 guards = self.active()
525 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
526 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
526 if exactneg:
527 if exactneg:
527 return False, repr(exactneg[0])
528 return False, repr(exactneg[0])
528 pos = [g for g in patchguards if g[0] == '+']
529 pos = [g for g in patchguards if g[0] == '+']
529 exactpos = [g for g in pos if g[1:] in guards]
530 exactpos = [g for g in pos if g[1:] in guards]
530 if pos:
531 if pos:
531 if exactpos:
532 if exactpos:
532 return True, repr(exactpos[0])
533 return True, repr(exactpos[0])
533 return False, ' '.join(map(repr, pos))
534 return False, ' '.join(map(repr, pos))
534 return True, ''
535 return True, ''
535
536
536 def explainpushable(self, idx, all_patches=False):
537 def explainpushable(self, idx, all_patches=False):
537 write = all_patches and self.ui.write or self.ui.warn
538 write = all_patches and self.ui.write or self.ui.warn
538 if all_patches or self.ui.verbose:
539 if all_patches or self.ui.verbose:
539 if isinstance(idx, str):
540 if isinstance(idx, str):
540 idx = self.series.index(idx)
541 idx = self.series.index(idx)
541 pushable, why = self.pushable(idx)
542 pushable, why = self.pushable(idx)
542 if all_patches and pushable:
543 if all_patches and pushable:
543 if why is None:
544 if why is None:
544 write(_('allowing %s - no guards in effect\n') %
545 write(_('allowing %s - no guards in effect\n') %
545 self.series[idx])
546 self.series[idx])
546 else:
547 else:
547 if not why:
548 if not why:
548 write(_('allowing %s - no matching negative guards\n') %
549 write(_('allowing %s - no matching negative guards\n') %
549 self.series[idx])
550 self.series[idx])
550 else:
551 else:
551 write(_('allowing %s - guarded by %s\n') %
552 write(_('allowing %s - guarded by %s\n') %
552 (self.series[idx], why))
553 (self.series[idx], why))
553 if not pushable:
554 if not pushable:
554 if why:
555 if why:
555 write(_('skipping %s - guarded by %s\n') %
556 write(_('skipping %s - guarded by %s\n') %
556 (self.series[idx], why))
557 (self.series[idx], why))
557 else:
558 else:
558 write(_('skipping %s - no matching guards\n') %
559 write(_('skipping %s - no matching guards\n') %
559 self.series[idx])
560 self.series[idx])
560
561
561 def savedirty(self):
562 def savedirty(self):
562 def writelist(items, path):
563 def writelist(items, path):
563 fp = self.opener(path, 'w')
564 fp = self.opener(path, 'w')
564 for i in items:
565 for i in items:
565 fp.write("%s\n" % i)
566 fp.write("%s\n" % i)
566 fp.close()
567 fp.close()
567 if self.applieddirty:
568 if self.applieddirty:
568 writelist(map(str, self.applied), self.statuspath)
569 writelist(map(str, self.applied), self.statuspath)
569 self.applieddirty = False
570 self.applieddirty = False
570 if self.seriesdirty:
571 if self.seriesdirty:
571 writelist(self.fullseries, self.seriespath)
572 writelist(self.fullseries, self.seriespath)
572 self.seriesdirty = False
573 self.seriesdirty = False
573 if self.guardsdirty:
574 if self.guardsdirty:
574 writelist(self.activeguards, self.guardspath)
575 writelist(self.activeguards, self.guardspath)
575 self.guardsdirty = False
576 self.guardsdirty = False
576 if self.added:
577 if self.added:
577 qrepo = self.qrepo()
578 qrepo = self.qrepo()
578 if qrepo:
579 if qrepo:
579 qrepo[None].add(f for f in self.added if f not in qrepo[None])
580 qrepo[None].add(f for f in self.added if f not in qrepo[None])
580 self.added = []
581 self.added = []
581
582
582 def removeundo(self, repo):
583 def removeundo(self, repo):
583 undo = repo.sjoin('undo')
584 undo = repo.sjoin('undo')
584 if not os.path.exists(undo):
585 if not os.path.exists(undo):
585 return
586 return
586 try:
587 try:
587 os.unlink(undo)
588 os.unlink(undo)
588 except OSError, inst:
589 except OSError, inst:
589 self.ui.warn(_('error removing undo: %s\n') % str(inst))
590 self.ui.warn(_('error removing undo: %s\n') % str(inst))
590
591
591 def backup(self, repo, files, copy=False):
592 def backup(self, repo, files, copy=False):
592 # backup local changes in --force case
593 # backup local changes in --force case
593 for f in sorted(files):
594 for f in sorted(files):
594 absf = repo.wjoin(f)
595 absf = repo.wjoin(f)
595 if os.path.lexists(absf):
596 if os.path.lexists(absf):
596 self.ui.note(_('saving current version of %s as %s\n') %
597 self.ui.note(_('saving current version of %s as %s\n') %
597 (f, f + '.orig'))
598 (f, f + '.orig'))
598 if copy:
599 if copy:
599 util.copyfile(absf, absf + '.orig')
600 util.copyfile(absf, absf + '.orig')
600 else:
601 else:
601 util.rename(absf, absf + '.orig')
602 util.rename(absf, absf + '.orig')
602
603
603 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
604 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
604 fp=None, changes=None, opts={}):
605 fp=None, changes=None, opts={}):
605 stat = opts.get('stat')
606 stat = opts.get('stat')
606 m = scmutil.match(repo[node1], files, opts)
607 m = scmutil.match(repo[node1], files, opts)
607 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
608 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
608 changes, stat, fp)
609 changes, stat, fp)
609
610
610 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
611 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
611 # first try just applying the patch
612 # first try just applying the patch
612 (err, n) = self.apply(repo, [patch], update_status=False,
613 (err, n) = self.apply(repo, [patch], update_status=False,
613 strict=True, merge=rev)
614 strict=True, merge=rev)
614
615
615 if err == 0:
616 if err == 0:
616 return (err, n)
617 return (err, n)
617
618
618 if n is None:
619 if n is None:
619 raise util.Abort(_("apply failed for patch %s") % patch)
620 raise util.Abort(_("apply failed for patch %s") % patch)
620
621
621 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
622 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
622
623
623 # apply failed, strip away that rev and merge.
624 # apply failed, strip away that rev and merge.
624 hg.clean(repo, head)
625 hg.clean(repo, head)
625 strip(self.ui, repo, [n], update=False, backup='strip')
626 strip(self.ui, repo, [n], update=False, backup='strip')
626
627
627 ctx = repo[rev]
628 ctx = repo[rev]
628 ret = hg.merge(repo, rev)
629 ret = hg.merge(repo, rev)
629 if ret:
630 if ret:
630 raise util.Abort(_("update returned %d") % ret)
631 raise util.Abort(_("update returned %d") % ret)
631 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
632 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
632 if n is None:
633 if n is None:
633 raise util.Abort(_("repo commit failed"))
634 raise util.Abort(_("repo commit failed"))
634 try:
635 try:
635 ph = patchheader(mergeq.join(patch), self.plainmode)
636 ph = patchheader(mergeq.join(patch), self.plainmode)
636 except Exception:
637 except Exception:
637 raise util.Abort(_("unable to read %s") % patch)
638 raise util.Abort(_("unable to read %s") % patch)
638
639
639 diffopts = self.patchopts(diffopts, patch)
640 diffopts = self.patchopts(diffopts, patch)
640 patchf = self.opener(patch, "w")
641 patchf = self.opener(patch, "w")
641 comments = str(ph)
642 comments = str(ph)
642 if comments:
643 if comments:
643 patchf.write(comments)
644 patchf.write(comments)
644 self.printdiff(repo, diffopts, head, n, fp=patchf)
645 self.printdiff(repo, diffopts, head, n, fp=patchf)
645 patchf.close()
646 patchf.close()
646 self.removeundo(repo)
647 self.removeundo(repo)
647 return (0, n)
648 return (0, n)
648
649
649 def qparents(self, repo, rev=None):
650 def qparents(self, repo, rev=None):
650 """return the mq handled parent or p1
651 """return the mq handled parent or p1
651
652
652 In some case where mq get himself in being the parent of a merge the
653 In some case where mq get himself in being the parent of a merge the
653 paappropriate parent may be p2.
654 paappropriate parent may be p2.
654 (eg: an in progress merge started with mq disabled)
655 (eg: an in progress merge started with mq disabled)
655
656
656 If no parent are managed by mq, p1 is returned.
657 If no parent are managed by mq, p1 is returned.
657 """
658 """
658 if rev is None:
659 if rev is None:
659 (p1, p2) = repo.dirstate.parents()
660 (p1, p2) = repo.dirstate.parents()
660 if p2 == nullid:
661 if p2 == nullid:
661 return p1
662 return p1
662 if not self.applied:
663 if not self.applied:
663 return None
664 return None
664 return self.applied[-1].node
665 return self.applied[-1].node
665 p1, p2 = repo.changelog.parents(rev)
666 p1, p2 = repo.changelog.parents(rev)
666 if p2 != nullid and p2 in [x.node for x in self.applied]:
667 if p2 != nullid and p2 in [x.node for x in self.applied]:
667 return p2
668 return p2
668 return p1
669 return p1
669
670
670 def mergepatch(self, repo, mergeq, series, diffopts):
671 def mergepatch(self, repo, mergeq, series, diffopts):
671 if not self.applied:
672 if not self.applied:
672 # each of the patches merged in will have two parents. This
673 # each of the patches merged in will have two parents. This
673 # can confuse the qrefresh, qdiff, and strip code because it
674 # can confuse the qrefresh, qdiff, and strip code because it
674 # needs to know which parent is actually in the patch queue.
675 # needs to know which parent is actually in the patch queue.
675 # so, we insert a merge marker with only one parent. This way
676 # so, we insert a merge marker with only one parent. This way
676 # the first patch in the queue is never a merge patch
677 # the first patch in the queue is never a merge patch
677 #
678 #
678 pname = ".hg.patches.merge.marker"
679 pname = ".hg.patches.merge.marker"
679 n = newcommit(repo, None, '[mq]: merge marker', force=True)
680 n = newcommit(repo, None, '[mq]: merge marker', force=True)
680 self.removeundo(repo)
681 self.removeundo(repo)
681 self.applied.append(statusentry(n, pname))
682 self.applied.append(statusentry(n, pname))
682 self.applieddirty = True
683 self.applieddirty = True
683
684
684 head = self.qparents(repo)
685 head = self.qparents(repo)
685
686
686 for patch in series:
687 for patch in series:
687 patch = mergeq.lookup(patch, strict=True)
688 patch = mergeq.lookup(patch, strict=True)
688 if not patch:
689 if not patch:
689 self.ui.warn(_("patch %s does not exist\n") % patch)
690 self.ui.warn(_("patch %s does not exist\n") % patch)
690 return (1, None)
691 return (1, None)
691 pushable, reason = self.pushable(patch)
692 pushable, reason = self.pushable(patch)
692 if not pushable:
693 if not pushable:
693 self.explainpushable(patch, all_patches=True)
694 self.explainpushable(patch, all_patches=True)
694 continue
695 continue
695 info = mergeq.isapplied(patch)
696 info = mergeq.isapplied(patch)
696 if not info:
697 if not info:
697 self.ui.warn(_("patch %s is not applied\n") % patch)
698 self.ui.warn(_("patch %s is not applied\n") % patch)
698 return (1, None)
699 return (1, None)
699 rev = info[1]
700 rev = info[1]
700 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
701 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
701 if head:
702 if head:
702 self.applied.append(statusentry(head, patch))
703 self.applied.append(statusentry(head, patch))
703 self.applieddirty = True
704 self.applieddirty = True
704 if err:
705 if err:
705 return (err, head)
706 return (err, head)
706 self.savedirty()
707 self.savedirty()
707 return (0, head)
708 return (0, head)
708
709
709 def patch(self, repo, patchfile):
710 def patch(self, repo, patchfile):
710 '''Apply patchfile to the working directory.
711 '''Apply patchfile to the working directory.
711 patchfile: name of patch file'''
712 patchfile: name of patch file'''
712 files = set()
713 files = set()
713 try:
714 try:
714 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
715 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
715 files=files, eolmode=None)
716 files=files, eolmode=None)
716 return (True, list(files), fuzz)
717 return (True, list(files), fuzz)
717 except Exception, inst:
718 except Exception, inst:
718 self.ui.note(str(inst) + '\n')
719 self.ui.note(str(inst) + '\n')
719 if not self.ui.verbose:
720 if not self.ui.verbose:
720 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
721 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
721 self.ui.traceback()
722 self.ui.traceback()
722 return (False, list(files), False)
723 return (False, list(files), False)
723
724
724 def apply(self, repo, series, list=False, update_status=True,
725 def apply(self, repo, series, list=False, update_status=True,
725 strict=False, patchdir=None, merge=None, all_files=None,
726 strict=False, patchdir=None, merge=None, all_files=None,
726 tobackup=None, keepchanges=False):
727 tobackup=None, keepchanges=False):
727 wlock = lock = tr = None
728 wlock = lock = tr = None
728 try:
729 try:
729 wlock = repo.wlock()
730 wlock = repo.wlock()
730 lock = repo.lock()
731 lock = repo.lock()
731 tr = repo.transaction("qpush")
732 tr = repo.transaction("qpush")
732 try:
733 try:
733 ret = self._apply(repo, series, list, update_status,
734 ret = self._apply(repo, series, list, update_status,
734 strict, patchdir, merge, all_files=all_files,
735 strict, patchdir, merge, all_files=all_files,
735 tobackup=tobackup, keepchanges=keepchanges)
736 tobackup=tobackup, keepchanges=keepchanges)
736 tr.close()
737 tr.close()
737 self.savedirty()
738 self.savedirty()
738 return ret
739 return ret
739 except AbortNoCleanup:
740 except AbortNoCleanup:
740 tr.close()
741 tr.close()
741 self.savedirty()
742 self.savedirty()
742 return 2, repo.dirstate.p1()
743 return 2, repo.dirstate.p1()
743 except: # re-raises
744 except: # re-raises
744 try:
745 try:
745 tr.abort()
746 tr.abort()
746 finally:
747 finally:
747 repo.invalidate()
748 repo.invalidate()
748 repo.dirstate.invalidate()
749 repo.dirstate.invalidate()
749 self.invalidate()
750 self.invalidate()
750 raise
751 raise
751 finally:
752 finally:
752 release(tr, lock, wlock)
753 release(tr, lock, wlock)
753 self.removeundo(repo)
754 self.removeundo(repo)
754
755
755 def _apply(self, repo, series, list=False, update_status=True,
756 def _apply(self, repo, series, list=False, update_status=True,
756 strict=False, patchdir=None, merge=None, all_files=None,
757 strict=False, patchdir=None, merge=None, all_files=None,
757 tobackup=None, keepchanges=False):
758 tobackup=None, keepchanges=False):
758 """returns (error, hash)
759 """returns (error, hash)
759
760
760 error = 1 for unable to read, 2 for patch failed, 3 for patch
761 error = 1 for unable to read, 2 for patch failed, 3 for patch
761 fuzz. tobackup is None or a set of files to backup before they
762 fuzz. tobackup is None or a set of files to backup before they
762 are modified by a patch.
763 are modified by a patch.
763 """
764 """
764 # TODO unify with commands.py
765 # TODO unify with commands.py
765 if not patchdir:
766 if not patchdir:
766 patchdir = self.path
767 patchdir = self.path
767 err = 0
768 err = 0
768 n = None
769 n = None
769 for patchname in series:
770 for patchname in series:
770 pushable, reason = self.pushable(patchname)
771 pushable, reason = self.pushable(patchname)
771 if not pushable:
772 if not pushable:
772 self.explainpushable(patchname, all_patches=True)
773 self.explainpushable(patchname, all_patches=True)
773 continue
774 continue
774 self.ui.status(_("applying %s\n") % patchname)
775 self.ui.status(_("applying %s\n") % patchname)
775 pf = os.path.join(patchdir, patchname)
776 pf = os.path.join(patchdir, patchname)
776
777
777 try:
778 try:
778 ph = patchheader(self.join(patchname), self.plainmode)
779 ph = patchheader(self.join(patchname), self.plainmode)
779 except IOError:
780 except IOError:
780 self.ui.warn(_("unable to read %s\n") % patchname)
781 self.ui.warn(_("unable to read %s\n") % patchname)
781 err = 1
782 err = 1
782 break
783 break
783
784
784 message = ph.message
785 message = ph.message
785 if not message:
786 if not message:
786 # The commit message should not be translated
787 # The commit message should not be translated
787 message = "imported patch %s\n" % patchname
788 message = "imported patch %s\n" % patchname
788 else:
789 else:
789 if list:
790 if list:
790 # The commit message should not be translated
791 # The commit message should not be translated
791 message.append("\nimported patch %s" % patchname)
792 message.append("\nimported patch %s" % patchname)
792 message = '\n'.join(message)
793 message = '\n'.join(message)
793
794
794 if ph.haspatch:
795 if ph.haspatch:
795 if tobackup:
796 if tobackup:
796 touched = patchmod.changedfiles(self.ui, repo, pf)
797 touched = patchmod.changedfiles(self.ui, repo, pf)
797 touched = set(touched) & tobackup
798 touched = set(touched) & tobackup
798 if touched and keepchanges:
799 if touched and keepchanges:
799 raise AbortNoCleanup(
800 raise AbortNoCleanup(
800 _("local changes found, refresh first"))
801 _("local changes found, refresh first"))
801 self.backup(repo, touched, copy=True)
802 self.backup(repo, touched, copy=True)
802 tobackup = tobackup - touched
803 tobackup = tobackup - touched
803 (patcherr, files, fuzz) = self.patch(repo, pf)
804 (patcherr, files, fuzz) = self.patch(repo, pf)
804 if all_files is not None:
805 if all_files is not None:
805 all_files.update(files)
806 all_files.update(files)
806 patcherr = not patcherr
807 patcherr = not patcherr
807 else:
808 else:
808 self.ui.warn(_("patch %s is empty\n") % patchname)
809 self.ui.warn(_("patch %s is empty\n") % patchname)
809 patcherr, files, fuzz = 0, [], 0
810 patcherr, files, fuzz = 0, [], 0
810
811
811 if merge and files:
812 if merge and files:
812 # Mark as removed/merged and update dirstate parent info
813 # Mark as removed/merged and update dirstate parent info
813 removed = []
814 removed = []
814 merged = []
815 merged = []
815 for f in files:
816 for f in files:
816 if os.path.lexists(repo.wjoin(f)):
817 if os.path.lexists(repo.wjoin(f)):
817 merged.append(f)
818 merged.append(f)
818 else:
819 else:
819 removed.append(f)
820 removed.append(f)
820 for f in removed:
821 for f in removed:
821 repo.dirstate.remove(f)
822 repo.dirstate.remove(f)
822 for f in merged:
823 for f in merged:
823 repo.dirstate.merge(f)
824 repo.dirstate.merge(f)
824 p1, p2 = repo.dirstate.parents()
825 p1, p2 = repo.dirstate.parents()
825 repo.setparents(p1, merge)
826 repo.setparents(p1, merge)
826
827
827 if all_files and '.hgsubstate' in all_files:
828 if all_files and '.hgsubstate' in all_files:
828 wctx = repo['.']
829 wctx = repo['.']
829 mctx = actx = repo[None]
830 mctx = actx = repo[None]
830 overwrite = False
831 overwrite = False
831 mergedsubstate = subrepo.submerge(repo, wctx, mctx, actx,
832 mergedsubstate = subrepo.submerge(repo, wctx, mctx, actx,
832 overwrite)
833 overwrite)
833 files += mergedsubstate.keys()
834 files += mergedsubstate.keys()
834
835
835 match = scmutil.matchfiles(repo, files or [])
836 match = scmutil.matchfiles(repo, files or [])
836 oldtip = repo['tip']
837 oldtip = repo['tip']
837 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
838 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
838 force=True)
839 force=True)
839 if repo['tip'] == oldtip:
840 if repo['tip'] == oldtip:
840 raise util.Abort(_("qpush exactly duplicates child changeset"))
841 raise util.Abort(_("qpush exactly duplicates child changeset"))
841 if n is None:
842 if n is None:
842 raise util.Abort(_("repository commit failed"))
843 raise util.Abort(_("repository commit failed"))
843
844
844 if update_status:
845 if update_status:
845 self.applied.append(statusentry(n, patchname))
846 self.applied.append(statusentry(n, patchname))
846
847
847 if patcherr:
848 if patcherr:
848 self.ui.warn(_("patch failed, rejects left in working dir\n"))
849 self.ui.warn(_("patch failed, rejects left in working dir\n"))
849 err = 2
850 err = 2
850 break
851 break
851
852
852 if fuzz and strict:
853 if fuzz and strict:
853 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
854 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
854 err = 3
855 err = 3
855 break
856 break
856 return (err, n)
857 return (err, n)
857
858
858 def _cleanup(self, patches, numrevs, keep=False):
859 def _cleanup(self, patches, numrevs, keep=False):
859 if not keep:
860 if not keep:
860 r = self.qrepo()
861 r = self.qrepo()
861 if r:
862 if r:
862 r[None].forget(patches)
863 r[None].forget(patches)
863 for p in patches:
864 for p in patches:
864 try:
865 try:
865 os.unlink(self.join(p))
866 os.unlink(self.join(p))
866 except OSError, inst:
867 except OSError, inst:
867 if inst.errno != errno.ENOENT:
868 if inst.errno != errno.ENOENT:
868 raise
869 raise
869
870
870 qfinished = []
871 qfinished = []
871 if numrevs:
872 if numrevs:
872 qfinished = self.applied[:numrevs]
873 qfinished = self.applied[:numrevs]
873 del self.applied[:numrevs]
874 del self.applied[:numrevs]
874 self.applieddirty = True
875 self.applieddirty = True
875
876
876 unknown = []
877 unknown = []
877
878
878 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
879 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
879 reverse=True):
880 reverse=True):
880 if i is not None:
881 if i is not None:
881 del self.fullseries[i]
882 del self.fullseries[i]
882 else:
883 else:
883 unknown.append(p)
884 unknown.append(p)
884
885
885 if unknown:
886 if unknown:
886 if numrevs:
887 if numrevs:
887 rev = dict((entry.name, entry.node) for entry in qfinished)
888 rev = dict((entry.name, entry.node) for entry in qfinished)
888 for p in unknown:
889 for p in unknown:
889 msg = _('revision %s refers to unknown patches: %s\n')
890 msg = _('revision %s refers to unknown patches: %s\n')
890 self.ui.warn(msg % (short(rev[p]), p))
891 self.ui.warn(msg % (short(rev[p]), p))
891 else:
892 else:
892 msg = _('unknown patches: %s\n')
893 msg = _('unknown patches: %s\n')
893 raise util.Abort(''.join(msg % p for p in unknown))
894 raise util.Abort(''.join(msg % p for p in unknown))
894
895
895 self.parseseries()
896 self.parseseries()
896 self.seriesdirty = True
897 self.seriesdirty = True
897 return [entry.node for entry in qfinished]
898 return [entry.node for entry in qfinished]
898
899
899 def _revpatches(self, repo, revs):
900 def _revpatches(self, repo, revs):
900 firstrev = repo[self.applied[0].node].rev()
901 firstrev = repo[self.applied[0].node].rev()
901 patches = []
902 patches = []
902 for i, rev in enumerate(revs):
903 for i, rev in enumerate(revs):
903
904
904 if rev < firstrev:
905 if rev < firstrev:
905 raise util.Abort(_('revision %d is not managed') % rev)
906 raise util.Abort(_('revision %d is not managed') % rev)
906
907
907 ctx = repo[rev]
908 ctx = repo[rev]
908 base = self.applied[i].node
909 base = self.applied[i].node
909 if ctx.node() != base:
910 if ctx.node() != base:
910 msg = _('cannot delete revision %d above applied patches')
911 msg = _('cannot delete revision %d above applied patches')
911 raise util.Abort(msg % rev)
912 raise util.Abort(msg % rev)
912
913
913 patch = self.applied[i].name
914 patch = self.applied[i].name
914 for fmt in ('[mq]: %s', 'imported patch %s'):
915 for fmt in ('[mq]: %s', 'imported patch %s'):
915 if ctx.description() == fmt % patch:
916 if ctx.description() == fmt % patch:
916 msg = _('patch %s finalized without changeset message\n')
917 msg = _('patch %s finalized without changeset message\n')
917 repo.ui.status(msg % patch)
918 repo.ui.status(msg % patch)
918 break
919 break
919
920
920 patches.append(patch)
921 patches.append(patch)
921 return patches
922 return patches
922
923
923 def finish(self, repo, revs):
924 def finish(self, repo, revs):
924 # Manually trigger phase computation to ensure phasedefaults is
925 # Manually trigger phase computation to ensure phasedefaults is
925 # executed before we remove the patches.
926 # executed before we remove the patches.
926 repo._phasecache
927 repo._phasecache
927 patches = self._revpatches(repo, sorted(revs))
928 patches = self._revpatches(repo, sorted(revs))
928 qfinished = self._cleanup(patches, len(patches))
929 qfinished = self._cleanup(patches, len(patches))
929 if qfinished and repo.ui.configbool('mq', 'secret', False):
930 if qfinished and repo.ui.configbool('mq', 'secret', False):
930 # only use this logic when the secret option is added
931 # only use this logic when the secret option is added
931 oldqbase = repo[qfinished[0]]
932 oldqbase = repo[qfinished[0]]
932 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
933 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
933 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
934 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
934 phases.advanceboundary(repo, tphase, qfinished)
935 phases.advanceboundary(repo, tphase, qfinished)
935
936
936 def delete(self, repo, patches, opts):
937 def delete(self, repo, patches, opts):
937 if not patches and not opts.get('rev'):
938 if not patches and not opts.get('rev'):
938 raise util.Abort(_('qdelete requires at least one revision or '
939 raise util.Abort(_('qdelete requires at least one revision or '
939 'patch name'))
940 'patch name'))
940
941
941 realpatches = []
942 realpatches = []
942 for patch in patches:
943 for patch in patches:
943 patch = self.lookup(patch, strict=True)
944 patch = self.lookup(patch, strict=True)
944 info = self.isapplied(patch)
945 info = self.isapplied(patch)
945 if info:
946 if info:
946 raise util.Abort(_("cannot delete applied patch %s") % patch)
947 raise util.Abort(_("cannot delete applied patch %s") % patch)
947 if patch not in self.series:
948 if patch not in self.series:
948 raise util.Abort(_("patch %s not in series file") % patch)
949 raise util.Abort(_("patch %s not in series file") % patch)
949 if patch not in realpatches:
950 if patch not in realpatches:
950 realpatches.append(patch)
951 realpatches.append(patch)
951
952
952 numrevs = 0
953 numrevs = 0
953 if opts.get('rev'):
954 if opts.get('rev'):
954 if not self.applied:
955 if not self.applied:
955 raise util.Abort(_('no patches applied'))
956 raise util.Abort(_('no patches applied'))
956 revs = scmutil.revrange(repo, opts.get('rev'))
957 revs = scmutil.revrange(repo, opts.get('rev'))
957 if len(revs) > 1 and revs[0] > revs[1]:
958 if len(revs) > 1 and revs[0] > revs[1]:
958 revs.reverse()
959 revs.reverse()
959 revpatches = self._revpatches(repo, revs)
960 revpatches = self._revpatches(repo, revs)
960 realpatches += revpatches
961 realpatches += revpatches
961 numrevs = len(revpatches)
962 numrevs = len(revpatches)
962
963
963 self._cleanup(realpatches, numrevs, opts.get('keep'))
964 self._cleanup(realpatches, numrevs, opts.get('keep'))
964
965
965 def checktoppatch(self, repo):
966 def checktoppatch(self, repo):
966 '''check that working directory is at qtip'''
967 '''check that working directory is at qtip'''
967 if self.applied:
968 if self.applied:
968 top = self.applied[-1].node
969 top = self.applied[-1].node
969 patch = self.applied[-1].name
970 patch = self.applied[-1].name
970 if repo.dirstate.p1() != top:
971 if repo.dirstate.p1() != top:
971 raise util.Abort(_("working directory revision is not qtip"))
972 raise util.Abort(_("working directory revision is not qtip"))
972 return top, patch
973 return top, patch
973 return None, None
974 return None, None
974
975
975 def putsubstate2changes(self, substatestate, changes):
976 def putsubstate2changes(self, substatestate, changes):
976 for files in changes[:3]:
977 for files in changes[:3]:
977 if '.hgsubstate' in files:
978 if '.hgsubstate' in files:
978 return # already listed up
979 return # already listed up
979 # not yet listed up
980 # not yet listed up
980 if substatestate in 'a?':
981 if substatestate in 'a?':
981 changes[1].append('.hgsubstate')
982 changes[1].append('.hgsubstate')
982 elif substatestate in 'r':
983 elif substatestate in 'r':
983 changes[2].append('.hgsubstate')
984 changes[2].append('.hgsubstate')
984 else: # modified
985 else: # modified
985 changes[0].append('.hgsubstate')
986 changes[0].append('.hgsubstate')
986
987
987 def checklocalchanges(self, repo, force=False, refresh=True):
988 def checklocalchanges(self, repo, force=False, refresh=True):
988 excsuffix = ''
989 excsuffix = ''
989 if refresh:
990 if refresh:
990 excsuffix = ', refresh first'
991 excsuffix = ', refresh first'
991 # plain versions for i18n tool to detect them
992 # plain versions for i18n tool to detect them
992 _("local changes found, refresh first")
993 _("local changes found, refresh first")
993 _("local changed subrepos found, refresh first")
994 _("local changed subrepos found, refresh first")
994 return checklocalchanges(repo, force, excsuffix)
995 return checklocalchanges(repo, force, excsuffix)
995
996
996 _reserved = ('series', 'status', 'guards', '.', '..')
997 _reserved = ('series', 'status', 'guards', '.', '..')
997 def checkreservedname(self, name):
998 def checkreservedname(self, name):
998 if name in self._reserved:
999 if name in self._reserved:
999 raise util.Abort(_('"%s" cannot be used as the name of a patch')
1000 raise util.Abort(_('"%s" cannot be used as the name of a patch')
1000 % name)
1001 % name)
1001 for prefix in ('.hg', '.mq'):
1002 for prefix in ('.hg', '.mq'):
1002 if name.startswith(prefix):
1003 if name.startswith(prefix):
1003 raise util.Abort(_('patch name cannot begin with "%s"')
1004 raise util.Abort(_('patch name cannot begin with "%s"')
1004 % prefix)
1005 % prefix)
1005 for c in ('#', ':'):
1006 for c in ('#', ':'):
1006 if c in name:
1007 if c in name:
1007 raise util.Abort(_('"%s" cannot be used in the name of a patch')
1008 raise util.Abort(_('"%s" cannot be used in the name of a patch')
1008 % c)
1009 % c)
1009
1010
1010 def checkpatchname(self, name, force=False):
1011 def checkpatchname(self, name, force=False):
1011 self.checkreservedname(name)
1012 self.checkreservedname(name)
1012 if not force and os.path.exists(self.join(name)):
1013 if not force and os.path.exists(self.join(name)):
1013 if os.path.isdir(self.join(name)):
1014 if os.path.isdir(self.join(name)):
1014 raise util.Abort(_('"%s" already exists as a directory')
1015 raise util.Abort(_('"%s" already exists as a directory')
1015 % name)
1016 % name)
1016 else:
1017 else:
1017 raise util.Abort(_('patch "%s" already exists') % name)
1018 raise util.Abort(_('patch "%s" already exists') % name)
1018
1019
1019 def checkkeepchanges(self, keepchanges, force):
1020 def checkkeepchanges(self, keepchanges, force):
1020 if force and keepchanges:
1021 if force and keepchanges:
1021 raise util.Abort(_('cannot use both --force and --keep-changes'))
1022 raise util.Abort(_('cannot use both --force and --keep-changes'))
1022
1023
1023 def new(self, repo, patchfn, *pats, **opts):
1024 def new(self, repo, patchfn, *pats, **opts):
1024 """options:
1025 """options:
1025 msg: a string or a no-argument function returning a string
1026 msg: a string or a no-argument function returning a string
1026 """
1027 """
1027 msg = opts.get('msg')
1028 msg = opts.get('msg')
1028 user = opts.get('user')
1029 user = opts.get('user')
1029 date = opts.get('date')
1030 date = opts.get('date')
1030 if date:
1031 if date:
1031 date = util.parsedate(date)
1032 date = util.parsedate(date)
1032 diffopts = self.diffopts({'git': opts.get('git')})
1033 diffopts = self.diffopts({'git': opts.get('git')})
1033 if opts.get('checkname', True):
1034 if opts.get('checkname', True):
1034 self.checkpatchname(patchfn)
1035 self.checkpatchname(patchfn)
1035 inclsubs = checksubstate(repo)
1036 inclsubs = checksubstate(repo)
1036 if inclsubs:
1037 if inclsubs:
1037 inclsubs.append('.hgsubstate')
1038 inclsubs.append('.hgsubstate')
1038 substatestate = repo.dirstate['.hgsubstate']
1039 substatestate = repo.dirstate['.hgsubstate']
1039 if opts.get('include') or opts.get('exclude') or pats:
1040 if opts.get('include') or opts.get('exclude') or pats:
1040 if inclsubs:
1041 if inclsubs:
1041 pats = list(pats or []) + inclsubs
1042 pats = list(pats or []) + inclsubs
1042 match = scmutil.match(repo[None], pats, opts)
1043 match = scmutil.match(repo[None], pats, opts)
1043 # detect missing files in pats
1044 # detect missing files in pats
1044 def badfn(f, msg):
1045 def badfn(f, msg):
1045 if f != '.hgsubstate': # .hgsubstate is auto-created
1046 if f != '.hgsubstate': # .hgsubstate is auto-created
1046 raise util.Abort('%s: %s' % (f, msg))
1047 raise util.Abort('%s: %s' % (f, msg))
1047 match.bad = badfn
1048 match.bad = badfn
1048 changes = repo.status(match=match)
1049 changes = repo.status(match=match)
1049 m, a, r, d = changes[:4]
1050 m, a, r, d = changes[:4]
1050 else:
1051 else:
1051 changes = self.checklocalchanges(repo, force=True)
1052 changes = self.checklocalchanges(repo, force=True)
1052 m, a, r, d = changes
1053 m, a, r, d = changes
1053 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1054 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1054 if len(repo[None].parents()) > 1:
1055 if len(repo[None].parents()) > 1:
1055 raise util.Abort(_('cannot manage merge changesets'))
1056 raise util.Abort(_('cannot manage merge changesets'))
1056 commitfiles = m + a + r
1057 commitfiles = m + a + r
1057 self.checktoppatch(repo)
1058 self.checktoppatch(repo)
1058 insert = self.fullseriesend()
1059 insert = self.fullseriesend()
1059 wlock = repo.wlock()
1060 wlock = repo.wlock()
1060 try:
1061 try:
1061 try:
1062 try:
1062 # if patch file write fails, abort early
1063 # if patch file write fails, abort early
1063 p = self.opener(patchfn, "w")
1064 p = self.opener(patchfn, "w")
1064 except IOError, e:
1065 except IOError, e:
1065 raise util.Abort(_('cannot write patch "%s": %s')
1066 raise util.Abort(_('cannot write patch "%s": %s')
1066 % (patchfn, e.strerror))
1067 % (patchfn, e.strerror))
1067 try:
1068 try:
1068 if self.plainmode:
1069 if self.plainmode:
1069 if user:
1070 if user:
1070 p.write("From: " + user + "\n")
1071 p.write("From: " + user + "\n")
1071 if not date:
1072 if not date:
1072 p.write("\n")
1073 p.write("\n")
1073 if date:
1074 if date:
1074 p.write("Date: %d %d\n\n" % date)
1075 p.write("Date: %d %d\n\n" % date)
1075 else:
1076 else:
1076 p.write("# HG changeset patch\n")
1077 p.write("# HG changeset patch\n")
1077 p.write("# Parent "
1078 p.write("# Parent "
1078 + hex(repo[None].p1().node()) + "\n")
1079 + hex(repo[None].p1().node()) + "\n")
1079 if user:
1080 if user:
1080 p.write("# User " + user + "\n")
1081 p.write("# User " + user + "\n")
1081 if date:
1082 if date:
1082 p.write("# Date %s %s\n\n" % date)
1083 p.write("# Date %s %s\n\n" % date)
1083 if util.safehasattr(msg, '__call__'):
1084 if util.safehasattr(msg, '__call__'):
1084 msg = msg()
1085 msg = msg()
1085 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1086 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1086 n = newcommit(repo, None, commitmsg, user, date, match=match,
1087 n = newcommit(repo, None, commitmsg, user, date, match=match,
1087 force=True)
1088 force=True)
1088 if n is None:
1089 if n is None:
1089 raise util.Abort(_("repo commit failed"))
1090 raise util.Abort(_("repo commit failed"))
1090 try:
1091 try:
1091 self.fullseries[insert:insert] = [patchfn]
1092 self.fullseries[insert:insert] = [patchfn]
1092 self.applied.append(statusentry(n, patchfn))
1093 self.applied.append(statusentry(n, patchfn))
1093 self.parseseries()
1094 self.parseseries()
1094 self.seriesdirty = True
1095 self.seriesdirty = True
1095 self.applieddirty = True
1096 self.applieddirty = True
1096 if msg:
1097 if msg:
1097 msg = msg + "\n\n"
1098 msg = msg + "\n\n"
1098 p.write(msg)
1099 p.write(msg)
1099 if commitfiles:
1100 if commitfiles:
1100 parent = self.qparents(repo, n)
1101 parent = self.qparents(repo, n)
1101 if inclsubs:
1102 if inclsubs:
1102 self.putsubstate2changes(substatestate, changes)
1103 self.putsubstate2changes(substatestate, changes)
1103 chunks = patchmod.diff(repo, node1=parent, node2=n,
1104 chunks = patchmod.diff(repo, node1=parent, node2=n,
1104 changes=changes, opts=diffopts)
1105 changes=changes, opts=diffopts)
1105 for chunk in chunks:
1106 for chunk in chunks:
1106 p.write(chunk)
1107 p.write(chunk)
1107 p.close()
1108 p.close()
1108 r = self.qrepo()
1109 r = self.qrepo()
1109 if r:
1110 if r:
1110 r[None].add([patchfn])
1111 r[None].add([patchfn])
1111 except: # re-raises
1112 except: # re-raises
1112 repo.rollback()
1113 repo.rollback()
1113 raise
1114 raise
1114 except Exception:
1115 except Exception:
1115 patchpath = self.join(patchfn)
1116 patchpath = self.join(patchfn)
1116 try:
1117 try:
1117 os.unlink(patchpath)
1118 os.unlink(patchpath)
1118 except OSError:
1119 except OSError:
1119 self.ui.warn(_('error unlinking %s\n') % patchpath)
1120 self.ui.warn(_('error unlinking %s\n') % patchpath)
1120 raise
1121 raise
1121 self.removeundo(repo)
1122 self.removeundo(repo)
1122 finally:
1123 finally:
1123 release(wlock)
1124 release(wlock)
1124
1125
1125 def isapplied(self, patch):
1126 def isapplied(self, patch):
1126 """returns (index, rev, patch)"""
1127 """returns (index, rev, patch)"""
1127 for i, a in enumerate(self.applied):
1128 for i, a in enumerate(self.applied):
1128 if a.name == patch:
1129 if a.name == patch:
1129 return (i, a.node, a.name)
1130 return (i, a.node, a.name)
1130 return None
1131 return None
1131
1132
1132 # if the exact patch name does not exist, we try a few
1133 # if the exact patch name does not exist, we try a few
1133 # variations. If strict is passed, we try only #1
1134 # variations. If strict is passed, we try only #1
1134 #
1135 #
1135 # 1) a number (as string) to indicate an offset in the series file
1136 # 1) a number (as string) to indicate an offset in the series file
1136 # 2) a unique substring of the patch name was given
1137 # 2) a unique substring of the patch name was given
1137 # 3) patchname[-+]num to indicate an offset in the series file
1138 # 3) patchname[-+]num to indicate an offset in the series file
1138 def lookup(self, patch, strict=False):
1139 def lookup(self, patch, strict=False):
1139 def partialname(s):
1140 def partialname(s):
1140 if s in self.series:
1141 if s in self.series:
1141 return s
1142 return s
1142 matches = [x for x in self.series if s in x]
1143 matches = [x for x in self.series if s in x]
1143 if len(matches) > 1:
1144 if len(matches) > 1:
1144 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1145 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1145 for m in matches:
1146 for m in matches:
1146 self.ui.warn(' %s\n' % m)
1147 self.ui.warn(' %s\n' % m)
1147 return None
1148 return None
1148 if matches:
1149 if matches:
1149 return matches[0]
1150 return matches[0]
1150 if self.series and self.applied:
1151 if self.series and self.applied:
1151 if s == 'qtip':
1152 if s == 'qtip':
1152 return self.series[self.seriesend(True) - 1]
1153 return self.series[self.seriesend(True) - 1]
1153 if s == 'qbase':
1154 if s == 'qbase':
1154 return self.series[0]
1155 return self.series[0]
1155 return None
1156 return None
1156
1157
1157 if patch in self.series:
1158 if patch in self.series:
1158 return patch
1159 return patch
1159
1160
1160 if not os.path.isfile(self.join(patch)):
1161 if not os.path.isfile(self.join(patch)):
1161 try:
1162 try:
1162 sno = int(patch)
1163 sno = int(patch)
1163 except (ValueError, OverflowError):
1164 except (ValueError, OverflowError):
1164 pass
1165 pass
1165 else:
1166 else:
1166 if -len(self.series) <= sno < len(self.series):
1167 if -len(self.series) <= sno < len(self.series):
1167 return self.series[sno]
1168 return self.series[sno]
1168
1169
1169 if not strict:
1170 if not strict:
1170 res = partialname(patch)
1171 res = partialname(patch)
1171 if res:
1172 if res:
1172 return res
1173 return res
1173 minus = patch.rfind('-')
1174 minus = patch.rfind('-')
1174 if minus >= 0:
1175 if minus >= 0:
1175 res = partialname(patch[:minus])
1176 res = partialname(patch[:minus])
1176 if res:
1177 if res:
1177 i = self.series.index(res)
1178 i = self.series.index(res)
1178 try:
1179 try:
1179 off = int(patch[minus + 1:] or 1)
1180 off = int(patch[minus + 1:] or 1)
1180 except (ValueError, OverflowError):
1181 except (ValueError, OverflowError):
1181 pass
1182 pass
1182 else:
1183 else:
1183 if i - off >= 0:
1184 if i - off >= 0:
1184 return self.series[i - off]
1185 return self.series[i - off]
1185 plus = patch.rfind('+')
1186 plus = patch.rfind('+')
1186 if plus >= 0:
1187 if plus >= 0:
1187 res = partialname(patch[:plus])
1188 res = partialname(patch[:plus])
1188 if res:
1189 if res:
1189 i = self.series.index(res)
1190 i = self.series.index(res)
1190 try:
1191 try:
1191 off = int(patch[plus + 1:] or 1)
1192 off = int(patch[plus + 1:] or 1)
1192 except (ValueError, OverflowError):
1193 except (ValueError, OverflowError):
1193 pass
1194 pass
1194 else:
1195 else:
1195 if i + off < len(self.series):
1196 if i + off < len(self.series):
1196 return self.series[i + off]
1197 return self.series[i + off]
1197 raise util.Abort(_("patch %s not in series") % patch)
1198 raise util.Abort(_("patch %s not in series") % patch)
1198
1199
1199 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1200 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1200 all=False, move=False, exact=False, nobackup=False,
1201 all=False, move=False, exact=False, nobackup=False,
1201 keepchanges=False):
1202 keepchanges=False):
1202 self.checkkeepchanges(keepchanges, force)
1203 self.checkkeepchanges(keepchanges, force)
1203 diffopts = self.diffopts()
1204 diffopts = self.diffopts()
1204 wlock = repo.wlock()
1205 wlock = repo.wlock()
1205 try:
1206 try:
1206 heads = []
1207 heads = []
1207 for b, ls in repo.branchmap().iteritems():
1208 for b, ls in repo.branchmap().iteritems():
1208 heads += ls
1209 heads += ls
1209 if not heads:
1210 if not heads:
1210 heads = [nullid]
1211 heads = [nullid]
1211 if repo.dirstate.p1() not in heads and not exact:
1212 if repo.dirstate.p1() not in heads and not exact:
1212 self.ui.status(_("(working directory not at a head)\n"))
1213 self.ui.status(_("(working directory not at a head)\n"))
1213
1214
1214 if not self.series:
1215 if not self.series:
1215 self.ui.warn(_('no patches in series\n'))
1216 self.ui.warn(_('no patches in series\n'))
1216 return 0
1217 return 0
1217
1218
1218 # Suppose our series file is: A B C and the current 'top'
1219 # Suppose our series file is: A B C and the current 'top'
1219 # patch is B. qpush C should be performed (moving forward)
1220 # patch is B. qpush C should be performed (moving forward)
1220 # qpush B is a NOP (no change) qpush A is an error (can't
1221 # qpush B is a NOP (no change) qpush A is an error (can't
1221 # go backwards with qpush)
1222 # go backwards with qpush)
1222 if patch:
1223 if patch:
1223 patch = self.lookup(patch)
1224 patch = self.lookup(patch)
1224 info = self.isapplied(patch)
1225 info = self.isapplied(patch)
1225 if info and info[0] >= len(self.applied) - 1:
1226 if info and info[0] >= len(self.applied) - 1:
1226 self.ui.warn(
1227 self.ui.warn(
1227 _('qpush: %s is already at the top\n') % patch)
1228 _('qpush: %s is already at the top\n') % patch)
1228 return 0
1229 return 0
1229
1230
1230 pushable, reason = self.pushable(patch)
1231 pushable, reason = self.pushable(patch)
1231 if pushable:
1232 if pushable:
1232 if self.series.index(patch) < self.seriesend():
1233 if self.series.index(patch) < self.seriesend():
1233 raise util.Abort(
1234 raise util.Abort(
1234 _("cannot push to a previous patch: %s") % patch)
1235 _("cannot push to a previous patch: %s") % patch)
1235 else:
1236 else:
1236 if reason:
1237 if reason:
1237 reason = _('guarded by %s') % reason
1238 reason = _('guarded by %s') % reason
1238 else:
1239 else:
1239 reason = _('no matching guards')
1240 reason = _('no matching guards')
1240 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1241 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1241 return 1
1242 return 1
1242 elif all:
1243 elif all:
1243 patch = self.series[-1]
1244 patch = self.series[-1]
1244 if self.isapplied(patch):
1245 if self.isapplied(patch):
1245 self.ui.warn(_('all patches are currently applied\n'))
1246 self.ui.warn(_('all patches are currently applied\n'))
1246 return 0
1247 return 0
1247
1248
1248 # Following the above example, starting at 'top' of B:
1249 # Following the above example, starting at 'top' of B:
1249 # qpush should be performed (pushes C), but a subsequent
1250 # qpush should be performed (pushes C), but a subsequent
1250 # qpush without an argument is an error (nothing to
1251 # qpush without an argument is an error (nothing to
1251 # apply). This allows a loop of "...while hg qpush..." to
1252 # apply). This allows a loop of "...while hg qpush..." to
1252 # work as it detects an error when done
1253 # work as it detects an error when done
1253 start = self.seriesend()
1254 start = self.seriesend()
1254 if start == len(self.series):
1255 if start == len(self.series):
1255 self.ui.warn(_('patch series already fully applied\n'))
1256 self.ui.warn(_('patch series already fully applied\n'))
1256 return 1
1257 return 1
1257 if not force and not keepchanges:
1258 if not force and not keepchanges:
1258 self.checklocalchanges(repo, refresh=self.applied)
1259 self.checklocalchanges(repo, refresh=self.applied)
1259
1260
1260 if exact:
1261 if exact:
1261 if keepchanges:
1262 if keepchanges:
1262 raise util.Abort(
1263 raise util.Abort(
1263 _("cannot use --exact and --keep-changes together"))
1264 _("cannot use --exact and --keep-changes together"))
1264 if move:
1265 if move:
1265 raise util.Abort(_('cannot use --exact and --move '
1266 raise util.Abort(_('cannot use --exact and --move '
1266 'together'))
1267 'together'))
1267 if self.applied:
1268 if self.applied:
1268 raise util.Abort(_('cannot push --exact with applied '
1269 raise util.Abort(_('cannot push --exact with applied '
1269 'patches'))
1270 'patches'))
1270 root = self.series[start]
1271 root = self.series[start]
1271 target = patchheader(self.join(root), self.plainmode).parent
1272 target = patchheader(self.join(root), self.plainmode).parent
1272 if not target:
1273 if not target:
1273 raise util.Abort(
1274 raise util.Abort(
1274 _("%s does not have a parent recorded") % root)
1275 _("%s does not have a parent recorded") % root)
1275 if not repo[target] == repo['.']:
1276 if not repo[target] == repo['.']:
1276 hg.update(repo, target)
1277 hg.update(repo, target)
1277
1278
1278 if move:
1279 if move:
1279 if not patch:
1280 if not patch:
1280 raise util.Abort(_("please specify the patch to move"))
1281 raise util.Abort(_("please specify the patch to move"))
1281 for fullstart, rpn in enumerate(self.fullseries):
1282 for fullstart, rpn in enumerate(self.fullseries):
1282 # strip markers for patch guards
1283 # strip markers for patch guards
1283 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1284 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1284 break
1285 break
1285 for i, rpn in enumerate(self.fullseries[fullstart:]):
1286 for i, rpn in enumerate(self.fullseries[fullstart:]):
1286 # strip markers for patch guards
1287 # strip markers for patch guards
1287 if self.guard_re.split(rpn, 1)[0] == patch:
1288 if self.guard_re.split(rpn, 1)[0] == patch:
1288 break
1289 break
1289 index = fullstart + i
1290 index = fullstart + i
1290 assert index < len(self.fullseries)
1291 assert index < len(self.fullseries)
1291 fullpatch = self.fullseries[index]
1292 fullpatch = self.fullseries[index]
1292 del self.fullseries[index]
1293 del self.fullseries[index]
1293 self.fullseries.insert(fullstart, fullpatch)
1294 self.fullseries.insert(fullstart, fullpatch)
1294 self.parseseries()
1295 self.parseseries()
1295 self.seriesdirty = True
1296 self.seriesdirty = True
1296
1297
1297 self.applieddirty = True
1298 self.applieddirty = True
1298 if start > 0:
1299 if start > 0:
1299 self.checktoppatch(repo)
1300 self.checktoppatch(repo)
1300 if not patch:
1301 if not patch:
1301 patch = self.series[start]
1302 patch = self.series[start]
1302 end = start + 1
1303 end = start + 1
1303 else:
1304 else:
1304 end = self.series.index(patch, start) + 1
1305 end = self.series.index(patch, start) + 1
1305
1306
1306 tobackup = set()
1307 tobackup = set()
1307 if (not nobackup and force) or keepchanges:
1308 if (not nobackup and force) or keepchanges:
1308 m, a, r, d = self.checklocalchanges(repo, force=True)
1309 m, a, r, d = self.checklocalchanges(repo, force=True)
1309 if keepchanges:
1310 if keepchanges:
1310 tobackup.update(m + a + r + d)
1311 tobackup.update(m + a + r + d)
1311 else:
1312 else:
1312 tobackup.update(m + a)
1313 tobackup.update(m + a)
1313
1314
1314 s = self.series[start:end]
1315 s = self.series[start:end]
1315 all_files = set()
1316 all_files = set()
1316 try:
1317 try:
1317 if mergeq:
1318 if mergeq:
1318 ret = self.mergepatch(repo, mergeq, s, diffopts)
1319 ret = self.mergepatch(repo, mergeq, s, diffopts)
1319 else:
1320 else:
1320 ret = self.apply(repo, s, list, all_files=all_files,
1321 ret = self.apply(repo, s, list, all_files=all_files,
1321 tobackup=tobackup, keepchanges=keepchanges)
1322 tobackup=tobackup, keepchanges=keepchanges)
1322 except: # re-raises
1323 except: # re-raises
1323 self.ui.warn(_('cleaning up working directory...'))
1324 self.ui.warn(_('cleaning up working directory...'))
1324 node = repo.dirstate.p1()
1325 node = repo.dirstate.p1()
1325 hg.revert(repo, node, None)
1326 hg.revert(repo, node, None)
1326 # only remove unknown files that we know we touched or
1327 # only remove unknown files that we know we touched or
1327 # created while patching
1328 # created while patching
1328 for f in all_files:
1329 for f in all_files:
1329 if f not in repo.dirstate:
1330 if f not in repo.dirstate:
1330 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1331 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1331 self.ui.warn(_('done\n'))
1332 self.ui.warn(_('done\n'))
1332 raise
1333 raise
1333
1334
1334 if not self.applied:
1335 if not self.applied:
1335 return ret[0]
1336 return ret[0]
1336 top = self.applied[-1].name
1337 top = self.applied[-1].name
1337 if ret[0] and ret[0] > 1:
1338 if ret[0] and ret[0] > 1:
1338 msg = _("errors during apply, please fix and refresh %s\n")
1339 msg = _("errors during apply, please fix and refresh %s\n")
1339 self.ui.write(msg % top)
1340 self.ui.write(msg % top)
1340 else:
1341 else:
1341 self.ui.write(_("now at: %s\n") % top)
1342 self.ui.write(_("now at: %s\n") % top)
1342 return ret[0]
1343 return ret[0]
1343
1344
1344 finally:
1345 finally:
1345 wlock.release()
1346 wlock.release()
1346
1347
1347 def pop(self, repo, patch=None, force=False, update=True, all=False,
1348 def pop(self, repo, patch=None, force=False, update=True, all=False,
1348 nobackup=False, keepchanges=False):
1349 nobackup=False, keepchanges=False):
1349 self.checkkeepchanges(keepchanges, force)
1350 self.checkkeepchanges(keepchanges, force)
1350 wlock = repo.wlock()
1351 wlock = repo.wlock()
1351 try:
1352 try:
1352 if patch:
1353 if patch:
1353 # index, rev, patch
1354 # index, rev, patch
1354 info = self.isapplied(patch)
1355 info = self.isapplied(patch)
1355 if not info:
1356 if not info:
1356 patch = self.lookup(patch)
1357 patch = self.lookup(patch)
1357 info = self.isapplied(patch)
1358 info = self.isapplied(patch)
1358 if not info:
1359 if not info:
1359 raise util.Abort(_("patch %s is not applied") % patch)
1360 raise util.Abort(_("patch %s is not applied") % patch)
1360
1361
1361 if not self.applied:
1362 if not self.applied:
1362 # Allow qpop -a to work repeatedly,
1363 # Allow qpop -a to work repeatedly,
1363 # but not qpop without an argument
1364 # but not qpop without an argument
1364 self.ui.warn(_("no patches applied\n"))
1365 self.ui.warn(_("no patches applied\n"))
1365 return not all
1366 return not all
1366
1367
1367 if all:
1368 if all:
1368 start = 0
1369 start = 0
1369 elif patch:
1370 elif patch:
1370 start = info[0] + 1
1371 start = info[0] + 1
1371 else:
1372 else:
1372 start = len(self.applied) - 1
1373 start = len(self.applied) - 1
1373
1374
1374 if start >= len(self.applied):
1375 if start >= len(self.applied):
1375 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1376 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1376 return
1377 return
1377
1378
1378 if not update:
1379 if not update:
1379 parents = repo.dirstate.parents()
1380 parents = repo.dirstate.parents()
1380 rr = [x.node for x in self.applied]
1381 rr = [x.node for x in self.applied]
1381 for p in parents:
1382 for p in parents:
1382 if p in rr:
1383 if p in rr:
1383 self.ui.warn(_("qpop: forcing dirstate update\n"))
1384 self.ui.warn(_("qpop: forcing dirstate update\n"))
1384 update = True
1385 update = True
1385 else:
1386 else:
1386 parents = [p.node() for p in repo[None].parents()]
1387 parents = [p.node() for p in repo[None].parents()]
1387 needupdate = False
1388 needupdate = False
1388 for entry in self.applied[start:]:
1389 for entry in self.applied[start:]:
1389 if entry.node in parents:
1390 if entry.node in parents:
1390 needupdate = True
1391 needupdate = True
1391 break
1392 break
1392 update = needupdate
1393 update = needupdate
1393
1394
1394 tobackup = set()
1395 tobackup = set()
1395 if update:
1396 if update:
1396 m, a, r, d = self.checklocalchanges(
1397 m, a, r, d = self.checklocalchanges(
1397 repo, force=force or keepchanges)
1398 repo, force=force or keepchanges)
1398 if force:
1399 if force:
1399 if not nobackup:
1400 if not nobackup:
1400 tobackup.update(m + a)
1401 tobackup.update(m + a)
1401 elif keepchanges:
1402 elif keepchanges:
1402 tobackup.update(m + a + r + d)
1403 tobackup.update(m + a + r + d)
1403
1404
1404 self.applieddirty = True
1405 self.applieddirty = True
1405 end = len(self.applied)
1406 end = len(self.applied)
1406 rev = self.applied[start].node
1407 rev = self.applied[start].node
1407
1408
1408 try:
1409 try:
1409 heads = repo.changelog.heads(rev)
1410 heads = repo.changelog.heads(rev)
1410 except error.LookupError:
1411 except error.LookupError:
1411 node = short(rev)
1412 node = short(rev)
1412 raise util.Abort(_('trying to pop unknown node %s') % node)
1413 raise util.Abort(_('trying to pop unknown node %s') % node)
1413
1414
1414 if heads != [self.applied[-1].node]:
1415 if heads != [self.applied[-1].node]:
1415 raise util.Abort(_("popping would remove a revision not "
1416 raise util.Abort(_("popping would remove a revision not "
1416 "managed by this patch queue"))
1417 "managed by this patch queue"))
1417 if not repo[self.applied[-1].node].mutable():
1418 if not repo[self.applied[-1].node].mutable():
1418 raise util.Abort(
1419 raise util.Abort(
1419 _("popping would remove an immutable revision"),
1420 _("popping would remove an immutable revision"),
1420 hint=_('see "hg help phases" for details'))
1421 hint=_('see "hg help phases" for details'))
1421
1422
1422 # we know there are no local changes, so we can make a simplified
1423 # we know there are no local changes, so we can make a simplified
1423 # form of hg.update.
1424 # form of hg.update.
1424 if update:
1425 if update:
1425 qp = self.qparents(repo, rev)
1426 qp = self.qparents(repo, rev)
1426 ctx = repo[qp]
1427 ctx = repo[qp]
1427 m, a, r, d = repo.status(qp, '.')[:4]
1428 m, a, r, d = repo.status(qp, '.')[:4]
1428 if d:
1429 if d:
1429 raise util.Abort(_("deletions found between repo revs"))
1430 raise util.Abort(_("deletions found between repo revs"))
1430
1431
1431 tobackup = set(a + m + r) & tobackup
1432 tobackup = set(a + m + r) & tobackup
1432 if keepchanges and tobackup:
1433 if keepchanges and tobackup:
1433 raise util.Abort(_("local changes found, refresh first"))
1434 raise util.Abort(_("local changes found, refresh first"))
1434 self.backup(repo, tobackup)
1435 self.backup(repo, tobackup)
1435
1436
1436 for f in a:
1437 for f in a:
1437 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1438 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1438 repo.dirstate.drop(f)
1439 repo.dirstate.drop(f)
1439 for f in m + r:
1440 for f in m + r:
1440 fctx = ctx[f]
1441 fctx = ctx[f]
1441 repo.wwrite(f, fctx.data(), fctx.flags())
1442 repo.wwrite(f, fctx.data(), fctx.flags())
1442 repo.dirstate.normal(f)
1443 repo.dirstate.normal(f)
1443 repo.setparents(qp, nullid)
1444 repo.setparents(qp, nullid)
1444 for patch in reversed(self.applied[start:end]):
1445 for patch in reversed(self.applied[start:end]):
1445 self.ui.status(_("popping %s\n") % patch.name)
1446 self.ui.status(_("popping %s\n") % patch.name)
1446 del self.applied[start:end]
1447 del self.applied[start:end]
1447 strip(self.ui, repo, [rev], update=False, backup='strip')
1448 strip(self.ui, repo, [rev], update=False, backup='strip')
1448 for s, state in repo['.'].substate.items():
1449 for s, state in repo['.'].substate.items():
1449 repo['.'].sub(s).get(state)
1450 repo['.'].sub(s).get(state)
1450 if self.applied:
1451 if self.applied:
1451 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1452 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1452 else:
1453 else:
1453 self.ui.write(_("patch queue now empty\n"))
1454 self.ui.write(_("patch queue now empty\n"))
1454 finally:
1455 finally:
1455 wlock.release()
1456 wlock.release()
1456
1457
1457 def diff(self, repo, pats, opts):
1458 def diff(self, repo, pats, opts):
1458 top, patch = self.checktoppatch(repo)
1459 top, patch = self.checktoppatch(repo)
1459 if not top:
1460 if not top:
1460 self.ui.write(_("no patches applied\n"))
1461 self.ui.write(_("no patches applied\n"))
1461 return
1462 return
1462 qp = self.qparents(repo, top)
1463 qp = self.qparents(repo, top)
1463 if opts.get('reverse'):
1464 if opts.get('reverse'):
1464 node1, node2 = None, qp
1465 node1, node2 = None, qp
1465 else:
1466 else:
1466 node1, node2 = qp, None
1467 node1, node2 = qp, None
1467 diffopts = self.diffopts(opts, patch)
1468 diffopts = self.diffopts(opts, patch)
1468 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1469 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1469
1470
1470 def refresh(self, repo, pats=None, **opts):
1471 def refresh(self, repo, pats=None, **opts):
1471 if not self.applied:
1472 if not self.applied:
1472 self.ui.write(_("no patches applied\n"))
1473 self.ui.write(_("no patches applied\n"))
1473 return 1
1474 return 1
1474 msg = opts.get('msg', '').rstrip()
1475 msg = opts.get('msg', '').rstrip()
1475 newuser = opts.get('user')
1476 newuser = opts.get('user')
1476 newdate = opts.get('date')
1477 newdate = opts.get('date')
1477 if newdate:
1478 if newdate:
1478 newdate = '%d %d' % util.parsedate(newdate)
1479 newdate = '%d %d' % util.parsedate(newdate)
1479 wlock = repo.wlock()
1480 wlock = repo.wlock()
1480
1481
1481 try:
1482 try:
1482 self.checktoppatch(repo)
1483 self.checktoppatch(repo)
1483 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1484 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1484 if repo.changelog.heads(top) != [top]:
1485 if repo.changelog.heads(top) != [top]:
1485 raise util.Abort(_("cannot refresh a revision with children"))
1486 raise util.Abort(_("cannot refresh a revision with children"))
1486 if not repo[top].mutable():
1487 if not repo[top].mutable():
1487 raise util.Abort(_("cannot refresh immutable revision"),
1488 raise util.Abort(_("cannot refresh immutable revision"),
1488 hint=_('see "hg help phases" for details'))
1489 hint=_('see "hg help phases" for details'))
1489
1490
1490 cparents = repo.changelog.parents(top)
1491 cparents = repo.changelog.parents(top)
1491 patchparent = self.qparents(repo, top)
1492 patchparent = self.qparents(repo, top)
1492
1493
1493 inclsubs = checksubstate(repo, hex(patchparent))
1494 inclsubs = checksubstate(repo, hex(patchparent))
1494 if inclsubs:
1495 if inclsubs:
1495 inclsubs.append('.hgsubstate')
1496 inclsubs.append('.hgsubstate')
1496 substatestate = repo.dirstate['.hgsubstate']
1497 substatestate = repo.dirstate['.hgsubstate']
1497
1498
1498 ph = patchheader(self.join(patchfn), self.plainmode)
1499 ph = patchheader(self.join(patchfn), self.plainmode)
1499 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1500 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1500 if msg:
1501 if msg:
1501 ph.setmessage(msg)
1502 ph.setmessage(msg)
1502 if newuser:
1503 if newuser:
1503 ph.setuser(newuser)
1504 ph.setuser(newuser)
1504 if newdate:
1505 if newdate:
1505 ph.setdate(newdate)
1506 ph.setdate(newdate)
1506 ph.setparent(hex(patchparent))
1507 ph.setparent(hex(patchparent))
1507
1508
1508 # only commit new patch when write is complete
1509 # only commit new patch when write is complete
1509 patchf = self.opener(patchfn, 'w', atomictemp=True)
1510 patchf = self.opener(patchfn, 'w', atomictemp=True)
1510
1511
1511 comments = str(ph)
1512 comments = str(ph)
1512 if comments:
1513 if comments:
1513 patchf.write(comments)
1514 patchf.write(comments)
1514
1515
1515 # update the dirstate in place, strip off the qtip commit
1516 # update the dirstate in place, strip off the qtip commit
1516 # and then commit.
1517 # and then commit.
1517 #
1518 #
1518 # this should really read:
1519 # this should really read:
1519 # mm, dd, aa = repo.status(top, patchparent)[:3]
1520 # mm, dd, aa = repo.status(top, patchparent)[:3]
1520 # but we do it backwards to take advantage of manifest/changelog
1521 # but we do it backwards to take advantage of manifest/changelog
1521 # caching against the next repo.status call
1522 # caching against the next repo.status call
1522 mm, aa, dd = repo.status(patchparent, top)[:3]
1523 mm, aa, dd = repo.status(patchparent, top)[:3]
1523 changes = repo.changelog.read(top)
1524 changes = repo.changelog.read(top)
1524 man = repo.manifest.read(changes[0])
1525 man = repo.manifest.read(changes[0])
1525 aaa = aa[:]
1526 aaa = aa[:]
1526 matchfn = scmutil.match(repo[None], pats, opts)
1527 matchfn = scmutil.match(repo[None], pats, opts)
1527 # in short mode, we only diff the files included in the
1528 # in short mode, we only diff the files included in the
1528 # patch already plus specified files
1529 # patch already plus specified files
1529 if opts.get('short'):
1530 if opts.get('short'):
1530 # if amending a patch, we start with existing
1531 # if amending a patch, we start with existing
1531 # files plus specified files - unfiltered
1532 # files plus specified files - unfiltered
1532 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1533 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1533 # filter with include/exclude options
1534 # filter with include/exclude options
1534 matchfn = scmutil.match(repo[None], opts=opts)
1535 matchfn = scmutil.match(repo[None], opts=opts)
1535 else:
1536 else:
1536 match = scmutil.matchall(repo)
1537 match = scmutil.matchall(repo)
1537 m, a, r, d = repo.status(match=match)[:4]
1538 m, a, r, d = repo.status(match=match)[:4]
1538 mm = set(mm)
1539 mm = set(mm)
1539 aa = set(aa)
1540 aa = set(aa)
1540 dd = set(dd)
1541 dd = set(dd)
1541
1542
1542 # we might end up with files that were added between
1543 # we might end up with files that were added between
1543 # qtip and the dirstate parent, but then changed in the
1544 # qtip and the dirstate parent, but then changed in the
1544 # local dirstate. in this case, we want them to only
1545 # local dirstate. in this case, we want them to only
1545 # show up in the added section
1546 # show up in the added section
1546 for x in m:
1547 for x in m:
1547 if x not in aa:
1548 if x not in aa:
1548 mm.add(x)
1549 mm.add(x)
1549 # we might end up with files added by the local dirstate that
1550 # we might end up with files added by the local dirstate that
1550 # were deleted by the patch. In this case, they should only
1551 # were deleted by the patch. In this case, they should only
1551 # show up in the changed section.
1552 # show up in the changed section.
1552 for x in a:
1553 for x in a:
1553 if x in dd:
1554 if x in dd:
1554 dd.remove(x)
1555 dd.remove(x)
1555 mm.add(x)
1556 mm.add(x)
1556 else:
1557 else:
1557 aa.add(x)
1558 aa.add(x)
1558 # make sure any files deleted in the local dirstate
1559 # make sure any files deleted in the local dirstate
1559 # are not in the add or change column of the patch
1560 # are not in the add or change column of the patch
1560 forget = []
1561 forget = []
1561 for x in d + r:
1562 for x in d + r:
1562 if x in aa:
1563 if x in aa:
1563 aa.remove(x)
1564 aa.remove(x)
1564 forget.append(x)
1565 forget.append(x)
1565 continue
1566 continue
1566 else:
1567 else:
1567 mm.discard(x)
1568 mm.discard(x)
1568 dd.add(x)
1569 dd.add(x)
1569
1570
1570 m = list(mm)
1571 m = list(mm)
1571 r = list(dd)
1572 r = list(dd)
1572 a = list(aa)
1573 a = list(aa)
1573
1574
1574 # create 'match' that includes the files to be recommitted.
1575 # create 'match' that includes the files to be recommitted.
1575 # apply matchfn via repo.status to ensure correct case handling.
1576 # apply matchfn via repo.status to ensure correct case handling.
1576 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1577 cm, ca, cr, cd = repo.status(patchparent, match=matchfn)[:4]
1577 allmatches = set(cm + ca + cr + cd)
1578 allmatches = set(cm + ca + cr + cd)
1578 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1579 refreshchanges = [x.intersection(allmatches) for x in (mm, aa, dd)]
1579
1580
1580 files = set(inclsubs)
1581 files = set(inclsubs)
1581 for x in refreshchanges:
1582 for x in refreshchanges:
1582 files.update(x)
1583 files.update(x)
1583 match = scmutil.matchfiles(repo, files)
1584 match = scmutil.matchfiles(repo, files)
1584
1585
1585 bmlist = repo[top].bookmarks()
1586 bmlist = repo[top].bookmarks()
1586
1587
1587 try:
1588 try:
1588 if diffopts.git or diffopts.upgrade:
1589 if diffopts.git or diffopts.upgrade:
1589 copies = {}
1590 copies = {}
1590 for dst in a:
1591 for dst in a:
1591 src = repo.dirstate.copied(dst)
1592 src = repo.dirstate.copied(dst)
1592 # during qfold, the source file for copies may
1593 # during qfold, the source file for copies may
1593 # be removed. Treat this as a simple add.
1594 # be removed. Treat this as a simple add.
1594 if src is not None and src in repo.dirstate:
1595 if src is not None and src in repo.dirstate:
1595 copies.setdefault(src, []).append(dst)
1596 copies.setdefault(src, []).append(dst)
1596 repo.dirstate.add(dst)
1597 repo.dirstate.add(dst)
1597 # remember the copies between patchparent and qtip
1598 # remember the copies between patchparent and qtip
1598 for dst in aaa:
1599 for dst in aaa:
1599 f = repo.file(dst)
1600 f = repo.file(dst)
1600 src = f.renamed(man[dst])
1601 src = f.renamed(man[dst])
1601 if src:
1602 if src:
1602 copies.setdefault(src[0], []).extend(
1603 copies.setdefault(src[0], []).extend(
1603 copies.get(dst, []))
1604 copies.get(dst, []))
1604 if dst in a:
1605 if dst in a:
1605 copies[src[0]].append(dst)
1606 copies[src[0]].append(dst)
1606 # we can't copy a file created by the patch itself
1607 # we can't copy a file created by the patch itself
1607 if dst in copies:
1608 if dst in copies:
1608 del copies[dst]
1609 del copies[dst]
1609 for src, dsts in copies.iteritems():
1610 for src, dsts in copies.iteritems():
1610 for dst in dsts:
1611 for dst in dsts:
1611 repo.dirstate.copy(src, dst)
1612 repo.dirstate.copy(src, dst)
1612 else:
1613 else:
1613 for dst in a:
1614 for dst in a:
1614 repo.dirstate.add(dst)
1615 repo.dirstate.add(dst)
1615 # Drop useless copy information
1616 # Drop useless copy information
1616 for f in list(repo.dirstate.copies()):
1617 for f in list(repo.dirstate.copies()):
1617 repo.dirstate.copy(None, f)
1618 repo.dirstate.copy(None, f)
1618 for f in r:
1619 for f in r:
1619 repo.dirstate.remove(f)
1620 repo.dirstate.remove(f)
1620 # if the patch excludes a modified file, mark that
1621 # if the patch excludes a modified file, mark that
1621 # file with mtime=0 so status can see it.
1622 # file with mtime=0 so status can see it.
1622 mm = []
1623 mm = []
1623 for i in xrange(len(m) - 1, -1, -1):
1624 for i in xrange(len(m) - 1, -1, -1):
1624 if not matchfn(m[i]):
1625 if not matchfn(m[i]):
1625 mm.append(m[i])
1626 mm.append(m[i])
1626 del m[i]
1627 del m[i]
1627 for f in m:
1628 for f in m:
1628 repo.dirstate.normal(f)
1629 repo.dirstate.normal(f)
1629 for f in mm:
1630 for f in mm:
1630 repo.dirstate.normallookup(f)
1631 repo.dirstate.normallookup(f)
1631 for f in forget:
1632 for f in forget:
1632 repo.dirstate.drop(f)
1633 repo.dirstate.drop(f)
1633
1634
1634 if not msg:
1635 if not msg:
1635 if not ph.message:
1636 if not ph.message:
1636 message = "[mq]: %s\n" % patchfn
1637 message = "[mq]: %s\n" % patchfn
1637 else:
1638 else:
1638 message = "\n".join(ph.message)
1639 message = "\n".join(ph.message)
1639 else:
1640 else:
1640 message = msg
1641 message = msg
1641
1642
1642 user = ph.user or changes[1]
1643 user = ph.user or changes[1]
1643
1644
1644 oldphase = repo[top].phase()
1645 oldphase = repo[top].phase()
1645
1646
1646 # assumes strip can roll itself back if interrupted
1647 # assumes strip can roll itself back if interrupted
1647 repo.setparents(*cparents)
1648 repo.setparents(*cparents)
1648 self.applied.pop()
1649 self.applied.pop()
1649 self.applieddirty = True
1650 self.applieddirty = True
1650 strip(self.ui, repo, [top], update=False, backup='strip')
1651 strip(self.ui, repo, [top], update=False, backup='strip')
1651 except: # re-raises
1652 except: # re-raises
1652 repo.dirstate.invalidate()
1653 repo.dirstate.invalidate()
1653 raise
1654 raise
1654
1655
1655 try:
1656 try:
1656 # might be nice to attempt to roll back strip after this
1657 # might be nice to attempt to roll back strip after this
1657
1658
1658 # Ensure we create a new changeset in the same phase than
1659 # Ensure we create a new changeset in the same phase than
1659 # the old one.
1660 # the old one.
1660 n = newcommit(repo, oldphase, message, user, ph.date,
1661 n = newcommit(repo, oldphase, message, user, ph.date,
1661 match=match, force=True)
1662 match=match, force=True)
1662 # only write patch after a successful commit
1663 # only write patch after a successful commit
1663 c = [list(x) for x in refreshchanges]
1664 c = [list(x) for x in refreshchanges]
1664 if inclsubs:
1665 if inclsubs:
1665 self.putsubstate2changes(substatestate, c)
1666 self.putsubstate2changes(substatestate, c)
1666 chunks = patchmod.diff(repo, patchparent,
1667 chunks = patchmod.diff(repo, patchparent,
1667 changes=c, opts=diffopts)
1668 changes=c, opts=diffopts)
1668 for chunk in chunks:
1669 for chunk in chunks:
1669 patchf.write(chunk)
1670 patchf.write(chunk)
1670 patchf.close()
1671 patchf.close()
1671
1672
1672 marks = repo._bookmarks
1673 marks = repo._bookmarks
1673 for bm in bmlist:
1674 for bm in bmlist:
1674 marks[bm] = n
1675 marks[bm] = n
1675 marks.write()
1676 marks.write()
1676
1677
1677 self.applied.append(statusentry(n, patchfn))
1678 self.applied.append(statusentry(n, patchfn))
1678 except: # re-raises
1679 except: # re-raises
1679 ctx = repo[cparents[0]]
1680 ctx = repo[cparents[0]]
1680 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1681 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1681 self.savedirty()
1682 self.savedirty()
1682 self.ui.warn(_('refresh interrupted while patch was popped! '
1683 self.ui.warn(_('refresh interrupted while patch was popped! '
1683 '(revert --all, qpush to recover)\n'))
1684 '(revert --all, qpush to recover)\n'))
1684 raise
1685 raise
1685 finally:
1686 finally:
1686 wlock.release()
1687 wlock.release()
1687 self.removeundo(repo)
1688 self.removeundo(repo)
1688
1689
1689 def init(self, repo, create=False):
1690 def init(self, repo, create=False):
1690 if not create and os.path.isdir(self.path):
1691 if not create and os.path.isdir(self.path):
1691 raise util.Abort(_("patch queue directory already exists"))
1692 raise util.Abort(_("patch queue directory already exists"))
1692 try:
1693 try:
1693 os.mkdir(self.path)
1694 os.mkdir(self.path)
1694 except OSError, inst:
1695 except OSError, inst:
1695 if inst.errno != errno.EEXIST or not create:
1696 if inst.errno != errno.EEXIST or not create:
1696 raise
1697 raise
1697 if create:
1698 if create:
1698 return self.qrepo(create=True)
1699 return self.qrepo(create=True)
1699
1700
1700 def unapplied(self, repo, patch=None):
1701 def unapplied(self, repo, patch=None):
1701 if patch and patch not in self.series:
1702 if patch and patch not in self.series:
1702 raise util.Abort(_("patch %s is not in series file") % patch)
1703 raise util.Abort(_("patch %s is not in series file") % patch)
1703 if not patch:
1704 if not patch:
1704 start = self.seriesend()
1705 start = self.seriesend()
1705 else:
1706 else:
1706 start = self.series.index(patch) + 1
1707 start = self.series.index(patch) + 1
1707 unapplied = []
1708 unapplied = []
1708 for i in xrange(start, len(self.series)):
1709 for i in xrange(start, len(self.series)):
1709 pushable, reason = self.pushable(i)
1710 pushable, reason = self.pushable(i)
1710 if pushable:
1711 if pushable:
1711 unapplied.append((i, self.series[i]))
1712 unapplied.append((i, self.series[i]))
1712 self.explainpushable(i)
1713 self.explainpushable(i)
1713 return unapplied
1714 return unapplied
1714
1715
1715 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1716 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1716 summary=False):
1717 summary=False):
1717 def displayname(pfx, patchname, state):
1718 def displayname(pfx, patchname, state):
1718 if pfx:
1719 if pfx:
1719 self.ui.write(pfx)
1720 self.ui.write(pfx)
1720 if summary:
1721 if summary:
1721 ph = patchheader(self.join(patchname), self.plainmode)
1722 ph = patchheader(self.join(patchname), self.plainmode)
1722 msg = ph.message and ph.message[0] or ''
1723 msg = ph.message and ph.message[0] or ''
1723 if self.ui.formatted():
1724 if self.ui.formatted():
1724 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1725 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1725 if width > 0:
1726 if width > 0:
1726 msg = util.ellipsis(msg, width)
1727 msg = util.ellipsis(msg, width)
1727 else:
1728 else:
1728 msg = ''
1729 msg = ''
1729 self.ui.write(patchname, label='qseries.' + state)
1730 self.ui.write(patchname, label='qseries.' + state)
1730 self.ui.write(': ')
1731 self.ui.write(': ')
1731 self.ui.write(msg, label='qseries.message.' + state)
1732 self.ui.write(msg, label='qseries.message.' + state)
1732 else:
1733 else:
1733 self.ui.write(patchname, label='qseries.' + state)
1734 self.ui.write(patchname, label='qseries.' + state)
1734 self.ui.write('\n')
1735 self.ui.write('\n')
1735
1736
1736 applied = set([p.name for p in self.applied])
1737 applied = set([p.name for p in self.applied])
1737 if length is None:
1738 if length is None:
1738 length = len(self.series) - start
1739 length = len(self.series) - start
1739 if not missing:
1740 if not missing:
1740 if self.ui.verbose:
1741 if self.ui.verbose:
1741 idxwidth = len(str(start + length - 1))
1742 idxwidth = len(str(start + length - 1))
1742 for i in xrange(start, start + length):
1743 for i in xrange(start, start + length):
1743 patch = self.series[i]
1744 patch = self.series[i]
1744 if patch in applied:
1745 if patch in applied:
1745 char, state = 'A', 'applied'
1746 char, state = 'A', 'applied'
1746 elif self.pushable(i)[0]:
1747 elif self.pushable(i)[0]:
1747 char, state = 'U', 'unapplied'
1748 char, state = 'U', 'unapplied'
1748 else:
1749 else:
1749 char, state = 'G', 'guarded'
1750 char, state = 'G', 'guarded'
1750 pfx = ''
1751 pfx = ''
1751 if self.ui.verbose:
1752 if self.ui.verbose:
1752 pfx = '%*d %s ' % (idxwidth, i, char)
1753 pfx = '%*d %s ' % (idxwidth, i, char)
1753 elif status and status != char:
1754 elif status and status != char:
1754 continue
1755 continue
1755 displayname(pfx, patch, state)
1756 displayname(pfx, patch, state)
1756 else:
1757 else:
1757 msng_list = []
1758 msng_list = []
1758 for root, dirs, files in os.walk(self.path):
1759 for root, dirs, files in os.walk(self.path):
1759 d = root[len(self.path) + 1:]
1760 d = root[len(self.path) + 1:]
1760 for f in files:
1761 for f in files:
1761 fl = os.path.join(d, f)
1762 fl = os.path.join(d, f)
1762 if (fl not in self.series and
1763 if (fl not in self.series and
1763 fl not in (self.statuspath, self.seriespath,
1764 fl not in (self.statuspath, self.seriespath,
1764 self.guardspath)
1765 self.guardspath)
1765 and not fl.startswith('.')):
1766 and not fl.startswith('.')):
1766 msng_list.append(fl)
1767 msng_list.append(fl)
1767 for x in sorted(msng_list):
1768 for x in sorted(msng_list):
1768 pfx = self.ui.verbose and ('D ') or ''
1769 pfx = self.ui.verbose and ('D ') or ''
1769 displayname(pfx, x, 'missing')
1770 displayname(pfx, x, 'missing')
1770
1771
1771 def issaveline(self, l):
1772 def issaveline(self, l):
1772 if l.name == '.hg.patches.save.line':
1773 if l.name == '.hg.patches.save.line':
1773 return True
1774 return True
1774
1775
1775 def qrepo(self, create=False):
1776 def qrepo(self, create=False):
1776 ui = self.baseui.copy()
1777 ui = self.baseui.copy()
1777 if create or os.path.isdir(self.join(".hg")):
1778 if create or os.path.isdir(self.join(".hg")):
1778 return hg.repository(ui, path=self.path, create=create)
1779 return hg.repository(ui, path=self.path, create=create)
1779
1780
1780 def restore(self, repo, rev, delete=None, qupdate=None):
1781 def restore(self, repo, rev, delete=None, qupdate=None):
1781 desc = repo[rev].description().strip()
1782 desc = repo[rev].description().strip()
1782 lines = desc.splitlines()
1783 lines = desc.splitlines()
1783 i = 0
1784 i = 0
1784 datastart = None
1785 datastart = None
1785 series = []
1786 series = []
1786 applied = []
1787 applied = []
1787 qpp = None
1788 qpp = None
1788 for i, line in enumerate(lines):
1789 for i, line in enumerate(lines):
1789 if line == 'Patch Data:':
1790 if line == 'Patch Data:':
1790 datastart = i + 1
1791 datastart = i + 1
1791 elif line.startswith('Dirstate:'):
1792 elif line.startswith('Dirstate:'):
1792 l = line.rstrip()
1793 l = line.rstrip()
1793 l = l[10:].split(' ')
1794 l = l[10:].split(' ')
1794 qpp = [bin(x) for x in l]
1795 qpp = [bin(x) for x in l]
1795 elif datastart is not None:
1796 elif datastart is not None:
1796 l = line.rstrip()
1797 l = line.rstrip()
1797 n, name = l.split(':', 1)
1798 n, name = l.split(':', 1)
1798 if n:
1799 if n:
1799 applied.append(statusentry(bin(n), name))
1800 applied.append(statusentry(bin(n), name))
1800 else:
1801 else:
1801 series.append(l)
1802 series.append(l)
1802 if datastart is None:
1803 if datastart is None:
1803 self.ui.warn(_("no saved patch data found\n"))
1804 self.ui.warn(_("no saved patch data found\n"))
1804 return 1
1805 return 1
1805 self.ui.warn(_("restoring status: %s\n") % lines[0])
1806 self.ui.warn(_("restoring status: %s\n") % lines[0])
1806 self.fullseries = series
1807 self.fullseries = series
1807 self.applied = applied
1808 self.applied = applied
1808 self.parseseries()
1809 self.parseseries()
1809 self.seriesdirty = True
1810 self.seriesdirty = True
1810 self.applieddirty = True
1811 self.applieddirty = True
1811 heads = repo.changelog.heads()
1812 heads = repo.changelog.heads()
1812 if delete:
1813 if delete:
1813 if rev not in heads:
1814 if rev not in heads:
1814 self.ui.warn(_("save entry has children, leaving it alone\n"))
1815 self.ui.warn(_("save entry has children, leaving it alone\n"))
1815 else:
1816 else:
1816 self.ui.warn(_("removing save entry %s\n") % short(rev))
1817 self.ui.warn(_("removing save entry %s\n") % short(rev))
1817 pp = repo.dirstate.parents()
1818 pp = repo.dirstate.parents()
1818 if rev in pp:
1819 if rev in pp:
1819 update = True
1820 update = True
1820 else:
1821 else:
1821 update = False
1822 update = False
1822 strip(self.ui, repo, [rev], update=update, backup='strip')
1823 strip(self.ui, repo, [rev], update=update, backup='strip')
1823 if qpp:
1824 if qpp:
1824 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1825 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1825 (short(qpp[0]), short(qpp[1])))
1826 (short(qpp[0]), short(qpp[1])))
1826 if qupdate:
1827 if qupdate:
1827 self.ui.status(_("updating queue directory\n"))
1828 self.ui.status(_("updating queue directory\n"))
1828 r = self.qrepo()
1829 r = self.qrepo()
1829 if not r:
1830 if not r:
1830 self.ui.warn(_("unable to load queue repository\n"))
1831 self.ui.warn(_("unable to load queue repository\n"))
1831 return 1
1832 return 1
1832 hg.clean(r, qpp[0])
1833 hg.clean(r, qpp[0])
1833
1834
1834 def save(self, repo, msg=None):
1835 def save(self, repo, msg=None):
1835 if not self.applied:
1836 if not self.applied:
1836 self.ui.warn(_("save: no patches applied, exiting\n"))
1837 self.ui.warn(_("save: no patches applied, exiting\n"))
1837 return 1
1838 return 1
1838 if self.issaveline(self.applied[-1]):
1839 if self.issaveline(self.applied[-1]):
1839 self.ui.warn(_("status is already saved\n"))
1840 self.ui.warn(_("status is already saved\n"))
1840 return 1
1841 return 1
1841
1842
1842 if not msg:
1843 if not msg:
1843 msg = _("hg patches saved state")
1844 msg = _("hg patches saved state")
1844 else:
1845 else:
1845 msg = "hg patches: " + msg.rstrip('\r\n')
1846 msg = "hg patches: " + msg.rstrip('\r\n')
1846 r = self.qrepo()
1847 r = self.qrepo()
1847 if r:
1848 if r:
1848 pp = r.dirstate.parents()
1849 pp = r.dirstate.parents()
1849 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1850 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1850 msg += "\n\nPatch Data:\n"
1851 msg += "\n\nPatch Data:\n"
1851 msg += ''.join('%s\n' % x for x in self.applied)
1852 msg += ''.join('%s\n' % x for x in self.applied)
1852 msg += ''.join(':%s\n' % x for x in self.fullseries)
1853 msg += ''.join(':%s\n' % x for x in self.fullseries)
1853 n = repo.commit(msg, force=True)
1854 n = repo.commit(msg, force=True)
1854 if not n:
1855 if not n:
1855 self.ui.warn(_("repo commit failed\n"))
1856 self.ui.warn(_("repo commit failed\n"))
1856 return 1
1857 return 1
1857 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1858 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1858 self.applieddirty = True
1859 self.applieddirty = True
1859 self.removeundo(repo)
1860 self.removeundo(repo)
1860
1861
1861 def fullseriesend(self):
1862 def fullseriesend(self):
1862 if self.applied:
1863 if self.applied:
1863 p = self.applied[-1].name
1864 p = self.applied[-1].name
1864 end = self.findseries(p)
1865 end = self.findseries(p)
1865 if end is None:
1866 if end is None:
1866 return len(self.fullseries)
1867 return len(self.fullseries)
1867 return end + 1
1868 return end + 1
1868 return 0
1869 return 0
1869
1870
1870 def seriesend(self, all_patches=False):
1871 def seriesend(self, all_patches=False):
1871 """If all_patches is False, return the index of the next pushable patch
1872 """If all_patches is False, return the index of the next pushable patch
1872 in the series, or the series length. If all_patches is True, return the
1873 in the series, or the series length. If all_patches is True, return the
1873 index of the first patch past the last applied one.
1874 index of the first patch past the last applied one.
1874 """
1875 """
1875 end = 0
1876 end = 0
1876 def nextpatch(start):
1877 def nextpatch(start):
1877 if all_patches or start >= len(self.series):
1878 if all_patches or start >= len(self.series):
1878 return start
1879 return start
1879 for i in xrange(start, len(self.series)):
1880 for i in xrange(start, len(self.series)):
1880 p, reason = self.pushable(i)
1881 p, reason = self.pushable(i)
1881 if p:
1882 if p:
1882 return i
1883 return i
1883 self.explainpushable(i)
1884 self.explainpushable(i)
1884 return len(self.series)
1885 return len(self.series)
1885 if self.applied:
1886 if self.applied:
1886 p = self.applied[-1].name
1887 p = self.applied[-1].name
1887 try:
1888 try:
1888 end = self.series.index(p)
1889 end = self.series.index(p)
1889 except ValueError:
1890 except ValueError:
1890 return 0
1891 return 0
1891 return nextpatch(end + 1)
1892 return nextpatch(end + 1)
1892 return nextpatch(end)
1893 return nextpatch(end)
1893
1894
1894 def appliedname(self, index):
1895 def appliedname(self, index):
1895 pname = self.applied[index].name
1896 pname = self.applied[index].name
1896 if not self.ui.verbose:
1897 if not self.ui.verbose:
1897 p = pname
1898 p = pname
1898 else:
1899 else:
1899 p = str(self.series.index(pname)) + " " + pname
1900 p = str(self.series.index(pname)) + " " + pname
1900 return p
1901 return p
1901
1902
1902 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1903 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1903 force=None, git=False):
1904 force=None, git=False):
1904 def checkseries(patchname):
1905 def checkseries(patchname):
1905 if patchname in self.series:
1906 if patchname in self.series:
1906 raise util.Abort(_('patch %s is already in the series file')
1907 raise util.Abort(_('patch %s is already in the series file')
1907 % patchname)
1908 % patchname)
1908
1909
1909 if rev:
1910 if rev:
1910 if files:
1911 if files:
1911 raise util.Abort(_('option "-r" not valid when importing '
1912 raise util.Abort(_('option "-r" not valid when importing '
1912 'files'))
1913 'files'))
1913 rev = scmutil.revrange(repo, rev)
1914 rev = scmutil.revrange(repo, rev)
1914 rev.sort(reverse=True)
1915 rev.sort(reverse=True)
1915 elif not files:
1916 elif not files:
1916 raise util.Abort(_('no files or revisions specified'))
1917 raise util.Abort(_('no files or revisions specified'))
1917 if (len(files) > 1 or len(rev) > 1) and patchname:
1918 if (len(files) > 1 or len(rev) > 1) and patchname:
1918 raise util.Abort(_('option "-n" not valid when importing multiple '
1919 raise util.Abort(_('option "-n" not valid when importing multiple '
1919 'patches'))
1920 'patches'))
1920 imported = []
1921 imported = []
1921 if rev:
1922 if rev:
1922 # If mq patches are applied, we can only import revisions
1923 # If mq patches are applied, we can only import revisions
1923 # that form a linear path to qbase.
1924 # that form a linear path to qbase.
1924 # Otherwise, they should form a linear path to a head.
1925 # Otherwise, they should form a linear path to a head.
1925 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1926 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1926 if len(heads) > 1:
1927 if len(heads) > 1:
1927 raise util.Abort(_('revision %d is the root of more than one '
1928 raise util.Abort(_('revision %d is the root of more than one '
1928 'branch') % rev[-1])
1929 'branch') % rev[-1])
1929 if self.applied:
1930 if self.applied:
1930 base = repo.changelog.node(rev[0])
1931 base = repo.changelog.node(rev[0])
1931 if base in [n.node for n in self.applied]:
1932 if base in [n.node for n in self.applied]:
1932 raise util.Abort(_('revision %d is already managed')
1933 raise util.Abort(_('revision %d is already managed')
1933 % rev[0])
1934 % rev[0])
1934 if heads != [self.applied[-1].node]:
1935 if heads != [self.applied[-1].node]:
1935 raise util.Abort(_('revision %d is not the parent of '
1936 raise util.Abort(_('revision %d is not the parent of '
1936 'the queue') % rev[0])
1937 'the queue') % rev[0])
1937 base = repo.changelog.rev(self.applied[0].node)
1938 base = repo.changelog.rev(self.applied[0].node)
1938 lastparent = repo.changelog.parentrevs(base)[0]
1939 lastparent = repo.changelog.parentrevs(base)[0]
1939 else:
1940 else:
1940 if heads != [repo.changelog.node(rev[0])]:
1941 if heads != [repo.changelog.node(rev[0])]:
1941 raise util.Abort(_('revision %d has unmanaged children')
1942 raise util.Abort(_('revision %d has unmanaged children')
1942 % rev[0])
1943 % rev[0])
1943 lastparent = None
1944 lastparent = None
1944
1945
1945 diffopts = self.diffopts({'git': git})
1946 diffopts = self.diffopts({'git': git})
1946 for r in rev:
1947 for r in rev:
1947 if not repo[r].mutable():
1948 if not repo[r].mutable():
1948 raise util.Abort(_('revision %d is not mutable') % r,
1949 raise util.Abort(_('revision %d is not mutable') % r,
1949 hint=_('see "hg help phases" for details'))
1950 hint=_('see "hg help phases" for details'))
1950 p1, p2 = repo.changelog.parentrevs(r)
1951 p1, p2 = repo.changelog.parentrevs(r)
1951 n = repo.changelog.node(r)
1952 n = repo.changelog.node(r)
1952 if p2 != nullrev:
1953 if p2 != nullrev:
1953 raise util.Abort(_('cannot import merge revision %d') % r)
1954 raise util.Abort(_('cannot import merge revision %d') % r)
1954 if lastparent and lastparent != r:
1955 if lastparent and lastparent != r:
1955 raise util.Abort(_('revision %d is not the parent of %d')
1956 raise util.Abort(_('revision %d is not the parent of %d')
1956 % (r, lastparent))
1957 % (r, lastparent))
1957 lastparent = p1
1958 lastparent = p1
1958
1959
1959 if not patchname:
1960 if not patchname:
1960 patchname = normname('%d.diff' % r)
1961 patchname = normname('%d.diff' % r)
1961 checkseries(patchname)
1962 checkseries(patchname)
1962 self.checkpatchname(patchname, force)
1963 self.checkpatchname(patchname, force)
1963 self.fullseries.insert(0, patchname)
1964 self.fullseries.insert(0, patchname)
1964
1965
1965 patchf = self.opener(patchname, "w")
1966 patchf = self.opener(patchname, "w")
1966 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1967 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1967 patchf.close()
1968 patchf.close()
1968
1969
1969 se = statusentry(n, patchname)
1970 se = statusentry(n, patchname)
1970 self.applied.insert(0, se)
1971 self.applied.insert(0, se)
1971
1972
1972 self.added.append(patchname)
1973 self.added.append(patchname)
1973 imported.append(patchname)
1974 imported.append(patchname)
1974 patchname = None
1975 patchname = None
1975 if rev and repo.ui.configbool('mq', 'secret', False):
1976 if rev and repo.ui.configbool('mq', 'secret', False):
1976 # if we added anything with --rev, we must move the secret root
1977 # if we added anything with --rev, we must move the secret root
1977 phases.retractboundary(repo, phases.secret, [n])
1978 phases.retractboundary(repo, phases.secret, [n])
1978 self.parseseries()
1979 self.parseseries()
1979 self.applieddirty = True
1980 self.applieddirty = True
1980 self.seriesdirty = True
1981 self.seriesdirty = True
1981
1982
1982 for i, filename in enumerate(files):
1983 for i, filename in enumerate(files):
1983 if existing:
1984 if existing:
1984 if filename == '-':
1985 if filename == '-':
1985 raise util.Abort(_('-e is incompatible with import from -'))
1986 raise util.Abort(_('-e is incompatible with import from -'))
1986 filename = normname(filename)
1987 filename = normname(filename)
1987 self.checkreservedname(filename)
1988 self.checkreservedname(filename)
1988 originpath = self.join(filename)
1989 originpath = self.join(filename)
1989 if not os.path.isfile(originpath):
1990 if not os.path.isfile(originpath):
1990 raise util.Abort(_("patch %s does not exist") % filename)
1991 raise util.Abort(_("patch %s does not exist") % filename)
1991
1992
1992 if patchname:
1993 if patchname:
1993 self.checkpatchname(patchname, force)
1994 self.checkpatchname(patchname, force)
1994
1995
1995 self.ui.write(_('renaming %s to %s\n')
1996 self.ui.write(_('renaming %s to %s\n')
1996 % (filename, patchname))
1997 % (filename, patchname))
1997 util.rename(originpath, self.join(patchname))
1998 util.rename(originpath, self.join(patchname))
1998 else:
1999 else:
1999 patchname = filename
2000 patchname = filename
2000
2001
2001 else:
2002 else:
2002 if filename == '-' and not patchname:
2003 if filename == '-' and not patchname:
2003 raise util.Abort(_('need --name to import a patch from -'))
2004 raise util.Abort(_('need --name to import a patch from -'))
2004 elif not patchname:
2005 elif not patchname:
2005 patchname = normname(os.path.basename(filename.rstrip('/')))
2006 patchname = normname(os.path.basename(filename.rstrip('/')))
2006 self.checkpatchname(patchname, force)
2007 self.checkpatchname(patchname, force)
2007 try:
2008 try:
2008 if filename == '-':
2009 if filename == '-':
2009 text = self.ui.fin.read()
2010 text = self.ui.fin.read()
2010 else:
2011 else:
2011 fp = hg.openpath(self.ui, filename)
2012 fp = hg.openpath(self.ui, filename)
2012 text = fp.read()
2013 text = fp.read()
2013 fp.close()
2014 fp.close()
2014 except (OSError, IOError):
2015 except (OSError, IOError):
2015 raise util.Abort(_("unable to read file %s") % filename)
2016 raise util.Abort(_("unable to read file %s") % filename)
2016 patchf = self.opener(patchname, "w")
2017 patchf = self.opener(patchname, "w")
2017 patchf.write(text)
2018 patchf.write(text)
2018 patchf.close()
2019 patchf.close()
2019 if not force:
2020 if not force:
2020 checkseries(patchname)
2021 checkseries(patchname)
2021 if patchname not in self.series:
2022 if patchname not in self.series:
2022 index = self.fullseriesend() + i
2023 index = self.fullseriesend() + i
2023 self.fullseries[index:index] = [patchname]
2024 self.fullseries[index:index] = [patchname]
2024 self.parseseries()
2025 self.parseseries()
2025 self.seriesdirty = True
2026 self.seriesdirty = True
2026 self.ui.warn(_("adding %s to series file\n") % patchname)
2027 self.ui.warn(_("adding %s to series file\n") % patchname)
2027 self.added.append(patchname)
2028 self.added.append(patchname)
2028 imported.append(patchname)
2029 imported.append(patchname)
2029 patchname = None
2030 patchname = None
2030
2031
2031 self.removeundo(repo)
2032 self.removeundo(repo)
2032 return imported
2033 return imported
2033
2034
2034 def fixkeepchangesopts(ui, opts):
2035 def fixkeepchangesopts(ui, opts):
2035 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2036 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2036 or opts.get('exact')):
2037 or opts.get('exact')):
2037 return opts
2038 return opts
2038 opts = dict(opts)
2039 opts = dict(opts)
2039 opts['keep_changes'] = True
2040 opts['keep_changes'] = True
2040 return opts
2041 return opts
2041
2042
2042 @command("qdelete|qremove|qrm",
2043 @command("qdelete|qremove|qrm",
2043 [('k', 'keep', None, _('keep patch file')),
2044 [('k', 'keep', None, _('keep patch file')),
2044 ('r', 'rev', [],
2045 ('r', 'rev', [],
2045 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2046 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2046 _('hg qdelete [-k] [PATCH]...'))
2047 _('hg qdelete [-k] [PATCH]...'))
2047 def delete(ui, repo, *patches, **opts):
2048 def delete(ui, repo, *patches, **opts):
2048 """remove patches from queue
2049 """remove patches from queue
2049
2050
2050 The patches must not be applied, and at least one patch is required. Exact
2051 The patches must not be applied, and at least one patch is required. Exact
2051 patch identifiers must be given. With -k/--keep, the patch files are
2052 patch identifiers must be given. With -k/--keep, the patch files are
2052 preserved in the patch directory.
2053 preserved in the patch directory.
2053
2054
2054 To stop managing a patch and move it into permanent history,
2055 To stop managing a patch and move it into permanent history,
2055 use the :hg:`qfinish` command."""
2056 use the :hg:`qfinish` command."""
2056 q = repo.mq
2057 q = repo.mq
2057 q.delete(repo, patches, opts)
2058 q.delete(repo, patches, opts)
2058 q.savedirty()
2059 q.savedirty()
2059 return 0
2060 return 0
2060
2061
2061 @command("qapplied",
2062 @command("qapplied",
2062 [('1', 'last', None, _('show only the preceding applied patch'))
2063 [('1', 'last', None, _('show only the preceding applied patch'))
2063 ] + seriesopts,
2064 ] + seriesopts,
2064 _('hg qapplied [-1] [-s] [PATCH]'))
2065 _('hg qapplied [-1] [-s] [PATCH]'))
2065 def applied(ui, repo, patch=None, **opts):
2066 def applied(ui, repo, patch=None, **opts):
2066 """print the patches already applied
2067 """print the patches already applied
2067
2068
2068 Returns 0 on success."""
2069 Returns 0 on success."""
2069
2070
2070 q = repo.mq
2071 q = repo.mq
2071
2072
2072 if patch:
2073 if patch:
2073 if patch not in q.series:
2074 if patch not in q.series:
2074 raise util.Abort(_("patch %s is not in series file") % patch)
2075 raise util.Abort(_("patch %s is not in series file") % patch)
2075 end = q.series.index(patch) + 1
2076 end = q.series.index(patch) + 1
2076 else:
2077 else:
2077 end = q.seriesend(True)
2078 end = q.seriesend(True)
2078
2079
2079 if opts.get('last') and not end:
2080 if opts.get('last') and not end:
2080 ui.write(_("no patches applied\n"))
2081 ui.write(_("no patches applied\n"))
2081 return 1
2082 return 1
2082 elif opts.get('last') and end == 1:
2083 elif opts.get('last') and end == 1:
2083 ui.write(_("only one patch applied\n"))
2084 ui.write(_("only one patch applied\n"))
2084 return 1
2085 return 1
2085 elif opts.get('last'):
2086 elif opts.get('last'):
2086 start = end - 2
2087 start = end - 2
2087 end = 1
2088 end = 1
2088 else:
2089 else:
2089 start = 0
2090 start = 0
2090
2091
2091 q.qseries(repo, length=end, start=start, status='A',
2092 q.qseries(repo, length=end, start=start, status='A',
2092 summary=opts.get('summary'))
2093 summary=opts.get('summary'))
2093
2094
2094
2095
2095 @command("qunapplied",
2096 @command("qunapplied",
2096 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2097 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2097 _('hg qunapplied [-1] [-s] [PATCH]'))
2098 _('hg qunapplied [-1] [-s] [PATCH]'))
2098 def unapplied(ui, repo, patch=None, **opts):
2099 def unapplied(ui, repo, patch=None, **opts):
2099 """print the patches not yet applied
2100 """print the patches not yet applied
2100
2101
2101 Returns 0 on success."""
2102 Returns 0 on success."""
2102
2103
2103 q = repo.mq
2104 q = repo.mq
2104 if patch:
2105 if patch:
2105 if patch not in q.series:
2106 if patch not in q.series:
2106 raise util.Abort(_("patch %s is not in series file") % patch)
2107 raise util.Abort(_("patch %s is not in series file") % patch)
2107 start = q.series.index(patch) + 1
2108 start = q.series.index(patch) + 1
2108 else:
2109 else:
2109 start = q.seriesend(True)
2110 start = q.seriesend(True)
2110
2111
2111 if start == len(q.series) and opts.get('first'):
2112 if start == len(q.series) and opts.get('first'):
2112 ui.write(_("all patches applied\n"))
2113 ui.write(_("all patches applied\n"))
2113 return 1
2114 return 1
2114
2115
2115 length = opts.get('first') and 1 or None
2116 length = opts.get('first') and 1 or None
2116 q.qseries(repo, start=start, length=length, status='U',
2117 q.qseries(repo, start=start, length=length, status='U',
2117 summary=opts.get('summary'))
2118 summary=opts.get('summary'))
2118
2119
2119 @command("qimport",
2120 @command("qimport",
2120 [('e', 'existing', None, _('import file in patch directory')),
2121 [('e', 'existing', None, _('import file in patch directory')),
2121 ('n', 'name', '',
2122 ('n', 'name', '',
2122 _('name of patch file'), _('NAME')),
2123 _('name of patch file'), _('NAME')),
2123 ('f', 'force', None, _('overwrite existing files')),
2124 ('f', 'force', None, _('overwrite existing files')),
2124 ('r', 'rev', [],
2125 ('r', 'rev', [],
2125 _('place existing revisions under mq control'), _('REV')),
2126 _('place existing revisions under mq control'), _('REV')),
2126 ('g', 'git', None, _('use git extended diff format')),
2127 ('g', 'git', None, _('use git extended diff format')),
2127 ('P', 'push', None, _('qpush after importing'))],
2128 ('P', 'push', None, _('qpush after importing'))],
2128 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2129 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2129 def qimport(ui, repo, *filename, **opts):
2130 def qimport(ui, repo, *filename, **opts):
2130 """import a patch or existing changeset
2131 """import a patch or existing changeset
2131
2132
2132 The patch is inserted into the series after the last applied
2133 The patch is inserted into the series after the last applied
2133 patch. If no patches have been applied, qimport prepends the patch
2134 patch. If no patches have been applied, qimport prepends the patch
2134 to the series.
2135 to the series.
2135
2136
2136 The patch will have the same name as its source file unless you
2137 The patch will have the same name as its source file unless you
2137 give it a new one with -n/--name.
2138 give it a new one with -n/--name.
2138
2139
2139 You can register an existing patch inside the patch directory with
2140 You can register an existing patch inside the patch directory with
2140 the -e/--existing flag.
2141 the -e/--existing flag.
2141
2142
2142 With -f/--force, an existing patch of the same name will be
2143 With -f/--force, an existing patch of the same name will be
2143 overwritten.
2144 overwritten.
2144
2145
2145 An existing changeset may be placed under mq control with -r/--rev
2146 An existing changeset may be placed under mq control with -r/--rev
2146 (e.g. qimport --rev . -n patch will place the current revision
2147 (e.g. qimport --rev . -n patch will place the current revision
2147 under mq control). With -g/--git, patches imported with --rev will
2148 under mq control). With -g/--git, patches imported with --rev will
2148 use the git diff format. See the diffs help topic for information
2149 use the git diff format. See the diffs help topic for information
2149 on why this is important for preserving rename/copy information
2150 on why this is important for preserving rename/copy information
2150 and permission changes. Use :hg:`qfinish` to remove changesets
2151 and permission changes. Use :hg:`qfinish` to remove changesets
2151 from mq control.
2152 from mq control.
2152
2153
2153 To import a patch from standard input, pass - as the patch file.
2154 To import a patch from standard input, pass - as the patch file.
2154 When importing from standard input, a patch name must be specified
2155 When importing from standard input, a patch name must be specified
2155 using the --name flag.
2156 using the --name flag.
2156
2157
2157 To import an existing patch while renaming it::
2158 To import an existing patch while renaming it::
2158
2159
2159 hg qimport -e existing-patch -n new-name
2160 hg qimport -e existing-patch -n new-name
2160
2161
2161 Returns 0 if import succeeded.
2162 Returns 0 if import succeeded.
2162 """
2163 """
2163 lock = repo.lock() # cause this may move phase
2164 lock = repo.lock() # cause this may move phase
2164 try:
2165 try:
2165 q = repo.mq
2166 q = repo.mq
2166 try:
2167 try:
2167 imported = q.qimport(
2168 imported = q.qimport(
2168 repo, filename, patchname=opts.get('name'),
2169 repo, filename, patchname=opts.get('name'),
2169 existing=opts.get('existing'), force=opts.get('force'),
2170 existing=opts.get('existing'), force=opts.get('force'),
2170 rev=opts.get('rev'), git=opts.get('git'))
2171 rev=opts.get('rev'), git=opts.get('git'))
2171 finally:
2172 finally:
2172 q.savedirty()
2173 q.savedirty()
2173 finally:
2174 finally:
2174 lock.release()
2175 lock.release()
2175
2176
2176 if imported and opts.get('push') and not opts.get('rev'):
2177 if imported and opts.get('push') and not opts.get('rev'):
2177 return q.push(repo, imported[-1])
2178 return q.push(repo, imported[-1])
2178 return 0
2179 return 0
2179
2180
2180 def qinit(ui, repo, create):
2181 def qinit(ui, repo, create):
2181 """initialize a new queue repository
2182 """initialize a new queue repository
2182
2183
2183 This command also creates a series file for ordering patches, and
2184 This command also creates a series file for ordering patches, and
2184 an mq-specific .hgignore file in the queue repository, to exclude
2185 an mq-specific .hgignore file in the queue repository, to exclude
2185 the status and guards files (these contain mostly transient state).
2186 the status and guards files (these contain mostly transient state).
2186
2187
2187 Returns 0 if initialization succeeded."""
2188 Returns 0 if initialization succeeded."""
2188 q = repo.mq
2189 q = repo.mq
2189 r = q.init(repo, create)
2190 r = q.init(repo, create)
2190 q.savedirty()
2191 q.savedirty()
2191 if r:
2192 if r:
2192 if not os.path.exists(r.wjoin('.hgignore')):
2193 if not os.path.exists(r.wjoin('.hgignore')):
2193 fp = r.wopener('.hgignore', 'w')
2194 fp = r.wopener('.hgignore', 'w')
2194 fp.write('^\\.hg\n')
2195 fp.write('^\\.hg\n')
2195 fp.write('^\\.mq\n')
2196 fp.write('^\\.mq\n')
2196 fp.write('syntax: glob\n')
2197 fp.write('syntax: glob\n')
2197 fp.write('status\n')
2198 fp.write('status\n')
2198 fp.write('guards\n')
2199 fp.write('guards\n')
2199 fp.close()
2200 fp.close()
2200 if not os.path.exists(r.wjoin('series')):
2201 if not os.path.exists(r.wjoin('series')):
2201 r.wopener('series', 'w').close()
2202 r.wopener('series', 'w').close()
2202 r[None].add(['.hgignore', 'series'])
2203 r[None].add(['.hgignore', 'series'])
2203 commands.add(ui, r)
2204 commands.add(ui, r)
2204 return 0
2205 return 0
2205
2206
2206 @command("^qinit",
2207 @command("^qinit",
2207 [('c', 'create-repo', None, _('create queue repository'))],
2208 [('c', 'create-repo', None, _('create queue repository'))],
2208 _('hg qinit [-c]'))
2209 _('hg qinit [-c]'))
2209 def init(ui, repo, **opts):
2210 def init(ui, repo, **opts):
2210 """init a new queue repository (DEPRECATED)
2211 """init a new queue repository (DEPRECATED)
2211
2212
2212 The queue repository is unversioned by default. If
2213 The queue repository is unversioned by default. If
2213 -c/--create-repo is specified, qinit will create a separate nested
2214 -c/--create-repo is specified, qinit will create a separate nested
2214 repository for patches (qinit -c may also be run later to convert
2215 repository for patches (qinit -c may also be run later to convert
2215 an unversioned patch repository into a versioned one). You can use
2216 an unversioned patch repository into a versioned one). You can use
2216 qcommit to commit changes to this queue repository.
2217 qcommit to commit changes to this queue repository.
2217
2218
2218 This command is deprecated. Without -c, it's implied by other relevant
2219 This command is deprecated. Without -c, it's implied by other relevant
2219 commands. With -c, use :hg:`init --mq` instead."""
2220 commands. With -c, use :hg:`init --mq` instead."""
2220 return qinit(ui, repo, create=opts.get('create_repo'))
2221 return qinit(ui, repo, create=opts.get('create_repo'))
2221
2222
2222 @command("qclone",
2223 @command("qclone",
2223 [('', 'pull', None, _('use pull protocol to copy metadata')),
2224 [('', 'pull', None, _('use pull protocol to copy metadata')),
2224 ('U', 'noupdate', None,
2225 ('U', 'noupdate', None,
2225 _('do not update the new working directories')),
2226 _('do not update the new working directories')),
2226 ('', 'uncompressed', None,
2227 ('', 'uncompressed', None,
2227 _('use uncompressed transfer (fast over LAN)')),
2228 _('use uncompressed transfer (fast over LAN)')),
2228 ('p', 'patches', '',
2229 ('p', 'patches', '',
2229 _('location of source patch repository'), _('REPO')),
2230 _('location of source patch repository'), _('REPO')),
2230 ] + commands.remoteopts,
2231 ] + commands.remoteopts,
2231 _('hg qclone [OPTION]... SOURCE [DEST]'))
2232 _('hg qclone [OPTION]... SOURCE [DEST]'))
2232 def clone(ui, source, dest=None, **opts):
2233 def clone(ui, source, dest=None, **opts):
2233 '''clone main and patch repository at same time
2234 '''clone main and patch repository at same time
2234
2235
2235 If source is local, destination will have no patches applied. If
2236 If source is local, destination will have no patches applied. If
2236 source is remote, this command can not check if patches are
2237 source is remote, this command can not check if patches are
2237 applied in source, so cannot guarantee that patches are not
2238 applied in source, so cannot guarantee that patches are not
2238 applied in destination. If you clone remote repository, be sure
2239 applied in destination. If you clone remote repository, be sure
2239 before that it has no patches applied.
2240 before that it has no patches applied.
2240
2241
2241 Source patch repository is looked for in <src>/.hg/patches by
2242 Source patch repository is looked for in <src>/.hg/patches by
2242 default. Use -p <url> to change.
2243 default. Use -p <url> to change.
2243
2244
2244 The patch directory must be a nested Mercurial repository, as
2245 The patch directory must be a nested Mercurial repository, as
2245 would be created by :hg:`init --mq`.
2246 would be created by :hg:`init --mq`.
2246
2247
2247 Return 0 on success.
2248 Return 0 on success.
2248 '''
2249 '''
2249 def patchdir(repo):
2250 def patchdir(repo):
2250 """compute a patch repo url from a repo object"""
2251 """compute a patch repo url from a repo object"""
2251 url = repo.url()
2252 url = repo.url()
2252 if url.endswith('/'):
2253 if url.endswith('/'):
2253 url = url[:-1]
2254 url = url[:-1]
2254 return url + '/.hg/patches'
2255 return url + '/.hg/patches'
2255
2256
2256 # main repo (destination and sources)
2257 # main repo (destination and sources)
2257 if dest is None:
2258 if dest is None:
2258 dest = hg.defaultdest(source)
2259 dest = hg.defaultdest(source)
2259 sr = hg.peer(ui, opts, ui.expandpath(source))
2260 sr = hg.peer(ui, opts, ui.expandpath(source))
2260
2261
2261 # patches repo (source only)
2262 # patches repo (source only)
2262 if opts.get('patches'):
2263 if opts.get('patches'):
2263 patchespath = ui.expandpath(opts.get('patches'))
2264 patchespath = ui.expandpath(opts.get('patches'))
2264 else:
2265 else:
2265 patchespath = patchdir(sr)
2266 patchespath = patchdir(sr)
2266 try:
2267 try:
2267 hg.peer(ui, opts, patchespath)
2268 hg.peer(ui, opts, patchespath)
2268 except error.RepoError:
2269 except error.RepoError:
2269 raise util.Abort(_('versioned patch repository not found'
2270 raise util.Abort(_('versioned patch repository not found'
2270 ' (see init --mq)'))
2271 ' (see init --mq)'))
2271 qbase, destrev = None, None
2272 qbase, destrev = None, None
2272 if sr.local():
2273 if sr.local():
2273 repo = sr.local()
2274 repo = sr.local()
2274 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2275 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2275 qbase = repo.mq.applied[0].node
2276 qbase = repo.mq.applied[0].node
2276 if not hg.islocal(dest):
2277 if not hg.islocal(dest):
2277 heads = set(repo.heads())
2278 heads = set(repo.heads())
2278 destrev = list(heads.difference(repo.heads(qbase)))
2279 destrev = list(heads.difference(repo.heads(qbase)))
2279 destrev.append(repo.changelog.parents(qbase)[0])
2280 destrev.append(repo.changelog.parents(qbase)[0])
2280 elif sr.capable('lookup'):
2281 elif sr.capable('lookup'):
2281 try:
2282 try:
2282 qbase = sr.lookup('qbase')
2283 qbase = sr.lookup('qbase')
2283 except error.RepoError:
2284 except error.RepoError:
2284 pass
2285 pass
2285
2286
2286 ui.note(_('cloning main repository\n'))
2287 ui.note(_('cloning main repository\n'))
2287 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2288 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2288 pull=opts.get('pull'),
2289 pull=opts.get('pull'),
2289 rev=destrev,
2290 rev=destrev,
2290 update=False,
2291 update=False,
2291 stream=opts.get('uncompressed'))
2292 stream=opts.get('uncompressed'))
2292
2293
2293 ui.note(_('cloning patch repository\n'))
2294 ui.note(_('cloning patch repository\n'))
2294 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2295 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2295 pull=opts.get('pull'), update=not opts.get('noupdate'),
2296 pull=opts.get('pull'), update=not opts.get('noupdate'),
2296 stream=opts.get('uncompressed'))
2297 stream=opts.get('uncompressed'))
2297
2298
2298 if dr.local():
2299 if dr.local():
2299 repo = dr.local()
2300 repo = dr.local()
2300 if qbase:
2301 if qbase:
2301 ui.note(_('stripping applied patches from destination '
2302 ui.note(_('stripping applied patches from destination '
2302 'repository\n'))
2303 'repository\n'))
2303 strip(ui, repo, [qbase], update=False, backup=None)
2304 strip(ui, repo, [qbase], update=False, backup=None)
2304 if not opts.get('noupdate'):
2305 if not opts.get('noupdate'):
2305 ui.note(_('updating destination repository\n'))
2306 ui.note(_('updating destination repository\n'))
2306 hg.update(repo, repo.changelog.tip())
2307 hg.update(repo, repo.changelog.tip())
2307
2308
2308 @command("qcommit|qci",
2309 @command("qcommit|qci",
2309 commands.table["^commit|ci"][1],
2310 commands.table["^commit|ci"][1],
2310 _('hg qcommit [OPTION]... [FILE]...'))
2311 _('hg qcommit [OPTION]... [FILE]...'))
2311 def commit(ui, repo, *pats, **opts):
2312 def commit(ui, repo, *pats, **opts):
2312 """commit changes in the queue repository (DEPRECATED)
2313 """commit changes in the queue repository (DEPRECATED)
2313
2314
2314 This command is deprecated; use :hg:`commit --mq` instead."""
2315 This command is deprecated; use :hg:`commit --mq` instead."""
2315 q = repo.mq
2316 q = repo.mq
2316 r = q.qrepo()
2317 r = q.qrepo()
2317 if not r:
2318 if not r:
2318 raise util.Abort('no queue repository')
2319 raise util.Abort('no queue repository')
2319 commands.commit(r.ui, r, *pats, **opts)
2320 commands.commit(r.ui, r, *pats, **opts)
2320
2321
2321 @command("qseries",
2322 @command("qseries",
2322 [('m', 'missing', None, _('print patches not in series')),
2323 [('m', 'missing', None, _('print patches not in series')),
2323 ] + seriesopts,
2324 ] + seriesopts,
2324 _('hg qseries [-ms]'))
2325 _('hg qseries [-ms]'))
2325 def series(ui, repo, **opts):
2326 def series(ui, repo, **opts):
2326 """print the entire series file
2327 """print the entire series file
2327
2328
2328 Returns 0 on success."""
2329 Returns 0 on success."""
2329 repo.mq.qseries(repo, missing=opts.get('missing'),
2330 repo.mq.qseries(repo, missing=opts.get('missing'),
2330 summary=opts.get('summary'))
2331 summary=opts.get('summary'))
2331 return 0
2332 return 0
2332
2333
2333 @command("qtop", seriesopts, _('hg qtop [-s]'))
2334 @command("qtop", seriesopts, _('hg qtop [-s]'))
2334 def top(ui, repo, **opts):
2335 def top(ui, repo, **opts):
2335 """print the name of the current patch
2336 """print the name of the current patch
2336
2337
2337 Returns 0 on success."""
2338 Returns 0 on success."""
2338 q = repo.mq
2339 q = repo.mq
2339 t = q.applied and q.seriesend(True) or 0
2340 t = q.applied and q.seriesend(True) or 0
2340 if t:
2341 if t:
2341 q.qseries(repo, start=t - 1, length=1, status='A',
2342 q.qseries(repo, start=t - 1, length=1, status='A',
2342 summary=opts.get('summary'))
2343 summary=opts.get('summary'))
2343 else:
2344 else:
2344 ui.write(_("no patches applied\n"))
2345 ui.write(_("no patches applied\n"))
2345 return 1
2346 return 1
2346
2347
2347 @command("qnext", seriesopts, _('hg qnext [-s]'))
2348 @command("qnext", seriesopts, _('hg qnext [-s]'))
2348 def next(ui, repo, **opts):
2349 def next(ui, repo, **opts):
2349 """print the name of the next pushable patch
2350 """print the name of the next pushable patch
2350
2351
2351 Returns 0 on success."""
2352 Returns 0 on success."""
2352 q = repo.mq
2353 q = repo.mq
2353 end = q.seriesend()
2354 end = q.seriesend()
2354 if end == len(q.series):
2355 if end == len(q.series):
2355 ui.write(_("all patches applied\n"))
2356 ui.write(_("all patches applied\n"))
2356 return 1
2357 return 1
2357 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2358 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2358
2359
2359 @command("qprev", seriesopts, _('hg qprev [-s]'))
2360 @command("qprev", seriesopts, _('hg qprev [-s]'))
2360 def prev(ui, repo, **opts):
2361 def prev(ui, repo, **opts):
2361 """print the name of the preceding applied patch
2362 """print the name of the preceding applied patch
2362
2363
2363 Returns 0 on success."""
2364 Returns 0 on success."""
2364 q = repo.mq
2365 q = repo.mq
2365 l = len(q.applied)
2366 l = len(q.applied)
2366 if l == 1:
2367 if l == 1:
2367 ui.write(_("only one patch applied\n"))
2368 ui.write(_("only one patch applied\n"))
2368 return 1
2369 return 1
2369 if not l:
2370 if not l:
2370 ui.write(_("no patches applied\n"))
2371 ui.write(_("no patches applied\n"))
2371 return 1
2372 return 1
2372 idx = q.series.index(q.applied[-2].name)
2373 idx = q.series.index(q.applied[-2].name)
2373 q.qseries(repo, start=idx, length=1, status='A',
2374 q.qseries(repo, start=idx, length=1, status='A',
2374 summary=opts.get('summary'))
2375 summary=opts.get('summary'))
2375
2376
2376 def setupheaderopts(ui, opts):
2377 def setupheaderopts(ui, opts):
2377 if not opts.get('user') and opts.get('currentuser'):
2378 if not opts.get('user') and opts.get('currentuser'):
2378 opts['user'] = ui.username()
2379 opts['user'] = ui.username()
2379 if not opts.get('date') and opts.get('currentdate'):
2380 if not opts.get('date') and opts.get('currentdate'):
2380 opts['date'] = "%d %d" % util.makedate()
2381 opts['date'] = "%d %d" % util.makedate()
2381
2382
2382 @command("^qnew",
2383 @command("^qnew",
2383 [('e', 'edit', None, _('edit commit message')),
2384 [('e', 'edit', None, _('edit commit message')),
2384 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2385 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2385 ('g', 'git', None, _('use git extended diff format')),
2386 ('g', 'git', None, _('use git extended diff format')),
2386 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2387 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2387 ('u', 'user', '',
2388 ('u', 'user', '',
2388 _('add "From: <USER>" to patch'), _('USER')),
2389 _('add "From: <USER>" to patch'), _('USER')),
2389 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2390 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2390 ('d', 'date', '',
2391 ('d', 'date', '',
2391 _('add "Date: <DATE>" to patch'), _('DATE'))
2392 _('add "Date: <DATE>" to patch'), _('DATE'))
2392 ] + commands.walkopts + commands.commitopts,
2393 ] + commands.walkopts + commands.commitopts,
2393 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2394 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2394 def new(ui, repo, patch, *args, **opts):
2395 def new(ui, repo, patch, *args, **opts):
2395 """create a new patch
2396 """create a new patch
2396
2397
2397 qnew creates a new patch on top of the currently-applied patch (if
2398 qnew creates a new patch on top of the currently-applied patch (if
2398 any). The patch will be initialized with any outstanding changes
2399 any). The patch will be initialized with any outstanding changes
2399 in the working directory. You may also use -I/--include,
2400 in the working directory. You may also use -I/--include,
2400 -X/--exclude, and/or a list of files after the patch name to add
2401 -X/--exclude, and/or a list of files after the patch name to add
2401 only changes to matching files to the new patch, leaving the rest
2402 only changes to matching files to the new patch, leaving the rest
2402 as uncommitted modifications.
2403 as uncommitted modifications.
2403
2404
2404 -u/--user and -d/--date can be used to set the (given) user and
2405 -u/--user and -d/--date can be used to set the (given) user and
2405 date, respectively. -U/--currentuser and -D/--currentdate set user
2406 date, respectively. -U/--currentuser and -D/--currentdate set user
2406 to current user and date to current date.
2407 to current user and date to current date.
2407
2408
2408 -e/--edit, -m/--message or -l/--logfile set the patch header as
2409 -e/--edit, -m/--message or -l/--logfile set the patch header as
2409 well as the commit message. If none is specified, the header is
2410 well as the commit message. If none is specified, the header is
2410 empty and the commit message is '[mq]: PATCH'.
2411 empty and the commit message is '[mq]: PATCH'.
2411
2412
2412 Use the -g/--git option to keep the patch in the git extended diff
2413 Use the -g/--git option to keep the patch in the git extended diff
2413 format. Read the diffs help topic for more information on why this
2414 format. Read the diffs help topic for more information on why this
2414 is important for preserving permission changes and copy/rename
2415 is important for preserving permission changes and copy/rename
2415 information.
2416 information.
2416
2417
2417 Returns 0 on successful creation of a new patch.
2418 Returns 0 on successful creation of a new patch.
2418 """
2419 """
2419 msg = cmdutil.logmessage(ui, opts)
2420 msg = cmdutil.logmessage(ui, opts)
2420 def getmsg():
2421 def getmsg():
2421 return ui.edit(msg, opts.get('user') or ui.username())
2422 return ui.edit(msg, opts.get('user') or ui.username())
2422 q = repo.mq
2423 q = repo.mq
2423 opts['msg'] = msg
2424 opts['msg'] = msg
2424 if opts.get('edit'):
2425 if opts.get('edit'):
2425 opts['msg'] = getmsg
2426 opts['msg'] = getmsg
2426 else:
2427 else:
2427 opts['msg'] = msg
2428 opts['msg'] = msg
2428 setupheaderopts(ui, opts)
2429 setupheaderopts(ui, opts)
2429 q.new(repo, patch, *args, **opts)
2430 q.new(repo, patch, *args, **opts)
2430 q.savedirty()
2431 q.savedirty()
2431 return 0
2432 return 0
2432
2433
2433 @command("^qrefresh",
2434 @command("^qrefresh",
2434 [('e', 'edit', None, _('edit commit message')),
2435 [('e', 'edit', None, _('edit commit message')),
2435 ('g', 'git', None, _('use git extended diff format')),
2436 ('g', 'git', None, _('use git extended diff format')),
2436 ('s', 'short', None,
2437 ('s', 'short', None,
2437 _('refresh only files already in the patch and specified files')),
2438 _('refresh only files already in the patch and specified files')),
2438 ('U', 'currentuser', None,
2439 ('U', 'currentuser', None,
2439 _('add/update author field in patch with current user')),
2440 _('add/update author field in patch with current user')),
2440 ('u', 'user', '',
2441 ('u', 'user', '',
2441 _('add/update author field in patch with given user'), _('USER')),
2442 _('add/update author field in patch with given user'), _('USER')),
2442 ('D', 'currentdate', None,
2443 ('D', 'currentdate', None,
2443 _('add/update date field in patch with current date')),
2444 _('add/update date field in patch with current date')),
2444 ('d', 'date', '',
2445 ('d', 'date', '',
2445 _('add/update date field in patch with given date'), _('DATE'))
2446 _('add/update date field in patch with given date'), _('DATE'))
2446 ] + commands.walkopts + commands.commitopts,
2447 ] + commands.walkopts + commands.commitopts,
2447 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2448 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2448 def refresh(ui, repo, *pats, **opts):
2449 def refresh(ui, repo, *pats, **opts):
2449 """update the current patch
2450 """update the current patch
2450
2451
2451 If any file patterns are provided, the refreshed patch will
2452 If any file patterns are provided, the refreshed patch will
2452 contain only the modifications that match those patterns; the
2453 contain only the modifications that match those patterns; the
2453 remaining modifications will remain in the working directory.
2454 remaining modifications will remain in the working directory.
2454
2455
2455 If -s/--short is specified, files currently included in the patch
2456 If -s/--short is specified, files currently included in the patch
2456 will be refreshed just like matched files and remain in the patch.
2457 will be refreshed just like matched files and remain in the patch.
2457
2458
2458 If -e/--edit is specified, Mercurial will start your configured editor for
2459 If -e/--edit is specified, Mercurial will start your configured editor for
2459 you to enter a message. In case qrefresh fails, you will find a backup of
2460 you to enter a message. In case qrefresh fails, you will find a backup of
2460 your message in ``.hg/last-message.txt``.
2461 your message in ``.hg/last-message.txt``.
2461
2462
2462 hg add/remove/copy/rename work as usual, though you might want to
2463 hg add/remove/copy/rename work as usual, though you might want to
2463 use git-style patches (-g/--git or [diff] git=1) to track copies
2464 use git-style patches (-g/--git or [diff] git=1) to track copies
2464 and renames. See the diffs help topic for more information on the
2465 and renames. See the diffs help topic for more information on the
2465 git diff format.
2466 git diff format.
2466
2467
2467 Returns 0 on success.
2468 Returns 0 on success.
2468 """
2469 """
2469 q = repo.mq
2470 q = repo.mq
2470 message = cmdutil.logmessage(ui, opts)
2471 message = cmdutil.logmessage(ui, opts)
2471 if opts.get('edit'):
2472 if opts.get('edit'):
2472 if not q.applied:
2473 if not q.applied:
2473 ui.write(_("no patches applied\n"))
2474 ui.write(_("no patches applied\n"))
2474 return 1
2475 return 1
2475 if message:
2476 if message:
2476 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2477 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2477 patch = q.applied[-1].name
2478 patch = q.applied[-1].name
2478 ph = patchheader(q.join(patch), q.plainmode)
2479 ph = patchheader(q.join(patch), q.plainmode)
2479 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2480 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2480 # We don't want to lose the patch message if qrefresh fails (issue2062)
2481 # We don't want to lose the patch message if qrefresh fails (issue2062)
2481 repo.savecommitmessage(message)
2482 repo.savecommitmessage(message)
2482 setupheaderopts(ui, opts)
2483 setupheaderopts(ui, opts)
2483 wlock = repo.wlock()
2484 wlock = repo.wlock()
2484 try:
2485 try:
2485 ret = q.refresh(repo, pats, msg=message, **opts)
2486 ret = q.refresh(repo, pats, msg=message, **opts)
2486 q.savedirty()
2487 q.savedirty()
2487 return ret
2488 return ret
2488 finally:
2489 finally:
2489 wlock.release()
2490 wlock.release()
2490
2491
2491 @command("^qdiff",
2492 @command("^qdiff",
2492 commands.diffopts + commands.diffopts2 + commands.walkopts,
2493 commands.diffopts + commands.diffopts2 + commands.walkopts,
2493 _('hg qdiff [OPTION]... [FILE]...'))
2494 _('hg qdiff [OPTION]... [FILE]...'))
2494 def diff(ui, repo, *pats, **opts):
2495 def diff(ui, repo, *pats, **opts):
2495 """diff of the current patch and subsequent modifications
2496 """diff of the current patch and subsequent modifications
2496
2497
2497 Shows a diff which includes the current patch as well as any
2498 Shows a diff which includes the current patch as well as any
2498 changes which have been made in the working directory since the
2499 changes which have been made in the working directory since the
2499 last refresh (thus showing what the current patch would become
2500 last refresh (thus showing what the current patch would become
2500 after a qrefresh).
2501 after a qrefresh).
2501
2502
2502 Use :hg:`diff` if you only want to see the changes made since the
2503 Use :hg:`diff` if you only want to see the changes made since the
2503 last qrefresh, or :hg:`export qtip` if you want to see changes
2504 last qrefresh, or :hg:`export qtip` if you want to see changes
2504 made by the current patch without including changes made since the
2505 made by the current patch without including changes made since the
2505 qrefresh.
2506 qrefresh.
2506
2507
2507 Returns 0 on success.
2508 Returns 0 on success.
2508 """
2509 """
2509 repo.mq.diff(repo, pats, opts)
2510 repo.mq.diff(repo, pats, opts)
2510 return 0
2511 return 0
2511
2512
2512 @command('qfold',
2513 @command('qfold',
2513 [('e', 'edit', None, _('edit patch header')),
2514 [('e', 'edit', None, _('edit patch header')),
2514 ('k', 'keep', None, _('keep folded patch files')),
2515 ('k', 'keep', None, _('keep folded patch files')),
2515 ] + commands.commitopts,
2516 ] + commands.commitopts,
2516 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2517 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2517 def fold(ui, repo, *files, **opts):
2518 def fold(ui, repo, *files, **opts):
2518 """fold the named patches into the current patch
2519 """fold the named patches into the current patch
2519
2520
2520 Patches must not yet be applied. Each patch will be successively
2521 Patches must not yet be applied. Each patch will be successively
2521 applied to the current patch in the order given. If all the
2522 applied to the current patch in the order given. If all the
2522 patches apply successfully, the current patch will be refreshed
2523 patches apply successfully, the current patch will be refreshed
2523 with the new cumulative patch, and the folded patches will be
2524 with the new cumulative patch, and the folded patches will be
2524 deleted. With -k/--keep, the folded patch files will not be
2525 deleted. With -k/--keep, the folded patch files will not be
2525 removed afterwards.
2526 removed afterwards.
2526
2527
2527 The header for each folded patch will be concatenated with the
2528 The header for each folded patch will be concatenated with the
2528 current patch header, separated by a line of ``* * *``.
2529 current patch header, separated by a line of ``* * *``.
2529
2530
2530 Returns 0 on success."""
2531 Returns 0 on success."""
2531 q = repo.mq
2532 q = repo.mq
2532 if not files:
2533 if not files:
2533 raise util.Abort(_('qfold requires at least one patch name'))
2534 raise util.Abort(_('qfold requires at least one patch name'))
2534 if not q.checktoppatch(repo)[0]:
2535 if not q.checktoppatch(repo)[0]:
2535 raise util.Abort(_('no patches applied'))
2536 raise util.Abort(_('no patches applied'))
2536 q.checklocalchanges(repo)
2537 q.checklocalchanges(repo)
2537
2538
2538 message = cmdutil.logmessage(ui, opts)
2539 message = cmdutil.logmessage(ui, opts)
2539 if opts.get('edit'):
2540 if opts.get('edit'):
2540 if message:
2541 if message:
2541 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2542 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2542
2543
2543 parent = q.lookup('qtip')
2544 parent = q.lookup('qtip')
2544 patches = []
2545 patches = []
2545 messages = []
2546 messages = []
2546 for f in files:
2547 for f in files:
2547 p = q.lookup(f)
2548 p = q.lookup(f)
2548 if p in patches or p == parent:
2549 if p in patches or p == parent:
2549 ui.warn(_('skipping already folded patch %s\n') % p)
2550 ui.warn(_('skipping already folded patch %s\n') % p)
2550 if q.isapplied(p):
2551 if q.isapplied(p):
2551 raise util.Abort(_('qfold cannot fold already applied patch %s')
2552 raise util.Abort(_('qfold cannot fold already applied patch %s')
2552 % p)
2553 % p)
2553 patches.append(p)
2554 patches.append(p)
2554
2555
2555 for p in patches:
2556 for p in patches:
2556 if not message:
2557 if not message:
2557 ph = patchheader(q.join(p), q.plainmode)
2558 ph = patchheader(q.join(p), q.plainmode)
2558 if ph.message:
2559 if ph.message:
2559 messages.append(ph.message)
2560 messages.append(ph.message)
2560 pf = q.join(p)
2561 pf = q.join(p)
2561 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2562 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2562 if not patchsuccess:
2563 if not patchsuccess:
2563 raise util.Abort(_('error folding patch %s') % p)
2564 raise util.Abort(_('error folding patch %s') % p)
2564
2565
2565 if not message:
2566 if not message:
2566 ph = patchheader(q.join(parent), q.plainmode)
2567 ph = patchheader(q.join(parent), q.plainmode)
2567 message, user = ph.message, ph.user
2568 message, user = ph.message, ph.user
2568 for msg in messages:
2569 for msg in messages:
2569 message.append('* * *')
2570 message.append('* * *')
2570 message.extend(msg)
2571 message.extend(msg)
2571 message = '\n'.join(message)
2572 message = '\n'.join(message)
2572
2573
2573 if opts.get('edit'):
2574 if opts.get('edit'):
2574 message = ui.edit(message, user or ui.username())
2575 message = ui.edit(message, user or ui.username())
2575
2576
2576 diffopts = q.patchopts(q.diffopts(), *patches)
2577 diffopts = q.patchopts(q.diffopts(), *patches)
2577 wlock = repo.wlock()
2578 wlock = repo.wlock()
2578 try:
2579 try:
2579 q.refresh(repo, msg=message, git=diffopts.git)
2580 q.refresh(repo, msg=message, git=diffopts.git)
2580 q.delete(repo, patches, opts)
2581 q.delete(repo, patches, opts)
2581 q.savedirty()
2582 q.savedirty()
2582 finally:
2583 finally:
2583 wlock.release()
2584 wlock.release()
2584
2585
2585 @command("qgoto",
2586 @command("qgoto",
2586 [('', 'keep-changes', None,
2587 [('', 'keep-changes', None,
2587 _('tolerate non-conflicting local changes')),
2588 _('tolerate non-conflicting local changes')),
2588 ('f', 'force', None, _('overwrite any local changes')),
2589 ('f', 'force', None, _('overwrite any local changes')),
2589 ('', 'no-backup', None, _('do not save backup copies of files'))],
2590 ('', 'no-backup', None, _('do not save backup copies of files'))],
2590 _('hg qgoto [OPTION]... PATCH'))
2591 _('hg qgoto [OPTION]... PATCH'))
2591 def goto(ui, repo, patch, **opts):
2592 def goto(ui, repo, patch, **opts):
2592 '''push or pop patches until named patch is at top of stack
2593 '''push or pop patches until named patch is at top of stack
2593
2594
2594 Returns 0 on success.'''
2595 Returns 0 on success.'''
2595 opts = fixkeepchangesopts(ui, opts)
2596 opts = fixkeepchangesopts(ui, opts)
2596 q = repo.mq
2597 q = repo.mq
2597 patch = q.lookup(patch)
2598 patch = q.lookup(patch)
2598 nobackup = opts.get('no_backup')
2599 nobackup = opts.get('no_backup')
2599 keepchanges = opts.get('keep_changes')
2600 keepchanges = opts.get('keep_changes')
2600 if q.isapplied(patch):
2601 if q.isapplied(patch):
2601 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2602 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2602 keepchanges=keepchanges)
2603 keepchanges=keepchanges)
2603 else:
2604 else:
2604 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2605 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2605 keepchanges=keepchanges)
2606 keepchanges=keepchanges)
2606 q.savedirty()
2607 q.savedirty()
2607 return ret
2608 return ret
2608
2609
2609 @command("qguard",
2610 @command("qguard",
2610 [('l', 'list', None, _('list all patches and guards')),
2611 [('l', 'list', None, _('list all patches and guards')),
2611 ('n', 'none', None, _('drop all guards'))],
2612 ('n', 'none', None, _('drop all guards'))],
2612 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2613 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2613 def guard(ui, repo, *args, **opts):
2614 def guard(ui, repo, *args, **opts):
2614 '''set or print guards for a patch
2615 '''set or print guards for a patch
2615
2616
2616 Guards control whether a patch can be pushed. A patch with no
2617 Guards control whether a patch can be pushed. A patch with no
2617 guards is always pushed. A patch with a positive guard ("+foo") is
2618 guards is always pushed. A patch with a positive guard ("+foo") is
2618 pushed only if the :hg:`qselect` command has activated it. A patch with
2619 pushed only if the :hg:`qselect` command has activated it. A patch with
2619 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2620 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2620 has activated it.
2621 has activated it.
2621
2622
2622 With no arguments, print the currently active guards.
2623 With no arguments, print the currently active guards.
2623 With arguments, set guards for the named patch.
2624 With arguments, set guards for the named patch.
2624
2625
2625 .. note::
2626 .. note::
2626 Specifying negative guards now requires '--'.
2627 Specifying negative guards now requires '--'.
2627
2628
2628 To set guards on another patch::
2629 To set guards on another patch::
2629
2630
2630 hg qguard other.patch -- +2.6.17 -stable
2631 hg qguard other.patch -- +2.6.17 -stable
2631
2632
2632 Returns 0 on success.
2633 Returns 0 on success.
2633 '''
2634 '''
2634 def status(idx):
2635 def status(idx):
2635 guards = q.seriesguards[idx] or ['unguarded']
2636 guards = q.seriesguards[idx] or ['unguarded']
2636 if q.series[idx] in applied:
2637 if q.series[idx] in applied:
2637 state = 'applied'
2638 state = 'applied'
2638 elif q.pushable(idx)[0]:
2639 elif q.pushable(idx)[0]:
2639 state = 'unapplied'
2640 state = 'unapplied'
2640 else:
2641 else:
2641 state = 'guarded'
2642 state = 'guarded'
2642 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2643 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2643 ui.write('%s: ' % ui.label(q.series[idx], label))
2644 ui.write('%s: ' % ui.label(q.series[idx], label))
2644
2645
2645 for i, guard in enumerate(guards):
2646 for i, guard in enumerate(guards):
2646 if guard.startswith('+'):
2647 if guard.startswith('+'):
2647 ui.write(guard, label='qguard.positive')
2648 ui.write(guard, label='qguard.positive')
2648 elif guard.startswith('-'):
2649 elif guard.startswith('-'):
2649 ui.write(guard, label='qguard.negative')
2650 ui.write(guard, label='qguard.negative')
2650 else:
2651 else:
2651 ui.write(guard, label='qguard.unguarded')
2652 ui.write(guard, label='qguard.unguarded')
2652 if i != len(guards) - 1:
2653 if i != len(guards) - 1:
2653 ui.write(' ')
2654 ui.write(' ')
2654 ui.write('\n')
2655 ui.write('\n')
2655 q = repo.mq
2656 q = repo.mq
2656 applied = set(p.name for p in q.applied)
2657 applied = set(p.name for p in q.applied)
2657 patch = None
2658 patch = None
2658 args = list(args)
2659 args = list(args)
2659 if opts.get('list'):
2660 if opts.get('list'):
2660 if args or opts.get('none'):
2661 if args or opts.get('none'):
2661 raise util.Abort(_('cannot mix -l/--list with options or '
2662 raise util.Abort(_('cannot mix -l/--list with options or '
2662 'arguments'))
2663 'arguments'))
2663 for i in xrange(len(q.series)):
2664 for i in xrange(len(q.series)):
2664 status(i)
2665 status(i)
2665 return
2666 return
2666 if not args or args[0][0:1] in '-+':
2667 if not args or args[0][0:1] in '-+':
2667 if not q.applied:
2668 if not q.applied:
2668 raise util.Abort(_('no patches applied'))
2669 raise util.Abort(_('no patches applied'))
2669 patch = q.applied[-1].name
2670 patch = q.applied[-1].name
2670 if patch is None and args[0][0:1] not in '-+':
2671 if patch is None and args[0][0:1] not in '-+':
2671 patch = args.pop(0)
2672 patch = args.pop(0)
2672 if patch is None:
2673 if patch is None:
2673 raise util.Abort(_('no patch to work with'))
2674 raise util.Abort(_('no patch to work with'))
2674 if args or opts.get('none'):
2675 if args or opts.get('none'):
2675 idx = q.findseries(patch)
2676 idx = q.findseries(patch)
2676 if idx is None:
2677 if idx is None:
2677 raise util.Abort(_('no patch named %s') % patch)
2678 raise util.Abort(_('no patch named %s') % patch)
2678 q.setguards(idx, args)
2679 q.setguards(idx, args)
2679 q.savedirty()
2680 q.savedirty()
2680 else:
2681 else:
2681 status(q.series.index(q.lookup(patch)))
2682 status(q.series.index(q.lookup(patch)))
2682
2683
2683 @command("qheader", [], _('hg qheader [PATCH]'))
2684 @command("qheader", [], _('hg qheader [PATCH]'))
2684 def header(ui, repo, patch=None):
2685 def header(ui, repo, patch=None):
2685 """print the header of the topmost or specified patch
2686 """print the header of the topmost or specified patch
2686
2687
2687 Returns 0 on success."""
2688 Returns 0 on success."""
2688 q = repo.mq
2689 q = repo.mq
2689
2690
2690 if patch:
2691 if patch:
2691 patch = q.lookup(patch)
2692 patch = q.lookup(patch)
2692 else:
2693 else:
2693 if not q.applied:
2694 if not q.applied:
2694 ui.write(_('no patches applied\n'))
2695 ui.write(_('no patches applied\n'))
2695 return 1
2696 return 1
2696 patch = q.lookup('qtip')
2697 patch = q.lookup('qtip')
2697 ph = patchheader(q.join(patch), q.plainmode)
2698 ph = patchheader(q.join(patch), q.plainmode)
2698
2699
2699 ui.write('\n'.join(ph.message) + '\n')
2700 ui.write('\n'.join(ph.message) + '\n')
2700
2701
2701 def lastsavename(path):
2702 def lastsavename(path):
2702 (directory, base) = os.path.split(path)
2703 (directory, base) = os.path.split(path)
2703 names = os.listdir(directory)
2704 names = os.listdir(directory)
2704 namere = re.compile("%s.([0-9]+)" % base)
2705 namere = re.compile("%s.([0-9]+)" % base)
2705 maxindex = None
2706 maxindex = None
2706 maxname = None
2707 maxname = None
2707 for f in names:
2708 for f in names:
2708 m = namere.match(f)
2709 m = namere.match(f)
2709 if m:
2710 if m:
2710 index = int(m.group(1))
2711 index = int(m.group(1))
2711 if maxindex is None or index > maxindex:
2712 if maxindex is None or index > maxindex:
2712 maxindex = index
2713 maxindex = index
2713 maxname = f
2714 maxname = f
2714 if maxname:
2715 if maxname:
2715 return (os.path.join(directory, maxname), maxindex)
2716 return (os.path.join(directory, maxname), maxindex)
2716 return (None, None)
2717 return (None, None)
2717
2718
2718 def savename(path):
2719 def savename(path):
2719 (last, index) = lastsavename(path)
2720 (last, index) = lastsavename(path)
2720 if last is None:
2721 if last is None:
2721 index = 0
2722 index = 0
2722 newpath = path + ".%d" % (index + 1)
2723 newpath = path + ".%d" % (index + 1)
2723 return newpath
2724 return newpath
2724
2725
2725 @command("^qpush",
2726 @command("^qpush",
2726 [('', 'keep-changes', None,
2727 [('', 'keep-changes', None,
2727 _('tolerate non-conflicting local changes')),
2728 _('tolerate non-conflicting local changes')),
2728 ('f', 'force', None, _('apply on top of local changes')),
2729 ('f', 'force', None, _('apply on top of local changes')),
2729 ('e', 'exact', None,
2730 ('e', 'exact', None,
2730 _('apply the target patch to its recorded parent')),
2731 _('apply the target patch to its recorded parent')),
2731 ('l', 'list', None, _('list patch name in commit text')),
2732 ('l', 'list', None, _('list patch name in commit text')),
2732 ('a', 'all', None, _('apply all patches')),
2733 ('a', 'all', None, _('apply all patches')),
2733 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2734 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2734 ('n', 'name', '',
2735 ('n', 'name', '',
2735 _('merge queue name (DEPRECATED)'), _('NAME')),
2736 _('merge queue name (DEPRECATED)'), _('NAME')),
2736 ('', 'move', None,
2737 ('', 'move', None,
2737 _('reorder patch series and apply only the patch')),
2738 _('reorder patch series and apply only the patch')),
2738 ('', 'no-backup', None, _('do not save backup copies of files'))],
2739 ('', 'no-backup', None, _('do not save backup copies of files'))],
2739 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2740 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2740 def push(ui, repo, patch=None, **opts):
2741 def push(ui, repo, patch=None, **opts):
2741 """push the next patch onto the stack
2742 """push the next patch onto the stack
2742
2743
2743 By default, abort if the working directory contains uncommitted
2744 By default, abort if the working directory contains uncommitted
2744 changes. With --keep-changes, abort only if the uncommitted files
2745 changes. With --keep-changes, abort only if the uncommitted files
2745 overlap with patched files. With -f/--force, backup and patch over
2746 overlap with patched files. With -f/--force, backup and patch over
2746 uncommitted changes.
2747 uncommitted changes.
2747
2748
2748 Return 0 on success.
2749 Return 0 on success.
2749 """
2750 """
2750 q = repo.mq
2751 q = repo.mq
2751 mergeq = None
2752 mergeq = None
2752
2753
2753 opts = fixkeepchangesopts(ui, opts)
2754 opts = fixkeepchangesopts(ui, opts)
2754 if opts.get('merge'):
2755 if opts.get('merge'):
2755 if opts.get('name'):
2756 if opts.get('name'):
2756 newpath = repo.join(opts.get('name'))
2757 newpath = repo.join(opts.get('name'))
2757 else:
2758 else:
2758 newpath, i = lastsavename(q.path)
2759 newpath, i = lastsavename(q.path)
2759 if not newpath:
2760 if not newpath:
2760 ui.warn(_("no saved queues found, please use -n\n"))
2761 ui.warn(_("no saved queues found, please use -n\n"))
2761 return 1
2762 return 1
2762 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2763 mergeq = queue(ui, repo.baseui, repo.path, newpath)
2763 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2764 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2764 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2765 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2765 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2766 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2766 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2767 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2767 keepchanges=opts.get('keep_changes'))
2768 keepchanges=opts.get('keep_changes'))
2768 return ret
2769 return ret
2769
2770
2770 @command("^qpop",
2771 @command("^qpop",
2771 [('a', 'all', None, _('pop all patches')),
2772 [('a', 'all', None, _('pop all patches')),
2772 ('n', 'name', '',
2773 ('n', 'name', '',
2773 _('queue name to pop (DEPRECATED)'), _('NAME')),
2774 _('queue name to pop (DEPRECATED)'), _('NAME')),
2774 ('', 'keep-changes', None,
2775 ('', 'keep-changes', None,
2775 _('tolerate non-conflicting local changes')),
2776 _('tolerate non-conflicting local changes')),
2776 ('f', 'force', None, _('forget any local changes to patched files')),
2777 ('f', 'force', None, _('forget any local changes to patched files')),
2777 ('', 'no-backup', None, _('do not save backup copies of files'))],
2778 ('', 'no-backup', None, _('do not save backup copies of files'))],
2778 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2779 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2779 def pop(ui, repo, patch=None, **opts):
2780 def pop(ui, repo, patch=None, **opts):
2780 """pop the current patch off the stack
2781 """pop the current patch off the stack
2781
2782
2782 Without argument, pops off the top of the patch stack. If given a
2783 Without argument, pops off the top of the patch stack. If given a
2783 patch name, keeps popping off patches until the named patch is at
2784 patch name, keeps popping off patches until the named patch is at
2784 the top of the stack.
2785 the top of the stack.
2785
2786
2786 By default, abort if the working directory contains uncommitted
2787 By default, abort if the working directory contains uncommitted
2787 changes. With --keep-changes, abort only if the uncommitted files
2788 changes. With --keep-changes, abort only if the uncommitted files
2788 overlap with patched files. With -f/--force, backup and discard
2789 overlap with patched files. With -f/--force, backup and discard
2789 changes made to such files.
2790 changes made to such files.
2790
2791
2791 Return 0 on success.
2792 Return 0 on success.
2792 """
2793 """
2793 opts = fixkeepchangesopts(ui, opts)
2794 opts = fixkeepchangesopts(ui, opts)
2794 localupdate = True
2795 localupdate = True
2795 if opts.get('name'):
2796 if opts.get('name'):
2796 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2797 q = queue(ui, repo.baseui, repo.path, repo.join(opts.get('name')))
2797 ui.warn(_('using patch queue: %s\n') % q.path)
2798 ui.warn(_('using patch queue: %s\n') % q.path)
2798 localupdate = False
2799 localupdate = False
2799 else:
2800 else:
2800 q = repo.mq
2801 q = repo.mq
2801 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2802 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2802 all=opts.get('all'), nobackup=opts.get('no_backup'),
2803 all=opts.get('all'), nobackup=opts.get('no_backup'),
2803 keepchanges=opts.get('keep_changes'))
2804 keepchanges=opts.get('keep_changes'))
2804 q.savedirty()
2805 q.savedirty()
2805 return ret
2806 return ret
2806
2807
2807 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2808 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2808 def rename(ui, repo, patch, name=None, **opts):
2809 def rename(ui, repo, patch, name=None, **opts):
2809 """rename a patch
2810 """rename a patch
2810
2811
2811 With one argument, renames the current patch to PATCH1.
2812 With one argument, renames the current patch to PATCH1.
2812 With two arguments, renames PATCH1 to PATCH2.
2813 With two arguments, renames PATCH1 to PATCH2.
2813
2814
2814 Returns 0 on success."""
2815 Returns 0 on success."""
2815 q = repo.mq
2816 q = repo.mq
2816 if not name:
2817 if not name:
2817 name = patch
2818 name = patch
2818 patch = None
2819 patch = None
2819
2820
2820 if patch:
2821 if patch:
2821 patch = q.lookup(patch)
2822 patch = q.lookup(patch)
2822 else:
2823 else:
2823 if not q.applied:
2824 if not q.applied:
2824 ui.write(_('no patches applied\n'))
2825 ui.write(_('no patches applied\n'))
2825 return
2826 return
2826 patch = q.lookup('qtip')
2827 patch = q.lookup('qtip')
2827 absdest = q.join(name)
2828 absdest = q.join(name)
2828 if os.path.isdir(absdest):
2829 if os.path.isdir(absdest):
2829 name = normname(os.path.join(name, os.path.basename(patch)))
2830 name = normname(os.path.join(name, os.path.basename(patch)))
2830 absdest = q.join(name)
2831 absdest = q.join(name)
2831 q.checkpatchname(name)
2832 q.checkpatchname(name)
2832
2833
2833 ui.note(_('renaming %s to %s\n') % (patch, name))
2834 ui.note(_('renaming %s to %s\n') % (patch, name))
2834 i = q.findseries(patch)
2835 i = q.findseries(patch)
2835 guards = q.guard_re.findall(q.fullseries[i])
2836 guards = q.guard_re.findall(q.fullseries[i])
2836 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2837 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2837 q.parseseries()
2838 q.parseseries()
2838 q.seriesdirty = True
2839 q.seriesdirty = True
2839
2840
2840 info = q.isapplied(patch)
2841 info = q.isapplied(patch)
2841 if info:
2842 if info:
2842 q.applied[info[0]] = statusentry(info[1], name)
2843 q.applied[info[0]] = statusentry(info[1], name)
2843 q.applieddirty = True
2844 q.applieddirty = True
2844
2845
2845 destdir = os.path.dirname(absdest)
2846 destdir = os.path.dirname(absdest)
2846 if not os.path.isdir(destdir):
2847 if not os.path.isdir(destdir):
2847 os.makedirs(destdir)
2848 os.makedirs(destdir)
2848 util.rename(q.join(patch), absdest)
2849 util.rename(q.join(patch), absdest)
2849 r = q.qrepo()
2850 r = q.qrepo()
2850 if r and patch in r.dirstate:
2851 if r and patch in r.dirstate:
2851 wctx = r[None]
2852 wctx = r[None]
2852 wlock = r.wlock()
2853 wlock = r.wlock()
2853 try:
2854 try:
2854 if r.dirstate[patch] == 'a':
2855 if r.dirstate[patch] == 'a':
2855 r.dirstate.drop(patch)
2856 r.dirstate.drop(patch)
2856 r.dirstate.add(name)
2857 r.dirstate.add(name)
2857 else:
2858 else:
2858 wctx.copy(patch, name)
2859 wctx.copy(patch, name)
2859 wctx.forget([patch])
2860 wctx.forget([patch])
2860 finally:
2861 finally:
2861 wlock.release()
2862 wlock.release()
2862
2863
2863 q.savedirty()
2864 q.savedirty()
2864
2865
2865 @command("qrestore",
2866 @command("qrestore",
2866 [('d', 'delete', None, _('delete save entry')),
2867 [('d', 'delete', None, _('delete save entry')),
2867 ('u', 'update', None, _('update queue working directory'))],
2868 ('u', 'update', None, _('update queue working directory'))],
2868 _('hg qrestore [-d] [-u] REV'))
2869 _('hg qrestore [-d] [-u] REV'))
2869 def restore(ui, repo, rev, **opts):
2870 def restore(ui, repo, rev, **opts):
2870 """restore the queue state saved by a revision (DEPRECATED)
2871 """restore the queue state saved by a revision (DEPRECATED)
2871
2872
2872 This command is deprecated, use :hg:`rebase` instead."""
2873 This command is deprecated, use :hg:`rebase` instead."""
2873 rev = repo.lookup(rev)
2874 rev = repo.lookup(rev)
2874 q = repo.mq
2875 q = repo.mq
2875 q.restore(repo, rev, delete=opts.get('delete'),
2876 q.restore(repo, rev, delete=opts.get('delete'),
2876 qupdate=opts.get('update'))
2877 qupdate=opts.get('update'))
2877 q.savedirty()
2878 q.savedirty()
2878 return 0
2879 return 0
2879
2880
2880 @command("qsave",
2881 @command("qsave",
2881 [('c', 'copy', None, _('copy patch directory')),
2882 [('c', 'copy', None, _('copy patch directory')),
2882 ('n', 'name', '',
2883 ('n', 'name', '',
2883 _('copy directory name'), _('NAME')),
2884 _('copy directory name'), _('NAME')),
2884 ('e', 'empty', None, _('clear queue status file')),
2885 ('e', 'empty', None, _('clear queue status file')),
2885 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2886 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2886 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2887 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2887 def save(ui, repo, **opts):
2888 def save(ui, repo, **opts):
2888 """save current queue state (DEPRECATED)
2889 """save current queue state (DEPRECATED)
2889
2890
2890 This command is deprecated, use :hg:`rebase` instead."""
2891 This command is deprecated, use :hg:`rebase` instead."""
2891 q = repo.mq
2892 q = repo.mq
2892 message = cmdutil.logmessage(ui, opts)
2893 message = cmdutil.logmessage(ui, opts)
2893 ret = q.save(repo, msg=message)
2894 ret = q.save(repo, msg=message)
2894 if ret:
2895 if ret:
2895 return ret
2896 return ret
2896 q.savedirty() # save to .hg/patches before copying
2897 q.savedirty() # save to .hg/patches before copying
2897 if opts.get('copy'):
2898 if opts.get('copy'):
2898 path = q.path
2899 path = q.path
2899 if opts.get('name'):
2900 if opts.get('name'):
2900 newpath = os.path.join(q.basepath, opts.get('name'))
2901 newpath = os.path.join(q.basepath, opts.get('name'))
2901 if os.path.exists(newpath):
2902 if os.path.exists(newpath):
2902 if not os.path.isdir(newpath):
2903 if not os.path.isdir(newpath):
2903 raise util.Abort(_('destination %s exists and is not '
2904 raise util.Abort(_('destination %s exists and is not '
2904 'a directory') % newpath)
2905 'a directory') % newpath)
2905 if not opts.get('force'):
2906 if not opts.get('force'):
2906 raise util.Abort(_('destination %s exists, '
2907 raise util.Abort(_('destination %s exists, '
2907 'use -f to force') % newpath)
2908 'use -f to force') % newpath)
2908 else:
2909 else:
2909 newpath = savename(path)
2910 newpath = savename(path)
2910 ui.warn(_("copy %s to %s\n") % (path, newpath))
2911 ui.warn(_("copy %s to %s\n") % (path, newpath))
2911 util.copyfiles(path, newpath)
2912 util.copyfiles(path, newpath)
2912 if opts.get('empty'):
2913 if opts.get('empty'):
2913 del q.applied[:]
2914 del q.applied[:]
2914 q.applieddirty = True
2915 q.applieddirty = True
2915 q.savedirty()
2916 q.savedirty()
2916 return 0
2917 return 0
2917
2918
2918
2919
2919 @command("qselect",
2920 @command("qselect",
2920 [('n', 'none', None, _('disable all guards')),
2921 [('n', 'none', None, _('disable all guards')),
2921 ('s', 'series', None, _('list all guards in series file')),
2922 ('s', 'series', None, _('list all guards in series file')),
2922 ('', 'pop', None, _('pop to before first guarded applied patch')),
2923 ('', 'pop', None, _('pop to before first guarded applied patch')),
2923 ('', 'reapply', None, _('pop, then reapply patches'))],
2924 ('', 'reapply', None, _('pop, then reapply patches'))],
2924 _('hg qselect [OPTION]... [GUARD]...'))
2925 _('hg qselect [OPTION]... [GUARD]...'))
2925 def select(ui, repo, *args, **opts):
2926 def select(ui, repo, *args, **opts):
2926 '''set or print guarded patches to push
2927 '''set or print guarded patches to push
2927
2928
2928 Use the :hg:`qguard` command to set or print guards on patch, then use
2929 Use the :hg:`qguard` command to set or print guards on patch, then use
2929 qselect to tell mq which guards to use. A patch will be pushed if
2930 qselect to tell mq which guards to use. A patch will be pushed if
2930 it has no guards or any positive guards match the currently
2931 it has no guards or any positive guards match the currently
2931 selected guard, but will not be pushed if any negative guards
2932 selected guard, but will not be pushed if any negative guards
2932 match the current guard. For example::
2933 match the current guard. For example::
2933
2934
2934 qguard foo.patch -- -stable (negative guard)
2935 qguard foo.patch -- -stable (negative guard)
2935 qguard bar.patch +stable (positive guard)
2936 qguard bar.patch +stable (positive guard)
2936 qselect stable
2937 qselect stable
2937
2938
2938 This activates the "stable" guard. mq will skip foo.patch (because
2939 This activates the "stable" guard. mq will skip foo.patch (because
2939 it has a negative match) but push bar.patch (because it has a
2940 it has a negative match) but push bar.patch (because it has a
2940 positive match).
2941 positive match).
2941
2942
2942 With no arguments, prints the currently active guards.
2943 With no arguments, prints the currently active guards.
2943 With one argument, sets the active guard.
2944 With one argument, sets the active guard.
2944
2945
2945 Use -n/--none to deactivate guards (no other arguments needed).
2946 Use -n/--none to deactivate guards (no other arguments needed).
2946 When no guards are active, patches with positive guards are
2947 When no guards are active, patches with positive guards are
2947 skipped and patches with negative guards are pushed.
2948 skipped and patches with negative guards are pushed.
2948
2949
2949 qselect can change the guards on applied patches. It does not pop
2950 qselect can change the guards on applied patches. It does not pop
2950 guarded patches by default. Use --pop to pop back to the last
2951 guarded patches by default. Use --pop to pop back to the last
2951 applied patch that is not guarded. Use --reapply (which implies
2952 applied patch that is not guarded. Use --reapply (which implies
2952 --pop) to push back to the current patch afterwards, but skip
2953 --pop) to push back to the current patch afterwards, but skip
2953 guarded patches.
2954 guarded patches.
2954
2955
2955 Use -s/--series to print a list of all guards in the series file
2956 Use -s/--series to print a list of all guards in the series file
2956 (no other arguments needed). Use -v for more information.
2957 (no other arguments needed). Use -v for more information.
2957
2958
2958 Returns 0 on success.'''
2959 Returns 0 on success.'''
2959
2960
2960 q = repo.mq
2961 q = repo.mq
2961 guards = q.active()
2962 guards = q.active()
2962 if args or opts.get('none'):
2963 if args or opts.get('none'):
2963 old_unapplied = q.unapplied(repo)
2964 old_unapplied = q.unapplied(repo)
2964 old_guarded = [i for i in xrange(len(q.applied)) if
2965 old_guarded = [i for i in xrange(len(q.applied)) if
2965 not q.pushable(i)[0]]
2966 not q.pushable(i)[0]]
2966 q.setactive(args)
2967 q.setactive(args)
2967 q.savedirty()
2968 q.savedirty()
2968 if not args:
2969 if not args:
2969 ui.status(_('guards deactivated\n'))
2970 ui.status(_('guards deactivated\n'))
2970 if not opts.get('pop') and not opts.get('reapply'):
2971 if not opts.get('pop') and not opts.get('reapply'):
2971 unapplied = q.unapplied(repo)
2972 unapplied = q.unapplied(repo)
2972 guarded = [i for i in xrange(len(q.applied))
2973 guarded = [i for i in xrange(len(q.applied))
2973 if not q.pushable(i)[0]]
2974 if not q.pushable(i)[0]]
2974 if len(unapplied) != len(old_unapplied):
2975 if len(unapplied) != len(old_unapplied):
2975 ui.status(_('number of unguarded, unapplied patches has '
2976 ui.status(_('number of unguarded, unapplied patches has '
2976 'changed from %d to %d\n') %
2977 'changed from %d to %d\n') %
2977 (len(old_unapplied), len(unapplied)))
2978 (len(old_unapplied), len(unapplied)))
2978 if len(guarded) != len(old_guarded):
2979 if len(guarded) != len(old_guarded):
2979 ui.status(_('number of guarded, applied patches has changed '
2980 ui.status(_('number of guarded, applied patches has changed '
2980 'from %d to %d\n') %
2981 'from %d to %d\n') %
2981 (len(old_guarded), len(guarded)))
2982 (len(old_guarded), len(guarded)))
2982 elif opts.get('series'):
2983 elif opts.get('series'):
2983 guards = {}
2984 guards = {}
2984 noguards = 0
2985 noguards = 0
2985 for gs in q.seriesguards:
2986 for gs in q.seriesguards:
2986 if not gs:
2987 if not gs:
2987 noguards += 1
2988 noguards += 1
2988 for g in gs:
2989 for g in gs:
2989 guards.setdefault(g, 0)
2990 guards.setdefault(g, 0)
2990 guards[g] += 1
2991 guards[g] += 1
2991 if ui.verbose:
2992 if ui.verbose:
2992 guards['NONE'] = noguards
2993 guards['NONE'] = noguards
2993 guards = guards.items()
2994 guards = guards.items()
2994 guards.sort(key=lambda x: x[0][1:])
2995 guards.sort(key=lambda x: x[0][1:])
2995 if guards:
2996 if guards:
2996 ui.note(_('guards in series file:\n'))
2997 ui.note(_('guards in series file:\n'))
2997 for guard, count in guards:
2998 for guard, count in guards:
2998 ui.note('%2d ' % count)
2999 ui.note('%2d ' % count)
2999 ui.write(guard, '\n')
3000 ui.write(guard, '\n')
3000 else:
3001 else:
3001 ui.note(_('no guards in series file\n'))
3002 ui.note(_('no guards in series file\n'))
3002 else:
3003 else:
3003 if guards:
3004 if guards:
3004 ui.note(_('active guards:\n'))
3005 ui.note(_('active guards:\n'))
3005 for g in guards:
3006 for g in guards:
3006 ui.write(g, '\n')
3007 ui.write(g, '\n')
3007 else:
3008 else:
3008 ui.write(_('no active guards\n'))
3009 ui.write(_('no active guards\n'))
3009 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3010 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3010 popped = False
3011 popped = False
3011 if opts.get('pop') or opts.get('reapply'):
3012 if opts.get('pop') or opts.get('reapply'):
3012 for i in xrange(len(q.applied)):
3013 for i in xrange(len(q.applied)):
3013 pushable, reason = q.pushable(i)
3014 pushable, reason = q.pushable(i)
3014 if not pushable:
3015 if not pushable:
3015 ui.status(_('popping guarded patches\n'))
3016 ui.status(_('popping guarded patches\n'))
3016 popped = True
3017 popped = True
3017 if i == 0:
3018 if i == 0:
3018 q.pop(repo, all=True)
3019 q.pop(repo, all=True)
3019 else:
3020 else:
3020 q.pop(repo, str(i - 1))
3021 q.pop(repo, str(i - 1))
3021 break
3022 break
3022 if popped:
3023 if popped:
3023 try:
3024 try:
3024 if reapply:
3025 if reapply:
3025 ui.status(_('reapplying unguarded patches\n'))
3026 ui.status(_('reapplying unguarded patches\n'))
3026 q.push(repo, reapply)
3027 q.push(repo, reapply)
3027 finally:
3028 finally:
3028 q.savedirty()
3029 q.savedirty()
3029
3030
3030 @command("qfinish",
3031 @command("qfinish",
3031 [('a', 'applied', None, _('finish all applied changesets'))],
3032 [('a', 'applied', None, _('finish all applied changesets'))],
3032 _('hg qfinish [-a] [REV]...'))
3033 _('hg qfinish [-a] [REV]...'))
3033 def finish(ui, repo, *revrange, **opts):
3034 def finish(ui, repo, *revrange, **opts):
3034 """move applied patches into repository history
3035 """move applied patches into repository history
3035
3036
3036 Finishes the specified revisions (corresponding to applied
3037 Finishes the specified revisions (corresponding to applied
3037 patches) by moving them out of mq control into regular repository
3038 patches) by moving them out of mq control into regular repository
3038 history.
3039 history.
3039
3040
3040 Accepts a revision range or the -a/--applied option. If --applied
3041 Accepts a revision range or the -a/--applied option. If --applied
3041 is specified, all applied mq revisions are removed from mq
3042 is specified, all applied mq revisions are removed from mq
3042 control. Otherwise, the given revisions must be at the base of the
3043 control. Otherwise, the given revisions must be at the base of the
3043 stack of applied patches.
3044 stack of applied patches.
3044
3045
3045 This can be especially useful if your changes have been applied to
3046 This can be especially useful if your changes have been applied to
3046 an upstream repository, or if you are about to push your changes
3047 an upstream repository, or if you are about to push your changes
3047 to upstream.
3048 to upstream.
3048
3049
3049 Returns 0 on success.
3050 Returns 0 on success.
3050 """
3051 """
3051 if not opts.get('applied') and not revrange:
3052 if not opts.get('applied') and not revrange:
3052 raise util.Abort(_('no revisions specified'))
3053 raise util.Abort(_('no revisions specified'))
3053 elif opts.get('applied'):
3054 elif opts.get('applied'):
3054 revrange = ('qbase::qtip',) + revrange
3055 revrange = ('qbase::qtip',) + revrange
3055
3056
3056 q = repo.mq
3057 q = repo.mq
3057 if not q.applied:
3058 if not q.applied:
3058 ui.status(_('no patches applied\n'))
3059 ui.status(_('no patches applied\n'))
3059 return 0
3060 return 0
3060
3061
3061 revs = scmutil.revrange(repo, revrange)
3062 revs = scmutil.revrange(repo, revrange)
3062 if repo['.'].rev() in revs and repo[None].files():
3063 if repo['.'].rev() in revs and repo[None].files():
3063 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3064 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3064 # queue.finish may changes phases but leave the responsibility to lock the
3065 # queue.finish may changes phases but leave the responsibility to lock the
3065 # repo to the caller to avoid deadlock with wlock. This command code is
3066 # repo to the caller to avoid deadlock with wlock. This command code is
3066 # responsibility for this locking.
3067 # responsibility for this locking.
3067 lock = repo.lock()
3068 lock = repo.lock()
3068 try:
3069 try:
3069 q.finish(repo, revs)
3070 q.finish(repo, revs)
3070 q.savedirty()
3071 q.savedirty()
3071 finally:
3072 finally:
3072 lock.release()
3073 lock.release()
3073 return 0
3074 return 0
3074
3075
3075 @command("qqueue",
3076 @command("qqueue",
3076 [('l', 'list', False, _('list all available queues')),
3077 [('l', 'list', False, _('list all available queues')),
3077 ('', 'active', False, _('print name of active queue')),
3078 ('', 'active', False, _('print name of active queue')),
3078 ('c', 'create', False, _('create new queue')),
3079 ('c', 'create', False, _('create new queue')),
3079 ('', 'rename', False, _('rename active queue')),
3080 ('', 'rename', False, _('rename active queue')),
3080 ('', 'delete', False, _('delete reference to queue')),
3081 ('', 'delete', False, _('delete reference to queue')),
3081 ('', 'purge', False, _('delete queue, and remove patch dir')),
3082 ('', 'purge', False, _('delete queue, and remove patch dir')),
3082 ],
3083 ],
3083 _('[OPTION] [QUEUE]'))
3084 _('[OPTION] [QUEUE]'))
3084 def qqueue(ui, repo, name=None, **opts):
3085 def qqueue(ui, repo, name=None, **opts):
3085 '''manage multiple patch queues
3086 '''manage multiple patch queues
3086
3087
3087 Supports switching between different patch queues, as well as creating
3088 Supports switching between different patch queues, as well as creating
3088 new patch queues and deleting existing ones.
3089 new patch queues and deleting existing ones.
3089
3090
3090 Omitting a queue name or specifying -l/--list will show you the registered
3091 Omitting a queue name or specifying -l/--list will show you the registered
3091 queues - by default the "normal" patches queue is registered. The currently
3092 queues - by default the "normal" patches queue is registered. The currently
3092 active queue will be marked with "(active)". Specifying --active will print
3093 active queue will be marked with "(active)". Specifying --active will print
3093 only the name of the active queue.
3094 only the name of the active queue.
3094
3095
3095 To create a new queue, use -c/--create. The queue is automatically made
3096 To create a new queue, use -c/--create. The queue is automatically made
3096 active, except in the case where there are applied patches from the
3097 active, except in the case where there are applied patches from the
3097 currently active queue in the repository. Then the queue will only be
3098 currently active queue in the repository. Then the queue will only be
3098 created and switching will fail.
3099 created and switching will fail.
3099
3100
3100 To delete an existing queue, use --delete. You cannot delete the currently
3101 To delete an existing queue, use --delete. You cannot delete the currently
3101 active queue.
3102 active queue.
3102
3103
3103 Returns 0 on success.
3104 Returns 0 on success.
3104 '''
3105 '''
3105 q = repo.mq
3106 q = repo.mq
3106 _defaultqueue = 'patches'
3107 _defaultqueue = 'patches'
3107 _allqueues = 'patches.queues'
3108 _allqueues = 'patches.queues'
3108 _activequeue = 'patches.queue'
3109 _activequeue = 'patches.queue'
3109
3110
3110 def _getcurrent():
3111 def _getcurrent():
3111 cur = os.path.basename(q.path)
3112 cur = os.path.basename(q.path)
3112 if cur.startswith('patches-'):
3113 if cur.startswith('patches-'):
3113 cur = cur[8:]
3114 cur = cur[8:]
3114 return cur
3115 return cur
3115
3116
3116 def _noqueues():
3117 def _noqueues():
3117 try:
3118 try:
3118 fh = repo.opener(_allqueues, 'r')
3119 fh = repo.opener(_allqueues, 'r')
3119 fh.close()
3120 fh.close()
3120 except IOError:
3121 except IOError:
3121 return True
3122 return True
3122
3123
3123 return False
3124 return False
3124
3125
3125 def _getqueues():
3126 def _getqueues():
3126 current = _getcurrent()
3127 current = _getcurrent()
3127
3128
3128 try:
3129 try:
3129 fh = repo.opener(_allqueues, 'r')
3130 fh = repo.opener(_allqueues, 'r')
3130 queues = [queue.strip() for queue in fh if queue.strip()]
3131 queues = [queue.strip() for queue in fh if queue.strip()]
3131 fh.close()
3132 fh.close()
3132 if current not in queues:
3133 if current not in queues:
3133 queues.append(current)
3134 queues.append(current)
3134 except IOError:
3135 except IOError:
3135 queues = [_defaultqueue]
3136 queues = [_defaultqueue]
3136
3137
3137 return sorted(queues)
3138 return sorted(queues)
3138
3139
3139 def _setactive(name):
3140 def _setactive(name):
3140 if q.applied:
3141 if q.applied:
3141 raise util.Abort(_('new queue created, but cannot make active '
3142 raise util.Abort(_('new queue created, but cannot make active '
3142 'as patches are applied'))
3143 'as patches are applied'))
3143 _setactivenocheck(name)
3144 _setactivenocheck(name)
3144
3145
3145 def _setactivenocheck(name):
3146 def _setactivenocheck(name):
3146 fh = repo.opener(_activequeue, 'w')
3147 fh = repo.opener(_activequeue, 'w')
3147 if name != 'patches':
3148 if name != 'patches':
3148 fh.write(name)
3149 fh.write(name)
3149 fh.close()
3150 fh.close()
3150
3151
3151 def _addqueue(name):
3152 def _addqueue(name):
3152 fh = repo.opener(_allqueues, 'a')
3153 fh = repo.opener(_allqueues, 'a')
3153 fh.write('%s\n' % (name,))
3154 fh.write('%s\n' % (name,))
3154 fh.close()
3155 fh.close()
3155
3156
3156 def _queuedir(name):
3157 def _queuedir(name):
3157 if name == 'patches':
3158 if name == 'patches':
3158 return repo.join('patches')
3159 return repo.join('patches')
3159 else:
3160 else:
3160 return repo.join('patches-' + name)
3161 return repo.join('patches-' + name)
3161
3162
3162 def _validname(name):
3163 def _validname(name):
3163 for n in name:
3164 for n in name:
3164 if n in ':\\/.':
3165 if n in ':\\/.':
3165 return False
3166 return False
3166 return True
3167 return True
3167
3168
3168 def _delete(name):
3169 def _delete(name):
3169 if name not in existing:
3170 if name not in existing:
3170 raise util.Abort(_('cannot delete queue that does not exist'))
3171 raise util.Abort(_('cannot delete queue that does not exist'))
3171
3172
3172 current = _getcurrent()
3173 current = _getcurrent()
3173
3174
3174 if name == current:
3175 if name == current:
3175 raise util.Abort(_('cannot delete currently active queue'))
3176 raise util.Abort(_('cannot delete currently active queue'))
3176
3177
3177 fh = repo.opener('patches.queues.new', 'w')
3178 fh = repo.opener('patches.queues.new', 'w')
3178 for queue in existing:
3179 for queue in existing:
3179 if queue == name:
3180 if queue == name:
3180 continue
3181 continue
3181 fh.write('%s\n' % (queue,))
3182 fh.write('%s\n' % (queue,))
3182 fh.close()
3183 fh.close()
3183 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3184 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3184
3185
3185 if not name or opts.get('list') or opts.get('active'):
3186 if not name or opts.get('list') or opts.get('active'):
3186 current = _getcurrent()
3187 current = _getcurrent()
3187 if opts.get('active'):
3188 if opts.get('active'):
3188 ui.write('%s\n' % (current,))
3189 ui.write('%s\n' % (current,))
3189 return
3190 return
3190 for queue in _getqueues():
3191 for queue in _getqueues():
3191 ui.write('%s' % (queue,))
3192 ui.write('%s' % (queue,))
3192 if queue == current and not ui.quiet:
3193 if queue == current and not ui.quiet:
3193 ui.write(_(' (active)\n'))
3194 ui.write(_(' (active)\n'))
3194 else:
3195 else:
3195 ui.write('\n')
3196 ui.write('\n')
3196 return
3197 return
3197
3198
3198 if not _validname(name):
3199 if not _validname(name):
3199 raise util.Abort(
3200 raise util.Abort(
3200 _('invalid queue name, may not contain the characters ":\\/."'))
3201 _('invalid queue name, may not contain the characters ":\\/."'))
3201
3202
3202 existing = _getqueues()
3203 existing = _getqueues()
3203
3204
3204 if opts.get('create'):
3205 if opts.get('create'):
3205 if name in existing:
3206 if name in existing:
3206 raise util.Abort(_('queue "%s" already exists') % name)
3207 raise util.Abort(_('queue "%s" already exists') % name)
3207 if _noqueues():
3208 if _noqueues():
3208 _addqueue(_defaultqueue)
3209 _addqueue(_defaultqueue)
3209 _addqueue(name)
3210 _addqueue(name)
3210 _setactive(name)
3211 _setactive(name)
3211 elif opts.get('rename'):
3212 elif opts.get('rename'):
3212 current = _getcurrent()
3213 current = _getcurrent()
3213 if name == current:
3214 if name == current:
3214 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3215 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3215 if name in existing:
3216 if name in existing:
3216 raise util.Abort(_('queue "%s" already exists') % name)
3217 raise util.Abort(_('queue "%s" already exists') % name)
3217
3218
3218 olddir = _queuedir(current)
3219 olddir = _queuedir(current)
3219 newdir = _queuedir(name)
3220 newdir = _queuedir(name)
3220
3221
3221 if os.path.exists(newdir):
3222 if os.path.exists(newdir):
3222 raise util.Abort(_('non-queue directory "%s" already exists') %
3223 raise util.Abort(_('non-queue directory "%s" already exists') %
3223 newdir)
3224 newdir)
3224
3225
3225 fh = repo.opener('patches.queues.new', 'w')
3226 fh = repo.opener('patches.queues.new', 'w')
3226 for queue in existing:
3227 for queue in existing:
3227 if queue == current:
3228 if queue == current:
3228 fh.write('%s\n' % (name,))
3229 fh.write('%s\n' % (name,))
3229 if os.path.exists(olddir):
3230 if os.path.exists(olddir):
3230 util.rename(olddir, newdir)
3231 util.rename(olddir, newdir)
3231 else:
3232 else:
3232 fh.write('%s\n' % (queue,))
3233 fh.write('%s\n' % (queue,))
3233 fh.close()
3234 fh.close()
3234 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3235 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3235 _setactivenocheck(name)
3236 _setactivenocheck(name)
3236 elif opts.get('delete'):
3237 elif opts.get('delete'):
3237 _delete(name)
3238 _delete(name)
3238 elif opts.get('purge'):
3239 elif opts.get('purge'):
3239 if name in existing:
3240 if name in existing:
3240 _delete(name)
3241 _delete(name)
3241 qdir = _queuedir(name)
3242 qdir = _queuedir(name)
3242 if os.path.exists(qdir):
3243 if os.path.exists(qdir):
3243 shutil.rmtree(qdir)
3244 shutil.rmtree(qdir)
3244 else:
3245 else:
3245 if name not in existing:
3246 if name not in existing:
3246 raise util.Abort(_('use --create to create a new queue'))
3247 raise util.Abort(_('use --create to create a new queue'))
3247 _setactive(name)
3248 _setactive(name)
3248
3249
3249 def mqphasedefaults(repo, roots):
3250 def mqphasedefaults(repo, roots):
3250 """callback used to set mq changeset as secret when no phase data exists"""
3251 """callback used to set mq changeset as secret when no phase data exists"""
3251 if repo.mq.applied:
3252 if repo.mq.applied:
3252 if repo.ui.configbool('mq', 'secret', False):
3253 if repo.ui.configbool('mq', 'secret', False):
3253 mqphase = phases.secret
3254 mqphase = phases.secret
3254 else:
3255 else:
3255 mqphase = phases.draft
3256 mqphase = phases.draft
3256 qbase = repo[repo.mq.applied[0].node]
3257 qbase = repo[repo.mq.applied[0].node]
3257 roots[mqphase].add(qbase.node())
3258 roots[mqphase].add(qbase.node())
3258 return roots
3259 return roots
3259
3260
3260 def reposetup(ui, repo):
3261 def reposetup(ui, repo):
3261 class mqrepo(repo.__class__):
3262 class mqrepo(repo.__class__):
3262 @localrepo.unfilteredpropertycache
3263 @localrepo.unfilteredpropertycache
3263 def mq(self):
3264 def mq(self):
3264 return queue(self.ui, self.baseui, self.path)
3265 return queue(self.ui, self.baseui, self.path)
3265
3266
3266 def abortifwdirpatched(self, errmsg, force=False):
3267 def abortifwdirpatched(self, errmsg, force=False):
3267 if self.mq.applied and not force:
3268 if self.mq.applied and self.mq.checkapplied and not force:
3268 parents = self.dirstate.parents()
3269 parents = self.dirstate.parents()
3269 patches = [s.node for s in self.mq.applied]
3270 patches = [s.node for s in self.mq.applied]
3270 if parents[0] in patches or parents[1] in patches:
3271 if parents[0] in patches or parents[1] in patches:
3271 raise util.Abort(errmsg)
3272 raise util.Abort(errmsg)
3272
3273
3273 def commit(self, text="", user=None, date=None, match=None,
3274 def commit(self, text="", user=None, date=None, match=None,
3274 force=False, editor=False, extra={}):
3275 force=False, editor=False, extra={}):
3275 self.abortifwdirpatched(
3276 self.abortifwdirpatched(
3276 _('cannot commit over an applied mq patch'),
3277 _('cannot commit over an applied mq patch'),
3277 force)
3278 force)
3278
3279
3279 return super(mqrepo, self).commit(text, user, date, match, force,
3280 return super(mqrepo, self).commit(text, user, date, match, force,
3280 editor, extra)
3281 editor, extra)
3281
3282
3282 def checkpush(self, force, revs):
3283 def checkpush(self, force, revs):
3283 if self.mq.applied and not force:
3284 if self.mq.applied and self.mq.checkapplied and not force:
3284 outapplied = [e.node for e in self.mq.applied]
3285 outapplied = [e.node for e in self.mq.applied]
3285 if revs:
3286 if revs:
3286 # Assume applied patches have no non-patch descendants and
3287 # Assume applied patches have no non-patch descendants and
3287 # are not on remote already. Filtering any changeset not
3288 # are not on remote already. Filtering any changeset not
3288 # pushed.
3289 # pushed.
3289 heads = set(revs)
3290 heads = set(revs)
3290 for node in reversed(outapplied):
3291 for node in reversed(outapplied):
3291 if node in heads:
3292 if node in heads:
3292 break
3293 break
3293 else:
3294 else:
3294 outapplied.pop()
3295 outapplied.pop()
3295 # looking for pushed and shared changeset
3296 # looking for pushed and shared changeset
3296 for node in outapplied:
3297 for node in outapplied:
3297 if self[node].phase() < phases.secret:
3298 if self[node].phase() < phases.secret:
3298 raise util.Abort(_('source has mq patches applied'))
3299 raise util.Abort(_('source has mq patches applied'))
3299 # no non-secret patches pushed
3300 # no non-secret patches pushed
3300 super(mqrepo, self).checkpush(force, revs)
3301 super(mqrepo, self).checkpush(force, revs)
3301
3302
3302 def _findtags(self):
3303 def _findtags(self):
3303 '''augment tags from base class with patch tags'''
3304 '''augment tags from base class with patch tags'''
3304 result = super(mqrepo, self)._findtags()
3305 result = super(mqrepo, self)._findtags()
3305
3306
3306 q = self.mq
3307 q = self.mq
3307 if not q.applied:
3308 if not q.applied:
3308 return result
3309 return result
3309
3310
3310 mqtags = [(patch.node, patch.name) for patch in q.applied]
3311 mqtags = [(patch.node, patch.name) for patch in q.applied]
3311
3312
3312 try:
3313 try:
3313 # for now ignore filtering business
3314 # for now ignore filtering business
3314 self.unfiltered().changelog.rev(mqtags[-1][0])
3315 self.unfiltered().changelog.rev(mqtags[-1][0])
3315 except error.LookupError:
3316 except error.LookupError:
3316 self.ui.warn(_('mq status file refers to unknown node %s\n')
3317 self.ui.warn(_('mq status file refers to unknown node %s\n')
3317 % short(mqtags[-1][0]))
3318 % short(mqtags[-1][0]))
3318 return result
3319 return result
3319
3320
3320 # do not add fake tags for filtered revisions
3321 # do not add fake tags for filtered revisions
3321 included = self.changelog.hasnode
3322 included = self.changelog.hasnode
3322 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3323 mqtags = [mqt for mqt in mqtags if included(mqt[0])]
3323 if not mqtags:
3324 if not mqtags:
3324 return result
3325 return result
3325
3326
3326 mqtags.append((mqtags[-1][0], 'qtip'))
3327 mqtags.append((mqtags[-1][0], 'qtip'))
3327 mqtags.append((mqtags[0][0], 'qbase'))
3328 mqtags.append((mqtags[0][0], 'qbase'))
3328 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3329 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3329 tags = result[0]
3330 tags = result[0]
3330 for patch in mqtags:
3331 for patch in mqtags:
3331 if patch[1] in tags:
3332 if patch[1] in tags:
3332 self.ui.warn(_('tag %s overrides mq patch of the same '
3333 self.ui.warn(_('tag %s overrides mq patch of the same '
3333 'name\n') % patch[1])
3334 'name\n') % patch[1])
3334 else:
3335 else:
3335 tags[patch[1]] = patch[0]
3336 tags[patch[1]] = patch[0]
3336
3337
3337 return result
3338 return result
3338
3339
3339 if repo.local():
3340 if repo.local():
3340 repo.__class__ = mqrepo
3341 repo.__class__ = mqrepo
3341
3342
3342 repo._phasedefaults.append(mqphasedefaults)
3343 repo._phasedefaults.append(mqphasedefaults)
3343
3344
3344 def mqimport(orig, ui, repo, *args, **kwargs):
3345 def mqimport(orig, ui, repo, *args, **kwargs):
3345 if (util.safehasattr(repo, 'abortifwdirpatched')
3346 if (util.safehasattr(repo, 'abortifwdirpatched')
3346 and not kwargs.get('no_commit', False)):
3347 and not kwargs.get('no_commit', False)):
3347 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3348 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3348 kwargs.get('force'))
3349 kwargs.get('force'))
3349 return orig(ui, repo, *args, **kwargs)
3350 return orig(ui, repo, *args, **kwargs)
3350
3351
3351 def mqinit(orig, ui, *args, **kwargs):
3352 def mqinit(orig, ui, *args, **kwargs):
3352 mq = kwargs.pop('mq', None)
3353 mq = kwargs.pop('mq', None)
3353
3354
3354 if not mq:
3355 if not mq:
3355 return orig(ui, *args, **kwargs)
3356 return orig(ui, *args, **kwargs)
3356
3357
3357 if args:
3358 if args:
3358 repopath = args[0]
3359 repopath = args[0]
3359 if not hg.islocal(repopath):
3360 if not hg.islocal(repopath):
3360 raise util.Abort(_('only a local queue repository '
3361 raise util.Abort(_('only a local queue repository '
3361 'may be initialized'))
3362 'may be initialized'))
3362 else:
3363 else:
3363 repopath = cmdutil.findrepo(os.getcwd())
3364 repopath = cmdutil.findrepo(os.getcwd())
3364 if not repopath:
3365 if not repopath:
3365 raise util.Abort(_('there is no Mercurial repository here '
3366 raise util.Abort(_('there is no Mercurial repository here '
3366 '(.hg not found)'))
3367 '(.hg not found)'))
3367 repo = hg.repository(ui, repopath)
3368 repo = hg.repository(ui, repopath)
3368 return qinit(ui, repo, True)
3369 return qinit(ui, repo, True)
3369
3370
3370 def mqcommand(orig, ui, repo, *args, **kwargs):
3371 def mqcommand(orig, ui, repo, *args, **kwargs):
3371 """Add --mq option to operate on patch repository instead of main"""
3372 """Add --mq option to operate on patch repository instead of main"""
3372
3373
3373 # some commands do not like getting unknown options
3374 # some commands do not like getting unknown options
3374 mq = kwargs.pop('mq', None)
3375 mq = kwargs.pop('mq', None)
3375
3376
3376 if not mq:
3377 if not mq:
3377 return orig(ui, repo, *args, **kwargs)
3378 return orig(ui, repo, *args, **kwargs)
3378
3379
3379 q = repo.mq
3380 q = repo.mq
3380 r = q.qrepo()
3381 r = q.qrepo()
3381 if not r:
3382 if not r:
3382 raise util.Abort(_('no queue repository'))
3383 raise util.Abort(_('no queue repository'))
3383 return orig(r.ui, r, *args, **kwargs)
3384 return orig(r.ui, r, *args, **kwargs)
3384
3385
3385 def summaryhook(ui, repo):
3386 def summaryhook(ui, repo):
3386 q = repo.mq
3387 q = repo.mq
3387 m = []
3388 m = []
3388 a, u = len(q.applied), len(q.unapplied(repo))
3389 a, u = len(q.applied), len(q.unapplied(repo))
3389 if a:
3390 if a:
3390 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3391 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3391 if u:
3392 if u:
3392 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3393 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3393 if m:
3394 if m:
3394 # i18n: column positioning for "hg summary"
3395 # i18n: column positioning for "hg summary"
3395 ui.write(_("mq: %s\n") % ', '.join(m))
3396 ui.write(_("mq: %s\n") % ', '.join(m))
3396 else:
3397 else:
3397 # i18n: column positioning for "hg summary"
3398 # i18n: column positioning for "hg summary"
3398 ui.note(_("mq: (empty queue)\n"))
3399 ui.note(_("mq: (empty queue)\n"))
3399
3400
3400 def revsetmq(repo, subset, x):
3401 def revsetmq(repo, subset, x):
3401 """``mq()``
3402 """``mq()``
3402 Changesets managed by MQ.
3403 Changesets managed by MQ.
3403 """
3404 """
3404 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3405 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3405 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3406 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3406 return [r for r in subset if r in applied]
3407 return [r for r in subset if r in applied]
3407
3408
3408 # tell hggettext to extract docstrings from these functions:
3409 # tell hggettext to extract docstrings from these functions:
3409 i18nfunctions = [revsetmq]
3410 i18nfunctions = [revsetmq]
3410
3411
3411 def extsetup(ui):
3412 def extsetup(ui):
3412 # Ensure mq wrappers are called first, regardless of extension load order by
3413 # Ensure mq wrappers are called first, regardless of extension load order by
3413 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3414 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3414 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3415 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3415
3416
3416 extensions.wrapcommand(commands.table, 'import', mqimport)
3417 extensions.wrapcommand(commands.table, 'import', mqimport)
3417 cmdutil.summaryhooks.add('mq', summaryhook)
3418 cmdutil.summaryhooks.add('mq', summaryhook)
3418
3419
3419 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3420 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3420 entry[1].extend(mqopt)
3421 entry[1].extend(mqopt)
3421
3422
3422 nowrap = set(commands.norepo.split(" "))
3423 nowrap = set(commands.norepo.split(" "))
3423
3424
3424 def dotable(cmdtable):
3425 def dotable(cmdtable):
3425 for cmd in cmdtable.keys():
3426 for cmd in cmdtable.keys():
3426 cmd = cmdutil.parsealiases(cmd)[0]
3427 cmd = cmdutil.parsealiases(cmd)[0]
3427 if cmd in nowrap:
3428 if cmd in nowrap:
3428 continue
3429 continue
3429 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3430 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3430 entry[1].extend(mqopt)
3431 entry[1].extend(mqopt)
3431
3432
3432 dotable(commands.table)
3433 dotable(commands.table)
3433
3434
3434 for extname, extmodule in extensions.extensions():
3435 for extname, extmodule in extensions.extensions():
3435 if extmodule.__file__ != __file__:
3436 if extmodule.__file__ != __file__:
3436 dotable(getattr(extmodule, 'cmdtable', {}))
3437 dotable(getattr(extmodule, 'cmdtable', {}))
3437
3438
3438 revset.symbols['mq'] = revsetmq
3439 revset.symbols['mq'] = revsetmq
3439
3440
3440 colortable = {'qguard.negative': 'red',
3441 colortable = {'qguard.negative': 'red',
3441 'qguard.positive': 'yellow',
3442 'qguard.positive': 'yellow',
3442 'qguard.unguarded': 'green',
3443 'qguard.unguarded': 'green',
3443 'qseries.applied': 'blue bold underline',
3444 'qseries.applied': 'blue bold underline',
3444 'qseries.guarded': 'black bold',
3445 'qseries.guarded': 'black bold',
3445 'qseries.missing': 'red bold',
3446 'qseries.missing': 'red bold',
3446 'qseries.unapplied': 'black bold'}
3447 'qseries.unapplied': 'black bold'}
3447
3448
3448 commands.inferrepo += " qnew qrefresh qdiff qcommit"
3449 commands.inferrepo += " qnew qrefresh qdiff qcommit"
@@ -1,605 +1,609 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 try:
24 try:
25 import cPickle as pickle
25 import cPickle as pickle
26 pickle.dump # import now
26 pickle.dump # import now
27 except ImportError:
27 except ImportError:
28 import pickle
28 import pickle
29 from mercurial.i18n import _
29 from mercurial.i18n import _
30 from mercurial.node import nullid
30 from mercurial.node import nullid
31 from mercurial import changegroup, cmdutil, scmutil, phases
31 from mercurial import changegroup, cmdutil, scmutil, phases
32 from mercurial import error, hg, mdiff, merge, patch, repair, util
32 from mercurial import error, hg, mdiff, merge, patch, repair, util
33 from mercurial import templatefilters
33 from mercurial import templatefilters
34 from mercurial import lock as lockmod
34 from mercurial import lock as lockmod
35 import errno
35 import errno
36
36
37 cmdtable = {}
37 cmdtable = {}
38 command = cmdutil.command(cmdtable)
38 command = cmdutil.command(cmdtable)
39 testedwith = 'internal'
39 testedwith = 'internal'
40
40
41 class shelvedfile(object):
41 class shelvedfile(object):
42 """Handles common functions on shelve files (.hg/.files/.patch) using
42 """Handles common functions on shelve files (.hg/.files/.patch) using
43 the vfs layer"""
43 the vfs layer"""
44 def __init__(self, repo, name, filetype=None):
44 def __init__(self, repo, name, filetype=None):
45 self.repo = repo
45 self.repo = repo
46 self.name = name
46 self.name = name
47 self.vfs = scmutil.vfs(repo.join('shelved'))
47 self.vfs = scmutil.vfs(repo.join('shelved'))
48 if filetype:
48 if filetype:
49 self.fname = name + '.' + filetype
49 self.fname = name + '.' + filetype
50 else:
50 else:
51 self.fname = name
51 self.fname = name
52
52
53 def exists(self):
53 def exists(self):
54 return self.vfs.exists(self.fname)
54 return self.vfs.exists(self.fname)
55
55
56 def filename(self):
56 def filename(self):
57 return self.vfs.join(self.fname)
57 return self.vfs.join(self.fname)
58
58
59 def unlink(self):
59 def unlink(self):
60 util.unlink(self.filename())
60 util.unlink(self.filename())
61
61
62 def stat(self):
62 def stat(self):
63 return self.vfs.stat(self.fname)
63 return self.vfs.stat(self.fname)
64
64
65 def opener(self, mode='rb'):
65 def opener(self, mode='rb'):
66 try:
66 try:
67 return self.vfs(self.fname, mode)
67 return self.vfs(self.fname, mode)
68 except IOError, err:
68 except IOError, err:
69 if err.errno != errno.ENOENT:
69 if err.errno != errno.ENOENT:
70 raise
70 raise
71 if mode[0] in 'wa':
71 if mode[0] in 'wa':
72 try:
72 try:
73 self.vfs.mkdir()
73 self.vfs.mkdir()
74 return self.vfs(self.fname, mode)
74 return self.vfs(self.fname, mode)
75 except IOError, err:
75 except IOError, err:
76 if err.errno != errno.EEXIST:
76 if err.errno != errno.EEXIST:
77 raise
77 raise
78 elif mode[0] == 'r':
78 elif mode[0] == 'r':
79 raise util.Abort(_("shelved change '%s' not found") %
79 raise util.Abort(_("shelved change '%s' not found") %
80 self.name)
80 self.name)
81
81
82 class shelvedstate(object):
82 class shelvedstate(object):
83 """Handles saving and restoring a shelved state. Ensures that different
83 """Handles saving and restoring a shelved state. Ensures that different
84 versions of a shelved state are possible and handles them appropriate"""
84 versions of a shelved state are possible and handles them appropriate"""
85 _version = 1
85 _version = 1
86 _filename = 'shelvedstate'
86 _filename = 'shelvedstate'
87
87
88 @classmethod
88 @classmethod
89 def load(cls, repo):
89 def load(cls, repo):
90 fp = repo.opener(cls._filename)
90 fp = repo.opener(cls._filename)
91 (version, name, parents, stripnodes) = pickle.load(fp)
91 (version, name, parents, stripnodes) = pickle.load(fp)
92
92
93 if version != cls._version:
93 if version != cls._version:
94 raise util.Abort(_('this version of shelve is incompatible '
94 raise util.Abort(_('this version of shelve is incompatible '
95 'with the version used in this repo'))
95 'with the version used in this repo'))
96
96
97 obj = cls()
97 obj = cls()
98 obj.name = name
98 obj.name = name
99 obj.parents = parents
99 obj.parents = parents
100 obj.stripnodes = stripnodes
100 obj.stripnodes = stripnodes
101
101
102 return obj
102 return obj
103
103
104 @classmethod
104 @classmethod
105 def save(cls, repo, name, stripnodes):
105 def save(cls, repo, name, stripnodes):
106 fp = repo.opener(cls._filename, 'wb')
106 fp = repo.opener(cls._filename, 'wb')
107 pickle.dump((cls._version, name,
107 pickle.dump((cls._version, name,
108 repo.dirstate.parents(),
108 repo.dirstate.parents(),
109 stripnodes), fp)
109 stripnodes), fp)
110 fp.close()
110 fp.close()
111
111
112 @staticmethod
112 @staticmethod
113 def clear(repo):
113 def clear(repo):
114 util.unlinkpath(repo.join('shelvedstate'), ignoremissing=True)
114 util.unlinkpath(repo.join('shelvedstate'), ignoremissing=True)
115
115
116 def createcmd(ui, repo, pats, opts):
116 def createcmd(ui, repo, pats, opts):
117 def publicancestors(ctx):
117 def publicancestors(ctx):
118 """Compute the heads of the public ancestors of a commit.
118 """Compute the heads of the public ancestors of a commit.
119
119
120 Much faster than the revset heads(ancestors(ctx) - draft())"""
120 Much faster than the revset heads(ancestors(ctx) - draft())"""
121 seen = set()
121 seen = set()
122 visit = util.deque()
122 visit = util.deque()
123 visit.append(ctx)
123 visit.append(ctx)
124 while visit:
124 while visit:
125 ctx = visit.popleft()
125 ctx = visit.popleft()
126 for parent in ctx.parents():
126 for parent in ctx.parents():
127 rev = parent.rev()
127 rev = parent.rev()
128 if rev not in seen:
128 if rev not in seen:
129 seen.add(rev)
129 seen.add(rev)
130 if parent.mutable():
130 if parent.mutable():
131 visit.append(parent)
131 visit.append(parent)
132 else:
132 else:
133 yield parent.node()
133 yield parent.node()
134
134
135 wctx = repo[None]
135 wctx = repo[None]
136 parents = wctx.parents()
136 parents = wctx.parents()
137 if len(parents) > 1:
137 if len(parents) > 1:
138 raise util.Abort(_('cannot shelve while merging'))
138 raise util.Abort(_('cannot shelve while merging'))
139 parent = parents[0]
139 parent = parents[0]
140
140
141 # we never need the user, so we use a generic user for all shelve operations
141 # we never need the user, so we use a generic user for all shelve operations
142 user = 'shelve@localhost'
142 user = 'shelve@localhost'
143 label = repo._bookmarkcurrent or parent.branch() or 'default'
143 label = repo._bookmarkcurrent or parent.branch() or 'default'
144
144
145 # slashes aren't allowed in filenames, therefore we rename it
145 # slashes aren't allowed in filenames, therefore we rename it
146 origlabel, label = label, label.replace('/', '_')
146 origlabel, label = label, label.replace('/', '_')
147
147
148 def gennames():
148 def gennames():
149 yield label
149 yield label
150 for i in xrange(1, 100):
150 for i in xrange(1, 100):
151 yield '%s-%02d' % (label, i)
151 yield '%s-%02d' % (label, i)
152
152
153 shelvedfiles = []
153 shelvedfiles = []
154
154
155 def commitfunc(ui, repo, message, match, opts):
155 def commitfunc(ui, repo, message, match, opts):
156 # check modified, added, removed, deleted only
156 # check modified, added, removed, deleted only
157 for flist in repo.status(match=match)[:4]:
157 for flist in repo.status(match=match)[:4]:
158 shelvedfiles.extend(flist)
158 shelvedfiles.extend(flist)
159 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
160 try:
159 return repo.commit(message, user, opts.get('date'), match)
161 return repo.commit(message, user, opts.get('date'), match)
162 finally:
163 repo.mq.checkapplied = saved
160
164
161 if parent.node() != nullid:
165 if parent.node() != nullid:
162 desc = parent.description().split('\n', 1)[0]
166 desc = parent.description().split('\n', 1)[0]
163 else:
167 else:
164 desc = '(empty repository)'
168 desc = '(empty repository)'
165
169
166 if not opts['message']:
170 if not opts['message']:
167 opts['message'] = desc
171 opts['message'] = desc
168
172
169 name = opts['name']
173 name = opts['name']
170
174
171 wlock = lock = tr = None
175 wlock = lock = tr = None
172 try:
176 try:
173 wlock = repo.wlock()
177 wlock = repo.wlock()
174 lock = repo.lock()
178 lock = repo.lock()
175
179
176 # use an uncommited transaction to generate the bundle to avoid
180 # use an uncommited transaction to generate the bundle to avoid
177 # pull races. ensure we don't print the abort message to stderr.
181 # pull races. ensure we don't print the abort message to stderr.
178 tr = repo.transaction('commit', report=lambda x: None)
182 tr = repo.transaction('commit', report=lambda x: None)
179
183
180 if name:
184 if name:
181 if shelvedfile(repo, name, 'hg').exists():
185 if shelvedfile(repo, name, 'hg').exists():
182 raise util.Abort(_("a shelved change named '%s' already exists")
186 raise util.Abort(_("a shelved change named '%s' already exists")
183 % name)
187 % name)
184 else:
188 else:
185 for n in gennames():
189 for n in gennames():
186 if not shelvedfile(repo, n, 'hg').exists():
190 if not shelvedfile(repo, n, 'hg').exists():
187 name = n
191 name = n
188 break
192 break
189 else:
193 else:
190 raise util.Abort(_("too many shelved changes named '%s'") %
194 raise util.Abort(_("too many shelved changes named '%s'") %
191 label)
195 label)
192
196
193 # ensure we are not creating a subdirectory or a hidden file
197 # ensure we are not creating a subdirectory or a hidden file
194 if '/' in name or '\\' in name:
198 if '/' in name or '\\' in name:
195 raise util.Abort(_('shelved change names may not contain slashes'))
199 raise util.Abort(_('shelved change names may not contain slashes'))
196 if name.startswith('.'):
200 if name.startswith('.'):
197 raise util.Abort(_("shelved change names may not start with '.'"))
201 raise util.Abort(_("shelved change names may not start with '.'"))
198
202
199 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
203 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
200
204
201 if not node:
205 if not node:
202 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
206 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
203 if stat[3]:
207 if stat[3]:
204 ui.status(_("nothing changed (%d missing files, see "
208 ui.status(_("nothing changed (%d missing files, see "
205 "'hg status')\n") % len(stat[3]))
209 "'hg status')\n") % len(stat[3]))
206 else:
210 else:
207 ui.status(_("nothing changed\n"))
211 ui.status(_("nothing changed\n"))
208 return 1
212 return 1
209
213
210 phases.retractboundary(repo, phases.secret, [node])
214 phases.retractboundary(repo, phases.secret, [node])
211
215
212 fp = shelvedfile(repo, name, 'files').opener('wb')
216 fp = shelvedfile(repo, name, 'files').opener('wb')
213 fp.write('\0'.join(shelvedfiles))
217 fp.write('\0'.join(shelvedfiles))
214
218
215 bases = list(publicancestors(repo[node]))
219 bases = list(publicancestors(repo[node]))
216 cg = repo.changegroupsubset(bases, [node], 'shelve')
220 cg = repo.changegroupsubset(bases, [node], 'shelve')
217 changegroup.writebundle(cg, shelvedfile(repo, name, 'hg').filename(),
221 changegroup.writebundle(cg, shelvedfile(repo, name, 'hg').filename(),
218 'HG10UN')
222 'HG10UN')
219 cmdutil.export(repo, [node],
223 cmdutil.export(repo, [node],
220 fp=shelvedfile(repo, name, 'patch').opener('wb'),
224 fp=shelvedfile(repo, name, 'patch').opener('wb'),
221 opts=mdiff.diffopts(git=True))
225 opts=mdiff.diffopts(git=True))
222
226
223 if ui.formatted():
227 if ui.formatted():
224 desc = util.ellipsis(desc, ui.termwidth())
228 desc = util.ellipsis(desc, ui.termwidth())
225 ui.status(_('shelved as %s\n') % name)
229 ui.status(_('shelved as %s\n') % name)
226 hg.update(repo, parent.node())
230 hg.update(repo, parent.node())
227 finally:
231 finally:
228 if tr:
232 if tr:
229 tr.abort()
233 tr.abort()
230 lockmod.release(lock, wlock)
234 lockmod.release(lock, wlock)
231
235
232 def cleanupcmd(ui, repo):
236 def cleanupcmd(ui, repo):
233 wlock = None
237 wlock = None
234 try:
238 try:
235 wlock = repo.wlock()
239 wlock = repo.wlock()
236 for (name, _) in repo.vfs.readdir('shelved'):
240 for (name, _) in repo.vfs.readdir('shelved'):
237 suffix = name.rsplit('.', 1)[-1]
241 suffix = name.rsplit('.', 1)[-1]
238 if suffix in ('hg', 'files', 'patch'):
242 if suffix in ('hg', 'files', 'patch'):
239 shelvedfile(repo, name).unlink()
243 shelvedfile(repo, name).unlink()
240 finally:
244 finally:
241 lockmod.release(wlock)
245 lockmod.release(wlock)
242
246
243 def deletecmd(ui, repo, pats):
247 def deletecmd(ui, repo, pats):
244 if not pats:
248 if not pats:
245 raise util.Abort(_('no shelved changes specified!'))
249 raise util.Abort(_('no shelved changes specified!'))
246 wlock = None
250 wlock = None
247 try:
251 try:
248 wlock = repo.wlock()
252 wlock = repo.wlock()
249 try:
253 try:
250 for name in pats:
254 for name in pats:
251 for suffix in 'hg files patch'.split():
255 for suffix in 'hg files patch'.split():
252 shelvedfile(repo, name, suffix).unlink()
256 shelvedfile(repo, name, suffix).unlink()
253 except OSError, err:
257 except OSError, err:
254 if err.errno != errno.ENOENT:
258 if err.errno != errno.ENOENT:
255 raise
259 raise
256 raise util.Abort(_("shelved change '%s' not found") % name)
260 raise util.Abort(_("shelved change '%s' not found") % name)
257 finally:
261 finally:
258 lockmod.release(wlock)
262 lockmod.release(wlock)
259
263
260 def listshelves(repo):
264 def listshelves(repo):
261 try:
265 try:
262 names = repo.vfs.readdir('shelved')
266 names = repo.vfs.readdir('shelved')
263 except OSError, err:
267 except OSError, err:
264 if err.errno != errno.ENOENT:
268 if err.errno != errno.ENOENT:
265 raise
269 raise
266 return []
270 return []
267 info = []
271 info = []
268 for (name, _) in names:
272 for (name, _) in names:
269 pfx, sfx = name.rsplit('.', 1)
273 pfx, sfx = name.rsplit('.', 1)
270 if not pfx or sfx != 'patch':
274 if not pfx or sfx != 'patch':
271 continue
275 continue
272 st = shelvedfile(repo, name).stat()
276 st = shelvedfile(repo, name).stat()
273 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
277 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
274 return sorted(info, reverse=True)
278 return sorted(info, reverse=True)
275
279
276 def listcmd(ui, repo, pats, opts):
280 def listcmd(ui, repo, pats, opts):
277 pats = set(pats)
281 pats = set(pats)
278 width = 80
282 width = 80
279 if not ui.plain():
283 if not ui.plain():
280 width = ui.termwidth()
284 width = ui.termwidth()
281 namelabel = 'shelve.newest'
285 namelabel = 'shelve.newest'
282 for mtime, name in listshelves(repo):
286 for mtime, name in listshelves(repo):
283 sname = util.split(name)[1]
287 sname = util.split(name)[1]
284 if pats and sname not in pats:
288 if pats and sname not in pats:
285 continue
289 continue
286 ui.write(sname, label=namelabel)
290 ui.write(sname, label=namelabel)
287 namelabel = 'shelve.name'
291 namelabel = 'shelve.name'
288 if ui.quiet:
292 if ui.quiet:
289 ui.write('\n')
293 ui.write('\n')
290 continue
294 continue
291 ui.write(' ' * (16 - len(sname)))
295 ui.write(' ' * (16 - len(sname)))
292 used = 16
296 used = 16
293 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
297 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
294 ui.write(age, label='shelve.age')
298 ui.write(age, label='shelve.age')
295 ui.write(' ' * (12 - len(age)))
299 ui.write(' ' * (12 - len(age)))
296 used += 12
300 used += 12
297 fp = open(name + '.patch', 'rb')
301 fp = open(name + '.patch', 'rb')
298 try:
302 try:
299 while True:
303 while True:
300 line = fp.readline()
304 line = fp.readline()
301 if not line:
305 if not line:
302 break
306 break
303 if not line.startswith('#'):
307 if not line.startswith('#'):
304 desc = line.rstrip()
308 desc = line.rstrip()
305 if ui.formatted():
309 if ui.formatted():
306 desc = util.ellipsis(desc, width - used)
310 desc = util.ellipsis(desc, width - used)
307 ui.write(desc)
311 ui.write(desc)
308 break
312 break
309 ui.write('\n')
313 ui.write('\n')
310 if not (opts['patch'] or opts['stat']):
314 if not (opts['patch'] or opts['stat']):
311 continue
315 continue
312 difflines = fp.readlines()
316 difflines = fp.readlines()
313 if opts['patch']:
317 if opts['patch']:
314 for chunk, label in patch.difflabel(iter, difflines):
318 for chunk, label in patch.difflabel(iter, difflines):
315 ui.write(chunk, label=label)
319 ui.write(chunk, label=label)
316 if opts['stat']:
320 if opts['stat']:
317 for chunk, label in patch.diffstatui(difflines, width=width,
321 for chunk, label in patch.diffstatui(difflines, width=width,
318 git=True):
322 git=True):
319 ui.write(chunk, label=label)
323 ui.write(chunk, label=label)
320 finally:
324 finally:
321 fp.close()
325 fp.close()
322
326
323 def readshelvedfiles(repo, basename):
327 def readshelvedfiles(repo, basename):
324 fp = shelvedfile(repo, basename, 'files').opener()
328 fp = shelvedfile(repo, basename, 'files').opener()
325 return fp.read().split('\0')
329 return fp.read().split('\0')
326
330
327 def checkparents(repo, state):
331 def checkparents(repo, state):
328 if state.parents != repo.dirstate.parents():
332 if state.parents != repo.dirstate.parents():
329 raise util.Abort(_('working directory parents do not match unshelve '
333 raise util.Abort(_('working directory parents do not match unshelve '
330 'state'))
334 'state'))
331
335
332 def unshelveabort(ui, repo, state, opts):
336 def unshelveabort(ui, repo, state, opts):
333 wlock = repo.wlock()
337 wlock = repo.wlock()
334 lock = None
338 lock = None
335 try:
339 try:
336 checkparents(repo, state)
340 checkparents(repo, state)
337 lock = repo.lock()
341 lock = repo.lock()
338 merge.mergestate(repo).reset()
342 merge.mergestate(repo).reset()
339 if opts['keep']:
343 if opts['keep']:
340 repo.setparents(repo.dirstate.parents()[0])
344 repo.setparents(repo.dirstate.parents()[0])
341 else:
345 else:
342 revertfiles = readshelvedfiles(repo, state.name)
346 revertfiles = readshelvedfiles(repo, state.name)
343 wctx = repo.parents()[0]
347 wctx = repo.parents()[0]
344 cmdutil.revert(ui, repo, wctx, [wctx.node(), nullid],
348 cmdutil.revert(ui, repo, wctx, [wctx.node(), nullid],
345 *revertfiles, no_backup=True)
349 *revertfiles, no_backup=True)
346 # fix up the weird dirstate states the merge left behind
350 # fix up the weird dirstate states the merge left behind
347 mf = wctx.manifest()
351 mf = wctx.manifest()
348 dirstate = repo.dirstate
352 dirstate = repo.dirstate
349 for f in revertfiles:
353 for f in revertfiles:
350 if f in mf:
354 if f in mf:
351 dirstate.normallookup(f)
355 dirstate.normallookup(f)
352 else:
356 else:
353 dirstate.drop(f)
357 dirstate.drop(f)
354 dirstate._pl = (wctx.node(), nullid)
358 dirstate._pl = (wctx.node(), nullid)
355 dirstate._dirty = True
359 dirstate._dirty = True
356 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
360 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
357 shelvedstate.clear(repo)
361 shelvedstate.clear(repo)
358 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
362 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
359 finally:
363 finally:
360 lockmod.release(lock, wlock)
364 lockmod.release(lock, wlock)
361
365
362 def unshelvecleanup(ui, repo, name, opts):
366 def unshelvecleanup(ui, repo, name, opts):
363 if not opts['keep']:
367 if not opts['keep']:
364 for filetype in 'hg files patch'.split():
368 for filetype in 'hg files patch'.split():
365 shelvedfile(repo, name, filetype).unlink()
369 shelvedfile(repo, name, filetype).unlink()
366
370
367 def finishmerge(ui, repo, ms, stripnodes, name, opts):
371 def finishmerge(ui, repo, ms, stripnodes, name, opts):
368 # Reset the working dir so it's no longer in a merge state.
372 # Reset the working dir so it's no longer in a merge state.
369 dirstate = repo.dirstate
373 dirstate = repo.dirstate
370 for f in ms:
374 for f in ms:
371 if dirstate[f] == 'm':
375 if dirstate[f] == 'm':
372 dirstate.normallookup(f)
376 dirstate.normallookup(f)
373 dirstate._pl = (dirstate._pl[0], nullid)
377 dirstate._pl = (dirstate._pl[0], nullid)
374 dirstate._dirty = dirstate._dirtypl = True
378 dirstate._dirty = dirstate._dirtypl = True
375 shelvedstate.clear(repo)
379 shelvedstate.clear(repo)
376
380
377 def unshelvecontinue(ui, repo, state, opts):
381 def unshelvecontinue(ui, repo, state, opts):
378 # We're finishing off a merge. First parent is our original
382 # We're finishing off a merge. First parent is our original
379 # parent, second is the temporary "fake" commit we're unshelving.
383 # parent, second is the temporary "fake" commit we're unshelving.
380 wlock = repo.wlock()
384 wlock = repo.wlock()
381 lock = None
385 lock = None
382 try:
386 try:
383 checkparents(repo, state)
387 checkparents(repo, state)
384 ms = merge.mergestate(repo)
388 ms = merge.mergestate(repo)
385 if [f for f in ms if ms[f] == 'u']:
389 if [f for f in ms if ms[f] == 'u']:
386 raise util.Abort(
390 raise util.Abort(
387 _("unresolved conflicts, can't continue"),
391 _("unresolved conflicts, can't continue"),
388 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
392 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
389 finishmerge(ui, repo, ms, state.stripnodes, state.name, opts)
393 finishmerge(ui, repo, ms, state.stripnodes, state.name, opts)
390 lock = repo.lock()
394 lock = repo.lock()
391 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
395 repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
392 unshelvecleanup(ui, repo, state.name, opts)
396 unshelvecleanup(ui, repo, state.name, opts)
393 ui.status(_("unshelve of '%s' complete\n") % state.name)
397 ui.status(_("unshelve of '%s' complete\n") % state.name)
394 finally:
398 finally:
395 lockmod.release(lock, wlock)
399 lockmod.release(lock, wlock)
396
400
397 @command('unshelve',
401 @command('unshelve',
398 [('a', 'abort', None,
402 [('a', 'abort', None,
399 _('abort an incomplete unshelve operation')),
403 _('abort an incomplete unshelve operation')),
400 ('c', 'continue', None,
404 ('c', 'continue', None,
401 _('continue an incomplete unshelve operation')),
405 _('continue an incomplete unshelve operation')),
402 ('', 'keep', None,
406 ('', 'keep', None,
403 _('keep shelve after unshelving'))],
407 _('keep shelve after unshelving'))],
404 _('hg unshelve [SHELVED]'))
408 _('hg unshelve [SHELVED]'))
405 def unshelve(ui, repo, *shelved, **opts):
409 def unshelve(ui, repo, *shelved, **opts):
406 """restore a shelved change to the working directory
410 """restore a shelved change to the working directory
407
411
408 This command accepts an optional name of a shelved change to
412 This command accepts an optional name of a shelved change to
409 restore. If none is given, the most recent shelved change is used.
413 restore. If none is given, the most recent shelved change is used.
410
414
411 If a shelved change is applied successfully, the bundle that
415 If a shelved change is applied successfully, the bundle that
412 contains the shelved changes is deleted afterwards.
416 contains the shelved changes is deleted afterwards.
413
417
414 Since you can restore a shelved change on top of an arbitrary
418 Since you can restore a shelved change on top of an arbitrary
415 commit, it is possible that unshelving will result in a conflict
419 commit, it is possible that unshelving will result in a conflict
416 between your changes and the commits you are unshelving onto. If
420 between your changes and the commits you are unshelving onto. If
417 this occurs, you must resolve the conflict, then use
421 this occurs, you must resolve the conflict, then use
418 ``--continue`` to complete the unshelve operation. (The bundle
422 ``--continue`` to complete the unshelve operation. (The bundle
419 will not be deleted until you successfully complete the unshelve.)
423 will not be deleted until you successfully complete the unshelve.)
420
424
421 (Alternatively, you can use ``--abort`` to abandon an unshelve
425 (Alternatively, you can use ``--abort`` to abandon an unshelve
422 that causes a conflict. This reverts the unshelved changes, and
426 that causes a conflict. This reverts the unshelved changes, and
423 does not delete the bundle.)
427 does not delete the bundle.)
424 """
428 """
425 abortf = opts['abort']
429 abortf = opts['abort']
426 continuef = opts['continue']
430 continuef = opts['continue']
427 if not abortf and not continuef:
431 if not abortf and not continuef:
428 cmdutil.checkunfinished(repo)
432 cmdutil.checkunfinished(repo)
429
433
430 if abortf or continuef:
434 if abortf or continuef:
431 if abortf and continuef:
435 if abortf and continuef:
432 raise util.Abort(_('cannot use both abort and continue'))
436 raise util.Abort(_('cannot use both abort and continue'))
433 if shelved:
437 if shelved:
434 raise util.Abort(_('cannot combine abort/continue with '
438 raise util.Abort(_('cannot combine abort/continue with '
435 'naming a shelved change'))
439 'naming a shelved change'))
436
440
437 try:
441 try:
438 state = shelvedstate.load(repo)
442 state = shelvedstate.load(repo)
439 except IOError, err:
443 except IOError, err:
440 if err.errno != errno.ENOENT:
444 if err.errno != errno.ENOENT:
441 raise
445 raise
442 raise util.Abort(_('no unshelve operation underway'))
446 raise util.Abort(_('no unshelve operation underway'))
443
447
444 if abortf:
448 if abortf:
445 return unshelveabort(ui, repo, state, opts)
449 return unshelveabort(ui, repo, state, opts)
446 elif continuef:
450 elif continuef:
447 return unshelvecontinue(ui, repo, state, opts)
451 return unshelvecontinue(ui, repo, state, opts)
448 elif len(shelved) > 1:
452 elif len(shelved) > 1:
449 raise util.Abort(_('can only unshelve one change at a time'))
453 raise util.Abort(_('can only unshelve one change at a time'))
450 elif not shelved:
454 elif not shelved:
451 shelved = listshelves(repo)
455 shelved = listshelves(repo)
452 if not shelved:
456 if not shelved:
453 raise util.Abort(_('no shelved changes to apply!'))
457 raise util.Abort(_('no shelved changes to apply!'))
454 basename = util.split(shelved[0][1])[1]
458 basename = util.split(shelved[0][1])[1]
455 ui.status(_("unshelving change '%s'\n") % basename)
459 ui.status(_("unshelving change '%s'\n") % basename)
456 else:
460 else:
457 basename = shelved[0]
461 basename = shelved[0]
458
462
459 shelvedfiles = readshelvedfiles(repo, basename)
463 shelvedfiles = readshelvedfiles(repo, basename)
460
464
461 m, a, r, d = repo.status()[:4]
465 m, a, r, d = repo.status()[:4]
462 unsafe = set(m + a + r + d).intersection(shelvedfiles)
466 unsafe = set(m + a + r + d).intersection(shelvedfiles)
463 if unsafe:
467 if unsafe:
464 ui.warn(_('the following shelved files have been modified:\n'))
468 ui.warn(_('the following shelved files have been modified:\n'))
465 for f in sorted(unsafe):
469 for f in sorted(unsafe):
466 ui.warn(' %s\n' % f)
470 ui.warn(' %s\n' % f)
467 ui.warn(_('you must commit, revert, or shelve your changes before you '
471 ui.warn(_('you must commit, revert, or shelve your changes before you '
468 'can proceed\n'))
472 'can proceed\n'))
469 raise util.Abort(_('cannot unshelve due to local changes\n'))
473 raise util.Abort(_('cannot unshelve due to local changes\n'))
470
474
471 wlock = lock = tr = None
475 wlock = lock = tr = None
472 try:
476 try:
473 lock = repo.lock()
477 lock = repo.lock()
474
478
475 tr = repo.transaction('unshelve', report=lambda x: None)
479 tr = repo.transaction('unshelve', report=lambda x: None)
476 oldtiprev = len(repo)
480 oldtiprev = len(repo)
477 try:
481 try:
478 fp = shelvedfile(repo, basename, 'hg').opener()
482 fp = shelvedfile(repo, basename, 'hg').opener()
479 gen = changegroup.readbundle(fp, fp.name)
483 gen = changegroup.readbundle(fp, fp.name)
480 repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
484 repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
481 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
485 nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
482 phases.retractboundary(repo, phases.secret, nodes)
486 phases.retractboundary(repo, phases.secret, nodes)
483 tr.close()
487 tr.close()
484 finally:
488 finally:
485 fp.close()
489 fp.close()
486
490
487 tip = repo['tip']
491 tip = repo['tip']
488 wctx = repo['.']
492 wctx = repo['.']
489 ancestor = tip.ancestor(wctx)
493 ancestor = tip.ancestor(wctx)
490
494
491 wlock = repo.wlock()
495 wlock = repo.wlock()
492
496
493 if ancestor.node() != wctx.node():
497 if ancestor.node() != wctx.node():
494 conflicts = hg.merge(repo, tip.node(), force=True, remind=False)
498 conflicts = hg.merge(repo, tip.node(), force=True, remind=False)
495 ms = merge.mergestate(repo)
499 ms = merge.mergestate(repo)
496 stripnodes = [repo.changelog.node(rev)
500 stripnodes = [repo.changelog.node(rev)
497 for rev in xrange(oldtiprev, len(repo))]
501 for rev in xrange(oldtiprev, len(repo))]
498 if conflicts:
502 if conflicts:
499 shelvedstate.save(repo, basename, stripnodes)
503 shelvedstate.save(repo, basename, stripnodes)
500 # Fix up the dirstate entries of files from the second
504 # Fix up the dirstate entries of files from the second
501 # parent as if we were not merging, except for those
505 # parent as if we were not merging, except for those
502 # with unresolved conflicts.
506 # with unresolved conflicts.
503 parents = repo.parents()
507 parents = repo.parents()
504 revertfiles = set(parents[1].files()).difference(ms)
508 revertfiles = set(parents[1].files()).difference(ms)
505 cmdutil.revert(ui, repo, parents[1],
509 cmdutil.revert(ui, repo, parents[1],
506 (parents[0].node(), nullid),
510 (parents[0].node(), nullid),
507 *revertfiles, no_backup=True)
511 *revertfiles, no_backup=True)
508 raise error.InterventionRequired(
512 raise error.InterventionRequired(
509 _("unresolved conflicts (see 'hg resolve', then "
513 _("unresolved conflicts (see 'hg resolve', then "
510 "'hg unshelve --continue')"))
514 "'hg unshelve --continue')"))
511 finishmerge(ui, repo, ms, stripnodes, basename, opts)
515 finishmerge(ui, repo, ms, stripnodes, basename, opts)
512 else:
516 else:
513 parent = tip.parents()[0]
517 parent = tip.parents()[0]
514 hg.update(repo, parent.node())
518 hg.update(repo, parent.node())
515 cmdutil.revert(ui, repo, tip, repo.dirstate.parents(), *tip.files(),
519 cmdutil.revert(ui, repo, tip, repo.dirstate.parents(), *tip.files(),
516 no_backup=True)
520 no_backup=True)
517
521
518 prevquiet = ui.quiet
522 prevquiet = ui.quiet
519 ui.quiet = True
523 ui.quiet = True
520 try:
524 try:
521 repo.rollback(force=True)
525 repo.rollback(force=True)
522 finally:
526 finally:
523 ui.quiet = prevquiet
527 ui.quiet = prevquiet
524
528
525 unshelvecleanup(ui, repo, basename, opts)
529 unshelvecleanup(ui, repo, basename, opts)
526 finally:
530 finally:
527 if tr:
531 if tr:
528 tr.release()
532 tr.release()
529 lockmod.release(lock, wlock)
533 lockmod.release(lock, wlock)
530
534
531 @command('shelve',
535 @command('shelve',
532 [('A', 'addremove', None,
536 [('A', 'addremove', None,
533 _('mark new/missing files as added/removed before shelving')),
537 _('mark new/missing files as added/removed before shelving')),
534 ('', 'cleanup', None,
538 ('', 'cleanup', None,
535 _('delete all shelved changes')),
539 _('delete all shelved changes')),
536 ('', 'date', '',
540 ('', 'date', '',
537 _('shelve with the specified commit date'), _('DATE')),
541 _('shelve with the specified commit date'), _('DATE')),
538 ('d', 'delete', None,
542 ('d', 'delete', None,
539 _('delete the named shelved change(s)')),
543 _('delete the named shelved change(s)')),
540 ('l', 'list', None,
544 ('l', 'list', None,
541 _('list current shelves')),
545 _('list current shelves')),
542 ('m', 'message', '',
546 ('m', 'message', '',
543 _('use text as shelve message'), _('TEXT')),
547 _('use text as shelve message'), _('TEXT')),
544 ('n', 'name', '',
548 ('n', 'name', '',
545 _('use the given name for the shelved commit'), _('NAME')),
549 _('use the given name for the shelved commit'), _('NAME')),
546 ('p', 'patch', None,
550 ('p', 'patch', None,
547 _('show patch')),
551 _('show patch')),
548 ('', 'stat', None,
552 ('', 'stat', None,
549 _('output diffstat-style summary of changes'))],
553 _('output diffstat-style summary of changes'))],
550 _('hg shelve'))
554 _('hg shelve'))
551 def shelvecmd(ui, repo, *pats, **opts):
555 def shelvecmd(ui, repo, *pats, **opts):
552 '''save and set aside changes from the working directory
556 '''save and set aside changes from the working directory
553
557
554 Shelving takes files that "hg status" reports as not clean, saves
558 Shelving takes files that "hg status" reports as not clean, saves
555 the modifications to a bundle (a shelved change), and reverts the
559 the modifications to a bundle (a shelved change), and reverts the
556 files so that their state in the working directory becomes clean.
560 files so that their state in the working directory becomes clean.
557
561
558 To restore these changes to the working directory, using "hg
562 To restore these changes to the working directory, using "hg
559 unshelve"; this will work even if you switch to a different
563 unshelve"; this will work even if you switch to a different
560 commit.
564 commit.
561
565
562 When no files are specified, "hg shelve" saves all not-clean
566 When no files are specified, "hg shelve" saves all not-clean
563 files. If specific files or directories are named, only changes to
567 files. If specific files or directories are named, only changes to
564 those files are shelved.
568 those files are shelved.
565
569
566 Each shelved change has a name that makes it easier to find later.
570 Each shelved change has a name that makes it easier to find later.
567 The name of a shelved change defaults to being based on the active
571 The name of a shelved change defaults to being based on the active
568 bookmark, or if there is no active bookmark, the current named
572 bookmark, or if there is no active bookmark, the current named
569 branch. To specify a different name, use ``--name``.
573 branch. To specify a different name, use ``--name``.
570
574
571 To see a list of existing shelved changes, use the ``--list``
575 To see a list of existing shelved changes, use the ``--list``
572 option. For each shelved change, this will print its name, age,
576 option. For each shelved change, this will print its name, age,
573 and description; use ``--patch`` or ``--stat`` for more details.
577 and description; use ``--patch`` or ``--stat`` for more details.
574
578
575 To delete specific shelved changes, use ``--delete``. To delete
579 To delete specific shelved changes, use ``--delete``. To delete
576 all shelved changes, use ``--cleanup``.
580 all shelved changes, use ``--cleanup``.
577 '''
581 '''
578 cmdutil.checkunfinished(repo)
582 cmdutil.checkunfinished(repo)
579
583
580 def checkopt(opt, incompatible):
584 def checkopt(opt, incompatible):
581 if opts[opt]:
585 if opts[opt]:
582 for i in incompatible.split():
586 for i in incompatible.split():
583 if opts[i]:
587 if opts[i]:
584 raise util.Abort(_("options '--%s' and '--%s' may not be "
588 raise util.Abort(_("options '--%s' and '--%s' may not be "
585 "used together") % (opt, i))
589 "used together") % (opt, i))
586 return True
590 return True
587 if checkopt('cleanup', 'addremove delete list message name patch stat'):
591 if checkopt('cleanup', 'addremove delete list message name patch stat'):
588 if pats:
592 if pats:
589 raise util.Abort(_("cannot specify names when using '--cleanup'"))
593 raise util.Abort(_("cannot specify names when using '--cleanup'"))
590 return cleanupcmd(ui, repo)
594 return cleanupcmd(ui, repo)
591 elif checkopt('delete', 'addremove cleanup list message name patch stat'):
595 elif checkopt('delete', 'addremove cleanup list message name patch stat'):
592 return deletecmd(ui, repo, pats)
596 return deletecmd(ui, repo, pats)
593 elif checkopt('list', 'addremove cleanup delete message name'):
597 elif checkopt('list', 'addremove cleanup delete message name'):
594 return listcmd(ui, repo, pats, opts)
598 return listcmd(ui, repo, pats, opts)
595 else:
599 else:
596 for i in ('patch', 'stat'):
600 for i in ('patch', 'stat'):
597 if opts[i]:
601 if opts[i]:
598 raise util.Abort(_("option '--%s' may not be "
602 raise util.Abort(_("option '--%s' may not be "
599 "used when shelving a change") % (i,))
603 "used when shelving a change") % (i,))
600 return createcmd(ui, repo, pats, opts)
604 return createcmd(ui, repo, pats, opts)
601
605
602 def extsetup(ui):
606 def extsetup(ui):
603 cmdutil.unfinishedstates.append(
607 cmdutil.unfinishedstates.append(
604 [shelvedstate._filename, False, True, _('unshelve already in progress'),
608 [shelvedstate._filename, False, True, _('unshelve already in progress'),
605 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
609 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
@@ -1,416 +1,420 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "mq=" >> $HGRCPATH
2 $ echo "shelve=" >> $HGRCPATH
3 $ echo "shelve=" >> $HGRCPATH
3 $ echo "[defaults]" >> $HGRCPATH
4 $ echo "[defaults]" >> $HGRCPATH
4 $ echo "diff = --nodates --git" >> $HGRCPATH
5 $ echo "diff = --nodates --git" >> $HGRCPATH
6 $ echo "qnew = --date '0 0'" >> $HGRCPATH
5
7
6 $ hg init repo
8 $ hg init repo
7 $ cd repo
9 $ cd repo
8 $ mkdir a b
10 $ mkdir a b
9 $ echo a > a/a
11 $ echo a > a/a
10 $ echo b > b/b
12 $ echo b > b/b
11 $ echo c > c
13 $ echo c > c
12 $ echo d > d
14 $ echo d > d
13 $ echo x > x
15 $ echo x > x
14 $ hg addremove -q
16 $ hg addremove -q
15
17
16 shelving in an empty repo should be possible
18 shelving in an empty repo should be possible
17
19
18 $ hg shelve
20 $ hg shelve
19 shelved as default
21 shelved as default
20 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
22 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
21
23
22 $ hg unshelve
24 $ hg unshelve
23 unshelving change 'default'
25 unshelving change 'default'
24 adding changesets
26 adding changesets
25 adding manifests
27 adding manifests
26 adding file changes
28 adding file changes
27 added 1 changesets with 5 changes to 5 files
29 added 1 changesets with 5 changes to 5 files
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
29
31
30 $ hg commit -q -m 'initial commit'
32 $ hg commit -q -m 'initial commit'
31
33
32 $ hg shelve
34 $ hg shelve
33 nothing changed
35 nothing changed
34 [1]
36 [1]
35
37
36 create another commit
38 create an mq patch - shelving should work fine with a patch applied
37
39
38 $ echo n > n
40 $ echo n > n
39 $ hg add n
41 $ hg add n
40 $ hg commit n -m second
42 $ hg commit n -m second
43 $ hg qnew second.patch
41
44
42 shelve a change that we will delete later
45 shelve a change that we will delete later
43
46
44 $ echo a >> a/a
47 $ echo a >> a/a
45 $ hg shelve
48 $ hg shelve
46 shelved as default
49 shelved as default
47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48
51
49 set up some more complex changes to shelve
52 set up some more complex changes to shelve
50
53
51 $ echo a >> a/a
54 $ echo a >> a/a
52 $ hg mv b b.rename
55 $ hg mv b b.rename
53 moving b/b to b.rename/b (glob)
56 moving b/b to b.rename/b (glob)
54 $ hg cp c c.copy
57 $ hg cp c c.copy
55 $ hg status -C
58 $ hg status -C
56 M a/a
59 M a/a
57 A b.rename/b
60 A b.rename/b
58 b/b
61 b/b
59 A c.copy
62 A c.copy
60 c
63 c
61 R b/b
64 R b/b
62
65
63 prevent some foot-shooting
66 prevent some foot-shooting
64
67
65 $ hg shelve -n foo/bar
68 $ hg shelve -n foo/bar
66 abort: shelved change names may not contain slashes
69 abort: shelved change names may not contain slashes
67 [255]
70 [255]
68 $ hg shelve -n .baz
71 $ hg shelve -n .baz
69 abort: shelved change names may not start with '.'
72 abort: shelved change names may not start with '.'
70 [255]
73 [255]
71
74
72 the common case - no options or filenames
75 the common case - no options or filenames
73
76
74 $ hg shelve
77 $ hg shelve
75 shelved as default-01
78 shelved as default-01
76 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
79 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
77 $ hg status -C
80 $ hg status -C
78
81
79 ensure that our shelved changes exist
82 ensure that our shelved changes exist
80
83
81 $ hg shelve -l
84 $ hg shelve -l
82 default-01 (*) second (glob)
85 default-01 (*) [mq]: second.patch (glob)
83 default (*) second (glob)
86 default (*) [mq]: second.patch (glob)
84
87
85 $ hg shelve -l -p default
88 $ hg shelve -l -p default
86 default (*) second (glob)
89 default (*) [mq]: second.patch (glob)
87
90
88 diff --git a/a/a b/a/a
91 diff --git a/a/a b/a/a
89 --- a/a/a
92 --- a/a/a
90 +++ b/a/a
93 +++ b/a/a
91 @@ -1,1 +1,2 @@
94 @@ -1,1 +1,2 @@
92 a
95 a
93 +a
96 +a
94
97
95 delete our older shelved change
98 delete our older shelved change
96
99
97 $ hg shelve -d default
100 $ hg shelve -d default
101 $ hg qfinish -a -q
98
102
99 local edits should prevent a shelved change from applying
103 local edits should prevent a shelved change from applying
100
104
101 $ echo e>>a/a
105 $ echo e>>a/a
102 $ hg unshelve
106 $ hg unshelve
103 unshelving change 'default-01'
107 unshelving change 'default-01'
104 the following shelved files have been modified:
108 the following shelved files have been modified:
105 a/a
109 a/a
106 you must commit, revert, or shelve your changes before you can proceed
110 you must commit, revert, or shelve your changes before you can proceed
107 abort: cannot unshelve due to local changes
111 abort: cannot unshelve due to local changes
108
112
109 [255]
113 [255]
110
114
111 $ hg revert -C a/a
115 $ hg revert -C a/a
112
116
113 apply it and make sure our state is as expected
117 apply it and make sure our state is as expected
114
118
115 $ hg unshelve
119 $ hg unshelve
116 unshelving change 'default-01'
120 unshelving change 'default-01'
117 adding changesets
121 adding changesets
118 adding manifests
122 adding manifests
119 adding file changes
123 adding file changes
120 added 1 changesets with 3 changes to 8 files
124 added 1 changesets with 3 changes to 8 files
121 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 $ hg status -C
126 $ hg status -C
123 M a/a
127 M a/a
124 A b.rename/b
128 A b.rename/b
125 b/b
129 b/b
126 A c.copy
130 A c.copy
127 c
131 c
128 R b/b
132 R b/b
129 $ hg shelve -l
133 $ hg shelve -l
130
134
131 $ hg unshelve
135 $ hg unshelve
132 abort: no shelved changes to apply!
136 abort: no shelved changes to apply!
133 [255]
137 [255]
134 $ hg unshelve foo
138 $ hg unshelve foo
135 abort: shelved change 'foo' not found
139 abort: shelved change 'foo' not found
136 [255]
140 [255]
137
141
138 named shelves, specific filenames, and "commit messages" should all work
142 named shelves, specific filenames, and "commit messages" should all work
139
143
140 $ hg status -C
144 $ hg status -C
141 M a/a
145 M a/a
142 A b.rename/b
146 A b.rename/b
143 b/b
147 b/b
144 A c.copy
148 A c.copy
145 c
149 c
146 R b/b
150 R b/b
147 $ hg shelve -q -n wibble -m wat a
151 $ hg shelve -q -n wibble -m wat a
148
152
149 expect "a" to no longer be present, but status otherwise unchanged
153 expect "a" to no longer be present, but status otherwise unchanged
150
154
151 $ hg status -C
155 $ hg status -C
152 A b.rename/b
156 A b.rename/b
153 b/b
157 b/b
154 A c.copy
158 A c.copy
155 c
159 c
156 R b/b
160 R b/b
157 $ hg shelve -l --stat
161 $ hg shelve -l --stat
158 wibble (*) wat (glob)
162 wibble (*) wat (glob)
159 a/a | 1 +
163 a/a | 1 +
160 1 files changed, 1 insertions(+), 0 deletions(-)
164 1 files changed, 1 insertions(+), 0 deletions(-)
161
165
162 and now "a/a" should reappear
166 and now "a/a" should reappear
163
167
164 $ hg unshelve -q wibble
168 $ hg unshelve -q wibble
165 $ hg status -C
169 $ hg status -C
166 M a/a
170 M a/a
167 A b.rename/b
171 A b.rename/b
168 b/b
172 b/b
169 A c.copy
173 A c.copy
170 c
174 c
171 R b/b
175 R b/b
172
176
173 cause unshelving to result in a merge with 'a' conflicting
177 cause unshelving to result in a merge with 'a' conflicting
174
178
175 $ hg shelve -q
179 $ hg shelve -q
176 $ echo c>>a/a
180 $ echo c>>a/a
177 $ hg commit -m second
181 $ hg commit -m second
178 $ hg tip --template '{files}\n'
182 $ hg tip --template '{files}\n'
179 a/a
183 a/a
180
184
181 add an unrelated change that should be preserved
185 add an unrelated change that should be preserved
182
186
183 $ mkdir foo
187 $ mkdir foo
184 $ echo foo > foo/foo
188 $ echo foo > foo/foo
185 $ hg add foo/foo
189 $ hg add foo/foo
186
190
187 force a conflicted merge to occur
191 force a conflicted merge to occur
188
192
189 $ hg unshelve
193 $ hg unshelve
190 unshelving change 'default'
194 unshelving change 'default'
191 adding changesets
195 adding changesets
192 adding manifests
196 adding manifests
193 adding file changes
197 adding file changes
194 added 1 changesets with 3 changes to 8 files (+1 heads)
198 added 1 changesets with 3 changes to 8 files (+1 heads)
195 merging a/a
199 merging a/a
196 warning: conflicts during merge.
200 warning: conflicts during merge.
197 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
201 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
198 2 files updated, 0 files merged, 1 files removed, 1 files unresolved
202 2 files updated, 0 files merged, 1 files removed, 1 files unresolved
199 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
203 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
200 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
204 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
201 [1]
205 [1]
202
206
203 ensure that we have a merge with unresolved conflicts
207 ensure that we have a merge with unresolved conflicts
204
208
205 $ hg heads -q
209 $ hg heads -q
206 3:6ea6529cfc65
210 4:cebf2b8de087
207 2:ceefc37abe1e
211 3:2e69b451d1ea
208 $ hg parents -q
212 $ hg parents -q
209 2:ceefc37abe1e
213 3:2e69b451d1ea
210 3:6ea6529cfc65
214 4:cebf2b8de087
211 $ hg status
215 $ hg status
212 M a/a
216 M a/a
213 M b.rename/b
217 M b.rename/b
214 M c.copy
218 M c.copy
215 A foo/foo
219 A foo/foo
216 R b/b
220 R b/b
217 ? a/a.orig
221 ? a/a.orig
218 $ hg diff
222 $ hg diff
219 diff --git a/a/a b/a/a
223 diff --git a/a/a b/a/a
220 --- a/a/a
224 --- a/a/a
221 +++ b/a/a
225 +++ b/a/a
222 @@ -1,2 +1,6 @@
226 @@ -1,2 +1,6 @@
223 a
227 a
224 +<<<<<<< local
228 +<<<<<<< local
225 c
229 c
226 +=======
230 +=======
227 +a
231 +a
228 +>>>>>>> other
232 +>>>>>>> other
229 diff --git a/b.rename/b b/b.rename/b
233 diff --git a/b.rename/b b/b.rename/b
230 --- /dev/null
234 --- /dev/null
231 +++ b/b.rename/b
235 +++ b/b.rename/b
232 @@ -0,0 +1,1 @@
236 @@ -0,0 +1,1 @@
233 +b
237 +b
234 diff --git a/b/b b/b/b
238 diff --git a/b/b b/b/b
235 deleted file mode 100644
239 deleted file mode 100644
236 --- a/b/b
240 --- a/b/b
237 +++ /dev/null
241 +++ /dev/null
238 @@ -1,1 +0,0 @@
242 @@ -1,1 +0,0 @@
239 -b
243 -b
240 diff --git a/c.copy b/c.copy
244 diff --git a/c.copy b/c.copy
241 --- /dev/null
245 --- /dev/null
242 +++ b/c.copy
246 +++ b/c.copy
243 @@ -0,0 +1,1 @@
247 @@ -0,0 +1,1 @@
244 +c
248 +c
245 diff --git a/foo/foo b/foo/foo
249 diff --git a/foo/foo b/foo/foo
246 new file mode 100644
250 new file mode 100644
247 --- /dev/null
251 --- /dev/null
248 +++ b/foo/foo
252 +++ b/foo/foo
249 @@ -0,0 +1,1 @@
253 @@ -0,0 +1,1 @@
250 +foo
254 +foo
251 $ hg resolve -l
255 $ hg resolve -l
252 U a/a
256 U a/a
253
257
254 $ hg shelve
258 $ hg shelve
255 abort: unshelve already in progress
259 abort: unshelve already in progress
256 (use 'hg unshelve --continue' or 'hg unshelve --abort')
260 (use 'hg unshelve --continue' or 'hg unshelve --abort')
257 [255]
261 [255]
258
262
259 abort the unshelve and be happy
263 abort the unshelve and be happy
260
264
261 $ hg status
265 $ hg status
262 M a/a
266 M a/a
263 M b.rename/b
267 M b.rename/b
264 M c.copy
268 M c.copy
265 A foo/foo
269 A foo/foo
266 R b/b
270 R b/b
267 ? a/a.orig
271 ? a/a.orig
268 $ hg unshelve -a
272 $ hg unshelve -a
269 unshelve of 'default' aborted
273 unshelve of 'default' aborted
270 $ hg heads -q
274 $ hg heads -q
271 2:ceefc37abe1e
275 3:2e69b451d1ea
272 $ hg parents
276 $ hg parents
273 changeset: 2:ceefc37abe1e
277 changeset: 3:2e69b451d1ea
274 tag: tip
278 tag: tip
275 user: test
279 user: test
276 date: Thu Jan 01 00:00:00 1970 +0000
280 date: Thu Jan 01 00:00:00 1970 +0000
277 summary: second
281 summary: second
278
282
279 $ hg resolve -l
283 $ hg resolve -l
280 $ hg status
284 $ hg status
281 A foo/foo
285 A foo/foo
282 ? a/a.orig
286 ? a/a.orig
283
287
284 try to continue with no unshelve underway
288 try to continue with no unshelve underway
285
289
286 $ hg unshelve -c
290 $ hg unshelve -c
287 abort: no unshelve operation underway
291 abort: no unshelve operation underway
288 [255]
292 [255]
289 $ hg status
293 $ hg status
290 A foo/foo
294 A foo/foo
291 ? a/a.orig
295 ? a/a.orig
292
296
293 redo the unshelve to get a conflict
297 redo the unshelve to get a conflict
294
298
295 $ hg unshelve -q
299 $ hg unshelve -q
296 warning: conflicts during merge.
300 warning: conflicts during merge.
297 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
301 merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
298 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
302 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
299 [1]
303 [1]
300
304
301 attempt to continue
305 attempt to continue
302
306
303 $ hg unshelve -c
307 $ hg unshelve -c
304 abort: unresolved conflicts, can't continue
308 abort: unresolved conflicts, can't continue
305 (see 'hg resolve', then 'hg unshelve --continue')
309 (see 'hg resolve', then 'hg unshelve --continue')
306 [255]
310 [255]
307
311
308 $ hg revert -r . a/a
312 $ hg revert -r . a/a
309 $ hg resolve -m a/a
313 $ hg resolve -m a/a
310
314
311 $ hg unshelve -c
315 $ hg unshelve -c
312 unshelve of 'default' complete
316 unshelve of 'default' complete
313
317
314 ensure the repo is as we hope
318 ensure the repo is as we hope
315
319
316 $ hg parents
320 $ hg parents
317 changeset: 2:ceefc37abe1e
321 changeset: 3:2e69b451d1ea
318 tag: tip
322 tag: tip
319 user: test
323 user: test
320 date: Thu Jan 01 00:00:00 1970 +0000
324 date: Thu Jan 01 00:00:00 1970 +0000
321 summary: second
325 summary: second
322
326
323 $ hg heads -q
327 $ hg heads -q
324 2:ceefc37abe1e
328 3:2e69b451d1ea
325
329
326 $ hg status -C
330 $ hg status -C
327 M a/a
331 M a/a
328 M b.rename/b
332 M b.rename/b
329 b/b
333 b/b
330 M c.copy
334 M c.copy
331 c
335 c
332 A foo/foo
336 A foo/foo
333 R b/b
337 R b/b
334 ? a/a.orig
338 ? a/a.orig
335
339
336 there should be no shelves left
340 there should be no shelves left
337
341
338 $ hg shelve -l
342 $ hg shelve -l
339
343
340 $ hg commit -m whee a/a
344 $ hg commit -m whee a/a
341
345
342 #if execbit
346 #if execbit
343
347
344 ensure that metadata-only changes are shelved
348 ensure that metadata-only changes are shelved
345
349
346 $ chmod +x a/a
350 $ chmod +x a/a
347 $ hg shelve -q -n execbit a/a
351 $ hg shelve -q -n execbit a/a
348 $ hg status a/a
352 $ hg status a/a
349 $ hg unshelve -q execbit
353 $ hg unshelve -q execbit
350 $ hg status a/a
354 $ hg status a/a
351 M a/a
355 M a/a
352 $ hg revert a/a
356 $ hg revert a/a
353
357
354 #endif
358 #endif
355
359
356 #if symlink
360 #if symlink
357
361
358 $ rm a/a
362 $ rm a/a
359 $ ln -s foo a/a
363 $ ln -s foo a/a
360 $ hg shelve -q -n symlink a/a
364 $ hg shelve -q -n symlink a/a
361 $ hg status a/a
365 $ hg status a/a
362 $ hg unshelve -q symlink
366 $ hg unshelve -q symlink
363 $ hg status a/a
367 $ hg status a/a
364 M a/a
368 M a/a
365 $ hg revert a/a
369 $ hg revert a/a
366
370
367 #endif
371 #endif
368
372
369 set up another conflict between a commit and a shelved change
373 set up another conflict between a commit and a shelved change
370
374
371 $ hg revert -q -C -a
375 $ hg revert -q -C -a
372 $ echo a >> a/a
376 $ echo a >> a/a
373 $ hg shelve -q
377 $ hg shelve -q
374 $ echo x >> a/a
378 $ echo x >> a/a
375 $ hg ci -m 'create conflict'
379 $ hg ci -m 'create conflict'
376 $ hg add foo/foo
380 $ hg add foo/foo
377
381
378 if we resolve a conflict while unshelving, the unshelve should succeed
382 if we resolve a conflict while unshelving, the unshelve should succeed
379
383
380 $ HGMERGE=true hg unshelve
384 $ HGMERGE=true hg unshelve
381 unshelving change 'default'
385 unshelving change 'default'
382 adding changesets
386 adding changesets
383 adding manifests
387 adding manifests
384 adding file changes
388 adding file changes
385 added 1 changesets with 1 changes to 6 files (+1 heads)
389 added 1 changesets with 1 changes to 6 files (+1 heads)
386 merging a/a
390 merging a/a
387 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
391 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
388 $ hg parents -q
392 $ hg parents -q
389 4:be7e79683c99
393 5:01ba9745dc5a
390 $ hg shelve -l
394 $ hg shelve -l
391 $ hg status
395 $ hg status
392 M a/a
396 M a/a
393 A foo/foo
397 A foo/foo
394 $ cat a/a
398 $ cat a/a
395 a
399 a
396 c
400 c
397 x
401 x
398
402
399 test keep and cleanup
403 test keep and cleanup
400
404
401 $ hg shelve
405 $ hg shelve
402 shelved as default
406 shelved as default
403 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
407 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
404 $ hg shelve --list
408 $ hg shelve --list
405 default (*) create conflict (glob)
409 default (*) create conflict (glob)
406 $ hg unshelve --keep
410 $ hg unshelve --keep
407 unshelving change 'default'
411 unshelving change 'default'
408 adding changesets
412 adding changesets
409 adding manifests
413 adding manifests
410 adding file changes
414 adding file changes
411 added 1 changesets with 1 changes to 7 files
415 added 1 changesets with 1 changes to 7 files
412 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
416 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 $ hg shelve --list
417 $ hg shelve --list
414 default (*) create conflict (glob)
418 default (*) create conflict (glob)
415 $ hg shelve --cleanup
419 $ hg shelve --cleanup
416 $ hg shelve --list
420 $ hg shelve --list
General Comments 0
You need to be logged in to leave comments. Login now